Raw File
common.h
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright Authors of Cilium */

#ifndef ____BPF_TEST_COMMON____
#define ____BPF_TEST_COMMON____

#include <linux/types.h>
#include <linux/bpf.h>
#include <bpf/compiler.h>
#include <bpf/loader.h>
#include <bpf/section.h>

/* We can use this macro inside the actual datapath code
 * to compile-in the code for testing. The primary usecase
 * is initializing map-in-map or prog-map.
 */
#define BPF_TEST

#ifndef ___bpf_concat
#define ___bpf_concat(a, b) a ## b
#endif
#ifndef ___bpf_apply
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
#endif
#ifndef ___bpf_nth
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
#endif
#ifndef ___bpf_narg
#define ___bpf_narg(...) \
	___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#endif

#define __bpf_log_arg0(ptr, arg) do {} while (0)
#define __bpf_log_arg1(ptr, arg) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; ptr += sizeof(__u64)
#define __bpf_log_arg2(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg1(ptr, args)
#define __bpf_log_arg3(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg2(ptr, args)
#define __bpf_log_arg4(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg3(ptr, args)
#define __bpf_log_arg5(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg4(ptr, args)
#define __bpf_log_arg6(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg5(ptr, args)
#define __bpf_log_arg7(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg6(ptr, args)
#define __bpf_log_arg8(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg7(ptr, args)
#define __bpf_log_arg9(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg8(ptr, args)
#define __bpf_log_arg10(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg9(ptr, args)
#define __bpf_log_arg11(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg10(ptr, args)
#define __bpf_log_arg12(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \
					  ptr += sizeof(__u64); __bpf_log_arg11(ptr, args)
#define __bpf_log_arg(ptr, args...) \
	___bpf_apply(__bpf_log_arg, ___bpf_narg(args))(ptr, args)

/* These values have to stay in sync with the enum */
/* values in test/bpf_tests/trf.proto */
#define TEST_ERROR 0
#define TEST_PASS 1
#define TEST_FAIL 2
#define TEST_SKIP 3

/* Use an array map with 1 key and a large value size as buffer to write results */
/* into. */
struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, 8192);
	__uint(pinning, LIBBPF_PIN_BY_NAME);
	__uint(max_entries, 1);
} suite_result_map __section_maps_btf;

/* Values for the markers below are derived from this guide: */
/* https://developers.google.com/protocol-buffers/docs/encoding#structure */

#define PROTOBUF_WIRE_TYPE(field, type) ((field) << 3 | (type))

#define PROTOBUF_VARINT 0
#define PROTOBUF_FIXED64 1
#define PROTOBUF_LENGTH_DELIMITED 2

/* message SuiteResult */
#define MKR_TEST_RESULT PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
#define MKR_SUITE_LOG	PROTOBUF_WIRE_TYPE(2, PROTOBUF_LENGTH_DELIMITED)

/* message TestResult */
#define MKR_TEST_NAME	PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
#define MKR_TEST_STATUS PROTOBUF_WIRE_TYPE(2, PROTOBUF_VARINT)
#define MKR_TEST_LOG	PROTOBUF_WIRE_TYPE(3, PROTOBUF_LENGTH_DELIMITED)

/* message Log */
#define MKR_LOG_FMT	PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
#define MKR_LOG_ARG	PROTOBUF_WIRE_TYPE(2, PROTOBUF_FIXED64)

/* Write a message to the unit log
 *	The conversion specifiers supported by *fmt* are the same as for
 *      bpf_trace_printk(). They are **%d**, **%i**, **%u**, **%x**, **%ld**,
 *      **%li**, **%lu**, **%lx**, **%lld**, **%lli**, **%llu**, **%llx**,
 *      **%p**. No modifier (size of field, padding with zeroes, etc.)
 *      is available
 */
#define test_log(fmt, args...)						\
({									\
	static const char ____fmt[] = fmt;				\
	if (test_result_cursor) {					\
		*(suite_result_cursor++) = MKR_TEST_LOG;		\
	} else {							\
		*(suite_result_cursor++) = MKR_SUITE_LOG;		\
	}								\
	*(suite_result_cursor++) = 2 + sizeof(____fmt) +		\
		___bpf_narg(args) +					\
		(___bpf_narg(args) * sizeof(unsigned long long));	\
	*(suite_result_cursor++) = MKR_LOG_FMT;			\
	*(suite_result_cursor++) = sizeof(____fmt);			\
	memcpy(suite_result_cursor, ____fmt, sizeof(____fmt));		\
	suite_result_cursor += sizeof(____fmt);			\
									\
									\
	if (___bpf_narg(args) > 0) {					\
		__bpf_log_arg(suite_result_cursor, args);		\
	}								\
})

/* This is a hack to allow us to convert the integer produced by __LINE__ */
/* to a string so we can concat it at compile time. */
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_STRING STRINGIZE(__LINE__)

/* Mark the current test as failed */
#define test_fail()				\
	if (test_result_cursor) {		\
		*test_result_status = TEST_FAIL;\
	} else {				\
		suite_result = TEST_FAIL;	\
	}					\

/* Mark the current test as failed and exit the current TEST/CHECK */
#define test_fail_now()				\
	if (test_result_cursor) {		\
		*test_result_status = TEST_FAIL;\
		break;				\
	} else {				\
		return TEST_FAIL;		\
	}

/* Mark the current test as skipped */
#define test_skip()				\
	if (test_result_cursor) {		\
		*test_result_status = TEST_SKIP;\
	} else {				\
		suite_result = TEST_SKIP;	\
	}

/* Mark the current test as skipped and exit the current TEST/CHECK */
#define test_skip_now()					\
	if (test_result_cursor) {			\
		*test_result_status = TEST_SKIP;	\
		break;					\
	} else {					\
		return TEST_SKIP;			\
	}

/* Write message to the log and mark current test as failed. */
#define test_error(fmt, ...)			\
	{					\
		test_log(fmt, ##__VA_ARGS__);	\
		test_fail();			\
	}

/* Log a message bpf_then fail_now */
#define test_fatal(fmt, ...)			\
	{					\
		test_log(fmt, ##__VA_ARGS__);	\
		test_fail_now()			\
	}

/* Assert that `cond` is true, fail the rest otherwise */
#define assert(cond)							\
	if (!(cond)) {							\
		test_log("assert failed at " __FILE__ ":" LINE_STRING);	\
		test_fail_now();					\
	}

/* Declare bpf_map_lookup_elem with the test_ prefix to avoid conflicts in the */
/* future. */
static void *(*test_bpf_map_lookup_elem)(void *map, const void *key) = (void *)1;

/* Init sets up a number of variables which will be used by other macros. */
/* - suite_result will be returned from the eBPF program */
/* - test_result_status is a pointer into the suite_result_map when in a test */
/* - suite_result_cursor keeps track of where in the suite result we are. */
/* - test_result_cursor is a pointer to the varint of a test result, used to */
/*   write the amount of bytes used after a test is done. */
#define test_init()							  \
	char suite_result = TEST_PASS;					  \
	__maybe_unused char *test_result_status = 0;			  \
	char *suite_result_cursor;					  \
	{								  \
		__u32 __key = 0;						  \
		suite_result_cursor =					  \
			test_bpf_map_lookup_elem(&suite_result_map, &__key);\
		if (!suite_result_cursor) {				  \
			return TEST_ERROR;				  \
		}							  \
	}								  \
	__maybe_unused char *test_result_cursor = 0;			  \
	__maybe_unused __u16 test_result_size;				  \
	do {
/* */
/* Each test is single iteration do-while loop so we can break, to exit the */
/* test without unique label names and goto's */
#define TEST(name, body)						   \
do {									   \
	*(suite_result_cursor++) = MKR_TEST_RESULT;			   \
	/* test_result_cursor will stay at test result length varint */    \
	test_result_cursor = suite_result_cursor;			   \
	/* Reserve 2 bytes for the varint indicating test result length */ \
	suite_result_cursor += 2;					   \
									   \
	static const char ____name[] = name;				   \
	*(suite_result_cursor++) = MKR_TEST_NAME;			   \
	*(suite_result_cursor++) = sizeof(____name);			   \
	memcpy(suite_result_cursor, ____name, sizeof(____name));	   \
	suite_result_cursor += sizeof(____name);			   \
									   \
	*(suite_result_cursor++) = MKR_TEST_STATUS;			   \
	test_result_status = suite_result_cursor;			   \
									   \
	*test_result_status = TEST_PASS;				   \
	suite_result_cursor++;						   \
									   \
	body								   \
} while (0);								   \
/* Write the total size of the test result in bytes as varint */	   \
test_result_size = (__u16)((long)suite_result_cursor -			   \
	(long)test_result_cursor) - 2;					   \
if (test_result_size > 127) {						   \
	*(test_result_cursor) = (__u8)(test_result_size & 0b01111111) |	   \
		0b10000000;						   \
	test_result_size >>= 7;						   \
	*(test_result_cursor + 1) = (__u8)test_result_size;		   \
} else {								   \
	*test_result_cursor = (__u8)(test_result_size) | 0b10000000;	   \
}									   \
test_result_cursor = 0;

#define test_finish()		\
	} while (0);		\
	return suite_result

#define PKTGEN(progtype, name) __section(progtype "/test/" name "/pktgen")
#define SETUP(progtype, name) __section(progtype "/test/" name "/setup")
#define CHECK(progtype, name) __section(progtype "/test/" name "/check")

#define LPM_LOOKUP_FN(NAME, IPTYPE, PREFIXES, MAP, LOOKUP_FN)	\
static __always_inline int __##NAME(IPTYPE addr)		\
{								\
	int prefixes[] = { PREFIXES };				\
	const int size = ARRAY_SIZE(prefixes);			\
	int i;							\
								\
_Pragma("unroll")						\
	for (i = 0; i < size; i++)				\
		if (LOOKUP_FN(&(MAP), addr, prefixes[i]))	\
			return 1;				\
								\
	return 0;						\
}

#endif /* ____BPF_TEST_COMMON____ */
back to top