npfctl.c revision 1.7 1 /* $NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan 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.7 2011/11/04 01:00:28 zoltan 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 __dead 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 = npf_sessions_send(fd, NULL);
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_cidr(arg,
256 npfctl_get_addrfamily(arg),
257 &tbl.nct_addr, &tbl.nct_mask)) {
258 errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
259 }
260 ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
261 break;
262 case NPFCTL_STATS:
263 ret = npfctl_print_stats(fd);
264 break;
265 case NPFCTL_SESSIONS_SAVE:
266 ret = npf_sessions_recv(fd, NPF_SESSDB_PATH);
267 if (ret) {
268 errx(EXIT_FAILURE, "could not save sessions to '%s'",
269 NPF_SESSDB_PATH);
270 }
271 break;
272 case NPFCTL_SESSIONS_LOAD:
273 ret = npf_sessions_send(fd, NPF_SESSDB_PATH);
274 if (ret) {
275 errx(EXIT_FAILURE, "no sessions loaded from '%s'",
276 NPF_SESSDB_PATH);
277 }
278 break;
279 }
280 if (ret) {
281 err(EXIT_FAILURE, "ioctl");
282 }
283 close(fd);
284 }
285
286 int
287 main(int argc, char **argv)
288 {
289 char *cmd;
290 int n;
291
292 if (argc < 2) {
293 usage();
294 }
295 cmd = argv[1];
296
297 #ifdef _NPF_TESTING
298 /* Special testing case. */
299 npfctl_init_data();
300 npfctl_parsecfg("npf.conf");
301 npfctl_ioctl_send(0);
302 return 0;
303 #endif
304
305 /* Find and call the subroutine */
306 for (n = 0; operations[n].cmd != NULL; n++) {
307 if (strcmp(cmd, operations[n].cmd) != 0)
308 continue;
309 npfctl(operations[n].action, argc, argv);
310 return 0;
311 }
312 usage();
313 return 0;
314 }
315