Full rewrite of npfctl(8) parser and rework of n-code generation part.
Fixes most of the known bugs and issues with the utility. Note: rule procedures are not yet (as we want to make them fully modular). Huge thanks to Martin Husemann who wrote the parser and Christos Zoulas who wrote intermediate structures and helped to complete the work.
This commit is contained in:
parent
1bfc197123
commit
d3c56566f0
@ -1,14 +1,18 @@
|
||||
# $NetBSD: Makefile,v 1.4 2011/02/04 00:19:51 rmind Exp $
|
||||
# $NetBSD: Makefile,v 1.5 2012/01/08 21:34:21 rmind Exp $
|
||||
|
||||
PROG= npfctl
|
||||
MAN= npfctl.8 npf.conf.5
|
||||
|
||||
SRCS= npfctl.c npf_parser.c npf_data.c npf_ncgen.c
|
||||
SRCS= npfctl.c npf_var.c npf_data.c npf_ncgen.c npf_build.c
|
||||
|
||||
LDADD+= -lnpf -lprop
|
||||
CPPFLAGS+= -I${.CURDIR}
|
||||
SRCS+= npf_scan.l npf_parse.y
|
||||
YHEADER= 1
|
||||
|
||||
LDADD+= -lnpf -lprop -ly
|
||||
DPADD+= ${LIBNPF} ${LIBPROP}
|
||||
|
||||
WARNS?= 4
|
||||
NOLINT= # defined (note: deliberately)
|
||||
NOLINT= # disabled (note: deliberately)
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
495
usr.sbin/npf/npfctl/npf_build.c
Normal file
495
usr.sbin/npf/npfctl/npf_build.c
Normal file
@ -0,0 +1,495 @@
|
||||
/* $NetBSD: npf_build.c,v 1.1 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
* NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* npfctl(8) building of the configuration.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_build.c,v 1.1 2012/01/08 21:34:21 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
static nl_config_t * npf_conf = NULL;
|
||||
static nl_rule_t * current_group = NULL;
|
||||
static bool npf_debug = false;
|
||||
static bool defgroup_set = false;
|
||||
|
||||
void
|
||||
npfctl_config_init(bool debug)
|
||||
{
|
||||
|
||||
npf_conf = npf_config_create();
|
||||
if (npf_conf == NULL) {
|
||||
errx(EXIT_FAILURE, "npf_config_create failed");
|
||||
}
|
||||
npf_debug = debug;
|
||||
}
|
||||
|
||||
int
|
||||
npfctl_config_send(int fd)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (!fd) {
|
||||
_npf_config_setsubmit(npf_conf, "./npf.plist");
|
||||
}
|
||||
if (!defgroup_set) {
|
||||
errx(EXIT_FAILURE, "default group was not defined");
|
||||
}
|
||||
error = npf_config_submit(npf_conf, fd);
|
||||
npf_config_destroy(npf_conf);
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
npfctl_config_flush(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Pass empty configuration to flush. */
|
||||
npfctl_config_init(false);
|
||||
defgroup_set = true;
|
||||
ret = npfctl_config_send(fd);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
return npf_sessions_send(fd, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
npfctl_table_exists_p(const char *id)
|
||||
{
|
||||
return npf_table_exists_p(npf_conf, atoi(id));
|
||||
}
|
||||
|
||||
static in_port_t *
|
||||
npfctl_get_singleport(const npfvar_t *vp)
|
||||
{
|
||||
port_range_t *pr;
|
||||
|
||||
if (npfvar_get_count(vp) > 1) {
|
||||
yyerror("multiple ports are not valid");
|
||||
}
|
||||
pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0);
|
||||
if (pr->pr_start != pr->pr_end) {
|
||||
yyerror("port range is not valid");
|
||||
}
|
||||
return &pr->pr_start;
|
||||
}
|
||||
|
||||
static fam_addr_mask_t *
|
||||
npfctl_get_singlefam(const npfvar_t *vp)
|
||||
{
|
||||
if (npfvar_get_count(vp) > 1) {
|
||||
yyerror("multiple addresses are not valid");
|
||||
}
|
||||
return npfvar_get_data(vp, NPFVAR_FAM, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
npfctl_build_fam(nc_ctx_t *nc, sa_family_t family,
|
||||
fam_addr_mask_t *fam, int opts)
|
||||
{
|
||||
/*
|
||||
* If family is specified, address does not match it and the
|
||||
* address is extracted from the interface, then simply ignore.
|
||||
* Otherwise, address of invalid family was passed manually.
|
||||
*/
|
||||
if (family != AF_UNSPEC && family != fam->fam_family) {
|
||||
if (!fam->fam_interface) {
|
||||
yyerror("specified address is not of the required "
|
||||
"family %d", family);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimise 0.0.0.0/0 case to be NOP. Otherwise, address with
|
||||
* zero mask would never match and therefore is not valid.
|
||||
*/
|
||||
if (fam->fam_mask == 0) {
|
||||
npf_addr_t zero;
|
||||
memset(&zero, 0, sizeof(npf_addr_t));
|
||||
if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
|
||||
yyerror("filter criterion would never match");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fam->fam_family) {
|
||||
case AF_INET:
|
||||
npfctl_gennc_v4cidr(nc, opts,
|
||||
&fam->fam_addr, fam->fam_mask);
|
||||
break;
|
||||
case AF_INET6:
|
||||
npfctl_gennc_v6cidr(nc, opts,
|
||||
&fam->fam_addr, fam->fam_mask);
|
||||
break;
|
||||
default:
|
||||
yyerror("family %d is not supported", fam->fam_family);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
npfctl_build_vars(nc_ctx_t *nc, sa_family_t family, npfvar_t *vars, int opts)
|
||||
{
|
||||
const int type = npfvar_get_type(vars);
|
||||
size_t i;
|
||||
|
||||
npfctl_ncgen_group(nc);
|
||||
for (i = 0; i < npfvar_get_count(vars); i++) {
|
||||
void *data = npfvar_get_data(vars, type, i);
|
||||
assert(data != NULL);
|
||||
|
||||
switch (type) {
|
||||
case NPFVAR_FAM: {
|
||||
fam_addr_mask_t *fam = data;
|
||||
npfctl_build_fam(nc, family, fam, opts);
|
||||
break;
|
||||
}
|
||||
case NPFVAR_PORT_RANGE: {
|
||||
port_range_t *pr = data;
|
||||
if (opts & NC_MATCH_TCP) {
|
||||
npfctl_gennc_ports(nc, opts & ~NC_MATCH_UDP,
|
||||
pr->pr_start, pr->pr_end);
|
||||
}
|
||||
if (opts & NC_MATCH_UDP) {
|
||||
npfctl_gennc_ports(nc, opts & ~NC_MATCH_TCP,
|
||||
pr->pr_start, pr->pr_end);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NPFVAR_TABLE: {
|
||||
u_int tid = atoi(data);
|
||||
npfctl_gennc_tbl(nc, opts, tid);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
npfctl_ncgen_endgroup(nc);
|
||||
}
|
||||
|
||||
static int
|
||||
npfctl_build_proto(nc_ctx_t *nc, const opt_proto_t *op)
|
||||
{
|
||||
const npfvar_t *popts = op->op_opts;
|
||||
int pflag = 0;
|
||||
|
||||
switch (op->op_proto) {
|
||||
case IPPROTO_TCP:
|
||||
pflag = NC_MATCH_TCP;
|
||||
if (!popts) {
|
||||
break;
|
||||
}
|
||||
assert(npfvar_get_count(popts) == 2);
|
||||
|
||||
/* Build TCP flags block (optional). */
|
||||
uint8_t *tf, *tf_mask;
|
||||
|
||||
tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
|
||||
tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
|
||||
npfctl_gennc_tcpfl(nc, *tf, *tf_mask);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
pflag = NC_MATCH_UDP;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
/*
|
||||
* Build ICMP block.
|
||||
*/
|
||||
assert(npfvar_get_count(popts) == 2);
|
||||
|
||||
int *icmp_type, *icmp_code;
|
||||
icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
|
||||
icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
|
||||
npfctl_gennc_icmp(nc, *icmp_type, *icmp_code);
|
||||
break;
|
||||
case -1:
|
||||
pflag = NC_MATCH_TCP | NC_MATCH_UDP;
|
||||
break;
|
||||
default:
|
||||
yyerror("protocol %d is not supported", op->op_proto);
|
||||
}
|
||||
return pflag;
|
||||
}
|
||||
|
||||
static bool
|
||||
npfctl_build_ncode(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
|
||||
const filt_opts_t *fopts, bool invert)
|
||||
{
|
||||
nc_ctx_t *nc;
|
||||
void *code;
|
||||
size_t len;
|
||||
|
||||
if (family == AF_UNSPEC && op->op_proto == -1 &&
|
||||
op->op_opts == NULL && !fopts->fo_from && !fopts->fo_to &&
|
||||
!fopts->fo_from_port_range && !fopts->fo_to_port_range)
|
||||
return false;
|
||||
|
||||
int srcflag = NC_MATCH_SRC;
|
||||
int dstflag = NC_MATCH_DST;
|
||||
|
||||
if (invert) {
|
||||
srcflag = NC_MATCH_DST;
|
||||
dstflag = NC_MATCH_SRC;
|
||||
}
|
||||
|
||||
nc = npfctl_ncgen_create();
|
||||
|
||||
/* Build IP address blocks. */
|
||||
npfctl_build_vars(nc, family, fopts->fo_from, srcflag);
|
||||
npfctl_build_vars(nc, family, fopts->fo_to, dstflag);
|
||||
|
||||
/* Build layer 4 protocol blocks. */
|
||||
int pflag = npfctl_build_proto(nc, op);
|
||||
|
||||
/* Build port-range blocks. */
|
||||
if (fopts->fo_from_port_range) {
|
||||
npfctl_build_vars(nc, family, fopts->fo_from_port_range,
|
||||
srcflag | pflag);
|
||||
}
|
||||
if (fopts->fo_to_port_range) {
|
||||
npfctl_build_vars(nc, family, fopts->fo_to_port_range,
|
||||
dstflag | pflag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Complete n-code (destroys the context) and pass to the rule.
|
||||
*/
|
||||
code = npfctl_ncgen_complete(nc, &len);
|
||||
if (npf_debug) {
|
||||
extern int yylineno;
|
||||
printf("RULE AT LINE %d\n", yylineno - 1);
|
||||
npfctl_ncgen_print(code, len);
|
||||
}
|
||||
if (npf_rule_setcode(rl, NPF_CODE_NCODE, code, len) == -1) {
|
||||
errx(EXIT_FAILURE, "npf_rule_setcode failed");
|
||||
}
|
||||
free(code);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_rproc: create and insert a rule procedure.
|
||||
*/
|
||||
void
|
||||
npfctl_build_rproc(const char *name, npfvar_t *var)
|
||||
{
|
||||
nl_rproc_t *rp;
|
||||
|
||||
rp = npf_rproc_create(name);
|
||||
if (rp == NULL) {
|
||||
errx(EXIT_FAILURE, "npf_rproc_create failed");
|
||||
}
|
||||
npf_rproc_insert(npf_conf, rp);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_group: create a group, insert into the global ruleset
|
||||
* and update the current group pointer.
|
||||
*/
|
||||
void
|
||||
npfctl_build_group(const char *name, int attr, u_int if_idx)
|
||||
{
|
||||
const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
|
||||
nl_rule_t *rl;
|
||||
|
||||
if (attr & NPF_RULE_DEFAULT) {
|
||||
if (defgroup_set) {
|
||||
yyerror("multiple default groups are not valid");
|
||||
}
|
||||
defgroup_set = true;
|
||||
attr |= attr_di;
|
||||
|
||||
} else if ((attr & attr_di) == 0) {
|
||||
attr |= attr_di;
|
||||
}
|
||||
attr |= (NPF_RULE_PASS | NPF_RULE_FINAL);
|
||||
|
||||
rl = npf_rule_create(name, attr, if_idx);
|
||||
npf_rule_insert(npf_conf, NULL, rl, NPF_PRI_NEXT);
|
||||
current_group = rl;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_rule: create a rule, build n-code from filter options,
|
||||
* if any, and insert into the ruleset of current group.
|
||||
*/
|
||||
void
|
||||
npfctl_build_rule(int attr, u_int if_idx, sa_family_t family,
|
||||
const opt_proto_t *op, const filt_opts_t *fopts, const char *rproc)
|
||||
{
|
||||
nl_rule_t *rl;
|
||||
|
||||
rl = npf_rule_create(NULL, attr, if_idx);
|
||||
npfctl_build_ncode(rl, family, op, fopts, false);
|
||||
if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
|
||||
yyerror("rule procedure '%s' is not defined", rproc);
|
||||
}
|
||||
assert(current_group != NULL);
|
||||
npf_rule_insert(npf_conf, current_group, rl, NPF_PRI_NEXT);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_nat: create a NAT policy of a specified type with a
|
||||
* given filter options.
|
||||
*/
|
||||
void
|
||||
npfctl_build_nat(int type, u_int if_idx, const filt_opts_t *fopts,
|
||||
npfvar_t *var1, npfvar_t *var2)
|
||||
{
|
||||
opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
|
||||
nl_nat_t *nat;
|
||||
fam_addr_mask_t *ai;
|
||||
|
||||
assert(type != 0 && if_idx != 0);
|
||||
assert(fopts != NULL && var1 != NULL);
|
||||
|
||||
ai = npfctl_get_singlefam(var1);
|
||||
assert(ai != NULL);
|
||||
if (ai->fam_family != AF_INET) {
|
||||
yyerror("IPv6 NAT is not supported");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NPFCTL_RDR: {
|
||||
/*
|
||||
* Redirection: an inbound NAT with a specific port.
|
||||
*/
|
||||
in_port_t *port = npfctl_get_singleport(var2);
|
||||
nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
|
||||
if_idx, &ai->fam_addr, ai->fam_family, *port);
|
||||
break;
|
||||
}
|
||||
case NPFCTL_BINAT: {
|
||||
/*
|
||||
* Bi-directional NAT: a combination of inbound NAT and
|
||||
* outbound NAT policies. Note that the translation address
|
||||
* is local IP and filter criteria is inverted accordingly.
|
||||
*/
|
||||
fam_addr_mask_t *tai = npfctl_get_singlefam(var2);
|
||||
assert(tai != NULL);
|
||||
if (ai->fam_family != AF_INET) {
|
||||
yyerror("IPv6 NAT is not supported");
|
||||
}
|
||||
nat = npf_nat_create(NPF_NATIN, 0, if_idx,
|
||||
&tai->fam_addr, tai->fam_family, 0);
|
||||
npfctl_build_ncode(nat, AF_INET, &op, fopts, true);
|
||||
npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
|
||||
/* FALLTHROUGH */
|
||||
}
|
||||
case NPFCTL_NAT: {
|
||||
/*
|
||||
* Traditional NAPT: an outbound NAT policy with port.
|
||||
* If this is another hald for bi-directional NAT, then
|
||||
* no port translation with mapping.
|
||||
*/
|
||||
nat = npf_nat_create(NPF_NATOUT, type == NPFCTL_NAT ?
|
||||
(NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
|
||||
if_idx, &ai->fam_addr, ai->fam_family, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
npfctl_build_ncode(nat, AF_INET, &op, fopts, false);
|
||||
npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_fill_table: fill NPF table with entries from a specified file.
|
||||
*/
|
||||
static void
|
||||
npfctl_fill_table(nl_table_t *tl, const char *fname)
|
||||
{
|
||||
char *buf = NULL;
|
||||
int l = 0;
|
||||
FILE *fp;
|
||||
size_t n;
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
if (fp == NULL) {
|
||||
err(EXIT_FAILURE, "open '%s'", fname);
|
||||
}
|
||||
while (l++, getline(&buf, &n, fp) != -1) {
|
||||
fam_addr_mask_t *fam;
|
||||
|
||||
if (*buf == '\n' || *buf == '#') {
|
||||
continue;
|
||||
}
|
||||
fam = npfctl_parse_cidr(buf);
|
||||
if (fam == NULL) {
|
||||
errx(EXIT_FAILURE, "%s:%d: invalid table entry",
|
||||
fname, l);
|
||||
}
|
||||
|
||||
/* Create and add a table entry. */
|
||||
npf_table_add_entry(tl, &fam->fam_addr, fam->fam_mask);
|
||||
}
|
||||
if (buf != NULL) {
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_build_table: create an NPF table, add to the configuration and,
|
||||
* if required, fill with contents from a file.
|
||||
*/
|
||||
void
|
||||
npfctl_build_table(const char *tid, u_int type, const char *fname)
|
||||
{
|
||||
nl_table_t *tl;
|
||||
u_int id;
|
||||
|
||||
id = atoi(tid);
|
||||
tl = npf_table_create(id, type);
|
||||
assert(tl != NULL);
|
||||
|
||||
if (npf_table_insert(npf_conf, tl)) {
|
||||
errx(EXIT_FAILURE, "table '%d' is already defined\n", id);
|
||||
}
|
||||
|
||||
if (fname) {
|
||||
npfctl_fill_table(tl, fname);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $ */
|
||||
/* $NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -27,484 +27,427 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* npfctl(8) helper routines.
|
||||
* npfctl(8) data manipulation and helper routines.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $");
|
||||
__RCSID("$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/null.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#define ICMP_STRINGS
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
static struct ifaddrs * ifs_list = NULL;
|
||||
nl_config_t * npf_conf = NULL;
|
||||
|
||||
void
|
||||
npfctl_init_data(void)
|
||||
unsigned long
|
||||
npfctl_find_ifindex(const char *ifname)
|
||||
{
|
||||
|
||||
npf_conf = npf_config_create();
|
||||
if (npf_conf == NULL) {
|
||||
errx(EXIT_FAILURE, "npf_config_create");
|
||||
}
|
||||
if (getifaddrs(&ifs_list) == -1) {
|
||||
err(EXIT_FAILURE, "getifaddrs");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
npfctl_ioctl_send(int fd)
|
||||
{
|
||||
int error = npf_config_submit(npf_conf, fd);
|
||||
npf_config_destroy(npf_conf);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines:
|
||||
*
|
||||
* npfctl_getif() - get interface addresses and index number from name.
|
||||
* npfctl_parse_v4mask() - parse address/mask integers from CIDR block.
|
||||
* npfctl_parse_port() - parse port number (which may be a service name).
|
||||
* npfctl_parse_tcpfl() - parse TCP flags.
|
||||
*/
|
||||
|
||||
struct ifaddrs *
|
||||
npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr, sa_family_t addrtype)
|
||||
{
|
||||
struct ifaddrs *ifent;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
|
||||
sin = (struct sockaddr_in *)ifent->ifa_addr;
|
||||
if (sin->sin_family != addrtype && reqaddr)
|
||||
continue;
|
||||
if (strcmp(ifent->ifa_name, ifname) == 0)
|
||||
break;
|
||||
}
|
||||
if (ifent) {
|
||||
*if_idx = if_nametoindex(ifname);
|
||||
}
|
||||
return ifent;
|
||||
}
|
||||
|
||||
bool
|
||||
npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport)
|
||||
{
|
||||
char *str = xstrdup(ostr), *sep;
|
||||
|
||||
*range = false;
|
||||
if ((sep = strchr(str, ':')) != NULL) {
|
||||
/* Port range (only numeric). */
|
||||
*range = true;
|
||||
*sep = '\0';
|
||||
|
||||
} else if (isalpha((unsigned char)*str)) {
|
||||
struct servent *se;
|
||||
|
||||
se = getservbyname(str, NULL);
|
||||
if (se == NULL) {
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
*fport = se->s_port;
|
||||
} else {
|
||||
*fport = htons(atoi(str));
|
||||
}
|
||||
*tport = sep ? htons(atoi(sep + 1)) : *fport;
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
npfctl_create_mask(sa_family_t family, u_int length, npf_addr_t *omask)
|
||||
{
|
||||
uint32_t part;
|
||||
uint32_t *mask = (uint32_t*)omask;
|
||||
|
||||
memset(omask, 0, sizeof(npf_addr_t));
|
||||
if (family == AF_INET) {
|
||||
part = htonl(0xffffffff << (32 - length));
|
||||
memcpy(mask, &part, 4);
|
||||
} else if (family == AF_INET6) {
|
||||
while (length > 32) {
|
||||
part = htonl(0xffffffff);
|
||||
memcpy(mask, &part, 4);
|
||||
mask += 1;
|
||||
length -= 32;
|
||||
}
|
||||
part = htonl(0xffffffff << (32 - length));
|
||||
memcpy(mask, &part, 4);
|
||||
}
|
||||
}
|
||||
|
||||
sa_family_t
|
||||
npfctl_get_addrfamily(const char *ostr)
|
||||
{
|
||||
struct addrinfo hint, *res = NULL;
|
||||
int ret;
|
||||
char *str = xstrdup(ostr);
|
||||
char *p = strchr(str, '/');
|
||||
sa_family_t family;
|
||||
|
||||
if (p)
|
||||
*p = '\0';
|
||||
memset(&hint, '\0', sizeof(hint));
|
||||
hint.ai_family = PF_UNSPEC;
|
||||
hint.ai_flags = AI_NUMERICHOST;
|
||||
ret = getaddrinfo(str, NULL, &hint, &res);
|
||||
if (ret) {
|
||||
family = AF_UNSPEC;
|
||||
} else {
|
||||
family = res->ai_family;
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
free(str);
|
||||
return family;
|
||||
}
|
||||
|
||||
sa_family_t
|
||||
npfctl_parse_cidr(char *str, sa_family_t addrfamily, npf_addr_t *addr, npf_netmask_t *mask)
|
||||
{
|
||||
|
||||
if (strcmp(str, "any") == 0) {
|
||||
memset(addr, 0, sizeof(npf_addr_t));
|
||||
memset(mask, 0, sizeof(npf_netmask_t));
|
||||
} else if (isalpha((unsigned char)*str)) {
|
||||
/* TODO: handle multiple addresses per interface */
|
||||
struct ifaddrs *ifa;
|
||||
struct sockaddr_in *sin;
|
||||
u_int idx;
|
||||
if ((ifa = npfctl_getif(str, &idx, true, AF_INET)) == NULL) {
|
||||
errx(EXIT_FAILURE, "invalid interface '%s'", str);
|
||||
}
|
||||
/* Interface address. */
|
||||
sin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
memcpy(addr, &(sin->sin_addr.s_addr), sizeof(struct in_addr));
|
||||
//v4mask = 0xffffffff; - TODO!
|
||||
} else {
|
||||
char *p = strchr(str, '/');
|
||||
if (p != NULL) {
|
||||
*p++ = '\0';
|
||||
*mask = atoi(p);
|
||||
} else {
|
||||
if (addrfamily == AF_INET)
|
||||
*mask = 32;
|
||||
else
|
||||
*mask = 128;
|
||||
}
|
||||
memset(addr, 0, sizeof(npf_addr_t));
|
||||
int ret = inet_pton(addrfamily, str, addr);
|
||||
if (ret != 1) {
|
||||
printf("TODO: error");
|
||||
}
|
||||
}
|
||||
|
||||
return addrfamily;
|
||||
return if_nametoindex(ifname);
|
||||
}
|
||||
|
||||
static bool
|
||||
npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask)
|
||||
npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr)
|
||||
{
|
||||
uint8_t tcpfl = 0;
|
||||
bool mask = false;
|
||||
switch (fam) {
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *sin = ptr;
|
||||
memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr));
|
||||
return true;
|
||||
}
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sin6 = ptr;
|
||||
memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
yyerror("unknown address family %u", fam);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (*s) {
|
||||
switch (*s) {
|
||||
case 'F': tcpfl |= TH_FIN; break;
|
||||
case 'S': tcpfl |= TH_SYN; break;
|
||||
case 'R': tcpfl |= TH_RST; break;
|
||||
case 'P': tcpfl |= TH_PUSH; break;
|
||||
case 'A': tcpfl |= TH_ACK; break;
|
||||
case 'U': tcpfl |= TH_URG; break;
|
||||
case 'E': tcpfl |= TH_ECE; break;
|
||||
case 'W': tcpfl |= TH_CWR; break;
|
||||
case '/':
|
||||
*s = '\0';
|
||||
*tfl = tcpfl;
|
||||
tcpfl = 0;
|
||||
mask = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
s++;
|
||||
static bool
|
||||
npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr)
|
||||
{
|
||||
static const struct addrinfo hint = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_flags = AI_NUMERICHOST
|
||||
};
|
||||
struct addrinfo *ai;
|
||||
int ret;
|
||||
|
||||
ret = getaddrinfo(name, NULL, &hint, &ai);
|
||||
if (ret) {
|
||||
yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret));
|
||||
return false;
|
||||
}
|
||||
if (!mask) {
|
||||
*tfl = tcpfl;
|
||||
if (fam) {
|
||||
*fam = ai->ai_family;
|
||||
}
|
||||
*tfl_mask = tcpfl;
|
||||
if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) {
|
||||
return false;
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
npfctl_fill_table(nl_table_t *tl, char *fname)
|
||||
static bool
|
||||
npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask)
|
||||
{
|
||||
char *buf;
|
||||
FILE *fp;
|
||||
size_t n;
|
||||
int l;
|
||||
char *ep = NULL;
|
||||
npf_addr_t addr;
|
||||
uint8_t *ap;
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
if (fp == NULL) {
|
||||
err(EXIT_FAILURE, "open '%s'", fname);
|
||||
if (s) {
|
||||
errno = 0;
|
||||
*mask = (npf_netmask_t)strtol(s, &ep, 0);
|
||||
if (*ep == '\0' && s != ep && errno != ERANGE)
|
||||
return true;
|
||||
if (!npfctl_parse_fam_addr(s, &fam, &addr))
|
||||
return false;
|
||||
}
|
||||
l = 1;
|
||||
buf = NULL;
|
||||
while (getline(&buf, &n, fp) != -1) {
|
||||
npf_addr_t addr;
|
||||
npf_netmask_t mask;
|
||||
|
||||
if (*buf == '\n' || *buf == '#')
|
||||
continue;
|
||||
switch (fam) {
|
||||
case AF_INET:
|
||||
*mask = 32;
|
||||
break;
|
||||
case AF_INET6:
|
||||
*mask = 128;
|
||||
break;
|
||||
default:
|
||||
yyerror("unknown address family %u", fam);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!npfctl_parse_cidr(buf, npfctl_get_addrfamily(buf), &addr, &mask)) {
|
||||
errx(EXIT_FAILURE, "invalid table entry at line %d", l);
|
||||
if (ep == NULL) {
|
||||
return true;
|
||||
}
|
||||
ap = addr.s6_addr + (*mask / 8) - 1;
|
||||
while (ap >= addr.s6_addr) {
|
||||
for (int j = 8; j > 0; j--) {
|
||||
if (*ap & 1)
|
||||
return true;
|
||||
*ap >>= 1;
|
||||
(*mask)--;
|
||||
if (*mask == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Create and add table entry. */
|
||||
npf_table_add_entry(tl, &addr, mask);
|
||||
l++;
|
||||
}
|
||||
if (buf != NULL) {
|
||||
free(buf);
|
||||
ap--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* N-code generation helpers.
|
||||
* npfctl_parse_fam_addr_mask: return address family, address and mask.
|
||||
*
|
||||
* => Mask is optional and can be NULL.
|
||||
* => Returns true on success or false if unable to parse.
|
||||
*/
|
||||
|
||||
static void
|
||||
npfctl_rulenc_cidr(void **nc, int nblocks[], var_t *dat, bool sd, sa_family_t addrfamily)
|
||||
npfvar_t *
|
||||
npfctl_parse_fam_addr_mask(const char *addr, const char *mask,
|
||||
unsigned long *nummask)
|
||||
{
|
||||
element_t *el = dat->v_elements;
|
||||
int foff;
|
||||
npfvar_t *vp = npfvar_create(".addr");
|
||||
fam_addr_mask_t fam;
|
||||
|
||||
/* If table, generate a single table matching block. */
|
||||
if (dat->v_type == VAR_TABLE) {
|
||||
u_int tid = atoi(el->e_data);
|
||||
memset(&fam, 0, sizeof(fam));
|
||||
|
||||
nblocks[0]--;
|
||||
foff = npfctl_failure_offset(nblocks);
|
||||
npfctl_gennc_tbl(nc, foff, tid, sd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Generate v4/v6 CIDR matching blocks. */
|
||||
for (el = dat->v_elements; el != NULL; el = el->e_next) {
|
||||
npf_addr_t addr;
|
||||
npf_netmask_t mask;
|
||||
|
||||
npfctl_parse_cidr(el->e_data, addrfamily, &addr, &mask);
|
||||
if (addrfamily == AF_INET)
|
||||
nblocks[1]--;
|
||||
else if (addrfamily == AF_INET6)
|
||||
nblocks[3]--;
|
||||
foff = npfctl_failure_offset(nblocks);
|
||||
if (addrfamily == AF_INET)
|
||||
npfctl_gennc_v4cidr(nc, foff, &addr, mask, sd);
|
||||
else if (addrfamily == AF_INET6)
|
||||
npfctl_gennc_v6cidr(nc, foff, &addr, mask, sd);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp,
|
||||
bool both, bool sd)
|
||||
{
|
||||
element_t *el = dat->v_elements;
|
||||
int foff;
|
||||
|
||||
assert(dat->v_type != VAR_TABLE);
|
||||
|
||||
/* Generate TCP/UDP port matching blocks. */
|
||||
for (el = dat->v_elements; el != NULL; el = el->e_next) {
|
||||
in_port_t fport, tport;
|
||||
bool range;
|
||||
|
||||
if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) {
|
||||
errx(EXIT_FAILURE, "invalid service '%s'", el->e_data);
|
||||
}
|
||||
nblocks[0]--;
|
||||
foff = both ? 0 : npfctl_failure_offset(nblocks);
|
||||
npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
|
||||
bool both, bool tcpudp, bool sd, sa_family_t addrfamily)
|
||||
{
|
||||
|
||||
npfctl_rulenc_cidr(nc, nblocks, cidr, sd, addrfamily);
|
||||
if (ports == NULL) {
|
||||
return;
|
||||
}
|
||||
npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd);
|
||||
if (!both) {
|
||||
return;
|
||||
}
|
||||
npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd);
|
||||
}
|
||||
|
||||
void
|
||||
npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type,
|
||||
int icmp_code, var_t *from, sa_family_t addrfamily, var_t *fports, var_t *to, var_t *tports)
|
||||
{
|
||||
int nblocks[4] = { 0, 0, 0, 0 };
|
||||
bool icmp, tcpudp, both;
|
||||
void *ncptr, *nc;
|
||||
size_t sz, foff;
|
||||
if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Default: both TCP and UDP.
|
||||
* Note: both mask and nummask may be NULL. In such case,
|
||||
* npfctl_parse_mask() will handle and will set full mask.
|
||||
*/
|
||||
icmp = false;
|
||||
tcpudp = true;
|
||||
if (proto == NULL) {
|
||||
both = true;
|
||||
goto skip_proto;
|
||||
}
|
||||
both = false;
|
||||
|
||||
if (strcmp(proto, "icmp") == 0) {
|
||||
/* ICMP case. */
|
||||
fports = NULL;
|
||||
tports = NULL;
|
||||
icmp = true;
|
||||
|
||||
} else if (strcmp(proto, "tcp") == 0) {
|
||||
/* Just TCP. */
|
||||
tcpudp = true;
|
||||
|
||||
} else if (strcmp(proto, "udp") == 0) {
|
||||
/* Just UDP. */
|
||||
tcpudp = false;
|
||||
|
||||
} else {
|
||||
/* Default. */
|
||||
}
|
||||
skip_proto:
|
||||
if (icmp || icmp_type != -1) {
|
||||
assert(tcpfl == NULL);
|
||||
icmp = true;
|
||||
nblocks[2] += 1;
|
||||
}
|
||||
if (tcpudp && tcpfl) {
|
||||
assert(icmp_type == -1 && icmp_code == -1);
|
||||
nblocks[2] += 1;
|
||||
if (nummask) {
|
||||
fam.fam_mask = *nummask;
|
||||
} else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate how blocks to determince n-code. */
|
||||
if (from && from->v_count) {
|
||||
if (from->v_type == VAR_TABLE)
|
||||
nblocks[0] += 1;
|
||||
else {
|
||||
if (addrfamily == AF_INET)
|
||||
nblocks[1] += from->v_count;
|
||||
else
|
||||
nblocks[3] += from->v_count;
|
||||
}
|
||||
if (fports && fports->v_count)
|
||||
nblocks[0] += fports->v_count * (both ? 2 : 1);
|
||||
}
|
||||
if (to && to->v_count) {
|
||||
if (to->v_type == VAR_TABLE)
|
||||
nblocks[0] += 1;
|
||||
else {
|
||||
if (addrfamily == AF_INET)
|
||||
nblocks[1] += to->v_count;
|
||||
else
|
||||
nblocks[3] += to->v_count;
|
||||
}
|
||||
if (tports && tports->v_count)
|
||||
nblocks[0] += tports->v_count * (both ? 2 : 1);
|
||||
}
|
||||
if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
|
||||
goto out;
|
||||
|
||||
/* Any n-code to generate? */
|
||||
if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2] + nblocks[3]) == 0) {
|
||||
/* Done, if none. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate memory for the n-code. */
|
||||
sz = npfctl_calc_ncsize(nblocks);
|
||||
ncptr = malloc(sz);
|
||||
if (ncptr == NULL) {
|
||||
err(EXIT_FAILURE, "malloc");
|
||||
}
|
||||
nc = ncptr;
|
||||
|
||||
/*
|
||||
* Generate v4/v6 CIDR matching blocks and TCP/UDP port matching.
|
||||
*/
|
||||
if (from) {
|
||||
npfctl_rulenc_block(&nc, nblocks, from, fports,
|
||||
both, tcpudp, true, addrfamily);
|
||||
}
|
||||
if (to) {
|
||||
npfctl_rulenc_block(&nc, nblocks, to, tports,
|
||||
both, tcpudp, false, addrfamily);
|
||||
}
|
||||
|
||||
if (icmp) {
|
||||
/*
|
||||
* ICMP case.
|
||||
*/
|
||||
nblocks[2]--;
|
||||
foff = npfctl_failure_offset(nblocks);
|
||||
npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code);
|
||||
|
||||
} else if (tcpudp && tcpfl) {
|
||||
/*
|
||||
* TCP case, flags.
|
||||
*/
|
||||
uint8_t tfl = 0, tfl_mask;
|
||||
|
||||
nblocks[2]--;
|
||||
foff = npfctl_failure_offset(nblocks);
|
||||
if (!npfctl_parse_tcpfl(tcpfl, &tfl, &tfl_mask)) {
|
||||
errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcpfl);
|
||||
}
|
||||
npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask);
|
||||
}
|
||||
npfctl_gennc_complete(&nc);
|
||||
|
||||
if ((uintptr_t)nc - (uintptr_t)ncptr != sz) {
|
||||
errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)",
|
||||
(uintptr_t)nc - (uintptr_t)ncptr, sz);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t *op = ncptr;
|
||||
size_t n = sz;
|
||||
do {
|
||||
DPRINTF(("\t> |0x%02x|\n", (u_int)*op));
|
||||
op++;
|
||||
n -= sizeof(*op);
|
||||
} while (n);
|
||||
#endif
|
||||
|
||||
/* Create a final memory block of data, ready to send. */
|
||||
if (npf_rule_setcode(rl, NPF_CODE_NCODE, ncptr, sz) == -1) {
|
||||
errx(EXIT_FAILURE, "npf_rule_setcode");
|
||||
}
|
||||
free(ncptr);
|
||||
return vp;
|
||||
out:
|
||||
npfvar_destroy(vp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfctl_parse_table_id(const char *id)
|
||||
{
|
||||
npfvar_t *vp;
|
||||
|
||||
if (!npfctl_table_exists_p(id)) {
|
||||
yyerror("table '%s' is not defined", id);
|
||||
return NULL;
|
||||
}
|
||||
vp = npfvar_create(".table");
|
||||
|
||||
if (!npfvar_add_element(vp, NPFVAR_TABLE, id, strlen(id) + 1))
|
||||
goto out;
|
||||
|
||||
return vp;
|
||||
out:
|
||||
npfvar_destroy(vp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parse_port_range: create a port-range variable. Note that the
|
||||
* passed port numbers are in network byte order.
|
||||
*/
|
||||
npfvar_t *
|
||||
npfctl_parse_port_range(in_port_t s, in_port_t e)
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".port_range");
|
||||
port_range_t pr;
|
||||
|
||||
pr.pr_start = s;
|
||||
pr.pr_end = e;
|
||||
|
||||
if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
|
||||
goto out;
|
||||
|
||||
return vp;
|
||||
out:
|
||||
npfvar_destroy(vp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfctl_parse_iface(const char *ifname)
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".iface");
|
||||
struct ifaddrs *ifa;
|
||||
fam_addr_mask_t fam;
|
||||
bool gotif = false;
|
||||
|
||||
if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) {
|
||||
err(EXIT_FAILURE, "getifaddrs");
|
||||
}
|
||||
memset(&fam, 0, sizeof(fam));
|
||||
|
||||
npfvar_t *ip = npfvar_create(".ifname");
|
||||
if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1))
|
||||
goto out;
|
||||
|
||||
for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
struct sockaddr *sa;
|
||||
sa_family_t family;
|
||||
|
||||
if (strcmp(ifa->ifa_name, ifname) != 0)
|
||||
continue;
|
||||
|
||||
gotif = true;
|
||||
if ((ifa->ifa_flags & IFF_UP) == 0)
|
||||
warnx("interface '%s' is down", ifname);
|
||||
|
||||
sa = ifa->ifa_addr;
|
||||
family = sa->sa_family;
|
||||
if (family != AF_INET && family != AF_INET6)
|
||||
continue;
|
||||
|
||||
fam.fam_family = family;
|
||||
fam.fam_interface = ip;
|
||||
|
||||
if (!npfctl_copy_address(family, &fam.fam_addr, sa))
|
||||
goto out;
|
||||
|
||||
if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
|
||||
goto out;
|
||||
|
||||
if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
|
||||
goto out;
|
||||
}
|
||||
if (!gotif) {
|
||||
yyerror("interface '%s' not found", ifname);
|
||||
goto out;
|
||||
}
|
||||
if (npfvar_get_count(vp) == 0) {
|
||||
yyerror("no addresses matched for interface '%s'", ifname);
|
||||
goto out;
|
||||
}
|
||||
return vp;
|
||||
out:
|
||||
npfvar_destroy(vp);
|
||||
npfvar_destroy(ip);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fam_addr_mask_t *
|
||||
npfctl_parse_cidr(char *cidr)
|
||||
{
|
||||
npfvar_t *vp;
|
||||
char *p;
|
||||
|
||||
p = strchr(cidr, '/');
|
||||
if (p) {
|
||||
*p++ = '\0';
|
||||
}
|
||||
vp = npfctl_parse_fam_addr_mask(cidr, p, NULL);
|
||||
if (vp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return npfvar_get_data(vp, NPFVAR_FAM, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_portno: convert port identifier (string) to a number.
|
||||
*
|
||||
* => Returns port number in network byte order.
|
||||
*/
|
||||
in_port_t
|
||||
npfctl_portno(const char *port)
|
||||
{
|
||||
struct addrinfo *ai, *rai;
|
||||
in_port_t p = 0;
|
||||
int e;
|
||||
|
||||
e = getaddrinfo(NULL, port, NULL, &rai);
|
||||
if (e != 0) {
|
||||
yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ai = rai; ai; ai = ai->ai_next) {
|
||||
switch (ai->ai_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *sin = (void *)ai->ai_addr;
|
||||
p = sin->sin_port;
|
||||
goto out;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
|
||||
p = sin6->sin6_port;
|
||||
goto out;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
freeaddrinfo(rai);
|
||||
return p;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfctl_parse_tcpflag(const char *s)
|
||||
{
|
||||
uint8_t tfl = 0;
|
||||
|
||||
while (*s) {
|
||||
switch (*s) {
|
||||
case 'F': tfl |= TH_FIN; break;
|
||||
case 'S': tfl |= TH_SYN; break;
|
||||
case 'R': tfl |= TH_RST; break;
|
||||
case 'P': tfl |= TH_PUSH; break;
|
||||
case 'A': tfl |= TH_ACK; break;
|
||||
case 'U': tfl |= TH_URG; break;
|
||||
case 'E': tfl |= TH_ECE; break;
|
||||
case 'W': tfl |= TH_CWR; break;
|
||||
default:
|
||||
yyerror("invalid flag '%c'", *s);
|
||||
return NULL;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
npfvar_t *vp = npfvar_create(".tcp_flag");
|
||||
if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) {
|
||||
npfvar_destroy(vp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vp;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
npfctl_icmptype(const char *type)
|
||||
{
|
||||
for (uint8_t ul = 0; icmp_type[ul]; ul++)
|
||||
if (strcmp(icmp_type[ul], type) == 0)
|
||||
return ul;
|
||||
return ~0;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
npfctl_icmpcode(uint8_t type, const char *code)
|
||||
{
|
||||
const char **arr;
|
||||
|
||||
switch (type) {
|
||||
case ICMP_ECHOREPLY:
|
||||
case ICMP_SOURCEQUENCH:
|
||||
case ICMP_ALTHOSTADDR:
|
||||
case ICMP_ECHO:
|
||||
case ICMP_ROUTERSOLICIT:
|
||||
case ICMP_TSTAMP:
|
||||
case ICMP_TSTAMPREPLY:
|
||||
case ICMP_IREQ:
|
||||
case ICMP_IREQREPLY:
|
||||
case ICMP_MASKREQ:
|
||||
case ICMP_MASKREPLY:
|
||||
arr = icmp_code_none;
|
||||
break;
|
||||
case ICMP_ROUTERADVERT:
|
||||
arr = icmp_code_routeradvert;
|
||||
break;
|
||||
case ICMP_UNREACH:
|
||||
arr = icmp_code_unreach;
|
||||
break;
|
||||
case ICMP_REDIRECT:
|
||||
arr = icmp_code_redirect;
|
||||
break;
|
||||
case ICMP_TIMXCEED:
|
||||
arr = icmp_code_timxceed;
|
||||
break;
|
||||
case ICMP_PARAMPROB:
|
||||
arr = icmp_code_paramprob;
|
||||
break;
|
||||
case ICMP_PHOTURIS:
|
||||
arr = icmp_code_photuris;
|
||||
break;
|
||||
default:
|
||||
return ~0;
|
||||
}
|
||||
|
||||
for (uint8_t ul = 0; arr[ul]; ul++) {
|
||||
if (strcmp(arr[ul], code) == 0)
|
||||
return ul;
|
||||
}
|
||||
return ~0;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfctl_parse_icmp(uint8_t type, uint8_t code)
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".icmp");
|
||||
|
||||
if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type)))
|
||||
goto out;
|
||||
|
||||
if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code)))
|
||||
goto out;
|
||||
|
||||
return vp;
|
||||
out:
|
||||
npfvar_destroy(vp);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $ */
|
||||
/* $NetBSD: npf_ncgen.c,v 1.6 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
@ -30,194 +30,317 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* N-code generation.
|
||||
*
|
||||
* WARNING: Update npfctl_calc_ncsize() and npfctl_failure_offset()
|
||||
* calculations, when changing generation routines.
|
||||
* N-code generation interface.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $");
|
||||
__RCSID("$NetBSD: npf_ncgen.c,v 1.6 2012/01/08 21:34:21 rmind Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
/*
|
||||
* npfctl_calc_ncsize: calculate size required for the n-code.
|
||||
*/
|
||||
size_t
|
||||
npfctl_calc_ncsize(int nblocks[])
|
||||
{
|
||||
/* Reduce re-allocations by expanding in 64 byte blocks. */
|
||||
#define NC_ALLOC_MASK (64 - 1)
|
||||
#define NC_ALLOC_ROUND(x) (((x) + NC_ALLOC_MASK) & ~NC_ALLOC_MASK)
|
||||
|
||||
struct nc_ctx {
|
||||
/*
|
||||
* Blocks:
|
||||
* - 5 words each by npfctl_gennc_ports/tbl(), stored in nblocks[0].
|
||||
* - 6 words each by npfctl_gennc_v4cidr(), stored in nblocks[1].
|
||||
* - 4 words by npfctl_gennc_{icmp,tcpfl}(), stored in nblocks[2].
|
||||
* - 9 words each by npfctl_gennc_v6cidr(), stored in nblocks[3].
|
||||
* - 4 words by npfctl_gennc_complete(), single last fragment.
|
||||
* Original buffer address, size of the buffer and instruction
|
||||
* pointer for appending n-code fragments.
|
||||
*/
|
||||
return nblocks[0] * 5 * sizeof(uint32_t) +
|
||||
nblocks[1] * 6 * sizeof(uint32_t) +
|
||||
nblocks[2] * 4 * sizeof(uint32_t) +
|
||||
nblocks[3] * 9 * sizeof(uint32_t) +
|
||||
4 * sizeof(uint32_t);
|
||||
void * nc_buf;
|
||||
void * nc_iptr;
|
||||
size_t nc_len;
|
||||
/* Expected number of words for diagnostic check. */
|
||||
size_t nc_expected;
|
||||
/* List of jump values, length of the memory and iterator. */
|
||||
ptrdiff_t * nc_jmp_list;
|
||||
size_t nc_jmp_len;
|
||||
size_t nc_jmp_it;
|
||||
/* Current logical operation for a group and saved iterator. */
|
||||
size_t nc_saved_it;
|
||||
};
|
||||
|
||||
/*
|
||||
* npfctl_ncgen_getptr: return the instruction pointer and make sure that
|
||||
* buffer is large enough to add a new fragment of a given size.
|
||||
*/
|
||||
static uint32_t *
|
||||
npfctl_ncgen_getptr(nc_ctx_t *ctx, size_t nwords)
|
||||
{
|
||||
size_t offset, reqlen;
|
||||
|
||||
/* Save the number of expected words for diagnostic check. */
|
||||
assert(ctx->nc_expected == 0);
|
||||
ctx->nc_expected = (sizeof(uint32_t) * nwords);
|
||||
|
||||
/*
|
||||
* Calculate the required length. If buffer size is large enough,
|
||||
* just return the pointer.
|
||||
*/
|
||||
offset = (uintptr_t)ctx->nc_iptr - (uintptr_t)ctx->nc_buf;
|
||||
assert(offset <= ctx->nc_len);
|
||||
reqlen = offset + ctx->nc_expected;
|
||||
if (reqlen < ctx->nc_len) {
|
||||
return ctx->nc_iptr;
|
||||
}
|
||||
|
||||
/* Otherwise, re-allocate the buffer and update the pointers. */
|
||||
ctx->nc_len = NC_ALLOC_ROUND(reqlen);
|
||||
ctx->nc_buf = xrealloc(ctx->nc_buf, ctx->nc_len);
|
||||
ctx->nc_iptr = (uint8_t *)ctx->nc_buf + offset;
|
||||
return ctx->nc_iptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_failure_offset: calculate offset value to the failure block.
|
||||
* npfctl_ncgen_putptr: perform a diagnostic check whether expected words
|
||||
* were appended and save the instruction pointer.
|
||||
*/
|
||||
size_t
|
||||
npfctl_failure_offset(int nblocks[])
|
||||
static void
|
||||
npfctl_ncgen_putptr(nc_ctx_t *ctx, void *nc)
|
||||
{
|
||||
size_t tblport_blocks, v4cidr_blocks, v6cidr_blocks, icmp_tcpfl;
|
||||
/*
|
||||
* Take into account all blocks (plus 2 words for comparison each),
|
||||
* and additional 4 words to skip the last comparison and success path.
|
||||
*/
|
||||
tblport_blocks = (3 + 2) * nblocks[0];
|
||||
v4cidr_blocks = (4 + 2) * nblocks[1];
|
||||
icmp_tcpfl = (2 + 2) * nblocks[2];
|
||||
v6cidr_blocks = (7 + 2) * nblocks[3];
|
||||
return tblport_blocks + v4cidr_blocks + v6cidr_blocks + icmp_tcpfl + 4;
|
||||
ptrdiff_t diff = (uintptr_t)nc - (uintptr_t)ctx->nc_iptr;
|
||||
|
||||
if ((ptrdiff_t)ctx->nc_expected != diff) {
|
||||
errx(EXIT_FAILURE, "unexpected n-code fragment size "
|
||||
"(expected words %lu, diff %td)", ctx->nc_expected, diff);
|
||||
}
|
||||
ctx->nc_expected = 0;
|
||||
ctx->nc_iptr = nc;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* npfctl_gennc_ether: initial n-code fragment to check Ethernet frame.
|
||||
* npfctl_ncgen_addjmp: add the compare/jump opcode, dummy value and
|
||||
* its pointer into the list.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_ether(void **ncptr, int foff, uint16_t ethertype)
|
||||
static void
|
||||
npfctl_ncgen_addjmp(nc_ctx_t *ctx, uint32_t **nc_ptr)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
size_t reqlen, i = ctx->nc_jmp_it++;
|
||||
uint32_t *nc = *nc_ptr;
|
||||
|
||||
/* NPF handler will set REG_0 to either NPF_LAYER_2 or NPF_LAYER_3. */
|
||||
*nc++ = NPF_OPCODE_CMP;
|
||||
*nc++ = NPF_LAYER_3;
|
||||
*nc++ = 0;
|
||||
reqlen = NC_ALLOC_ROUND(ctx->nc_jmp_it * sizeof(ptrdiff_t));
|
||||
|
||||
/* Skip all further code, if layer 3. */
|
||||
*nc++ = NPF_OPCODE_BEQ;
|
||||
*nc++ = 0x0a;
|
||||
if (reqlen > NC_ALLOC_ROUND(ctx->nc_jmp_len)) {
|
||||
ctx->nc_jmp_list = xrealloc(ctx->nc_jmp_list, reqlen);
|
||||
ctx->nc_jmp_len = reqlen;
|
||||
}
|
||||
|
||||
/* Otherwise, assume layer 2 and perform NPF_OPCODE_ETHER. */
|
||||
*nc++ = NPF_OPCODE_ETHER;
|
||||
*nc++ = 0x00; /* reserved */
|
||||
*nc++ = 0x00; /* reserved */
|
||||
*nc++ = ethertype;
|
||||
/* Save the offset (note: we cannot save the pointer). */
|
||||
ctx->nc_jmp_list[i] = (uintptr_t)nc - (uintptr_t)ctx->nc_buf;
|
||||
|
||||
/* Fail (+ 2 words of ADVR) or advance to layer 3 (IPv4) header. */
|
||||
/* Note: if OR grouping case, BNE will be replaced with BEQ. */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff + 2;
|
||||
/* Offset to the header is returned by NPF_OPCODE_ETHER in REG_3. */
|
||||
*nc++ = NPF_OPCODE_ADVR;
|
||||
*nc++ = 3;
|
||||
|
||||
/* + 13 words. */
|
||||
*ncptr = (void *)nc;
|
||||
*nc++ = 0xdeadbeef;
|
||||
*nc_ptr = nc;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
npfctl_gennc_v6cidr(void **ncptr, int foff,
|
||||
const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
|
||||
/*
|
||||
* npfctl_ncgen_create: new n-code generation context.
|
||||
*/
|
||||
nc_ctx_t *
|
||||
npfctl_ncgen_create(void)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
return zalloc(sizeof(nc_ctx_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_ncgen_complete: complete generation, destroy the context and
|
||||
* return a pointer to the final buffer containing n-code.
|
||||
*/
|
||||
void *
|
||||
npfctl_ncgen_complete(nc_ctx_t *ctx, size_t *sz)
|
||||
{
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
|
||||
ptrdiff_t foff;
|
||||
size_t i;
|
||||
|
||||
assert(ctx->nc_saved_it == 0);
|
||||
|
||||
/* Success path (return 0x0). */
|
||||
*nc++ = NPF_OPCODE_RET;
|
||||
*nc++ = 0x0;
|
||||
|
||||
/* Failure path (return 0xff). */
|
||||
foff = ((uintptr_t)nc - (uintptr_t)ctx->nc_buf) / sizeof(uint32_t);
|
||||
*nc++ = NPF_OPCODE_RET;
|
||||
*nc++ = 0xff;
|
||||
|
||||
/* + 4 words. */
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
|
||||
/* Change the jump values. */
|
||||
for (i = 0; i < ctx->nc_jmp_it; i++) {
|
||||
ptrdiff_t off = ctx->nc_jmp_list[i] / sizeof(uint32_t);
|
||||
uint32_t *jmpop = (uint32_t *)ctx->nc_buf + off;
|
||||
uint32_t *jmpval = jmpop + 1;
|
||||
|
||||
assert(foff > off);
|
||||
assert(*jmpop == NPF_OPCODE_BNE);
|
||||
assert(*jmpval == 0xdeadbeef);
|
||||
*jmpval = foff - off;
|
||||
}
|
||||
|
||||
/* Return the buffer, destroy the context. */
|
||||
void *buf = ctx->nc_buf;
|
||||
*sz = (uintptr_t)ctx->nc_iptr - (uintptr_t)ctx->nc_buf;
|
||||
free(ctx->nc_jmp_list);
|
||||
free(ctx);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_ncgen_group: begin a logical group.
|
||||
*/
|
||||
void
|
||||
npfctl_ncgen_group(nc_ctx_t *ctx)
|
||||
{
|
||||
assert(ctx->nc_expected == 0);
|
||||
assert(ctx->nc_saved_it == 0);
|
||||
ctx->nc_saved_it = ctx->nc_jmp_it;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_ncgen_endgroup: end a logical group, fix up the code accordingly.
|
||||
*/
|
||||
void
|
||||
npfctl_ncgen_endgroup(nc_ctx_t *ctx)
|
||||
{
|
||||
uint32_t *nc;
|
||||
|
||||
/* If there are no fragments or only one - nothing to do. */
|
||||
if ((ctx->nc_jmp_it - ctx->nc_saved_it) <= 1) {
|
||||
ctx->nc_saved_it = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Append failure return for OR grouping. */
|
||||
nc = npfctl_ncgen_getptr(ctx, 2 /* words */);
|
||||
*nc++ = NPF_OPCODE_RET;
|
||||
*nc++ = 0xff;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
|
||||
/* Update any group jumps values on success to the current point. */
|
||||
for (size_t i = ctx->nc_saved_it; i < ctx->nc_jmp_it; i++) {
|
||||
ptrdiff_t off = ctx->nc_jmp_list[i] / sizeof(uint32_t);
|
||||
uint32_t *jmpop = (uint32_t *)ctx->nc_buf + off;
|
||||
uint32_t *jmpval = jmpop + 1;
|
||||
|
||||
assert(*jmpop == NPF_OPCODE_BNE);
|
||||
assert(*jmpval == 0xdeadbeef);
|
||||
|
||||
*jmpop = NPF_OPCODE_BEQ;
|
||||
*jmpval = nc - jmpop;
|
||||
ctx->nc_jmp_list[i] = 0;
|
||||
}
|
||||
|
||||
/* Reset the iterator. */
|
||||
ctx->nc_jmp_it = ctx->nc_saved_it;
|
||||
ctx->nc_saved_it = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_gennc_v6cidr: fragment to match IPv6 CIDR.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_v6cidr(nc_ctx_t *ctx, int opts, const npf_addr_t *netaddr,
|
||||
const npf_netmask_t mask)
|
||||
{
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 9 /* words */);
|
||||
const uint32_t *addr = (const uint32_t *)netaddr;
|
||||
|
||||
/* OP, direction, netaddr/subnet (10 words) */
|
||||
assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
|
||||
assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
|
||||
|
||||
/* OP, direction, netaddr/subnet (7 words) */
|
||||
*nc++ = NPF_OPCODE_IP6MASK;
|
||||
*nc++ = (sd ? 0x01 : 0x00);
|
||||
*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
|
||||
*nc++ = addr[0];
|
||||
*nc++ = addr[1];
|
||||
*nc++ = addr[2];
|
||||
*nc++ = addr[3];
|
||||
*nc++ = mask;
|
||||
|
||||
/* If not equal, jump to failure block, continue otherwise (2 words). */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 9 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* npfctl_gennc_v4cidr: fragment to match IPv4 CIDR.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_v4cidr(void **ncptr, int foff,
|
||||
const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
|
||||
npfctl_gennc_v4cidr(nc_ctx_t *ctx, int opts, const npf_addr_t *netaddr,
|
||||
const npf_netmask_t mask)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 6 /* words */);
|
||||
const uint32_t *addr = (const uint32_t *)netaddr;
|
||||
|
||||
assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
|
||||
assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
|
||||
|
||||
/* OP, direction, netaddr/subnet (4 words) */
|
||||
*nc++ = NPF_OPCODE_IP4MASK;
|
||||
*nc++ = (sd ? 0x01 : 0x00);
|
||||
*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
|
||||
*nc++ = addr[0];
|
||||
*nc++ = mask;
|
||||
|
||||
/* If not equal, jump to failure block, continue otherwise (2 words). */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 6 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_gennc_ports: fragment to match TCP or UDP ports.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_ports(void **ncptr, int foff,
|
||||
in_port_t pfrom, in_port_t pto, bool tcpudp, bool sd)
|
||||
npfctl_gennc_ports(nc_ctx_t *ctx, int opts, in_port_t from, in_port_t to)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 5 /* words */);
|
||||
|
||||
assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
|
||||
assert(((opts & NC_MATCH_TCP) != 0) ^ ((opts & NC_MATCH_UDP) != 0));
|
||||
|
||||
/* OP, direction, port range (3 words). */
|
||||
*nc++ = (tcpudp ? NPF_OPCODE_TCP_PORTS : NPF_OPCODE_UDP_PORTS);
|
||||
*nc++ = (sd ? 0x01 : 0x00);
|
||||
*nc++ = ((uint32_t)pfrom << 16) | pto;
|
||||
*nc++ = (opts & NC_MATCH_TCP) ?
|
||||
NPF_OPCODE_TCP_PORTS : NPF_OPCODE_UDP_PORTS;
|
||||
*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
|
||||
*nc++ = ((uint32_t)from << 16) | to;
|
||||
|
||||
/*
|
||||
* If not equal, jump to failure block, continue otherwise (2 words).
|
||||
* Specific case (foff == 0): when matching both TCP and UDP ports,
|
||||
* skip next port-matching fragment on success (5 + 2 words).
|
||||
*/
|
||||
if (foff) {
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
} else {
|
||||
*nc++ = NPF_OPCODE_BEQ;
|
||||
*nc++ = 5 + 2;
|
||||
}
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 5 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_gennc_icmp: fragment to match ICMP type and code.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_icmp(void **ncptr, int foff, int type, int code)
|
||||
npfctl_gennc_icmp(nc_ctx_t *ctx, int type, int code)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
|
||||
|
||||
/* OP, code, type (2 words) */
|
||||
*nc++ = NPF_OPCODE_ICMP4;
|
||||
*nc++ = (type == -1 ? 0 : (1 << 31) & (type & 0xff << 8)) |
|
||||
(code == -1 ? 0 : (1 << 31) & (code & 0xff));
|
||||
|
||||
/* If not equal, jump to failure block, continue otherwise (2 words). */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 4 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -225,59 +348,50 @@ npfctl_gennc_icmp(void **ncptr, int foff, int type, int code)
|
||||
* the packet against table specified by ID.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_tbl(void **ncptr, int foff, u_int tid, bool sd)
|
||||
npfctl_gennc_tbl(nc_ctx_t *ctx, int opts, u_int tableid)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 5 /* words */);
|
||||
|
||||
assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
|
||||
|
||||
/* OP, direction, table ID (3 words). */
|
||||
*nc++ = NPF_OPCODE_TABLE;
|
||||
*nc++ = (sd ? 0x01 : 0x00);
|
||||
*nc++ = tid;
|
||||
*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
|
||||
*nc++ = tableid;
|
||||
|
||||
/* If not equal, jump to failure block, continue otherwise (2 words). */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 5 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_gennc_tcpfl: fragment to match TCP flags/mask.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_tcpfl(void **ncptr, int foff, uint8_t tf, uint8_t tf_mask)
|
||||
npfctl_gennc_tcpfl(nc_ctx_t *ctx, uint8_t tf, uint8_t tf_mask)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
|
||||
|
||||
/* OP, code, type (2 words) */
|
||||
*nc++ = NPF_OPCODE_TCP_FLAGS;
|
||||
*nc++ = (tf << 8) | tf_mask;
|
||||
|
||||
/* If not equal, jump to failure block, continue otherwise (2 words). */
|
||||
*nc++ = NPF_OPCODE_BNE;
|
||||
*nc++ = foff;
|
||||
/* Comparison block (2 words). */
|
||||
npfctl_ncgen_addjmp(ctx, &nc);
|
||||
|
||||
/* + 4 words. */
|
||||
*ncptr = (void *)nc;
|
||||
npfctl_ncgen_putptr(ctx, nc);
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_gennc_complete: append success and failure fragments.
|
||||
*/
|
||||
void
|
||||
npfctl_gennc_complete(void **ncptr)
|
||||
npfctl_ncgen_print(const void *code, size_t len)
|
||||
{
|
||||
uint32_t *nc = *ncptr;
|
||||
const uint32_t *op = code;
|
||||
|
||||
/* Success path (return 0x0). */
|
||||
*nc++ = NPF_OPCODE_RET;
|
||||
*nc++ = 0x0;
|
||||
|
||||
/* Failure path (return 0xff). */
|
||||
*nc++ = NPF_OPCODE_RET;
|
||||
*nc++ = 0xff;
|
||||
|
||||
/* + 4 words. */
|
||||
*ncptr = (void *)nc;
|
||||
while (len) {
|
||||
printf("\t> |0x%02x|\n", (u_int)*op++);
|
||||
len -= sizeof(*op);
|
||||
}
|
||||
}
|
||||
|
713
usr.sbin/npf/npfctl/npf_parse.y
Normal file
713
usr.sbin/npf/npfctl/npf_parse.y
Normal file
@ -0,0 +1,713 @@
|
||||
/* $NetBSD: npf_parse.y,v 1.1 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Martin Husemann and Christos Zoulas.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <vis.h>
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
const char * yyfilename;
|
||||
|
||||
extern int yylineno, yycolumn;
|
||||
extern int yylex(void);
|
||||
|
||||
/* Variable under construction (bottom up). */
|
||||
static npfvar_t * cvar;
|
||||
|
||||
void
|
||||
yyerror(const char *fmt, ...)
|
||||
{
|
||||
extern int yyleng;
|
||||
extern char *yytext;
|
||||
|
||||
char *msg, *context = xstrndup(yytext, yyleng);
|
||||
size_t len = strlen(context);
|
||||
char *dst = zalloc(len * 4 + 1);
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vasprintf(&msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
|
||||
fprintf(stderr, "%s:%d:%d: %s near '%s'\n", yyfilename, yylineno,
|
||||
yycolumn, msg, dst);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%token ALL
|
||||
%token ANY
|
||||
%token APPLY
|
||||
%token ARROW
|
||||
%token BINAT
|
||||
%token BLOCK
|
||||
%token CURLY_CLOSE
|
||||
%token CURLY_OPEN
|
||||
%token CODE
|
||||
%token COLON
|
||||
%token COMMA
|
||||
%token DEFAULT
|
||||
%token TDYNAMIC
|
||||
%token EQ
|
||||
%token TFILE
|
||||
%token FLAGS
|
||||
%token FROM
|
||||
%token GROUP
|
||||
%token HASH
|
||||
%token ICMPTYPE
|
||||
%token ID
|
||||
%token IN
|
||||
%token INET
|
||||
%token INET6
|
||||
%token INTERFACE
|
||||
%token KEEP
|
||||
%token MINUS
|
||||
%token NAT
|
||||
%token NAME
|
||||
%token ON
|
||||
%token OUT
|
||||
%token PAR_CLOSE
|
||||
%token PAR_OPEN
|
||||
%token PASS
|
||||
%token PORT
|
||||
%token PROCEDURE
|
||||
%token PROTO
|
||||
%token FAMILY
|
||||
%token QUICK
|
||||
%token RDR
|
||||
%token RETURN
|
||||
%token RETURNICMP
|
||||
%token RETURNRST
|
||||
%token SEPLINE
|
||||
%token SLASH
|
||||
%token STATE
|
||||
%token TABLE
|
||||
%token TCP
|
||||
%token TO
|
||||
%token TREE
|
||||
%token TYPE
|
||||
%token UDP
|
||||
%token ICMP
|
||||
|
||||
%token <num> HEX
|
||||
%token <str> IDENTIFIER
|
||||
%token <str> IPV4ADDR
|
||||
%token <str> IPV6ADDR
|
||||
%token <num> NUM
|
||||
%token <str> STRING
|
||||
%token <str> TABLE_ID
|
||||
%token <str> VAR_ID
|
||||
|
||||
%type <str> addr, iface_name, moduleargname, list_elem, table_store
|
||||
%type <str> opt_apply
|
||||
%type <num> ifindex, port, opt_quick, on_iface
|
||||
%type <num> block_or_pass, rule_dir, block_opts, family, opt_family
|
||||
%type <num> opt_keep_state, icmp_type, table_type
|
||||
%type <var> addr_or_iface, port_range, iface, icmp_type_and_code
|
||||
%type <var> filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
|
||||
%type <var> modulearg_opts, procs, proc_op, modulearg, moduleargs
|
||||
%type <filtopts> filt_opts, all_or_filt_opts
|
||||
%type <optproto> opt_proto
|
||||
%type <rulegroup> group_attr, group_opt
|
||||
|
||||
%union {
|
||||
char * str;
|
||||
unsigned long num;
|
||||
filt_opts_t filtopts;
|
||||
npfvar_t * var;
|
||||
opt_proto_t optproto;
|
||||
rule_group_t rulegroup;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
input
|
||||
: lines
|
||||
;
|
||||
|
||||
lines
|
||||
: line SEPLINE lines
|
||||
| line
|
||||
;
|
||||
|
||||
line
|
||||
: def
|
||||
| table
|
||||
| nat
|
||||
| group
|
||||
| rproc
|
||||
|
|
||||
;
|
||||
|
||||
def
|
||||
: VAR_ID
|
||||
{
|
||||
cvar = npfvar_create($1);
|
||||
npfvar_add(cvar);
|
||||
}
|
||||
EQ definition
|
||||
{
|
||||
cvar = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
definition
|
||||
: list_elem
|
||||
| listdef
|
||||
;
|
||||
|
||||
listdef
|
||||
: CURLY_OPEN list_elems CURLY_CLOSE
|
||||
;
|
||||
|
||||
list_elems
|
||||
: list_elem COMMA list_elems
|
||||
| list_elem
|
||||
;
|
||||
|
||||
list_elem
|
||||
: IDENTIFIER
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".identifier");
|
||||
npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
|
||||
npfvar_add_elements(cvar, vp);
|
||||
}
|
||||
| STRING
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".string");
|
||||
npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
|
||||
npfvar_add_elements(cvar, vp);
|
||||
}
|
||||
| NUM
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".num");
|
||||
npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
|
||||
npfvar_add_elements(cvar, vp);
|
||||
}
|
||||
| VAR_ID
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".var_id");
|
||||
npfvar_add_element(vp, NPFVAR_VAR_ID, $1, strlen($1) + 1);
|
||||
npfvar_add_elements(cvar, vp);
|
||||
}
|
||||
| addr_and_mask
|
||||
{
|
||||
npfvar_add_elements(cvar, $1);
|
||||
}
|
||||
;
|
||||
|
||||
table
|
||||
: TABLE TABLE_ID TYPE table_type table_store
|
||||
{
|
||||
npfctl_build_table($2, $4, $5);
|
||||
}
|
||||
;
|
||||
|
||||
table_type
|
||||
: HASH { $$ = NPF_TABLE_HASH; }
|
||||
| TREE { $$ = NPF_TABLE_RBTREE; }
|
||||
;
|
||||
|
||||
table_store
|
||||
: TDYNAMIC { $$ = NULL; }
|
||||
| TFILE STRING { $$ = $2; }
|
||||
;
|
||||
|
||||
nat
|
||||
: natdef
|
||||
| binatdef
|
||||
| rdrdef
|
||||
;
|
||||
|
||||
natdef
|
||||
: NAT ifindex filt_opts ARROW addr_or_iface
|
||||
{
|
||||
npfctl_build_nat(NPFCTL_NAT, $2, &$3, $5, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
binatdef
|
||||
: BINAT ifindex filt_opts ARROW addr_or_iface
|
||||
{
|
||||
npfctl_build_nat(NPFCTL_BINAT, $2, &$3, $5, $3.fo_from);
|
||||
}
|
||||
;
|
||||
|
||||
rdrdef
|
||||
: RDR ifindex filt_opts ARROW addr_or_iface port_range
|
||||
{
|
||||
npfctl_build_nat(NPFCTL_RDR, $2, &$3, $5, $6);
|
||||
}
|
||||
;
|
||||
|
||||
rproc
|
||||
: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
|
||||
{
|
||||
npfctl_build_rproc($2, $4);
|
||||
}
|
||||
;
|
||||
|
||||
procs
|
||||
: proc_op SEPLINE procs { $$ = npfvar_add_elements($1, $3); }
|
||||
| proc_op { $$ = $1; }
|
||||
;
|
||||
|
||||
proc_op
|
||||
: IDENTIFIER COLON moduleargs
|
||||
{
|
||||
proc_op_t po;
|
||||
|
||||
po.po_name = xstrdup($1);
|
||||
po.po_opts = $3;
|
||||
$$ = npfvar_create(".proc_ops");
|
||||
npfvar_add_element($$, NPFVAR_PROC_OP, &po, sizeof(po));
|
||||
}
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
moduleargs
|
||||
: modulearg COMMA moduleargs
|
||||
{
|
||||
$$ = npfvar_add_elements($1, $3);
|
||||
}
|
||||
| modulearg { $$ = $1; }
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
modulearg
|
||||
: moduleargname modulearg_opts
|
||||
{
|
||||
module_arg_t ma;
|
||||
|
||||
ma.ma_name = xstrdup($1);
|
||||
ma.ma_opts = $2;
|
||||
$$ = npfvar_create(".module_arg");
|
||||
npfvar_add_element($$, NPFVAR_MODULE_ARG, &ma, sizeof(ma));
|
||||
}
|
||||
;
|
||||
|
||||
moduleargname
|
||||
: STRING { $$ = $1; }
|
||||
| IDENTIFIER { $$ = $1; }
|
||||
;
|
||||
|
||||
modulearg_opts
|
||||
: STRING modulearg_opts
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".modstring");
|
||||
npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
|
||||
$$ = $2 ? npfvar_add_elements($2, vp) : vp;
|
||||
}
|
||||
| IDENTIFIER modulearg_opts
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".modident");
|
||||
npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
|
||||
$$ = $2 ? npfvar_add_elements($2, vp) : vp;
|
||||
}
|
||||
| NUM modulearg_opts
|
||||
{
|
||||
npfvar_t *vp = npfvar_create(".modnum");
|
||||
npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
|
||||
$$ = $2 ? npfvar_add_elements($2, vp) : vp;
|
||||
}
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
group
|
||||
: GROUP PAR_OPEN group_attr PAR_CLOSE
|
||||
{
|
||||
npfctl_build_group($3.rg_name, $3.rg_attr, $3.rg_ifnum);
|
||||
}
|
||||
ruleset
|
||||
;
|
||||
|
||||
group_attr
|
||||
: group_opt COMMA group_attr
|
||||
{
|
||||
$$ = $3;
|
||||
|
||||
if (($1.rg_name && $$.rg_name) ||
|
||||
($1.rg_ifnum && $$.rg_ifnum) ||
|
||||
($1.rg_attr & $$.rg_attr) != 0)
|
||||
yyerror("duplicate group option");
|
||||
|
||||
if ($1.rg_name) {
|
||||
$$.rg_name = $1.rg_name;
|
||||
}
|
||||
if ($1.rg_attr) {
|
||||
$$.rg_attr |= $1.rg_attr;
|
||||
}
|
||||
if ($1.rg_ifnum) {
|
||||
$$.rg_ifnum = $1.rg_ifnum;
|
||||
}
|
||||
}
|
||||
| group_opt { $$ = $1; }
|
||||
;
|
||||
|
||||
group_opt
|
||||
: DEFAULT
|
||||
{
|
||||
$$.rg_name = NULL;
|
||||
$$.rg_ifnum = 0;
|
||||
$$.rg_attr = NPF_RULE_DEFAULT;
|
||||
}
|
||||
| NAME STRING
|
||||
{
|
||||
$$.rg_name = $2;
|
||||
$$.rg_ifnum = 0;
|
||||
$$.rg_attr = 0;
|
||||
}
|
||||
| INTERFACE ifindex
|
||||
{
|
||||
$$.rg_name = NULL;
|
||||
$$.rg_ifnum = $2;
|
||||
$$.rg_attr = 0;
|
||||
}
|
||||
| rule_dir
|
||||
{
|
||||
$$.rg_name = NULL;
|
||||
$$.rg_ifnum = 0;
|
||||
$$.rg_attr = $1;
|
||||
}
|
||||
;
|
||||
|
||||
ruleset
|
||||
: CURLY_OPEN rules CURLY_CLOSE
|
||||
;
|
||||
|
||||
rules
|
||||
: rule SEPLINE rules
|
||||
| rule
|
||||
;
|
||||
|
||||
rule
|
||||
: block_or_pass rule_dir opt_quick on_iface opt_family
|
||||
opt_proto all_or_filt_opts opt_keep_state opt_apply
|
||||
{
|
||||
/*
|
||||
* Arguments: attributes, interface index, address
|
||||
* family, protocol options, filter options.
|
||||
*/
|
||||
npfctl_build_rule($1 | $2 | $3 | $8, $4,
|
||||
$5, &$6, &$7, $9);
|
||||
}
|
||||
|
|
||||
;
|
||||
|
||||
block_or_pass
|
||||
: BLOCK block_opts { $$ = $2; }
|
||||
| PASS { $$ = NPF_RULE_PASS; }
|
||||
;
|
||||
|
||||
rule_dir
|
||||
: IN { $$ = NPF_RULE_IN; }
|
||||
| OUT { $$ = NPF_RULE_OUT; }
|
||||
| { $$ = NPF_RULE_IN | NPF_RULE_OUT; }
|
||||
;
|
||||
|
||||
opt_quick
|
||||
: QUICK { $$ = NPF_RULE_FINAL; }
|
||||
| { $$ = 0; }
|
||||
;
|
||||
|
||||
on_iface
|
||||
: ON ifindex { $$ = $2; }
|
||||
| { $$ = 0; }
|
||||
;
|
||||
|
||||
family
|
||||
: INET { $$ = AF_INET; }
|
||||
| INET6 { $$ = AF_INET6; }
|
||||
;
|
||||
|
||||
opt_proto
|
||||
: PROTO TCP tcp_flags_and_mask
|
||||
{
|
||||
$$.op_proto = IPPROTO_TCP;
|
||||
$$.op_opts = $3;
|
||||
}
|
||||
| PROTO ICMP icmp_type_and_code
|
||||
{
|
||||
$$.op_proto = IPPROTO_ICMP;
|
||||
$$.op_opts = $3;
|
||||
}
|
||||
| PROTO UDP
|
||||
{
|
||||
$$.op_proto = IPPROTO_UDP;
|
||||
$$.op_opts = NULL;
|
||||
}
|
||||
|
|
||||
{
|
||||
$$.op_proto = -1;
|
||||
$$.op_opts = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
opt_family
|
||||
: FAMILY family { $$ = $2; }
|
||||
| { $$ = AF_UNSPEC; }
|
||||
;
|
||||
|
||||
all_or_filt_opts
|
||||
: ALL
|
||||
{
|
||||
$$.fo_from = NULL;
|
||||
$$.fo_from_port_range = NULL;
|
||||
$$.fo_to = NULL;
|
||||
$$.fo_to_port_range = NULL;
|
||||
}
|
||||
| filt_opts { $$ = $1; }
|
||||
;
|
||||
|
||||
opt_keep_state
|
||||
: KEEP STATE { $$ = NPF_RULE_KEEPSTATE; }
|
||||
| { $$ = 0; }
|
||||
;
|
||||
|
||||
opt_apply
|
||||
: APPLY STRING { $$ = $2; }
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
block_opts
|
||||
: RETURNRST { $$ = NPF_RULE_RETRST; }
|
||||
| RETURNICMP { $$ = NPF_RULE_RETICMP; }
|
||||
| RETURN { $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
|
||||
| { $$ = 0; }
|
||||
;
|
||||
|
||||
filt_opts
|
||||
: FROM filt_addr port_range TO filt_addr port_range
|
||||
{
|
||||
$$.fo_from = $2;
|
||||
$$.fo_from_port_range = $3;
|
||||
$$.fo_to = $5;
|
||||
$$.fo_to_port_range = $6;
|
||||
}
|
||||
| FROM filt_addr port_range
|
||||
{
|
||||
$$.fo_from = $2;
|
||||
$$.fo_from_port_range = $3;
|
||||
$$.fo_to = NULL;
|
||||
$$.fo_to_port_range = NULL;
|
||||
}
|
||||
| TO filt_addr port_range
|
||||
{
|
||||
$$.fo_from = NULL;
|
||||
$$.fo_from_port_range = NULL;
|
||||
$$.fo_to = $2;
|
||||
$$.fo_to_port_range = $3;
|
||||
}
|
||||
;
|
||||
|
||||
filt_addr
|
||||
: iface { $$ = $1; }
|
||||
| addr_and_mask { $$ = $1; }
|
||||
| TABLE_ID { $$ = npfctl_parse_table_id($1); }
|
||||
| ANY { $$ = NULL; }
|
||||
;
|
||||
|
||||
addr_and_mask
|
||||
: addr SLASH NUM
|
||||
{
|
||||
$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
|
||||
}
|
||||
| addr SLASH HEX
|
||||
{
|
||||
$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
|
||||
}
|
||||
| addr SLASH addr
|
||||
{
|
||||
$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
|
||||
}
|
||||
| addr
|
||||
{
|
||||
$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
addr_or_iface
|
||||
: addr_and_mask { assert($1 != NULL); $$ = $1; }
|
||||
| iface { assert($1 != NULL); $$ = $1; }
|
||||
;
|
||||
|
||||
addr
|
||||
: IPV4ADDR { $$ = $1; }
|
||||
| IPV6ADDR { $$ = $1; }
|
||||
;
|
||||
|
||||
|
||||
port_range
|
||||
: PORT port /* just port */
|
||||
{
|
||||
$$ = npfctl_parse_port_range($2, $2);
|
||||
}
|
||||
| PORT port MINUS port /* port from:to */
|
||||
{
|
||||
$$ = npfctl_parse_port_range($2, $4);
|
||||
}
|
||||
|
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
port
|
||||
: NUM { $$ = htons($1); }
|
||||
| IDENTIFIER { $$ = npfctl_portno($1); }
|
||||
| VAR_ID
|
||||
{
|
||||
char *s = npfvar_expand_string(npfvar_lookup($1));
|
||||
$$ = npfctl_portno(s);
|
||||
}
|
||||
;
|
||||
|
||||
icmp_type_and_code
|
||||
: ICMPTYPE icmp_type
|
||||
{
|
||||
$$ = npfctl_parse_icmp($2, -1);
|
||||
}
|
||||
| ICMPTYPE icmp_type CODE NUM
|
||||
{
|
||||
$$ = npfctl_parse_icmp($2, $4);
|
||||
}
|
||||
| ICMPTYPE icmp_type CODE IDENTIFIER
|
||||
{
|
||||
$$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, $4));
|
||||
}
|
||||
| ICMPTYPE icmp_type CODE VAR_ID
|
||||
{
|
||||
char *s = npfvar_expand_string(npfvar_lookup($4));
|
||||
$$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, s));
|
||||
}
|
||||
|
|
||||
{
|
||||
$$ = npfctl_parse_icmp(-1, -1);
|
||||
}
|
||||
;
|
||||
|
||||
tcp_flags_and_mask
|
||||
: FLAGS tcp_flags SLASH tcp_flags
|
||||
{
|
||||
npfvar_add_elements($2, $4);
|
||||
$$ = $2;
|
||||
}
|
||||
| FLAGS tcp_flags
|
||||
{
|
||||
char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
|
||||
npfvar_add_elements($2, npfctl_parse_tcpflag(s));
|
||||
$$ = $2;
|
||||
}
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
|
||||
tcp_flags
|
||||
: IDENTIFIER { $$ = npfctl_parse_tcpflag($1); }
|
||||
;
|
||||
|
||||
icmp_type
|
||||
: NUM { $$ = $1; }
|
||||
| IDENTIFIER { $$ = npfctl_icmptype($1); }
|
||||
| VAR_ID
|
||||
{
|
||||
char *s = npfvar_expand_string(npfvar_lookup($1));
|
||||
$$ = npfctl_icmptype(s);
|
||||
}
|
||||
;
|
||||
|
||||
iface
|
||||
: iface_name
|
||||
{
|
||||
$$ = npfctl_parse_iface($1);
|
||||
}
|
||||
| VAR_ID
|
||||
{
|
||||
npfvar_t *vp = npfvar_lookup($1);
|
||||
const int type = npfvar_get_type(vp);
|
||||
|
||||
switch (type) {
|
||||
case NPFVAR_STRING:
|
||||
$$ = npfctl_parse_iface(npfvar_expand_string(vp));
|
||||
break;
|
||||
case NPFVAR_FAM:
|
||||
$$ = vp;
|
||||
break;
|
||||
case -1:
|
||||
yyerror("undefined variable '%s' for interface", $1);
|
||||
break;
|
||||
default:
|
||||
yyerror("wrong variable '%s' type '%s' or interface",
|
||||
$1, npfvar_type(type));
|
||||
$$ = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
ifindex
|
||||
: iface_name
|
||||
{
|
||||
$$ = npfctl_find_ifindex($1);
|
||||
}
|
||||
| VAR_ID
|
||||
{
|
||||
npfvar_t *vp = npfvar_lookup($1);
|
||||
const int type = npfvar_get_type(vp);
|
||||
|
||||
switch (type) {
|
||||
case NPFVAR_STRING:
|
||||
$$ = npfctl_find_ifindex(npfvar_expand_string(vp));
|
||||
break;
|
||||
case -1:
|
||||
yyerror("undefined variable '%s' for interface", $1);
|
||||
break;
|
||||
default:
|
||||
yyerror("wrong variable '%s' type '%s' for interface",
|
||||
$1, npfvar_type(type));
|
||||
$$ = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
iface_name
|
||||
: IDENTIFIER { $$ = $1; }
|
||||
| STRING { $$ = $1; }
|
||||
;
|
||||
|
||||
%%
|
@ -1,875 +0,0 @@
|
||||
/* $NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* XXX: This needs clean-up!
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
/*
|
||||
* Few ugly helpers.
|
||||
*/
|
||||
|
||||
#define PARSE_ERR() (-1)
|
||||
|
||||
#define PARSE_TOKEN(_arg_) \
|
||||
if ((p = strtok_r(_arg_, " \t", &sptr)) == NULL) \
|
||||
return PARSE_ERR();
|
||||
|
||||
#define PARSE_FIRST_TOKEN() PARSE_TOKEN(p)
|
||||
#define PARSE_NEXT_TOKEN() PARSE_TOKEN(NULL)
|
||||
#define PARSE_NEXT_TOKEN_NOCHECK() p = strtok_r(NULL, " \t", &sptr)
|
||||
|
||||
/*
|
||||
* Global variable list.
|
||||
*
|
||||
* npfctl_lookup_varlist(): lookups the list by key.
|
||||
*/
|
||||
|
||||
static var_t * var_list = NULL;
|
||||
|
||||
static var_t *
|
||||
npfctl_lookup_varlist(char *key)
|
||||
{
|
||||
var_t *it;
|
||||
|
||||
for (it = var_list; it != NULL; it = it->v_next)
|
||||
if (strcmp(it->v_key, key) == 0)
|
||||
break;
|
||||
return it;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parsevalue: helper function to parse a value.
|
||||
*
|
||||
* => Value could be a single element (no quotes),
|
||||
* => or an array of elements between { }.
|
||||
*/
|
||||
static var_t *
|
||||
npfctl_parsevalue(char *buf)
|
||||
{
|
||||
var_t *vr = NULL;
|
||||
element_t *el = NULL, *it = NULL;
|
||||
char *p = buf, *tend, *sptr;
|
||||
|
||||
switch (*p) {
|
||||
case '$':
|
||||
/* Definition - lookup. */
|
||||
vr = npfctl_lookup_varlist(++p);
|
||||
if (vr == NULL) {
|
||||
errx(EXIT_FAILURE, "variable '%s' is not defined", p);
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
/* Array. */
|
||||
vr = zalloc(sizeof(var_t));
|
||||
p = strtok_r(buf, ", \t", &sptr);
|
||||
while (p) {
|
||||
if (*p == '}')
|
||||
break;
|
||||
el = zalloc(sizeof(element_t));
|
||||
el->e_data = xstrdup(p);
|
||||
el->e_next = it;
|
||||
vr->v_count++;
|
||||
it = el;
|
||||
p = strtok_r(NULL, ", \t", &sptr);
|
||||
}
|
||||
if (el) {
|
||||
vr->v_type = VAR_ARRAY;
|
||||
vr->v_elements = el;
|
||||
} else {
|
||||
free(vr);
|
||||
vr = NULL;
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
/* Table. */
|
||||
if ((tend = strchr(++p, '>')) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*tend = '\0';
|
||||
if (!npf_table_exists_p(npf_conf, (u_int)atoi(p))) {
|
||||
errx(EXIT_FAILURE, "table '%s' is not defined", p);
|
||||
}
|
||||
vr = zalloc(sizeof(var_t));
|
||||
vr->v_type = VAR_TABLE;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
/* Data. */
|
||||
el = zalloc(sizeof(element_t));
|
||||
el->e_data = xstrdup(p);
|
||||
if (vr == NULL) {
|
||||
vr = zalloc(sizeof(var_t));
|
||||
vr->v_type = VAR_SINGLE;
|
||||
}
|
||||
vr->v_elements = el;
|
||||
vr->v_count = 1;
|
||||
}
|
||||
return vr;
|
||||
}
|
||||
|
||||
static char *
|
||||
npfctl_val_single(var_t *v, char *p)
|
||||
{
|
||||
element_t *el;
|
||||
|
||||
if (v->v_type != VAR_SINGLE) {
|
||||
errx(EXIT_FAILURE, "multiple elements in variable '%s'", p);
|
||||
}
|
||||
el = v->v_elements;
|
||||
return el->e_data;
|
||||
}
|
||||
|
||||
static u_int
|
||||
npfctl_val_interface(var_t *v, char *p, bool reqaddr)
|
||||
{
|
||||
char *iface = npfctl_val_single(v, p);
|
||||
u_int if_idx;
|
||||
|
||||
if (iface == NULL ||
|
||||
((npfctl_getif(iface, &if_idx, reqaddr, AF_INET) == NULL) &&
|
||||
(npfctl_getif(iface, &if_idx, reqaddr, AF_INET6) == NULL))) {
|
||||
errx(EXIT_FAILURE, "invalid interface '%s'", iface);
|
||||
}
|
||||
return if_idx;
|
||||
}
|
||||
|
||||
static int
|
||||
npfctl_parsenorm(char *buf, nl_rproc_t *rp)
|
||||
{
|
||||
char *p = buf, *sptr;
|
||||
int minttl = 0, maxmss = 0;
|
||||
bool rnd = false, no_df = false;
|
||||
|
||||
p = strtok_r(buf, ", \t", &sptr);
|
||||
if (p == NULL) {
|
||||
return -1;
|
||||
}
|
||||
do {
|
||||
if (strcmp(p, "random-id") == 0) {
|
||||
rnd = true;
|
||||
} else if (strcmp(p, "min-ttl") == 0) {
|
||||
p = strtok_r(NULL, ", \t", &sptr);
|
||||
minttl = atoi(p);
|
||||
} else if (strcmp(p, "max-mss") == 0) {
|
||||
p = strtok_r(NULL, ", \t", &sptr);
|
||||
maxmss = atoi(p);
|
||||
} else if (strcmp(p, "no-df") == 0) {
|
||||
no_df = true;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} while ((p = strtok_r(NULL, ", \t", &sptr)) != 0);
|
||||
|
||||
return _npf_rproc_setnorm(rp, rnd, no_df, minttl, maxmss);
|
||||
}
|
||||
|
||||
static int
|
||||
npfctl_parserproc(char *buf, nl_rproc_t **rp)
|
||||
{
|
||||
char *p = buf, *end;
|
||||
|
||||
DPRINTF(("rproc\t|%s|\n", buf));
|
||||
|
||||
if ((p = strchr(p, '"')) == NULL)
|
||||
return -1;
|
||||
if ((end = strchr(++p, '"')) == NULL)
|
||||
return -1;
|
||||
*end = '\0';
|
||||
|
||||
*rp = npf_rproc_create(p);
|
||||
if (*rp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return npf_rproc_insert(npf_conf, *rp) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
npfctl_parserproc_lines(char *buf, nl_rproc_t *rp)
|
||||
{
|
||||
char *p = buf, *sptr;
|
||||
|
||||
DPRINTF(("rproc\t|%s|\n", p));
|
||||
PARSE_FIRST_TOKEN();
|
||||
|
||||
/* log <interface> */
|
||||
if (strcmp(p, "log") == 0) {
|
||||
var_t *ifvar;
|
||||
u_int if_idx;
|
||||
|
||||
PARSE_NEXT_TOKEN();
|
||||
if ((ifvar = npfctl_parsevalue(p)) == NULL)
|
||||
return PARSE_ERR();
|
||||
if_idx = npfctl_val_interface(ifvar, p, false);
|
||||
(void)_npf_rproc_setlog(rp, if_idx);
|
||||
|
||||
} else if (strcmp(p, "normalize") == 0) {
|
||||
/* normalize ( .. ) */
|
||||
p = strtok_r(NULL, "()", &sptr);
|
||||
if (p == NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
if (npfctl_parsenorm(p, rp)) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parserule: main routine to parse a rule. Syntax:
|
||||
*
|
||||
* { pass | block } [ in | out ] [ quick ]
|
||||
* [on <if>] [inet | inet6 ] proto <array>
|
||||
* from <addr/mask> port <port(s)|range>
|
||||
* to <addr/mask> port <port(s)|range>
|
||||
* [ keep state ] [ apply "<rproc>" ]
|
||||
*/
|
||||
static int
|
||||
npfctl_parserule(char *buf, nl_rule_t *parent)
|
||||
{
|
||||
var_t *from_v = NULL, *fports = NULL, *to_v = NULL, *tports = NULL;
|
||||
char *p, *sptr, *proto = NULL, *tcp_flags = NULL, *rproc = NULL;
|
||||
int icmp_type = -1, icmp_code = -1;
|
||||
bool icmp = false, tcp = false;
|
||||
u_int if_idx = 0;
|
||||
int ret, attr = 0;
|
||||
nl_rule_t *rl;
|
||||
sa_family_t addrfamily = AF_UNSPEC;
|
||||
|
||||
DPRINTF(("rule\t|%s|\n", buf));
|
||||
|
||||
p = buf;
|
||||
PARSE_FIRST_TOKEN();
|
||||
|
||||
/* pass or block (mandatory) */
|
||||
if (strcmp(p, "block") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
/* return-rst or return-icmp */
|
||||
if (strcmp(p, "return-rst") == 0) {
|
||||
attr |= NPF_RULE_RETRST;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else if (strcmp(p, "return-icmp") == 0) {
|
||||
attr |= NPF_RULE_RETICMP;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else if (strcmp(p, "return") == 0) {
|
||||
attr |= NPF_RULE_RETRST | NPF_RULE_RETICMP;
|
||||
PARSE_NEXT_TOKEN();
|
||||
}
|
||||
} else if (strcmp(p, "pass") == 0) {
|
||||
attr |= NPF_RULE_PASS;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
|
||||
/* in or out */
|
||||
if (strcmp(p, "in") == 0) {
|
||||
attr |= NPF_RULE_IN;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else if (strcmp(p, "out") == 0) {
|
||||
attr |= NPF_RULE_OUT;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else {
|
||||
attr |= (NPF_RULE_IN | NPF_RULE_OUT);
|
||||
}
|
||||
|
||||
/* quick */
|
||||
if (strcmp(p, "quick") == 0) {
|
||||
attr |= NPF_RULE_FINAL;
|
||||
PARSE_NEXT_TOKEN();
|
||||
}
|
||||
|
||||
/* on <interface> */
|
||||
if (strcmp(p, "on") == 0) {
|
||||
var_t *ifvar;
|
||||
|
||||
PARSE_NEXT_TOKEN();
|
||||
if ((ifvar = npfctl_parsevalue(p)) == NULL)
|
||||
return PARSE_ERR();
|
||||
if_idx = npfctl_val_interface(ifvar, p, true);
|
||||
PARSE_NEXT_TOKEN();
|
||||
}
|
||||
|
||||
if (strcmp(p, "inet") == 0) {
|
||||
addrfamily = AF_INET;
|
||||
PARSE_NEXT_TOKEN();
|
||||
} else if (strcmp(p, "inet6") == 0) {
|
||||
addrfamily = AF_INET6;
|
||||
PARSE_NEXT_TOKEN();
|
||||
}
|
||||
|
||||
/* proto <proto> */
|
||||
if (strcmp(p, "proto") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
var_t *pvar = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN();
|
||||
if (pvar->v_type != VAR_SINGLE) {
|
||||
errx(EXIT_FAILURE, "only one protocol can be specified");
|
||||
}
|
||||
element_t *el = pvar->v_elements;
|
||||
proto = el->e_data;
|
||||
/* Determine TCP, ICMP. */
|
||||
tcp = (strcmp(proto, "tcp") == 0);
|
||||
icmp = (strcmp(proto, "icmp") == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Can be: "all", "from", "to" or "from + to".
|
||||
*/
|
||||
|
||||
if (strcmp(p, "all") == 0) {
|
||||
/* Should be no "from"/"to" after it. */
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
goto last;
|
||||
}
|
||||
|
||||
ret = PARSE_ERR();
|
||||
|
||||
/* from <addr> port <port | range> */
|
||||
if (strcmp(p, "from") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
if (addrfamily == AF_UNSPEC)
|
||||
addrfamily = npfctl_get_addrfamily(p);
|
||||
from_v = npfctl_parsevalue(p);
|
||||
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
if (p && strcmp(p, "port") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
fports = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* to <addr> port <port | range> */
|
||||
if (p && strcmp(p, "to") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
if (addrfamily == AF_UNSPEC)
|
||||
addrfamily = npfctl_get_addrfamily(p);
|
||||
to_v = npfctl_parsevalue(p);
|
||||
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
if (p && strcmp(p, "port") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
tports = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* flags <fl/mask> */
|
||||
if (p && strcmp(p, "flags") == 0) {
|
||||
if (icmp) {
|
||||
errx(EXIT_FAILURE,
|
||||
"TCP flags used with ICMP protocol");
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
var_t *tfvar = npfctl_parsevalue(p);
|
||||
tcp_flags = npfctl_val_single(tfvar, p);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
|
||||
/* icmp-type <t> code <c> */
|
||||
if (p && strcmp(p, "icmp-type") == 0) {
|
||||
if (tcp) {
|
||||
errx(EXIT_FAILURE,
|
||||
"ICMP options used with TCP protocol");
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
icmp_type = atoi(p);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
if (p && strcmp(p, "code") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
icmp_code = atoi(p);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
}
|
||||
last:
|
||||
/* keep state */
|
||||
if (p && strcmp(p, "keep") == 0) {
|
||||
attr |= NPF_RULE_KEEPSTATE;
|
||||
PARSE_NEXT_TOKEN();
|
||||
if (p == NULL || strcmp(p, "state") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
|
||||
/* apply "<rproc>" */
|
||||
if (p && strcmp(p, "apply") == 0) {
|
||||
char *end;
|
||||
PARSE_NEXT_TOKEN();
|
||||
if ((p = strchr(p, '"')) == NULL)
|
||||
return PARSE_ERR();
|
||||
if ((end = strchr(++p, '"')) == NULL)
|
||||
return PARSE_ERR();
|
||||
*end = '\0';
|
||||
if (!npf_rproc_exists_p(npf_conf, p)) {
|
||||
errx(EXIT_FAILURE, "procedure '%s' is not defined", p);
|
||||
}
|
||||
rproc = p;
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
|
||||
/* Should have nothing more. */
|
||||
if (p != NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the rule attributes and interface. Generate all protocol data.
|
||||
*/
|
||||
rl = npf_rule_create(NULL, attr, if_idx);
|
||||
npfctl_rule_ncode(rl, proto, tcp_flags, icmp_type, icmp_code,
|
||||
from_v, addrfamily, fports, to_v, tports);
|
||||
if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
|
||||
errx(EXIT_FAILURE, "procedure '%s' is not defined", rproc);
|
||||
}
|
||||
npf_rule_insert(npf_conf, parent, rl, NPF_PRI_NEXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parsegroup: parse group definition. Syntax:
|
||||
*
|
||||
* group (name <name>, interface <if>, [ in | out ]) { <rules> }
|
||||
* group (default) { <rules> }
|
||||
*/
|
||||
|
||||
#define GROUP_ATTRS (NPF_RULE_PASS | NPF_RULE_FINAL)
|
||||
|
||||
static int
|
||||
npfctl_parsegroup(char *buf, nl_rule_t **rl)
|
||||
{
|
||||
char *p = buf, *end, *sptr, *rname = NULL;
|
||||
u_int if_idx = 0;
|
||||
int attr_dir;
|
||||
|
||||
DPRINTF(("group\t|%s|\n", buf));
|
||||
|
||||
p = strchr(p, '(');
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
*p = '\0';
|
||||
end = strchr(++p, ')');
|
||||
if (end == NULL)
|
||||
return -1;
|
||||
*end = '\0';
|
||||
if (strchr(++end, '{') == NULL)
|
||||
return -1;
|
||||
while (isspace((unsigned char)*p))
|
||||
p++;
|
||||
|
||||
/*
|
||||
* If default group - no other options.
|
||||
*/
|
||||
if (strcmp(p, "default") == 0) {
|
||||
attr_dir = NPF_RULE_DEFAULT | (NPF_RULE_IN | NPF_RULE_OUT);
|
||||
goto done;
|
||||
}
|
||||
|
||||
PARSE_FIRST_TOKEN();
|
||||
|
||||
/* Name of the group (mandatory). */
|
||||
if (strcmp(p, "name") == 0) {
|
||||
PARSE_NEXT_TOKEN()
|
||||
if (*p != '"')
|
||||
return -1;
|
||||
rname = ++p;
|
||||
if ((p = strchr(rname, '"')) == NULL)
|
||||
return -1;
|
||||
*p = '\0';
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
|
||||
/* Interface for this group (optional). */
|
||||
if (p && strcmp(p, "interface") == 0) {
|
||||
var_t *ifvar;
|
||||
PARSE_NEXT_TOKEN();
|
||||
if ((ifvar = npfctl_parsevalue(p)) == NULL)
|
||||
return -1;
|
||||
if_idx = npfctl_val_interface(ifvar, p, true);
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
}
|
||||
|
||||
/* Direction (optional). */
|
||||
if (p) {
|
||||
if (strcmp(p, "in") == 0)
|
||||
attr_dir = NPF_RULE_IN;
|
||||
else if (strcmp(p, "out") == 0)
|
||||
attr_dir = NPF_RULE_OUT;
|
||||
else
|
||||
return -1;
|
||||
} else {
|
||||
attr_dir = NPF_RULE_IN | NPF_RULE_OUT;
|
||||
}
|
||||
done:
|
||||
*rl = npf_rule_create(rname, GROUP_ATTRS | attr_dir, if_idx);
|
||||
if (*rl == NULL) {
|
||||
return -1;
|
||||
}
|
||||
npf_rule_insert(npf_conf, NULL, *rl, NPF_PRI_NEXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parsetable: parse table definition.
|
||||
*
|
||||
* table <num> type <t> [ dynamic | file <path> ]
|
||||
*/
|
||||
static int
|
||||
npfctl_parsetable(char *buf)
|
||||
{
|
||||
char *p, *sptr, *fname;
|
||||
unsigned int id, type;
|
||||
nl_table_t *tl;
|
||||
|
||||
DPRINTF(("table\t|%s|\n", buf));
|
||||
|
||||
/* Name of the set. */
|
||||
if ((p = strchr(buf, '"')) == NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
id = atoi(++p);
|
||||
p = strchr(p, '"');
|
||||
*p++ = '\0';
|
||||
|
||||
PARSE_FIRST_TOKEN();
|
||||
|
||||
/* Table type (mandatory). */
|
||||
if (strcmp(p, "type") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN_NOCHECK();
|
||||
if (p == NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
if (strcmp(p, "hash") == 0) {
|
||||
type = NPF_TABLE_HASH;
|
||||
} else if (strcmp(p, "tree") == 0) {
|
||||
type = NPF_TABLE_RBTREE;
|
||||
} else {
|
||||
errx(EXIT_FAILURE, "invalid table type '%s'", p);
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
/*
|
||||
* Setup the table.
|
||||
*/
|
||||
tl = npf_table_create(id, type);
|
||||
if (npf_table_insert(npf_conf, tl)) {
|
||||
errx(EXIT_FAILURE, "table '%d' is already defined\n", id);
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
|
||||
/* Dynamic. */
|
||||
if (strcmp(p, "dynamic") == 0) {
|
||||
/* No other options. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* File. */
|
||||
if (strcmp(p, "file") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
fname = ++p;
|
||||
p = strchr(p, '"');
|
||||
*p = '\0';
|
||||
|
||||
/* Fill the table. */
|
||||
npfctl_fill_table(tl, fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parse_nat: parse NAT policy definition.
|
||||
*
|
||||
* [bi]nat <if> from <net> to <net/addr> -> <ip>
|
||||
* rdr <if> from <net> to <addr> -> <ip>
|
||||
* nat <if> dynamic "<name>"
|
||||
*/
|
||||
static int
|
||||
npfctl_parse_nat(char *buf)
|
||||
{
|
||||
var_t *ifvar, *from_v, *to_v, *raddr_v;
|
||||
var_t *tports = NULL, *rport_v = NULL;
|
||||
char *p, *sptr, *raddr_s, *rport_s;
|
||||
npf_addr_t raddr;
|
||||
npf_netmask_t dummy;
|
||||
bool binat, rdr;
|
||||
nl_nat_t *nat;
|
||||
u_int if_idx;
|
||||
|
||||
DPRINTF(("[bi]nat/rdr\t|%s|\n", buf));
|
||||
binat = (strncmp(buf, "binat", 5) == 0);
|
||||
rdr = (strncmp(buf, "rdr", 3) == 0);
|
||||
|
||||
if ((p = strchr(buf, ' ')) == NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_FIRST_TOKEN();
|
||||
|
||||
/* <interface> */
|
||||
if ((ifvar = npfctl_parsevalue(p)) == NULL) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
if_idx = npfctl_val_interface(ifvar, p, true);
|
||||
PARSE_NEXT_TOKEN();
|
||||
|
||||
/* dynamic <name> */
|
||||
if (!binat && !rdr && strcmp(p, "dynamic") == 0) {
|
||||
char *nname;
|
||||
|
||||
/* Parse name. */
|
||||
PARSE_NEXT_TOKEN()
|
||||
if (*p != '"')
|
||||
return PARSE_ERR();
|
||||
nname = ++p;
|
||||
if ((p = strchr(nname, '"')) == NULL)
|
||||
return PARSE_ERR();
|
||||
*p = '\0';
|
||||
|
||||
/* Create a rule and insert into the NAT list. */
|
||||
nat = npf_rule_create(nname, NPF_RULE_PASS | NPF_RULE_FINAL |
|
||||
NPF_RULE_OUT | NPF_RULE_IN, if_idx);
|
||||
(void)npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* from <addr> */
|
||||
if (strcmp(p, "from") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
from_v = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN();
|
||||
|
||||
/* to <addr> */
|
||||
if (strcmp(p, "to") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
to_v = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN();
|
||||
|
||||
if (rdr && strcmp(p, "port") == 0) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
tports = npfctl_parsevalue(p);
|
||||
PARSE_NEXT_TOKEN();
|
||||
}
|
||||
|
||||
/* -> <ip> */
|
||||
if (strcmp(p, "->") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
raddr_v = npfctl_parsevalue(p);
|
||||
raddr_s = npfctl_val_single(raddr_v, p);
|
||||
npfctl_parse_cidr(raddr_s, npfctl_get_addrfamily(raddr_s), &raddr, &dummy);
|
||||
|
||||
if (rdr) {
|
||||
PARSE_NEXT_TOKEN();
|
||||
if (strcmp(p, "port") != 0) {
|
||||
return PARSE_ERR();
|
||||
}
|
||||
PARSE_NEXT_TOKEN();
|
||||
rport_v = npfctl_parsevalue(p);
|
||||
rport_s = npfctl_val_single(rport_v, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup NAT policy (rule as filter and extra info), which is
|
||||
* Outbound NAT (NPF_NATOUT). Unless it is a redirect rule,
|
||||
* in which case it is Inbound NAT with specified port.
|
||||
*
|
||||
* XXX mess
|
||||
*/
|
||||
if (!rdr) {
|
||||
nat = npf_nat_create(NPF_NATOUT,
|
||||
binat ? 0 : (NPF_NAT_PORTS | NPF_NAT_PORTMAP),
|
||||
if_idx, &raddr, AF_INET, 0);
|
||||
} else {
|
||||
in_port_t rport;
|
||||
bool range;
|
||||
|
||||
if (!npfctl_parse_port(rport_s, &range, &rport, &rport)) {
|
||||
errx(EXIT_FAILURE, "invalid service '%s'", rport_s);
|
||||
}
|
||||
if (range) {
|
||||
errx(EXIT_FAILURE, "range is not supported for 'rdr'");
|
||||
}
|
||||
nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
|
||||
if_idx, &raddr, AF_INET, rport);
|
||||
}
|
||||
npfctl_rule_ncode(nat, NULL, NULL, -1, -1, from_v, AF_INET, NULL, to_v, tports);
|
||||
(void)npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
|
||||
|
||||
/*
|
||||
* For bi-directional NAT case, create and setup additional
|
||||
* Inbound NAT (NPF_NATIN) policy. Note that translation address
|
||||
* is local IP, and filter criteria is inverted accordingly.
|
||||
*
|
||||
* XXX mess
|
||||
*/
|
||||
if (binat) {
|
||||
char *taddr_s = npfctl_val_single(from_v, NULL);
|
||||
npf_addr_t taddr;
|
||||
nl_nat_t *bn;
|
||||
|
||||
npfctl_parse_cidr(taddr_s, npfctl_get_addrfamily(taddr_s), &taddr, &dummy);
|
||||
bn = npf_nat_create(NPF_NATIN, 0, if_idx, &taddr, AF_INET, 0);
|
||||
npfctl_rule_ncode(bn, NULL, NULL, -1, -1,
|
||||
to_v, AF_INET, NULL, raddr_v, NULL);
|
||||
(void)npf_nat_insert(npf_conf, bn, NPF_PRI_NEXT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npfctl_parsevar: parse defined variable.
|
||||
*
|
||||
* => Assigned value should be with double quotes (").
|
||||
* => Value can be an array, use npf_parsevalue().
|
||||
* => Insert variable into the global list.
|
||||
*/
|
||||
static int
|
||||
npfctl_parsevar(char *buf)
|
||||
{
|
||||
char *s = buf, *p, *key;
|
||||
var_t *vr;
|
||||
|
||||
DPRINTF(("def\t|%s|\n", buf));
|
||||
|
||||
if ((p = strpbrk(s, "= \t")) == NULL)
|
||||
return PARSE_ERR();
|
||||
|
||||
/* Validation of '='. */
|
||||
if (*p != '=' && strchr(p, '=') == NULL)
|
||||
return PARSE_ERR();
|
||||
*p = '\0';
|
||||
key = s;
|
||||
|
||||
/* Check for duplicates. */
|
||||
if (npfctl_lookup_varlist(key))
|
||||
return PARSE_ERR();
|
||||
|
||||
/* Parse quotes before. */
|
||||
if ((s = strchr(p + 1, '"')) == NULL)
|
||||
return PARSE_ERR();
|
||||
if ((p = strchr(++s, '"')) == NULL)
|
||||
return PARSE_ERR();
|
||||
*p = '\0';
|
||||
|
||||
if ((vr = npfctl_parsevalue(s)) == NULL)
|
||||
return PARSE_ERR();
|
||||
vr->v_key = xstrdup(key);
|
||||
vr->v_next = var_list;
|
||||
var_list = vr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* npf_parseline: main function parsing a single configuration line.
|
||||
*
|
||||
* Distinguishes 'group', rule (in-group), 'procedure', in-procedure,
|
||||
* 'table' and definitions. Tracks begin-end of the group and procedure
|
||||
* i.e. in-group or in-procedure states.
|
||||
*/
|
||||
int
|
||||
npf_parseline(char *buf)
|
||||
{
|
||||
static nl_rule_t *curgr = NULL;
|
||||
static nl_rproc_t *currp = NULL;
|
||||
char *p = buf;
|
||||
int ret;
|
||||
|
||||
/* Skip empty lines and comments. */
|
||||
while (isspace((unsigned char)*p))
|
||||
p++;
|
||||
if (*p == '\0' || *p == '\n' || *p == '#')
|
||||
return 0;
|
||||
|
||||
/* At first, check if inside the group or rproc. */
|
||||
if (curgr) {
|
||||
/* End of the group. */
|
||||
if (*p == '}') {
|
||||
curgr = NULL;
|
||||
return 0;
|
||||
}
|
||||
/* Rule. */
|
||||
ret = npfctl_parserule(p, curgr);
|
||||
|
||||
} else if (currp) {
|
||||
/* End of the procedure. */
|
||||
if (*p == '}') {
|
||||
currp = NULL;
|
||||
return 0;
|
||||
}
|
||||
/* Procedure contents. */
|
||||
ret = npfctl_parserproc_lines(p, currp);
|
||||
|
||||
} else if (strncmp(p, "group", 5) == 0) {
|
||||
/* Group. */
|
||||
ret = npfctl_parsegroup(p, &curgr);
|
||||
|
||||
} else if (strncmp(p, "procedure", 9) == 0) {
|
||||
/* Rule procedure. */
|
||||
ret = npfctl_parserproc(p, &currp);
|
||||
|
||||
} else if (strncmp(p, "table", 5) == 0) {
|
||||
/* Table. */
|
||||
ret = npfctl_parsetable(p);
|
||||
|
||||
} else if (strncmp(p, "nat", 3) == 0 || strncmp(p, "rdr", 3) == 0 ||
|
||||
strncmp(p, "binat", 5) == 0) {
|
||||
/* NAT policy. */
|
||||
ret = npfctl_parse_nat(p);
|
||||
|
||||
} else {
|
||||
/* Defined variable or syntax error. */
|
||||
ret = npfctl_parsevar(p);
|
||||
}
|
||||
return ret;
|
||||
}
|
155
usr.sbin/npf/npfctl/npf_scan.l
Normal file
155
usr.sbin/npf/npfctl/npf_scan.l
Normal file
@ -0,0 +1,155 @@
|
||||
/* $NetBSD: npf_scan.l,v 1.1 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Martin Husemann.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "npfctl.h"
|
||||
#include "npf_parse.h"
|
||||
|
||||
int yycolumn;
|
||||
|
||||
#define YY_USER_ACTION yycolumn += yyleng;
|
||||
|
||||
%}
|
||||
|
||||
%option noyywrap nounput noinput
|
||||
|
||||
ID [a-zA-Z_][a-zA-Z_0-9]*
|
||||
NID [a-zA-Z_0-9]+
|
||||
NUMBER [0-9]+
|
||||
|
||||
%%
|
||||
table return TABLE;
|
||||
type return TYPE;
|
||||
hash return HASH;
|
||||
tree return TREE;
|
||||
dynamic return TDYNAMIC;
|
||||
file return TFILE;
|
||||
nat return NAT;
|
||||
binat return BINAT;
|
||||
"->" return ARROW;
|
||||
"-" return MINUS;
|
||||
rdr return RDR;
|
||||
procedure return PROCEDURE;
|
||||
\n yylineno++; yycolumn = 0; return SEPLINE;
|
||||
; return SEPLINE;
|
||||
name return NAME;
|
||||
group return GROUP;
|
||||
default return DEFAULT;
|
||||
in return IN;
|
||||
out return OUT;
|
||||
interface return INTERFACE;
|
||||
all return ALL;
|
||||
block return BLOCK;
|
||||
pass return PASS;
|
||||
keep return KEEP;
|
||||
state return STATE;
|
||||
apply return APPLY;
|
||||
quick return QUICK;
|
||||
on return ON;
|
||||
inet6 return INET6;
|
||||
inet4 return INET;
|
||||
inet return INET;
|
||||
proto return PROTO;
|
||||
family return FAMILY;
|
||||
tcp return TCP;
|
||||
icmp return ICMP;
|
||||
udp return UDP;
|
||||
return-rst return RETURNRST;
|
||||
return-icmp return RETURNICMP;
|
||||
return return RETURN;
|
||||
from return FROM;
|
||||
to return TO;
|
||||
port return PORT;
|
||||
flags return FLAGS;
|
||||
icmp-type return ICMPTYPE;
|
||||
code return CODE;
|
||||
any return ANY;
|
||||
|
||||
"/" return SLASH;
|
||||
"{" return CURLY_OPEN;
|
||||
"}" return CURLY_CLOSE;
|
||||
"(" return PAR_OPEN;
|
||||
")" return PAR_CLOSE;
|
||||
"," return COMMA;
|
||||
"=" return EQ;
|
||||
|
||||
"0x"[0-9a-fA-F]+ {
|
||||
char *endp, *buf = zalloc(yyleng + 1);
|
||||
buf[yyleng] = 0;
|
||||
yylval.num = strtoul(buf+2, &endp, 16);
|
||||
free(buf);
|
||||
return HEX;
|
||||
}
|
||||
|
||||
[0-9a-fA-F]+":"[0-9a-fA-F:]* {
|
||||
yylval.str = xstrndup(yytext, yyleng);
|
||||
return IPV6ADDR;
|
||||
}
|
||||
|
||||
{NUMBER}"."[0-9][0-9.]* {
|
||||
yylval.str = xstrndup(yytext, yyleng);
|
||||
return IPV4ADDR;
|
||||
}
|
||||
|
||||
{NUMBER} {
|
||||
char *endp, *buf = xstrndup(yytext, yyleng);
|
||||
yylval.num = strtoul(buf, &endp, 10);
|
||||
free(buf);
|
||||
return NUM;
|
||||
}
|
||||
|
||||
"<"{NID}">" {
|
||||
yylval.str = xstrndup(yytext + 1, yyleng - 2);
|
||||
return TABLE_ID;
|
||||
}
|
||||
|
||||
"$"{NID} {
|
||||
yylval.str = xstrndup(yytext + 1, yyleng - 1);
|
||||
return VAR_ID;
|
||||
}
|
||||
|
||||
{ID} {
|
||||
yylval.str = xstrndup(yytext, yyleng);
|
||||
return IDENTIFIER;
|
||||
}
|
||||
|
||||
\"[^\"]*\" {
|
||||
yylval.str = xstrndup(yytext + 1, yyleng - 2);
|
||||
return STRING;
|
||||
}
|
||||
|
||||
#.*$ /* drop comment until end of line */
|
||||
[ \t] /* eat whitespace */
|
||||
|
||||
: return COLON;
|
213
usr.sbin/npf/npfctl/npf_var.c
Normal file
213
usr.sbin/npf/npfctl/npf_var.c
Normal file
@ -0,0 +1,213 @@
|
||||
/* $NetBSD: npf_var.c,v 1.1 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Christos Zoulas.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npf_var.c,v 1.1 2012/01/08 21:34:21 rmind Exp $");
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define _NPFVAR_PRIVATE
|
||||
#include "npfctl.h"
|
||||
|
||||
typedef struct npf_element {
|
||||
void * e_data;
|
||||
struct npf_element *e_next;
|
||||
} npf_element_t;
|
||||
|
||||
struct npfvar {
|
||||
char * v_key;
|
||||
npf_element_t * v_elements;
|
||||
npf_element_t * v_last;
|
||||
int v_type;
|
||||
size_t v_count;
|
||||
void * v_next;
|
||||
};
|
||||
|
||||
static npfvar_t * var_list = NULL;
|
||||
|
||||
npfvar_t *
|
||||
npfvar_create(const char *name)
|
||||
{
|
||||
npfvar_t *vp = zalloc(sizeof(*vp));
|
||||
vp->v_key = xstrdup(name);
|
||||
return vp;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfvar_lookup(const char *key)
|
||||
{
|
||||
for (npfvar_t *it = var_list; it != NULL; it = it->v_next)
|
||||
if (strcmp(it->v_key, key) == 0)
|
||||
return it;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
npfvar_type(size_t t)
|
||||
{
|
||||
if (t >= __arraycount(npfvar_types)) {
|
||||
return "unknown";
|
||||
}
|
||||
return npfvar_types[t];
|
||||
}
|
||||
|
||||
void
|
||||
npfvar_add(npfvar_t *vp)
|
||||
{
|
||||
vp->v_next = var_list;
|
||||
var_list = vp;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfvar_add_element(npfvar_t *vp, int type, const void *data, size_t len)
|
||||
{
|
||||
npf_element_t *el;
|
||||
|
||||
if (vp->v_count == 0) {
|
||||
vp->v_type = type;
|
||||
} else if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
|
||||
yyerror("element type '%s' does not match variable type '%s'",
|
||||
npfvar_type(type), npfvar_type(vp->v_type));
|
||||
return NULL;
|
||||
}
|
||||
vp->v_count++;
|
||||
el = zalloc(sizeof(*el));
|
||||
el->e_data = zalloc(len);
|
||||
memcpy(el->e_data, data, len);
|
||||
|
||||
/* Preserve order of insertion. */
|
||||
if (vp->v_elements == NULL) {
|
||||
vp->v_elements = el;
|
||||
} else {
|
||||
vp->v_last->e_next = el;
|
||||
}
|
||||
vp->v_last = el;
|
||||
return vp;
|
||||
}
|
||||
|
||||
npfvar_t *
|
||||
npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2)
|
||||
{
|
||||
if (vp2 == NULL)
|
||||
return vp;
|
||||
if (vp == NULL)
|
||||
return vp2;
|
||||
|
||||
if (vp->v_elements == NULL) {
|
||||
if (vp2->v_elements) {
|
||||
vp->v_type = vp2->v_type;
|
||||
vp->v_elements = vp2->v_elements;
|
||||
}
|
||||
} else if (vp2->v_elements) {
|
||||
if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(vp2->v_type)) {
|
||||
yyerror("variable '%s' type '%s' does not match "
|
||||
"variable '%s' type '%s'", vp->v_key,
|
||||
npfvar_type(vp->v_type),
|
||||
vp2->v_key, npfvar_type(vp2->v_type));
|
||||
return NULL;
|
||||
}
|
||||
vp->v_last->e_next = vp2->v_elements;
|
||||
}
|
||||
if (vp2->v_elements) {
|
||||
vp->v_last = vp2->v_last;
|
||||
vp->v_count += vp2->v_count;
|
||||
vp2->v_elements = NULL;
|
||||
vp2->v_count = 0;
|
||||
vp2->v_last = NULL;
|
||||
}
|
||||
npfvar_destroy(vp2);
|
||||
return vp;
|
||||
}
|
||||
|
||||
static void
|
||||
npfvar_free_elements(npf_element_t *el)
|
||||
{
|
||||
if (el == NULL)
|
||||
return;
|
||||
npfvar_free_elements(el->e_next);
|
||||
free(el->e_data);
|
||||
free(el);
|
||||
}
|
||||
|
||||
void
|
||||
npfvar_destroy(npfvar_t *vp)
|
||||
{
|
||||
npfvar_free_elements(vp->v_elements);
|
||||
free(vp->v_key);
|
||||
free(vp);
|
||||
}
|
||||
|
||||
char *
|
||||
npfvar_expand_string(const npfvar_t *vp)
|
||||
{
|
||||
return npfvar_get_data(vp, NPFVAR_STRING, 0);
|
||||
}
|
||||
|
||||
size_t
|
||||
npfvar_get_count(const npfvar_t *vp)
|
||||
{
|
||||
return vp ? vp->v_count : 0;
|
||||
}
|
||||
|
||||
int
|
||||
npfvar_get_type(const npfvar_t *vp)
|
||||
{
|
||||
return vp ? vp->v_type : -1;
|
||||
}
|
||||
|
||||
void *
|
||||
npfvar_get_data(const npfvar_t *vp, int type, size_t idx)
|
||||
{
|
||||
npf_element_t *el;
|
||||
|
||||
if (vp == NULL)
|
||||
return NULL;
|
||||
|
||||
if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
|
||||
yyerror("variable '%s' is of type '%s' not '%s'", vp->v_key,
|
||||
npfvar_type(vp->v_type), npfvar_type(type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vp->v_count <= idx) {
|
||||
yyerror("variable '%s' has only %zu elements, requested %zu",
|
||||
vp->v_key, vp->v_count, idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
el = vp->v_elements;
|
||||
while (idx--) {
|
||||
el = el->e_next;
|
||||
}
|
||||
return el->e_data;
|
||||
}
|
75
usr.sbin/npf/npfctl/npf_var.h
Normal file
75
usr.sbin/npf/npfctl/npf_var.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* $NetBSD: npf_var.h,v 1.1 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Christos Zoulas.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _NPF_VAR_H_
|
||||
#define _NPF_VAR_H_
|
||||
|
||||
#define NPFVAR_STRING 0
|
||||
#define NPFVAR_IDENTIFIER 1
|
||||
#define NPFVAR_VAR_ID 2
|
||||
#define NPFVAR_NUM 3
|
||||
|
||||
/* Note: primitive types are equivalent. */
|
||||
#define NPFVAR_PRIM NPFVAR_NUM
|
||||
#define NPFVAR_TYPE(x) (((x) & ~NPFVAR_PRIM) ? (x) : 0)
|
||||
|
||||
#define NPFVAR_TABLE 4
|
||||
#define NPFVAR_FAM 5
|
||||
#define NPFVAR_PORT_RANGE 6
|
||||
#define NPFVAR_TCPFLAG 7
|
||||
#define NPFVAR_ICMP 8
|
||||
#define NPFVAR_PROC_OP 9
|
||||
#define NPFVAR_MODULE_ARG 10
|
||||
|
||||
#ifdef _NPFVAR_PRIVATE
|
||||
static const char *npfvar_types[ ] = {
|
||||
"string", "identifier", "var_id", "num", "table", "fam", "port_range",
|
||||
"tcpflag", "icmp", "proc_op", "module_arg"
|
||||
};
|
||||
#endif
|
||||
|
||||
struct npfvar;
|
||||
typedef struct npfvar npfvar_t;
|
||||
|
||||
npfvar_t * npfvar_create(const char *);
|
||||
npfvar_t * npfvar_lookup(const char *);
|
||||
const char * npfvar_type(size_t);
|
||||
void npfvar_add(npfvar_t *);
|
||||
npfvar_t * npfvar_add_element(npfvar_t *, int, const void *, size_t);
|
||||
npfvar_t * npfvar_add_elements(npfvar_t *, npfvar_t *);
|
||||
void npfvar_destroy(npfvar_t *);
|
||||
|
||||
char * npfvar_expand_string(const npfvar_t *);
|
||||
size_t npfvar_get_count(const npfvar_t *);
|
||||
int npfvar_get_type(const npfvar_t *);
|
||||
void * npfvar_get_data(const npfvar_t *, int, size_t);
|
||||
|
||||
#endif
|
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $ */
|
||||
/* $NetBSD: npfctl.c,v 1.8 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This material is based upon work partially supported by The
|
||||
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
|
||||
__RCSID("$NetBSD: npfctl.c,v 1.8 2012/01/08 21:34:21 rmind Exp $");
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
@ -45,6 +45,11 @@ __RCSID("$NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
|
||||
|
||||
#include "npfctl.h"
|
||||
|
||||
extern int yylineno, yycolumn;
|
||||
extern const char * yyfilename;
|
||||
extern int yyparse(void);
|
||||
extern void yyrestart(FILE *);
|
||||
|
||||
#define NPFCTL_START 1
|
||||
#define NPFCTL_STOP 2
|
||||
#define NPFCTL_RELOAD 3
|
||||
@ -77,9 +82,8 @@ static struct operations_s {
|
||||
void *
|
||||
zalloc(size_t sz)
|
||||
{
|
||||
void *p;
|
||||
void *p = malloc(sz);
|
||||
|
||||
p = malloc(sz);
|
||||
if (p == NULL) {
|
||||
err(EXIT_FAILURE, "zalloc");
|
||||
}
|
||||
@ -87,18 +91,40 @@ zalloc(size_t sz)
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
xrealloc(void *ptr, size_t size)
|
||||
{
|
||||
void *p = realloc(ptr, size);
|
||||
|
||||
if (p == NULL) {
|
||||
err(EXIT_FAILURE, "xrealloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char *
|
||||
xstrdup(const char *s)
|
||||
{
|
||||
char *p;
|
||||
char *p = strdup(s);
|
||||
|
||||
p = strdup(s);
|
||||
if (p == NULL) {
|
||||
err(EXIT_FAILURE, "xstrdup");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char *
|
||||
xstrndup(const char *s, size_t len)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strndup(s, len);
|
||||
if (p == NULL) {
|
||||
err(EXIT_FAILURE, "xstrndup");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
__dead static void
|
||||
usage(void)
|
||||
{
|
||||
@ -123,37 +149,24 @@ usage(void)
|
||||
static void
|
||||
npfctl_parsecfg(const char *cfg)
|
||||
{
|
||||
char *buf, *p;
|
||||
FILE *fp;
|
||||
size_t n;
|
||||
int l;
|
||||
|
||||
fp = fopen(cfg, "r");
|
||||
if (fp == NULL) {
|
||||
err(EXIT_FAILURE, "open '%s'", cfg);
|
||||
}
|
||||
l = 0;
|
||||
buf = NULL;
|
||||
while (getline(&buf, &n, fp) != -1) {
|
||||
l++;
|
||||
p = strpbrk(buf, "#\n");
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
}
|
||||
if (npf_parseline(buf)) {
|
||||
fprintf(stderr, "invalid syntax at line %d\n", l);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (buf != NULL) {
|
||||
free(buf);
|
||||
}
|
||||
yyrestart(fp);
|
||||
yylineno = 1;
|
||||
yycolumn = 0;
|
||||
yyfilename = cfg;
|
||||
yyparse();
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int
|
||||
npfctl_print_stats(int fd)
|
||||
{
|
||||
uint64_t *st = malloc(NPF_STATS_SIZE);
|
||||
uint64_t *st = zalloc(NPF_STATS_SIZE);
|
||||
|
||||
if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
|
||||
err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
|
||||
@ -221,18 +234,12 @@ npfctl(int action, int argc, char **argv)
|
||||
ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
|
||||
break;
|
||||
case NPFCTL_RELOAD:
|
||||
npfctl_init_data();
|
||||
npfctl_config_init(false);
|
||||
npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
|
||||
ret = npfctl_ioctl_send(fd);
|
||||
ret = npfctl_config_send(fd);
|
||||
break;
|
||||
case NPFCTL_FLUSH:
|
||||
/* Pass empty configuration to flush. */
|
||||
npfctl_init_data();
|
||||
ret = npfctl_ioctl_send(fd);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
ret = npf_sessions_send(fd, NULL);
|
||||
ret = npfctl_config_flush(fd);
|
||||
break;
|
||||
case NPFCTL_TABLE:
|
||||
if (argc < 5) {
|
||||
@ -252,12 +259,17 @@ npfctl(int action, int argc, char **argv)
|
||||
tbl.nct_action = 0;
|
||||
arg = argv[3];
|
||||
}
|
||||
if (!npfctl_parse_cidr(arg,
|
||||
npfctl_get_addrfamily(arg),
|
||||
&tbl.nct_addr, &tbl.nct_mask)) {
|
||||
fam_addr_mask_t *fam = npfctl_parse_cidr(arg);
|
||||
if (fam == NULL) {
|
||||
errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
|
||||
}
|
||||
memcpy(&tbl.nct_addr, &fam->fam_addr, sizeof(npf_addr_t));
|
||||
tbl.nct_mask = fam->fam_mask;
|
||||
ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
|
||||
if (tbl.nct_action == 0) {
|
||||
printf("%s\n", ret ? "not found" : "found");
|
||||
exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
case NPFCTL_STATS:
|
||||
ret = npfctl_print_stats(fd);
|
||||
@ -287,28 +299,26 @@ int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *cmd;
|
||||
int n;
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
cmd = argv[1];
|
||||
|
||||
#ifdef _NPF_TESTING
|
||||
/* Special testing case. */
|
||||
npfctl_init_data();
|
||||
npfctl_parsecfg("npf.conf");
|
||||
npfctl_ioctl_send(0);
|
||||
return 0;
|
||||
#endif
|
||||
if (strcmp(cmd, "debug") == 0) {
|
||||
const char *cfg = argc > 2 ? argv[2] : "npf.conf";
|
||||
npfctl_config_init(true);
|
||||
npfctl_parsecfg(cfg);
|
||||
npfctl_config_send(0);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Find and call the subroutine */
|
||||
for (n = 0; operations[n].cmd != NULL; n++) {
|
||||
for (int n = 0; operations[n].cmd != NULL; n++) {
|
||||
if (strcmp(cmd, operations[n].cmd) != 0)
|
||||
continue;
|
||||
npfctl(operations[n].action, argc, argv);
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: npfctl.h,v 1.7 2011/11/04 01:00:28 zoltan Exp $ */
|
||||
/* $NetBSD: npfctl.h,v 1.8 2012/01/08 21:34:21 rmind Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,9 +29,9 @@
|
||||
#ifndef _NPFCTL_H_
|
||||
#define _NPFCTL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <net/npf_ncode.h>
|
||||
#include <net/npf.h>
|
||||
@ -39,67 +39,119 @@
|
||||
#define _NPF_PRIVATE
|
||||
#include <npf.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTF(x) printf x
|
||||
#else
|
||||
#define DPRINTF(x)
|
||||
#endif
|
||||
#include "npf_var.h"
|
||||
|
||||
#define NPF_DEV_PATH "/dev/npf"
|
||||
#define NPF_CONF_PATH "/etc/npf.conf"
|
||||
#define NPF_SESSDB_PATH "/var/db/npf_sessions.db"
|
||||
|
||||
typedef struct {
|
||||
char * e_data;
|
||||
void * e_next;
|
||||
} element_t;
|
||||
typedef struct fam_addr_mask {
|
||||
sa_family_t fam_family;
|
||||
npf_addr_t fam_addr;
|
||||
npf_netmask_t fam_mask;
|
||||
npfvar_t * fam_interface;
|
||||
} fam_addr_mask_t;
|
||||
|
||||
#define VAR_SINGLE 1
|
||||
#define VAR_ARRAY 2
|
||||
#define VAR_TABLE 3
|
||||
typedef struct port_range {
|
||||
in_port_t pr_start;
|
||||
in_port_t pr_end;
|
||||
} port_range_t;
|
||||
|
||||
typedef struct {
|
||||
char * v_key;
|
||||
element_t * v_elements;
|
||||
int v_type;
|
||||
int v_count;
|
||||
void * v_next;
|
||||
} var_t;
|
||||
typedef struct filt_opts {
|
||||
npfvar_t * fo_from;
|
||||
npfvar_t * fo_from_port_range;
|
||||
npfvar_t * fo_to;
|
||||
npfvar_t * fo_to_port_range;
|
||||
} filt_opts_t;
|
||||
|
||||
extern nl_config_t * npf_conf;
|
||||
typedef struct opt_proto {
|
||||
int op_proto;
|
||||
npfvar_t * op_opts;
|
||||
} opt_proto_t;
|
||||
|
||||
typedef struct rule_group {
|
||||
const char * rg_name;
|
||||
int rg_attr;
|
||||
u_int rg_ifnum;
|
||||
} rule_group_t;
|
||||
|
||||
typedef struct proc_op {
|
||||
const char * po_name;
|
||||
npfvar_t * po_opts;
|
||||
} proc_op_t;
|
||||
|
||||
typedef struct module_arg {
|
||||
const char * ma_name;
|
||||
npfvar_t * ma_opts;
|
||||
} module_arg_t;
|
||||
|
||||
void yyerror(const char *, ...) __printflike(1, 2);
|
||||
void * zalloc(size_t);
|
||||
void * xrealloc(void *, size_t);
|
||||
char * xstrdup(const char *);
|
||||
char * xstrndup(const char *, size_t);
|
||||
|
||||
void npfctl_init_data(void);
|
||||
int npfctl_ioctl_send(int);
|
||||
bool npfctl_table_exists_p(const char *);
|
||||
in_port_t npfctl_portno(const char *);
|
||||
uint8_t npfctl_icmpcode(uint8_t, const char *);
|
||||
uint8_t npfctl_icmptype(const char *);
|
||||
unsigned long npfctl_find_ifindex(const char *);
|
||||
npfvar_t * npfctl_parse_tcpflag(const char *);
|
||||
npfvar_t * npfctl_parse_table_id(const char *);
|
||||
npfvar_t * npfctl_parse_icmpcode(const char *);
|
||||
npfvar_t * npfctl_parse_icmp(uint8_t, uint8_t);
|
||||
npfvar_t * npfctl_parse_iface(const char *);
|
||||
npfvar_t * npfctl_parse_port_range(in_port_t, in_port_t);
|
||||
npfvar_t * npfctl_parse_fam_addr_mask(const char *, const char *,
|
||||
unsigned long *);
|
||||
fam_addr_mask_t *npfctl_parse_cidr(char *);
|
||||
|
||||
struct ifaddrs *npfctl_getif(char *, unsigned int *, bool, sa_family_t);
|
||||
void npfctl_create_mask(sa_family_t, u_int, npf_addr_t *);
|
||||
sa_family_t npfctl_get_addrfamily(const char *);
|
||||
sa_family_t npfctl_parse_cidr(char *, sa_family_t, npf_addr_t *, npf_netmask_t *);
|
||||
bool npfctl_parse_port(char *, bool *, in_port_t *, in_port_t *);
|
||||
/*
|
||||
* N-code generation interface.
|
||||
*/
|
||||
|
||||
void npfctl_fill_table(nl_table_t *, char *);
|
||||
typedef struct nc_ctx nc_ctx_t;
|
||||
|
||||
void npfctl_rule_ncode(nl_rule_t *, char *, char *,
|
||||
int, int, var_t *, sa_family_t, var_t *, var_t *, var_t *);
|
||||
#define NC_MATCH_DST 0x01
|
||||
#define NC_MATCH_SRC 0x02
|
||||
|
||||
size_t npfctl_calc_ncsize(int []);
|
||||
size_t npfctl_failure_offset(int []);
|
||||
#define NC_MATCH_TCP 0x04
|
||||
#define NC_MATCH_UDP 0x08
|
||||
|
||||
void npfctl_gennc_ether(void **, int, uint16_t);
|
||||
void npfctl_gennc_v4cidr(void **, int,
|
||||
const npf_addr_t *, const npf_netmask_t, bool);
|
||||
void npfctl_gennc_v6cidr(void **, int,
|
||||
const npf_addr_t *, const npf_netmask_t, bool);
|
||||
void npfctl_gennc_icmp(void **, int, int, int);
|
||||
void npfctl_gennc_tcpfl(void **, int , uint8_t, uint8_t);
|
||||
void npfctl_gennc_ports(void **, int,
|
||||
in_port_t, in_port_t, bool, bool);
|
||||
void npfctl_gennc_tbl(void **, int, u_int , bool);
|
||||
void npfctl_gennc_complete(void **);
|
||||
nc_ctx_t * npfctl_ncgen_create(void);
|
||||
void * npfctl_ncgen_complete(nc_ctx_t *, size_t *);
|
||||
void npfctl_ncgen_print(const void *, size_t);
|
||||
|
||||
int npf_parseline(char *);
|
||||
void npfctl_ncgen_group(nc_ctx_t *);
|
||||
void npfctl_ncgen_endgroup(nc_ctx_t *);
|
||||
|
||||
void npfctl_gennc_v4cidr(nc_ctx_t *, int, const npf_addr_t *,
|
||||
const npf_netmask_t);
|
||||
void npfctl_gennc_v6cidr(nc_ctx_t *, int, const npf_addr_t *,
|
||||
const npf_netmask_t);
|
||||
void npfctl_gennc_ports(nc_ctx_t *, int, in_port_t, in_port_t);
|
||||
void npfctl_gennc_icmp(nc_ctx_t *, int, int);
|
||||
void npfctl_gennc_tbl(nc_ctx_t *, int, u_int);
|
||||
void npfctl_gennc_tcpfl(nc_ctx_t *, uint8_t, uint8_t);
|
||||
|
||||
/*
|
||||
* Configuration building interface.
|
||||
*/
|
||||
|
||||
#define NPFCTL_RDR 1
|
||||
#define NPFCTL_BINAT 2
|
||||
#define NPFCTL_NAT 3
|
||||
|
||||
void npfctl_config_init(bool);
|
||||
int npfctl_config_send(int);
|
||||
int npfctl_config_flush(int);
|
||||
|
||||
void npfctl_build_rproc(const char *, npfvar_t *);
|
||||
void npfctl_build_group(const char *, int, u_int);
|
||||
void npfctl_build_rule(int, u_int, sa_family_t,
|
||||
const opt_proto_t *, const filt_opts_t *, const char *);
|
||||
void npfctl_build_nat(int, u_int, const filt_opts_t *,
|
||||
npfvar_t *, npfvar_t *);
|
||||
void npfctl_build_table(const char *, u_int, const char *);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user