1*a71a9546SAutomerger Merge Worker /*
2*a71a9546SAutomerger Merge Worker * Xtables BPF extension
3*a71a9546SAutomerger Merge Worker *
4*a71a9546SAutomerger Merge Worker * Written by Willem de Bruijn ([email protected])
5*a71a9546SAutomerger Merge Worker * Copyright Google, Inc. 2013
6*a71a9546SAutomerger Merge Worker * Licensed under the GNU General Public License version 2 (GPLv2)
7*a71a9546SAutomerger Merge Worker */
8*a71a9546SAutomerger Merge Worker
9*a71a9546SAutomerger Merge Worker #include <linux/netfilter/xt_bpf.h>
10*a71a9546SAutomerger Merge Worker #include <errno.h>
11*a71a9546SAutomerger Merge Worker #include <fcntl.h>
12*a71a9546SAutomerger Merge Worker #include <stdio.h>
13*a71a9546SAutomerger Merge Worker #include <stdlib.h>
14*a71a9546SAutomerger Merge Worker #include <string.h>
15*a71a9546SAutomerger Merge Worker #include <sys/stat.h>
16*a71a9546SAutomerger Merge Worker #include <sys/types.h>
17*a71a9546SAutomerger Merge Worker #include <unistd.h>
18*a71a9546SAutomerger Merge Worker #include <xtables.h>
19*a71a9546SAutomerger Merge Worker #include "config.h"
20*a71a9546SAutomerger Merge Worker
21*a71a9546SAutomerger Merge Worker #ifdef HAVE_LINUX_BPF_H
22*a71a9546SAutomerger Merge Worker #include <linux/bpf.h>
23*a71a9546SAutomerger Merge Worker #endif
24*a71a9546SAutomerger Merge Worker
25*a71a9546SAutomerger Merge Worker #include <linux/magic.h>
26*a71a9546SAutomerger Merge Worker #include <linux/unistd.h>
27*a71a9546SAutomerger Merge Worker
28*a71a9546SAutomerger Merge Worker #define BCODE_FILE_MAX_LEN_B 1024
29*a71a9546SAutomerger Merge Worker
30*a71a9546SAutomerger Merge Worker enum {
31*a71a9546SAutomerger Merge Worker O_BCODE_STDIN = 0,
32*a71a9546SAutomerger Merge Worker O_OBJ_PINNED = 1,
33*a71a9546SAutomerger Merge Worker };
34*a71a9546SAutomerger Merge Worker
bpf_help(void)35*a71a9546SAutomerger Merge Worker static void bpf_help(void)
36*a71a9546SAutomerger Merge Worker {
37*a71a9546SAutomerger Merge Worker printf(
38*a71a9546SAutomerger Merge Worker "bpf match options:\n"
39*a71a9546SAutomerger Merge Worker "--bytecode <program> : a bpf program as generated by\n"
40*a71a9546SAutomerger Merge Worker " $(nfbpf_compile RAW '<filter>')\n");
41*a71a9546SAutomerger Merge Worker }
42*a71a9546SAutomerger Merge Worker
bpf_help_v1(void)43*a71a9546SAutomerger Merge Worker static void bpf_help_v1(void)
44*a71a9546SAutomerger Merge Worker {
45*a71a9546SAutomerger Merge Worker printf(
46*a71a9546SAutomerger Merge Worker "bpf match options:\n"
47*a71a9546SAutomerger Merge Worker "--bytecode <program> : a bpf program as generated by\n"
48*a71a9546SAutomerger Merge Worker " $(nfbpf_compile RAW '<filter>')\n"
49*a71a9546SAutomerger Merge Worker "--object-pinned <bpf object> : a path to a pinned BPF object in bpf fs\n");
50*a71a9546SAutomerger Merge Worker }
51*a71a9546SAutomerger Merge Worker
52*a71a9546SAutomerger Merge Worker static const struct xt_option_entry bpf_opts[] = {
53*a71a9546SAutomerger Merge Worker {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
54*a71a9546SAutomerger Merge Worker XTOPT_TABLEEND,
55*a71a9546SAutomerger Merge Worker };
56*a71a9546SAutomerger Merge Worker
57*a71a9546SAutomerger Merge Worker static const struct xt_option_entry bpf_opts_v1[] = {
58*a71a9546SAutomerger Merge Worker {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
59*a71a9546SAutomerger Merge Worker {.name = "object-pinned" , .id = O_OBJ_PINNED, .type = XTTYPE_STRING,
60*a71a9546SAutomerger Merge Worker .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_bpf_info_v1, path)},
61*a71a9546SAutomerger Merge Worker XTOPT_TABLEEND,
62*a71a9546SAutomerger Merge Worker };
63*a71a9546SAutomerger Merge Worker
bpf_obj_get_readonly(const char * filepath)64*a71a9546SAutomerger Merge Worker static int bpf_obj_get_readonly(const char *filepath)
65*a71a9546SAutomerger Merge Worker {
66*a71a9546SAutomerger Merge Worker #if defined HAVE_LINUX_BPF_H && defined __NR_bpf && defined BPF_FS_MAGIC
67*a71a9546SAutomerger Merge Worker /* union bpf_attr includes this in an anonymous struct, but the
68*a71a9546SAutomerger Merge Worker * file_flags field and the BPF_F_RDONLY constant are only present
69*a71a9546SAutomerger Merge Worker * in Linux 4.15+ kernel headers (include/uapi/linux/bpf.h)
70*a71a9546SAutomerger Merge Worker */
71*a71a9546SAutomerger Merge Worker struct { // this part of union bpf_attr is for BPF_OBJ_* commands
72*a71a9546SAutomerger Merge Worker __aligned_u64 pathname;
73*a71a9546SAutomerger Merge Worker __u32 bpf_fd;
74*a71a9546SAutomerger Merge Worker __u32 file_flags;
75*a71a9546SAutomerger Merge Worker } attr = {
76*a71a9546SAutomerger Merge Worker .pathname = (__u64)filepath,
77*a71a9546SAutomerger Merge Worker .file_flags = (1U << 3), // BPF_F_RDONLY
78*a71a9546SAutomerger Merge Worker };
79*a71a9546SAutomerger Merge Worker int fd = syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
80*a71a9546SAutomerger Merge Worker if (fd >= 0) return fd;
81*a71a9546SAutomerger Merge Worker
82*a71a9546SAutomerger Merge Worker /* on any error fallback to default R/W access for pre-4.15-rc1 kernels */
83*a71a9546SAutomerger Merge Worker attr.file_flags = 0;
84*a71a9546SAutomerger Merge Worker return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
85*a71a9546SAutomerger Merge Worker #else
86*a71a9546SAutomerger Merge Worker xtables_error(OTHER_PROBLEM, "No bpf header, kernel headers too old?");
87*a71a9546SAutomerger Merge Worker return -EINVAL;
88*a71a9546SAutomerger Merge Worker #endif
89*a71a9546SAutomerger Merge Worker }
90*a71a9546SAutomerger Merge Worker
bpf_parse_string(struct sock_filter * pc,__u16 * lenp,__u16 len_max,const char * bpf_program)91*a71a9546SAutomerger Merge Worker static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max,
92*a71a9546SAutomerger Merge Worker const char *bpf_program)
93*a71a9546SAutomerger Merge Worker {
94*a71a9546SAutomerger Merge Worker const char separator = ',';
95*a71a9546SAutomerger Merge Worker const char *token;
96*a71a9546SAutomerger Merge Worker char sp;
97*a71a9546SAutomerger Merge Worker int i;
98*a71a9546SAutomerger Merge Worker __u16 len;
99*a71a9546SAutomerger Merge Worker
100*a71a9546SAutomerger Merge Worker /* parse head: length. */
101*a71a9546SAutomerger Merge Worker if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 ||
102*a71a9546SAutomerger Merge Worker sp != separator)
103*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
104*a71a9546SAutomerger Merge Worker "bpf: error parsing program length");
105*a71a9546SAutomerger Merge Worker if (!len)
106*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
107*a71a9546SAutomerger Merge Worker "bpf: illegal zero length program");
108*a71a9546SAutomerger Merge Worker if (len > len_max)
109*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
110*a71a9546SAutomerger Merge Worker "bpf: number of instructions exceeds maximum");
111*a71a9546SAutomerger Merge Worker
112*a71a9546SAutomerger Merge Worker /* parse instructions. */
113*a71a9546SAutomerger Merge Worker i = 0;
114*a71a9546SAutomerger Merge Worker token = bpf_program;
115*a71a9546SAutomerger Merge Worker while ((token = strchr(token, separator)) && (++token)[0]) {
116*a71a9546SAutomerger Merge Worker if (i >= len)
117*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
118*a71a9546SAutomerger Merge Worker "bpf: real program length exceeds"
119*a71a9546SAutomerger Merge Worker " the encoded length parameter");
120*a71a9546SAutomerger Merge Worker if (sscanf(token, "%hu %hhu %hhu %u,",
121*a71a9546SAutomerger Merge Worker &pc->code, &pc->jt, &pc->jf, &pc->k) != 4)
122*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
123*a71a9546SAutomerger Merge Worker "bpf: error at instr %d", i);
124*a71a9546SAutomerger Merge Worker i++;
125*a71a9546SAutomerger Merge Worker pc++;
126*a71a9546SAutomerger Merge Worker }
127*a71a9546SAutomerger Merge Worker
128*a71a9546SAutomerger Merge Worker if (i != len)
129*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
130*a71a9546SAutomerger Merge Worker "bpf: parsed program length is less than the"
131*a71a9546SAutomerger Merge Worker " encoded length parameter");
132*a71a9546SAutomerger Merge Worker
133*a71a9546SAutomerger Merge Worker *lenp = len;
134*a71a9546SAutomerger Merge Worker }
135*a71a9546SAutomerger Merge Worker
bpf_parse_obj_pinned(struct xt_bpf_info_v1 * bi,const char * filepath)136*a71a9546SAutomerger Merge Worker static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi,
137*a71a9546SAutomerger Merge Worker const char *filepath)
138*a71a9546SAutomerger Merge Worker {
139*a71a9546SAutomerger Merge Worker bi->fd = bpf_obj_get_readonly(filepath);
140*a71a9546SAutomerger Merge Worker if (bi->fd < 0)
141*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
142*a71a9546SAutomerger Merge Worker "bpf: failed to get bpf object");
143*a71a9546SAutomerger Merge Worker
144*a71a9546SAutomerger Merge Worker /* Cannot close bi->fd explicitly. Rely on exit */
145*a71a9546SAutomerger Merge Worker if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) {
146*a71a9546SAutomerger Merge Worker xtables_error(OTHER_PROBLEM,
147*a71a9546SAutomerger Merge Worker "Could not set close on exec: %s\n",
148*a71a9546SAutomerger Merge Worker strerror(errno));
149*a71a9546SAutomerger Merge Worker }
150*a71a9546SAutomerger Merge Worker }
151*a71a9546SAutomerger Merge Worker
bpf_parse(struct xt_option_call * cb)152*a71a9546SAutomerger Merge Worker static void bpf_parse(struct xt_option_call *cb)
153*a71a9546SAutomerger Merge Worker {
154*a71a9546SAutomerger Merge Worker struct xt_bpf_info *bi = (void *) cb->data;
155*a71a9546SAutomerger Merge Worker
156*a71a9546SAutomerger Merge Worker xtables_option_parse(cb);
157*a71a9546SAutomerger Merge Worker switch (cb->entry->id) {
158*a71a9546SAutomerger Merge Worker case O_BCODE_STDIN:
159*a71a9546SAutomerger Merge Worker bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
160*a71a9546SAutomerger Merge Worker ARRAY_SIZE(bi->bpf_program), cb->arg);
161*a71a9546SAutomerger Merge Worker break;
162*a71a9546SAutomerger Merge Worker default:
163*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
164*a71a9546SAutomerger Merge Worker }
165*a71a9546SAutomerger Merge Worker }
166*a71a9546SAutomerger Merge Worker
bpf_parse_v1(struct xt_option_call * cb)167*a71a9546SAutomerger Merge Worker static void bpf_parse_v1(struct xt_option_call *cb)
168*a71a9546SAutomerger Merge Worker {
169*a71a9546SAutomerger Merge Worker struct xt_bpf_info_v1 *bi = (void *) cb->data;
170*a71a9546SAutomerger Merge Worker
171*a71a9546SAutomerger Merge Worker xtables_option_parse(cb);
172*a71a9546SAutomerger Merge Worker switch (cb->entry->id) {
173*a71a9546SAutomerger Merge Worker case O_BCODE_STDIN:
174*a71a9546SAutomerger Merge Worker bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
175*a71a9546SAutomerger Merge Worker ARRAY_SIZE(bi->bpf_program), cb->arg);
176*a71a9546SAutomerger Merge Worker bi->mode = XT_BPF_MODE_BYTECODE;
177*a71a9546SAutomerger Merge Worker break;
178*a71a9546SAutomerger Merge Worker case O_OBJ_PINNED:
179*a71a9546SAutomerger Merge Worker bpf_parse_obj_pinned(bi, cb->arg);
180*a71a9546SAutomerger Merge Worker bi->mode = XT_BPF_MODE_FD_PINNED;
181*a71a9546SAutomerger Merge Worker break;
182*a71a9546SAutomerger Merge Worker default:
183*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
184*a71a9546SAutomerger Merge Worker }
185*a71a9546SAutomerger Merge Worker }
186*a71a9546SAutomerger Merge Worker
bpf_print_code(const struct sock_filter * pc,__u16 len,char tail)187*a71a9546SAutomerger Merge Worker static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail)
188*a71a9546SAutomerger Merge Worker {
189*a71a9546SAutomerger Merge Worker for (; len; len--, pc++)
190*a71a9546SAutomerger Merge Worker printf("%hu %hhu %hhu %u%c",
191*a71a9546SAutomerger Merge Worker pc->code, pc->jt, pc->jf, pc->k,
192*a71a9546SAutomerger Merge Worker len > 1 ? ',' : tail);
193*a71a9546SAutomerger Merge Worker }
194*a71a9546SAutomerger Merge Worker
bpf_save_code(const struct sock_filter * pc,__u16 len)195*a71a9546SAutomerger Merge Worker static void bpf_save_code(const struct sock_filter *pc, __u16 len)
196*a71a9546SAutomerger Merge Worker {
197*a71a9546SAutomerger Merge Worker printf(" --bytecode \"%hu,", len);
198*a71a9546SAutomerger Merge Worker bpf_print_code(pc, len, '\"');
199*a71a9546SAutomerger Merge Worker }
200*a71a9546SAutomerger Merge Worker
bpf_save(const void * ip,const struct xt_entry_match * match)201*a71a9546SAutomerger Merge Worker static void bpf_save(const void *ip, const struct xt_entry_match *match)
202*a71a9546SAutomerger Merge Worker {
203*a71a9546SAutomerger Merge Worker const struct xt_bpf_info *info = (void *) match->data;
204*a71a9546SAutomerger Merge Worker
205*a71a9546SAutomerger Merge Worker bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
206*a71a9546SAutomerger Merge Worker }
207*a71a9546SAutomerger Merge Worker
bpf_save_v1(const void * ip,const struct xt_entry_match * match)208*a71a9546SAutomerger Merge Worker static void bpf_save_v1(const void *ip, const struct xt_entry_match *match)
209*a71a9546SAutomerger Merge Worker {
210*a71a9546SAutomerger Merge Worker const struct xt_bpf_info_v1 *info = (void *) match->data;
211*a71a9546SAutomerger Merge Worker
212*a71a9546SAutomerger Merge Worker if (info->mode == XT_BPF_MODE_BYTECODE)
213*a71a9546SAutomerger Merge Worker bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
214*a71a9546SAutomerger Merge Worker else if (info->mode == XT_BPF_MODE_FD_PINNED)
215*a71a9546SAutomerger Merge Worker printf(" --object-pinned %s", info->path);
216*a71a9546SAutomerger Merge Worker else
217*a71a9546SAutomerger Merge Worker xtables_error(OTHER_PROBLEM, "unknown bpf mode");
218*a71a9546SAutomerger Merge Worker }
219*a71a9546SAutomerger Merge Worker
bpf_fcheck(struct xt_fcheck_call * cb)220*a71a9546SAutomerger Merge Worker static void bpf_fcheck(struct xt_fcheck_call *cb)
221*a71a9546SAutomerger Merge Worker {
222*a71a9546SAutomerger Merge Worker if (!(cb->xflags & (1 << O_BCODE_STDIN)))
223*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
224*a71a9546SAutomerger Merge Worker "bpf: missing --bytecode parameter");
225*a71a9546SAutomerger Merge Worker }
226*a71a9546SAutomerger Merge Worker
bpf_fcheck_v1(struct xt_fcheck_call * cb)227*a71a9546SAutomerger Merge Worker static void bpf_fcheck_v1(struct xt_fcheck_call *cb)
228*a71a9546SAutomerger Merge Worker {
229*a71a9546SAutomerger Merge Worker const unsigned int bit_bcode = 1 << O_BCODE_STDIN;
230*a71a9546SAutomerger Merge Worker const unsigned int bit_pinned = 1 << O_OBJ_PINNED;
231*a71a9546SAutomerger Merge Worker unsigned int flags;
232*a71a9546SAutomerger Merge Worker
233*a71a9546SAutomerger Merge Worker flags = cb->xflags & (bit_bcode | bit_pinned);
234*a71a9546SAutomerger Merge Worker if (flags != bit_bcode && flags != bit_pinned)
235*a71a9546SAutomerger Merge Worker xtables_error(PARAMETER_PROBLEM,
236*a71a9546SAutomerger Merge Worker "bpf: one of --bytecode or --pinned is required");
237*a71a9546SAutomerger Merge Worker }
238*a71a9546SAutomerger Merge Worker
bpf_print(const void * ip,const struct xt_entry_match * match,int numeric)239*a71a9546SAutomerger Merge Worker static void bpf_print(const void *ip, const struct xt_entry_match *match,
240*a71a9546SAutomerger Merge Worker int numeric)
241*a71a9546SAutomerger Merge Worker {
242*a71a9546SAutomerger Merge Worker const struct xt_bpf_info *info = (void *) match->data;
243*a71a9546SAutomerger Merge Worker
244*a71a9546SAutomerger Merge Worker printf("match bpf ");
245*a71a9546SAutomerger Merge Worker bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
246*a71a9546SAutomerger Merge Worker }
247*a71a9546SAutomerger Merge Worker
bpf_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)248*a71a9546SAutomerger Merge Worker static void bpf_print_v1(const void *ip, const struct xt_entry_match *match,
249*a71a9546SAutomerger Merge Worker int numeric)
250*a71a9546SAutomerger Merge Worker {
251*a71a9546SAutomerger Merge Worker const struct xt_bpf_info_v1 *info = (void *) match->data;
252*a71a9546SAutomerger Merge Worker
253*a71a9546SAutomerger Merge Worker printf("match bpf ");
254*a71a9546SAutomerger Merge Worker if (info->mode == XT_BPF_MODE_BYTECODE)
255*a71a9546SAutomerger Merge Worker bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
256*a71a9546SAutomerger Merge Worker else if (info->mode == XT_BPF_MODE_FD_PINNED)
257*a71a9546SAutomerger Merge Worker printf("pinned %s", info->path);
258*a71a9546SAutomerger Merge Worker else
259*a71a9546SAutomerger Merge Worker printf("unknown");
260*a71a9546SAutomerger Merge Worker }
261*a71a9546SAutomerger Merge Worker
262*a71a9546SAutomerger Merge Worker static struct xtables_match bpf_matches[] = {
263*a71a9546SAutomerger Merge Worker {
264*a71a9546SAutomerger Merge Worker .family = NFPROTO_UNSPEC,
265*a71a9546SAutomerger Merge Worker .name = "bpf",
266*a71a9546SAutomerger Merge Worker .version = XTABLES_VERSION,
267*a71a9546SAutomerger Merge Worker .revision = 0,
268*a71a9546SAutomerger Merge Worker .size = XT_ALIGN(sizeof(struct xt_bpf_info)),
269*a71a9546SAutomerger Merge Worker .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
270*a71a9546SAutomerger Merge Worker .help = bpf_help,
271*a71a9546SAutomerger Merge Worker .print = bpf_print,
272*a71a9546SAutomerger Merge Worker .save = bpf_save,
273*a71a9546SAutomerger Merge Worker .x6_parse = bpf_parse,
274*a71a9546SAutomerger Merge Worker .x6_fcheck = bpf_fcheck,
275*a71a9546SAutomerger Merge Worker .x6_options = bpf_opts,
276*a71a9546SAutomerger Merge Worker },
277*a71a9546SAutomerger Merge Worker {
278*a71a9546SAutomerger Merge Worker .family = NFPROTO_UNSPEC,
279*a71a9546SAutomerger Merge Worker .name = "bpf",
280*a71a9546SAutomerger Merge Worker .version = XTABLES_VERSION,
281*a71a9546SAutomerger Merge Worker .revision = 1,
282*a71a9546SAutomerger Merge Worker .size = XT_ALIGN(sizeof(struct xt_bpf_info_v1)),
283*a71a9546SAutomerger Merge Worker .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)),
284*a71a9546SAutomerger Merge Worker .help = bpf_help_v1,
285*a71a9546SAutomerger Merge Worker .print = bpf_print_v1,
286*a71a9546SAutomerger Merge Worker .save = bpf_save_v1,
287*a71a9546SAutomerger Merge Worker .x6_parse = bpf_parse_v1,
288*a71a9546SAutomerger Merge Worker .x6_fcheck = bpf_fcheck_v1,
289*a71a9546SAutomerger Merge Worker .x6_options = bpf_opts_v1,
290*a71a9546SAutomerger Merge Worker },
291*a71a9546SAutomerger Merge Worker };
292*a71a9546SAutomerger Merge Worker
_init(void)293*a71a9546SAutomerger Merge Worker void _init(void)
294*a71a9546SAutomerger Merge Worker {
295*a71a9546SAutomerger Merge Worker xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
296*a71a9546SAutomerger Merge Worker }
297