Blame extensions/libxt_bpf.c

Packit 7b22a4
/*
Packit 7b22a4
 * Xtables BPF extension
Packit 7b22a4
 *
Packit 7b22a4
 * Written by Willem de Bruijn (willemb@google.com)
Packit 7b22a4
 * Copyright Google, Inc. 2013
Packit 7b22a4
 * Licensed under the GNU General Public License version 2 (GPLv2)
Packit 7b22a4
*/
Packit 7b22a4
Packit 7b22a4
#include <linux/netfilter/xt_bpf.h>
Packit 7b22a4
#include <errno.h>
Packit 7b22a4
#include <fcntl.h>
Packit 7b22a4
#include <stdio.h>
Packit 7b22a4
#include <stdlib.h>
Packit 7b22a4
#include <string.h>
Packit 7b22a4
#include <sys/stat.h>
Packit 7b22a4
#include <sys/types.h>
Packit 7b22a4
#include <unistd.h>
Packit 7b22a4
#include <xtables.h>
Packit 7b22a4
#include "config.h"
Packit 7b22a4
Packit 7b22a4
#ifdef HAVE_LINUX_BPF_H
Packit 7b22a4
#include <linux/bpf.h>
Packit 7b22a4
#endif
Packit 7b22a4
Packit 7b22a4
#include <linux/magic.h>
Packit 7b22a4
#include <linux/unistd.h>
Packit 7b22a4
Packit 7b22a4
#define BCODE_FILE_MAX_LEN_B	1024
Packit 7b22a4
Packit 7b22a4
enum {
Packit 7b22a4
	O_BCODE_STDIN = 0,
Packit 7b22a4
	O_OBJ_PINNED = 1,
Packit 7b22a4
};
Packit 7b22a4
Packit 7b22a4
static void bpf_help(void)
Packit 7b22a4
{
Packit 7b22a4
	printf(
Packit 7b22a4
"bpf match options:\n"
Packit 7b22a4
"--bytecode <program>	: a bpf program as generated by\n"
Packit 7b22a4
"                         $(nfbpf_compile RAW '<filter>')\n");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_help_v1(void)
Packit 7b22a4
{
Packit 7b22a4
	printf(
Packit 7b22a4
"bpf match options:\n"
Packit 7b22a4
"--bytecode <program>	        : a bpf program as generated by\n"
Packit 7b22a4
"                                 $(nfbpf_compile RAW '<filter>')\n"
Packit 7b22a4
"--object-pinned <bpf object>	: a path to a pinned BPF object in bpf fs\n");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static const struct xt_option_entry bpf_opts[] = {
Packit 7b22a4
	{.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
Packit 7b22a4
	XTOPT_TABLEEND,
Packit 7b22a4
};
Packit 7b22a4
Packit 7b22a4
static const struct xt_option_entry bpf_opts_v1[] = {
Packit 7b22a4
	{.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
Packit 7b22a4
	{.name = "object-pinned" , .id = O_OBJ_PINNED, .type = XTTYPE_STRING,
Packit 7b22a4
	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_bpf_info_v1, path)},
Packit 7b22a4
	XTOPT_TABLEEND,
Packit 7b22a4
};
Packit 7b22a4
Packit 7b22a4
static int bpf_obj_get(const char *filepath)
Packit 7b22a4
{
Packit 7b22a4
#if defined HAVE_LINUX_BPF_H && defined __NR_bpf && defined BPF_FS_MAGIC
Packit 7b22a4
	union bpf_attr attr;
Packit 7b22a4
Packit 7b22a4
	memset(&attr, 0, sizeof(attr));
Packit 7b22a4
	attr.pathname = (__u64) filepath;
Packit 7b22a4
Packit 7b22a4
	return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
Packit 7b22a4
#else
Packit 7b22a4
	xtables_error(OTHER_PROBLEM,
Packit 7b22a4
		      "No bpf header, kernel headers too old?\n");
Packit 7b22a4
	return -EINVAL;
Packit 7b22a4
#endif
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max,
Packit 7b22a4
			     const char *bpf_program)
Packit 7b22a4
{
Packit 7b22a4
	const char separator = ',';
Packit 7b22a4
	const char *token;
Packit 7b22a4
	char sp;
Packit 7b22a4
	int i;
Packit 7b22a4
	__u16 len;
Packit 7b22a4
Packit 7b22a4
	/* parse head: length. */
Packit 7b22a4
	if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 ||
Packit 7b22a4
		   sp != separator)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: error parsing program length");
Packit 7b22a4
	if (!len)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: illegal zero length program");
Packit 7b22a4
	if (len > len_max)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: number of instructions exceeds maximum");
Packit 7b22a4
Packit 7b22a4
	/* parse instructions. */
Packit 7b22a4
	i = 0;
Packit 7b22a4
	token = bpf_program;
Packit 7b22a4
	while ((token = strchr(token, separator)) && (++token)[0]) {
Packit 7b22a4
		if (i >= len)
Packit 7b22a4
			xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
				      "bpf: real program length exceeds"
Packit 7b22a4
				      " the encoded length parameter");
Packit 7b22a4
		if (sscanf(token, "%hu %hhu %hhu %u,",
Packit 7b22a4
			   &pc->code, &pc->jt, &pc->jf, &pc->k) != 4)
Packit 7b22a4
			xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
				      "bpf: error at instr %d", i);
Packit 7b22a4
		i++;
Packit 7b22a4
		pc++;
Packit 7b22a4
	}
Packit 7b22a4
Packit 7b22a4
	if (i != len)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: parsed program length is less than the"
Packit 7b22a4
			      " encoded length parameter");
Packit 7b22a4
Packit 7b22a4
	*lenp = len;
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi,
Packit 7b22a4
				 const char *filepath)
Packit 7b22a4
{
Packit 7b22a4
	bi->fd = bpf_obj_get(filepath);
Packit 7b22a4
	if (bi->fd < 0)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: failed to get bpf object");
Packit 7b22a4
Packit 7b22a4
	/* Cannot close bi->fd explicitly. Rely on exit */
Packit 7b22a4
	if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) {
Packit 7b22a4
		xtables_error(OTHER_PROBLEM,
Packit 7b22a4
			      "Could not set close on exec: %s\n",
Packit 7b22a4
			      strerror(errno));
Packit 7b22a4
	}
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_parse(struct xt_option_call *cb)
Packit 7b22a4
{
Packit 7b22a4
	struct xt_bpf_info *bi = (void *) cb->data;
Packit 7b22a4
Packit 7b22a4
	xtables_option_parse(cb);
Packit 7b22a4
	switch (cb->entry->id) {
Packit 7b22a4
	case O_BCODE_STDIN:
Packit 7b22a4
		bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
Packit 7b22a4
				 ARRAY_SIZE(bi->bpf_program), cb->arg);
Packit 7b22a4
		break;
Packit 7b22a4
	default:
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
Packit 7b22a4
	}
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_parse_v1(struct xt_option_call *cb)
Packit 7b22a4
{
Packit 7b22a4
	struct xt_bpf_info_v1 *bi = (void *) cb->data;
Packit 7b22a4
Packit 7b22a4
	xtables_option_parse(cb);
Packit 7b22a4
	switch (cb->entry->id) {
Packit 7b22a4
	case O_BCODE_STDIN:
Packit 7b22a4
		bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem,
Packit 7b22a4
				 ARRAY_SIZE(bi->bpf_program), cb->arg);
Packit 7b22a4
		bi->mode = XT_BPF_MODE_BYTECODE;
Packit 7b22a4
		break;
Packit 7b22a4
	case O_OBJ_PINNED:
Packit 7b22a4
		bpf_parse_obj_pinned(bi, cb->arg);
Packit 7b22a4
		bi->mode = XT_BPF_MODE_FD_PINNED;
Packit 7b22a4
		break;
Packit 7b22a4
	default:
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
Packit 7b22a4
	}
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail)
Packit 7b22a4
{
Packit 7b22a4
	for (; len; len--, pc++)
Packit 7b22a4
		printf("%hu %hhu %hhu %u%c",
Packit 7b22a4
		       pc->code, pc->jt, pc->jf, pc->k,
Packit 7b22a4
		       len > 1 ? ',' : tail);
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_save_code(const struct sock_filter *pc, __u16 len)
Packit 7b22a4
{
Packit 7b22a4
	printf(" --bytecode \"%hu,", len);
Packit 7b22a4
	bpf_print_code(pc, len, '\"');
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_save(const void *ip, const struct xt_entry_match *match)
Packit 7b22a4
{
Packit 7b22a4
	const struct xt_bpf_info *info = (void *) match->data;
Packit 7b22a4
Packit 7b22a4
	bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_save_v1(const void *ip, const struct xt_entry_match *match)
Packit 7b22a4
{
Packit 7b22a4
	const struct xt_bpf_info_v1 *info = (void *) match->data;
Packit 7b22a4
Packit 7b22a4
	if (info->mode == XT_BPF_MODE_BYTECODE)
Packit 7b22a4
		bpf_save_code(info->bpf_program, info->bpf_program_num_elem);
Packit 7b22a4
	else if (info->mode == XT_BPF_MODE_FD_PINNED)
Packit 7b22a4
		printf(" --object-pinned %s", info->path);
Packit 7b22a4
	else
Packit 7b22a4
		xtables_error(OTHER_PROBLEM, "unknown bpf mode");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_fcheck(struct xt_fcheck_call *cb)
Packit 7b22a4
{
Packit 7b22a4
	if (!(cb->xflags & (1 << O_BCODE_STDIN)))
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: missing --bytecode parameter");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_fcheck_v1(struct xt_fcheck_call *cb)
Packit 7b22a4
{
Packit 7b22a4
	const unsigned int bit_bcode = 1 << O_BCODE_STDIN;
Packit 7b22a4
	const unsigned int bit_pinned = 1 << O_OBJ_PINNED;
Packit 7b22a4
	unsigned int flags;
Packit 7b22a4
Packit 7b22a4
	flags = cb->xflags & (bit_bcode | bit_pinned);
Packit 7b22a4
	if (flags != bit_bcode && flags != bit_pinned)
Packit 7b22a4
		xtables_error(PARAMETER_PROBLEM,
Packit 7b22a4
			      "bpf: one of --bytecode or --pinned is required");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_print(const void *ip, const struct xt_entry_match *match,
Packit 7b22a4
		      int numeric)
Packit 7b22a4
{
Packit 7b22a4
	const struct xt_bpf_info *info = (void *) match->data;
Packit 7b22a4
Packit 7b22a4
	printf("match bpf ");
Packit 7b22a4
	bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static void bpf_print_v1(const void *ip, const struct xt_entry_match *match,
Packit 7b22a4
			 int numeric)
Packit 7b22a4
{
Packit 7b22a4
	const struct xt_bpf_info_v1 *info = (void *) match->data;
Packit 7b22a4
Packit 7b22a4
	printf("match bpf ");
Packit 7b22a4
	if (info->mode == XT_BPF_MODE_BYTECODE)
Packit 7b22a4
		bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0');
Packit 7b22a4
	else if (info->mode == XT_BPF_MODE_FD_PINNED)
Packit 7b22a4
		printf("pinned %s", info->path);
Packit 7b22a4
	else
Packit 7b22a4
		printf("unknown");
Packit 7b22a4
}
Packit 7b22a4
Packit 7b22a4
static struct xtables_match bpf_matches[] = {
Packit 7b22a4
	{
Packit 7b22a4
		.family		= NFPROTO_UNSPEC,
Packit 7b22a4
		.name		= "bpf",
Packit 7b22a4
		.version	= XTABLES_VERSION,
Packit 7b22a4
		.revision	= 0,
Packit 7b22a4
		.size		= XT_ALIGN(sizeof(struct xt_bpf_info)),
Packit 7b22a4
		.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
Packit 7b22a4
		.help		= bpf_help,
Packit 7b22a4
		.print		= bpf_print,
Packit 7b22a4
		.save		= bpf_save,
Packit 7b22a4
		.x6_parse	= bpf_parse,
Packit 7b22a4
		.x6_fcheck	= bpf_fcheck,
Packit 7b22a4
		.x6_options	= bpf_opts,
Packit 7b22a4
	},
Packit 7b22a4
	{
Packit 7b22a4
		.family		= NFPROTO_UNSPEC,
Packit 7b22a4
		.name		= "bpf",
Packit 7b22a4
		.version	= XTABLES_VERSION,
Packit 7b22a4
		.revision	= 1,
Packit 7b22a4
		.size		= XT_ALIGN(sizeof(struct xt_bpf_info_v1)),
Packit 7b22a4
		.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)),
Packit 7b22a4
		.help		= bpf_help_v1,
Packit 7b22a4
		.print		= bpf_print_v1,
Packit 7b22a4
		.save		= bpf_save_v1,
Packit 7b22a4
		.x6_parse	= bpf_parse_v1,
Packit 7b22a4
		.x6_fcheck	= bpf_fcheck_v1,
Packit 7b22a4
		.x6_options	= bpf_opts_v1,
Packit 7b22a4
	},
Packit 7b22a4
};
Packit 7b22a4
Packit 7b22a4
void _init(void)
Packit 7b22a4
{
Packit 7b22a4
	xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches));
Packit 7b22a4
}