• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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