• 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/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