npfctl.c revision 1.10 1 /* $NetBSD: npfctl.c,v 1.10 2012/02/05 00:37:13 rmind 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.10 2012/02/05 00:37:13 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 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