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