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