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