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