1 /*
2 * Xtables BPF extension
3 *
4 * Written by Willem de Bruijn (willemb@google.com)
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,
87 "No bpf header, kernel headers too old?\n");
88 return -EINVAL;
89 #endif
90 }
91
bpf_parse_string(struct sock_filter * pc,__u16 * lenp,__u16 len_max,const char * bpf_program)92 static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max,
93 const char *bpf_program)
94 {
95 const char separator = ',';
96 const char *token;
97 char sp;
98 int i;
99 __u16 len;
100
101 /* parse head: length. */
102 if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 ||
103 sp != separator)
104 xtables_error(PARAMETER_PROBLEM,
105 "bpf: error parsing program length");
106 if (!len)
107 xtables_error(PARAMETER_PROBLEM,
108 "bpf: illegal zero length program");
109 if (len > len_max)
110 xtables_error(PARAMETER_PROBLEM,
111 "bpf: number of instructions exceeds maximum");
112
113 /* parse instructions. */
114 i = 0;
115 token = bpf_program;
116 while ((token = strchr(token, separator)) && (++token)[0]) {
117 if (i >= len)
118 xtables_error(PARAMETER_PROBLEM,
119 "bpf: real program length exceeds"
120 " the encoded length parameter");
121 if (sscanf(token, "%hu %hhu %hhu %u,",
122 &pc->code, &pc->jt, &pc->jf, &pc->k) != 4)
123 xtables_error(PARAMETER_PROBLEM,
124 "bpf: error at instr %d", i);
125 i++;
126 pc++;
127 }
128
129 if (i != len)
130 xtables_error(PARAMETER_PROBLEM,
131 "bpf: parsed program length is less than the"
132 " encoded length parameter");
133
134 *lenp = len;
135 }
136
bpf_parse_obj_pinned(struct xt_bpf_info_v1 * bi,const char * filepath)137 static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi,
138 const char *filepath)
139 {
140 bi->fd = bpf_obj_get_readonly(filepath);
141 if (bi->fd < 0)
142 xtables_error(PARAMETER_PROBLEM,
143 "bpf: failed to get bpf object");
144
145 /* Cannot close bi->fd explicitly. Rely on exit */
146 if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) {
147 xtables_error(OTHER_PROBLEM,
148 "Could not set close on exec: %s\n",
149 strerror(errno));
150 }
151 }
152
bpf_parse(struct xt_option_call * cb)153 static void bpf_parse(struct xt_option_call *cb)
154 {
155 struct xt_bpf_info *bi = (void *) cb->data;
156
157 xtables_option_parse(cb);
158 switch (cb->entry->id) {
159 case O_BCODE_STDIN:
160 bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
161 ARRAY_SIZE(bi->bpf_program), cb->arg);
162 break;
163 default:
164 xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
165 }
166 }
167
bpf_parse_v1(struct xt_option_call * cb)168 static void bpf_parse_v1(struct xt_option_call *cb)
169 {
170 struct xt_bpf_info_v1 *bi = (void *) cb->data;
171
172 xtables_option_parse(cb);
173 switch (cb->entry->id) {
174 case O_BCODE_STDIN:
175 bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
176 ARRAY_SIZE(bi->bpf_program), cb->arg);
177 bi->mode = XT_BPF_MODE_BYTECODE;
178 break;
179 case O_OBJ_PINNED:
180 bpf_parse_obj_pinned(bi, cb->arg);
181 bi->mode = XT_BPF_MODE_FD_PINNED;
182 break;
183 default:
184 xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
185 }
186 }
187
bpf_print_code(const struct sock_filter * pc,__u16 len,char tail)188 static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail)
189 {
190 for (; len; len--, pc++)
191 printf("%hu %hhu %hhu %u%c",
192 pc->code, pc->jt, pc->jf, pc->k,
193 len > 1 ? ',' : tail);
194 }
195
bpf_save_code(const struct sock_filter * pc,__u16 len)196 static void bpf_save_code(const struct sock_filter *pc, __u16 len)
197 {
198 printf(" --bytecode \"%hu,", len);
199 bpf_print_code(pc, len, '\"');
200 }
201
bpf_save(const void * ip,const struct xt_entry_match * match)202 static void bpf_save(const void *ip, const struct xt_entry_match *match)
203 {
204 const struct xt_bpf_info *info = (void *) match->data;
205
206 bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
207 }
208
bpf_save_v1(const void * ip,const struct xt_entry_match * match)209 static void bpf_save_v1(const void *ip, const struct xt_entry_match *match)
210 {
211 const struct xt_bpf_info_v1 *info = (void *) match->data;
212
213 if (info->mode == XT_BPF_MODE_BYTECODE)
214 bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
215 else if (info->mode == XT_BPF_MODE_FD_PINNED)
216 printf(" --object-pinned %s", info->path);
217 else
218 xtables_error(OTHER_PROBLEM, "unknown bpf mode");
219 }
220
bpf_fcheck(struct xt_fcheck_call * cb)221 static void bpf_fcheck(struct xt_fcheck_call *cb)
222 {
223 if (!(cb->xflags & (1 << O_BCODE_STDIN)))
224 xtables_error(PARAMETER_PROBLEM,
225 "bpf: missing --bytecode parameter");
226 }
227
bpf_fcheck_v1(struct xt_fcheck_call * cb)228 static void bpf_fcheck_v1(struct xt_fcheck_call *cb)
229 {
230 const unsigned int bit_bcode = 1 << O_BCODE_STDIN;
231 const unsigned int bit_pinned = 1 << O_OBJ_PINNED;
232 unsigned int flags;
233
234 flags = cb->xflags & (bit_bcode | bit_pinned);
235 if (flags != bit_bcode && flags != bit_pinned)
236 xtables_error(PARAMETER_PROBLEM,
237 "bpf: one of --bytecode or --pinned is required");
238 }
239
bpf_print(const void * ip,const struct xt_entry_match * match,int numeric)240 static void bpf_print(const void *ip, const struct xt_entry_match *match,
241 int numeric)
242 {
243 const struct xt_bpf_info *info = (void *) match->data;
244
245 printf("match bpf ");
246 bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
247 }
248
bpf_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)249 static void bpf_print_v1(const void *ip, const struct xt_entry_match *match,
250 int numeric)
251 {
252 const struct xt_bpf_info_v1 *info = (void *) match->data;
253
254 printf("match bpf ");
255 if (info->mode == XT_BPF_MODE_BYTECODE)
256 bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
257 else if (info->mode == XT_BPF_MODE_FD_PINNED)
258 printf("pinned %s", info->path);
259 else
260 printf("unknown");
261 }
262
263 static struct xtables_match bpf_matches[] = {
264 {
265 .family = NFPROTO_UNSPEC,
266 .name = "bpf",
267 .version = XTABLES_VERSION,
268 .revision = 0,
269 .size = XT_ALIGN(sizeof(struct xt_bpf_info)),
270 .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
271 .help = bpf_help,
272 .print = bpf_print,
273 .save = bpf_save,
274 .x6_parse = bpf_parse,
275 .x6_fcheck = bpf_fcheck,
276 .x6_options = bpf_opts,
277 },
278 {
279 .family = NFPROTO_UNSPEC,
280 .name = "bpf",
281 .version = XTABLES_VERSION,
282 .revision = 1,
283 .size = XT_ALIGN(sizeof(struct xt_bpf_info_v1)),
284 .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)),
285 .help = bpf_help_v1,
286 .print = bpf_print_v1,
287 .save = bpf_save_v1,
288 .x6_parse = bpf_parse_v1,
289 .x6_fcheck = bpf_fcheck_v1,
290 .x6_options = bpf_opts_v1,
291 },
292 };
293
_init(void)294 void _init(void)
295 {
296 xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
297 }
298