summaryrefslogtreecommitdiffstats
path: root/lib/libxdp/xdp-dispatcher.c.in
blob: 6214d7812885c5f2c9057873118b62040d3d5f17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* SPDX-License-Identifier: GPL-2.0 */
divert(-1)
#forloop definition taken from example in the M4 manual
define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')')
define(`_forloop',`$4`'ifelse($1, decr(`$3'), `', `define(`$1', incr($1))$0($@)')')
define(`NUM_PROGS',ifdef(`MAX_DISPATCHER_ACTIONS', MAX_DISPATCHER_ACTIONS, `10'))
divert(0)dnl

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

#include <xdp/prog_dispatcher.h>

/* While 'const volatile' sounds a little like an oxymoron, there's reason
 * behind the madness:
 *
 * - const places the data in rodata, where libbpf will mark it as read-only and
 *   frozen on program load, letting the kernel do dead code elimination based
 *   on the values.
 *
 * - volatile prevents the compiler from optimising away the checks based on the
 *   compile-time value of the variables, which is important since we will be
 *   changing the values before loading the program into the kernel.
 */
static volatile const struct xdp_dispatcher_config conf = {};

/* The volatile return value prevents the compiler from assuming it knows the
 * return value and optimising based on that.
 */
forloop(`i', `0', NUM_PROGS,
`__attribute__ ((noinline))
int format(`prog%d', i)(struct xdp_md *ctx) {
        volatile int ret = XDP_DISPATCHER_RETVAL;

        if (!ctx)
          return XDP_ABORTED;
        return ret;
}
')

__attribute__ ((noinline))
int compat_test(struct xdp_md *ctx) {
        volatile int ret = XDP_DISPATCHER_RETVAL;

        if (!ctx)
          return XDP_ABORTED;
        return ret;
}


SEC("xdp")
int xdp_dispatcher(struct xdp_md *ctx)
{
        __u8 num_progs_enabled = conf.num_progs_enabled;
        int ret;
forloop(`i', `0', NUM_PROGS,
`
        if (num_progs_enabled < incr(i))
                goto out;
        ret = format(`prog%d', i)(ctx);
        if (!((1U << ret) & conf.chain_call_actions[i]))
                return ret;
')
        /* keep a reference to the compat_test() function so we can use it
         * as an freplace target in xdp_multiprog__check_compat() in libxdp
         */
        if (num_progs_enabled < incr(NUM_PROGS))
                goto out;
        ret = compat_test(ctx);
out:
        return XDP_PASS;
}

SEC("xdp")
int xdp_pass(struct xdp_md *ctx)
{
        return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
__uint(dispatcher_version, XDP_DISPATCHER_VERSION) SEC(XDP_METADATA_SECTION);