npfctl.c revision 1.6.2.2       1 /*	$NetBSD: npfctl.c,v 1.6.2.2 2012/04/17 00:09:50 yamt Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This material is based upon work partially supported by The
      8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: npfctl.c,v 1.6.2.2 2012/04/17 00:09:50 yamt Exp $");
     34 
     35 #include <sys/ioctl.h>
     36 #include <sys/stat.h>
     37 #include <sys/types.h>
     38 
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <err.h>
     43 #include <fcntl.h>
     44 #include <unistd.h>
     45 
     46 #include "npfctl.h"
     47 
     48 extern int		yylineno, yycolumn;
     49 extern const char *	yyfilename;
     50 extern int		yyparse(void);
     51 extern void		yyrestart(FILE *);
     52 
     53 #define	NPFCTL_START		1
     54 #define	NPFCTL_STOP		2
     55 #define	NPFCTL_RELOAD		3
     56 #define	NPFCTL_FLUSH		4
     57 #define	NPFCTL_TABLE		5
     58 #define	NPFCTL_STATS		6
     59 #define	NPFCTL_SESSIONS_SAVE	7
     60 #define	NPFCTL_SESSIONS_LOAD	8
     61 
     62 static struct operations_s {
     63 	const char *		cmd;
     64 	int			action;
     65 } operations[] = {
     66 	/* Start, stop, reload */
     67 	{	"start",		NPFCTL_START		},
     68 	{	"stop",			NPFCTL_STOP		},
     69 	{	"reload",		NPFCTL_RELOAD		},
     70 	{	"flush",		NPFCTL_FLUSH		},
     71 	/* Table */
     72 	{	"table",		NPFCTL_TABLE		},
     73 	/* Stats */
     74 	{	"stats",		NPFCTL_STATS		},
     75 	/* Sessions */
     76 	{	"sess-save",		NPFCTL_SESSIONS_SAVE	},
     77 	{	"sess-load",		NPFCTL_SESSIONS_LOAD	},
     78 	/* --- */
     79 	{	NULL,			0			}
     80 };
     81 
     82 void *
     83 zalloc(size_t sz)
     84 {
     85 	void *p = malloc(sz);
     86 
     87 	if (p == NULL) {
     88 		err(EXIT_FAILURE, "zalloc");
     89 	}
     90 	memset(p, 0, sz);
     91 	return p;
     92 }
     93 
     94 void *
     95 xrealloc(void *ptr, size_t size)
     96 {
     97 	void *p = realloc(ptr, size);
     98 
     99 	if (p == NULL) {
    100 		err(EXIT_FAILURE, "xrealloc");
    101 	}
    102 	return p;
    103 }
    104 
    105 char *
    106 xstrdup(const char *s)
    107 {
    108 	char *p = strdup(s);
    109 
    110 	if (p == NULL) {
    111 		err(EXIT_FAILURE, "xstrdup");
    112 	}
    113 	return p;
    114 }
    115 
    116 char *
    117 xstrndup(const char *s, size_t len)
    118 {
    119 	char *p;
    120 
    121 	p = strndup(s, len);
    122 	if (p == NULL) {
    123 		err(EXIT_FAILURE, "xstrndup");
    124 	}
    125 	return p;
    126 }
    127 
    128 __dead static void
    129 usage(void)
    130 {
    131 	const char *progname = getprogname();
    132 
    133 	fprintf(stderr,
    134 	    "usage:\t%s [ start | stop | reload | flush | stats ]\n",
    135 	    progname);
    136 	fprintf(stderr,
    137 	    "usage:\t%s [ sess-save | sess-load ]\n",
    138 	    progname);
    139 	fprintf(stderr,
    140 	    "\t%s table <tid> [ flush ]\n",
    141 	    progname);
    142 	fprintf(stderr,
    143 	    "\t%s table <tid> { add | rem } <address/mask>\n",
    144 	    progname);
    145 
    146 	exit(EXIT_FAILURE);
    147 }
    148 
    149 static void
    150 npfctl_parsecfg(const char *cfg)
    151 {
    152 	FILE *fp;
    153 
    154 	fp = fopen(cfg, "r");
    155 	if (fp == NULL) {
    156 		err(EXIT_FAILURE, "open '%s'", cfg);
    157 	}
    158 	yyrestart(fp);
    159 	yylineno = 1;
    160 	yycolumn = 0;
    161 	yyfilename = cfg;
    162 	yyparse();
    163 	fclose(fp);
    164 }
    165 
    166 static int
    167 npfctl_print_stats(int fd)
    168 {
    169 	uint64_t *st = zalloc(NPF_STATS_SIZE);
    170 
    171 	if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
    172 		err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
    173 	}
    174 
    175 	printf("Packets passed:\n\t%"PRIu64" default pass\n\t"
    176 	    "%"PRIu64 " ruleset pass\n\t%"PRIu64" session pass\n\n",
    177 	    st[NPF_STAT_PASS_DEFAULT], st[NPF_STAT_PASS_RULESET],
    178 	    st[NPF_STAT_PASS_SESSION]);
    179 
    180 	printf("Packets blocked:\n\t%"PRIu64" default block\n\t"
    181 	    "%"PRIu64 " ruleset block\n\n", st[NPF_STAT_BLOCK_DEFAULT],
    182 	    st[NPF_STAT_BLOCK_RULESET]);
    183 
    184 	printf("Session and NAT entries:\n\t%"PRIu64" session allocations\n\t"
    185 	    "%"PRIu64" session destructions\n\t%"PRIu64" NAT entry allocations\n\t"
    186 	    "%"PRIu64" NAT entry destructions\n\n", st[NPF_STAT_SESSION_CREATE],
    187 	    st[NPF_STAT_SESSION_DESTROY], st[NPF_STAT_NAT_CREATE],
    188 	    st[NPF_STAT_NAT_DESTROY]);
    189 
    190 	printf("Invalid packet state cases:\n\t%"PRIu64" cases in total\n\t"
    191 	    "%"PRIu64" TCP case I\n\t%"PRIu64" TCP case II\n\t%"PRIu64
    192 	    " TCP case III\n\n", st[NPF_STAT_INVALID_STATE],
    193 	    st[NPF_STAT_INVALID_STATE_TCP1], st[NPF_STAT_INVALID_STATE_TCP2],
    194 	    st[NPF_STAT_INVALID_STATE_TCP3]);
    195 
    196 	printf("Packet race cases:\n\t%"PRIu64" NAT association race\n\t"
    197 	    "%"PRIu64" duplicate session race\n\n", st[NPF_STAT_RACE_NAT],
    198 	    st[NPF_STAT_RACE_SESSION]);
    199 
    200 	printf("Rule processing procedure cases:\n"
    201 	    "\t%"PRIu64" packets logged\n\t%"PRIu64" packets normalized\n\n",
    202 	    st[NPF_STAT_RPROC_LOG], st[NPF_STAT_RPROC_NORM]);
    203 
    204 	printf("Unexpected error cases:\n\t%"PRIu64"\n", st[NPF_STAT_ERROR]);
    205 
    206 	free(st);
    207 	return 0;
    208 }
    209 
    210 void
    211 npfctl_print_error(const nl_error_t *ne)
    212 {
    213 	static const char *ncode_errors[] = {
    214 		[-NPF_ERR_OPCODE]	= "invalid instruction",
    215 		[-NPF_ERR_JUMP]		= "invalid jump",
    216 		[-NPF_ERR_REG]		= "invalid register",
    217 		[-NPF_ERR_INVAL]	= "invalid argument value",
    218 		[-NPF_ERR_RANGE]	= "processing out of range"
    219 	};
    220 	const int nc_err = ne->ne_ncode_error;
    221 	const char *srcfile = ne->ne_source_file;
    222 
    223 	if (srcfile) {
    224 		warnx("source %s line %d", srcfile, ne->ne_source_line);
    225 	}
    226 	if (nc_err) {
    227 		warnx("n-code error (%d): %s at offset 0x%x",
    228 		    nc_err, ncode_errors[-nc_err], ne->ne_ncode_errat);
    229 	}
    230 	if (ne->ne_id) {
    231 		warnx("object: %d", ne->ne_id);
    232 	}
    233 }
    234 
    235 static void
    236 npfctl(int action, int argc, char **argv)
    237 {
    238 	int fd, ret, ver, boolval;
    239 	npf_ioctl_table_t tbl;
    240 	char *arg;
    241 
    242 	fd = open(NPF_DEV_PATH, O_RDONLY);
    243 	if (fd == -1) {
    244 		err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH);
    245 	}
    246 	ret = ioctl(fd, IOC_NPF_VERSION, &ver);
    247 	if (ver != NPF_VERSION) {
    248 		errx(EXIT_FAILURE,
    249 		    "incompatible NPF interface version (%d, kernel %d)",
    250 		    NPF_VERSION, ver);
    251 	}
    252 	switch (action) {
    253 	case NPFCTL_START:
    254 		boolval = true;
    255 		ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
    256 		break;
    257 	case NPFCTL_STOP:
    258 		boolval = false;
    259 		ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
    260 		break;
    261 	case NPFCTL_RELOAD:
    262 		npfctl_config_init(false);
    263 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
    264 		ret = npfctl_config_send(fd);
    265 		break;
    266 	case NPFCTL_FLUSH:
    267 		ret = npf_config_flush(fd);
    268 		break;
    269 	case NPFCTL_TABLE:
    270 		if (argc < 5) {
    271 			usage();
    272 		}
    273 		tbl.nct_tid = atoi(argv[2]);
    274 		if (strcmp(argv[3], "add") == 0) {
    275 			/* Add table entry. */
    276 			tbl.nct_action = NPF_IOCTL_TBLENT_ADD;
    277 			arg = argv[4];
    278 		} else if (strcmp(argv[3], "rem") == 0) {
    279 			/* Remove entry. */
    280 			tbl.nct_action = NPF_IOCTL_TBLENT_REM;
    281 			arg = argv[4];
    282 		} else {
    283 			/* Default: lookup. */
    284 			tbl.nct_action = 0;
    285 			arg = argv[3];
    286 		}
    287 		fam_addr_mask_t *fam = npfctl_parse_cidr(arg);
    288 		if (fam == NULL) {
    289 			errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
    290 		}
    291 		memcpy(&tbl.nct_addr, &fam->fam_addr, sizeof(npf_addr_t));
    292 		tbl.nct_mask = fam->fam_mask;
    293 		ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
    294 		if (tbl.nct_action == 0) {
    295 			printf("%s\n", ret ? "not found" : "found");
    296 			exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
    297 		}
    298 		break;
    299 	case NPFCTL_STATS:
    300 		ret = npfctl_print_stats(fd);
    301 		break;
    302 	case NPFCTL_SESSIONS_SAVE:
    303 		ret = npf_sessions_recv(fd, NPF_SESSDB_PATH);
    304 		if (ret) {
    305 			errx(EXIT_FAILURE, "could not save sessions to '%s'",
    306 			    NPF_SESSDB_PATH);
    307 		}
    308 		break;
    309 	case NPFCTL_SESSIONS_LOAD:
    310 		ret = npf_sessions_send(fd, NPF_SESSDB_PATH);
    311 		if (ret) {
    312 			errx(EXIT_FAILURE, "no sessions loaded from '%s'",
    313 			    NPF_SESSDB_PATH);
    314 		}
    315 		break;
    316 	}
    317 	if (ret) {
    318 		err(EXIT_FAILURE, "ioctl");
    319 	}
    320 	close(fd);
    321 }
    322 
    323 int
    324 main(int argc, char **argv)
    325 {
    326 	char *cmd;
    327 
    328 	if (argc < 2) {
    329 		usage();
    330 	}
    331 	cmd = argv[1];
    332 
    333 	if (strcmp(cmd, "debug") == 0) {
    334 		const char *cfg = argc > 2 ? argv[2] : "npf.conf";
    335 		npfctl_config_init(true);
    336 		npfctl_parsecfg(cfg);
    337 		npfctl_config_send(0);
    338 		return EXIT_SUCCESS;
    339 	}
    340 
    341 	/* Find and call the subroutine */
    342 	for (int n = 0; operations[n].cmd != NULL; n++) {
    343 		if (strcmp(cmd, operations[n].cmd) != 0)
    344 			continue;
    345 		npfctl(operations[n].action, argc, argv);
    346 		return EXIT_SUCCESS;
    347 	}
    348 	usage();
    349 }
    350