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
20#define BCODE_FILE_MAX_LEN_B	1024
21
22enum {
23	O_BCODE_STDIN = 0,
24};
25
26static void bpf_help(void)
27{
28	printf(
29"bpf match options:\n"
30"--bytecode <program>	: a bpf program as generated by\n"
31"  `nfbpf_compiler RAW <filter>`\n");
32}
33
34static const struct xt_option_entry bpf_opts[] = {
35	{.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
36	XTOPT_TABLEEND,
37};
38
39static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program,
40			     const char separator)
41{
42	struct xt_bpf_info *bi = (void *) cb->data;
43	const char *token;
44	char sp;
45	int i;
46
47	/* parse head: length. */
48	if (sscanf(bpf_program, "%hu%c", &bi->bpf_program_num_elem, &sp) != 2 ||
49		   sp != separator)
50		xtables_error(PARAMETER_PROBLEM,
51			      "bpf: error parsing program length");
52	if (!bi->bpf_program_num_elem)
53		xtables_error(PARAMETER_PROBLEM,
54			      "bpf: illegal zero length program");
55	if (bi->bpf_program_num_elem > XT_BPF_MAX_NUM_INSTR)
56		xtables_error(PARAMETER_PROBLEM,
57			      "bpf: number of instructions exceeds maximum");
58
59	/* parse instructions. */
60	i = 0;
61	token = bpf_program;
62	while ((token = strchr(token, separator)) && (++token)[0]) {
63		if (i >= bi->bpf_program_num_elem)
64			xtables_error(PARAMETER_PROBLEM,
65				      "bpf: real program length exceeds"
66				      " the encoded length parameter");
67		if (sscanf(token, "%hu %hhu %hhu %u,",
68			   &bi->bpf_program[i].code,
69			   &bi->bpf_program[i].jt,
70			   &bi->bpf_program[i].jf,
71			   &bi->bpf_program[i].k) != 4)
72			xtables_error(PARAMETER_PROBLEM,
73				      "bpf: error at instr %d", i);
74		i++;
75	}
76
77	if (i != bi->bpf_program_num_elem)
78		xtables_error(PARAMETER_PROBLEM,
79			      "bpf: parsed program length is less than the"
80			      " encoded length parameter");
81}
82
83static void bpf_parse(struct xt_option_call *cb)
84{
85	xtables_option_parse(cb);
86	switch (cb->entry->id) {
87	case O_BCODE_STDIN:
88		bpf_parse_string(cb, cb->arg, ',');
89		break;
90	default:
91		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
92	}
93}
94
95static void bpf_print_code(const void *ip, const struct xt_entry_match *match)
96{
97	const struct xt_bpf_info *info = (void *) match->data;
98	int i;
99
100	for (i = 0; i < info->bpf_program_num_elem-1; i++)
101		printf("%hu %hhu %hhu %u,", info->bpf_program[i].code,
102					    info->bpf_program[i].jt,
103					    info->bpf_program[i].jf,
104					    info->bpf_program[i].k);
105
106	printf("%hu %hhu %hhu %u", info->bpf_program[i].code,
107				    info->bpf_program[i].jt,
108				    info->bpf_program[i].jf,
109				    info->bpf_program[i].k);
110}
111
112static void bpf_save(const void *ip, const struct xt_entry_match *match)
113{
114	const struct xt_bpf_info *info = (void *) match->data;
115
116	printf(" --bytecode \"%hu,", info->bpf_program_num_elem);
117	bpf_print_code(ip, match);
118	printf("\"");
119}
120
121static void bpf_fcheck(struct xt_fcheck_call *cb)
122{
123	if (!(cb->xflags & (1 << O_BCODE_STDIN)))
124		xtables_error(PARAMETER_PROBLEM,
125			      "bpf: missing --bytecode parameter");
126}
127
128static void bpf_print(const void *ip, const struct xt_entry_match *match,
129		      int numeric)
130{
131	printf("match bpf ");
132	return bpf_print_code(ip, match);
133}
134
135static struct xtables_match bpf_match = {
136	.family		= NFPROTO_UNSPEC,
137	.name		= "bpf",
138	.version	= XTABLES_VERSION,
139	.size		= XT_ALIGN(sizeof(struct xt_bpf_info)),
140	.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
141	.help		= bpf_help,
142	.print		= bpf_print,
143	.save		= bpf_save,
144	.x6_parse	= bpf_parse,
145	.x6_fcheck	= bpf_fcheck,
146	.x6_options	= bpf_opts,
147};
148
149void _init(void)
150{
151	xtables_register_match(&bpf_match);
152}
153