npfctl.c revision 1.4.2.1 1 /* $NetBSD: npfctl.c,v 1.4.2.1 2011/02/08 16:20:15 bouyer 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.2.1 2011/02/08 16:20:15 bouyer 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 = 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_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 = npf_sessions_recv(fd, NPF_SESSDB_PATH);
266 if (ret) {
267 errx(EXIT_FAILURE, "could not save sessions to '%s'",
268 NPF_SESSDB_PATH);
269 }
270 break;
271 case NPFCTL_SESSIONS_LOAD:
272 ret = npf_sessions_send(fd, NPF_SESSDB_PATH);
273 if (ret) {
274 errx(EXIT_FAILURE, "no sessions loaded from '%s'",
275 NPF_SESSDB_PATH);
276 }
277 break;
278 }
279 if (ret == -1) {
280 err(EXIT_FAILURE, "ioctl");
281 }
282 close(fd);
283 }
284
285 int
286 main(int argc, char **argv)
287 {
288 char *cmd;
289 int n;
290
291 if (argc < 2) {
292 usage();
293 }
294 cmd = argv[1];
295
296 #ifdef _NPF_TESTING
297 /* Special testing case. */
298 npfctl_init_data();
299 npfctl_parsecfg("npf.conf");
300 npfctl_ioctl_send(0);
301 return 0;
302 #endif
303
304 /* Find and call the subroutine */
305 for (n = 0; operations[n].cmd != NULL; n++) {
306 if (strcmp(cmd, operations[n].cmd) != 0)
307 continue;
308 npfctl(operations[n].action, argc, argv);
309 return 0;
310 }
311 usage();
312 return 0;
313 }
314