Home | History | Annotate | Download | only in extensions
      1 /*
      2  * Xtables BPF extension
      3  *
      4  * Written by Willem de Bruijn (willemb (at) 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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    281 void _init(void)
    282 {
    283 	xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
    284 }
    285