xref: /netbsd-src/usr.sbin/npf/npfctl/npfctl.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: npfctl.c,v 1.4 2011/01/18 20:33:45 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2011 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.4 2011/01/18 20:33:45 rmind 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 #define	NPFCTL_START		1
49 #define	NPFCTL_STOP		2
50 #define	NPFCTL_RELOAD		3
51 #define	NPFCTL_FLUSH		4
52 #define	NPFCTL_TABLE		5
53 #define	NPFCTL_STATS		6
54 #define	NPFCTL_SESSIONS_SAVE	7
55 #define	NPFCTL_SESSIONS_LOAD	8
56 
57 static struct operations_s {
58 	const char *		cmd;
59 	int			action;
60 } operations[] = {
61 	/* Start, stop, reload */
62 	{	"start",		NPFCTL_START		},
63 	{	"stop",			NPFCTL_STOP		},
64 	{	"reload",		NPFCTL_RELOAD		},
65 	{	"flush",		NPFCTL_FLUSH		},
66 	/* Table */
67 	{	"table",		NPFCTL_TABLE		},
68 	/* Stats */
69 	{	"stats",		NPFCTL_STATS		},
70 	/* Sessions */
71 	{	"sess-save",		NPFCTL_SESSIONS_SAVE	},
72 	{	"sess-load",		NPFCTL_SESSIONS_LOAD	},
73 	/* --- */
74 	{	NULL,			0			}
75 };
76 
77 void *
78 zalloc(size_t sz)
79 {
80 	void *p;
81 
82 	p = malloc(sz);
83 	if (p == NULL) {
84 		err(EXIT_FAILURE, "zalloc");
85 	}
86 	memset(p, 0, sz);
87 	return p;
88 }
89 
90 char *
91 xstrdup(const char *s)
92 {
93 	char *p;
94 
95 	p = strdup(s);
96 	if (p == NULL) {
97 		err(EXIT_FAILURE, "xstrdup");
98 	}
99 	return p;
100 }
101 
102 static void
103 usage(void)
104 {
105 	const char *progname = getprogname();
106 
107 	fprintf(stderr,
108 	    "usage:\t%s [ start | stop | reload | flush | stats ]\n",
109 	    progname);
110 	fprintf(stderr,
111 	    "usage:\t%s [ sess-save | sess-load ]\n",
112 	    progname);
113 	fprintf(stderr,
114 	    "\t%s table <tid> [ flush ]\n",
115 	    progname);
116 	fprintf(stderr,
117 	    "\t%s table <tid> { add | rem } <address/mask>\n",
118 	    progname);
119 
120 	exit(EXIT_FAILURE);
121 }
122 
123 static void
124 npfctl_parsecfg(const char *cfg)
125 {
126 	char *buf, *p;
127 	FILE *fp;
128 	size_t n;
129 	int l;
130 
131 	fp = fopen(cfg, "r");
132 	if (fp == NULL) {
133 		err(EXIT_FAILURE, "open '%s'", cfg);
134 	}
135 	l = 0;
136 	buf = NULL;
137 	while (getline(&buf, &n, fp) != -1) {
138 		l++;
139 		p = strpbrk(buf, "#\n");
140 		if (p != NULL) {
141 			*p = '\0';
142 		}
143 		if (npf_parseline(buf)) {
144 			fprintf(stderr, "invalid syntax at line %d\n", l);
145 			exit(EXIT_FAILURE);
146 		}
147 	}
148 	if (buf != NULL) {
149 		free(buf);
150 	}
151 }
152 
153 static int
154 npfctl_print_stats(int fd)
155 {
156 	uint64_t *st = malloc(NPF_STATS_SIZE);
157 
158 	if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
159 		err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
160 	}
161 
162 	printf("Packets passed:\n\t%"PRIu64" default pass\n\t"
163 	    "%"PRIu64 " ruleset pass\n\t%"PRIu64" session pass\n\n",
164 	    st[NPF_STAT_PASS_DEFAULT], st[NPF_STAT_PASS_RULESET],
165 	    st[NPF_STAT_PASS_SESSION]);
166 
167 	printf("Packets blocked:\n\t%"PRIu64" default block\n\t"
168 	    "%"PRIu64 " ruleset block\n\n", st[NPF_STAT_BLOCK_DEFAULT],
169 	    st[NPF_STAT_BLOCK_RULESET]);
170 
171 	printf("Session and NAT entries:\n\t%"PRIu64" session allocations\n\t"
172 	    "%"PRIu64" session destructions\n\t%"PRIu64" NAT entry allocations\n\t"
173 	    "%"PRIu64" NAT entry destructions\n\n", st[NPF_STAT_SESSION_CREATE],
174 	    st[NPF_STAT_SESSION_DESTROY], st[NPF_STAT_NAT_CREATE],
175 	    st[NPF_STAT_NAT_DESTROY]);
176 
177 	printf("Invalid packet state cases:\n\t%"PRIu64" cases in total\n\t"
178 	    "%"PRIu64" TCP case I\n\t%"PRIu64" TCP case II\n\t%"PRIu64
179 	    " TCP case III\n\n", st[NPF_STAT_INVALID_STATE],
180 	    st[NPF_STAT_INVALID_STATE_TCP1], st[NPF_STAT_INVALID_STATE_TCP2],
181 	    st[NPF_STAT_INVALID_STATE_TCP3]);
182 
183 	printf("Packet race cases:\n\t%"PRIu64" NAT association race\n\t"
184 	    "%"PRIu64" duplicate session race\n\n", st[NPF_STAT_RACE_NAT],
185 	    st[NPF_STAT_RACE_SESSION]);
186 
187 	printf("Rule processing procedure cases:\n"
188 	    "\t%"PRIu64" packets logged\n\t%"PRIu64" packets normalized\n\n",
189 	    st[NPF_STAT_RPROC_LOG], st[NPF_STAT_RPROC_NORM]);
190 
191 	printf("Unexpected error cases:\n\t%"PRIu64"\n", st[NPF_STAT_ERROR]);
192 
193 	free(st);
194 	return 0;
195 }
196 
197 static void
198 npfctl(int action, int argc, char **argv)
199 {
200 	int fd, ret, ver, boolval;
201 	npf_ioctl_table_t tbl;
202 	char *arg;
203 
204 	fd = open(NPF_DEV_PATH, O_RDONLY);
205 	if (fd == -1) {
206 		err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH);
207 	}
208 	ret = ioctl(fd, IOC_NPF_VERSION, &ver);
209 	if (ver != NPF_VERSION) {
210 		errx(EXIT_FAILURE,
211 		    "incompatible NPF interface version (%d, kernel %d)",
212 		    NPF_VERSION, ver);
213 	}
214 	switch (action) {
215 	case NPFCTL_START:
216 		boolval = true;
217 		ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
218 		break;
219 	case NPFCTL_STOP:
220 		boolval = false;
221 		ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
222 		break;
223 	case NPFCTL_RELOAD:
224 		npfctl_init_data();
225 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
226 		ret = npfctl_ioctl_send(fd);
227 		break;
228 	case NPFCTL_FLUSH:
229 		/* Pass empty configuration to flush. */
230 		npfctl_init_data();
231 		ret = npfctl_ioctl_send(fd);
232 		if (ret) {
233 			break;
234 		}
235 		ret = npfctl_ioctl_flushse(fd);
236 		break;
237 	case NPFCTL_TABLE:
238 		if (argc < 5) {
239 			usage();
240 		}
241 		tbl.nct_tid = atoi(argv[2]);
242 		if (strcmp(argv[3], "add") == 0) {
243 			/* Add table entry. */
244 			tbl.nct_action = NPF_IOCTL_TBLENT_ADD;
245 			arg = argv[4];
246 		} else if (strcmp(argv[3], "rem") == 0) {
247 			/* Remove entry. */
248 			tbl.nct_action = NPF_IOCTL_TBLENT_REM;
249 			arg = argv[4];
250 		} else {
251 			/* Default: lookup. */
252 			tbl.nct_action = 0;
253 			arg = argv[3];
254 		}
255 		if (!npfctl_parse_v4mask(arg,
256 		    &tbl.nct_addr, &tbl.nct_mask)) {
257 			errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
258 		}
259 		ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
260 		break;
261 	case NPFCTL_STATS:
262 		ret = npfctl_print_stats(fd);
263 		break;
264 	case NPFCTL_SESSIONS_SAVE:
265 		ret = npfctl_ioctl_recvse(fd);
266 		break;
267 	case NPFCTL_SESSIONS_LOAD:
268 		ret = npfctl_ioctl_sendse(fd);
269 		break;
270 	}
271 	if (ret == -1) {
272 		err(EXIT_FAILURE, "ioctl");
273 	}
274 	close(fd);
275 }
276 
277 int
278 main(int argc, char **argv)
279 {
280 	char *cmd;
281 	int n;
282 
283 	if (argc < 2) {
284 		usage();
285 	}
286 	cmd = argv[1];
287 
288 #ifdef _NPF_TESTING
289 	/* Special testing case. */
290 	npfctl_init_data();
291 	npfctl_parsecfg("npf.conf");
292 	npfctl_ioctl_send(0);
293 	return 0;
294 #endif
295 
296 	/* Find and call the subroutine */
297 	for (n = 0; operations[n].cmd != NULL; n++) {
298 		if (strcmp(cmd, operations[n].cmd) != 0)
299 			continue;
300 		npfctl(operations[n].action, argc, argv);
301 		return 0;
302 	}
303 	usage();
304 	return 0;
305 }
306