npfctl.c revision 1.10.2.1 1 /* $NetBSD: npfctl.c,v 1.10.2.1 2012/06/26 00:07:20 riz 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.2.1 2012/06/26 00:07:20 riz 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 enum {
54 NPFCTL_START,
55 NPFCTL_STOP,
56 NPFCTL_RELOAD,
57 NPFCTL_SHOWCONF,
58 NPFCTL_FLUSH,
59 NPFCTL_TABLE,
60 NPFCTL_STATS,
61 NPFCTL_SESSIONS_SAVE,
62 NPFCTL_SESSIONS_LOAD,
63 };
64
65 static struct operations_s {
66 const char * cmd;
67 int action;
68 } operations[] = {
69 /* Start, stop, reload */
70 { "start", NPFCTL_START },
71 { "stop", NPFCTL_STOP },
72 { "reload", NPFCTL_RELOAD },
73 { "show", NPFCTL_SHOWCONF, },
74 { "flush", NPFCTL_FLUSH },
75 /* Table */
76 { "table", NPFCTL_TABLE },
77 /* Stats */
78 { "stats", NPFCTL_STATS },
79 /* Sessions */
80 { "sess-save", NPFCTL_SESSIONS_SAVE },
81 { "sess-load", NPFCTL_SESSIONS_LOAD },
82 /* --- */
83 { NULL, 0 }
84 };
85
86 void *
87 zalloc(size_t sz)
88 {
89 void *p = malloc(sz);
90
91 if (p == NULL) {
92 err(EXIT_FAILURE, "zalloc");
93 }
94 memset(p, 0, sz);
95 return p;
96 }
97
98 void *
99 xrealloc(void *ptr, size_t size)
100 {
101 void *p = realloc(ptr, size);
102
103 if (p == NULL) {
104 err(EXIT_FAILURE, "xrealloc");
105 }
106 return p;
107 }
108
109 char *
110 xstrdup(const char *s)
111 {
112 char *p = strdup(s);
113
114 if (p == NULL) {
115 err(EXIT_FAILURE, "xstrdup");
116 }
117 return p;
118 }
119
120 char *
121 xstrndup(const char *s, size_t len)
122 {
123 char *p;
124
125 p = strndup(s, len);
126 if (p == NULL) {
127 err(EXIT_FAILURE, "xstrndup");
128 }
129 return p;
130 }
131
132 __dead static void
133 usage(void)
134 {
135 const char *progname = getprogname();
136
137 fprintf(stderr,
138 "usage:\t%s [ start | stop | reload | flush | stats ]\n",
139 progname);
140 fprintf(stderr,
141 "usage:\t%s [ sess-save | sess-load ]\n",
142 progname);
143 fprintf(stderr,
144 "\t%s table <tid> [ flush ]\n",
145 progname);
146 fprintf(stderr,
147 "\t%s table <tid> { add | rem } <address/mask>\n",
148 progname);
149
150 exit(EXIT_FAILURE);
151 }
152
153 static void
154 npfctl_parsecfg(const char *cfg)
155 {
156 FILE *fp;
157
158 fp = fopen(cfg, "r");
159 if (fp == NULL) {
160 err(EXIT_FAILURE, "open '%s'", cfg);
161 }
162 yyrestart(fp);
163 yylineno = 1;
164 yycolumn = 0;
165 yyfilename = cfg;
166 yyparse();
167 fclose(fp);
168 }
169
170 static int
171 npfctl_print_stats(int fd)
172 {
173 uint64_t *st = zalloc(NPF_STATS_SIZE);
174
175 if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
176 err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
177 }
178
179 printf("Packets passed:\n\t%"PRIu64" default pass\n\t"
180 "%"PRIu64 " ruleset pass\n\t%"PRIu64" session pass\n\n",
181 st[NPF_STAT_PASS_DEFAULT], st[NPF_STAT_PASS_RULESET],
182 st[NPF_STAT_PASS_SESSION]);
183
184 printf("Packets blocked:\n\t%"PRIu64" default block\n\t"
185 "%"PRIu64 " ruleset block\n\n", st[NPF_STAT_BLOCK_DEFAULT],
186 st[NPF_STAT_BLOCK_RULESET]);
187
188 printf("Session and NAT entries:\n\t%"PRIu64" session allocations\n\t"
189 "%"PRIu64" session destructions\n\t%"PRIu64" NAT entry allocations\n\t"
190 "%"PRIu64" NAT entry destructions\n\n", st[NPF_STAT_SESSION_CREATE],
191 st[NPF_STAT_SESSION_DESTROY], st[NPF_STAT_NAT_CREATE],
192 st[NPF_STAT_NAT_DESTROY]);
193
194 printf("Invalid packet state cases:\n\t%"PRIu64" cases in total\n\t"
195 "%"PRIu64" TCP case I\n\t%"PRIu64" TCP case II\n\t%"PRIu64
196 " TCP case III\n\n", st[NPF_STAT_INVALID_STATE],
197 st[NPF_STAT_INVALID_STATE_TCP1], st[NPF_STAT_INVALID_STATE_TCP2],
198 st[NPF_STAT_INVALID_STATE_TCP3]);
199
200 printf("Packet race cases:\n\t%"PRIu64" NAT association race\n\t"
201 "%"PRIu64" duplicate session race\n\n", st[NPF_STAT_RACE_NAT],
202 st[NPF_STAT_RACE_SESSION]);
203
204 printf("Rule processing procedure cases:\n"
205 "\t%"PRIu64" packets logged\n\t%"PRIu64" packets normalized\n\n",
206 st[NPF_STAT_RPROC_LOG], st[NPF_STAT_RPROC_NORM]);
207
208 printf("Unexpected error cases:\n\t%"PRIu64"\n", st[NPF_STAT_ERROR]);
209
210 free(st);
211 return 0;
212 }
213
214 void
215 npfctl_print_error(const nl_error_t *ne)
216 {
217 static const char *ncode_errors[] = {
218 [-NPF_ERR_OPCODE] = "invalid instruction",
219 [-NPF_ERR_JUMP] = "invalid jump",
220 [-NPF_ERR_REG] = "invalid register",
221 [-NPF_ERR_INVAL] = "invalid argument value",
222 [-NPF_ERR_RANGE] = "processing out of range"
223 };
224 const int nc_err = ne->ne_ncode_error;
225 const char *srcfile = ne->ne_source_file;
226
227 if (srcfile) {
228 warnx("source %s line %d", srcfile, ne->ne_source_line);
229 }
230 if (nc_err) {
231 warnx("n-code error (%d): %s at offset 0x%x",
232 nc_err, ncode_errors[-nc_err], ne->ne_ncode_errat);
233 }
234 if (ne->ne_id) {
235 warnx("object: %d", ne->ne_id);
236 }
237 }
238
239 static void
240 npfctl(int action, int argc, char **argv)
241 {
242 int fd, ret, ver, boolval;
243 npf_ioctl_table_t tbl;
244 char *arg;
245
246 fd = open(NPF_DEV_PATH, O_RDONLY);
247 if (fd == -1) {
248 err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH);
249 }
250 ret = ioctl(fd, IOC_NPF_VERSION, &ver);
251 if (ver != NPF_VERSION) {
252 errx(EXIT_FAILURE,
253 "incompatible NPF interface version (%d, kernel %d)",
254 NPF_VERSION, ver);
255 }
256 switch (action) {
257 case NPFCTL_START:
258 boolval = true;
259 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
260 break;
261 case NPFCTL_STOP:
262 boolval = false;
263 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
264 break;
265 case NPFCTL_RELOAD:
266 npfctl_config_init(false);
267 npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
268 ret = npfctl_config_send(fd);
269 break;
270 case NPFCTL_SHOWCONF:
271 ret = npfctl_config_show(fd);
272 break;
273 case NPFCTL_FLUSH:
274 ret = npf_config_flush(fd);
275 break;
276 case NPFCTL_TABLE:
277 if (argc < 5) {
278 usage();
279 }
280 tbl.nct_tid = atoi(argv[2]);
281 if (strcmp(argv[3], "add") == 0) {
282 /* Add table entry. */
283 tbl.nct_action = NPF_IOCTL_TBLENT_ADD;
284 arg = argv[4];
285 } else if (strcmp(argv[3], "rem") == 0) {
286 /* Remove entry. */
287 tbl.nct_action = NPF_IOCTL_TBLENT_REM;
288 arg = argv[4];
289 } else {
290 /* Default: lookup. */
291 tbl.nct_action = 0;
292 arg = argv[3];
293 }
294 fam_addr_mask_t *fam = npfctl_parse_cidr(arg);
295 if (fam == NULL) {
296 errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
297 }
298 memcpy(&tbl.nct_addr, &fam->fam_addr, sizeof(npf_addr_t));
299 tbl.nct_mask = fam->fam_mask;
300 ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
301 if (tbl.nct_action == 0) {
302 printf("%s\n", ret ? "not found" : "found");
303 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
304 }
305 break;
306 case NPFCTL_STATS:
307 ret = npfctl_print_stats(fd);
308 break;
309 case NPFCTL_SESSIONS_SAVE:
310 ret = npf_sessions_recv(fd, NPF_SESSDB_PATH);
311 if (ret) {
312 errx(EXIT_FAILURE, "could not save sessions to '%s'",
313 NPF_SESSDB_PATH);
314 }
315 break;
316 case NPFCTL_SESSIONS_LOAD:
317 ret = npf_sessions_send(fd, NPF_SESSDB_PATH);
318 if (ret) {
319 errx(EXIT_FAILURE, "no sessions loaded from '%s'",
320 NPF_SESSDB_PATH);
321 }
322 break;
323 }
324 if (ret) {
325 err(EXIT_FAILURE, "ioctl");
326 }
327 close(fd);
328 }
329
330 int
331 main(int argc, char **argv)
332 {
333 char *cmd;
334
335 if (argc < 2) {
336 usage();
337 }
338 cmd = argv[1];
339
340 if (strcmp(cmd, "debug") == 0) {
341 const char *cfg = argc > 2 ? argv[2] : "/tmp/npf.conf";
342 npfctl_config_init(true);
343 npfctl_parsecfg(cfg);
344 npfctl_config_send(0);
345 return EXIT_SUCCESS;
346 }
347
348 /* Find and call the subroutine. */
349 for (int n = 0; operations[n].cmd != NULL; n++) {
350 if (strcmp(cmd, operations[n].cmd) != 0)
351 continue;
352 npfctl(operations[n].action, argc, argv);
353 return EXIT_SUCCESS;
354 }
355 usage();
356 }
357