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