| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 2002 Luigi Rizzo | |
| 3 | * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp | |
| 4 | * Copyright (c) 1994 Ugen J.S.Antsilevich | |
| 5 | * | |
| 6 | * Idea and grammar partially left from: | |
| 7 | * Copyright (c) 1993 Daniel Boulet | |
| 8 | * | |
| 9 | * Redistribution and use in source forms, with and without modification, | |
| 10 | * are permitted provided that this entire comment appears intact. | |
| 11 | * | |
| 12 | * Redistribution in binary form may occur without any restrictions. | |
| 13 | * Obviously, it would be nice if you gave credit where credit is due | |
| 14 | * but requiring it would be too onerous. | |
| 15 | * | |
| 16 | * This software is provided ``AS IS'' without any warranties of any kind. | |
| 17 | * | |
| 18 | * NEW command line interface for IP firewall facility | |
| 19 | * | |
| 20 | * $FreeBSD: src/sbin/ipfw/ipfw2.c,v 1.4.2.13 2003/05/27 22:21:11 gshapiro Exp $ | |
| b78533e2 | 21 | * $DragonFly: src/sbin/ipfw/ipfw2.c,v 1.12 2007/11/05 08:58:35 sephe Exp $ |
| 984263bc MD |
22 | */ |
| 23 | ||
| 24 | #include <sys/param.h> | |
| 25 | #include <sys/mbuf.h> | |
| 26 | #include <sys/socket.h> | |
| 27 | #include <sys/sockio.h> | |
| 28 | #include <sys/sysctl.h> | |
| 29 | #include <sys/time.h> | |
| 30 | #include <sys/wait.h> | |
| 31 | ||
| 32 | #include <ctype.h> | |
| 33 | #include <err.h> | |
| 34 | #include <errno.h> | |
| 35 | #include <grp.h> | |
| 36 | #include <limits.h> | |
| 37 | #include <netdb.h> | |
| 38 | #include <pwd.h> | |
| 39 | #include <signal.h> | |
| 40 | #include <stdio.h> | |
| 41 | #include <stdlib.h> | |
| 42 | #include <stdarg.h> | |
| 43 | #include <string.h> | |
| 44 | #include <unistd.h> | |
| 45 | #include <sysexits.h> | |
| 46 | ||
| 47 | #include <net/if.h> | |
| 48 | #include <netinet/in.h> | |
| 49 | #include <netinet/in_systm.h> | |
| 50 | #include <netinet/ip.h> | |
| 51 | #include <netinet/ip_icmp.h> | |
| 38a690d7 | 52 | #include <net/ipfw/ip_fw.h> |
| 984263bc | 53 | #include <net/route.h> /* def. of struct route */ |
| f9edcb74 | 54 | #include <net/ethernet.h> |
| 38a690d7 | 55 | #include <net/dummynet/ip_dummynet.h> |
| 984263bc MD |
56 | #include <netinet/tcp.h> |
| 57 | #include <arpa/inet.h> | |
| 58 | ||
| 59 | int s, /* main RAW socket */ | |
| 60 | do_resolv, /* Would try to resolve all */ | |
| 61 | do_acct, /* Show packet/byte count */ | |
| 62 | do_time, /* Show time stamps */ | |
| 63 | do_quiet, /* Be quiet in add and flush */ | |
| 64 | do_force, /* Don't ask for confirmation */ | |
| 65 | do_pipe, /* this cmd refers to a pipe */ | |
| 66 | do_sort, /* field to sort results (0 = no) */ | |
| 67 | do_dynamic, /* display dynamic rules */ | |
| 68 | do_expired, /* display expired dynamic rules */ | |
| 69 | do_compact, /* show rules in compact mode */ | |
| 70 | show_sets, /* display rule sets */ | |
| 71 | verbose; | |
| 72 | ||
| 73 | #define IP_MASK_ALL 0xffffffff | |
| 74 | ||
| 75 | /* | |
| 76 | * structure to hold flag names and associated values to be | |
| 77 | * set in the appropriate masks. | |
| 78 | * A NULL string terminates the array. | |
| 79 | * Often, an element with 0 value contains an error string. | |
| 80 | * | |
| 81 | */ | |
| 82 | struct _s_x { | |
| 83 | char *s; | |
| 84 | int x; | |
| 85 | }; | |
| 86 | ||
| 87 | static struct _s_x f_tcpflags[] = { | |
| 88 | { "syn", TH_SYN }, | |
| 89 | { "fin", TH_FIN }, | |
| 90 | { "ack", TH_ACK }, | |
| 91 | { "psh", TH_PUSH }, | |
| 92 | { "rst", TH_RST }, | |
| 93 | { "urg", TH_URG }, | |
| 94 | { "tcp flag", 0 }, | |
| 95 | { NULL, 0 } | |
| 96 | }; | |
| 97 | ||
| 98 | static struct _s_x f_tcpopts[] = { | |
| 99 | { "mss", IP_FW_TCPOPT_MSS }, | |
| 100 | { "maxseg", IP_FW_TCPOPT_MSS }, | |
| 101 | { "window", IP_FW_TCPOPT_WINDOW }, | |
| 102 | { "sack", IP_FW_TCPOPT_SACK }, | |
| 103 | { "ts", IP_FW_TCPOPT_TS }, | |
| 104 | { "timestamp", IP_FW_TCPOPT_TS }, | |
| 105 | { "cc", IP_FW_TCPOPT_CC }, | |
| 106 | { "tcp option", 0 }, | |
| 107 | { NULL, 0 } | |
| 108 | }; | |
| 109 | ||
| 110 | /* | |
| 111 | * IP options span the range 0 to 255 so we need to remap them | |
| 112 | * (though in fact only the low 5 bits are significant). | |
| 113 | */ | |
| 114 | static struct _s_x f_ipopts[] = { | |
| 115 | { "ssrr", IP_FW_IPOPT_SSRR}, | |
| 116 | { "lsrr", IP_FW_IPOPT_LSRR}, | |
| 117 | { "rr", IP_FW_IPOPT_RR}, | |
| 118 | { "ts", IP_FW_IPOPT_TS}, | |
| 119 | { "ip option", 0 }, | |
| 120 | { NULL, 0 } | |
| 121 | }; | |
| 122 | ||
| 123 | static struct _s_x f_iptos[] = { | |
| 124 | { "lowdelay", IPTOS_LOWDELAY}, | |
| 125 | { "throughput", IPTOS_THROUGHPUT}, | |
| 126 | { "reliability", IPTOS_RELIABILITY}, | |
| 127 | { "mincost", IPTOS_MINCOST}, | |
| 128 | { "congestion", IPTOS_CE}, | |
| 129 | { "ecntransport", IPTOS_ECT}, | |
| 130 | { "ip tos option", 0}, | |
| 131 | { NULL, 0 } | |
| 132 | }; | |
| 133 | ||
| 134 | static struct _s_x limit_masks[] = { | |
| 135 | {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, | |
| 136 | {"src-addr", DYN_SRC_ADDR}, | |
| 137 | {"src-port", DYN_SRC_PORT}, | |
| 138 | {"dst-addr", DYN_DST_ADDR}, | |
| 139 | {"dst-port", DYN_DST_PORT}, | |
| 140 | {NULL, 0} | |
| 141 | }; | |
| 142 | ||
| 143 | /* | |
| 144 | * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines | |
| 145 | * This is only used in this code. | |
| 146 | */ | |
| 147 | #define IPPROTO_ETHERTYPE 0x1000 | |
| 148 | static struct _s_x ether_types[] = { | |
| 149 | /* | |
| 150 | * Note, we cannot use "-:&/" in the names because they are field | |
| 151 | * separators in the type specifications. Also, we use s = NULL as | |
| 152 | * end-delimiter, because a type of 0 can be legal. | |
| 153 | */ | |
| 154 | { "ip", 0x0800 }, | |
| 155 | { "ipv4", 0x0800 }, | |
| 156 | { "ipv6", 0x86dd }, | |
| 157 | { "arp", 0x0806 }, | |
| 158 | { "rarp", 0x8035 }, | |
| 159 | { "vlan", 0x8100 }, | |
| 160 | { "loop", 0x9000 }, | |
| 161 | { "trail", 0x1000 }, | |
| 162 | { "at", 0x809b }, | |
| 163 | { "atalk", 0x809b }, | |
| 164 | { "aarp", 0x80f3 }, | |
| 165 | { "pppoe_disc", 0x8863 }, | |
| 166 | { "pppoe_sess", 0x8864 }, | |
| 167 | { "ipx_8022", 0x00E0 }, | |
| 168 | { "ipx_8023", 0x0000 }, | |
| 169 | { "ipx_ii", 0x8137 }, | |
| 170 | { "ipx_snap", 0x8137 }, | |
| 171 | { "ipx", 0x8137 }, | |
| 172 | { "ns", 0x0600 }, | |
| 173 | { NULL, 0 } | |
| 174 | }; | |
| 175 | ||
| 176 | static void show_usage(void); | |
| 177 | ||
| 178 | enum tokens { | |
| 179 | TOK_NULL=0, | |
| 180 | ||
| 181 | TOK_OR, | |
| 182 | TOK_NOT, | |
| 183 | TOK_STARTBRACE, | |
| 184 | TOK_ENDBRACE, | |
| 185 | ||
| 186 | TOK_ACCEPT, | |
| 187 | TOK_COUNT, | |
| 188 | TOK_PIPE, | |
| 189 | TOK_QUEUE, | |
| 190 | TOK_DIVERT, | |
| 191 | TOK_TEE, | |
| 192 | TOK_FORWARD, | |
| 193 | TOK_SKIPTO, | |
| 194 | TOK_DENY, | |
| 195 | TOK_REJECT, | |
| 196 | TOK_RESET, | |
| 197 | TOK_UNREACH, | |
| 198 | TOK_CHECKSTATE, | |
| 199 | ||
| 200 | TOK_UID, | |
| 201 | TOK_GID, | |
| 202 | TOK_IN, | |
| 203 | TOK_LIMIT, | |
| 204 | TOK_KEEPSTATE, | |
| 205 | TOK_LAYER2, | |
| 206 | TOK_OUT, | |
| 207 | TOK_XMIT, | |
| 208 | TOK_RECV, | |
| 209 | TOK_VIA, | |
| 210 | TOK_FRAG, | |
| 211 | TOK_IPOPTS, | |
| 212 | TOK_IPLEN, | |
| 213 | TOK_IPID, | |
| 214 | TOK_IPPRECEDENCE, | |
| 215 | TOK_IPTOS, | |
| 216 | TOK_IPTTL, | |
| 217 | TOK_IPVER, | |
| 218 | TOK_ESTAB, | |
| 219 | TOK_SETUP, | |
| 220 | TOK_TCPFLAGS, | |
| 221 | TOK_TCPOPTS, | |
| 222 | TOK_TCPSEQ, | |
| 223 | TOK_TCPACK, | |
| 224 | TOK_TCPWIN, | |
| 225 | TOK_ICMPTYPES, | |
| 226 | TOK_MAC, | |
| 227 | TOK_MACTYPE, | |
| 228 | ||
| 229 | TOK_PLR, | |
| 230 | TOK_NOERROR, | |
| 231 | TOK_BUCKETS, | |
| 232 | TOK_DSTIP, | |
| 233 | TOK_SRCIP, | |
| 234 | TOK_DSTPORT, | |
| 235 | TOK_SRCPORT, | |
| 236 | TOK_ALL, | |
| 237 | TOK_MASK, | |
| 238 | TOK_BW, | |
| 239 | TOK_DELAY, | |
| 240 | TOK_RED, | |
| 241 | TOK_GRED, | |
| 242 | TOK_DROPTAIL, | |
| 243 | TOK_PROTO, | |
| 244 | TOK_WEIGHT, | |
| 245 | }; | |
| 246 | ||
| 247 | struct _s_x dummynet_params[] = { | |
| 248 | { "plr", TOK_PLR }, | |
| 249 | { "noerror", TOK_NOERROR }, | |
| 250 | { "buckets", TOK_BUCKETS }, | |
| 251 | { "dst-ip", TOK_DSTIP }, | |
| 252 | { "src-ip", TOK_SRCIP }, | |
| 253 | { "dst-port", TOK_DSTPORT }, | |
| 254 | { "src-port", TOK_SRCPORT }, | |
| 255 | { "proto", TOK_PROTO }, | |
| 256 | { "weight", TOK_WEIGHT }, | |
| 257 | { "all", TOK_ALL }, | |
| 258 | { "mask", TOK_MASK }, | |
| 259 | { "droptail", TOK_DROPTAIL }, | |
| 260 | { "red", TOK_RED }, | |
| 261 | { "gred", TOK_GRED }, | |
| 262 | { "bw", TOK_BW }, | |
| 263 | { "bandwidth", TOK_BW }, | |
| 264 | { "delay", TOK_DELAY }, | |
| 265 | { "pipe", TOK_PIPE }, | |
| 266 | { "queue", TOK_QUEUE }, | |
| 267 | { "dummynet-params", TOK_NULL }, | |
| 268 | { NULL, 0 } | |
| 269 | }; | |
| 270 | ||
| 271 | struct _s_x rule_actions[] = { | |
| 272 | { "accept", TOK_ACCEPT }, | |
| 273 | { "pass", TOK_ACCEPT }, | |
| 274 | { "allow", TOK_ACCEPT }, | |
| 275 | { "permit", TOK_ACCEPT }, | |
| 276 | { "count", TOK_COUNT }, | |
| 277 | { "pipe", TOK_PIPE }, | |
| 278 | { "queue", TOK_QUEUE }, | |
| 279 | { "divert", TOK_DIVERT }, | |
| 280 | { "tee", TOK_TEE }, | |
| 281 | { "fwd", TOK_FORWARD }, | |
| 282 | { "forward", TOK_FORWARD }, | |
| 283 | { "skipto", TOK_SKIPTO }, | |
| 284 | { "deny", TOK_DENY }, | |
| 285 | { "drop", TOK_DENY }, | |
| 286 | { "reject", TOK_REJECT }, | |
| 287 | { "reset", TOK_RESET }, | |
| 288 | { "unreach", TOK_UNREACH }, | |
| 289 | { "check-state", TOK_CHECKSTATE }, | |
| 290 | { NULL, TOK_NULL }, | |
| 291 | { NULL, 0 } | |
| 292 | }; | |
| 293 | ||
| 294 | struct _s_x rule_options[] = { | |
| 295 | { "uid", TOK_UID }, | |
| 296 | { "gid", TOK_GID }, | |
| 297 | { "in", TOK_IN }, | |
| 298 | { "limit", TOK_LIMIT }, | |
| 299 | { "keep-state", TOK_KEEPSTATE }, | |
| 984263bc MD |
300 | { "layer2", TOK_LAYER2 }, |
| 301 | { "out", TOK_OUT }, | |
| 302 | { "xmit", TOK_XMIT }, | |
| 303 | { "recv", TOK_RECV }, | |
| 304 | { "via", TOK_VIA }, | |
| 305 | { "fragment", TOK_FRAG }, | |
| 306 | { "frag", TOK_FRAG }, | |
| 307 | { "ipoptions", TOK_IPOPTS }, | |
| 308 | { "ipopts", TOK_IPOPTS }, | |
| 309 | { "iplen", TOK_IPLEN }, | |
| 310 | { "ipid", TOK_IPID }, | |
| 311 | { "ipprecedence", TOK_IPPRECEDENCE }, | |
| 312 | { "iptos", TOK_IPTOS }, | |
| 313 | { "ipttl", TOK_IPTTL }, | |
| 314 | { "ipversion", TOK_IPVER }, | |
| 315 | { "ipver", TOK_IPVER }, | |
| 316 | { "estab", TOK_ESTAB }, | |
| 317 | { "established", TOK_ESTAB }, | |
| 318 | { "setup", TOK_SETUP }, | |
| 319 | { "tcpflags", TOK_TCPFLAGS }, | |
| 320 | { "tcpflgs", TOK_TCPFLAGS }, | |
| 321 | { "tcpoptions", TOK_TCPOPTS }, | |
| 322 | { "tcpopts", TOK_TCPOPTS }, | |
| 323 | { "tcpseq", TOK_TCPSEQ }, | |
| 324 | { "tcpack", TOK_TCPACK }, | |
| 325 | { "tcpwin", TOK_TCPWIN }, | |
| 326 | { "icmptype", TOK_ICMPTYPES }, | |
| 327 | { "icmptypes", TOK_ICMPTYPES }, | |
| 328 | { "dst-ip", TOK_DSTIP }, | |
| 329 | { "src-ip", TOK_SRCIP }, | |
| 330 | { "dst-port", TOK_DSTPORT }, | |
| 331 | { "src-port", TOK_SRCPORT }, | |
| 332 | { "proto", TOK_PROTO }, | |
| 333 | { "MAC", TOK_MAC }, | |
| 334 | { "mac", TOK_MAC }, | |
| 335 | { "mac-type", TOK_MACTYPE }, | |
| 336 | ||
| 337 | { "not", TOK_NOT }, /* pseudo option */ | |
| 338 | { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ | |
| 339 | { "or", TOK_OR }, /* pseudo option */ | |
| 340 | { "|", /* escape */ TOK_OR }, /* pseudo option */ | |
| 341 | { "{", TOK_STARTBRACE }, /* pseudo option */ | |
| 342 | { "(", TOK_STARTBRACE }, /* pseudo option */ | |
| 343 | { "}", TOK_ENDBRACE }, /* pseudo option */ | |
| 344 | { ")", TOK_ENDBRACE }, /* pseudo option */ | |
| 345 | { NULL, TOK_NULL }, | |
| 346 | { NULL, 0 } | |
| 347 | }; | |
| 348 | ||
| 349 | /** | |
| 350 | * match_token takes a table and a string, returns the value associated | |
| 351 | * with the string (0 meaning an error in most cases) | |
| 352 | */ | |
| 353 | static int | |
| 354 | match_token(struct _s_x *table, char *string) | |
| 355 | { | |
| 356 | struct _s_x *pt; | |
| 357 | int i = strlen(string); | |
| 358 | ||
| 359 | for (pt = table ; i && pt->s != NULL ; pt++) | |
| 360 | if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) | |
| 361 | return pt->x; | |
| 362 | return -1; | |
| 363 | }; | |
| 364 | ||
| 365 | static char * | |
| 366 | match_value(struct _s_x *p, u_int32_t value) | |
| 367 | { | |
| 368 | for (; p->s != NULL; p++) | |
| 369 | if (p->x == value) | |
| 370 | return p->s; | |
| 371 | return NULL; | |
| 372 | } | |
| 373 | ||
| 374 | /* | |
| 375 | * prints one port, symbolic or numeric | |
| 376 | */ | |
| 377 | static void | |
| 378 | print_port(int proto, u_int16_t port) | |
| 379 | { | |
| 380 | ||
| 381 | if (proto == IPPROTO_ETHERTYPE) { | |
| 382 | char *s; | |
| 383 | ||
| 384 | if (do_resolv && (s = match_value(ether_types, port)) ) | |
| 385 | printf("%s", s); | |
| 386 | else | |
| 387 | printf("0x%04x", port); | |
| 388 | } else { | |
| 389 | struct servent *se = NULL; | |
| 390 | if (do_resolv) { | |
| 391 | struct protoent *pe = getprotobynumber(proto); | |
| 392 | ||
| 393 | se = getservbyport(htons(port), pe ? pe->p_name : NULL); | |
| 394 | } | |
| 395 | if (se) | |
| 396 | printf("%s", se->s_name); | |
| 397 | else | |
| 398 | printf("%d", port); | |
| 399 | } | |
| 400 | } | |
| 401 | ||
| 402 | /* | |
| 403 | * print the values in a list of ports | |
| 404 | * XXX todo: add support for mask. | |
| 405 | */ | |
| 406 | static void | |
| 407 | print_newports(ipfw_insn_u16 *cmd, int proto, int opcode) | |
| 408 | { | |
| 409 | u_int16_t *p = cmd->ports; | |
| 410 | int i; | |
| 411 | char *sep= " "; | |
| 412 | ||
| 413 | if (cmd->o.len & F_NOT) | |
| 414 | printf(" not"); | |
| 415 | if (opcode != 0) | |
| 416 | printf ("%s", opcode == O_MAC_TYPE ? " mac-type" : | |
| 417 | (opcode == O_IP_DSTPORT ? " dst-port" : " src-port")); | |
| 418 | for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { | |
| 419 | printf(sep); | |
| 420 | print_port(proto, p[0]); | |
| 421 | if (p[0] != p[1]) { | |
| 422 | printf("-"); | |
| 423 | print_port(proto, p[1]); | |
| 424 | } | |
| 425 | sep = ","; | |
| 426 | } | |
| 427 | } | |
| 428 | ||
| 429 | /* | |
| 430 | * Like strtol, but also translates service names into port numbers | |
| 431 | * for some protocols. | |
| 432 | * In particular: | |
| 433 | * proto == -1 disables the protocol check; | |
| 434 | * proto == IPPROTO_ETHERTYPE looks up an internal table | |
| 435 | * proto == <some value in /etc/protocols> matches the values there. | |
| 436 | * Returns *end == s in case the parameter is not found. | |
| 437 | */ | |
| 438 | static int | |
| 439 | strtoport(char *s, char **end, int base, int proto) | |
| 440 | { | |
| 441 | char *p, *buf; | |
| 442 | char *s1; | |
| 443 | int i; | |
| 444 | ||
| 445 | *end = s; /* default - not found */ | |
| 446 | if ( *s == '\0') | |
| 447 | return 0; /* not found */ | |
| 448 | ||
| 449 | if (isdigit(*s)) | |
| 450 | return strtol(s, end, base); | |
| 451 | ||
| 452 | /* | |
| 453 | * find separator. '\\' escapes the next char. | |
| 454 | */ | |
| 455 | for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) | |
| 456 | if (*s1 == '\\' && s1[1] != '\0') | |
| 457 | s1++; | |
| 458 | ||
| 459 | buf = malloc(s1 - s + 1); | |
| 460 | if (buf == NULL) | |
| 461 | return 0; | |
| 462 | ||
| 463 | /* | |
| 464 | * copy into a buffer skipping backslashes | |
| 465 | */ | |
| 466 | for (p = s, i = 0; p != s1 ; p++) | |
| 467 | if ( *p != '\\') | |
| 468 | buf[i++] = *p; | |
| 469 | buf[i++] = '\0'; | |
| 470 | ||
| 471 | if (proto == IPPROTO_ETHERTYPE) { | |
| 472 | i = match_token(ether_types, buf); | |
| 473 | free(buf); | |
| 474 | if (i != -1) { /* found */ | |
| 475 | *end = s1; | |
| 476 | return i; | |
| 477 | } | |
| 478 | } else { | |
| 479 | struct protoent *pe = NULL; | |
| 480 | struct servent *se; | |
| 481 | ||
| 482 | if (proto != 0) | |
| 483 | pe = getprotobynumber(proto); | |
| 484 | setservent(1); | |
| 485 | se = getservbyname(buf, pe ? pe->p_name : NULL); | |
| 486 | free(buf); | |
| 487 | if (se != NULL) { | |
| 488 | *end = s1; | |
| 489 | return ntohs(se->s_port); | |
| 490 | } | |
| 491 | } | |
| 492 | return 0; /* not found */ | |
| 493 | } | |
| 494 | ||
| 495 | /* | |
| 496 | * fill the body of the command with the list of port ranges. | |
| 497 | * At the moment it only understands numeric ranges. | |
| 498 | */ | |
| 499 | static int | |
| 500 | fill_newports(ipfw_insn_u16 *cmd, char *av, int proto) | |
| 501 | { | |
| 502 | u_int16_t *p = cmd->ports; | |
| 503 | int i = 0; | |
| 504 | char *s = av; | |
| 505 | ||
| 506 | while (*s) { | |
| 507 | u_int16_t a, b; | |
| 508 | ||
| 509 | a = strtoport(av, &s, 0, proto); | |
| 510 | if (s == av) /* no parameter */ | |
| 511 | break; | |
| 512 | if (*s == '-') { /* a range */ | |
| 513 | av = s+1; | |
| 514 | b = strtoport(av, &s, 0, proto); | |
| 515 | if (s == av) /* no parameter */ | |
| 516 | break; | |
| 517 | p[0] = a; | |
| 518 | p[1] = b; | |
| 519 | } else if (*s == ',' || *s == '\0' ) { | |
| 520 | p[0] = p[1] = a; | |
| 521 | } else { /* invalid separator */ | |
| 522 | errx(EX_DATAERR, "invalid separator <%c> in <%s>\n", | |
| 523 | *s, av); | |
| 524 | } | |
| 525 | i++; | |
| 526 | p += 2; | |
| 527 | av = s+1; | |
| 528 | } | |
| 529 | if (i > 0) { | |
| 530 | if (i+1 > F_LEN_MASK) | |
| 531 | errx(EX_DATAERR, "too many ports/ranges\n"); | |
| 532 | cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */ | |
| 533 | } | |
| 534 | return i; | |
| 535 | } | |
| 536 | ||
| 537 | static struct _s_x icmpcodes[] = { | |
| 538 | { "net", ICMP_UNREACH_NET }, | |
| 539 | { "host", ICMP_UNREACH_HOST }, | |
| 540 | { "protocol", ICMP_UNREACH_PROTOCOL }, | |
| 541 | { "port", ICMP_UNREACH_PORT }, | |
| 542 | { "needfrag", ICMP_UNREACH_NEEDFRAG }, | |
| 543 | { "srcfail", ICMP_UNREACH_SRCFAIL }, | |
| 544 | { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, | |
| 545 | { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, | |
| 546 | { "isolated", ICMP_UNREACH_ISOLATED }, | |
| 547 | { "net-prohib", ICMP_UNREACH_NET_PROHIB }, | |
| 548 | { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, | |
| 549 | { "tosnet", ICMP_UNREACH_TOSNET }, | |
| 550 | { "toshost", ICMP_UNREACH_TOSHOST }, | |
| 551 | { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, | |
| 552 | { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, | |
| 553 | { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, | |
| 554 | { NULL, 0 } | |
| 555 | }; | |
| 556 | ||
| 557 | static void | |
| 558 | fill_reject_code(u_short *codep, char *str) | |
| 559 | { | |
| 560 | int val; | |
| 561 | char *s; | |
| 562 | ||
| 563 | val = strtoul(str, &s, 0); | |
| 564 | if (s == str || *s != '\0' || val >= 0x100) | |
| 565 | val = match_token(icmpcodes, str); | |
| 566 | if (val < 0) | |
| 567 | errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); | |
| 568 | *codep = val; | |
| 569 | return; | |
| 570 | } | |
| 571 | ||
| 572 | static void | |
| 573 | print_reject_code(u_int16_t code) | |
| 574 | { | |
| 575 | char *s = match_value(icmpcodes, code); | |
| 576 | ||
| 577 | if (s != NULL) | |
| 578 | printf("unreach %s", s); | |
| 579 | else | |
| 580 | printf("unreach %u", code); | |
| 581 | } | |
| 582 | ||
| 583 | /* | |
| 584 | * Returns the number of bits set (from left) in a contiguous bitmask, | |
| 585 | * or -1 if the mask is not contiguous. | |
| 586 | * XXX this needs a proper fix. | |
| 587 | * This effectively works on masks in big-endian (network) format. | |
| 588 | * when compiled on little endian architectures. | |
| 589 | * | |
| 590 | * First bit is bit 7 of the first byte -- note, for MAC addresses, | |
| 591 | * the first bit on the wire is bit 0 of the first byte. | |
| 592 | * len is the max length in bits. | |
| 593 | */ | |
| 594 | static int | |
| 595 | contigmask(u_char *p, int len) | |
| 596 | { | |
| 597 | int i, n; | |
| 598 | for (i=0; i<len ; i++) | |
| 599 | if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */ | |
| 600 | break; | |
| 601 | for (n=i+1; n < len; n++) | |
| 602 | if ( (p[n/8] & (1 << (7 - (n%8)))) != 0) | |
| 603 | return -1; /* mask not contiguous */ | |
| 604 | return i; | |
| 605 | } | |
| 606 | ||
| 607 | /* | |
| 608 | * print flags set/clear in the two bitmasks passed as parameters. | |
| 609 | * There is a specialized check for f_tcpflags. | |
| 610 | */ | |
| 611 | static void | |
| 612 | print_flags(char *name, ipfw_insn *cmd, struct _s_x *list) | |
| 613 | { | |
| 614 | char *comma=""; | |
| 615 | int i; | |
| 616 | u_char set = cmd->arg1 & 0xff; | |
| 617 | u_char clear = (cmd->arg1 >> 8) & 0xff; | |
| 618 | ||
| 619 | if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { | |
| 620 | printf(" setup"); | |
| 621 | return; | |
| 622 | } | |
| 623 | ||
| 624 | printf(" %s ", name); | |
| 625 | for (i=0; list[i].x != 0; i++) { | |
| 626 | if (set & list[i].x) { | |
| 627 | set &= ~list[i].x; | |
| 628 | printf("%s%s", comma, list[i].s); | |
| 629 | comma = ","; | |
| 630 | } | |
| 631 | if (clear & list[i].x) { | |
| 632 | clear &= ~list[i].x; | |
| 633 | printf("%s!%s", comma, list[i].s); | |
| 634 | comma = ","; | |
| 635 | } | |
| 636 | } | |
| 637 | } | |
| 638 | ||
| 639 | /* | |
| 640 | * Print the ip address contained in a command. | |
| 641 | */ | |
| 642 | static void | |
| 643 | print_ip(ipfw_insn_ip *cmd, char *s) | |
| 644 | { | |
| 645 | struct hostent *he = NULL; | |
| 646 | int mb; | |
| 647 | ||
| 648 | printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); | |
| 649 | ||
| 650 | if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { | |
| 651 | printf("me"); | |
| 652 | return; | |
| 653 | } | |
| 654 | if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { | |
| 655 | u_int32_t x, *d; | |
| 656 | int i; | |
| 657 | char comma = '{'; | |
| 658 | ||
| 659 | x = cmd->o.arg1 - 1; | |
| 660 | x = htonl( ~x ); | |
| 661 | cmd->addr.s_addr = htonl(cmd->addr.s_addr); | |
| 662 | printf("%s/%d", inet_ntoa(cmd->addr), | |
| 663 | contigmask((u_char *)&x, 32)); | |
| 664 | x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); | |
| 665 | x &= 0xff; /* base */ | |
| 666 | d = (u_int32_t *)&(cmd->mask); | |
| 667 | for (i=0; i < cmd->o.arg1; i++) | |
| 668 | if (d[ i/32] & (1<<(i & 31))) { | |
| 669 | printf("%c%d", comma, i+x); | |
| 670 | comma = ','; | |
| 671 | } | |
| 672 | printf("}"); | |
| 673 | return; | |
| 674 | } | |
| 675 | if (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) | |
| 676 | mb = 32; | |
| 677 | else | |
| 678 | mb = contigmask((u_char *)&(cmd->mask.s_addr), 32); | |
| 679 | if (mb == 32 && do_resolv) | |
| 15b85273 SW |
680 | he = gethostbyaddr(&(cmd->addr.s_addr), sizeof(u_long), |
| 681 | AF_INET); | |
| 984263bc MD |
682 | if (he != NULL) /* resolved to name */ |
| 683 | printf("%s", he->h_name); | |
| 684 | else if (mb == 0) /* any */ | |
| 685 | printf("any"); | |
| 686 | else { /* numeric IP followed by some kind of mask */ | |
| 687 | printf("%s", inet_ntoa(cmd->addr)); | |
| 688 | if (mb < 0) | |
| 689 | printf(":%s", inet_ntoa(cmd->mask)); | |
| 690 | else if (mb < 32) | |
| 691 | printf("/%d", mb); | |
| 692 | } | |
| 693 | } | |
| 694 | ||
| 695 | /* | |
| 696 | * prints a MAC address/mask pair | |
| 697 | */ | |
| 698 | static void | |
| 699 | print_mac(u_char *addr, u_char *mask) | |
| 700 | { | |
| 701 | int l = contigmask(mask, 48); | |
| 702 | ||
| 703 | if (l == 0) | |
| 704 | printf(" any"); | |
| 705 | else { | |
| 706 | printf(" %02x:%02x:%02x:%02x:%02x:%02x", | |
| 707 | addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); | |
| 708 | if (l == -1) | |
| 709 | printf("&%02x:%02x:%02x:%02x:%02x:%02x", | |
| 710 | mask[0], mask[1], mask[2], | |
| 711 | mask[3], mask[4], mask[5]); | |
| 712 | else if (l < 48) | |
| 713 | printf("/%d", l); | |
| 714 | } | |
| 715 | } | |
| 716 | ||
| 717 | static void | |
| 718 | fill_icmptypes(ipfw_insn_u32 *cmd, char *av) | |
| 719 | { | |
| 720 | u_int8_t type; | |
| 721 | ||
| 722 | cmd->d[0] = 0; | |
| 723 | while (*av) { | |
| 724 | if (*av == ',') | |
| 725 | av++; | |
| 726 | ||
| 727 | type = strtoul(av, &av, 0); | |
| 728 | ||
| 729 | if (*av != ',' && *av != '\0') | |
| 730 | errx(EX_DATAERR, "invalid ICMP type"); | |
| 731 | ||
| 732 | if (type > 31) | |
| 733 | errx(EX_DATAERR, "ICMP type out of range"); | |
| 734 | ||
| 735 | cmd->d[0] |= 1 << type; | |
| 736 | } | |
| 737 | cmd->o.opcode = O_ICMPTYPE; | |
| 738 | cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); | |
| 739 | } | |
| 740 | ||
| 741 | static void | |
| 742 | print_icmptypes(ipfw_insn_u32 *cmd) | |
| 743 | { | |
| 744 | int i; | |
| 745 | char sep= ' '; | |
| 746 | ||
| 747 | printf(" icmptypes"); | |
| 748 | for (i = 0; i < 32; i++) { | |
| 749 | if ( (cmd->d[0] & (1 << (i))) == 0) | |
| 750 | continue; | |
| 751 | printf("%c%d", sep, i); | |
| 752 | sep = ','; | |
| 753 | } | |
| 754 | } | |
| 755 | ||
| 756 | /* | |
| 757 | * show_ipfw() prints the body of an ipfw rule. | |
| 758 | * Because the standard rule has at least proto src_ip dst_ip, we use | |
| 759 | * a helper function to produce these entries if not provided explicitly. | |
| 760 | * The first argument is the list of fields we have, the second is | |
| 761 | * the list of fields we want to be printed. | |
| 762 | * | |
| 763 | * Special cases if we have provided a MAC header: | |
| 764 | * + if the rule does not contain IP addresses/ports, do not print them; | |
| 765 | * + if the rule does not contain an IP proto, print "all" instead of "ip"; | |
| 766 | * | |
| 767 | * Once we have 'have_options', IP header fields are printed as options. | |
| 768 | */ | |
| 769 | #define HAVE_PROTO 0x0001 | |
| 770 | #define HAVE_SRCIP 0x0002 | |
| 771 | #define HAVE_DSTIP 0x0004 | |
| 772 | #define HAVE_MAC 0x0008 | |
| 773 | #define HAVE_MACTYPE 0x0010 | |
| 774 | #define HAVE_OPTIONS 0x8000 | |
| 775 | ||
| 776 | #define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) | |
| 777 | static void | |
| 778 | show_prerequisites(int *flags, int want, int cmd) | |
| 779 | { | |
| 780 | if ( (*flags & HAVE_IP) == HAVE_IP) | |
| 781 | *flags |= HAVE_OPTIONS; | |
| 782 | ||
| 783 | if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC && | |
| 784 | cmd != O_MAC_TYPE) { | |
| 785 | /* | |
| 786 | * mac-type was optimized out by the compiler, | |
| 787 | * restore it | |
| 788 | */ | |
| 789 | printf(" any"); | |
| 790 | *flags |= HAVE_MACTYPE | HAVE_OPTIONS; | |
| 791 | return; | |
| 792 | } | |
| 793 | if ( !(*flags & HAVE_OPTIONS)) { | |
| 794 | if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) | |
| 795 | printf(" ip"); | |
| 796 | if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) | |
| 797 | printf(" from any"); | |
| 798 | if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP)) | |
| 799 | printf(" to any"); | |
| 800 | } | |
| 801 | *flags |= want; | |
| 802 | } | |
| 803 | ||
| 804 | static void | |
| b78533e2 | 805 | show_ipfw(struct ipfw_ioc_rule *rule, int pcwidth, int bcwidth) |
| 984263bc MD |
806 | { |
| 807 | static int twidth = 0; | |
| 808 | int l; | |
| 809 | ipfw_insn *cmd; | |
| 810 | int proto = 0; /* default */ | |
| 811 | int flags = 0; /* prerequisites */ | |
| 812 | ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ | |
| 813 | int or_block = 0; /* we are in an or block */ | |
| 814 | ||
| b78533e2 | 815 | u_int32_t set_disable = rule->set_disable; |
| 984263bc MD |
816 | |
| 817 | if (set_disable & (1 << rule->set)) { /* disabled */ | |
| 818 | if (!show_sets) | |
| 819 | return; | |
| 820 | else | |
| 821 | printf("# DISABLED "); | |
| 822 | } | |
| 823 | printf("%05u ", rule->rulenum); | |
| 824 | ||
| 825 | if (do_acct) | |
| 826 | printf("%*llu %*llu ", pcwidth, rule->pcnt, bcwidth, | |
| 827 | rule->bcnt); | |
| 828 | ||
| 829 | if (do_time) { | |
| 830 | char timestr[30]; | |
| 831 | ||
| 832 | if (twidth == 0) { | |
| 833 | strcpy(timestr, ctime((time_t *)&twidth)); | |
| 834 | *strchr(timestr, '\n') = '\0'; | |
| 835 | twidth = strlen(timestr); | |
| 836 | } | |
| 837 | if (rule->timestamp) { | |
| 838 | #if _FreeBSD_version < 500000 /* XXX check */ | |
| 839 | #define _long_to_time(x) (time_t)(x) | |
| 840 | #endif | |
| 841 | time_t t = _long_to_time(rule->timestamp); | |
| 842 | ||
| 843 | strcpy(timestr, ctime(&t)); | |
| 844 | *strchr(timestr, '\n') = '\0'; | |
| 845 | printf("%s ", timestr); | |
| 846 | } else { | |
| 847 | printf("%*s ", twidth, " "); | |
| 848 | } | |
| 849 | } | |
| 850 | ||
| 851 | if (show_sets) | |
| 852 | printf("set %d ", rule->set); | |
| 853 | ||
| 854 | /* | |
| 855 | * print the optional "match probability" | |
| 856 | */ | |
| 857 | if (rule->cmd_len > 0) { | |
| 858 | cmd = rule->cmd ; | |
| 859 | if (cmd->opcode == O_PROB) { | |
| 860 | ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd; | |
| 861 | double d = 1.0 * p->d[0]; | |
| 862 | ||
| 863 | d = (d / 0x7fffffff); | |
| 864 | printf("prob %f ", d); | |
| 865 | } | |
| 866 | } | |
| 867 | ||
| 868 | /* | |
| 869 | * first print actions | |
| 870 | */ | |
| 871 | for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule); | |
| 872 | l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { | |
| 873 | switch(cmd->opcode) { | |
| 874 | case O_CHECK_STATE: | |
| 875 | printf("check-state"); | |
| 876 | flags = HAVE_IP; /* avoid printing anything else */ | |
| 877 | break; | |
| 878 | ||
| 879 | case O_ACCEPT: | |
| 880 | printf("allow"); | |
| 881 | break; | |
| 882 | ||
| 883 | case O_COUNT: | |
| 884 | printf("count"); | |
| 885 | break; | |
| 886 | ||
| 887 | case O_DENY: | |
| 888 | printf("deny"); | |
| 889 | break; | |
| 890 | ||
| 891 | case O_REJECT: | |
| 892 | if (cmd->arg1 == ICMP_REJECT_RST) | |
| 893 | printf("reset"); | |
| 894 | else if (cmd->arg1 == ICMP_UNREACH_HOST) | |
| 895 | printf("reject"); | |
| 896 | else | |
| 897 | print_reject_code(cmd->arg1); | |
| 898 | break; | |
| 899 | ||
| 900 | case O_SKIPTO: | |
| 901 | printf("skipto %u", cmd->arg1); | |
| 902 | break; | |
| 903 | ||
| 904 | case O_PIPE: | |
| 905 | printf("pipe %u", cmd->arg1); | |
| 906 | break; | |
| 907 | ||
| 908 | case O_QUEUE: | |
| 909 | printf("queue %u", cmd->arg1); | |
| 910 | break; | |
| 911 | ||
| 912 | case O_DIVERT: | |
| 913 | printf("divert %u", cmd->arg1); | |
| 914 | break; | |
| 915 | ||
| 916 | case O_TEE: | |
| 917 | printf("tee %u", cmd->arg1); | |
| 918 | break; | |
| 919 | ||
| 920 | case O_FORWARD_IP: | |
| 921 | { | |
| 922 | ipfw_insn_sa *s = (ipfw_insn_sa *)cmd; | |
| 923 | ||
| 924 | printf("fwd %s", inet_ntoa(s->sa.sin_addr)); | |
| 925 | if (s->sa.sin_port) | |
| 926 | printf(",%d", s->sa.sin_port); | |
| 927 | } | |
| 928 | break; | |
| 929 | ||
| 930 | case O_LOG: /* O_LOG is printed last */ | |
| 931 | logptr = (ipfw_insn_log *)cmd; | |
| 932 | break; | |
| 933 | ||
| 934 | default: | |
| 935 | printf("** unrecognized action %d len %d", | |
| 936 | cmd->opcode, cmd->len); | |
| 937 | } | |
| 938 | } | |
| 939 | if (logptr) { | |
| 940 | if (logptr->max_log > 0) | |
| 941 | printf(" log logamount %d", logptr->max_log); | |
| 942 | else | |
| 943 | printf(" log"); | |
| 944 | } | |
| 945 | ||
| 946 | /* | |
| 947 | * then print the body. | |
| 948 | */ | |
| b78533e2 SZ |
949 | if (rule->usr_flags & IPFW_USR_F_NORULE) { |
| 950 | /* empty rules before options */ | |
| 984263bc MD |
951 | if (!do_compact) |
| 952 | printf(" ip from any to any"); | |
| 953 | flags |= HAVE_IP | HAVE_OPTIONS; | |
| 954 | } | |
| 955 | ||
| 956 | for (l = rule->act_ofs, cmd = rule->cmd ; | |
| 957 | l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { | |
| 958 | /* useful alias */ | |
| 959 | ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; | |
| 960 | ||
| 961 | show_prerequisites(&flags, 0, cmd->opcode); | |
| 962 | ||
| 963 | switch(cmd->opcode) { | |
| 964 | case O_PROB: | |
| 965 | break; /* done already */ | |
| 966 | ||
| 967 | case O_PROBE_STATE: | |
| 968 | break; /* no need to print anything here */ | |
| 969 | ||
| 970 | case O_MACADDR2: { | |
| 971 | ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; | |
| 972 | ||
| 973 | if ((cmd->len & F_OR) && !or_block) | |
| 974 | printf(" {"); | |
| 975 | if (cmd->len & F_NOT) | |
| 976 | printf(" not"); | |
| 977 | printf(" MAC"); | |
| 978 | flags |= HAVE_MAC; | |
| 979 | print_mac( m->addr, m->mask); | |
| 980 | print_mac( m->addr + 6, m->mask + 6); | |
| 981 | } | |
| 982 | break; | |
| 983 | ||
| 984 | case O_MAC_TYPE: | |
| 985 | if ((cmd->len & F_OR) && !or_block) | |
| 986 | printf(" {"); | |
| 987 | print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE, | |
| 988 | (flags & HAVE_OPTIONS) ? cmd->opcode : 0); | |
| 989 | flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS; | |
| 990 | break; | |
| 991 | ||
| 992 | case O_IP_SRC: | |
| 993 | case O_IP_SRC_MASK: | |
| 994 | case O_IP_SRC_ME: | |
| 995 | case O_IP_SRC_SET: | |
| 996 | show_prerequisites(&flags, HAVE_PROTO, 0); | |
| 997 | if (!(flags & HAVE_SRCIP)) | |
| 998 | printf(" from"); | |
| 999 | if ((cmd->len & F_OR) && !or_block) | |
| 1000 | printf(" {"); | |
| 1001 | print_ip((ipfw_insn_ip *)cmd, | |
| 1002 | (flags & HAVE_OPTIONS) ? " src-ip" : ""); | |
| 1003 | flags |= HAVE_SRCIP; | |
| 1004 | break; | |
| 1005 | ||
| 1006 | case O_IP_DST: | |
| 1007 | case O_IP_DST_MASK: | |
| 1008 | case O_IP_DST_ME: | |
| 1009 | case O_IP_DST_SET: | |
| 1010 | show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); | |
| 1011 | if (!(flags & HAVE_DSTIP)) | |
| 1012 | printf(" to"); | |
| 1013 | if ((cmd->len & F_OR) && !or_block) | |
| 1014 | printf(" {"); | |
| 1015 | print_ip((ipfw_insn_ip *)cmd, | |
| 1016 | (flags & HAVE_OPTIONS) ? " dst-ip" : ""); | |
| 1017 | flags |= HAVE_DSTIP; | |
| 1018 | break; | |
| 1019 | ||
| 1020 | case O_IP_DSTPORT: | |
| 1021 | show_prerequisites(&flags, HAVE_IP, 0); | |
| 1022 | case O_IP_SRCPORT: | |
| 1023 | show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); | |
| 1024 | if ((cmd->len & F_OR) && !or_block) | |
| 1025 | printf(" {"); | |
| 1026 | print_newports((ipfw_insn_u16 *)cmd, proto, | |
| 1027 | (flags & HAVE_OPTIONS) ? cmd->opcode : 0); | |
| 1028 | break; | |
| 1029 | ||
| 1030 | case O_PROTO: { | |
| 1031 | struct protoent *pe; | |
| 1032 | ||
| 1033 | if ((cmd->len & F_OR) && !or_block) | |
| 1034 | printf(" {"); | |
| 1035 | if (cmd->len & F_NOT) | |
| 1036 | printf(" not"); | |
| 1037 | proto = cmd->arg1; | |
| 1038 | pe = getprotobynumber(cmd->arg1); | |
| 1039 | if (flags & HAVE_OPTIONS) | |
| 1040 | printf(" proto"); | |
| 1041 | if (pe) | |
| 1042 | printf(" %s", pe->p_name); | |
| 1043 | else | |
| 1044 | printf(" %u", cmd->arg1); | |
| 1045 | } | |
| 1046 | flags |= HAVE_PROTO; | |
| 1047 | break; | |
| 1048 | ||
| 1049 | default: /*options ... */ | |
| 1050 | show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0); | |
| 1051 | if ((cmd->len & F_OR) && !or_block) | |
| 1052 | printf(" {"); | |
| 1053 | if (cmd->len & F_NOT && cmd->opcode != O_IN) | |
| 1054 | printf(" not"); | |
| 1055 | switch(cmd->opcode) { | |
| 1056 | case O_FRAG: | |
| 1057 | printf(" frag"); | |
| 1058 | break; | |
| 1059 | ||
| 1060 | case O_IN: | |
| 1061 | printf(cmd->len & F_NOT ? " out" : " in"); | |
| 1062 | break; | |
| 1063 | ||
| 1064 | case O_LAYER2: | |
| 1065 | printf(" layer2"); | |
| 1066 | break; | |
| 1067 | case O_XMIT: | |
| 1068 | case O_RECV: | |
| 1069 | case O_VIA: { | |
| 1070 | char *s; | |
| 1071 | ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd; | |
| 1072 | ||
| 1073 | if (cmd->opcode == O_XMIT) | |
| 1074 | s = "xmit"; | |
| 1075 | else if (cmd->opcode == O_RECV) | |
| 1076 | s = "recv"; | |
| 1077 | else if (cmd->opcode == O_VIA) | |
| 1078 | s = "via"; | |
| 7700730e MD |
1079 | else |
| 1080 | s = "?huh?"; | |
| 984263bc MD |
1081 | if (cmdif->name[0] == '\0') |
| 1082 | printf(" %s %s", s, | |
| 1083 | inet_ntoa(cmdif->p.ip)); | |
| 3e4a09e7 | 1084 | printf(" %s %s", s, cmdif->name); |
| 984263bc MD |
1085 | } |
| 1086 | break; | |
| 1087 | ||
| 1088 | case O_IPID: | |
| 1089 | printf(" ipid %u", cmd->arg1 ); | |
| 1090 | break; | |
| 1091 | ||
| 1092 | case O_IPTTL: | |
| 1093 | printf(" ipttl %u", cmd->arg1 ); | |
| 1094 | break; | |
| 1095 | ||
| 1096 | case O_IPVER: | |
| 1097 | printf(" ipver %u", cmd->arg1 ); | |
| 1098 | break; | |
| 1099 | ||
| 1100 | case O_IPPRECEDENCE: | |
| 1101 | printf(" ipprecedence %u", (cmd->arg1) >> 5 ); | |
| 1102 | break; | |
| 1103 | ||
| 1104 | case O_IPLEN: | |
| 1105 | printf(" iplen %u", cmd->arg1 ); | |
| 1106 | break; | |
| 1107 | ||
| 1108 | case O_IPOPT: | |
| 1109 | print_flags("ipoptions", cmd, f_ipopts); | |
| 1110 | break; | |
| 1111 | ||
| 1112 | case O_IPTOS: | |
| 1113 | print_flags("iptos", cmd, f_iptos); | |
| 1114 | break; | |
| 1115 | ||
| 1116 | case O_ICMPTYPE: | |
| 1117 | print_icmptypes((ipfw_insn_u32 *)cmd); | |
| 1118 | break; | |
| 1119 | ||
| 1120 | case O_ESTAB: | |
| 1121 | printf(" established"); | |
| 1122 | break; | |
| 1123 | ||
| 1124 | case O_TCPFLAGS: | |
| 1125 | print_flags("tcpflags", cmd, f_tcpflags); | |
| 1126 | break; | |
| 1127 | ||
| 1128 | case O_TCPOPTS: | |
| 1129 | print_flags("tcpoptions", cmd, f_tcpopts); | |
| 1130 | break; | |
| 1131 | ||
| 1132 | case O_TCPWIN: | |
| 1133 | printf(" tcpwin %d", ntohs(cmd->arg1)); | |
| 1134 | break; | |
| 1135 | ||
| 1136 | case O_TCPACK: | |
| 7700730e | 1137 | printf(" tcpack %ld", ntohl(cmd32->d[0])); |
| 984263bc MD |
1138 | break; |
| 1139 | ||
| 1140 | case O_TCPSEQ: | |
| 7700730e | 1141 | printf(" tcpseq %ld", ntohl(cmd32->d[0])); |
| 984263bc MD |
1142 | break; |
| 1143 | ||
| 1144 | case O_UID: | |
| 1145 | { | |
| 1146 | struct passwd *pwd = getpwuid(cmd32->d[0]); | |
| 1147 | ||
| 1148 | if (pwd) | |
| 1149 | printf(" uid %s", pwd->pw_name); | |
| 1150 | else | |
| 1151 | printf(" uid %u", cmd32->d[0]); | |
| 1152 | } | |
| 1153 | break; | |
| 1154 | ||
| 1155 | case O_GID: | |
| 1156 | { | |
| 1157 | struct group *grp = getgrgid(cmd32->d[0]); | |
| 1158 | ||
| 1159 | if (grp) | |
| 1160 | printf(" gid %s", grp->gr_name); | |
| 1161 | else | |
| 1162 | printf(" gid %u", cmd32->d[0]); | |
| 1163 | } | |
| 1164 | break; | |
| 1165 | ||
| 1166 | case O_KEEP_STATE: | |
| 1167 | printf(" keep-state"); | |
| 1168 | break; | |
| 1169 | ||
| 1170 | case O_LIMIT: | |
| 1171 | { | |
| 1172 | struct _s_x *p = limit_masks; | |
| 1173 | ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; | |
| 1174 | u_int8_t x = c->limit_mask; | |
| 1175 | char *comma = " "; | |
| 1176 | ||
| 1177 | printf(" limit"); | |
| 1178 | for ( ; p->x != 0 ; p++) | |
| 1179 | if ((x & p->x) == p->x) { | |
| 1180 | x &= ~p->x; | |
| 1181 | printf("%s%s", comma, p->s); | |
| 1182 | comma = ","; | |
| 1183 | } | |
| 1184 | printf(" %d", c->conn_limit); | |
| 1185 | } | |
| 1186 | break; | |
| 1187 | ||
| 1188 | default: | |
| 1189 | printf(" [opcode %d len %d]", | |
| 1190 | cmd->opcode, cmd->len); | |
| 1191 | } | |
| 1192 | } | |
| 1193 | if (cmd->len & F_OR) { | |
| 1194 | printf(" or"); | |
| 1195 | or_block = 1; | |
| 1196 | } else if (or_block) { | |
| 1197 | printf(" }"); | |
| 1198 | or_block = 0; | |
| 1199 | } | |
| 1200 | } | |
| 1201 | show_prerequisites(&flags, HAVE_IP, 0); | |
| 1202 | ||
| 1203 | printf("\n"); | |
| 1204 | } | |
| 1205 | ||
| 1206 | static void | |
| b78533e2 | 1207 | show_dyn_ipfw(struct ipfw_ioc_state *d, int pcwidth, int bcwidth) |
| 984263bc MD |
1208 | { |
| 1209 | struct protoent *pe; | |
| 1210 | struct in_addr a; | |
| 1211 | ||
| 1212 | if (!do_expired) { | |
| 1213 | if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT)) | |
| 1214 | return; | |
| 1215 | } | |
| 1216 | ||
| b78533e2 | 1217 | printf("%05u %*llu %*llu (%ds)", d->rulenum, pcwidth, d->pcnt, |
| 984263bc MD |
1218 | bcwidth, d->bcnt, d->expire); |
| 1219 | switch (d->dyn_type) { | |
| 1220 | case O_LIMIT_PARENT: | |
| 1221 | printf(" PARENT %d", d->count); | |
| 1222 | break; | |
| 1223 | case O_LIMIT: | |
| 1224 | printf(" LIMIT"); | |
| 1225 | break; | |
| 1226 | case O_KEEP_STATE: /* bidir, no mask */ | |
| 1227 | printf(" STATE"); | |
| 1228 | break; | |
| 1229 | } | |
| 1230 | ||
| b78533e2 | 1231 | if ((pe = getprotobynumber(d->id.u.ip.proto)) != NULL) |
| 984263bc MD |
1232 | printf(" %s", pe->p_name); |
| 1233 | else | |
| b78533e2 | 1234 | printf(" proto %u", d->id.u.ip.proto); |
| 984263bc | 1235 | |
| b78533e2 SZ |
1236 | a.s_addr = htonl(d->id.u.ip.src_ip); |
| 1237 | printf(" %s %d", inet_ntoa(a), d->id.u.ip.src_port); | |
| 984263bc | 1238 | |
| b78533e2 SZ |
1239 | a.s_addr = htonl(d->id.u.ip.dst_ip); |
| 1240 | printf(" <-> %s %d", inet_ntoa(a), d->id.u.ip.dst_port); | |
| 984263bc MD |
1241 | printf("\n"); |
| 1242 | } | |
| 1243 | ||
| 1244 | int | |
| 1245 | sort_q(const void *pa, const void *pb) | |
| 1246 | { | |
| 1247 | int rev = (do_sort < 0); | |
| 1248 | int field = rev ? -do_sort : do_sort; | |
| 1249 | long long res = 0; | |
| f9edcb74 SZ |
1250 | const struct dn_ioc_flowqueue *a = pa; |
| 1251 | const struct dn_ioc_flowqueue *b = pb; | |
| 984263bc MD |
1252 | |
| 1253 | switch (field) { | |
| 1254 | case 1: /* pkts */ | |
| 1255 | res = a->len - b->len; | |
| 1256 | break; | |
| 1257 | case 2: /* bytes */ | |
| 1258 | res = a->len_bytes - b->len_bytes; | |
| 1259 | break; | |
| 1260 | ||
| 1261 | case 3: /* tot pkts */ | |
| 1262 | res = a->tot_pkts - b->tot_pkts; | |
| 1263 | break; | |
| 1264 | ||
| 1265 | case 4: /* tot bytes */ | |
| 1266 | res = a->tot_bytes - b->tot_bytes; | |
| 1267 | break; | |
| 1268 | } | |
| 1269 | if (res < 0) | |
| 1270 | res = -1; | |
| 1271 | if (res > 0) | |
| 1272 | res = 1; | |
| 1273 | return (int)(rev ? res : -res); | |
| 1274 | } | |
| 1275 | ||
| 1276 | static void | |
| f9edcb74 | 1277 | list_queues(struct dn_ioc_flowset *fs, struct dn_ioc_flowqueue *q) |
| 984263bc MD |
1278 | { |
| 1279 | int l; | |
| 1280 | ||
| 1281 | printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", | |
| f9edcb74 SZ |
1282 | fs->flow_mask.u.ip.proto, |
| 1283 | fs->flow_mask.u.ip.src_ip, fs->flow_mask.u.ip.src_port, | |
| 1284 | fs->flow_mask.u.ip.dst_ip, fs->flow_mask.u.ip.dst_port); | |
| 984263bc MD |
1285 | if (fs->rq_elements == 0) |
| 1286 | return; | |
| 1287 | ||
| 1288 | printf("BKT Prot ___Source IP/port____ " | |
| 1289 | "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); | |
| 1290 | if (do_sort != 0) | |
| f9edcb74 | 1291 | heapsort(q, fs->rq_elements, sizeof(*q), sort_q); |
| 984263bc MD |
1292 | for (l = 0; l < fs->rq_elements; l++) { |
| 1293 | struct in_addr ina; | |
| 1294 | struct protoent *pe; | |
| 1295 | ||
| f9edcb74 | 1296 | ina.s_addr = htonl(q[l].id.u.ip.src_ip); |
| 984263bc | 1297 | printf("%3d ", q[l].hash_slot); |
| f9edcb74 | 1298 | pe = getprotobynumber(q[l].id.u.ip.proto); |
| 984263bc MD |
1299 | if (pe) |
| 1300 | printf("%-4s ", pe->p_name); | |
| 1301 | else | |
| f9edcb74 | 1302 | printf("%4u ", q[l].id.u.ip.proto); |
| 984263bc | 1303 | printf("%15s/%-5d ", |
| f9edcb74 SZ |
1304 | inet_ntoa(ina), q[l].id.u.ip.src_port); |
| 1305 | ina.s_addr = htonl(q[l].id.u.ip.dst_ip); | |
| 984263bc | 1306 | printf("%15s/%-5d ", |
| f9edcb74 | 1307 | inet_ntoa(ina), q[l].id.u.ip.dst_port); |
| 984263bc MD |
1308 | printf("%4qu %8qu %2u %4u %3u\n", |
| 1309 | q[l].tot_pkts, q[l].tot_bytes, | |
| 1310 | q[l].len, q[l].len_bytes, q[l].drops); | |
| 1311 | if (verbose) | |
| 1312 | printf(" S %20qd F %20qd\n", | |
| 1313 | q[l].S, q[l].F); | |
| 1314 | } | |
| 1315 | } | |
| 1316 | ||
| 1317 | static void | |
| f9edcb74 | 1318 | print_flowset_parms(struct dn_ioc_flowset *fs, char *prefix) |
| 984263bc MD |
1319 | { |
| 1320 | int l; | |
| 1321 | char qs[30]; | |
| 1322 | char plr[30]; | |
| 1323 | char red[90]; /* Display RED parameters */ | |
| 1324 | ||
| 1325 | l = fs->qsize; | |
| 1326 | if (fs->flags_fs & DN_QSIZE_IS_BYTES) { | |
| 1327 | if (l >= 8192) | |
| 1328 | sprintf(qs, "%d KB", l / 1024); | |
| 1329 | else | |
| 1330 | sprintf(qs, "%d B", l); | |
| 1331 | } else | |
| 1332 | sprintf(qs, "%3d sl.", l); | |
| 1333 | if (fs->plr) | |
| 1334 | sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); | |
| 1335 | else | |
| 1336 | plr[0] = '\0'; | |
| 1337 | if (fs->flags_fs & DN_IS_RED) /* RED parameters */ | |
| 1338 | sprintf(red, | |
| 1339 | "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", | |
| 1340 | (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', | |
| 1341 | 1.0 * fs->w_q / (double)(1 << SCALE_RED), | |
| 1342 | SCALE_VAL(fs->min_th), | |
| 1343 | SCALE_VAL(fs->max_th), | |
| 1344 | 1.0 * fs->max_p / (double)(1 << SCALE_RED)); | |
| 1345 | else | |
| 1346 | sprintf(red, "droptail"); | |
| 1347 | ||
| 1348 | printf("%s %s%s %d queues (%d buckets) %s\n", | |
| 1349 | prefix, qs, plr, fs->rq_elements, fs->rq_size, red); | |
| 1350 | } | |
| 1351 | ||
| 1352 | static void | |
| 1353 | list_pipes(void *data, int nbytes, int ac, char *av[]) | |
| 1354 | { | |
| 1355 | u_long rulenum; | |
| 1356 | void *next = data; | |
| f9edcb74 SZ |
1357 | struct dn_ioc_pipe *p = (struct dn_ioc_pipe *)data; |
| 1358 | struct dn_ioc_flowset *fs; | |
| 1359 | struct dn_ioc_flowqueue *q; | |
| 984263bc MD |
1360 | int l; |
| 1361 | ||
| 1362 | if (ac > 0) | |
| 1363 | rulenum = strtoul(*av++, NULL, 10); | |
| 1364 | else | |
| 1365 | rulenum = 0; | |
| f9edcb74 | 1366 | for (; nbytes >= sizeof(*p); p = (struct dn_ioc_pipe *)next) { |
| 984263bc MD |
1367 | double b = p->bandwidth; |
| 1368 | char buf[30]; | |
| 1369 | char prefix[80]; | |
| 1370 | ||
| f9edcb74 | 1371 | if (p->fs.fs_type != DN_IS_PIPE) |
| 984263bc MD |
1372 | break; /* done with pipes, now queues */ |
| 1373 | ||
| 1374 | /* | |
| 1375 | * compute length, as pipe have variable size | |
| 1376 | */ | |
| 1377 | l = sizeof(*p) + p->fs.rq_elements * sizeof(*q); | |
| 1378 | next = (void *)p + l; | |
| 1379 | nbytes -= l; | |
| 1380 | ||
| 1381 | if (rulenum != 0 && rulenum != p->pipe_nr) | |
| 1382 | continue; | |
| 1383 | ||
| 1384 | /* | |
| ff6f118a | 1385 | * Print rate |
| 984263bc | 1386 | */ |
| ff6f118a | 1387 | if (b == 0) |
| 984263bc MD |
1388 | sprintf(buf, "unlimited"); |
| 1389 | else if (b >= 1000000) | |
| 1390 | sprintf(buf, "%7.3f Mbit/s", b/1000000); | |
| 1391 | else if (b >= 1000) | |
| 1392 | sprintf(buf, "%7.3f Kbit/s", b/1000); | |
| 1393 | else | |
| 1394 | sprintf(buf, "%7.3f bit/s ", b); | |
| 1395 | ||
| 1396 | sprintf(prefix, "%05d: %s %4d ms ", | |
| 1397 | p->pipe_nr, buf, p->delay); | |
| f9edcb74 | 1398 | print_flowset_parms(&p->fs, prefix); |
| 984263bc MD |
1399 | if (verbose) |
| 1400 | printf(" V %20qd\n", p->V >> MY_M); | |
| 1401 | ||
| f9edcb74 SZ |
1402 | q = (struct dn_ioc_flowqueue *)(p+1); |
| 1403 | list_queues(&p->fs, q); | |
| 984263bc | 1404 | } |
| f9edcb74 | 1405 | for (fs = next; nbytes >= sizeof(*fs); fs = next) { |
| 984263bc MD |
1406 | char prefix[80]; |
| 1407 | ||
| f9edcb74 | 1408 | if (fs->fs_type != DN_IS_QUEUE) |
| 984263bc MD |
1409 | break; |
| 1410 | l = sizeof(*fs) + fs->rq_elements * sizeof(*q); | |
| 1411 | next = (void *)fs + l; | |
| 1412 | nbytes -= l; | |
| f9edcb74 | 1413 | q = (struct dn_ioc_flowqueue *)(fs+1); |
| 984263bc MD |
1414 | sprintf(prefix, "q%05d: weight %d pipe %d ", |
| 1415 | fs->fs_nr, fs->weight, fs->parent_nr); | |
| 1416 | print_flowset_parms(fs, prefix); | |
| 1417 | list_queues(fs, q); | |
| 1418 | } | |
| 1419 | } | |
| 1420 | ||
| 1421 | /* | |
| 1422 | * This one handles all set-related commands | |
| 1423 | * ipfw set { show | enable | disable } | |
| 1424 | * ipfw set swap X Y | |
| 1425 | * ipfw set move X to Y | |
| 1426 | * ipfw set move rule X to Y | |
| 1427 | */ | |
| 1428 | static void | |
| 1429 | sets_handler(int ac, char *av[]) | |
| 1430 | { | |
| 1431 | u_int32_t set_disable, masks[2]; | |
| 1432 | int i, nbytes; | |
| 1433 | u_int16_t rulenum; | |
| 1434 | u_int8_t cmd, new_set; | |
| 1435 | ||
| 1436 | ac--; | |
| 1437 | av++; | |
| 1438 | ||
| 1439 | if (!ac) | |
| 1440 | errx(EX_USAGE, "set needs command"); | |
| 1441 | if (!strncmp(*av, "show", strlen(*av)) ) { | |
| 1442 | void *data; | |
| 1443 | char *msg; | |
| 1444 | ||
| b78533e2 | 1445 | nbytes = sizeof(struct ipfw_ioc_rule); |
| 984263bc MD |
1446 | if ((data = malloc(nbytes)) == NULL) |
| 1447 | err(EX_OSERR, "malloc"); | |
| 1448 | if (getsockopt(s, IPPROTO_IP, IP_FW_GET, data, &nbytes) < 0) | |
| 1449 | err(EX_OSERR, "getsockopt(IP_FW_GET)"); | |
| b78533e2 | 1450 | set_disable = ((struct ipfw_ioc_rule *)data)->set_disable; |
| 984263bc MD |
1451 | |
| 1452 | for (i = 0, msg = "disable" ; i < 31; i++) | |
| 1453 | if ( (set_disable & (1<<i))) { | |
| 1454 | printf("%s %d", msg, i); | |
| 1455 | msg = ""; | |
| 1456 | } | |
| 1457 | msg = (set_disable) ? " enable" : "enable"; | |
| 1458 | for (i = 0; i < 31; i++) | |
| 1459 | if ( !(set_disable & (1<<i))) { | |
| 1460 | printf("%s %d", msg, i); | |
| 1461 | msg = ""; | |
| 1462 | } | |
| 1463 | printf("\n"); | |
| 1464 | } else if (!strncmp(*av, "swap", strlen(*av))) { | |
| 1465 | ac--; av++; | |
| 1466 | if (ac != 2) | |
| 1467 | errx(EX_USAGE, "set swap needs 2 set numbers\n"); | |
| 1468 | rulenum = atoi(av[0]); | |
| 1469 | new_set = atoi(av[1]); | |
| 1470 | if (!isdigit(*(av[0])) || rulenum > 30) | |
| 1471 | errx(EX_DATAERR, "invalid set number %s\n", av[0]); | |
| 1472 | if (!isdigit(*(av[1])) || new_set > 30) | |
| 1473 | errx(EX_DATAERR, "invalid set number %s\n", av[1]); | |
| 1474 | masks[0] = (4 << 24) | (new_set << 16) | (rulenum); | |
| 1475 | i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, | |
| 1476 | masks, sizeof(u_int32_t)); | |
| 1477 | } else if (!strncmp(*av, "move", strlen(*av))) { | |
| 1478 | ac--; av++; | |
| 1479 | if (ac && !strncmp(*av, "rule", strlen(*av))) { | |
| 1480 | cmd = 2; | |
| 1481 | ac--; av++; | |
| 1482 | } else | |
| 1483 | cmd = 3; | |
| 1484 | if (ac != 3 || strncmp(av[1], "to", strlen(*av))) | |
| 1485 | errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); | |
| 1486 | rulenum = atoi(av[0]); | |
| 1487 | new_set = atoi(av[2]); | |
| 1488 | if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > 30) || | |
| 1489 | (cmd == 2 && rulenum == 65535) ) | |
| 1490 | errx(EX_DATAERR, "invalid source number %s\n", av[0]); | |
| 1491 | if (!isdigit(*(av[2])) || new_set > 30) | |
| 1492 | errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); | |
| 1493 | masks[0] = (cmd << 24) | (new_set << 16) | (rulenum); | |
| 1494 | i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, | |
| 1495 | masks, sizeof(u_int32_t)); | |
| 1496 | } else if (!strncmp(*av, "disable", strlen(*av)) || | |
| 1497 | !strncmp(*av, "enable", strlen(*av)) ) { | |
| 1498 | int which = !strncmp(*av, "enable", strlen(*av)) ? 1 : 0; | |
| 1499 | ||
| 1500 | ac--; av++; | |
| 1501 | masks[0] = masks[1] = 0; | |
| 1502 | ||
| 1503 | while (ac) { | |
| 1504 | if (isdigit(**av)) { | |
| 1505 | i = atoi(*av); | |
| 1506 | if (i < 0 || i > 30) | |
| 1507 | errx(EX_DATAERR, | |
| 1508 | "invalid set number %d\n", i); | |
| 1509 | masks[which] |= (1<<i); | |
| 1510 | } else if (!strncmp(*av, "disable", strlen(*av))) | |
| 1511 | which = 0; | |
| 1512 | else if (!strncmp(*av, "enable", strlen(*av))) | |
| 1513 | which = 1; | |
| 1514 | else | |
| 1515 | errx(EX_DATAERR, | |
| 1516 | "invalid set command %s\n", *av); | |
| 1517 | av++; ac--; | |
| 1518 | } | |
| 1519 | if ( (masks[0] & masks[1]) != 0 ) | |
| 1520 | errx(EX_DATAERR, | |
| 1521 | "cannot enable and disable the same set\n"); | |
| 1522 | ||
| 1523 | i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, masks, sizeof(masks)); | |
| 1524 | if (i) | |
| 1525 | warn("set enable/disable: setsockopt(IP_FW_DEL)"); | |
| 1526 | } else | |
| 1527 | errx(EX_USAGE, "invalid set command %s\n", *av); | |
| 1528 | } | |
| 1529 | ||
| 1530 | static void | |
| 1531 | sysctl_handler(int ac, char *av[], int which) | |
| 1532 | { | |
| 1533 | ac--; | |
| 1534 | av++; | |
| 1535 | ||
| 1536 | if (*av == NULL) { | |
| 1537 | warnx("missing keyword to enable/disable\n"); | |
| 1538 | } else if (strncmp(*av, "firewall", strlen(*av)) == 0) { | |
| 1539 | sysctlbyname("net.inet.ip.fw.enable", NULL, 0, | |
| 1540 | &which, sizeof(which)); | |
| 1541 | } else if (strncmp(*av, "one_pass", strlen(*av)) == 0) { | |
| 1542 | sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0, | |
| 1543 | &which, sizeof(which)); | |
| 1544 | } else if (strncmp(*av, "debug", strlen(*av)) == 0) { | |
| 1545 | sysctlbyname("net.inet.ip.fw.debug", NULL, 0, | |
| 1546 | &which, sizeof(which)); | |
| 1547 | } else if (strncmp(*av, "verbose", strlen(*av)) == 0) { | |
| 1548 | sysctlbyname("net.inet.ip.fw.verbose", NULL, 0, | |
| 1549 | &which, sizeof(which)); | |
| 1550 | } else if (strncmp(*av, "dyn_keepalive", strlen(*av)) == 0) { | |
| 1551 | sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, | |
| 1552 | &which, sizeof(which)); | |
| 1553 | } else { | |
| 1554 | warnx("unrecognize enable/disable keyword: %s\n", *av); | |
| 1555 | } | |
| 1556 | } | |
| 1557 | ||
| 1558 | static void | |
| 1559 | list(int ac, char *av[]) | |
| 1560 | { | |
| b78533e2 SZ |
1561 | struct ipfw_ioc_rule *r; |
| 1562 | struct ipfw_ioc_state *dynrules, *d; | |
| 984263bc | 1563 | |
| b78533e2 | 1564 | void *data = NULL; |
| 984263bc MD |
1565 | int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width; |
| 1566 | int exitval = EX_OK; | |
| 1567 | int lac; | |
| 1568 | char **lav; | |
| 1569 | u_long rnum; | |
| 1570 | char *endptr; | |
| 1571 | int seen = 0; | |
| 1572 | ||
| 1573 | const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; | |
| 1574 | int nalloc = 1024; /* start somewhere... */ | |
| 1575 | ||
| 1576 | ac--; | |
| 1577 | av++; | |
| 1578 | ||
| 1579 | /* get rules or pipes from kernel, resizing array as necessary */ | |
| 1580 | nbytes = nalloc; | |
| 1581 | ||
| 1582 | while (nbytes >= nalloc) { | |
| 1583 | nalloc = nalloc * 2 + 200; | |
| 1584 | nbytes = nalloc; | |
| 1585 | if ((data = realloc(data, nbytes)) == NULL) | |
| 1586 | err(EX_OSERR, "realloc"); | |
| 1587 | if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0) | |
| 1588 | err(EX_OSERR, "getsockopt(IP_%s_GET)", | |
| 1589 | do_pipe ? "DUMMYNET" : "FW"); | |
| 1590 | } | |
| 1591 | ||
| 1592 | if (do_pipe) { | |
| 1593 | list_pipes(data, nbytes, ac, av); | |
| 1594 | goto done; | |
| 1595 | } | |
| 1596 | ||
| 1597 | /* | |
| b78533e2 | 1598 | * Count static rules. |
| 984263bc | 1599 | */ |
| b78533e2 SZ |
1600 | r = data; |
| 1601 | nstat = r->static_count; | |
| 984263bc MD |
1602 | |
| 1603 | /* | |
| 1604 | * Count dynamic rules. This is easier as they have | |
| 1605 | * fixed size. | |
| 1606 | */ | |
| b78533e2 SZ |
1607 | dynrules = (struct ipfw_ioc_state *)((void *)r + r->static_len); |
| 1608 | ndyn = (nbytes - r->static_len) / sizeof(*dynrules); | |
| 984263bc MD |
1609 | |
| 1610 | /* if showing stats, figure out column widths ahead of time */ | |
| 1611 | bcwidth = pcwidth = 0; | |
| 1612 | if (do_acct) { | |
| 1613 | for (n = 0, r = data; n < nstat; | |
| b78533e2 | 1614 | n++, r = (void *)r + IOC_RULESIZE(r)) { |
| 984263bc MD |
1615 | /* packet counter */ |
| 1616 | width = snprintf(NULL, 0, "%llu", r->pcnt); | |
| 1617 | if (width > pcwidth) | |
| 1618 | pcwidth = width; | |
| 1619 | ||
| 1620 | /* byte counter */ | |
| 1621 | width = snprintf(NULL, 0, "%llu", r->bcnt); | |
| 1622 | if (width > bcwidth) | |
| 1623 | bcwidth = width; | |
| 1624 | } | |
| 1625 | } | |
| 1626 | if (do_dynamic && ndyn) { | |
| 1627 | for (n = 0, d = dynrules; n < ndyn; n++, d++) { | |
| 1628 | width = snprintf(NULL, 0, "%llu", d->pcnt); | |
| 1629 | if (width > pcwidth) | |
| 1630 | pcwidth = width; | |
| 1631 | ||
| 1632 | width = snprintf(NULL, 0, "%llu", d->bcnt); | |
| 1633 | if (width > bcwidth) | |
| 1634 | bcwidth = width; | |
| 1635 | } | |
| 1636 | } | |
| 1637 | /* if no rule numbers were specified, list all rules */ | |
| 1638 | if (ac == 0) { | |
| 1639 | for (n = 0, r = data; n < nstat; | |
| b78533e2 | 1640 | n++, r = (void *)r + IOC_RULESIZE(r) ) |
| 984263bc MD |
1641 | show_ipfw(r, pcwidth, bcwidth); |
| 1642 | ||
| 1643 | if (do_dynamic && ndyn) { | |
| 1644 | printf("## Dynamic rules (%d):\n", ndyn); | |
| 1645 | for (n = 0, d = dynrules; n < ndyn; n++, d++) | |
| 1646 | show_dyn_ipfw(d, pcwidth, bcwidth); | |
| 1647 | } | |
| 1648 | goto done; | |
| 1649 | } | |
| 1650 | ||
| 1651 | /* display specific rules requested on command line */ | |
| 1652 | ||
| 1653 | for (lac = ac, lav = av; lac != 0; lac--) { | |
| 1654 | /* convert command line rule # */ | |
| 1655 | rnum = strtoul(*lav++, &endptr, 10); | |
| 1656 | if (*endptr) { | |
| 1657 | exitval = EX_USAGE; | |
| 1658 | warnx("invalid rule number: %s", *(lav - 1)); | |
| 1659 | continue; | |
| 1660 | } | |
| 1661 | for (n = seen = 0, r = data; n < nstat; | |
| b78533e2 | 1662 | n++, r = (void *)r + IOC_RULESIZE(r) ) { |
| 984263bc MD |
1663 | if (r->rulenum > rnum) |
| 1664 | break; | |
| 1665 | if (r->rulenum == rnum) { | |
| 1666 | show_ipfw(r, pcwidth, bcwidth); | |
| 1667 | seen = 1; | |
| 1668 | } | |
| 1669 | } | |
| 1670 | if (!seen) { | |
| 1671 | /* give precedence to other error(s) */ | |
| 1672 | if (exitval == EX_OK) | |
| 1673 | exitval = EX_UNAVAILABLE; | |
| 1674 | warnx("rule %lu does not exist", rnum); | |
| 1675 | } | |
| 1676 | } | |
| 1677 | ||
| 1678 | if (do_dynamic && ndyn) { | |
| 1679 | printf("## Dynamic rules:\n"); | |
| 1680 | for (lac = ac, lav = av; lac != 0; lac--) { | |
| 1681 | rnum = strtoul(*lav++, &endptr, 10); | |
| 1682 | if (*endptr) | |
| 1683 | /* already warned */ | |
| 1684 | continue; | |
| 1685 | for (n = 0, d = dynrules; n < ndyn; n++, d++) { | |
| b78533e2 | 1686 | if (d->rulenum > rnum) |
| 984263bc | 1687 | break; |
| b78533e2 | 1688 | if (d->rulenum == rnum) |
| 984263bc MD |
1689 | show_dyn_ipfw(d, pcwidth, bcwidth); |
| 1690 | } | |
| 1691 | } | |
| 1692 | } | |
| 1693 | ||
| 1694 | ac = 0; | |
| 1695 | ||
| 1696 | done: | |
| 1697 | free(data); | |
| 1698 | ||
| 1699 | if (exitval != EX_OK) | |
| 1700 | exit(exitval); | |
| 1701 | } | |
| 1702 | ||
| 1703 | static void | |
| 1704 | show_usage(void) | |
| 1705 | { | |
| 1706 | fprintf(stderr, "usage: ipfw [options]\n" | |
| 1707 | " add [number] rule\n" | |
| 1708 | " pipe number config [pipeconfig]\n" | |
| 1709 | " queue number config [queueconfig]\n" | |
| 1710 | " [pipe] flush\n" | |
| 1711 | " [pipe] delete number ...\n" | |
| 1712 | " [pipe] {list|show} [number ...]\n" | |
| 1713 | " {zero|resetlog} [number ...]\n" | |
| 1714 | "do \"ipfw -h\" or see ipfw manpage for details\n" | |
| 1715 | ); | |
| 1716 | ||
| 1717 | exit(EX_USAGE); | |
| 1718 | } | |
| 1719 | ||
| 1720 | static void | |
| 1721 | help(void) | |
| 1722 | { | |
| 1723 | ||
| 1724 | fprintf(stderr, "ipfw syntax summary:\n" | |
| 1725 | "ipfw add [N] [prob {0..1}] ACTION [log [logamount N]] ADDR OPTIONS\n" | |
| 1726 | "ipfw {pipe|queue} N config BODY\n" | |
| 1727 | "ipfw [pipe] {zero|delete|show} [N{,N}]\n" | |
| 1728 | "\n" | |
| 1729 | "RULE: [1..] [PROB] BODY\n" | |
| 1730 | "RULENUM: INTEGER(1..65534)\n" | |
| 1731 | "PROB: prob REAL(0..1)\n" | |
| 1732 | "BODY: check-state [LOG] (no body) |\n" | |
| 1733 | " ACTION [LOG] MATCH_ADDR [OPTION_LIST]\n" | |
| 1734 | "ACTION: check-state | allow | count | deny | reject | skipto N |\n" | |
| 1735 | " {divert|tee} PORT | forward ADDR | pipe N | queue N\n" | |
| 1736 | "ADDR: [ MAC dst src ether_type ] \n" | |
| 1737 | " [ from IPLIST [ PORT ] to IPLIST [ PORTLIST ] ]\n" | |
| 1738 | "IPLIST: IPADDR | ( IPADDR or ... or IPADDR )\n" | |
| 1739 | "IPADDR: [not] { any | me | ip | ip/bits | ip:mask | ip/bits{x,y,z} }\n" | |
| 1740 | "OPTION_LIST: OPTION [,OPTION_LIST]\n" | |
| 1741 | ); | |
| 1742 | exit(0); | |
| 1743 | } | |
| 1744 | ||
| 1745 | ||
| 1746 | static int | |
| 1747 | lookup_host (char *host, struct in_addr *ipaddr) | |
| 1748 | { | |
| 1749 | struct hostent *he; | |
| 1750 | ||
| 1751 | if (!inet_aton(host, ipaddr)) { | |
| 1752 | if ((he = gethostbyname(host)) == NULL) | |
| 1753 | return(-1); | |
| 1754 | *ipaddr = *(struct in_addr *)he->h_addr_list[0]; | |
| 1755 | } | |
| 1756 | return(0); | |
| 1757 | } | |
| 1758 | ||
| 1759 | /* | |
| 1760 | * fills the addr and mask fields in the instruction as appropriate from av. | |
| 1761 | * Update length as appropriate. | |
| 1762 | * The following formats are allowed: | |
| 1763 | * any matches any IP. Actually returns an empty instruction. | |
| 1764 | * me returns O_IP_*_ME | |
| 1765 | * 1.2.3.4 single IP address | |
| 1766 | * 1.2.3.4:5.6.7.8 address:mask | |
| 1767 | * 1.2.3.4/24 address/mask | |
| 1768 | * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet | |
| 1769 | */ | |
| 1770 | static void | |
| 1771 | fill_ip(ipfw_insn_ip *cmd, char *av) | |
| 1772 | { | |
| 1773 | char *p = 0, md = 0; | |
| 1774 | u_int32_t i; | |
| 1775 | ||
| 1776 | cmd->o.len &= ~F_LEN_MASK; /* zero len */ | |
| 1777 | ||
| 1778 | if (!strncmp(av, "any", strlen(av))) | |
| 1779 | return; | |
| 1780 | ||
| 1781 | if (!strncmp(av, "me", strlen(av))) { | |
| 1782 | cmd->o.len |= F_INSN_SIZE(ipfw_insn); | |
| 1783 | return; | |
| 1784 | } | |
| 1785 | ||
| 1786 | p = strchr(av, '/'); | |
| 1787 | if (!p) | |
| 1788 | p = strchr(av, ':'); | |
| 1789 | if (p) { | |
| 1790 | md = *p; | |
| 1791 | *p++ = '\0'; | |
| 1792 | } | |
| 1793 | ||
| 1794 | if (lookup_host(av, &cmd->addr) != 0) | |
| 1795 | errx(EX_NOHOST, "hostname ``%s'' unknown", av); | |
| 1796 | switch (md) { | |
| 1797 | case ':': | |
| 1798 | if (!inet_aton(p, &cmd->mask)) | |
| 1799 | errx(EX_DATAERR, "bad netmask ``%s''", p); | |
| 1800 | break; | |
| 1801 | case '/': | |
| 1802 | i = atoi(p); | |
| 1803 | if (i == 0) | |
| 1804 | cmd->mask.s_addr = htonl(0); | |
| 1805 | else if (i > 32) | |
| 1806 | errx(EX_DATAERR, "bad width ``%s''", p); | |
| 1807 | else | |
| 1808 | cmd->mask.s_addr = htonl(~0 << (32 - i)); | |
| 1809 | break; | |
| 1810 | default: | |
| 1811 | cmd->mask.s_addr = htonl(~0); | |
| 1812 | break; | |
| 1813 | } | |
| 1814 | cmd->addr.s_addr &= cmd->mask.s_addr; | |
| 1815 | /* | |
| 1816 | * now look if we have a set of addresses. They are stored as follows: | |
| 1817 | * arg1 is the set size (powers of 2, 2..256) | |
| 1818 | * addr is the base address IN HOST FORMAT | |
| 1819 | * mask.. is an array of u_int32_t with bits set. | |
| 1820 | */ | |
| 1821 | if (p) | |
| 1822 | p = strchr(p, '{'); | |
| 1823 | if (p) { /* fetch addresses */ | |
| 1824 | u_int32_t *d; | |
| 1825 | int low, high; | |
| 1826 | int i = contigmask((u_char *)&(cmd->mask), 32); | |
| 1827 | ||
| 1828 | if (i < 24 || i > 31) { | |
| 1829 | fprintf(stderr, "invalid set with mask %d\n", | |
| 1830 | i); | |
| 1831 | exit(0); | |
| 1832 | } | |
| 1833 | cmd->o.arg1 = 1<<(32-i); | |
| 1834 | cmd->addr.s_addr = ntohl(cmd->addr.s_addr); | |
| 1835 | d = (u_int32_t *)&cmd->mask; | |
| 1836 | cmd->o.opcode = O_IP_DST_SET; /* default */ | |
| 1837 | cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; | |
| 1838 | for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) | |
| 1839 | d[i] = 0; /* clear masks */ | |
| 1840 | ||
| 1841 | av = p+1; | |
| 1842 | low = cmd->addr.s_addr & 0xff; | |
| 1843 | high = low + cmd->o.arg1 - 1; | |
| 1844 | while (isdigit(*av)) { | |
| 1845 | char *s; | |
| 1846 | u_int16_t a = strtol(av, &s, 0); | |
| 1847 | ||
| 1848 | if (s == av) /* no parameter */ | |
| 1849 | break; | |
| 1850 | if (a < low || a > high) { | |
| 1851 | fprintf(stderr, "addr %d out of range [%d-%d]\n", | |
| 1852 | a, low, high); | |
| 1853 | exit(0); | |
| 1854 | } | |
| 1855 | a -= low; | |
| 1856 | d[ a/32] |= 1<<(a & 31); | |
| 1857 | if (*s != ',') | |
| 1858 | break; | |
| 1859 | av = s+1; | |
| 1860 | } | |
| 1861 | return; | |
| 1862 | } | |
| 1863 | ||
| 1864 | if (cmd->mask.s_addr == 0) { /* any */ | |
| 1865 | if (cmd->o.len & F_NOT) | |
| 1866 | errx(EX_DATAERR, "not any never matches"); | |
| 1867 | else /* useless, nuke it */ | |
| 1868 | return; | |
| 1869 | } else if (cmd->mask.s_addr == IP_MASK_ALL) /* one IP */ | |
| 1870 | cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); | |
| 1871 | else /* addr/mask */ | |
| 1872 | cmd->o.len |= F_INSN_SIZE(ipfw_insn_ip); | |
| 1873 | } | |
| 1874 | ||
| 1875 | ||
| 1876 | /* | |
| 1877 | * helper function to process a set of flags and set bits in the | |
| 1878 | * appropriate masks. | |
| 1879 | */ | |
| 1880 | static void | |
| 1881 | fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, | |
| 1882 | struct _s_x *flags, char *p) | |
| 1883 | { | |
| 1884 | u_int8_t set=0, clear=0; | |
| 1885 | ||
| 1886 | while (p && *p) { | |
| 1887 | char *q; /* points to the separator */ | |
| 1888 | int val; | |
| 1889 | u_int8_t *which; /* mask we are working on */ | |
| 1890 | ||
| 1891 | if (*p == '!') { | |
| 1892 | p++; | |
| 1893 | which = &clear; | |
| 1894 | } else | |
| 1895 | which = &set; | |
| 1896 | q = strchr(p, ','); | |
| 1897 | if (q) | |
| 1898 | *q++ = '\0'; | |
| 1899 | val = match_token(flags, p); | |
| 1900 | if (val <= 0) | |
| 1901 | errx(EX_DATAERR, "invalid flag %s", p); | |
| 1902 | *which |= (u_int8_t)val; | |
| 1903 | p = q; | |
| 1904 | } | |
| 1905 | cmd->opcode = opcode; | |
| 1906 | cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; | |
| 1907 | cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); | |
| 1908 | } | |
| 1909 | ||
| 1910 | ||
| 1911 | static void | |
| 1912 | delete(int ac, char *av[]) | |
| 1913 | { | |
| 1914 | u_int32_t rulenum; | |
| f9edcb74 | 1915 | struct dn_ioc_pipe pipe; |
| 984263bc MD |
1916 | int i; |
| 1917 | int exitval = EX_OK; | |
| 1918 | int do_set = 0; | |
| 1919 | ||
| 1920 | memset(&pipe, 0, sizeof pipe); | |
| 1921 | ||
| 1922 | av++; ac--; | |
| 1923 | if (ac > 0 && !strncmp(*av, "set", strlen(*av))) { | |
| 1924 | do_set = 1; /* delete set */ | |
| 1925 | ac--; av++; | |
| 1926 | } | |
| 1927 | ||
| 1928 | /* Rule number */ | |
| 1929 | while (ac && isdigit(**av)) { | |
| 1930 | i = atoi(*av); av++; ac--; | |
| 1931 | if (do_pipe) { | |
| 1932 | if (do_pipe == 1) | |
| 1933 | pipe.pipe_nr = i; | |
| 1934 | else | |
| 1935 | pipe.fs.fs_nr = i; | |
| 1936 | i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL, | |
| 1937 | &pipe, sizeof pipe); | |
| 1938 | if (i) { | |
| 1939 | exitval = 1; | |
| 1940 | warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", | |
| 1941 | do_pipe == 1 ? pipe.pipe_nr : | |
| 1942 | pipe.fs.fs_nr); | |
| 1943 | } | |
| 1944 | } else { | |
| 1945 | rulenum = (i & 0xffff) | (do_set << 24); | |
| 1946 | i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rulenum, | |
| 1947 | sizeof rulenum); | |
| 1948 | if (i) { | |
| 1949 | exitval = EX_UNAVAILABLE; | |
| 1950 | warn("rule %u: setsockopt(IP_FW_DEL)", | |
| 1951 | rulenum); | |
| 1952 | } | |
| 1953 | } | |
| 1954 | } | |
| 1955 | if (exitval != EX_OK) | |
| 1956 | exit(exitval); | |
| 1957 | } | |
| 1958 | ||
| 1959 | ||
| 1960 | /* | |
| 1961 | * fill the interface structure. We do not check the name as we can | |
| 1962 | * create interfaces dynamically, so checking them at insert time | |
| 1963 | * makes relatively little sense. | |
| 3e4a09e7 MD |
1964 | * Interface names containing '*', '?', or '[' are assumed to be shell |
| 1965 | * patterns which match interfaces. | |
| 984263bc MD |
1966 | */ |
| 1967 | static void | |
| 1968 | fill_iface(ipfw_insn_if *cmd, char *arg) | |
| 1969 | { | |
| 1970 | cmd->name[0] = '\0'; | |
| 1971 | cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); | |
| 1972 | ||
| 1973 | /* Parse the interface or address */ | |
| 1974 | if (!strcmp(arg, "any")) | |
| 1975 | cmd->o.len = 0; /* effectively ignore this command */ | |
| 1976 | else if (!isdigit(*arg)) { | |
| 3e4a09e7 MD |
1977 | strlcpy(cmd->name, arg, sizeof(cmd->name)); |
| 1978 | cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; | |
| 984263bc MD |
1979 | } else if (!inet_aton(arg, &cmd->p.ip)) |
| 1980 | errx(EX_DATAERR, "bad ip address ``%s''", arg); | |
| 1981 | } | |
| 1982 | ||
| 7700730e MD |
1983 | static unsigned long |
| 1984 | getbw(const char *str, u_short *flags, int kb) | |
| 1985 | { | |
| 1986 | char *end; | |
| 1987 | unsigned long val; | |
| 1988 | int inbytes = 0; | |
| 1989 | ||
| 1990 | val = strtoul(str, &end, 0); | |
| 1991 | if (*end == 'k' || *end == 'K') { | |
| 1992 | ++end; | |
| 1993 | val *= kb; | |
| 1994 | } else if (*end == 'm' || *end == 'M') { | |
| 1995 | ++end; | |
| 1996 | val *= kb * kb; | |
| 1997 | } | |
| 1998 | ||
| 1999 | /* | |
| 2000 | * Deal with bits or bytes or b(bits) or B(bytes). If there is no | |
| 2001 | * trailer assume bits. | |
| 2002 | */ | |
| 2003 | if (strncasecmp(end, "bit", 3) == 0) { | |
| 2004 | ; | |
| 2005 | } else if (strncasecmp(end, "byte", 4) == 0) { | |
| 2006 | inbytes = 1; | |
| 2007 | } else if (*end == 'b') { | |
| 2008 | ; | |
| 2009 | } else if (*end == 'B') { | |
| 2010 | inbytes = 1; | |
| 2011 | } | |
| 2012 | ||
| 2013 | /* | |
| 2014 | * Return in bits if flags is NULL, else flag bits | |
| 2015 | * or bytes in flags and return the unconverted value. | |
| 2016 | */ | |
| 2017 | if (inbytes && flags) | |
| 2018 | *flags |= DN_QSIZE_IS_BYTES; | |
| 2019 | else if (inbytes && flags == NULL) | |
| 2020 | val *= 8; | |
| 2021 | return(val); | |
| 2022 | } | |
| 2023 | ||
| 984263bc MD |
2024 | /* |
| 2025 | * the following macro returns an error message if we run out of | |
| 2026 | * arguments. | |
| 2027 | */ | |
| 2028 | #define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} | |
| 2029 | ||
| 2030 | static void | |
| 2031 | config_pipe(int ac, char **av) | |
| 2032 | { | |
| f9edcb74 | 2033 | struct dn_ioc_pipe pipe; |
| 984263bc MD |
2034 | int i; |
| 2035 | char *end; | |
| 2036 | u_int32_t a; | |
| 2037 | void *par = NULL; | |
| 2038 | ||
| 2039 | memset(&pipe, 0, sizeof pipe); | |
| 2040 | ||
| 2041 | av++; ac--; | |
| 2042 | /* Pipe number */ | |
| 2043 | if (ac && isdigit(**av)) { | |
| 2044 | i = atoi(*av); av++; ac--; | |
| 2045 | if (do_pipe == 1) | |
| 2046 | pipe.pipe_nr = i; | |
| 2047 | else | |
| 2048 | pipe.fs.fs_nr = i; | |
| 2049 | } | |
| 2050 | while (ac > 0) { | |
| 2051 | double d; | |
| 2052 | int tok = match_token(dummynet_params, *av); | |
| 2053 | ac--; av++; | |
| 2054 | ||
| 2055 | switch(tok) { | |
| 2056 | case TOK_NOERROR: | |
| 2057 | pipe.fs.flags_fs |= DN_NOERROR; | |
| 2058 | break; | |
| 2059 | ||
| 2060 | case TOK_PLR: | |
| 2061 | NEED1("plr needs argument 0..1\n"); | |
| 2062 | d = strtod(av[0], NULL); | |
| 2063 | if (d > 1) | |
| 2064 | d = 1; | |
| 2065 | else if (d < 0) | |
| 2066 | d = 0; | |
| 2067 | pipe.fs.plr = (int)(d*0x7fffffff); | |
| 2068 | ac--; av++; | |
| 2069 | break; | |
| 2070 | ||
| 2071 | case TOK_QUEUE: | |
| 2072 | NEED1("queue needs queue size\n"); | |
| 2073 | end = NULL; | |
| 7700730e | 2074 | pipe.fs.qsize = getbw(av[0], &pipe.fs.flags_fs, 1024); |
| 984263bc MD |
2075 | ac--; av++; |
| 2076 | break; | |
| 2077 | ||
| 2078 | case TOK_BUCKETS: | |
| 2079 | NEED1("buckets needs argument\n"); | |
| 2080 | pipe.fs.rq_size = strtoul(av[0], NULL, 0); | |
| 2081 | ac--; av++; | |
| 2082 | break; | |
| 2083 | ||
| 2084 | case TOK_MASK: | |
| 2085 | NEED1("mask needs mask specifier\n"); | |
| 2086 | /* | |
| 2087 | * per-flow queue, mask is dst_ip, dst_port, | |
| 2088 | * src_ip, src_port, proto measured in bits | |
| 2089 | */ | |
| 2090 | par = NULL; | |
| 2091 | ||
| f9edcb74 SZ |
2092 | pipe.fs.flow_mask.type = ETHERTYPE_IP; |
| 2093 | pipe.fs.flow_mask.u.ip.dst_ip = 0; | |
| 2094 | pipe.fs.flow_mask.u.ip.src_ip = 0; | |
| 2095 | pipe.fs.flow_mask.u.ip.dst_port = 0; | |
| 2096 | pipe.fs.flow_mask.u.ip.src_port = 0; | |
| 2097 | pipe.fs.flow_mask.u.ip.proto = 0; | |
| 984263bc MD |
2098 | end = NULL; |
| 2099 | ||
| 2100 | while (ac >= 1) { | |
| 2101 | u_int32_t *p32 = NULL; | |
| 2102 | u_int16_t *p16 = NULL; | |
| 2103 | ||
| 2104 | tok = match_token(dummynet_params, *av); | |
| 2105 | ac--; av++; | |
| 2106 | switch(tok) { | |
| 2107 | case TOK_ALL: | |
| 2108 | /* | |
| 2109 | * special case, all bits significant | |
| 2110 | */ | |
| f9edcb74 SZ |
2111 | pipe.fs.flow_mask.u.ip.dst_ip = ~0; |
| 2112 | pipe.fs.flow_mask.u.ip.src_ip = ~0; | |
| 2113 | pipe.fs.flow_mask.u.ip.dst_port = ~0; | |
| 2114 | pipe.fs.flow_mask.u.ip.src_port = ~0; | |
| 2115 | pipe.fs.flow_mask.u.ip.proto = ~0; | |
| 984263bc MD |
2116 | pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; |
| 2117 | goto end_mask; | |
| 2118 | ||
| 2119 | case TOK_DSTIP: | |
| f9edcb74 | 2120 | p32 = &pipe.fs.flow_mask.u.ip.dst_ip; |
| 984263bc MD |
2121 | break; |
| 2122 | ||
| 2123 | case TOK_SRCIP: | |
| f9edcb74 | 2124 | p32 = &pipe.fs.flow_mask.u.ip.src_ip; |
| 984263bc MD |
2125 | break; |
| 2126 | ||
| 2127 | case TOK_DSTPORT: | |
| f9edcb74 | 2128 | p16 = &pipe.fs.flow_mask.u.ip.dst_port; |
| 984263bc MD |
2129 | break; |
| 2130 | ||
| 2131 | case TOK_SRCPORT: | |
| f9edcb74 | 2132 | p16 = &pipe.fs.flow_mask.u.ip.src_port; |
| 984263bc MD |
2133 | break; |
| 2134 | ||
| 2135 | case TOK_PROTO: | |
| 2136 | break; | |
| 2137 | ||
| 2138 | default: | |
| 2139 | ac++; av--; /* backtrack */ | |
| 2140 | goto end_mask; | |
| 2141 | } | |
| 2142 | if (ac < 1) | |
| 2143 | errx(EX_USAGE, "mask: value missing"); | |
| 2144 | if (*av[0] == '/') { | |
| 2145 | a = strtoul(av[0]+1, &end, 0); | |
| 2146 | a = (a == 32) ? ~0 : (1 << a) - 1; | |
| 2147 | } else | |
| 2148 | a = strtoul(av[0], &end, 0); | |
| 2149 | if (p32 != NULL) | |
| 2150 | *p32 = a; | |
| 2151 | else if (p16 != NULL) { | |
| 2152 | if (a > 65535) | |
| 2153 | errx(EX_DATAERR, | |
| 2154 | "mask: must be 16 bit"); | |
| 2155 | *p16 = (u_int16_t)a; | |
| 2156 | } else { | |
| 2157 | if (a > 255) | |
| 2158 | errx(EX_DATAERR, | |
| 2159 | "mask: must be 8 bit"); | |
| f9edcb74 | 2160 | pipe.fs.flow_mask.u.ip.proto = (uint8_t)a; |
| 984263bc MD |
2161 | } |
| 2162 | if (a != 0) | |
| 2163 | pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; | |
| 2164 | ac--; av++; | |
| 2165 | } /* end while, config masks */ | |
| 2166 | end_mask: | |
| 2167 | break; | |
| 2168 | ||
| 2169 | case TOK_RED: | |
| 2170 | case TOK_GRED: | |
| 2171 | NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); | |
| 2172 | pipe.fs.flags_fs |= DN_IS_RED; | |
| 2173 | if (tok == TOK_GRED) | |
| 2174 | pipe.fs.flags_fs |= DN_IS_GENTLE_RED; | |
| 2175 | /* | |
| 2176 | * the format for parameters is w_q/min_th/max_th/max_p | |
| 2177 | */ | |
| 2178 | if ((end = strsep(&av[0], "/"))) { | |
| 2179 | double w_q = strtod(end, NULL); | |
| 2180 | if (w_q > 1 || w_q <= 0) | |
| 2181 | errx(EX_DATAERR, "0 < w_q <= 1"); | |
| 2182 | pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED)); | |
| 2183 | } | |
| 2184 | if ((end = strsep(&av[0], "/"))) { | |
| 2185 | pipe.fs.min_th = strtoul(end, &end, 0); | |
| 2186 | if (*end == 'K' || *end == 'k') | |
| 2187 | pipe.fs.min_th *= 1024; | |
| 2188 | } | |
| 2189 | if ((end = strsep(&av[0], "/"))) { | |
| 2190 | pipe.fs.max_th = strtoul(end, &end, 0); | |
| 2191 | if (*end == 'K' || *end == 'k') | |
| 2192 | pipe.fs.max_th *= 1024; | |
| 2193 | } | |
| 2194 | if ((end = strsep(&av[0], "/"))) { | |
| 2195 | double max_p = strtod(end, NULL); | |
| 2196 | if (max_p > 1 || max_p <= 0) | |
| 2197 | errx(EX_DATAERR, "0 < max_p <= 1"); | |
| 2198 | pipe.fs.max_p = (int)(max_p * (1 << SCALE_RED)); | |
| 2199 | } | |
| 2200 | ac--; av++; | |
| 2201 | break; | |
| 2202 | ||
| 2203 | case TOK_DROPTAIL: | |
| 2204 | pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); | |
| 2205 | break; | |
| 2206 | ||
| 2207 | case TOK_BW: | |
| ff6f118a | 2208 | NEED1("bw needs bandwidth\n"); |
| 984263bc MD |
2209 | if (do_pipe != 1) |
| 2210 | errx(EX_DATAERR, "bandwidth only valid for pipes"); | |
| 2211 | /* | |
| ff6f118a | 2212 | * set bandwidth value |
| 984263bc | 2213 | */ |
| ff6f118a SZ |
2214 | pipe.bandwidth = getbw(av[0], NULL, 1000); |
| 2215 | if (pipe.bandwidth < 0) | |
| 2216 | errx(EX_DATAERR, "bandwidth too large"); | |
| 984263bc MD |
2217 | ac--; av++; |
| 2218 | break; | |
| 2219 | ||
| 2220 | case TOK_DELAY: | |
| 2221 | if (do_pipe != 1) | |
| 2222 | errx(EX_DATAERR, "delay only valid for pipes"); | |
| 2223 | NEED1("delay needs argument 0..10000ms\n"); | |
| 2224 | pipe.delay = strtoul(av[0], NULL, 0); | |
| 2225 | ac--; av++; | |
| 2226 | break; | |
| 2227 | ||
| 2228 | case TOK_WEIGHT: | |
| 2229 | if (do_pipe == 1) | |
| 2230 | errx(EX_DATAERR,"weight only valid for queues"); | |
| 2231 | NEED1("weight needs argument 0..100\n"); | |
| 2232 | pipe.fs.weight = strtoul(av[0], &end, 0); | |
| 2233 | ac--; av++; | |
| 2234 | break; | |
| 2235 | ||
| 2236 | case TOK_PIPE: | |
| 2237 | if (do_pipe == 1) | |
| 2238 | errx(EX_DATAERR,"pipe only valid for queues"); | |
| 2239 | NEED1("pipe needs pipe_number\n"); | |
| 2240 | pipe.fs.parent_nr = strtoul(av[0], &end, 0); | |
| 2241 | ac--; av++; | |
| 2242 | break; | |
| 2243 | ||
| 2244 | default: | |
| 2245 | errx(EX_DATAERR, "unrecognised option ``%s''", *av); | |
| 2246 | } | |
| 2247 | } | |
| 2248 | if (do_pipe == 1) { | |
| 2249 | if (pipe.pipe_nr == 0) | |
| 2250 | errx(EX_DATAERR, "pipe_nr must be > 0"); | |
| 2251 | if (pipe.delay > 10000) | |
| 2252 | errx(EX_DATAERR, "delay must be < 10000"); | |
| 2253 | } else { /* do_pipe == 2, queue */ | |
| 2254 | if (pipe.fs.parent_nr == 0) | |
| 2255 | errx(EX_DATAERR, "pipe must be > 0"); | |
| 2256 | if (pipe.fs.weight >100) | |
| 2257 | errx(EX_DATAERR, "weight must be <= 100"); | |
| 2258 | } | |
| 2259 | if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) { | |
| 2260 | if (pipe.fs.qsize > 1024*1024) | |
| 2261 | errx(EX_DATAERR, "queue size must be < 1MB"); | |
| 2262 | } else { | |
| 2263 | if (pipe.fs.qsize > 100) | |
| 2264 | errx(EX_DATAERR, "2 <= queue size <= 100"); | |
| 2265 | } | |
| 2266 | if (pipe.fs.flags_fs & DN_IS_RED) { | |
| 2267 | size_t len; | |
| 2268 | int lookup_depth, avg_pkt_size; | |
| 2269 | double s, idle, weight, w_q; | |
| 9caa622c | 2270 | int clock_hz; |
| 984263bc MD |
2271 | int t; |
| 2272 | ||
| 2273 | if (pipe.fs.min_th >= pipe.fs.max_th) | |
| 2274 | errx(EX_DATAERR, "min_th %d must be < than max_th %d", | |
| 2275 | pipe.fs.min_th, pipe.fs.max_th); | |
| 2276 | if (pipe.fs.max_th == 0) | |
| 2277 | errx(EX_DATAERR, "max_th must be > 0"); | |
| 2278 | ||
| 2279 | len = sizeof(int); | |
| 2280 | if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", | |
| 2281 | &lookup_depth, &len, NULL, 0) == -1) | |
| 2282 | ||
| 2283 | errx(1, "sysctlbyname(\"%s\")", | |
| 2284 | "net.inet.ip.dummynet.red_lookup_depth"); | |
| 2285 | if (lookup_depth == 0) | |
| 2286 | errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" | |
| 2287 | " must be greater than zero"); | |
| 2288 | ||
| 2289 | len = sizeof(int); | |
| 2290 | if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", | |
| 2291 | &avg_pkt_size, &len, NULL, 0) == -1) | |
| 2292 | ||
| 2293 | errx(1, "sysctlbyname(\"%s\")", | |
| 2294 | "net.inet.ip.dummynet.red_avg_pkt_size"); | |
| 2295 | if (avg_pkt_size == 0) | |
| 2296 | errx(EX_DATAERR, | |
| 2297 | "net.inet.ip.dummynet.red_avg_pkt_size must" | |
| 2298 | " be greater than zero"); | |
| 2299 | ||
| 9caa622c SZ |
2300 | len = sizeof(clock_hz); |
| 2301 | if (sysctlbyname("net.inet.ip.dummynet.hz", &clock_hz, &len, | |
| 2302 | NULL, 0) == -1) { | |
| 2303 | errx(1, "sysctlbyname(\"%s\")", | |
| 2304 | "net.inet.ip.dummynet.hz"); | |
| 2305 | } | |
| 984263bc MD |
2306 | |
| 2307 | /* | |
| 2308 | * Ticks needed for sending a medium-sized packet. | |
| 2309 | * Unfortunately, when we are configuring a WF2Q+ queue, we | |
| 2310 | * do not have bandwidth information, because that is stored | |
| 2311 | * in the parent pipe, and also we have multiple queues | |
| 2312 | * competing for it. So we set s=0, which is not very | |
| 2313 | * correct. But on the other hand, why do we want RED with | |
| 2314 | * WF2Q+ ? | |
| 2315 | */ | |
| 2316 | if (pipe.bandwidth==0) /* this is a WF2Q+ queue */ | |
| 2317 | s = 0; | |
| 2318 | else | |
| 9caa622c | 2319 | s = clock_hz * avg_pkt_size * 8 / pipe.bandwidth; |
| 984263bc MD |
2320 | |
| 2321 | /* | |
| 2322 | * max idle time (in ticks) before avg queue size becomes 0. | |
| 2323 | * NOTA: (3/w_q) is approx the value x so that | |
| 2324 | * (1-w_q)^x < 10^-3. | |
| 2325 | */ | |
| 2326 | w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED); | |
| 2327 | idle = s * 3. / w_q; | |
| 2328 | pipe.fs.lookup_step = (int)idle / lookup_depth; | |
| 2329 | if (!pipe.fs.lookup_step) | |
| 2330 | pipe.fs.lookup_step = 1; | |
| 2331 | weight = 1 - w_q; | |
| 2332 | for (t = pipe.fs.lookup_step; t > 0; --t) | |
| 2333 | weight *= weight; | |
| 2334 | pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); | |
| 2335 | } | |
| 2336 | i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe, | |
| 2337 | sizeof pipe); | |
| 2338 | if (i) | |
| 2339 | err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); | |
| 2340 | } | |
| 2341 | ||
| 2342 | static void | |
| 2343 | get_mac_addr_mask(char *p, u_char *addr, u_char *mask) | |
| 2344 | { | |
| 2345 | int i, l; | |
| 2346 | ||
| 2347 | for (i=0; i<6; i++) | |
| 2348 | addr[i] = mask[i] = 0; | |
| 2349 | if (!strcmp(p, "any")) | |
| 2350 | return; | |
| 2351 | ||
| 2352 | for (i=0; *p && i<6;i++, p++) { | |
| 2353 | addr[i] = strtol(p, &p, 16); | |
| 2354 | if (*p != ':') /* we start with the mask */ | |
| 2355 | break; | |
| 2356 | } | |
| 2357 | if (*p == '/') { /* mask len */ | |
| 2358 | l = strtol(p+1, &p, 0); | |
| 2359 | for (i=0; l>0; l -=8, i++) | |
| 2360 | mask[i] = (l >=8) ? 0xff : (~0) << (8-l); | |
| 2361 | } else if (*p == '&') { /* mask */ | |
| 2362 | for (i=0, p++; *p && i<6;i++, p++) { | |
| 2363 | mask[i] = strtol(p, &p, 16); | |
| 2364 | if (*p != ':') | |
| 2365 | break; | |
| 2366 | } | |
| 2367 | } else if (*p == '\0') { | |
| 2368 | for (i=0; i<6; i++) | |
| 2369 | mask[i] = 0xff; | |
| 2370 | } | |
| 2371 | for (i=0; i<6; i++) | |
| 2372 | addr[i] &= mask[i]; | |
| 2373 | } | |
| 2374 | ||
| 2375 | /* | |
| 2376 | * helper function, updates the pointer to cmd with the length | |
| 2377 | * of the current command, and also cleans up the first word of | |
| 2378 | * the new command in case it has been clobbered before. | |
| 2379 | */ | |
| 2380 | static ipfw_insn * | |
| 2381 | next_cmd(ipfw_insn *cmd) | |
| 2382 | { | |
| 2383 | cmd += F_LEN(cmd); | |
| 2384 | bzero(cmd, sizeof(*cmd)); | |
| 2385 | return cmd; | |
| 2386 | } | |
| 2387 | ||
| 2388 | /* | |
| 2389 | * A function to fill simple commands of size 1. | |
| 2390 | * Existing flags are preserved. | |
| 2391 | */ | |
| 2392 | static void | |
| 2393 | fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, u_int16_t arg) | |
| 2394 | { | |
| 2395 | cmd->opcode = opcode; | |
| 2396 | cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; | |
| 2397 | cmd->arg1 = arg; | |
| 2398 | } | |
| 2399 | ||
| 2400 | /* | |
| 2401 | * Fetch and add the MAC address and type, with masks. This generates one or | |
| 2402 | * two microinstructions, and returns the pointer to the last one. | |
| 2403 | */ | |
| 2404 | static ipfw_insn * | |
| 2405 | add_mac(ipfw_insn *cmd, int ac, char *av[]) | |
| 2406 | { | |
| 2407 | ipfw_insn_mac *mac; | |
| 2408 | ||
| 2409 | if (ac < 2) | |
| 2410 | errx(EX_DATAERR, "MAC dst src"); | |
| 2411 | ||
| 2412 | cmd->opcode = O_MACADDR2; | |
| 2413 | cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); | |
| 2414 | ||
| 2415 | mac = (ipfw_insn_mac *)cmd; | |
| 2416 | get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ | |
| 2417 | get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */ | |
| 2418 | return cmd; | |
| 2419 | } | |
| 2420 | ||
| 2421 | static ipfw_insn * | |
| 2422 | add_mactype(ipfw_insn *cmd, int ac, char *av) | |
| 2423 | { | |
| 2424 | if (ac < 1) | |
| 2425 | errx(EX_DATAERR, "missing MAC type"); | |
| 2426 | if (strcmp(av, "any") != 0) { /* we have a non-null type */ | |
| 2427 | fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE); | |
| 2428 | cmd->opcode = O_MAC_TYPE; | |
| 2429 | return cmd; | |
| 2430 | } else | |
| 2431 | return NULL; | |
| 2432 | } | |
| 2433 | ||
| 2434 | static ipfw_insn * | |
| 2435 | add_proto(ipfw_insn *cmd, char *av) | |
| 2436 | { | |
| 2437 | struct protoent *pe; | |
| 2438 | u_char proto = 0; | |
| 2439 | ||
| 2440 | if (!strncmp(av, "all", strlen(av))) | |
| 2441 | ; /* same as "ip" */ | |
| 2442 | else if ((proto = atoi(av)) > 0) | |
| 2443 | ; /* all done! */ | |
| 2444 | else if ((pe = getprotobyname(av)) != NULL) | |
| 2445 | proto = pe->p_proto; | |
| 2446 | else | |
| 2447 | return NULL; | |
| 2448 | if (proto != IPPROTO_IP) | |
| 2449 | fill_cmd(cmd, O_PROTO, 0, proto); | |
| 2450 | return cmd; | |
| 2451 | } | |
| 2452 | ||
| 2453 | static ipfw_insn * | |
| 2454 | add_srcip(ipfw_insn *cmd, char *av) | |
| 2455 | { | |
| 2456 | fill_ip((ipfw_insn_ip *)cmd, av); | |
| 2457 | if (cmd->opcode == O_IP_DST_SET) /* set */ | |
| 2458 | cmd->opcode = O_IP_SRC_SET; | |
| 2459 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ | |
| 2460 | cmd->opcode = O_IP_SRC_ME; | |
| 2461 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ | |
| 2462 | cmd->opcode = O_IP_SRC; | |
| 2463 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */ | |
| 2464 | cmd->opcode = O_IP_SRC_MASK; | |
| 2465 | return cmd; | |
| 2466 | } | |
| 2467 | ||
| 2468 | static ipfw_insn * | |
| 2469 | add_dstip(ipfw_insn *cmd, char *av) | |
| 2470 | { | |
| 2471 | fill_ip((ipfw_insn_ip *)cmd, av); | |
| 2472 | if (cmd->opcode == O_IP_DST_SET) /* set */ | |
| 2473 | ; | |
| 2474 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ | |
| 2475 | cmd->opcode = O_IP_DST_ME; | |
| 2476 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ | |
| 2477 | cmd->opcode = O_IP_DST; | |
| 2478 | else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */ | |
| 2479 | cmd->opcode = O_IP_DST_MASK; | |
| 2480 | return cmd; | |
| 2481 | } | |
| 2482 | ||
| 2483 | static ipfw_insn * | |
| 2484 | add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) | |
| 2485 | { | |
| 2486 | if (!strncmp(av, "any", strlen(av))) { | |
| 2487 | return NULL; | |
| 2488 | } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) { | |
| 2489 | /* XXX todo: check that we have a protocol with ports */ | |
| 2490 | cmd->opcode = opcode; | |
| 2491 | return cmd; | |
| 2492 | } | |
| 2493 | return NULL; | |
| 2494 | } | |
| 2495 | ||
| 2496 | /* | |
| 2497 | * Parse arguments and assemble the microinstructions which make up a rule. | |
| 2498 | * Rules are added into the 'rulebuf' and then copied in the correct order | |
| 2499 | * into the actual rule. | |
| 2500 | * | |
| 2501 | * The syntax for a rule starts with the action, followed by an | |
| 2502 | * optional log action, and the various match patterns. | |
| 2503 | * In the assembled microcode, the first opcode must be a O_PROBE_STATE | |
| 2504 | * (generated if the rule includes a keep-state option), then the | |
| 2505 | * various match patterns, the "log" action, and the actual action. | |
| 2506 | * | |
| 2507 | */ | |
| 2508 | static void | |
| 2509 | add(int ac, char *av[]) | |
| 2510 | { | |
| 2511 | /* | |
| 2512 | * rules are added into the 'rulebuf' and then copied in | |
| 2513 | * the correct order into the actual rule. | |
| 2514 | * Some things that need to go out of order (prob, action etc.) | |
| 2515 | * go into actbuf[]. | |
| 2516 | */ | |
| b78533e2 SZ |
2517 | static uint32_t rulebuf[IPFW_RULE_SIZE_MAX]; |
| 2518 | static uint32_t actbuf[IPFW_RULE_SIZE_MAX]; | |
| 2519 | static uint32_t cmdbuf[IPFW_RULE_SIZE_MAX]; | |
| 984263bc | 2520 | |
| 7700730e | 2521 | ipfw_insn *src, *dst, *cmd, *action, *prev = NULL; |
| 984263bc MD |
2522 | ipfw_insn *first_cmd; /* first match pattern */ |
| 2523 | ||
| b78533e2 | 2524 | struct ipfw_ioc_rule *rule; |
| 984263bc MD |
2525 | |
| 2526 | /* | |
| 2527 | * various flags used to record that we entered some fields. | |
| 2528 | */ | |
| 2529 | ipfw_insn *have_state = NULL; /* check-state or keep-state */ | |
| 2530 | ||
| 2531 | int i; | |
| 2532 | ||
| 2533 | int open_par = 0; /* open parenthesis ( */ | |
| 2534 | ||
| 2535 | /* proto is here because it is used to fetch ports */ | |
| 2536 | u_char proto = IPPROTO_IP; /* default protocol */ | |
| 2537 | ||
| 2538 | double match_prob = 1; /* match probability, default is always match */ | |
| 2539 | ||
| 2540 | bzero(actbuf, sizeof(actbuf)); /* actions go here */ | |
| 2541 | bzero(cmdbuf, sizeof(cmdbuf)); | |
| 2542 | bzero(rulebuf, sizeof(rulebuf)); | |
| 2543 | ||
| b78533e2 | 2544 | rule = (struct ipfw_ioc_rule *)rulebuf; |
| 984263bc MD |
2545 | cmd = (ipfw_insn *)cmdbuf; |
| 2546 | action = (ipfw_insn *)actbuf; | |
| 2547 | ||
| 2548 | av++; ac--; | |
| 2549 | ||
| 2550 | /* [rule N] -- Rule number optional */ | |
| 2551 | if (ac && isdigit(**av)) { | |
| 2552 | rule->rulenum = atoi(*av); | |
| 2553 | av++; | |
| 2554 | ac--; | |
| 2555 | } | |
| 2556 | ||
| 2557 | /* [set N] -- set number (0..30), optional */ | |
| 2558 | if (ac > 1 && !strncmp(*av, "set", strlen(*av))) { | |
| 2559 | int set = strtoul(av[1], NULL, 10); | |
| 2560 | if (set < 0 || set > 30) | |
| 2561 | errx(EX_DATAERR, "illegal set %s", av[1]); | |
| 2562 | rule->set = set; | |
| 2563 | av += 2; ac -= 2; | |
| 2564 | } | |
| 2565 | ||
| 2566 | /* [prob D] -- match probability, optional */ | |
| 2567 | if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) { | |
| 2568 | match_prob = strtod(av[1], NULL); | |
| 2569 | ||
| 2570 | if (match_prob <= 0 || match_prob > 1) | |
| 2571 | errx(EX_DATAERR, "illegal match prob. %s", av[1]); | |
| 2572 | av += 2; ac -= 2; | |
| 2573 | } | |
| 2574 | ||
| 2575 | /* action -- mandatory */ | |
| 2576 | NEED1("missing action"); | |
| 2577 | i = match_token(rule_actions, *av); | |
| 2578 | ac--; av++; | |
| 2579 | action->len = 1; /* default */ | |
| 2580 | switch(i) { | |
| 2581 | case TOK_CHECKSTATE: | |
| 2582 | have_state = action; | |
| 2583 | action->opcode = O_CHECK_STATE; | |
| 2584 | break; | |
| 2585 | ||
| 2586 | case TOK_ACCEPT: | |
| 2587 | action->opcode = O_ACCEPT; | |
| 2588 | break; | |
| 2589 | ||
| 2590 | case TOK_DENY: | |
| 2591 | action->opcode = O_DENY; | |
| 2592 | action->arg1 = 0; | |
| 2593 | break; | |
| 2594 | ||
| 2595 | case TOK_REJECT: | |
| 2596 | action->opcode = O_REJECT; | |
| 2597 | action->arg1 = ICMP_UNREACH_HOST; | |
| 2598 | break; | |
| 2599 | ||
| 2600 | case TOK_RESET: | |
| 2601 | action->opcode = O_REJECT; | |
| 2602 | action->arg1 = ICMP_REJECT_RST; | |
| 2603 | break; | |
| 2604 | ||
| 2605 | case TOK_UNREACH: | |
| 2606 | action->opcode = O_REJECT; | |
| 2607 | NEED1("missing reject code"); | |
| 2608 | fill_reject_code(&action->arg1, *av); | |
| 2609 | ac--; av++; | |
| 2610 | break; | |
| 2611 | ||
| 2612 | case TOK_COUNT: | |
| 2613 | action->opcode = O_COUNT; | |
| 2614 | break; | |
| 2615 | ||
| 2616 | case TOK_QUEUE: | |
| 2617 | case TOK_PIPE: | |
| 2618 | action->len = F_INSN_SIZE(ipfw_insn_pipe); | |
| 2619 | case TOK_SKIPTO: | |
| 2620 | if (i == TOK_QUEUE) | |
| 2621 | action->opcode = O_QUEUE; | |
| 2622 | else if (i == TOK_PIPE) | |
| 2623 | action->opcode = O_PIPE; | |
| 2624 | else if (i == TOK_SKIPTO) | |
| 2625 | action->opcode = O_SKIPTO; | |
| 2626 | NEED1("missing skipto/pipe/queue number"); | |
| 2627 | action->arg1 = strtoul(*av, NULL, 10); | |
| 2628 | av++; ac--; | |
| 2629 | break; | |
| 2630 | ||
| 2631 | case TOK_DIVERT: | |
| 2632 | case TOK_TEE: | |
| 2633 | action->opcode = (i == TOK_DIVERT) ? O_DIVERT : O_TEE; | |
| 2634 | NEED1("missing divert/tee port"); | |
| 2635 | action->arg1 = strtoul(*av, NULL, 0); | |
| 2636 | if (action->arg1 == 0) { | |
| 2637 | struct servent *s; | |
| 2638 | setservent(1); | |
| 2639 | s = getservbyname(av[0], "divert"); | |
| 2640 | if (s != NULL) | |
| 2641 | action->arg1 = ntohs(s->s_port); | |
| 2642 | else | |
| 2643 | errx(EX_DATAERR, "illegal divert/tee port"); | |
| 2644 | } | |
| 2645 | ac--; av++; | |
| 2646 | break; | |
| 2647 | ||
| 2648 | case TOK_FORWARD: { | |
| 2649 | ipfw_insn_sa *p = (ipfw_insn_sa *)action; | |
| 2650 | char *s, *end; | |
| 2651 | ||
| 2652 | NEED1("missing forward address[:port]"); | |
| 2653 | ||
| 2654 | action->opcode = O_FORWARD_IP; | |
| 2655 | action->len = F_INSN_SIZE(ipfw_insn_sa); | |
| 2656 | ||
| 2657 | p->sa.sin_len = sizeof(struct sockaddr_in); | |
| 2658 | p->sa.sin_family = AF_INET; | |
| 2659 | p->sa.sin_port = 0; | |
| 2660 | /* | |
| 2661 | * locate the address-port separator (':' or ',') | |
| 2662 | */ | |
| 2663 | s = strchr(*av, ':'); | |
| 2664 | if (s == NULL) | |
| 2665 | s = strchr(*av, ','); | |
| 2666 | if (s != NULL) { | |
| 2667 | *(s++) = '\0'; | |
| 2668 | i = strtoport(s, &end, 0 /* base */, 0 /* proto */); | |
| 2669 | if (s == end) | |
| 2670 | errx(EX_DATAERR, | |
| 2671 | "illegal forwarding port ``%s''", s); | |
| 2672 | p->sa.sin_port = (u_short)i; | |
| 2673 | } | |
| 2674 | lookup_host(*av, &(p->sa.sin_addr)); | |
| 2675 | } | |
| 2676 | ac--; av++; | |
| 2677 | break; | |
| 2678 | ||
| 2679 | default: | |
| 2680 | errx(EX_DATAERR, "invalid action %s\n", av[-1]); | |
| 2681 | } | |
| 2682 | action = next_cmd(action); | |
| 2683 | ||
| 2684 | /* | |
| 2685 | * [log [logamount N]] -- log, optional | |
| 2686 | * | |
| 2687 | * If exists, it goes first in the cmdbuf, but then it is | |
| 2688 | * skipped in the copy section to the end of the buffer. | |
| 2689 | */ | |
| 2690 | if (ac && !strncmp(*av, "log", strlen(*av))) { | |
| 2691 | ipfw_insn_log *c = (ipfw_insn_log *)cmd; | |
| 2692 | ||
| 2693 | cmd->len = F_INSN_SIZE(ipfw_insn_log); | |
| 2694 | cmd->opcode = O_LOG; | |
| 2695 | av++; ac--; | |
| 2696 | if (ac && !strncmp(*av, "logamount", strlen(*av))) { | |
| 2697 | ac--; av++; | |
| 2698 | NEED1("logamount requires argument"); | |
| 2699 | c->max_log = atoi(*av); | |
| 2700 | if (c->max_log < 0) | |
| 2701 | errx(EX_DATAERR, "logamount must be positive"); | |
| 2702 | ac--; av++; | |
| 2703 | } | |
| 2704 | cmd = next_cmd(cmd); | |
| 2705 | } | |
| 2706 | ||
| 2707 | if (have_state) /* must be a check-state, we are done */ | |
| 2708 | goto done; | |
| 2709 | ||
| 2710 | #define OR_START(target) \ | |
| 2711 | if (ac && (*av[0] == '(' || *av[0] == '{')) { \ | |
| 2712 | if (open_par) \ | |
| 2713 | errx(EX_USAGE, "nested \"(\" not allowed\n"); \ | |
| 2714 | prev = NULL; \ | |
| 2715 | open_par = 1; \ | |
| 2716 | if ( (av[0])[1] == '\0') { \ | |
| 2717 | ac--; av++; \ | |
| 2718 | } else \ | |
| 2719 | (*av)++; \ | |
| 2720 | } \ | |
| 2721 | target: \ | |
| 2722 | ||
| 2723 | ||
| 2724 | #define CLOSE_PAR \ | |
| 2725 | if (open_par) { \ | |
| 2726 | if (ac && ( \ | |
| 2727 | !strncmp(*av, ")", strlen(*av)) || \ | |
| 2728 | !strncmp(*av, "}", strlen(*av)) )) { \ | |
| 2729 | prev = NULL; \ | |
| 2730 | open_par = 0; \ | |
| 2731 | ac--; av++; \ | |
| 2732 | } else \ | |
| 2733 | errx(EX_USAGE, "missing \")\"\n"); \ | |
| 2734 | } | |
| 2735 | ||
| 2736 | #define NOT_BLOCK \ | |
| 2737 | if (ac && !strncmp(*av, "not", strlen(*av))) { \ | |
| 2738 | if (cmd->len & F_NOT) \ | |
| 2739 | errx(EX_USAGE, "double \"not\" not allowed\n"); \ | |
| 2740 | cmd->len |= F_NOT; \ | |
| 2741 | ac--; av++; \ | |
| 2742 | } | |
| 2743 | ||
| 2744 | #define OR_BLOCK(target) \ | |
| 2745 | if (ac && !strncmp(*av, "or", strlen(*av))) { \ | |
| 2746 | if (prev == NULL || open_par == 0) \ | |
| 2747 | errx(EX_DATAERR, "invalid OR block"); \ | |
| 2748 | prev->len |= F_OR; \ | |
| 2749 | ac--; av++; \ | |
| 2750 | goto target; \ | |
| 2751 | } \ | |
| 2752 | CLOSE_PAR; | |
| 2753 | ||
| 2754 | first_cmd = cmd; | |
| 2755 | ||
| 2756 | #if 0 | |
| 2757 | /* | |
| 2758 | * MAC addresses, optional. | |
| 2759 | * If we have this, we skip the part "proto from src to dst" | |
| 2760 | * and jump straight to the option parsing. | |
| 2761 | */ | |
| 2762 | NOT_BLOCK; | |
| 2763 | NEED1("missing protocol"); | |
| 2764 | if (!strncmp(*av, "MAC", strlen(*av)) || | |
| 2765 | !strncmp(*av, "mac", strlen(*av))) { | |
| 2766 | ac--; av++; /* the "MAC" keyword */ | |
| 2767 | add_mac(cmd, ac, av); /* exits in case of errors */ | |
| 2768 | cmd = next_cmd(cmd); | |
| 2769 | ac -= 2; av += 2; /* dst-mac and src-mac */ | |
| 2770 | NOT_BLOCK; | |
| 2771 | NEED1("missing mac type"); | |
| 2772 | if (add_mactype(cmd, ac, av[0])) | |
| 2773 | cmd = next_cmd(cmd); | |
| 2774 | ac--; av++; /* any or mac-type */ | |
| 2775 | goto read_options; | |
| 2776 | } | |
| 2777 | #endif | |
| 2778 | ||
| 2779 | /* | |
| 2780 | * protocol, mandatory | |
| 2781 | */ | |
| 2782 | OR_START(get_proto); | |
| 2783 | NOT_BLOCK; | |
| 2784 | NEED1("missing protocol"); | |
| 2785 | if (add_proto(cmd, *av)) { | |
| 2786 | av++; ac--; | |
| 2787 | if (F_LEN(cmd) == 0) /* plain IP */ | |
| 2788 | proto = 0; | |
| 2789 | else { | |
| 2790 | proto = cmd->arg1; | |
| 2791 | prev = cmd; | |
| 2792 | cmd = next_cmd(cmd); | |
| 2793 | } | |
| 2794 | } else if (first_cmd != cmd) { | |
| 7700730e | 2795 | errx(EX_DATAERR, "invalid protocol ``%s''", *av); |
| 984263bc MD |
2796 | } else |
| 2797 | goto read_options; | |
| 2798 | OR_BLOCK(get_proto); | |
| 2799 | ||
| 2800 | /* | |
| 2801 | * "from", mandatory | |
| 2802 | */ | |
| 2803 | if (!ac || strncmp(*av, "from", strlen(*av))) | |
| 2804 | errx(EX_USAGE, "missing ``from''"); | |
| 2805 | ac--; av++; | |
| 2806 | ||
| 2807 | /* | |
| 2808 | * source IP, mandatory | |
| 2809 | */ | |
| 2810 | OR_START(source_ip); | |
| 2811 | NOT_BLOCK; /* optional "not" */ | |
| 2812 | NEED1("missing source address"); | |
| 2813 | if (add_srcip(cmd, *av)) { | |
| 2814 | ac--; av++; | |
| 2815 | if (F_LEN(cmd) != 0) { /* ! any */ | |
| 2816 | prev = cmd; | |
| 2817 | cmd = next_cmd(cmd); | |
| 2818 | } | |
| 2819 | } | |
| 2820 | OR_BLOCK(source_ip); | |
| 2821 | ||
| 2822 | /* | |
| 2823 | * source ports, optional | |
| 2824 | */ | |
| 2825 | NOT_BLOCK; /* optional "not" */ | |
| 2826 | if (ac) { | |
| 2827 | if (!strncmp(*av, "any", strlen(*av)) || | |
| 2828 | add_ports(cmd, *av, proto, O_IP_SRCPORT)) { | |
| 2829 | ac--; av++; | |
| 2830 | if (F_LEN(cmd) != 0) | |
| 2831 | cmd = next_cmd(cmd); | |
| 2832 | } | |
| 2833 | } | |
| 2834 | ||
| 2835 | /* | |
| 2836 | * "to", mandatory | |
| 2837 | */ | |
| 2838 | if (!ac || strncmp(*av, "to", strlen(*av))) | |
| 2839 | errx(EX_USAGE, "missing ``to''"); | |
| 2840 | av++; ac--; | |
| 2841 | ||
| 2842 | /* | |
| 2843 | * destination, mandatory | |
| 2844 | */ | |
| 2845 | OR_START(dest_ip); | |
| 2846 | NOT_BLOCK; /* optional "not" */ | |
| 2847 | NEED1("missing dst address"); | |
| 2848 | if (add_dstip(cmd, *av)) { | |
| 2849 | ac--; av++; | |
| 2850 | if (F_LEN(cmd) != 0) { /* ! any */ | |
| 2851 | prev = cmd; | |
| 2852 | cmd = next_cmd(cmd); | |
| 2853 | } | |
| 2854 | } | |
| 2855 | OR_BLOCK(dest_ip); | |
| 2856 | ||
| 2857 | /* | |
| 2858 | * dest. ports, optional | |
| 2859 | */ | |
| 2860 | NOT_BLOCK; /* optional "not" */ | |
| 2861 | if (ac) { | |
| 2862 | if (!strncmp(*av, "any", strlen(*av)) || | |
| 2863 | add_ports(cmd, *av, proto, O_IP_DSTPORT)) { | |
| 2864 | ac--; av++; | |
| 2865 | if (F_LEN(cmd) != 0) | |
| 2866 | cmd = next_cmd(cmd); | |
| 2867 | } | |
| 2868 | } | |
| 2869 | ||
| 2870 | read_options: | |
| 2871 | if (ac && first_cmd == cmd) { | |
| 2872 | /* | |
| 2873 | * nothing specified so far, store in the rule to ease | |
| 2874 | * printout later. | |
| 2875 | */ | |
| b78533e2 | 2876 | rule->usr_flags = IPFW_USR_F_NORULE; |
| 984263bc MD |
2877 | } |
| 2878 | prev = NULL; | |
| 2879 | while (ac) { | |
| 2880 | char *s; | |
| 2881 | ipfw_insn_u32 *cmd32; /* alias for cmd */ | |
| 2882 | ||
| 2883 | s = *av; | |
| 2884 | cmd32 = (ipfw_insn_u32 *)cmd; | |
| 2885 | ||
| 2886 | if (*s == '!') { /* alternate syntax for NOT */ | |
| 2887 | if (cmd->len & F_NOT) | |
| 2888 | errx(EX_USAGE, "double \"not\" not allowed\n"); | |
| 2889 | cmd->len = F_NOT; | |
| 2890 | s++; | |
| 2891 | } | |
| 2892 | i = match_token(rule_options, s); | |
| 2893 | ac--; av++; | |
| 2894 | switch(i) { | |
| 2895 | case TOK_NOT: | |
| 2896 | if (cmd->len & F_NOT) | |
| 2897 | errx(EX_USAGE, "double \"not\" not allowed\n"); | |
| 2898 | cmd->len = F_NOT; | |
| 2899 | break; | |
| 2900 | ||
| 2901 | case TOK_OR: | |
| 2902 | if (open_par == 0 || prev == NULL) | |
| 2903 | errx(EX_USAGE, "invalid \"or\" block\n"); | |
| 2904 | prev->len |= F_OR; | |
| 2905 | break; | |
| 2906 | ||
| 2907 | case TOK_STARTBRACE: | |
| 2908 | if (open_par) | |
| 2909 | errx(EX_USAGE, "+nested \"(\" not allowed\n"); | |
| 2910 | open_par = 1; | |
| 2911 | break; | |
| 2912 | ||
| 2913 | case TOK_ENDBRACE: | |
| 2914 | if (!open_par) | |
| 2915 | errx(EX_USAGE, "+missing \")\"\n"); | |
| 2916 | open_par = 0; | |
| 2917 | prev = NULL; | |
| 2918 | break; | |
| 2919 | ||
| 2920 | case TOK_IN: | |
| 2921 | fill_cmd(cmd, O_IN, 0, 0); | |
| 2922 | break; | |
| 2923 | ||
| 2924 | case TOK_OUT: | |
| 2925 | cmd->len ^= F_NOT; /* toggle F_NOT */ | |
| 2926 | fill_cmd(cmd, O_IN, 0, 0); | |
| 2927 | break; | |
| 2928 | ||
| 2929 | case TOK_FRAG: | |
| 2930 | fill_cmd(cmd, O_FRAG, 0, 0); | |
| 2931 | break; | |
| 2932 | ||
| 2933 | case TOK_LAYER2: | |
| 2934 | fill_cmd(cmd, O_LAYER2, 0, 0); | |
| 2935 | break; | |
| 2936 | ||
| 2937 | case TOK_XMIT: | |
| 2938 | case TOK_RECV: | |
| 2939 | case TOK_VIA: | |
| 2940 | NEED1("recv, xmit, via require interface name" | |
| 2941 | " or address"); | |
| 2942 | fill_iface((ipfw_insn_if *)cmd, av[0]); | |
| 2943 | ac--; av++; | |
| 2944 | if (F_LEN(cmd) == 0) /* not a valid address */ | |
| 2945 | break; | |
| 2946 | if (i == TOK_XMIT) | |
| 2947 | cmd->opcode = O_XMIT; | |
| 2948 | else if (i == TOK_RECV) | |
| 2949 | cmd->opcode = O_RECV; | |
| 2950 | else if (i == TOK_VIA) | |
| 2951 | cmd->opcode = O_VIA; | |
| 2952 | break; | |
| 2953 | ||
| 2954 | case TOK_ICMPTYPES: | |
| 2955 | NEED1("icmptypes requires list of types"); | |
| 2956 | fill_icmptypes((ipfw_insn_u32 *)cmd, *av); | |
| 2957 | av++; ac--; | |
| 2958 | break; | |
| 2959 | ||
| 2960 | case TOK_IPTTL: | |
| 2961 | NEED1("ipttl requires TTL"); | |
| 2962 | fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); | |
| 2963 | ac--; av++; | |
| 2964 | break; | |
| 2965 | ||
| 2966 | case TOK_IPID: | |
| 2967 | NEED1("ipid requires length"); | |
| 2968 | fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); | |
| 2969 | ac--; av++; | |
| 2970 | break; | |
| 2971 | ||
| 2972 | case TOK_IPLEN: | |
| 2973 | NEED1("iplen requires length"); | |
| 2974 | fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); | |
| 2975 | ac--; av++; | |
| 2976 | break; | |
| 2977 | ||
| 2978 | case TOK_IPVER: | |
| 2979 | NEED1("ipver requires version"); | |
| 2980 | fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); | |
| 2981 | ac--; av++; | |
| 2982 | break; | |
| 2983 | ||
| 2984 | case TOK_IPPRECEDENCE: | |
| 2985 | NEED1("ipprecedence requires value"); | |
| 2986 | fill_cmd(cmd, O_IPPRECEDENCE, 0, | |
| 2987 | (strtoul(*av, NULL, 0) & 7) << 5); | |
| 2988 | ac--; av++; | |
| 2989 | break; | |
| 2990 | ||
| 2991 | case TOK_IPOPTS: | |
| 2992 | NEED1("missing argument for ipoptions"); | |
| 2993 | fill_flags(cmd, O_IPOPT, f_ipopts, *av); | |
| 2994 | ac--; av++; | |
| 2995 | break; | |
| 2996 | ||
| 2997 | case TOK_IPTOS: | |
| 2998 | NEED1("missing argument for iptos"); | |
| 2999 | fill_flags(cmd, O_IPTOS, f_iptos, *av); | |
| 3000 | ac--; av++; | |
| 3001 | break; | |
| 3002 | ||
| 3003 | case TOK_UID: | |
| 3004 | NEED1("uid requires argument"); | |
| 3005 | { | |
| 3006 | char *end; | |
| 3007 | uid_t uid; | |
| 3008 | struct passwd *pwd; | |
| 3009 | ||
| 3010 | cmd->opcode = O_UID; | |
| 3011 | uid = strtoul(*av, &end, 0); | |
| 3012 | pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); | |
| 3013 | if (pwd == NULL) | |
| 3014 | errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); | |
| 3015 | cmd32->d[0] = pwd->pw_uid; | |
| 3016 | cmd->len = F_INSN_SIZE(ipfw_insn_u32); | |
| 3017 | ac--; av++; | |
| 3018 | } | |
| 3019 | break; | |
| 3020 | ||
| 3021 | case TOK_GID: | |
| 3022 | NEED1("gid requires argument"); | |
| 3023 | { | |
| 3024 | char *end; | |
| 3025 | gid_t gid; | |
| 3026 | struct group *grp; | |
| 3027 | ||
| 3028 | cmd->opcode = O_GID; | |
| 3029 | gid = strtoul(*av, &end, 0); | |
| 3030 | grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); | |
| 3031 | if (grp == NULL) | |
| 3032 | errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); | |
| 3033 | cmd32->d[0] = grp->gr_gid; | |
| 3034 | cmd->len = F_INSN_SIZE(ipfw_insn_u32); | |
| 3035 | ac--; av++; | |
| 3036 | } | |
| 3037 | break; | |
| 3038 | ||
| 3039 | case TOK_ESTAB: | |
| 3040 | fill_cmd(cmd, O_ESTAB, 0, 0); | |
| 3041 | break; | |
| 3042 | ||
| 3043 | case TOK_SETUP: | |
| 3044 | fill_cmd(cmd, O_TCPFLAGS, 0, | |
| 3045 | (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); | |
| 3046 | break; | |
| 3047 | ||
| 3048 | case TOK_TCPOPTS: | |
| 3049 | NEED1("missing argument for tcpoptions"); | |
| 3050 | fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); | |
| 3051 | ac--; av++; | |
| 3052 | break; | |
| 3053 | ||
| 3054 | case TOK_TCPSEQ: | |
| 3055 | case TOK_TCPACK: | |
| 3056 | NEED1("tcpseq/tcpack requires argument"); | |
| 3057 | cmd->len = F_INSN_SIZE(ipfw_insn_u32); | |
| 3058 | cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; | |
| 3059 | cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); | |
| 3060 | ac--; av++; | |
| 3061 | break; | |
| 3062 | ||
| 3063 | case TOK_TCPWIN: | |
| 3064 | NEED1("tcpwin requires length"); | |
| 3065 | fill_cmd(cmd, O_TCPWIN, 0, | |
| 3066 | htons(strtoul(*av, NULL, 0))); | |
| 3067 | ac--; av++; | |
| 3068 | break; | |
| 3069 | ||
| 3070 | case TOK_TCPFLAGS: | |
| 3071 | NEED1("missing argument for tcpflags"); | |
| 3072 | cmd->opcode = O_TCPFLAGS; | |
| 3073 | fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); | |
| 3074 | ac--; av++; | |
| 3075 | break; | |
| 3076 | ||
| 3077 | case TOK_KEEPSTATE: | |
| 3078 | if (open_par) | |
| 3079 | errx(EX_USAGE, "keep-state cannot be part " | |
| 3080 | "of an or block"); | |
| 3081 | if (have_state) | |
| 3082 | errx(EX_USAGE, "only one of keep-state " | |
| 3083 | "and limit is allowed"); | |
| 3084 | have_state = cmd; | |
| 3085 | fill_cmd(cmd, O_KEEP_STATE, 0, 0); | |
| 3086 | break; | |
| 3087 | ||
| 3088 | case TOK_LIMIT: | |
| 3089 | if (open_par) | |
| 3090 | errx(EX_USAGE, "limit cannot be part " | |
| 3091 | "of an or block"); | |
| 3092 | if (have_state) | |
| 3093 | errx(EX_USAGE, "only one of keep-state " | |
| 3094 | "and limit is allowed"); | |
| 3095 | NEED1("limit needs mask and # of connections"); | |
| 3096 | have_state = cmd; | |
| 3097 | { | |
| 3098 | ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; | |
| 3099 | ||
| 3100 | cmd->len = F_INSN_SIZE(ipfw_insn_limit); | |
| 3101 | cmd->opcode = O_LIMIT; | |
| 3102 | c->limit_mask = 0; | |
| 3103 | c->conn_limit = 0; | |
| 3104 | for (; ac >1 ;) { | |
| 3105 | int val; | |
| 3106 | ||
| 3107 | val = match_token(limit_masks, *av); | |
| 3108 | if (val <= 0) | |
| 3109 | break; | |
| 3110 | c->limit_mask |= val; | |
| 3111 | ac--; av++; | |
| 3112 | } | |
| 3113 | c->conn_limit = atoi(*av); | |
| 3114 | if (c->conn_limit == 0) | |
| 3115 | errx(EX_USAGE, "limit: limit must be >0"); | |
| 3116 | if (c->limit_mask == 0) | |
| 3117 | errx(EX_USAGE, "missing limit mask"); | |
| 3118 | ac--; av++; | |
| 3119 | } | |
| 3120 | break; | |
| 3121 | ||
| 3122 | case TOK_PROTO: | |
| 3123 | NEED1("missing protocol"); | |
| 3124 | if (add_proto(cmd, *av)) { | |
| 3125 | proto = cmd->arg1; | |
| 3126 | ac--; av++; | |
| 3127 | } else | |
| 7700730e | 3128 | errx(EX_DATAERR, "invalid protocol ``%s''", *av); |
| 984263bc MD |
3129 | break; |
| 3130 | ||
| 3131 | case TOK_SRCIP: | |
| 3132 | NEED1("missing source IP"); | |
| 3133 | if (add_srcip(cmd, *av)) { | |
| 3134 | ac--; av++; | |
| 3135 | } | |
| 3136 | break; | |
| 3137 | ||
| 3138 | case TOK_DSTIP: | |
| 3139 | NEED1("missing destination IP"); | |
| 3140 | if (add_dstip(cmd, *av)) { | |
| 3141 | ac--; av++; | |
| 3142 | } | |
| 3143 | break; | |
| 3144 | ||
| 3145 | case TOK_SRCPORT: | |
| 3146 | NEED1("missing source port"); | |
| 3147 | if (!strncmp(*av, "any", strlen(*av)) || | |
| 3148 | add_ports(cmd, *av, proto, O_IP_SRCPORT)) { | |
| 3149 | ac--; av++; | |
| 3150 | } else | |
| 3151 | errx(EX_DATAERR, "invalid source port %s", *av); | |
| 3152 | break; | |
| 3153 | ||
| 3154 | case TOK_DSTPORT: | |
| 3155 | NEED1("missing destination port"); | |
| 3156 | if (!strncmp(*av, "any", strlen(*av)) || | |
| 3157 | add_ports(cmd, *av, proto, O_IP_DSTPORT)) { | |
| 3158 | ac--; av++; | |
| 3159 | } else | |
| 3160 | errx(EX_DATAERR, "invalid destination port %s", | |
| 3161 | *av); | |
| 3162 | break; | |
| 3163 | ||
| 3164 | case TOK_MAC: | |
| 3165 | if (ac < 2) | |
| 3166 | errx(EX_USAGE, "MAC dst-mac src-mac"); | |
| 3167 | if (add_mac(cmd, ac, av)) { | |
| 3168 | ac -= 2; av += 2; | |
| 3169 | } | |
| 3170 | break; | |
| 3171 | ||
| 3172 | case TOK_MACTYPE: | |
| 3173 | NEED1("missing mac type"); | |
| 3174 | if (!add_mactype(cmd, ac, *av)) | |
| 7700730e | 3175 | errx(EX_DATAERR, "invalid mac type %s", *av); |
| 984263bc MD |
3176 | ac--; av++; |
| 3177 | break; | |
| 3178 | ||
| 3179 | default: | |
| 3180 | errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); | |
| 3181 | } | |
| 3182 | if (F_LEN(cmd) > 0) { /* prepare to advance */ | |
| 3183 | prev = cmd; | |
| 3184 | cmd = next_cmd(cmd); | |
| 3185 | } | |
| 3186 | } | |
| 3187 | ||
| 3188 | done: | |
| 3189 | /* | |
| 3190 | * Now copy stuff into the rule. | |
| 3191 | * If we have a keep-state option, the first instruction | |
| 3192 | * must be a PROBE_STATE (which is generated here). | |
| 3193 | * If we have a LOG option, it was stored as the first command, | |
| 3194 | * and now must be moved to the top of the action part. | |
| 3195 | */ | |
| 3196 | dst = (ipfw_insn *)rule->cmd; | |
| 3197 | ||
| 3198 | /* | |
| 3199 | * First thing to write into the command stream is the match probability. | |
| 3200 | */ | |
| 3201 | if (match_prob != 1) { /* 1 means always match */ | |
| 3202 | dst->opcode = O_PROB; | |
| 3203 | dst->len = 2; | |
| 3204 | *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); | |
| 3205 | dst += dst->len; | |
| 3206 | } | |
| 3207 | ||
| 3208 | /* | |
| 3209 | * generate O_PROBE_STATE if necessary | |
| 3210 | */ | |
| 3211 | if (have_state && have_state->opcode != O_CHECK_STATE) { | |
| 3212 | fill_cmd(dst, O_PROBE_STATE, 0, 0); | |
| 3213 | dst = next_cmd(dst); | |
| 3214 | } | |
| 3215 | /* | |
| 3216 | * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT | |
| 3217 | */ | |
| 3218 | for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { | |
| 3219 | i = F_LEN(src); | |
| 3220 | ||
| 3221 | switch (src->opcode) { | |
| 3222 | case O_LOG: | |
| 3223 | case O_KEEP_STATE: | |
| 3224 | case O_LIMIT: | |
| 3225 | break; | |
| 3226 | default: | |
| 3227 | bcopy(src, dst, i * sizeof(u_int32_t)); | |
| 3228 | dst += i; | |
| 3229 | } | |
| 3230 | } | |
| 3231 | ||
| 3232 | /* | |
| 3233 | * put back the have_state command as last opcode | |
| 3234 | */ | |
| 3235 | if (have_state && have_state->opcode != O_CHECK_STATE) { | |
| 3236 | i = F_LEN(have_state); | |
| 3237 | bcopy(have_state, dst, i * sizeof(u_int32_t)); | |
| 3238 | dst += i; | |
| 3239 | } | |
| 3240 | /* | |
| 3241 | * start action section | |
| 3242 | */ | |
| 3243 | rule->act_ofs = dst - rule->cmd; | |
| 3244 | ||
| 3245 | /* | |
| 3246 | * put back O_LOG if necessary | |
| 3247 | */ | |
| 3248 | src = (ipfw_insn *)cmdbuf; | |
| 3249 | if ( src->opcode == O_LOG ) { | |
| 3250 | i = F_LEN(src); | |
| 3251 | bcopy(src, dst, i * sizeof(u_int32_t)); | |
| 3252 | dst += i; | |
| 3253 | } | |
| 3254 | /* | |
| 3255 | * copy all other actions | |
| 3256 | */ | |
| 3257 | for (src = (ipfw_insn *)actbuf; src != action; src += i) { | |
| 3258 | i = F_LEN(src); | |
| 3259 | bcopy(src, dst, i * sizeof(u_int32_t)); | |
| 3260 | dst += i; | |
| 3261 | } | |
| 3262 | ||
| 3263 | rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd); | |
| 3264 | i = (void *)dst - (void *)rule; | |
| 3265 | if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, rule, &i) == -1) | |
| 3266 | err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); | |
| 3267 | if (!do_quiet) | |
| 3268 | show_ipfw(rule, 10, 10); | |
| 3269 | } | |
| 3270 | ||
| 3271 | static void | |
| 3272 | zero(int ac, char *av[]) | |
| 3273 | { | |
| 3274 | int rulenum; | |
| 3275 | int failed = EX_OK; | |
| 3276 | ||
| 3277 | av++; ac--; | |
| 3278 | ||
| 3279 | if (!ac) { | |
| 3280 | /* clear all entries */ | |
| 3281 | if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, NULL, 0) < 0) | |
| 3282 | err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO"); | |
| 3283 | if (!do_quiet) | |
| 3284 | printf("Accounting cleared.\n"); | |
| 3285 | ||
| 3286 | return; | |
| 3287 | } | |
| 3288 | ||
| 3289 | while (ac) { | |
| 3290 | /* Rule number */ | |
| 3291 | if (isdigit(**av)) { | |
| 3292 | rulenum = atoi(*av); | |
| 3293 | av++; | |
| 3294 | ac--; | |
| 3295 | if (setsockopt(s, IPPROTO_IP, | |
| 3296 | IP_FW_ZERO, &rulenum, sizeof rulenum)) { | |
| 3297 | warn("rule %u: setsockopt(IP_FW_ZERO)", | |
| 3298 | rulenum); | |
| 3299 | failed = EX_UNAVAILABLE; | |
| 3300 | } else if (!do_quiet) | |
| 3301 | printf("Entry %d cleared\n", rulenum); | |
| 3302 | } else { | |
| 3303 | errx(EX_USAGE, "invalid rule number ``%s''", *av); | |
| 3304 | } | |
| 3305 | } | |
| 3306 | if (failed != EX_OK) | |
| 3307 | exit(failed); | |
| 3308 | } | |
| 3309 | ||
| 3310 | static void | |
| 3311 | resetlog(int ac, char *av[]) | |
| 3312 | { | |
| 3313 | int rulenum; | |
| 3314 | int failed = EX_OK; | |
| 3315 | ||
| 3316 | av++; ac--; | |
| 3317 | ||
| 3318 | if (!ac) { | |
| 3319 | /* clear all entries */ | |
| 3320 | if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, NULL, 0) < 0) | |
| 3321 | err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)"); | |
| 3322 | if (!do_quiet) | |
| 3323 | printf("Logging counts reset.\n"); | |
| 3324 | ||
| 3325 | return; | |
| 3326 | } | |
| 3327 | ||
| 3328 | while (ac) { | |
| 3329 | /* Rule number */ | |
| 3330 | if (isdigit(**av)) { | |
| 3331 | rulenum = atoi(*av); | |
| 3332 | av++; | |
| 3333 | ac--; | |
| 3334 | if (setsockopt(s, IPPROTO_IP, | |
| 3335 | IP_FW_RESETLOG, &rulenum, sizeof rulenum)) { | |
| 3336 | warn("rule %u: setsockopt(IP_FW_RESETLOG)", | |
| 3337 | rulenum); | |
| 3338 | failed = EX_UNAVAILABLE; | |
| 3339 | } else if (!do_quiet) | |
| 3340 | printf("Entry %d logging count reset\n", | |
| 3341 | rulenum); | |
| 3342 | } else { | |
| 3343 | errx(EX_DATAERR, "invalid rule number ``%s''", *av); | |
| 3344 | } | |
| 3345 | } | |
| 3346 | if (failed != EX_OK) | |
| 3347 | exit(failed); | |
| 3348 | } | |
| 3349 | ||
| 3350 | static void | |
| 1ab50dff | 3351 | flush(void) |
| 984263bc MD |
3352 | { |
| 3353 | int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; | |
| 3354 | ||
| 3355 | if (!do_force && !do_quiet) { /* need to ask user */ | |
| 3356 | int c; | |
| 3357 | ||
| 3358 | printf("Are you sure? [yn] "); | |
| 3359 | fflush(stdout); | |
| 3360 | do { | |
| 3361 | c = toupper(getc(stdin)); | |
| 3362 | while (c != '\n' && getc(stdin) != '\n') | |
| 3363 | if (feof(stdin)) | |
| 3364 | return; /* and do not flush */ | |
| 3365 | } while (c != 'Y' && c != 'N'); | |
| 3366 | printf("\n"); | |
| 3367 | if (c == 'N') /* user said no */ | |
| 3368 | return; | |
| 3369 | } | |
| 3370 | if (setsockopt(s, IPPROTO_IP, cmd, NULL, 0) < 0) | |
| 3371 | err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", | |
| 3372 | do_pipe ? "DUMMYNET" : "FW"); | |
| 3373 | if (!do_quiet) | |
| 3374 | printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules"); | |
| 3375 | } | |
| 3376 | ||
| 3377 | static int | |
| 3378 | ipfw_main(int ac, char **av) | |
| 3379 | { | |
| 3380 | int ch; | |
| 3381 | ||
| 3382 | if (ac == 1) | |
| 3383 | show_usage(); | |
| 3384 | ||
| 3385 | /* Set the force flag for non-interactive processes */ | |
| 3386 | do_force = !isatty(STDIN_FILENO); | |
| 3387 | ||
| 3388 | optind = optreset = 1; | |
| 3389 | while ((ch = getopt(ac, av, "hs:acdefNqStv")) != -1) | |
| 3390 | switch (ch) { | |
| 3391 | case 'h': /* help */ | |
| 3392 | help(); | |
| 3393 | break; /* NOTREACHED */ | |
| 3394 | ||
| 3395 | case 's': /* sort */ | |
| 3396 | do_sort = atoi(optarg); | |
| 3397 | break; | |
| 3398 | case 'a': | |
| 3399 | do_acct = 1; | |
| 3400 | break; | |
| 3401 | case 'c': | |
| 3402 | do_compact = 1; | |
| 3403 | break; | |
| 3404 | case 'd': | |
| 3405 | do_dynamic = 1; | |
| 3406 | break; | |
| 3407 | case 'e': | |
| 3408 | do_expired = 1; | |
| 3409 | break; | |
| 3410 | case 'f': | |
| 3411 | do_force = 1; | |
| 3412 | break; | |
| 3413 | case 'N': | |
| 3414 | do_resolv = 1; | |
| 3415 | break; | |
| 3416 | case 'q': | |
| 3417 | do_quiet = 1; | |
| 3418 | break; | |
| 3419 | case 'S': | |
| 3420 | show_sets = 1; | |
| 3421 | break; | |
| 3422 | case 't': | |
| 3423 | do_time = 1; | |
| 3424 | break; | |
| 3425 | case 'v': /* verbose */ | |
| 3426 | verbose++; | |
| 3427 | break; | |
| 3428 | default: | |
| 3429 | show_usage(); | |
| 3430 | } | |
| 3431 | ||
| 3432 | ac -= optind; | |
| 3433 | av += optind; | |
| 3434 | NEED1("bad arguments, for usage summary ``ipfw''"); | |
| 3435 | ||
| 3436 | /* | |
| 3437 | * optional: pipe or queue | |
| 3438 | */ | |
| 3439 | if (!strncmp(*av, "pipe", strlen(*av))) { | |
| 3440 | do_pipe = 1; | |
| 3441 | ac--; | |
| 3442 | av++; | |
| 3443 | } else if (!strncmp(*av, "queue", strlen(*av))) { | |
| 3444 | do_pipe = 2; | |
| 3445 | ac--; | |
| 3446 | av++; | |
| 3447 | } | |
| 3448 | NEED1("missing command"); | |
| 3449 | ||
| 3450 | /* | |
| 3451 | * for pipes and queues we normally say 'pipe NN config' | |
| 3452 | * but the code is easier to parse as 'pipe config NN' | |
| 3453 | * so we swap the two arguments. | |
| 3454 | */ | |
| 3455 | if (do_pipe > 0 && ac > 1 && *av[0] >= '0' && *av[0] <= '9') { | |
| 3456 | char *p = av[0]; | |
| 3457 | av[0] = av[1]; | |
| 3458 | av[1] = p; | |
| 3459 | } | |
| 3460 | if (!strncmp(*av, "add", strlen(*av))) | |
| 3461 | add(ac, av); | |
| 3462 | else if (do_pipe && !strncmp(*av, "config", strlen(*av))) | |
| 3463 | config_pipe(ac, av); | |
| 3464 | else if (!strncmp(*av, "delete", strlen(*av))) | |
| 3465 | delete(ac, av); | |
| 3466 | else if (!strncmp(*av, "flush", strlen(*av))) | |
| 3467 | flush(); | |
| 3468 | else if (!strncmp(*av, "zero", strlen(*av))) | |
| 3469 | zero(ac, av); | |
| 3470 | else if (!strncmp(*av, "resetlog", strlen(*av))) | |
| 3471 | resetlog(ac, av); | |
| 3472 | else if (!strncmp(*av, "print", strlen(*av)) || | |
| 3473 | !strncmp(*av, "list", strlen(*av))) | |
| 3474 | list(ac, av); | |
| 3475 | else if (!strncmp(*av, "enable", strlen(*av))) | |
| 3476 | sysctl_handler(ac, av, 1); | |
| 3477 | else if (!strncmp(*av, "disable", strlen(*av))) | |
| 3478 | sysctl_handler(ac, av, 0); | |
| 3479 | else if (!strncmp(*av, "set", strlen(*av))) | |
| 3480 | sets_handler(ac, av); | |
| 3481 | else if (!strncmp(*av, "show", strlen(*av))) { | |
| 3482 | do_acct++; | |
| 3483 | list(ac, av); | |
| 3484 | } else | |
| 3485 | errx(EX_USAGE, "bad command `%s'", *av); | |
| 3486 | return 0; | |
| 3487 | } | |
| 3488 | ||
| 3489 | ||
| 3490 | static void | |
| 3491 | ipfw_readfile(int ac, char *av[]) | |
| 3492 | { | |
| 3493 | #define MAX_ARGS 32 | |
| 3494 | #define WHITESP " \t\f\v\n\r" | |
| 3495 | char buf[BUFSIZ]; | |
| 3496 | char *a, *p, *args[MAX_ARGS], *cmd = NULL; | |
| 3497 | char linename[10]; | |
| 3498 | int i=0, lineno=0, qflag=0, pflag=0, status; | |
| 3499 | FILE *f = NULL; | |
| 3500 | pid_t preproc = 0; | |
| 3501 | int c; | |
| 3502 | ||
| 3503 | while ((c = getopt(ac, av, "D:U:p:q")) != -1) | |
| 3504 | switch(c) { | |
| 3505 | case 'D': | |
| 3506 | if (!pflag) | |
| 3507 | errx(EX_USAGE, "-D requires -p"); | |
| 3508 | if (i > MAX_ARGS - 2) | |
| 3509 | errx(EX_USAGE, | |
| 3510 | "too many -D or -U options"); | |
| 3511 | args[i++] = "-D"; | |
| 3512 | args[i++] = optarg; | |
| 3513 | break; | |
| 3514 | ||
| 3515 | case 'U': | |
| 3516 | if (!pflag) | |
| 3517 | errx(EX_USAGE, "-U requires -p"); | |
| 3518 | if (i > MAX_ARGS - 2) | |
| 3519 | errx(EX_USAGE, | |
| 3520 | "too many -D or -U options"); | |
| 3521 | args[i++] = "-U"; | |
| 3522 | args[i++] = optarg; | |
| 3523 | break; | |
| 3524 | ||
| 3525 | case 'p': | |
| 3526 | pflag = 1; | |
| 3527 | cmd = optarg; | |
| 3528 | args[0] = cmd; | |
| 3529 | i = 1; | |
| 3530 | break; | |
| 3531 | ||
| 3532 | case 'q': | |
| 3533 | qflag = 1; | |
| 3534 | break; | |
| 3535 | ||
| 3536 | default: | |
| 3537 | errx(EX_USAGE, "bad arguments, for usage" | |
| 3538 | " summary ``ipfw''"); | |
| 3539 | } | |
| 3540 | ||
| 3541 | av += optind; | |
| 3542 | ac -= optind; | |
| 3543 | if (ac != 1) | |
| 3544 | errx(EX_USAGE, "extraneous filename arguments"); | |
| 3545 | ||
| 3546 | if ((f = fopen(av[0], "r")) == NULL) | |
| 3547 | err(EX_UNAVAILABLE, "fopen: %s", av[0]); | |
| 3548 | ||
| 3549 | if (pflag) { | |
| 3550 | /* pipe through preprocessor (cpp or m4) */ | |
| 3551 | int pipedes[2]; | |
| 3552 | ||
| 3553 | args[i] = 0; | |
| 3554 | ||
| 3555 | if (pipe(pipedes) == -1) | |
| 3556 | err(EX_OSERR, "cannot create pipe"); | |
| 3557 | ||
| 3558 | switch((preproc = fork())) { | |
| 3559 | case -1: | |
| 3560 | err(EX_OSERR, "cannot fork"); | |
| 3561 | ||
| 3562 | case 0: | |
| 3563 | /* child */ | |
| 3564 | if (dup2(fileno(f), 0) == -1 | |
| 3565 | || dup2(pipedes[1], 1) == -1) | |
| 3566 | err(EX_OSERR, "dup2()"); | |
| 3567 | fclose(f); | |
| 3568 | close(pipedes[1]); | |
| 3569 | close(pipedes[0]); | |
| 3570 | execvp(cmd, args); | |
| 3571 | err(EX_OSERR, "execvp(%s) failed", cmd); | |
| 3572 | ||
| 3573 | default: | |
| 3574 | /* parent */ | |
| 3575 | fclose(f); | |
| 3576 | close(pipedes[1]); | |
| 3577 | if ((f = fdopen(pipedes[0], "r")) == NULL) { | |
| 3578 | int savederrno = errno; | |
| 3579 | ||
| 7cee7052 | 3580 | kill(preproc, SIGTERM); |
| 984263bc MD |
3581 | errno = savederrno; |
| 3582 | err(EX_OSERR, "fdopen()"); | |
| 3583 | } | |
| 3584 | } | |
| 3585 | } | |
| 3586 | ||
| 3587 | while (fgets(buf, BUFSIZ, f)) { | |
| 3588 | lineno++; | |
| 3589 | sprintf(linename, "Line %d", lineno); | |
| 3590 | args[0] = linename; | |
| 3591 | ||
| 3592 | if (*buf == '#') | |
| 3593 | continue; | |
| 3594 | if ((p = strchr(buf, '#')) != NULL) | |
| 3595 | *p = '\0'; | |
| 3596 | i = 1; | |
| 3597 | if (qflag) | |
| 3598 | args[i++] = "-q"; | |
| 3599 | for (a = strtok(buf, WHITESP); | |
| 3600 | a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++) | |
| 3601 | args[i] = a; | |
| 3602 | if (i == (qflag? 2: 1)) | |
| 3603 | continue; | |
| 3604 | if (i == MAX_ARGS) | |
| 3605 | errx(EX_USAGE, "%s: too many arguments", | |
| 3606 | linename); | |
| 3607 | args[i] = NULL; | |
| 3608 | ||
| 3609 | ipfw_main(i, args); | |
| 3610 | } | |
| 3611 | fclose(f); | |
| 3612 | if (pflag) { | |
| 3613 | if (waitpid(preproc, &status, 0) == -1) | |
| 3614 | errx(EX_OSERR, "waitpid()"); | |
| 3615 | if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) | |
| 3616 | errx(EX_UNAVAILABLE, | |
| 3617 | "preprocessor exited with status %d", | |
| 3618 | WEXITSTATUS(status)); | |
| 3619 | else if (WIFSIGNALED(status)) | |
| 3620 | errx(EX_UNAVAILABLE, | |
| 3621 | "preprocessor exited with signal %d", | |
| 3622 | WTERMSIG(status)); | |
| 3623 | } | |
| 3624 | } | |
| 3625 | ||
| 3626 | int | |
| 3627 | main(int ac, char *av[]) | |
| 3628 | { | |
| 3629 | s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); | |
| 3630 | if (s < 0) | |
| 3631 | err(EX_UNAVAILABLE, "socket"); | |
| 3632 | ||
| 3633 | /* | |
| 3634 | * If the last argument is an absolute pathname, interpret it | |
| 3635 | * as a file to be preprocessed. | |
| 3636 | */ | |
| 3637 | ||
| 3638 | if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) | |
| 3639 | ipfw_readfile(ac, av); | |
| 3640 | else | |
| 3641 | ipfw_main(ac, av); | |
| 3642 | return EX_OK; | |
| 3643 | } |