xref: /aosp_15_r20/external/iptables/extensions/libxt_bpf.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * Xtables BPF extension
3  *
4  * Written by Willem de Bruijn ([email protected])
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, "No bpf header, kernel headers too old?");
87 	return -EINVAL;
88 #endif
89 }
90 
bpf_parse_string(struct sock_filter * pc,__u16 * lenp,__u16 len_max,const char * bpf_program)91 static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max,
92 			     const char *bpf_program)
93 {
94 	const char separator = ',';
95 	const char *token;
96 	char sp;
97 	int i;
98 	__u16 len;
99 
100 	/* parse head: length. */
101 	if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 ||
102 		   sp != separator)
103 		xtables_error(PARAMETER_PROBLEM,
104 			      "bpf: error parsing program length");
105 	if (!len)
106 		xtables_error(PARAMETER_PROBLEM,
107 			      "bpf: illegal zero length program");
108 	if (len > len_max)
109 		xtables_error(PARAMETER_PROBLEM,
110 			      "bpf: number of instructions exceeds maximum");
111 
112 	/* parse instructions. */
113 	i = 0;
114 	token = bpf_program;
115 	while ((token = strchr(token, separator)) && (++token)[0]) {
116 		if (i >= len)
117 			xtables_error(PARAMETER_PROBLEM,
118 				      "bpf: real program length exceeds"
119 				      " the encoded length parameter");
120 		if (sscanf(token, "%hu %hhu %hhu %u,",
121 			   &pc->code, &pc->jt, &pc->jf, &pc->k) != 4)
122 			xtables_error(PARAMETER_PROBLEM,
123 				      "bpf: error at instr %d", i);
124 		i++;
125 		pc++;
126 	}
127 
128 	if (i != len)
129 		xtables_error(PARAMETER_PROBLEM,
130 			      "bpf: parsed program length is less than the"
131 			      " encoded length parameter");
132 
133 	*lenp = len;
134 }
135 
bpf_parse_obj_pinned(struct xt_bpf_info_v1 * bi,const char * filepath)136 static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi,
137 				 const char *filepath)
138 {
139 	bi->fd = bpf_obj_get_readonly(filepath);
140 	if (bi->fd < 0)
141 		xtables_error(PARAMETER_PROBLEM,
142 			      "bpf: failed to get bpf object");
143 
144 	/* Cannot close bi->fd explicitly. Rely on exit */
145 	if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) {
146 		xtables_error(OTHER_PROBLEM,
147 			      "Could not set close on exec: %s\n",
148 			      strerror(errno));
149 	}
150 }
151 
bpf_parse(struct xt_option_call * cb)152 static void bpf_parse(struct xt_option_call *cb)
153 {
154 	struct xt_bpf_info *bi = (void *) cb->data;
155 
156 	xtables_option_parse(cb);
157 	switch (cb->entry->id) {
158 	case O_BCODE_STDIN:
159 		bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
160 				 ARRAY_SIZE(bi->bpf_program), cb->arg);
161 		break;
162 	default:
163 		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
164 	}
165 }
166 
bpf_parse_v1(struct xt_option_call * cb)167 static void bpf_parse_v1(struct xt_option_call *cb)
168 {
169 	struct xt_bpf_info_v1 *bi = (void *) cb->data;
170 
171 	xtables_option_parse(cb);
172 	switch (cb->entry->id) {
173 	case O_BCODE_STDIN:
174 		bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
175 				 ARRAY_SIZE(bi->bpf_program), cb->arg);
176 		bi->mode = XT_BPF_MODE_BYTECODE;
177 		break;
178 	case O_OBJ_PINNED:
179 		bpf_parse_obj_pinned(bi, cb->arg);
180 		bi->mode = XT_BPF_MODE_FD_PINNED;
181 		break;
182 	default:
183 		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
184 	}
185 }
186 
bpf_print_code(const struct sock_filter * pc,__u16 len,char tail)187 static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail)
188 {
189 	for (; len; len--, pc++)
190 		printf("%hu %hhu %hhu %u%c",
191 		       pc->code, pc->jt, pc->jf, pc->k,
192 		       len > 1 ? ',' : tail);
193 }
194 
bpf_save_code(const struct sock_filter * pc,__u16 len)195 static void bpf_save_code(const struct sock_filter *pc, __u16 len)
196 {
197 	printf(" --bytecode \"%hu,", len);
198 	bpf_print_code(pc, len, '\"');
199 }
200 
bpf_save(const void * ip,const struct xt_entry_match * match)201 static void bpf_save(const void *ip, const struct xt_entry_match *match)
202 {
203 	const struct xt_bpf_info *info = (void *) match->data;
204 
205 	bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
206 }
207 
bpf_save_v1(const void * ip,const struct xt_entry_match * match)208 static void bpf_save_v1(const void *ip, const struct xt_entry_match *match)
209 {
210 	const struct xt_bpf_info_v1 *info = (void *) match->data;
211 
212 	if (info->mode == XT_BPF_MODE_BYTECODE)
213 		bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
214 	else if (info->mode == XT_BPF_MODE_FD_PINNED)
215 		printf(" --object-pinned %s", info->path);
216 	else
217 		xtables_error(OTHER_PROBLEM, "unknown bpf mode");
218 }
219 
bpf_fcheck(struct xt_fcheck_call * cb)220 static void bpf_fcheck(struct xt_fcheck_call *cb)
221 {
222 	if (!(cb->xflags & (1 << O_BCODE_STDIN)))
223 		xtables_error(PARAMETER_PROBLEM,
224 			      "bpf: missing --bytecode parameter");
225 }
226 
bpf_fcheck_v1(struct xt_fcheck_call * cb)227 static void bpf_fcheck_v1(struct xt_fcheck_call *cb)
228 {
229 	const unsigned int bit_bcode = 1 << O_BCODE_STDIN;
230 	const unsigned int bit_pinned = 1 << O_OBJ_PINNED;
231 	unsigned int flags;
232 
233 	flags = cb->xflags & (bit_bcode | bit_pinned);
234 	if (flags != bit_bcode && flags != bit_pinned)
235 		xtables_error(PARAMETER_PROBLEM,
236 			      "bpf: one of --bytecode or --pinned is required");
237 }
238 
bpf_print(const void * ip,const struct xt_entry_match * match,int numeric)239 static void bpf_print(const void *ip, const struct xt_entry_match *match,
240 		      int numeric)
241 {
242 	const struct xt_bpf_info *info = (void *) match->data;
243 
244 	printf("match bpf ");
245 	bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
246 }
247 
bpf_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)248 static void bpf_print_v1(const void *ip, const struct xt_entry_match *match,
249 			 int numeric)
250 {
251 	const struct xt_bpf_info_v1 *info = (void *) match->data;
252 
253 	printf("match bpf ");
254 	if (info->mode == XT_BPF_MODE_BYTECODE)
255 		bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
256 	else if (info->mode == XT_BPF_MODE_FD_PINNED)
257 		printf("pinned %s", info->path);
258 	else
259 		printf("unknown");
260 }
261 
262 static struct xtables_match bpf_matches[] = {
263 	{
264 		.family		= NFPROTO_UNSPEC,
265 		.name		= "bpf",
266 		.version	= XTABLES_VERSION,
267 		.revision	= 0,
268 		.size		= XT_ALIGN(sizeof(struct xt_bpf_info)),
269 		.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
270 		.help		= bpf_help,
271 		.print		= bpf_print,
272 		.save		= bpf_save,
273 		.x6_parse	= bpf_parse,
274 		.x6_fcheck	= bpf_fcheck,
275 		.x6_options	= bpf_opts,
276 	},
277 	{
278 		.family		= NFPROTO_UNSPEC,
279 		.name		= "bpf",
280 		.version	= XTABLES_VERSION,
281 		.revision	= 1,
282 		.size		= XT_ALIGN(sizeof(struct xt_bpf_info_v1)),
283 		.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)),
284 		.help		= bpf_help_v1,
285 		.print		= bpf_print_v1,
286 		.save		= bpf_save_v1,
287 		.x6_parse	= bpf_parse_v1,
288 		.x6_fcheck	= bpf_fcheck_v1,
289 		.x6_options	= bpf_opts_v1,
290 	},
291 };
292 
_init(void)293 void _init(void)
294 {
295 	xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
296 }
297