]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ipfw/main.c
camcontrol(8): Fix typo in a source code comment
[FreeBSD/FreeBSD.git] / sbin / ipfw / main.c
1 /*-
2  * Copyright (c) 2002-2003,2010 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  * Command line interface for IP firewall facility
19  *
20  * $FreeBSD$
21  */
22
23 #include <sys/wait.h>
24 #include <ctype.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sysexits.h>
32 #include <unistd.h>
33
34 #include "ipfw2.h"
35
36 static void
37 help(void)
38 {
39         if (is_ipfw()) {
40                 fprintf(stderr,
41 "ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n"
42 "\tipfw [-abcdefhnNqStTv] <command>\n\n"
43 "where <command> is one of the following:\n\n"
44 "add [num] [set N] [prob x] RULE-BODY\n"
45 "{pipe|queue} N config PIPE-BODY\n"
46 "[pipe|queue] {zero|delete|show} [N{,N}]\n"
47 "nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n"
48 "               reset|reverse|proxy_only|redirect_addr linkspec|\n"
49 "               redirect_port linkspec|redirect_proto linkspec|\n"
50 "               port_range lower-upper}\n"
51 "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
52 "set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
53 "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
54 "table all {flush | list}\n"
55 "\n"
56 "RULE-BODY:     check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
57 "ACTION:        check-state | allow | count | deny | unreach{,6} CODE |\n"
58 "               skipto N | {divert|tee} PORT | forward ADDR |\n"
59 "               pipe N | queue N | nat N | setfib FIB | reass\n"
60 "PARAMS:        [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
61 "ADDR:          [ MAC dst src ether_type ] \n"
62 "               [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
63 "               [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
64 "IPADDR:        [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
65 "IP6ADDR:       [not] { any | me | me6 | ip6/bits | IP6LIST }\n"
66 "IP6LIST:       { ip6 | ip6/bits }[,IP6LIST]\n"
67 "IPLIST:        { ip | ip/bits | ip:mask }[,IPLIST]\n"
68 "OPTION_LIST:   OPTION [OPTION_LIST]\n"
69 "OPTION:        bridged | diverted | diverted-loopback | diverted-output |\n"
70 "       {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
71 "       {dst-port|src-port} LIST |\n"
72 "       estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
73 "       iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
74 "       ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
75 "       icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n"
76 "       mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
77 "       setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
78 "       tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
79 );
80         } else {
81                 fprintf(stderr,
82 "dnctl syntax summary (but please do read the dnctl(8) manpage):\n\n"
83 "\tdnctl [-hnsv] <command>\n\n"
84 "where <command> is one of the following:\n\n"
85 "[pipe|queue|sched] N config PIPE-BODY\n"
86 "[pipe|queue|sched] {delete|list|show} [N{,N}]\n"
87 "\n"
88 );
89         }
90
91         exit(0);
92 }
93
94 /*
95  * Called with the arguments, including program name because getopt
96  * wants it to be present.
97  * Returns 0 if successful, 1 if empty command, errx() in case of errors.
98  * First thing we do is process parameters creating an argv[] array
99  * which includes the program name and a NULL entry at the end.
100  * If we are called with a single string, we split it on whitespace.
101  * Also, arguments with a trailing ',' are joined to the next one.
102  * The pointers (av[]) and data are in a single chunk of memory.
103  * av[0] points to the original program name, all other entries
104  * point into the allocated chunk.
105  */
106 static int
107 ipfw_main(int oldac, char **oldav)
108 {
109         int ch, ac;
110         const char *errstr;
111         char **av, **save_av;
112         int do_acct = 0;                /* Show packet/byte count */
113         int try_next = 0;               /* set if pipe cmd not found */
114         int av_size;                    /* compute the av size */
115         char *av_p;                     /* used to build the av list */
116
117 #define WHITESP         " \t\f\v\n\r"
118         if (oldac < 2)
119                 return 1;       /* need at least one argument */
120
121         if (oldac == 2) {
122                 /*
123                  * If we are called with one argument, try to split it into
124                  * words for subsequent parsing. Spaces after a ',' are
125                  * removed by copying the string in-place.
126                  */
127                 char *arg = oldav[1];   /* The string is the first arg. */
128                 int l = strlen(arg);
129                 int copy = 0;           /* 1 if we need to copy, 0 otherwise */
130                 int i, j;
131
132                 for (i = j = 0; i < l; i++) {
133                         if (arg[i] == '#')      /* comment marker */
134                                 break;
135                         if (copy) {
136                                 arg[j++] = arg[i];
137                                 copy = !strchr("," WHITESP, arg[i]);
138                         } else {
139                                 copy = !strchr(WHITESP, arg[i]);
140                                 if (copy)
141                                         arg[j++] = arg[i];
142                         }
143                 }
144                 if (!copy && j > 0)     /* last char was a 'blank', remove it */
145                         j--;
146                 l = j;                  /* the new argument length */
147                 arg[j++] = '\0';
148                 if (l == 0)             /* empty string! */
149                         return 1;
150
151                 /*
152                  * First, count number of arguments. Because of the previous
153                  * processing, this is just the number of blanks plus 1.
154                  */
155                 for (i = 0, ac = 1; i < l; i++)
156                         if (strchr(WHITESP, arg[i]) != NULL)
157                                 ac++;
158
159                 /*
160                  * Allocate the argument list structure as a single block
161                  * of memory, containing pointers and the argument
162                  * strings. We include one entry for the program name
163                  * because getopt expects it, and a NULL at the end
164                  * to simplify further parsing.
165                  */
166                 ac++;           /* add 1 for the program name */
167                 av_size = (ac+1) * sizeof(char *) + l + 1;
168                 av = safe_calloc(av_size, 1);
169
170                 /*
171                  * Init the argument pointer to the end of the array
172                  * and copy arguments from arg[] to av[]. For each one,
173                  * j is the initial character, i is the one past the end.
174                  */
175                 av_p = (char *)&av[ac+1];
176                 for (ac = 1, i = j = 0; i < l; i++) {
177                         if (strchr(WHITESP, arg[i]) != NULL || i == l-1) {
178                                 if (i == l-1)
179                                         i++;
180                                 bcopy(arg+j, av_p, i-j);
181                                 av[ac] = av_p;
182                                 av_p += i-j;    /* the length of the string */
183                                 *av_p++ = '\0';
184                                 ac++;
185                                 j = i + 1;
186                         }
187                 }
188         } else {
189                 /*
190                  * If an argument ends with ',' join with the next one.
191                  */
192                 int first, i, l=0;
193
194                 /*
195                  * Allocate the argument list structure as a single block
196                  * of memory, containing both pointers and the argument
197                  * strings. We include some space for the program name
198                  * because getopt expects it.
199                  * We add an extra pointer to the end of the array,
200                  * to make simpler further parsing.
201                  */
202                 for (i=0; i<oldac; i++)
203                         l += strlen(oldav[i]);
204
205                 av_size = (oldac+1) * sizeof(char *) + l + oldac;
206                 av = safe_calloc(av_size, 1);
207
208                 /*
209                  * Init the argument pointer to the end of the array
210                  * and copy arguments from arg[] to av[]
211                  */
212                 av_p = (char *)&av[oldac+1];
213                 for (first = i = ac = 1, l = 0; i < oldac; i++) {
214                         char *arg = oldav[i];
215                         int k = strlen(arg);
216
217                         l += k;
218                         if (arg[k-1] != ',' || i == oldac-1) {
219                                 /* Time to copy. */
220                                 av[ac] = av_p;
221                                 for (l=0; first <= i; first++) {
222                                         strcat(av_p, oldav[first]);
223                                         av_p += strlen(oldav[first]);
224                                 }
225                                 *av_p++ = '\0';
226                                 ac++;
227                                 l = 0;
228                                 first = i+1;
229                         }
230                 }
231         }
232
233         /*
234          * set the progname pointer to the original string
235          * and terminate the array with null
236          */
237         av[0] = oldav[0];
238         av[ac] = NULL;
239
240         /* Set the force flag for non-interactive processes */
241         if (!g_co.do_force)
242                 g_co.do_force = !isatty(STDIN_FILENO);
243
244 #ifdef EMULATE_SYSCTL /* sysctl emulation */
245         if (is_ipfw() && ac >= 2 &&
246             !strcmp(av[1], "sysctl")) {
247                 char *s;
248                 int i;
249
250                 if (ac != 3) {
251                         printf( "sysctl emulation usage:\n"
252                                 "       ipfw sysctl name[=value]\n"
253                                 "       ipfw sysctl -a\n");
254                         return 0;
255                 }
256                 s = strchr(av[2], '=');
257                 if (s == NULL) {
258                         s = !strcmp(av[2], "-a") ? NULL : av[2];
259                         sysctlbyname(s, NULL, NULL, NULL, 0);
260                 } else {        /* ipfw sysctl x.y.z=value */
261                         /* assume an INT value, will extend later */
262                         if (s[1] == '\0') {
263                                 printf("ipfw sysctl: missing value\n\n");
264                                 return 0;
265                         }
266                         *s = '\0';
267                         i = strtol(s+1, NULL, 0);
268                         sysctlbyname(av[2], NULL, NULL, &i, sizeof(int));
269                 }
270                 return 0;
271         }
272 #endif
273
274         /* Save arguments for final freeing of memory. */
275         save_av = av;
276
277         optind = optreset = 1;  /* restart getopt() */
278         if (is_ipfw()) {
279                 while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtv")) != -1)
280                         switch (ch) {
281                         case 'a':
282                                 do_acct = 1;
283                                 break;
284
285                         case 'b':
286                                 g_co.comment_only = 1;
287                                 g_co.do_compact = 1;
288                                 break;
289
290                         case 'c':
291                                 g_co.do_compact = 1;
292                                 break;
293
294                         case 'd':
295                                 g_co.do_dynamic = 1;
296                                 break;
297
298                         case 'D':
299                                 g_co.do_dynamic = 2;
300                                 break;
301
302                         case 'e':
303                                 /* nop for compatibility */
304                                 break;
305
306                         case 'f':
307                                 g_co.do_force = 1;
308                                 break;
309
310                         case 'h': /* help */
311                                 free(save_av);
312                                 help();
313                                 break;  /* NOTREACHED */
314
315                         case 'i':
316                                 g_co.do_value_as_ip = 1;
317                                 break;
318
319                         case 'n':
320                                 g_co.test_only = 1;
321                                 break;
322
323                         case 'N':
324                                 g_co.do_resolv = 1;
325                                 break;
326
327                         case 'p':
328                                 errx(EX_USAGE, "An absolute pathname must be used "
329                                     "with -p option.");
330                                 /* NOTREACHED */
331
332                         case 'q':
333                                 g_co.do_quiet = 1;
334                                 break;
335
336                         case 's': /* sort */
337                                 g_co.do_sort = atoi(optarg);
338                                 break;
339
340                         case 'S':
341                                 g_co.show_sets = 1;
342                                 break;
343
344                         case 't':
345                                 g_co.do_time = TIMESTAMP_STRING;
346                                 break;
347
348                         case 'T':
349                                 g_co.do_time = TIMESTAMP_NUMERIC;
350                                 break;
351
352                         case 'v': /* verbose */
353                                 g_co.verbose = 1;
354                                 break;
355
356                         default:
357                                 free(save_av);
358                                 return 1;
359                         }
360         } else {
361                 while ((ch = getopt(ac, av, "hns:v")) != -1)
362                         switch (ch) {
363
364                         case 'h': /* help */
365                                 free(save_av);
366                                 help();
367                                 break;  /* NOTREACHED */
368
369                         case 'n':
370                                 g_co.test_only = 1;
371                                 break;
372
373                         case 's': /* sort */
374                                 g_co.do_sort = atoi(optarg);
375                                 break;
376
377                         case 'v': /* verbose */
378                                 g_co.verbose = 1;
379                                 break;
380
381                         default:
382                                 free(save_av);
383                                 return 1;
384                         }
385
386         }
387
388         ac -= optind;
389         av += optind;
390         NEED1("bad arguments, for usage summary ``ipfw''");
391
392         /*
393          * An undocumented behaviour of ipfw1 was to allow rule numbers first,
394          * e.g. "100 add allow ..." instead of "add 100 allow ...".
395          * In case, swap first and second argument to get the normal form.
396          */
397         if (ac > 1 && isdigit(*av[0])) {
398                 char *p = av[0];
399
400                 av[0] = av[1];
401                 av[1] = p;
402         }
403
404         /*
405          * Optional: pipe, queue or nat.
406          */
407         g_co.do_nat = 0;
408         g_co.do_pipe = 0;
409         g_co.use_set = 0;
410         if (is_ipfw() && !strncmp(*av, "nat", strlen(*av)))
411                 g_co.do_nat = 1;
412         else if (!strncmp(*av, "pipe", strlen(*av)))
413                 g_co.do_pipe = 1;
414         else if (_substrcmp(*av, "queue") == 0)
415                 g_co.do_pipe = 2;
416         else if (_substrcmp(*av, "flowset") == 0)
417                 g_co.do_pipe = 2;
418         else if (_substrcmp(*av, "sched") == 0)
419                 g_co.do_pipe = 3;
420         else if (is_ipfw() && !strncmp(*av, "set", strlen(*av))) {
421                 if (ac > 1 && isdigit(av[1][0])) {
422                         g_co.use_set = strtonum(av[1], 0, resvd_set_number,
423                                         &errstr);
424                         if (errstr)
425                                 errx(EX_DATAERR,
426                                     "invalid set number %s\n", av[1]);
427                         ac -= 2; av += 2; g_co.use_set++;
428                 }
429         }
430
431         if (g_co.do_pipe || g_co.do_nat) {
432                 ac--;
433                 av++;
434         }
435         NEED1("missing command");
436
437         /*
438          * For pipes, queues and nats we normally say 'nat|pipe NN config'
439          * but the code is easier to parse as 'nat|pipe config NN'
440          * so we swap the two arguments.
441          */
442         if ((g_co.do_pipe || g_co.do_nat) && ac > 1 && isdigit(*av[0])) {
443                 char *p = av[0];
444
445                 av[0] = av[1];
446                 av[1] = p;
447         }
448
449         if (! is_ipfw() && g_co.do_pipe == 0) {
450                 help();
451         }
452
453         if (g_co.use_set == 0) {
454                 if (is_ipfw() && _substrcmp(*av, "add") == 0)
455                         ipfw_add(av);
456                 else if (g_co.do_nat && _substrcmp(*av, "show") == 0)
457                         ipfw_show_nat(ac, av);
458                 else if (g_co.do_pipe && _substrcmp(*av, "config") == 0)
459                         ipfw_config_pipe(ac, av);
460                 else if (g_co.do_nat && _substrcmp(*av, "config") == 0)
461                         ipfw_config_nat(ac, av);
462                 else if (is_ipfw() && _substrcmp(*av, "set") == 0)
463                         ipfw_sets_handler(av);
464                 else if (is_ipfw() && _substrcmp(*av, "table") == 0)
465                         ipfw_table_handler(ac, av);
466                 else if (is_ipfw() && _substrcmp(*av, "enable") == 0)
467                         ipfw_sysctl_handler(av, 1);
468                 else if (is_ipfw() && _substrcmp(*av, "disable") == 0)
469                         ipfw_sysctl_handler(av, 0);
470                 else
471                         try_next = 1;
472         }
473
474         if (g_co.use_set || try_next) {
475                 if (_substrcmp(*av, "delete") == 0)
476                         ipfw_delete(av);
477                 else if (is_ipfw() && !strncmp(*av, "nat64clat", strlen(*av)))
478                         ipfw_nat64clat_handler(ac, av);
479                 else if (is_ipfw() && !strncmp(*av, "nat64stl", strlen(*av)))
480                         ipfw_nat64stl_handler(ac, av);
481                 else if (is_ipfw() && !strncmp(*av, "nat64lsn", strlen(*av)))
482                         ipfw_nat64lsn_handler(ac, av);
483                 else if (is_ipfw() && !strncmp(*av, "nptv6", strlen(*av)))
484                         ipfw_nptv6_handler(ac, av);
485                 else if (_substrcmp(*av, "flush") == 0)
486                         ipfw_flush(g_co.do_force);
487                 else if (is_ipfw() && _substrcmp(*av, "zero") == 0)
488                         ipfw_zero(ac, av, 0 /* IP_FW_ZERO */);
489                 else if (is_ipfw() && _substrcmp(*av, "resetlog") == 0)
490                         ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */);
491                 else if (_substrcmp(*av, "print") == 0 ||
492                          _substrcmp(*av, "list") == 0)
493                         ipfw_list(ac, av, do_acct);
494                 else if (_substrcmp(*av, "show") == 0)
495                         ipfw_list(ac, av, 1 /* show counters */);
496                 else if (is_ipfw() && _substrcmp(*av, "table") == 0)
497                         ipfw_table_handler(ac, av);
498                 else if (is_ipfw() && _substrcmp(*av, "internal") == 0)
499                         ipfw_internal_handler(ac, av);
500                 else
501                         errx(EX_USAGE, "bad command `%s'", *av);
502         }
503
504         /* Free memory allocated in the argument parsing. */
505         free(save_av);
506         return 0;
507 }
508
509
510 static void
511 ipfw_readfile(int ac, char *av[])
512 {
513 #define MAX_ARGS        32
514         char buf[4096];
515         char *progname = av[0];         /* original program name */
516         const char *cmd = NULL;         /* preprocessor name, if any */
517         const char *filename = av[ac-1]; /* file to read */
518         int     c, lineno=0;
519         FILE    *f = NULL;
520         pid_t   preproc = 0;
521
522         if (is_ipfw()) {
523                 while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
524                         switch(c) {
525                         case 'c':
526                                 g_co.do_compact = 1;
527                                 break;
528
529                         case 'f':
530                                 g_co.do_force = 1;
531                                 break;
532
533                         case 'N':
534                                 g_co.do_resolv = 1;
535                                 break;
536
537                         case 'n':
538                                 g_co.test_only = 1;
539                                 break;
540
541                         case 'p':
542                                 /*
543                                  * ipfw -p cmd [args] filename
544                                  *
545                                  * We are done with getopt(). All arguments
546                                  * except the filename go to the preprocessor,
547                                  * so we need to do the following:
548                                  * - check that a filename is actually present;
549                                  * - advance av by optind-1 to skip arguments
550                                  *   already processed;
551                                  * - decrease ac by optind, to remove the args
552                                  *   already processed and the final filename;
553                                  * - set the last entry in av[] to NULL so
554                                  *   popen() can detect the end of the array;
555                                  * - set optind=ac to let getopt() terminate.
556                                  */
557                                 if (optind == ac)
558                                         errx(EX_USAGE, "no filename argument");
559                                 cmd = optarg;
560                                 av[ac-1] = NULL;
561                                 av += optind - 1;
562                                 ac -= optind;
563                                 optind = ac;
564                                 break;
565
566                         case 'q':
567                                 g_co.do_quiet = 1;
568                                 break;
569
570                         case 'S':
571                                 g_co.show_sets = 1;
572                                 break;
573
574                         default:
575                                 errx(EX_USAGE, "bad arguments, for usage"
576                                      " summary ``ipfw''");
577                         }
578                 }
579         } else {
580                 while ((c = getopt(ac, av, "nq")) != -1) {
581                         switch(c) {
582                         case 'n':
583                                 g_co.test_only = 1;
584                                 break;
585
586                         case 'q':
587                                 g_co.do_quiet = 1;
588                                 break;
589
590                         default:
591                                 errx(EX_USAGE, "bad arguments, for usage"
592                                      " summary ``dnctl''");
593                         }
594                 }
595         }
596
597         if (cmd == NULL && ac != optind + 1)
598                 errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]);
599
600         if ((f = fopen(filename, "r")) == NULL)
601                 err(EX_UNAVAILABLE, "fopen: %s", filename);
602
603         if (cmd != NULL) {                      /* pipe through preprocessor */
604                 int pipedes[2];
605
606                 if (pipe(pipedes) == -1)
607                         err(EX_OSERR, "cannot create pipe");
608
609                 preproc = fork();
610                 if (preproc == -1)
611                         err(EX_OSERR, "cannot fork");
612
613                 if (preproc == 0) {
614                         /*
615                          * Child, will run the preprocessor with the
616                          * file on stdin and the pipe on stdout.
617                          */
618                         if (dup2(fileno(f), 0) == -1
619                             || dup2(pipedes[1], 1) == -1)
620                                 err(EX_OSERR, "dup2()");
621                         fclose(f);
622                         close(pipedes[1]);
623                         close(pipedes[0]);
624                         execvp(cmd, av);
625                         err(EX_OSERR, "execvp(%s) failed", cmd);
626                 } else { /* parent, will reopen f as the pipe */
627                         fclose(f);
628                         close(pipedes[1]);
629                         if ((f = fdopen(pipedes[0], "r")) == NULL) {
630                                 int savederrno = errno;
631
632                                 (void)kill(preproc, SIGTERM);
633                                 errno = savederrno;
634                                 err(EX_OSERR, "fdopen()");
635                         }
636                 }
637         }
638
639         while (fgets(buf, sizeof(buf), f)) {            /* read commands */
640                 char linename[20];
641                 char *args[2];
642
643                 lineno++;
644                 snprintf(linename, sizeof(linename), "Line %d", lineno);
645                 setprogname(linename); /* XXX */
646                 args[0] = progname;
647                 args[1] = buf;
648                 ipfw_main(2, args);
649         }
650         fclose(f);
651         if (cmd != NULL) {
652                 int status;
653
654                 if (waitpid(preproc, &status, 0) == -1)
655                         errx(EX_OSERR, "waitpid()");
656                 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
657                         errx(EX_UNAVAILABLE,
658                             "preprocessor exited with status %d",
659                             WEXITSTATUS(status));
660                 else if (WIFSIGNALED(status))
661                         errx(EX_UNAVAILABLE,
662                             "preprocessor exited with signal %d",
663                             WTERMSIG(status));
664         }
665 }
666
667 int
668 main(int ac, char *av[])
669 {
670 #if defined(_WIN32) && defined(TCC)
671         {
672                 WSADATA wsaData;
673                 int ret=0;
674                 unsigned short wVersionRequested = MAKEWORD(2, 2);
675                 ret = WSAStartup(wVersionRequested, &wsaData);
676                 if (ret != 0) {
677                         /* Tell the user that we could not find a usable */
678                         /* Winsock DLL.                           */
679                         printf("WSAStartup failed with error: %d\n", ret);
680                         return 1;
681                 }
682         }
683 #endif
684
685         if (strcmp(av[0], "dnctl") == 0)
686                 g_co.prog = cmdline_prog_dnctl;
687         else
688                 g_co.prog = cmdline_prog_ipfw;
689
690         /*
691          * If the last argument is an absolute pathname, interpret it
692          * as a file to be preprocessed.
693          */
694
695         if (ac > 1 && av[ac - 1][0] == '/') {
696                 if (access(av[ac - 1], R_OK) == 0)
697                         ipfw_readfile(ac, av);
698                 else
699                         err(EX_USAGE, "pathname: %s", av[ac - 1]);
700         } else {
701                 if (ipfw_main(ac, av)) {
702                         errx(EX_USAGE,
703                             "usage: %s [options]\n"
704                             "do \"%s -h\" or \"man %s\" for details", av[0],
705                             av[0], av[0]);
706                 }
707         }
708         return EX_OK;
709 }