1d17aef79SPedro F. Giffuni /*- 2cc4d3c30SLuigi Rizzo * Copyright (c) 2002-2003,2010 Luigi Rizzo 33c0c8717SLuigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 43c0c8717SLuigi Rizzo * Copyright (c) 1994 Ugen J.S.Antsilevich 53c0c8717SLuigi Rizzo * 63c0c8717SLuigi Rizzo * Idea and grammar partially left from: 73c0c8717SLuigi Rizzo * Copyright (c) 1993 Daniel Boulet 83c0c8717SLuigi Rizzo * 93c0c8717SLuigi Rizzo * Redistribution and use in source forms, with and without modification, 103c0c8717SLuigi Rizzo * are permitted provided that this entire comment appears intact. 113c0c8717SLuigi Rizzo * 123c0c8717SLuigi Rizzo * Redistribution in binary form may occur without any restrictions. 133c0c8717SLuigi Rizzo * Obviously, it would be nice if you gave credit where credit is due 143c0c8717SLuigi Rizzo * but requiring it would be too onerous. 153c0c8717SLuigi Rizzo * 163c0c8717SLuigi Rizzo * This software is provided ``AS IS'' without any warranties of any kind. 173c0c8717SLuigi Rizzo * 183c0c8717SLuigi Rizzo * Command line interface for IP firewall facility 193c0c8717SLuigi Rizzo * 203c0c8717SLuigi Rizzo * $FreeBSD$ 213c0c8717SLuigi Rizzo */ 223c0c8717SLuigi Rizzo 233c0c8717SLuigi Rizzo #include <sys/wait.h> 243c0c8717SLuigi Rizzo #include <ctype.h> 253c0c8717SLuigi Rizzo #include <err.h> 263c0c8717SLuigi Rizzo #include <errno.h> 273c0c8717SLuigi Rizzo #include <signal.h> 283c0c8717SLuigi Rizzo #include <stdio.h> 293c0c8717SLuigi Rizzo #include <stdlib.h> 303c0c8717SLuigi Rizzo #include <string.h> 313c0c8717SLuigi Rizzo #include <sysexits.h> 323c0c8717SLuigi Rizzo #include <unistd.h> 333c0c8717SLuigi Rizzo 343c0c8717SLuigi Rizzo #include "ipfw2.h" 353c0c8717SLuigi Rizzo 363c0c8717SLuigi Rizzo static void 373c0c8717SLuigi Rizzo help(void) 383c0c8717SLuigi Rizzo { 39*0b95680eSKristof Provost if (is_ipfw()) { 403c0c8717SLuigi Rizzo fprintf(stderr, 413c0c8717SLuigi Rizzo "ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" 423c0c8717SLuigi Rizzo "\tipfw [-abcdefhnNqStTv] <command>\n\n" 433c0c8717SLuigi Rizzo "where <command> is one of the following:\n\n" 443c0c8717SLuigi Rizzo "add [num] [set N] [prob x] RULE-BODY\n" 453c0c8717SLuigi Rizzo "{pipe|queue} N config PIPE-BODY\n" 463c0c8717SLuigi Rizzo "[pipe|queue] {zero|delete|show} [N{,N}]\n" 4775b89337SAlexander V. Chernikov "nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" 4875b89337SAlexander V. Chernikov " reset|reverse|proxy_only|redirect_addr linkspec|\n" 49a08cdb6cSNeel Chauhan " redirect_port linkspec|redirect_proto linkspec|\n" 50a08cdb6cSNeel Chauhan " port_range lower-upper}\n" 513c0c8717SLuigi Rizzo "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" 523c0c8717SLuigi Rizzo "set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" 533c0c8717SLuigi Rizzo "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" 543c0c8717SLuigi Rizzo "table all {flush | list}\n" 553c0c8717SLuigi Rizzo "\n" 563c0c8717SLuigi Rizzo "RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" 573c0c8717SLuigi Rizzo "ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" 583c0c8717SLuigi Rizzo " skipto N | {divert|tee} PORT | forward ADDR |\n" 59eb2e4119SPaolo Pisati " pipe N | queue N | nat N | setfib FIB | reass\n" 603c0c8717SLuigi Rizzo "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" 613c0c8717SLuigi Rizzo "ADDR: [ MAC dst src ether_type ] \n" 623c0c8717SLuigi Rizzo " [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" 633c0c8717SLuigi Rizzo " [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" 643c0c8717SLuigi Rizzo "IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" 653c0c8717SLuigi Rizzo "IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" 663c0c8717SLuigi Rizzo "IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" 673c0c8717SLuigi Rizzo "IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" 683c0c8717SLuigi Rizzo "OPTION_LIST: OPTION [OPTION_LIST]\n" 693c0c8717SLuigi Rizzo "OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" 703c0c8717SLuigi Rizzo " {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" 713c0c8717SLuigi Rizzo " {dst-port|src-port} LIST |\n" 723c0c8717SLuigi Rizzo " estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" 733c0c8717SLuigi Rizzo " iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" 743c0c8717SLuigi Rizzo " ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" 753c0c8717SLuigi Rizzo " icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n" 763c0c8717SLuigi Rizzo " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" 773c0c8717SLuigi Rizzo " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" 783c0c8717SLuigi Rizzo " tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" 793c0c8717SLuigi Rizzo ); 80*0b95680eSKristof Provost } else { 81*0b95680eSKristof Provost fprintf(stderr, 82*0b95680eSKristof Provost "dnctl syntax summary (but please do read the dnctl(8) manpage):\n\n" 83*0b95680eSKristof Provost "\tdnctl [-hnsv] <command>\n\n" 84*0b95680eSKristof Provost "where <command> is one of the following:\n\n" 85*0b95680eSKristof Provost "[pipe|queue|sched] N config PIPE-BODY\n" 86*0b95680eSKristof Provost "[pipe|queue|sched] {delete|list|show} [N{,N}]\n" 87*0b95680eSKristof Provost "\n" 88*0b95680eSKristof Provost ); 89*0b95680eSKristof Provost } 903c0c8717SLuigi Rizzo 913c0c8717SLuigi Rizzo exit(0); 923c0c8717SLuigi Rizzo } 933c0c8717SLuigi Rizzo 943c0c8717SLuigi Rizzo /* 953c0c8717SLuigi Rizzo * Called with the arguments, including program name because getopt 963c0c8717SLuigi Rizzo * wants it to be present. 973c0c8717SLuigi Rizzo * Returns 0 if successful, 1 if empty command, errx() in case of errors. 98cc4d3c30SLuigi Rizzo * First thing we do is process parameters creating an argv[] array 99cc4d3c30SLuigi Rizzo * which includes the program name and a NULL entry at the end. 100cc4d3c30SLuigi Rizzo * If we are called with a single string, we split it on whitespace. 101cc4d3c30SLuigi Rizzo * Also, arguments with a trailing ',' are joined to the next one. 1023b6dc18eSEitan Adler * The pointers (av[]) and data are in a single chunk of memory. 103cc4d3c30SLuigi Rizzo * av[0] points to the original program name, all other entries 104cc4d3c30SLuigi Rizzo * point into the allocated chunk. 1053c0c8717SLuigi Rizzo */ 1063c0c8717SLuigi Rizzo static int 1073c0c8717SLuigi Rizzo ipfw_main(int oldac, char **oldav) 1083c0c8717SLuigi Rizzo { 109cc4d3c30SLuigi Rizzo int ch, ac; 1103c0c8717SLuigi Rizzo const char *errstr; 1113c0c8717SLuigi Rizzo char **av, **save_av; 1123c0c8717SLuigi Rizzo int do_acct = 0; /* Show packet/byte count */ 1131fd3fc69SLuigi Rizzo int try_next = 0; /* set if pipe cmd not found */ 114cc4d3c30SLuigi Rizzo int av_size; /* compute the av size */ 115cc4d3c30SLuigi Rizzo char *av_p; /* used to build the av list */ 1163c0c8717SLuigi Rizzo 1173c0c8717SLuigi Rizzo #define WHITESP " \t\f\v\n\r" 1183c0c8717SLuigi Rizzo if (oldac < 2) 1193c0c8717SLuigi Rizzo return 1; /* need at least one argument */ 1203c0c8717SLuigi Rizzo 1213c0c8717SLuigi Rizzo if (oldac == 2) { 1223c0c8717SLuigi Rizzo /* 123cc4d3c30SLuigi Rizzo * If we are called with one argument, try to split it into 124cc4d3c30SLuigi Rizzo * words for subsequent parsing. Spaces after a ',' are 125cc4d3c30SLuigi Rizzo * removed by copying the string in-place. 1263c0c8717SLuigi Rizzo */ 1273c0c8717SLuigi Rizzo char *arg = oldav[1]; /* The string is the first arg. */ 1283c0c8717SLuigi Rizzo int l = strlen(arg); 1293c0c8717SLuigi Rizzo int copy = 0; /* 1 if we need to copy, 0 otherwise */ 1303c0c8717SLuigi Rizzo int i, j; 1313c0c8717SLuigi Rizzo 1323c0c8717SLuigi Rizzo for (i = j = 0; i < l; i++) { 1333c0c8717SLuigi Rizzo if (arg[i] == '#') /* comment marker */ 1343c0c8717SLuigi Rizzo break; 1353c0c8717SLuigi Rizzo if (copy) { 1363c0c8717SLuigi Rizzo arg[j++] = arg[i]; 137b3608ae1SEd Schouten copy = !strchr("," WHITESP, arg[i]); 1383c0c8717SLuigi Rizzo } else { 139b3608ae1SEd Schouten copy = !strchr(WHITESP, arg[i]); 1403c0c8717SLuigi Rizzo if (copy) 1413c0c8717SLuigi Rizzo arg[j++] = arg[i]; 1423c0c8717SLuigi Rizzo } 1433c0c8717SLuigi Rizzo } 1443c0c8717SLuigi Rizzo if (!copy && j > 0) /* last char was a 'blank', remove it */ 1453c0c8717SLuigi Rizzo j--; 1463c0c8717SLuigi Rizzo l = j; /* the new argument length */ 1473c0c8717SLuigi Rizzo arg[j++] = '\0'; 1483c0c8717SLuigi Rizzo if (l == 0) /* empty string! */ 1493c0c8717SLuigi Rizzo return 1; 1503c0c8717SLuigi Rizzo 1513c0c8717SLuigi Rizzo /* 1523c0c8717SLuigi Rizzo * First, count number of arguments. Because of the previous 1533c0c8717SLuigi Rizzo * processing, this is just the number of blanks plus 1. 1543c0c8717SLuigi Rizzo */ 1553c0c8717SLuigi Rizzo for (i = 0, ac = 1; i < l; i++) 156b3608ae1SEd Schouten if (strchr(WHITESP, arg[i]) != NULL) 1573c0c8717SLuigi Rizzo ac++; 1583c0c8717SLuigi Rizzo 1593c0c8717SLuigi Rizzo /* 160cc4d3c30SLuigi Rizzo * Allocate the argument list structure as a single block 161cc4d3c30SLuigi Rizzo * of memory, containing pointers and the argument 162cc4d3c30SLuigi Rizzo * strings. We include one entry for the program name 163cc4d3c30SLuigi Rizzo * because getopt expects it, and a NULL at the end 164cc4d3c30SLuigi Rizzo * to simplify further parsing. 1653c0c8717SLuigi Rizzo */ 166cc4d3c30SLuigi Rizzo ac++; /* add 1 for the program name */ 167cc4d3c30SLuigi Rizzo av_size = (ac+1) * sizeof(char *) + l + 1; 168cc4d3c30SLuigi Rizzo av = safe_calloc(av_size, 1); 1693c0c8717SLuigi Rizzo 1703c0c8717SLuigi Rizzo /* 171cc4d3c30SLuigi Rizzo * Init the argument pointer to the end of the array 172cc4d3c30SLuigi Rizzo * and copy arguments from arg[] to av[]. For each one, 1733c0c8717SLuigi Rizzo * j is the initial character, i is the one past the end. 1743c0c8717SLuigi Rizzo */ 175cc4d3c30SLuigi Rizzo av_p = (char *)&av[ac+1]; 176cc4d3c30SLuigi Rizzo for (ac = 1, i = j = 0; i < l; i++) { 177b3608ae1SEd Schouten if (strchr(WHITESP, arg[i]) != NULL || i == l-1) { 1783c0c8717SLuigi Rizzo if (i == l-1) 1793c0c8717SLuigi Rizzo i++; 180cc4d3c30SLuigi Rizzo bcopy(arg+j, av_p, i-j); 181cc4d3c30SLuigi Rizzo av[ac] = av_p; 1824b85a12fSUlrich Spörlein av_p += i-j; /* the length of the string */ 183cc4d3c30SLuigi Rizzo *av_p++ = '\0'; 1843c0c8717SLuigi Rizzo ac++; 1853c0c8717SLuigi Rizzo j = i + 1; 1863c0c8717SLuigi Rizzo } 187cc4d3c30SLuigi Rizzo } 1883c0c8717SLuigi Rizzo } else { 1893c0c8717SLuigi Rizzo /* 1903c0c8717SLuigi Rizzo * If an argument ends with ',' join with the next one. 1913c0c8717SLuigi Rizzo */ 192cc4d3c30SLuigi Rizzo int first, i, l=0; 1933c0c8717SLuigi Rizzo 194cc4d3c30SLuigi Rizzo /* 195cc4d3c30SLuigi Rizzo * Allocate the argument list structure as a single block 196cc4d3c30SLuigi Rizzo * of memory, containing both pointers and the argument 197cc4d3c30SLuigi Rizzo * strings. We include some space for the program name 198cc4d3c30SLuigi Rizzo * because getopt expects it. 199cc4d3c30SLuigi Rizzo * We add an extra pointer to the end of the array, 200cc4d3c30SLuigi Rizzo * to make simpler further parsing. 201cc4d3c30SLuigi Rizzo */ 202cc4d3c30SLuigi Rizzo for (i=0; i<oldac; i++) 203cc4d3c30SLuigi Rizzo l += strlen(oldav[i]); 204cc4d3c30SLuigi Rizzo 205cc4d3c30SLuigi Rizzo av_size = (oldac+1) * sizeof(char *) + l + oldac; 206cc4d3c30SLuigi Rizzo av = safe_calloc(av_size, 1); 207cc4d3c30SLuigi Rizzo 208cc4d3c30SLuigi Rizzo /* 209cc4d3c30SLuigi Rizzo * Init the argument pointer to the end of the array 210cc4d3c30SLuigi Rizzo * and copy arguments from arg[] to av[] 211cc4d3c30SLuigi Rizzo */ 212cc4d3c30SLuigi Rizzo av_p = (char *)&av[oldac+1]; 2133c0c8717SLuigi Rizzo for (first = i = ac = 1, l = 0; i < oldac; i++) { 2143c0c8717SLuigi Rizzo char *arg = oldav[i]; 2153c0c8717SLuigi Rizzo int k = strlen(arg); 2163c0c8717SLuigi Rizzo 2173c0c8717SLuigi Rizzo l += k; 2183c0c8717SLuigi Rizzo if (arg[k-1] != ',' || i == oldac-1) { 2193c0c8717SLuigi Rizzo /* Time to copy. */ 220cc4d3c30SLuigi Rizzo av[ac] = av_p; 2213c0c8717SLuigi Rizzo for (l=0; first <= i; first++) { 222cc4d3c30SLuigi Rizzo strcat(av_p, oldav[first]); 223cc4d3c30SLuigi Rizzo av_p += strlen(oldav[first]); 2243c0c8717SLuigi Rizzo } 225cc4d3c30SLuigi Rizzo *av_p++ = '\0'; 2263c0c8717SLuigi Rizzo ac++; 2273c0c8717SLuigi Rizzo l = 0; 2283c0c8717SLuigi Rizzo first = i+1; 2293c0c8717SLuigi Rizzo } 2303c0c8717SLuigi Rizzo } 2313c0c8717SLuigi Rizzo } 2323c0c8717SLuigi Rizzo 233cc4d3c30SLuigi Rizzo /* 234cc4d3c30SLuigi Rizzo * set the progname pointer to the original string 235cc4d3c30SLuigi Rizzo * and terminate the array with null 236cc4d3c30SLuigi Rizzo */ 237cc4d3c30SLuigi Rizzo av[0] = oldav[0]; 238cc4d3c30SLuigi Rizzo av[ac] = NULL; 239cc4d3c30SLuigi Rizzo 2403c0c8717SLuigi Rizzo /* Set the force flag for non-interactive processes */ 24156707beeSMark Johnston if (!g_co.do_force) 24256707beeSMark Johnston g_co.do_force = !isatty(STDIN_FILENO); 2433c0c8717SLuigi Rizzo 244cc4d3c30SLuigi Rizzo #ifdef EMULATE_SYSCTL /* sysctl emulation */ 245*0b95680eSKristof Provost if (is_ipfw() && ac >= 2 && 246*0b95680eSKristof Provost !strcmp(av[1], "sysctl")) { 247cc4d3c30SLuigi Rizzo char *s; 248cc4d3c30SLuigi Rizzo int i; 249cc4d3c30SLuigi Rizzo 250cc4d3c30SLuigi Rizzo if (ac != 3) { 251cc4d3c30SLuigi Rizzo printf( "sysctl emulation usage:\n" 252cc4d3c30SLuigi Rizzo " ipfw sysctl name[=value]\n" 253cc4d3c30SLuigi Rizzo " ipfw sysctl -a\n"); 254cc4d3c30SLuigi Rizzo return 0; 255cc4d3c30SLuigi Rizzo } 256b3608ae1SEd Schouten s = strchr(av[2], '='); 257cc4d3c30SLuigi Rizzo if (s == NULL) { 258cc4d3c30SLuigi Rizzo s = !strcmp(av[2], "-a") ? NULL : av[2]; 259cc4d3c30SLuigi Rizzo sysctlbyname(s, NULL, NULL, NULL, 0); 260cc4d3c30SLuigi Rizzo } else { /* ipfw sysctl x.y.z=value */ 261cc4d3c30SLuigi Rizzo /* assume an INT value, will extend later */ 262cc4d3c30SLuigi Rizzo if (s[1] == '\0') { 263cc4d3c30SLuigi Rizzo printf("ipfw sysctl: missing value\n\n"); 264cc4d3c30SLuigi Rizzo return 0; 265cc4d3c30SLuigi Rizzo } 266cc4d3c30SLuigi Rizzo *s = '\0'; 267cc4d3c30SLuigi Rizzo i = strtol(s+1, NULL, 0); 268cc4d3c30SLuigi Rizzo sysctlbyname(av[2], NULL, NULL, &i, sizeof(int)); 269cc4d3c30SLuigi Rizzo } 270cc4d3c30SLuigi Rizzo return 0; 271cc4d3c30SLuigi Rizzo } 272cc4d3c30SLuigi Rizzo #endif 273cc4d3c30SLuigi Rizzo 2743c0c8717SLuigi Rizzo /* Save arguments for final freeing of memory. */ 2753c0c8717SLuigi Rizzo save_av = av; 2763c0c8717SLuigi Rizzo 2773c0c8717SLuigi Rizzo optind = optreset = 1; /* restart getopt() */ 278*0b95680eSKristof Provost if (is_ipfw()) { 279d66f9c86SAndrey V. Elsukov while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtv")) != -1) 2803c0c8717SLuigi Rizzo switch (ch) { 2813c0c8717SLuigi Rizzo case 'a': 2823c0c8717SLuigi Rizzo do_acct = 1; 2833c0c8717SLuigi Rizzo break; 2843c0c8717SLuigi Rizzo 2853c0c8717SLuigi Rizzo case 'b': 28656707beeSMark Johnston g_co.comment_only = 1; 28756707beeSMark Johnston g_co.do_compact = 1; 2883c0c8717SLuigi Rizzo break; 2893c0c8717SLuigi Rizzo 2903c0c8717SLuigi Rizzo case 'c': 29156707beeSMark Johnston g_co.do_compact = 1; 2923c0c8717SLuigi Rizzo break; 2933c0c8717SLuigi Rizzo 2943c0c8717SLuigi Rizzo case 'd': 29556707beeSMark Johnston g_co.do_dynamic = 1; 2963c0c8717SLuigi Rizzo break; 2973c0c8717SLuigi Rizzo 298d66f9c86SAndrey V. Elsukov case 'D': 29956707beeSMark Johnston g_co.do_dynamic = 2; 300d66f9c86SAndrey V. Elsukov break; 301d66f9c86SAndrey V. Elsukov 3023c0c8717SLuigi Rizzo case 'e': 303d66f9c86SAndrey V. Elsukov /* nop for compatibility */ 3043c0c8717SLuigi Rizzo break; 3053c0c8717SLuigi Rizzo 3063c0c8717SLuigi Rizzo case 'f': 30756707beeSMark Johnston g_co.do_force = 1; 3083c0c8717SLuigi Rizzo break; 3093c0c8717SLuigi Rizzo 3103c0c8717SLuigi Rizzo case 'h': /* help */ 311cc4d3c30SLuigi Rizzo free(save_av); 3123c0c8717SLuigi Rizzo help(); 3133c0c8717SLuigi Rizzo break; /* NOTREACHED */ 3143c0c8717SLuigi Rizzo 3153c0c8717SLuigi Rizzo case 'i': 31656707beeSMark Johnston g_co.do_value_as_ip = 1; 3173c0c8717SLuigi Rizzo break; 3183c0c8717SLuigi Rizzo 3193c0c8717SLuigi Rizzo case 'n': 32056707beeSMark Johnston g_co.test_only = 1; 3213c0c8717SLuigi Rizzo break; 3223c0c8717SLuigi Rizzo 3233c0c8717SLuigi Rizzo case 'N': 32456707beeSMark Johnston g_co.do_resolv = 1; 3253c0c8717SLuigi Rizzo break; 3263c0c8717SLuigi Rizzo 3270dba401dSAndrey V. Elsukov case 'p': 3280dba401dSAndrey V. Elsukov errx(EX_USAGE, "An absolute pathname must be used " 3290dba401dSAndrey V. Elsukov "with -p option."); 3309dad7387SEitan Adler /* NOTREACHED */ 3310dba401dSAndrey V. Elsukov 3323c0c8717SLuigi Rizzo case 'q': 33356707beeSMark Johnston g_co.do_quiet = 1; 3343c0c8717SLuigi Rizzo break; 3353c0c8717SLuigi Rizzo 3363c0c8717SLuigi Rizzo case 's': /* sort */ 33756707beeSMark Johnston g_co.do_sort = atoi(optarg); 3383c0c8717SLuigi Rizzo break; 3393c0c8717SLuigi Rizzo 3403c0c8717SLuigi Rizzo case 'S': 34156707beeSMark Johnston g_co.show_sets = 1; 3423c0c8717SLuigi Rizzo break; 3433c0c8717SLuigi Rizzo 3443c0c8717SLuigi Rizzo case 't': 34556707beeSMark Johnston g_co.do_time = TIMESTAMP_STRING; 3463c0c8717SLuigi Rizzo break; 3473c0c8717SLuigi Rizzo 3483c0c8717SLuigi Rizzo case 'T': 34956707beeSMark Johnston g_co.do_time = TIMESTAMP_NUMERIC; 3503c0c8717SLuigi Rizzo break; 3513c0c8717SLuigi Rizzo 3523c0c8717SLuigi Rizzo case 'v': /* verbose */ 35356707beeSMark Johnston g_co.verbose = 1; 3543c0c8717SLuigi Rizzo break; 3553c0c8717SLuigi Rizzo 3563c0c8717SLuigi Rizzo default: 357cc4d3c30SLuigi Rizzo free(save_av); 3583c0c8717SLuigi Rizzo return 1; 3593c0c8717SLuigi Rizzo } 360*0b95680eSKristof Provost } else { 361*0b95680eSKristof Provost while ((ch = getopt(ac, av, "hns:v")) != -1) 362*0b95680eSKristof Provost switch (ch) { 363*0b95680eSKristof Provost 364*0b95680eSKristof Provost case 'h': /* help */ 365*0b95680eSKristof Provost free(save_av); 366*0b95680eSKristof Provost help(); 367*0b95680eSKristof Provost break; /* NOTREACHED */ 368*0b95680eSKristof Provost 369*0b95680eSKristof Provost case 'n': 370*0b95680eSKristof Provost g_co.test_only = 1; 371*0b95680eSKristof Provost break; 372*0b95680eSKristof Provost 373*0b95680eSKristof Provost case 's': /* sort */ 374*0b95680eSKristof Provost g_co.do_sort = atoi(optarg); 375*0b95680eSKristof Provost break; 376*0b95680eSKristof Provost 377*0b95680eSKristof Provost case 'v': /* verbose */ 378*0b95680eSKristof Provost g_co.verbose = 1; 379*0b95680eSKristof Provost break; 380*0b95680eSKristof Provost 381*0b95680eSKristof Provost default: 382*0b95680eSKristof Provost free(save_av); 383*0b95680eSKristof Provost return 1; 384*0b95680eSKristof Provost } 385*0b95680eSKristof Provost 386*0b95680eSKristof Provost } 3873c0c8717SLuigi Rizzo 3883c0c8717SLuigi Rizzo ac -= optind; 3893c0c8717SLuigi Rizzo av += optind; 3903c0c8717SLuigi Rizzo NEED1("bad arguments, for usage summary ``ipfw''"); 3913c0c8717SLuigi Rizzo 3923c0c8717SLuigi Rizzo /* 3933c0c8717SLuigi Rizzo * An undocumented behaviour of ipfw1 was to allow rule numbers first, 3943c0c8717SLuigi Rizzo * e.g. "100 add allow ..." instead of "add 100 allow ...". 3953c0c8717SLuigi Rizzo * In case, swap first and second argument to get the normal form. 3963c0c8717SLuigi Rizzo */ 3973c0c8717SLuigi Rizzo if (ac > 1 && isdigit(*av[0])) { 3983c0c8717SLuigi Rizzo char *p = av[0]; 3993c0c8717SLuigi Rizzo 4003c0c8717SLuigi Rizzo av[0] = av[1]; 4013c0c8717SLuigi Rizzo av[1] = p; 4023c0c8717SLuigi Rizzo } 4033c0c8717SLuigi Rizzo 4043c0c8717SLuigi Rizzo /* 4053c0c8717SLuigi Rizzo * Optional: pipe, queue or nat. 4063c0c8717SLuigi Rizzo */ 40756707beeSMark Johnston g_co.do_nat = 0; 40856707beeSMark Johnston g_co.do_pipe = 0; 40956707beeSMark Johnston g_co.use_set = 0; 410*0b95680eSKristof Provost if (is_ipfw() && !strncmp(*av, "nat", strlen(*av))) 41156707beeSMark Johnston g_co.do_nat = 1; 4123c0c8717SLuigi Rizzo else if (!strncmp(*av, "pipe", strlen(*av))) 41356707beeSMark Johnston g_co.do_pipe = 1; 4143c0c8717SLuigi Rizzo else if (_substrcmp(*av, "queue") == 0) 41556707beeSMark Johnston g_co.do_pipe = 2; 416cc4d3c30SLuigi Rizzo else if (_substrcmp(*av, "flowset") == 0) 41756707beeSMark Johnston g_co.do_pipe = 2; 418cc4d3c30SLuigi Rizzo else if (_substrcmp(*av, "sched") == 0) 41956707beeSMark Johnston g_co.do_pipe = 3; 420*0b95680eSKristof Provost else if (is_ipfw() && !strncmp(*av, "set", strlen(*av))) { 4213c0c8717SLuigi Rizzo if (ac > 1 && isdigit(av[1][0])) { 42256707beeSMark Johnston g_co.use_set = strtonum(av[1], 0, resvd_set_number, 4233c0c8717SLuigi Rizzo &errstr); 4243c0c8717SLuigi Rizzo if (errstr) 4253c0c8717SLuigi Rizzo errx(EX_DATAERR, 4263c0c8717SLuigi Rizzo "invalid set number %s\n", av[1]); 42756707beeSMark Johnston ac -= 2; av += 2; g_co.use_set++; 4283c0c8717SLuigi Rizzo } 4293c0c8717SLuigi Rizzo } 4303c0c8717SLuigi Rizzo 43156707beeSMark Johnston if (g_co.do_pipe || g_co.do_nat) { 4323c0c8717SLuigi Rizzo ac--; 4333c0c8717SLuigi Rizzo av++; 4343c0c8717SLuigi Rizzo } 4353c0c8717SLuigi Rizzo NEED1("missing command"); 4363c0c8717SLuigi Rizzo 4373c0c8717SLuigi Rizzo /* 4383c0c8717SLuigi Rizzo * For pipes, queues and nats we normally say 'nat|pipe NN config' 4393c0c8717SLuigi Rizzo * but the code is easier to parse as 'nat|pipe config NN' 4403c0c8717SLuigi Rizzo * so we swap the two arguments. 4413c0c8717SLuigi Rizzo */ 44256707beeSMark Johnston if ((g_co.do_pipe || g_co.do_nat) && ac > 1 && isdigit(*av[0])) { 4433c0c8717SLuigi Rizzo char *p = av[0]; 4443c0c8717SLuigi Rizzo 4453c0c8717SLuigi Rizzo av[0] = av[1]; 4463c0c8717SLuigi Rizzo av[1] = p; 4473c0c8717SLuigi Rizzo } 4483c0c8717SLuigi Rizzo 449*0b95680eSKristof Provost if (! is_ipfw() && g_co.do_pipe == 0) { 450*0b95680eSKristof Provost help(); 451*0b95680eSKristof Provost } 452*0b95680eSKristof Provost 45356707beeSMark Johnston if (g_co.use_set == 0) { 454*0b95680eSKristof Provost if (is_ipfw() && _substrcmp(*av, "add") == 0) 455cc4d3c30SLuigi Rizzo ipfw_add(av); 45656707beeSMark Johnston else if (g_co.do_nat && _substrcmp(*av, "show") == 0) 4573c0c8717SLuigi Rizzo ipfw_show_nat(ac, av); 45856707beeSMark Johnston else if (g_co.do_pipe && _substrcmp(*av, "config") == 0) 4593c0c8717SLuigi Rizzo ipfw_config_pipe(ac, av); 46056707beeSMark Johnston else if (g_co.do_nat && _substrcmp(*av, "config") == 0) 4613c0c8717SLuigi Rizzo ipfw_config_nat(ac, av); 462*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "set") == 0) 463cc4d3c30SLuigi Rizzo ipfw_sets_handler(av); 464*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "table") == 0) 4653c0c8717SLuigi Rizzo ipfw_table_handler(ac, av); 466*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "enable") == 0) 467cc4d3c30SLuigi Rizzo ipfw_sysctl_handler(av, 1); 468*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "disable") == 0) 469cc4d3c30SLuigi Rizzo ipfw_sysctl_handler(av, 0); 4703c0c8717SLuigi Rizzo else 4713c0c8717SLuigi Rizzo try_next = 1; 4723c0c8717SLuigi Rizzo } 4733c0c8717SLuigi Rizzo 47456707beeSMark Johnston if (g_co.use_set || try_next) { 4753c0c8717SLuigi Rizzo if (_substrcmp(*av, "delete") == 0) 476cc4d3c30SLuigi Rizzo ipfw_delete(av); 477*0b95680eSKristof Provost else if (is_ipfw() && !strncmp(*av, "nat64clat", strlen(*av))) 4785c04f73eSAndrey V. Elsukov ipfw_nat64clat_handler(ac, av); 479*0b95680eSKristof Provost else if (is_ipfw() && !strncmp(*av, "nat64stl", strlen(*av))) 480d8caf56eSAndrey V. Elsukov ipfw_nat64stl_handler(ac, av); 481*0b95680eSKristof Provost else if (is_ipfw() && !strncmp(*av, "nat64lsn", strlen(*av))) 482d8caf56eSAndrey V. Elsukov ipfw_nat64lsn_handler(ac, av); 483*0b95680eSKristof Provost else if (is_ipfw() && !strncmp(*av, "nptv6", strlen(*av))) 484b867e84eSAndrey V. Elsukov ipfw_nptv6_handler(ac, av); 4853c0c8717SLuigi Rizzo else if (_substrcmp(*av, "flush") == 0) 48656707beeSMark Johnston ipfw_flush(g_co.do_force); 487*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "zero") == 0) 4883c0c8717SLuigi Rizzo ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); 489*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "resetlog") == 0) 4903c0c8717SLuigi Rizzo ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); 4913c0c8717SLuigi Rizzo else if (_substrcmp(*av, "print") == 0 || 4923c0c8717SLuigi Rizzo _substrcmp(*av, "list") == 0) 4933c0c8717SLuigi Rizzo ipfw_list(ac, av, do_acct); 4943c0c8717SLuigi Rizzo else if (_substrcmp(*av, "show") == 0) 4953c0c8717SLuigi Rizzo ipfw_list(ac, av, 1 /* show counters */); 496*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "table") == 0) 497ac35ff17SAlexander V. Chernikov ipfw_table_handler(ac, av); 498*0b95680eSKristof Provost else if (is_ipfw() && _substrcmp(*av, "internal") == 0) 499358b9d09SAlexander V. Chernikov ipfw_internal_handler(ac, av); 5003c0c8717SLuigi Rizzo else 5013c0c8717SLuigi Rizzo errx(EX_USAGE, "bad command `%s'", *av); 5023c0c8717SLuigi Rizzo } 5033c0c8717SLuigi Rizzo 5043c0c8717SLuigi Rizzo /* Free memory allocated in the argument parsing. */ 505cc4d3c30SLuigi Rizzo free(save_av); 5063c0c8717SLuigi Rizzo return 0; 5073c0c8717SLuigi Rizzo } 5083c0c8717SLuigi Rizzo 5093c0c8717SLuigi Rizzo 5103c0c8717SLuigi Rizzo static void 5113c0c8717SLuigi Rizzo ipfw_readfile(int ac, char *av[]) 5123c0c8717SLuigi Rizzo { 5133c0c8717SLuigi Rizzo #define MAX_ARGS 32 514796051d6SAndrey V. Elsukov char buf[4096]; 5153c0c8717SLuigi Rizzo char *progname = av[0]; /* original program name */ 5163c0c8717SLuigi Rizzo const char *cmd = NULL; /* preprocessor name, if any */ 5173c0c8717SLuigi Rizzo const char *filename = av[ac-1]; /* file to read */ 5183c0c8717SLuigi Rizzo int c, lineno=0; 5193c0c8717SLuigi Rizzo FILE *f = NULL; 5203c0c8717SLuigi Rizzo pid_t preproc = 0; 5213c0c8717SLuigi Rizzo 5223c0c8717SLuigi Rizzo while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { 5233c0c8717SLuigi Rizzo switch(c) { 5243c0c8717SLuigi Rizzo case 'c': 52556707beeSMark Johnston g_co.do_compact = 1; 5263c0c8717SLuigi Rizzo break; 5273c0c8717SLuigi Rizzo 5283c0c8717SLuigi Rizzo case 'f': 52956707beeSMark Johnston g_co.do_force = 1; 5303c0c8717SLuigi Rizzo break; 5313c0c8717SLuigi Rizzo 5323c0c8717SLuigi Rizzo case 'N': 53356707beeSMark Johnston g_co.do_resolv = 1; 5343c0c8717SLuigi Rizzo break; 5353c0c8717SLuigi Rizzo 5363c0c8717SLuigi Rizzo case 'n': 53756707beeSMark Johnston g_co.test_only = 1; 5383c0c8717SLuigi Rizzo break; 5393c0c8717SLuigi Rizzo 5403c0c8717SLuigi Rizzo case 'p': 5413c0c8717SLuigi Rizzo /* 5423c0c8717SLuigi Rizzo * ipfw -p cmd [args] filename 5433c0c8717SLuigi Rizzo * 5443c0c8717SLuigi Rizzo * We are done with getopt(). All arguments 5453c0c8717SLuigi Rizzo * except the filename go to the preprocessor, 5463c0c8717SLuigi Rizzo * so we need to do the following: 5473c0c8717SLuigi Rizzo * - check that a filename is actually present; 5483c0c8717SLuigi Rizzo * - advance av by optind-1 to skip arguments 5493c0c8717SLuigi Rizzo * already processed; 5503c0c8717SLuigi Rizzo * - decrease ac by optind, to remove the args 5513c0c8717SLuigi Rizzo * already processed and the final filename; 5523c0c8717SLuigi Rizzo * - set the last entry in av[] to NULL so 5533c0c8717SLuigi Rizzo * popen() can detect the end of the array; 5543c0c8717SLuigi Rizzo * - set optind=ac to let getopt() terminate. 5553c0c8717SLuigi Rizzo */ 5563c0c8717SLuigi Rizzo if (optind == ac) 5573c0c8717SLuigi Rizzo errx(EX_USAGE, "no filename argument"); 5583c0c8717SLuigi Rizzo cmd = optarg; 5593c0c8717SLuigi Rizzo av[ac-1] = NULL; 5603c0c8717SLuigi Rizzo av += optind - 1; 5613c0c8717SLuigi Rizzo ac -= optind; 5623c0c8717SLuigi Rizzo optind = ac; 5633c0c8717SLuigi Rizzo break; 5643c0c8717SLuigi Rizzo 5653c0c8717SLuigi Rizzo case 'q': 56656707beeSMark Johnston g_co.do_quiet = 1; 5673c0c8717SLuigi Rizzo break; 5683c0c8717SLuigi Rizzo 5693c0c8717SLuigi Rizzo case 'S': 57056707beeSMark Johnston g_co.show_sets = 1; 5713c0c8717SLuigi Rizzo break; 5723c0c8717SLuigi Rizzo 5733c0c8717SLuigi Rizzo default: 5743c0c8717SLuigi Rizzo errx(EX_USAGE, "bad arguments, for usage" 5753c0c8717SLuigi Rizzo " summary ``ipfw''"); 5763c0c8717SLuigi Rizzo } 5773c0c8717SLuigi Rizzo 5783c0c8717SLuigi Rizzo } 5793c0c8717SLuigi Rizzo 5803c0c8717SLuigi Rizzo if (cmd == NULL && ac != optind + 1) 5813c0c8717SLuigi Rizzo errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]); 5823c0c8717SLuigi Rizzo 5833c0c8717SLuigi Rizzo if ((f = fopen(filename, "r")) == NULL) 5843c0c8717SLuigi Rizzo err(EX_UNAVAILABLE, "fopen: %s", filename); 5853c0c8717SLuigi Rizzo 5863c0c8717SLuigi Rizzo if (cmd != NULL) { /* pipe through preprocessor */ 5873c0c8717SLuigi Rizzo int pipedes[2]; 5883c0c8717SLuigi Rizzo 5893c0c8717SLuigi Rizzo if (pipe(pipedes) == -1) 5903c0c8717SLuigi Rizzo err(EX_OSERR, "cannot create pipe"); 5913c0c8717SLuigi Rizzo 5923c0c8717SLuigi Rizzo preproc = fork(); 5933c0c8717SLuigi Rizzo if (preproc == -1) 5943c0c8717SLuigi Rizzo err(EX_OSERR, "cannot fork"); 5953c0c8717SLuigi Rizzo 5963c0c8717SLuigi Rizzo if (preproc == 0) { 5973c0c8717SLuigi Rizzo /* 5983c0c8717SLuigi Rizzo * Child, will run the preprocessor with the 5993c0c8717SLuigi Rizzo * file on stdin and the pipe on stdout. 6003c0c8717SLuigi Rizzo */ 6013c0c8717SLuigi Rizzo if (dup2(fileno(f), 0) == -1 6023c0c8717SLuigi Rizzo || dup2(pipedes[1], 1) == -1) 6033c0c8717SLuigi Rizzo err(EX_OSERR, "dup2()"); 6043c0c8717SLuigi Rizzo fclose(f); 6053c0c8717SLuigi Rizzo close(pipedes[1]); 6063c0c8717SLuigi Rizzo close(pipedes[0]); 6073c0c8717SLuigi Rizzo execvp(cmd, av); 6083c0c8717SLuigi Rizzo err(EX_OSERR, "execvp(%s) failed", cmd); 6093c0c8717SLuigi Rizzo } else { /* parent, will reopen f as the pipe */ 6103c0c8717SLuigi Rizzo fclose(f); 6113c0c8717SLuigi Rizzo close(pipedes[1]); 6123c0c8717SLuigi Rizzo if ((f = fdopen(pipedes[0], "r")) == NULL) { 6133c0c8717SLuigi Rizzo int savederrno = errno; 6143c0c8717SLuigi Rizzo 6153c0c8717SLuigi Rizzo (void)kill(preproc, SIGTERM); 6163c0c8717SLuigi Rizzo errno = savederrno; 6173c0c8717SLuigi Rizzo err(EX_OSERR, "fdopen()"); 6183c0c8717SLuigi Rizzo } 6193c0c8717SLuigi Rizzo } 6203c0c8717SLuigi Rizzo } 6213c0c8717SLuigi Rizzo 622796051d6SAndrey V. Elsukov while (fgets(buf, sizeof(buf), f)) { /* read commands */ 623c0831342SLuigi Rizzo char linename[20]; 6243c0c8717SLuigi Rizzo char *args[2]; 6253c0c8717SLuigi Rizzo 6263c0c8717SLuigi Rizzo lineno++; 627c0831342SLuigi Rizzo snprintf(linename, sizeof(linename), "Line %d", lineno); 6283c0c8717SLuigi Rizzo setprogname(linename); /* XXX */ 6293c0c8717SLuigi Rizzo args[0] = progname; 6303c0c8717SLuigi Rizzo args[1] = buf; 6313c0c8717SLuigi Rizzo ipfw_main(2, args); 6323c0c8717SLuigi Rizzo } 6333c0c8717SLuigi Rizzo fclose(f); 6343c0c8717SLuigi Rizzo if (cmd != NULL) { 6353c0c8717SLuigi Rizzo int status; 6363c0c8717SLuigi Rizzo 6373c0c8717SLuigi Rizzo if (waitpid(preproc, &status, 0) == -1) 6383c0c8717SLuigi Rizzo errx(EX_OSERR, "waitpid()"); 6393c0c8717SLuigi Rizzo if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) 6403c0c8717SLuigi Rizzo errx(EX_UNAVAILABLE, 6413c0c8717SLuigi Rizzo "preprocessor exited with status %d", 6423c0c8717SLuigi Rizzo WEXITSTATUS(status)); 6433c0c8717SLuigi Rizzo else if (WIFSIGNALED(status)) 6443c0c8717SLuigi Rizzo errx(EX_UNAVAILABLE, 6453c0c8717SLuigi Rizzo "preprocessor exited with signal %d", 6463c0c8717SLuigi Rizzo WTERMSIG(status)); 6473c0c8717SLuigi Rizzo } 6483c0c8717SLuigi Rizzo } 6493c0c8717SLuigi Rizzo 6503c0c8717SLuigi Rizzo int 6513c0c8717SLuigi Rizzo main(int ac, char *av[]) 6523c0c8717SLuigi Rizzo { 65364290678SLuigi Rizzo #if defined(_WIN32) && defined(TCC) 65464290678SLuigi Rizzo { 65564290678SLuigi Rizzo WSADATA wsaData; 65664290678SLuigi Rizzo int ret=0; 65764290678SLuigi Rizzo unsigned short wVersionRequested = MAKEWORD(2, 2); 65864290678SLuigi Rizzo ret = WSAStartup(wVersionRequested, &wsaData); 65964290678SLuigi Rizzo if (ret != 0) { 66064290678SLuigi Rizzo /* Tell the user that we could not find a usable */ 66164290678SLuigi Rizzo /* Winsock DLL. */ 66264290678SLuigi Rizzo printf("WSAStartup failed with error: %d\n", ret); 66364290678SLuigi Rizzo return 1; 66464290678SLuigi Rizzo } 66564290678SLuigi Rizzo } 66664290678SLuigi Rizzo #endif 667*0b95680eSKristof Provost 668*0b95680eSKristof Provost if (strcmp(av[0], "dnctl") == 0) 669*0b95680eSKristof Provost g_co.prog = cmdline_prog_dnctl; 670*0b95680eSKristof Provost else 671*0b95680eSKristof Provost g_co.prog = cmdline_prog_ipfw; 672*0b95680eSKristof Provost 6733c0c8717SLuigi Rizzo /* 6743c0c8717SLuigi Rizzo * If the last argument is an absolute pathname, interpret it 6753c0c8717SLuigi Rizzo * as a file to be preprocessed. 6763c0c8717SLuigi Rizzo */ 6773c0c8717SLuigi Rizzo 6780dba401dSAndrey V. Elsukov if (ac > 1 && av[ac - 1][0] == '/') { 679*0b95680eSKristof Provost if (! is_ipfw()) 680*0b95680eSKristof Provost errx(EX_USAGE, "usage: dnctl [options]\n" 681*0b95680eSKristof Provost "do \"dnctl -h\" for details"); 682*0b95680eSKristof Provost 6830dba401dSAndrey V. Elsukov if (access(av[ac - 1], R_OK) == 0) 6843c0c8717SLuigi Rizzo ipfw_readfile(ac, av); 6850dba401dSAndrey V. Elsukov else 6860dba401dSAndrey V. Elsukov err(EX_USAGE, "pathname: %s", av[ac - 1]); 6870dba401dSAndrey V. Elsukov } else { 6883c0c8717SLuigi Rizzo if (ipfw_main(ac, av)) { 6893c0c8717SLuigi Rizzo errx(EX_USAGE, 690*0b95680eSKristof Provost "usage: %s [options]\n" 691*0b95680eSKristof Provost "do \"%s -h\" or \"man %s\" for details", av[0], 692*0b95680eSKristof Provost av[0], av[0]); 6933c0c8717SLuigi Rizzo } 6943c0c8717SLuigi Rizzo } 6953c0c8717SLuigi Rizzo return EX_OK; 6963c0c8717SLuigi Rizzo } 697