11558Srgrimes/*- 21558Srgrimes * Copyright (c) 2002-2003,2010 Luigi Rizzo 31558Srgrimes * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 41558Srgrimes * Copyright (c) 1994 Ugen J.S.Antsilevich 51558Srgrimes * 61558Srgrimes * Idea and grammar partially left from: 71558Srgrimes * Copyright (c) 1993 Daniel Boulet 81558Srgrimes * 91558Srgrimes * Redistribution and use in source forms, with and without modification, 101558Srgrimes * are permitted provided that this entire comment appears intact. 111558Srgrimes * 121558Srgrimes * Redistribution in binary form may occur without any restrictions. 131558Srgrimes * Obviously, it would be nice if you gave credit where credit is due 141558Srgrimes * but requiring it would be too onerous. 151558Srgrimes * 161558Srgrimes * This software is provided ``AS IS'' without any warranties of any kind. 171558Srgrimes * 181558Srgrimes * Command line interface for IP firewall facility 191558Srgrimes */ 201558Srgrimes 211558Srgrimes#include <sys/wait.h> 221558Srgrimes#include <ctype.h> 231558Srgrimes#include <err.h> 241558Srgrimes#include <errno.h> 251558Srgrimes#include <signal.h> 261558Srgrimes#include <stdio.h> 271558Srgrimes#include <stdlib.h> 281558Srgrimes#include <string.h> 291558Srgrimes#include <sysexits.h> 301558Srgrimes#include <unistd.h> 3137906Scharnier#include <libgen.h> 3223685Speter 3337906Scharnier#include "ipfw2.h" 3437906Scharnier 3550476Speterstatic void 361558Srgrimeshelp(void) 371558Srgrimes{ 381558Srgrimes if (is_ipfw()) { 391558Srgrimes fprintf(stderr, 40102231Strhodes"ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" 411558Srgrimes"\tipfw [-abcdefhnNqStTv] <command>\n\n" 421558Srgrimes"where <command> is one of the following:\n\n" 431558Srgrimes"add [num] [set N] [prob x] RULE-BODY\n" 441558Srgrimes"{pipe|queue} N config PIPE-BODY\n" 451558Srgrimes"[pipe|queue] {zero|delete|show} [N{,N}]\n" 461558Srgrimes"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" 471558Srgrimes" reset|reverse|proxy_only|redirect_addr linkspec|\n" 481558Srgrimes" redirect_port linkspec|redirect_proto linkspec|\n" 491558Srgrimes" port_range lower-upper}\n" 501558Srgrimes"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" 511558Srgrimes"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" 521558Srgrimes"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" 531558Srgrimes"table all {flush | list}\n" 54103949Smike"\n" 55241013Smdf"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" 561558Srgrimes"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" 571558Srgrimes" skipto N | {divert|tee} PORT | forward ADDR |\n" 581558Srgrimes" pipe N | queue N | nat N | setfib FIB | reass\n" 591558Srgrimes"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" 601558Srgrimes"ADDR: [ MAC dst src ether_type ] \n" 611558Srgrimes" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" 621558Srgrimes" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" 631558Srgrimes"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" 641558Srgrimes"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" 651558Srgrimes"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" 661558Srgrimes"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" 67102231Strhodes"OPTION_LIST: OPTION [OPTION_LIST]\n" 681558Srgrimes"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" 691558Srgrimes" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" 701558Srgrimes" {dst-port|src-port} LIST |\n" 711558Srgrimes" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" 721558Srgrimes" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" 731558Srgrimes" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" 741558Srgrimes" icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n" 7592837Simp" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" 7692837Simp" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" 7792837Simp" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" 781558Srgrimes); 791558Srgrimes } else { 801558Srgrimes fprintf(stderr, 811558Srgrimes"dnctl syntax summary (but please do read the dnctl(8) manpage):\n\n" 821558Srgrimes"\tdnctl [-hnsv] <command>\n\n" 8392837Simp"where <command> is one of the following:\n\n" 841558Srgrimes"[pipe|queue|sched] N config PIPE-BODY\n" 8592806Sobrien"[pipe|queue|sched] {delete|list|show} [N{,N}]\n" 861558Srgrimes"\n" 8723685Speter); 881558Srgrimes } 891558Srgrimes 901558Srgrimes exit(0); 911558Srgrimes} 921558Srgrimes 931558Srgrimes/* 941558Srgrimes * Called with the arguments, including program name because getopt 951558Srgrimes * wants it to be present. 961558Srgrimes * Returns 0 if successful, 1 if empty command, errx() in case of errors. 971558Srgrimes * First thing we do is process parameters creating an argv[] array 981558Srgrimes * which includes the program name and a NULL entry at the end. 9992837Simp * If we are called with a single string, we split it on whitespace. 1001558Srgrimes * Also, arguments with a trailing ',' are joined to the next one. 1011558Srgrimes * The pointers (av[]) and data are in a single chunk of memory. 1021558Srgrimes * av[0] points to the original program name, all other entries 10323685Speter * point into the allocated chunk. 104241013Smdf */ 1051558Srgrimesstatic int 1061558Srgrimesipfw_main(int oldac, char **oldav) 1071558Srgrimes{ 1081558Srgrimes int ch, ac; 1091558Srgrimes const char *errstr; 1101558Srgrimes char **av, **save_av; 1111558Srgrimes int do_acct = 0; /* Show packet/byte count */ 1121558Srgrimes int try_next = 0; /* set if pipe cmd not found */ 1131558Srgrimes int av_size; /* compute the av size */ 1141558Srgrimes char *av_p; /* used to build the av list */ 1151558Srgrimes 1161558Srgrimes#define WHITESP " \t\f\v\n\r" 1171558Srgrimes if (oldac < 2) 1181558Srgrimes return 1; /* need at least one argument */ 11992837Simp 1201558Srgrimes if (oldac == 2) { 12192806Sobrien /* 1221558Srgrimes * If we are called with one argument, try to split it into 1231558Srgrimes * words for subsequent parsing. Spaces after a ',' are 12423685Speter * removed by copying the string in-place. 125241013Smdf */ 1261558Srgrimes char *arg = oldav[1]; /* The string is the first arg. */ 1271558Srgrimes int l = strlen(arg); 1281558Srgrimes int copy = 0; /* 1 if we need to copy, 0 otherwise */ 1291558Srgrimes int i, j; 1301558Srgrimes 1311558Srgrimes for (i = j = 0; i < l; i++) { 1321558Srgrimes if (arg[i] == '#') /* comment marker */ 1331558Srgrimes break; 1341558Srgrimes if (copy) { 135241013Smdf arg[j++] = arg[i]; 1361558Srgrimes copy = !strchr("," WHITESP, arg[i]); 1371558Srgrimes } else { 1381558Srgrimes copy = !strchr(WHITESP, arg[i]); 1391558Srgrimes if (copy) 1401558Srgrimes arg[j++] = arg[i]; 1411558Srgrimes } 14292837Simp } 1431558Srgrimes if (!copy && j > 0) /* last char was a 'blank', remove it */ 14492806Sobrien j--; 14592806Sobrien l = j; /* the new argument length */ 1461558Srgrimes arg[j++] = '\0'; 1471558Srgrimes if (l == 0) /* empty string! */ 1481558Srgrimes return 1; 1491558Srgrimes 15022482Seivind /* 15122482Seivind * First, count number of arguments. Because of the previous 1521558Srgrimes * processing, this is just the number of blanks plus 1. 15322482Seivind */ 15422482Seivind for (i = 0, ac = 1; i < l; i++) 1551558Srgrimes if (strchr(WHITESP, arg[i]) != NULL) 1561558Srgrimes ac++; 1571558Srgrimes 1581558Srgrimes /* 1591558Srgrimes * Allocate the argument list structure as a single block 1601558Srgrimes * of memory, containing pointers and the argument 1611558Srgrimes * strings. We include one entry for the program name 1621558Srgrimes * because getopt expects it, and a NULL at the end 1631558Srgrimes * to simplify further parsing. 1641558Srgrimes */ 1651558Srgrimes ac++; /* add 1 for the program name */ 1661558Srgrimes av_size = (ac+1) * sizeof(char *) + l + 1; 1671558Srgrimes av = safe_calloc(av_size, 1); 1681558Srgrimes 1691558Srgrimes /* 1701558Srgrimes * Init the argument pointer to the end of the array 17192837Simp * and copy arguments from arg[] to av[]. For each one, 1721558Srgrimes * j is the initial character, i is the one past the end. 1731558Srgrimes */ 1741558Srgrimes av_p = (char *)&av[ac+1]; 1751558Srgrimes for (ac = 1, i = j = 0; i < l; i++) { 17623685Speter if (strchr(WHITESP, arg[i]) != NULL || i == l-1) { 1771558Srgrimes if (i == l-1) 1781558Srgrimes i++; 1791558Srgrimes bcopy(arg+j, av_p, i-j); 1801558Srgrimes av[ac] = av_p; 1811558Srgrimes av_p += i-j; /* the length of the string */ 1821558Srgrimes *av_p++ = '\0'; 1831558Srgrimes ac++; 1841558Srgrimes j = i + 1; 1851558Srgrimes } 1861558Srgrimes } 1871558Srgrimes } else { 1881558Srgrimes /* 1891558Srgrimes * If an argument ends with ',' join with the next one. 1901558Srgrimes */ 1911558Srgrimes int first, i, l=0; 1921558Srgrimes 19392837Simp /* 1941558Srgrimes * Allocate the argument list structure as a single block 19592806Sobrien * of memory, containing both pointers and the argument 1961558Srgrimes * strings. We include some space for the program name 1971558Srgrimes * because getopt expects it. 1981558Srgrimes * We add an extra pointer to the end of the array, 1991558Srgrimes * to make simpler further parsing. 20023685Speter */ 2011558Srgrimes for (i=0; i<oldac; i++) 2021558Srgrimes l += strlen(oldav[i]); 2031558Srgrimes 2041558Srgrimes av_size = (oldac+1) * sizeof(char *) + l + oldac; 2051558Srgrimes av = safe_calloc(av_size, 1); 2061558Srgrimes 2071558Srgrimes /* 2081558Srgrimes * Init the argument pointer to the end of the array 2091558Srgrimes * and copy arguments from arg[] to av[] 2101558Srgrimes */ 21137906Scharnier av_p = (char *)&av[oldac+1]; 2121558Srgrimes for (first = i = ac = 1, l = 0; i < oldac; i++) { 2131558Srgrimes char *arg = oldav[i]; 2141558Srgrimes int k = strlen(arg); 2151558Srgrimes 2161558Srgrimes l += k; 2171558Srgrimes if (arg[k-1] != ',' || i == oldac-1) { 2181558Srgrimes /* Time to copy. */ 2191558Srgrimes av[ac] = av_p; 22092837Simp for (l=0; first <= i; first++) { 2211558Srgrimes strcat(av_p, oldav[first]); 22292806Sobrien av_p += strlen(oldav[first]); 2231558Srgrimes } 2241558Srgrimes *av_p++ = '\0'; 2251558Srgrimes ac++; 2261558Srgrimes l = 0; 22723685Speter first = i+1; 2281558Srgrimes } 2291558Srgrimes } 2301558Srgrimes } 2311558Srgrimes 2321558Srgrimes /* 2331558Srgrimes * set the progname pointer to the original string 2341558Srgrimes * and terminate the array with null 2351558Srgrimes */ 2361558Srgrimes av[0] = oldav[0]; 2371558Srgrimes av[ac] = NULL; 2381558Srgrimes 2391558Srgrimes /* Set the force flag for non-interactive processes */ 2401558Srgrimes if (!g_co.do_force) 2411558Srgrimes g_co.do_force = !isatty(STDIN_FILENO); 2421558Srgrimes 2431558Srgrimes#ifdef EMULATE_SYSCTL /* sysctl emulation */ 24423685Speter if (is_ipfw() && ac >= 2 && 2451558Srgrimes !strcmp(av[1], "sysctl")) { 2461558Srgrimes char *s; 2471558Srgrimes int i; 2481558Srgrimes 2491558Srgrimes if (ac != 3) { 2501558Srgrimes printf( "sysctl emulation usage:\n" 2511558Srgrimes " ipfw sysctl name[=value]\n" 25237906Scharnier " ipfw sysctl -a\n"); 2531558Srgrimes return 0; 2541558Srgrimes } 2551558Srgrimes s = strchr(av[2], '='); 2561558Srgrimes if (s == NULL) { 2571558Srgrimes s = !strcmp(av[2], "-a") ? NULL : av[2]; 2581558Srgrimes sysctlbyname(s, NULL, NULL, NULL, 0); 2591558Srgrimes } else { /* ipfw sysctl x.y.z=value */ 2601558Srgrimes /* assume an INT value, will extend later */ 2611558Srgrimes if (s[1] == '\0') { 2621558Srgrimes printf("ipfw sysctl: missing value\n\n"); 2631558Srgrimes return 0; 2641558Srgrimes } 2651558Srgrimes *s = '\0'; 2661558Srgrimes i = strtol(s+1, NULL, 0); 2671558Srgrimes sysctlbyname(av[2], NULL, NULL, &i, sizeof(int)); 26892837Simp } 2691558Srgrimes return 0; 27092806Sobrien } 2711558Srgrimes#endif 2721558Srgrimes 2731558Srgrimes /* Save arguments for final freeing of memory. */ 2741558Srgrimes save_av = av; 2751558Srgrimes 2761558Srgrimes optind = optreset = 1; /* restart getopt() */ 2771558Srgrimes if (is_ipfw()) { 2781558Srgrimes while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtvx")) != -1) 2791558Srgrimes switch (ch) { 2801558Srgrimes case 'a': 2811558Srgrimes do_acct = 1; 2821558Srgrimes break; 2831558Srgrimes 2841558Srgrimes case 'b': 2851558Srgrimes g_co.comment_only = 1; 2861558Srgrimes g_co.do_compact = 1; 2871558Srgrimes break; 2881558Srgrimes 2891558Srgrimes case 'c': 2901558Srgrimes g_co.do_compact = 1; 2911558Srgrimes break; 2921558Srgrimes 2931558Srgrimes case 'd': 2941558Srgrimes g_co.do_dynamic = 1; 2951558Srgrimes break; 2961558Srgrimes 2971558Srgrimes case 'D': 2981558Srgrimes g_co.do_dynamic = 2; 2991558Srgrimes break; 3001558Srgrimes 3011558Srgrimes case 'e': 3021558Srgrimes /* nop for compatibility */ 3031558Srgrimes break; 3041558Srgrimes 3051558Srgrimes case 'f': 3061558Srgrimes g_co.do_force = 1; 3071558Srgrimes break; 3081558Srgrimes 3091558Srgrimes case 'h': /* help */ 3101558Srgrimes free(save_av); 31192837Simp help(); 3121558Srgrimes break; /* NOTREACHED */ 3131558Srgrimes 3141558Srgrimes case 'i': 3151558Srgrimes g_co.do_value_as_ip = 1; 3161558Srgrimes break; 3171558Srgrimes 3181558Srgrimes case 'n': 3191558Srgrimes g_co.test_only = 1; 3201558Srgrimes break; 3211558Srgrimes 3221558Srgrimes case 'N': 3231558Srgrimes g_co.do_resolv = 1; 3241558Srgrimes break; 32523685Speter 3261558Srgrimes case 'p': 3271558Srgrimes errx(EX_USAGE, "An absolute pathname must be used " 3281558Srgrimes "with -p option."); 3291558Srgrimes /* NOTREACHED */ 3301558Srgrimes 3311558Srgrimes case 'q': 3321558Srgrimes g_co.do_quiet = 1; 3331558Srgrimes break; 3341558Srgrimes 3351558Srgrimes case 's': /* sort */ 3361558Srgrimes g_co.do_sort = atoi(optarg); 3371558Srgrimes break; 3381558Srgrimes 33992837Simp case 'S': 3401558Srgrimes g_co.show_sets = 1; 34192806Sobrien break; 3421558Srgrimes 3431558Srgrimes case 't': 3441558Srgrimes g_co.do_time = TIMESTAMP_STRING; 3451558Srgrimes break; 3461558Srgrimes 3471558Srgrimes case 'T': 3481558Srgrimes g_co.do_time = TIMESTAMP_NUMERIC; 3491558Srgrimes break; 3501558Srgrimes 3511558Srgrimes case 'v': /* verbose */ 3521558Srgrimes g_co.verbose = 1; 3531558Srgrimes break; 3541558Srgrimes 3551558Srgrimes case 'x': /* debug output */ 3561558Srgrimes g_co.debug_only = 1; 3571558Srgrimes break; 3581558Srgrimes 3591558Srgrimes default: 3608871Srgrimes free(save_av); 3611558Srgrimes return 1; 3621558Srgrimes } 3631558Srgrimes } else { 3641558Srgrimes while ((ch = getopt(ac, av, "hns:v")) != -1) 36537906Scharnier switch (ch) { 3661558Srgrimes 3671558Srgrimes case 'h': /* help */ 3681558Srgrimes free(save_av); 3691558Srgrimes help(); 3701558Srgrimes break; /* NOTREACHED */ 3711558Srgrimes 3721558Srgrimes case 'n': 3731558Srgrimes g_co.test_only = 1; 3741558Srgrimes break; 3751558Srgrimes 3761558Srgrimes case 's': /* sort */ 3771558Srgrimes g_co.do_sort = atoi(optarg); 3781558Srgrimes break; 3791558Srgrimes 3801558Srgrimes case 'v': /* verbose */ 3811558Srgrimes g_co.verbose = 1; 3821558Srgrimes break; 3831558Srgrimes 38492837Simp default: 3851558Srgrimes free(save_av); 3861558Srgrimes return 1; 3871558Srgrimes } 3881558Srgrimes 3891558Srgrimes } 3901558Srgrimes 3911558Srgrimes ac -= optind; 3921558Srgrimes av += optind; 3931558Srgrimes NEED1("bad arguments, for usage summary ``ipfw''"); 3941558Srgrimes 3951558Srgrimes /* 3961558Srgrimes * An undocumented behaviour of ipfw1 was to allow rule numbers first, 3971558Srgrimes * e.g. "100 add allow ..." instead of "add 100 allow ...". 3981558Srgrimes * In case, swap first and second argument to get the normal form. 3991558Srgrimes */ 4001558Srgrimes if (ac > 1 && isdigit(*av[0])) { 4011558Srgrimes char *p = av[0]; 4021558Srgrimes 4031558Srgrimes av[0] = av[1]; 4041558Srgrimes av[1] = p; 4051558Srgrimes } 4061558Srgrimes 4071558Srgrimes /* 4081558Srgrimes * Optional: pipe, queue or nat. 4091558Srgrimes */ 4101558Srgrimes g_co.do_nat = 0; 41192837Simp g_co.do_pipe = 0; 4121558Srgrimes g_co.use_set = 0; 4131558Srgrimes if (is_ipfw() && !strncmp(*av, "nat", strlen(*av))) 4148871Srgrimes g_co.do_nat = 1; 4151558Srgrimes else if (!strncmp(*av, "pipe", strlen(*av))) 4161558Srgrimes g_co.do_pipe = 1; 4171558Srgrimes else if (_substrcmp(*av, "queue") == 0) 4181558Srgrimes g_co.do_pipe = 2; 4191558Srgrimes else if (_substrcmp(*av, "flowset") == 0) 4201558Srgrimes g_co.do_pipe = 2; 4211558Srgrimes else if (_substrcmp(*av, "sched") == 0) 4221558Srgrimes g_co.do_pipe = 3; 4231558Srgrimes else if (is_ipfw() && !strncmp(*av, "set", strlen(*av))) { 4241558Srgrimes if (ac > 1 && isdigit(av[1][0])) { 42540668Sdima g_co.use_set = strtonum(av[1], 0, resvd_set_number, 42640668Sdima &errstr); 42740668Sdima if (errstr) 4281558Srgrimes errx(EX_DATAERR, 4291558Srgrimes "invalid set number %s\n", av[1]); 4301558Srgrimes ac -= 2; av += 2; g_co.use_set++; 43140668Sdima } 4321558Srgrimes } 4331558Srgrimes 4341558Srgrimes if (g_co.do_pipe || g_co.do_nat) { 4351558Srgrimes ac--; 4361558Srgrimes av++; 4371558Srgrimes } 43892837Simp NEED1("missing command"); 4391558Srgrimes 44092806Sobrien /* 44192806Sobrien * For pipes, queues and nats we normally say 'nat|pipe NN config' 4421558Srgrimes * but the code is easier to parse as 'nat|pipe config NN' 4431558Srgrimes * so we swap the two arguments. 4441558Srgrimes */ 4451558Srgrimes if ((g_co.do_pipe || g_co.do_nat) && ac > 1 && isdigit(*av[0])) { 4461558Srgrimes char *p = av[0]; 447207998Sbrueffer 4481558Srgrimes av[0] = av[1]; 4491558Srgrimes av[1] = p; 4501558Srgrimes } 4511558Srgrimes 4521558Srgrimes if (! is_ipfw() && g_co.do_pipe == 0) { 4531558Srgrimes help(); 45485746Stobez } 4551558Srgrimes 4561558Srgrimes if (g_co.use_set == 0) { 4571558Srgrimes if (is_ipfw() && _substrcmp(*av, "add") == 0) 45837906Scharnier ipfw_add(av); 4591558Srgrimes else if (g_co.do_nat && _substrcmp(*av, "show") == 0) 4601558Srgrimes ipfw_show_nat(ac, av); 46123685Speter else if (g_co.do_pipe && _substrcmp(*av, "config") == 0) 4621558Srgrimes ipfw_config_pipe(ac, av); 4631558Srgrimes else if (g_co.do_nat && _substrcmp(*av, "config") == 0) 4641558Srgrimes ipfw_config_nat(ac, av); 4651558Srgrimes else if (is_ipfw() && _substrcmp(*av, "set") == 0) 4661558Srgrimes ipfw_sets_handler(av); 4671558Srgrimes else if (is_ipfw() && _substrcmp(*av, "table") == 0) 4681558Srgrimes ipfw_table_handler(ac, av); 4691558Srgrimes else if (is_ipfw() && _substrcmp(*av, "enable") == 0) 4701558Srgrimes ipfw_sysctl_handler(av, 1); 4711558Srgrimes else if (is_ipfw() && _substrcmp(*av, "disable") == 0) 4721558Srgrimes ipfw_sysctl_handler(av, 0); 47323685Speter else 4741558Srgrimes try_next = 1; 47523685Speter } 4761558Srgrimes 4771558Srgrimes if (g_co.use_set || try_next) { 4781558Srgrimes if (_substrcmp(*av, "delete") == 0) 4791558Srgrimes ipfw_delete(av); 4801558Srgrimes else if (is_ipfw() && !strncmp(*av, "nat64clat", strlen(*av))) 4811558Srgrimes ipfw_nat64clat_handler(ac, av); 4821558Srgrimes else if (is_ipfw() && !strncmp(*av, "nat64stl", strlen(*av))) 4831558Srgrimes ipfw_nat64stl_handler(ac, av); 4841558Srgrimes else if (is_ipfw() && !strncmp(*av, "nat64lsn", strlen(*av))) 4851558Srgrimes ipfw_nat64lsn_handler(ac, av); 4861558Srgrimes else if (is_ipfw() && !strncmp(*av, "nptv6", strlen(*av))) 4871558Srgrimes ipfw_nptv6_handler(ac, av); 4881558Srgrimes else if (_substrcmp(*av, "flush") == 0) 4891558Srgrimes ipfw_flush(g_co.do_force); 4901558Srgrimes else if (is_ipfw() && _substrcmp(*av, "zero") == 0) 4911558Srgrimes ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); 4921558Srgrimes else if (is_ipfw() && _substrcmp(*av, "resetlog") == 0) 4931558Srgrimes ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); 4941558Srgrimes else if (_substrcmp(*av, "print") == 0 || 4951558Srgrimes _substrcmp(*av, "list") == 0) 4961558Srgrimes ipfw_list(ac, av, do_acct); 4971558Srgrimes else if (_substrcmp(*av, "show") == 0) 4981558Srgrimes ipfw_list(ac, av, 1 /* show counters */); 4991558Srgrimes else if (is_ipfw() && _substrcmp(*av, "table") == 0) 5001558Srgrimes ipfw_table_handler(ac, av); 5011558Srgrimes else if (is_ipfw() && _substrcmp(*av, "internal") == 0) 5021558Srgrimes ipfw_internal_handler(ac, av); 5031558Srgrimes else 5041558Srgrimes errx(EX_USAGE, "bad command `%s'", *av); 5051558Srgrimes } 5061558Srgrimes 5071558Srgrimes /* Free memory allocated in the argument parsing. */ 5081558Srgrimes free(save_av); 5091558Srgrimes return 0; 5101558Srgrimes} 5111558Srgrimes 5121558Srgrimes 5131558Srgrimesstatic void 5141558Srgrimesipfw_readfile(int ac, char *av[]) 5151558Srgrimes{ 5161558Srgrimes#define MAX_ARGS 32 5171558Srgrimes char buf[4096]; 5181558Srgrimes char *progname = av[0]; /* original program name */ 5191558Srgrimes const char *cmd = NULL; /* preprocessor name, if any */ 5201558Srgrimes const char *filename = av[ac-1]; /* file to read */ 5211558Srgrimes int c, lineno=0; 5221558Srgrimes FILE *f = NULL; 5231558Srgrimes pid_t preproc = 0; 52492837Simp 5251558Srgrimes if (is_ipfw()) { 5261558Srgrimes while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { 5271558Srgrimes switch(c) { 52892806Sobrien case 'c': 5291558Srgrimes g_co.do_compact = 1; 5301558Srgrimes break; 5311558Srgrimes 53292806Sobrien case 'f': 5331558Srgrimes g_co.do_force = 1; 5341558Srgrimes break; 5351558Srgrimes 5361558Srgrimes case 'N': 5371558Srgrimes g_co.do_resolv = 1; 5381558Srgrimes break; 5391558Srgrimes 5401558Srgrimes case 'n': 5411558Srgrimes g_co.test_only = 1; 5421558Srgrimes break; 5431558Srgrimes 5441558Srgrimes case 'p': 5451558Srgrimes /* 5461558Srgrimes * ipfw -p cmd [args] filename 5471558Srgrimes * 5481558Srgrimes * We are done with getopt(). All arguments 5491558Srgrimes * except the filename go to the preprocessor, 5501558Srgrimes * so we need to do the following: 5511558Srgrimes * - check that a filename is actually present; 5521558Srgrimes * - advance av by optind-1 to skip arguments 5531558Srgrimes * already processed; 5541558Srgrimes * - decrease ac by optind, to remove the args 5551558Srgrimes * already processed and the final filename; 5561558Srgrimes * - set the last entry in av[] to NULL so 5571558Srgrimes * popen() can detect the end of the array; 5581558Srgrimes * - set optind=ac to let getopt() terminate. 5591558Srgrimes */ 5601558Srgrimes if (optind == ac) 5611558Srgrimes errx(EX_USAGE, "no filename argument"); 5621558Srgrimes cmd = optarg; 563299148Spfg av[ac-1] = NULL; 5641558Srgrimes av += optind - 1; 5651558Srgrimes ac -= optind; 5661558Srgrimes optind = ac; 5671558Srgrimes break; 5681558Srgrimes 5691558Srgrimes case 'q': 5701558Srgrimes g_co.do_quiet = 1; 5711558Srgrimes break; 5721558Srgrimes 5731558Srgrimes case 'S': 5741558Srgrimes g_co.show_sets = 1; 5751558Srgrimes break; 5761558Srgrimes 5771558Srgrimes default: 5781558Srgrimes errx(EX_USAGE, "bad arguments, for usage" 5791558Srgrimes " summary ``ipfw''"); 5801558Srgrimes } 5811558Srgrimes } 5821558Srgrimes } else { 5831558Srgrimes while ((c = getopt(ac, av, "nq")) != -1) { 5841558Srgrimes switch(c) { 5851558Srgrimes case 'n': 5861558Srgrimes g_co.test_only = 1; 5871558Srgrimes break; 5881558Srgrimes 5891558Srgrimes case 'q': 5901558Srgrimes g_co.do_quiet = 1; 5911558Srgrimes break; 5921558Srgrimes 5931558Srgrimes default: 5941558Srgrimes errx(EX_USAGE, "bad arguments, for usage" 5951558Srgrimes " summary ``dnctl''"); 5961558Srgrimes } 5971558Srgrimes } 5981558Srgrimes } 5991558Srgrimes 6001558Srgrimes if (cmd == NULL && ac != optind + 1) 6011558Srgrimes errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]); 6021558Srgrimes 6031558Srgrimes if ((f = fopen(filename, "r")) == NULL) 6041558Srgrimes err(EX_UNAVAILABLE, "fopen: %s", filename); 6051558Srgrimes 6061558Srgrimes if (cmd != NULL) { /* pipe through preprocessor */ 6071558Srgrimes int pipedes[2]; 6081558Srgrimes 6091558Srgrimes if (pipe(pipedes) == -1) 6101558Srgrimes err(EX_OSERR, "cannot create pipe"); 6111558Srgrimes 6121558Srgrimes preproc = fork(); 6131558Srgrimes if (preproc == -1) 6141558Srgrimes err(EX_OSERR, "cannot fork"); 6151558Srgrimes 6161558Srgrimes if (preproc == 0) { 617 /* 618 * Child, will run the preprocessor with the 619 * file on stdin and the pipe on stdout. 620 */ 621 if (dup2(fileno(f), 0) == -1 622 || dup2(pipedes[1], 1) == -1) 623 err(EX_OSERR, "dup2()"); 624 fclose(f); 625 close(pipedes[1]); 626 close(pipedes[0]); 627 execvp(cmd, av); 628 err(EX_OSERR, "execvp(%s) failed", cmd); 629 } else { /* parent, will reopen f as the pipe */ 630 fclose(f); 631 close(pipedes[1]); 632 if ((f = fdopen(pipedes[0], "r")) == NULL) { 633 int savederrno = errno; 634 635 (void)kill(preproc, SIGTERM); 636 errno = savederrno; 637 err(EX_OSERR, "fdopen()"); 638 } 639 } 640 } 641 642 while (fgets(buf, sizeof(buf), f)) { /* read commands */ 643 char linename[20]; 644 char *args[2]; 645 646 lineno++; 647 snprintf(linename, sizeof(linename), "Line %d", lineno); 648 setprogname(linename); /* XXX */ 649 args[0] = progname; 650 args[1] = buf; 651 ipfw_main(2, args); 652 } 653 fclose(f); 654 if (cmd != NULL) { 655 int status; 656 657 if (waitpid(preproc, &status, 0) == -1) 658 errx(EX_OSERR, "waitpid()"); 659 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) 660 errx(EX_UNAVAILABLE, 661 "preprocessor exited with status %d", 662 WEXITSTATUS(status)); 663 else if (WIFSIGNALED(status)) 664 errx(EX_UNAVAILABLE, 665 "preprocessor exited with signal %d", 666 WTERMSIG(status)); 667 } 668} 669 670int 671main(int ac, char *av[]) 672{ 673#if defined(_WIN32) && defined(TCC) 674 { 675 WSADATA wsaData; 676 int ret=0; 677 unsigned short wVersionRequested = MAKEWORD(2, 2); 678 ret = WSAStartup(wVersionRequested, &wsaData); 679 if (ret != 0) { 680 /* Tell the user that we could not find a usable */ 681 /* Winsock DLL. */ 682 printf("WSAStartup failed with error: %d\n", ret); 683 return 1; 684 } 685 } 686#endif 687 688 if (strcmp("dnctl", basename(av[0])) == 0) 689 g_co.prog = cmdline_prog_dnctl; 690 else 691 g_co.prog = cmdline_prog_ipfw; 692 693 /* 694 * If the last argument is an absolute pathname, interpret it 695 * as a file to be preprocessed. 696 */ 697 698 if (ac > 1 && av[ac - 1][0] == '/') { 699 if (access(av[ac - 1], R_OK) == 0) 700 ipfw_readfile(ac, av); 701 else 702 err(EX_USAGE, "pathname: %s", av[ac - 1]); 703 } else { 704 if (ipfw_main(ac, av)) { 705 errx(EX_USAGE, 706 "usage: %s [options]\n" 707 "do \"%s -h\" or \"man %s\" for details", av[0], 708 av[0], av[0]); 709 } 710 } 711 return EX_OK; 712} 713