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