Setting tag iptables-1.3.8-12
[iptables.git] / iptables.c
1 /* Code to take an iptables-style command line and do it. */
2
3 /*
4  * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
5  *
6  * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
7  *                  Paul 'Rusty' Russell <rusty@rustcorp.com.au>
8  *                  Marc Boucher <marc+nf@mbsi.ca>
9  *                  James Morris <jmorris@intercode.com.au>
10  *                  Harald Welte <laforge@gnumonks.org>
11  *                  Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
12  *
13  *      This program is free software; you can redistribute it and/or modify
14  *      it under the terms of the GNU General Public License as published by
15  *      the Free Software Foundation; either version 2 of the License, or
16  *      (at your option) any later version.
17  *
18  *      This program is distributed in the hope that it will be useful,
19  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *      GNU General Public License for more details.
22  *
23  *      You should have received a copy of the GNU General Public License
24  *      along with this program; if not, write to the Free Software
25  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27
28 #include <getopt.h>
29 #include <string.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <dlfcn.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 #include <unistd.h>
39 #include <iptables.h>
40 #include <fcntl.h>
41 #include <sys/wait.h>
42 #include <sys/utsname.h>
43
44 #ifndef TRUE
45 #define TRUE 1
46 #endif
47 #ifndef FALSE
48 #define FALSE 0
49 #endif
50
51 #ifndef PROC_SYS_MODPROBE
52 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
53 #endif
54
55 #define FMT_NUMERIC     0x0001
56 #define FMT_NOCOUNTS    0x0002
57 #define FMT_KILOMEGAGIGA 0x0004
58 #define FMT_OPTIONS     0x0008
59 #define FMT_NOTABLE     0x0010
60 #define FMT_NOTARGET    0x0020
61 #define FMT_VIA         0x0040
62 #define FMT_NONEWLINE   0x0080
63 #define FMT_LINENUMBERS 0x0100
64
65 #define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
66                         | FMT_NUMERIC | FMT_NOTABLE)
67 #define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
68
69
70 #define CMD_NONE                0x0000U
71 #define CMD_INSERT              0x0001U
72 #define CMD_DELETE              0x0002U
73 #define CMD_DELETE_NUM          0x0004U
74 #define CMD_REPLACE             0x0008U
75 #define CMD_APPEND              0x0010U
76 #define CMD_LIST                0x0020U
77 #define CMD_FLUSH               0x0040U
78 #define CMD_ZERO                0x0080U
79 #define CMD_NEW_CHAIN           0x0100U
80 #define CMD_DELETE_CHAIN        0x0200U
81 #define CMD_SET_POLICY          0x0400U
82 #define CMD_RENAME_CHAIN        0x0800U
83 #define NUMBER_OF_CMD   13
84 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
85                                  'N', 'X', 'P', 'E' };
86
87 #define OPTION_OFFSET 256
88
89 #define OPT_NONE        0x00000U
90 #define OPT_NUMERIC     0x00001U
91 #define OPT_SOURCE      0x00002U
92 #define OPT_DESTINATION 0x00004U
93 #define OPT_PROTOCOL    0x00008U
94 #define OPT_JUMP        0x00010U
95 #define OPT_VERBOSE     0x00020U
96 #define OPT_EXPANDED    0x00040U
97 #define OPT_VIANAMEIN   0x00080U
98 #define OPT_VIANAMEOUT  0x00100U
99 #define OPT_FRAGMENT    0x00200U
100 #define OPT_LINENUMBERS 0x00400U
101 #define OPT_COUNTERS    0x00800U
102 #define NUMBER_OF_OPT   12
103 static const char optflags[NUMBER_OF_OPT]
104 = { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '0', 'c'};
105
106 static struct option original_opts[] = {
107         { "append", 1, 0, 'A' },
108         { "delete", 1, 0,  'D' },
109         { "insert", 1, 0,  'I' },
110         { "replace", 1, 0,  'R' },
111         { "list", 2, 0,  'L' },
112         { "flush", 2, 0,  'F' },
113         { "zero", 2, 0,  'Z' },
114         { "new-chain", 1, 0,  'N' },
115         { "delete-chain", 2, 0,  'X' },
116         { "rename-chain", 1, 0,  'E' },
117         { "policy", 1, 0,  'P' },
118         { "source", 1, 0, 's' },
119         { "destination", 1, 0,  'd' },
120         { "src", 1, 0,  's' }, /* synonym */
121         { "dst", 1, 0,  'd' }, /* synonym */
122         { "protocol", 1, 0,  'p' },
123         { "in-interface", 1, 0, 'i' },
124         { "jump", 1, 0, 'j' },
125         { "table", 1, 0, 't' },
126         { "match", 1, 0, 'm' },
127         { "numeric", 0, 0, 'n' },
128         { "out-interface", 1, 0, 'o' },
129         { "verbose", 0, 0, 'v' },
130         { "exact", 0, 0, 'x' },
131         { "fragments", 0, 0, 'f' },
132         { "version", 0, 0, 'V' },
133         { "help", 2, 0, 'h' },
134         { "line-numbers", 0, 0, '0' },
135         { "modprobe", 1, 0, 'M' },
136         { "set-counters", 1, 0, 'c' },
137         { "goto", 1, 0, 'g' },
138         { 0 }
139 };
140
141 /* we need this for iptables-restore.  iptables-restore.c sets line to the
142  * current line of the input file, in order  to give a more precise error
143  * message.  iptables itself doesn't need this, so it is initialized to the
144  * magic number of -1 */
145 int line = -1;
146
147 static struct option *opts = original_opts;
148 static unsigned int global_option_offset = 0;
149
150 /* Table of legal combinations of commands and options.  If any of the
151  * given commands make an option legal, that option is legal (applies to
152  * CMD_LIST and CMD_ZERO only).
153  * Key:
154  *  +  compulsory
155  *  x  illegal
156  *     optional
157  */
158
159 static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
160 /* Well, it's better than "Re: Linux vs FreeBSD" */
161 {
162         /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line -c */
163 /*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
164 /*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x','x'},
165 /*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
166 /*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
167 /*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
168 /*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' ','x'},
169 /*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x'},
170 /*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x'},
171 /*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
172 /*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
173 /*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
174 /*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x'}
175 };
176
177 static int inverse_for_options[NUMBER_OF_OPT] =
178 {
179 /* -n */ 0,
180 /* -s */ IPT_INV_SRCIP,
181 /* -d */ IPT_INV_DSTIP,
182 /* -p */ IPT_INV_PROTO,
183 /* -j */ 0,
184 /* -v */ 0,
185 /* -x */ 0,
186 /* -i */ IPT_INV_VIA_IN,
187 /* -o */ IPT_INV_VIA_OUT,
188 /* -f */ IPT_INV_FRAG,
189 /*--line*/ 0,
190 /* -c */ 0,
191 };
192
193 const char *program_version;
194 const char *program_name;
195 char *lib_dir;
196
197 int kernel_version;
198
199 /* the path to command to load kernel module */
200 const char *modprobe = NULL;
201
202 /* Keeping track of external matches and targets: linked lists.  */
203 struct iptables_match *iptables_matches = NULL;
204 struct iptables_target *iptables_targets = NULL;
205
206 /* Extra debugging from libiptc */
207 extern void dump_entries(const iptc_handle_t handle);
208
209 /* A few hardcoded protocols for 'all' and in case the user has no
210    /etc/protocols */
211 struct pprot {
212         char *name;
213         u_int8_t num;
214 };
215
216 /* Primitive headers... */
217 /* defined in netinet/in.h */
218 #if 0
219 #ifndef IPPROTO_ESP
220 #define IPPROTO_ESP 50
221 #endif
222 #ifndef IPPROTO_AH
223 #define IPPROTO_AH 51
224 #endif
225 #endif
226
227 static const struct pprot chain_protos[] = {
228         { "tcp", IPPROTO_TCP },
229         { "udp", IPPROTO_UDP },
230         { "udplite", IPPROTO_UDPLITE },
231         { "icmp", IPPROTO_ICMP },
232         { "esp", IPPROTO_ESP },
233         { "ah", IPPROTO_AH },
234         { "sctp", IPPROTO_SCTP },
235         { "all", 0 },
236 };
237
238 static char *
239 proto_to_name(u_int8_t proto, int nolookup)
240 {
241         unsigned int i;
242
243         if (proto && !nolookup) {
244                 struct protoent *pent = getprotobynumber(proto);
245                 if (pent)
246                         return pent->p_name;
247         }
248
249         for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
250                 if (chain_protos[i].num == proto)
251                         return chain_protos[i].name;
252
253         return NULL;
254 }
255
256 int
257 service_to_port(const char *name, const char *proto)
258 {
259         struct servent *service;
260
261         if ((service = getservbyname(name, proto)) != NULL)
262                 return ntohs((unsigned short) service->s_port);
263
264         return -1;
265 }
266
267 u_int16_t
268 parse_port(const char *port, const char *proto)
269 {
270         unsigned int portnum;
271
272         if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
273             (portnum = service_to_port(port, proto)) != -1)
274                 return (u_int16_t)portnum;
275
276         exit_error(PARAMETER_PROBLEM,
277                    "invalid port/service `%s' specified", port);
278 }
279
280 enum {
281         IPT_DOTTED_ADDR = 0,
282         IPT_DOTTED_MASK
283 };
284
285 static struct in_addr *
286 __dotted_to_addr(const char *dotted, int type)
287 {
288         static struct in_addr addr;
289         unsigned char *addrp;
290         char *p, *q;
291         unsigned int onebyte;
292         int i;
293         char buf[20];
294
295         /* copy dotted string, because we need to modify it */
296         strncpy(buf, dotted, sizeof(buf) - 1);
297         buf[sizeof(buf) - 1] = '\0';
298         addrp = (unsigned char *) &(addr.s_addr);
299
300         p = buf;
301         for (i = 0; i < 3; i++) {
302                 if ((q = strchr(p, '.')) == NULL) {
303                         if (type == IPT_DOTTED_ADDR) {
304                                 /* autocomplete, this is a network address */
305                                 if (string_to_number(p, 0, 255, &onebyte) == -1)
306                                         return (struct in_addr *) NULL;
307
308                                 addrp[i] = (unsigned char) onebyte;
309                                 while (i < 3)
310                                         addrp[++i] = 0;
311
312                                 return &addr;
313                         } else
314                                 return (struct in_addr *) NULL;
315                 }
316
317                 *q = '\0';
318                 if (string_to_number(p, 0, 255, &onebyte) == -1)
319                         return (struct in_addr *) NULL;
320
321                 addrp[i] = (unsigned char) onebyte;
322                 p = q + 1;
323         }
324
325         /* we've checked 3 bytes, now we check the last one */
326         if (string_to_number(p, 0, 255, &onebyte) == -1)
327                 return (struct in_addr *) NULL;
328
329         addrp[3] = (unsigned char) onebyte;
330
331         return &addr;
332 }
333
334 struct in_addr *
335 dotted_to_addr(const char *dotted)
336 {
337         return __dotted_to_addr(dotted, IPT_DOTTED_ADDR);
338 }
339
340 struct in_addr *
341 dotted_to_mask(const char *dotted)
342 {
343         return __dotted_to_addr(dotted, IPT_DOTTED_MASK);
344 }
345
346 static struct in_addr *
347 network_to_addr(const char *name)
348 {
349         struct netent *net;
350         static struct in_addr addr;
351
352         if ((net = getnetbyname(name)) != NULL) {
353                 if (net->n_addrtype != AF_INET)
354                         return (struct in_addr *) NULL;
355                 addr.s_addr = htonl((unsigned long) net->n_net);
356                 return &addr;
357         }
358
359         return (struct in_addr *) NULL;
360 }
361
362 static void
363 inaddrcpy(struct in_addr *dst, struct in_addr *src)
364 {
365         /* memcpy(dst, src, sizeof(struct in_addr)); */
366         dst->s_addr = src->s_addr;
367 }
368
369 static void free_opts(int reset_offset)
370 {
371         if (opts != original_opts) {
372                 free(opts);
373                 opts = original_opts;
374                 if (reset_offset)
375                         global_option_offset = 0;
376         }
377 }
378
379 void
380 exit_error(enum exittype status, char *msg, ...)
381 {
382         va_list args;
383
384         va_start(args, msg);
385         fprintf(stderr, "%s v%s: ", program_name, program_version);
386         vfprintf(stderr, msg, args);
387         va_end(args);
388         fprintf(stderr, "\n");
389         if (status == PARAMETER_PROBLEM)
390                 exit_tryhelp(status);
391         if (status == VERSION_PROBLEM)
392                 fprintf(stderr,
393                         "Perhaps iptables or your kernel needs to be upgraded.\n");
394         /* On error paths, make sure that we don't leak memory */
395         free_opts(1);
396         exit(status);
397 }
398
399 void
400 exit_tryhelp(int status)
401 {
402         if (line != -1)
403                 fprintf(stderr, "Error occurred at line: %d\n", line);
404         fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
405                         program_name, program_name );
406         free_opts(1);
407         exit(status);
408 }
409
410 void
411 exit_printhelp(struct iptables_rule_match *matches)
412 {
413         struct iptables_rule_match *matchp = NULL;
414         struct iptables_target *t = NULL;
415
416         printf("%s v%s\n\n"
417 "Usage: %s -[AD] chain rule-specification [options]\n"
418 "       %s -[RI] chain rulenum rule-specification [options]\n"
419 "       %s -D chain rulenum [options]\n"
420 "       %s -[LFZ] [chain] [options]\n"
421 "       %s -[NX] chain\n"
422 "       %s -E old-chain-name new-chain-name\n"
423 "       %s -P chain target [options]\n"
424 "       %s -h (print this help information)\n\n",
425                program_name, program_version, program_name, program_name,
426                program_name, program_name, program_name, program_name,
427                program_name, program_name);
428
429         printf(
430 "Commands:\n"
431 "Either long or short options are allowed.\n"
432 "  --append  -A chain           Append to chain\n"
433 "  --delete  -D chain           Delete matching rule from chain\n"
434 "  --delete  -D chain rulenum\n"
435 "                               Delete rule rulenum (1 = first) from chain\n"
436 "  --insert  -I chain [rulenum]\n"
437 "                               Insert in chain as rulenum (default 1=first)\n"
438 "  --replace -R chain rulenum\n"
439 "                               Replace rule rulenum (1 = first) in chain\n"
440 "  --list    -L [chain]         List the rules in a chain or all chains\n"
441 "  --flush   -F [chain]         Delete all rules in  chain or all chains\n"
442 "  --zero    -Z [chain]         Zero counters in chain or all chains\n"
443 "  --new     -N chain           Create a new user-defined chain\n"
444 "  --delete-chain\n"
445 "            -X [chain]         Delete a user-defined chain\n"
446 "  --policy  -P chain target\n"
447 "                               Change policy on chain to target\n"
448 "  --rename-chain\n"
449 "            -E old-chain new-chain\n"
450 "                               Change chain name, (moving any references)\n"
451
452 "Options:\n"
453 "  --proto      -p [!] proto    protocol: by number or name, eg. `tcp'\n"
454 "  --source     -s [!] address[/mask]\n"
455 "                               source specification\n"
456 "  --destination -d [!] address[/mask]\n"
457 "                               destination specification\n"
458 "  --in-interface -i [!] input name[+]\n"
459 "                               network interface name ([+] for wildcard)\n"
460 "  --jump       -j target\n"
461 "                               target for rule (may load target extension)\n"
462 #ifdef IPT_F_GOTO
463 "  --goto      -g chain\n"
464 "                              jump to chain with no return\n"
465 #endif
466 "  --match      -m match\n"
467 "                               extended match (may load extension)\n"
468 "  --numeric    -n              numeric output of addresses and ports\n"
469 "  --out-interface -o [!] output name[+]\n"
470 "                               network interface name ([+] for wildcard)\n"
471 "  --table      -t table        table to manipulate (default: `filter')\n"
472 "  --verbose    -v              verbose mode\n"
473 "  --line-numbers               print line numbers when listing\n"
474 "  --exact      -x              expand numbers (display exact values)\n"
475 "[!] --fragment -f              match second or further fragments only\n"
476 "  --modprobe=<command>         try to insert modules using this command\n"
477 "  --set-counters PKTS BYTES    set the counter during insert/append\n"
478 "[!] --version  -V              print package version.\n");
479
480         /* Print out any special helps. A user might like to be able
481            to add a --help to the commandline, and see expected
482            results. So we call help for all specified matches & targets */
483         for (t = iptables_targets; t ;t = t->next) {
484                 if (t->used) {
485                         printf("\n");
486                         t->help();
487                 }
488         }
489         for (matchp = matches; matchp; matchp = matchp->next) {
490                 printf("\n");
491                 matchp->match->help();
492         }
493         exit(0);
494 }
495
496 static void
497 generic_opt_check(int command, int options)
498 {
499         int i, j, legal = 0;
500
501         /* Check that commands are valid with options.  Complicated by the
502          * fact that if an option is legal with *any* command given, it is
503          * legal overall (ie. -z and -l).
504          */
505         for (i = 0; i < NUMBER_OF_OPT; i++) {
506                 legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
507
508                 for (j = 0; j < NUMBER_OF_CMD; j++) {
509                         if (!(command & (1<<j)))
510                                 continue;
511
512                         if (!(options & (1<<i))) {
513                                 if (commands_v_options[j][i] == '+')
514                                         exit_error(PARAMETER_PROBLEM,
515                                                    "You need to supply the `-%c' "
516                                                    "option for this command\n",
517                                                    optflags[i]);
518                         } else {
519                                 if (commands_v_options[j][i] != 'x')
520                                         legal = 1;
521                                 else if (legal == 0)
522                                         legal = -1;
523                         }
524                 }
525                 if (legal == -1)
526                         exit_error(PARAMETER_PROBLEM,
527                                    "Illegal option `-%c' with this command\n",
528                                    optflags[i]);
529         }
530 }
531
532 static char
533 opt2char(int option)
534 {
535         const char *ptr;
536         for (ptr = optflags; option > 1; option >>= 1, ptr++);
537
538         return *ptr;
539 }
540
541 static char
542 cmd2char(int option)
543 {
544         const char *ptr;
545         for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
546
547         return *ptr;
548 }
549
550 static void
551 add_command(unsigned int *cmd, const int newcmd, const int othercmds, 
552             int invert)
553 {
554         if (invert)
555                 exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
556         if (*cmd & (~othercmds))
557                 exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
558                            cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
559         *cmd |= newcmd;
560 }
561
562 int
563 check_inverse(const char option[], int *invert, int *optind, int argc)
564 {
565         if (option && strcmp(option, "!") == 0) {
566                 if (*invert)
567                         exit_error(PARAMETER_PROBLEM,
568                                    "Multiple `!' flags not allowed");
569                 *invert = TRUE;
570                 if (optind) {
571                         *optind = *optind+1;
572                         if (argc && *optind > argc)
573                                 exit_error(PARAMETER_PROBLEM,
574                                            "no argument following `!'");
575                 }
576
577                 return TRUE;
578         }
579         return FALSE;
580 }
581
582 static void *
583 fw_calloc(size_t count, size_t size)
584 {
585         void *p;
586
587         if ((p = calloc(count, size)) == NULL) {
588                 perror("iptables: calloc failed");
589                 exit(1);
590         }
591         return p;
592 }
593
594 static void *
595 fw_malloc(size_t size)
596 {
597         void *p;
598
599         if ((p = malloc(size)) == NULL) {
600                 perror("iptables: malloc failed");
601                 exit(1);
602         }
603         return p;
604 }
605
606 static struct in_addr *
607 host_to_addr(const char *name, unsigned int *naddr)
608 {
609         struct hostent *host;
610         struct in_addr *addr;
611         unsigned int i;
612
613         *naddr = 0;
614         if ((host = gethostbyname(name)) != NULL) {
615                 if (host->h_addrtype != AF_INET ||
616                     host->h_length != sizeof(struct in_addr))
617                         return (struct in_addr *) NULL;
618
619                 while (host->h_addr_list[*naddr] != (char *) NULL)
620                         (*naddr)++;
621                 addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr);
622                 for (i = 0; i < *naddr; i++)
623                         inaddrcpy(&(addr[i]),
624                                   (struct in_addr *) host->h_addr_list[i]);
625                 return addr;
626         }
627
628         return (struct in_addr *) NULL;
629 }
630
631 static char *
632 addr_to_host(const struct in_addr *addr)
633 {
634         struct hostent *host;
635
636         if ((host = gethostbyaddr((char *) addr,
637                                   sizeof(struct in_addr), AF_INET)) != NULL)
638                 return (char *) host->h_name;
639
640         return (char *) NULL;
641 }
642
643 /*
644  *      All functions starting with "parse" should succeed, otherwise
645  *      the program fails.
646  *      Most routines return pointers to static data that may change
647  *      between calls to the same or other routines with a few exceptions:
648  *      "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
649  *      return global static data.
650 */
651
652 static struct in_addr *
653 parse_hostnetwork(const char *name, unsigned int *naddrs)
654 {
655         struct in_addr *addrp, *addrptmp;
656
657         if ((addrptmp = dotted_to_addr(name)) != NULL ||
658             (addrptmp = network_to_addr(name)) != NULL) {
659                 addrp = fw_malloc(sizeof(struct in_addr));
660                 inaddrcpy(addrp, addrptmp);
661                 *naddrs = 1;
662                 return addrp;
663         }
664         if ((addrp = host_to_addr(name, naddrs)) != NULL)
665                 return addrp;
666
667         exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
668 }
669
670 static struct in_addr *
671 parse_mask(char *mask)
672 {
673         static struct in_addr maskaddr;
674         struct in_addr *addrp;
675         unsigned int bits;
676
677         if (mask == NULL) {
678                 /* no mask at all defaults to 32 bits */
679                 maskaddr.s_addr = 0xFFFFFFFF;
680                 return &maskaddr;
681         }
682         if ((addrp = dotted_to_mask(mask)) != NULL)
683                 /* dotted_to_addr already returns a network byte order addr */
684                 return addrp;
685         if (string_to_number(mask, 0, 32, &bits) == -1)
686                 exit_error(PARAMETER_PROBLEM,
687                            "invalid mask `%s' specified", mask);
688         if (bits != 0) {
689                 maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
690                 return &maskaddr;
691         }
692
693         maskaddr.s_addr = 0L;
694         return &maskaddr;
695 }
696
697 void
698 parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
699                       struct in_addr *maskp, unsigned int *naddrs)
700 {
701         struct in_addr *addrp;
702         char buf[256];
703         char *p;
704         int i, j, k, n;
705
706         strncpy(buf, name, sizeof(buf) - 1);
707         buf[sizeof(buf) - 1] = '\0';
708         if ((p = strrchr(buf, '/')) != NULL) {
709                 *p = '\0';
710                 addrp = parse_mask(p + 1);
711         } else
712                 addrp = parse_mask(NULL);
713         inaddrcpy(maskp, addrp);
714
715         /* if a null mask is given, the name is ignored, like in "any/0" */
716         if (maskp->s_addr == 0L)
717                 strcpy(buf, "0.0.0.0");
718
719         addrp = *addrpp = parse_hostnetwork(buf, naddrs);
720         n = *naddrs;
721         for (i = 0, j = 0; i < n; i++) {
722                 addrp[j++].s_addr &= maskp->s_addr;
723                 for (k = 0; k < j - 1; k++) {
724                         if (addrp[k].s_addr == addrp[j - 1].s_addr) {
725                                 (*naddrs)--;
726                                 j--;
727                                 break;
728                         }
729                 }
730         }
731 }
732
733 struct iptables_match *
734 find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches)
735 {
736         struct iptables_match *ptr;
737
738         for (ptr = iptables_matches; ptr; ptr = ptr->next) {
739                 if (strcmp(name, ptr->name) == 0) {
740                         struct iptables_match *clone;
741                         
742                         /* First match of this type: */
743                         if (ptr->m == NULL)
744                                 break;
745
746                         /* Second and subsequent clones */
747                         clone = fw_malloc(sizeof(struct iptables_match));
748                         memcpy(clone, ptr, sizeof(struct iptables_match));
749                         clone->mflags = 0;
750                         /* This is a clone: */
751                         clone->next = clone;
752
753                         ptr = clone;
754                         break;
755                 }
756         }               
757
758 #ifndef NO_SHARED_LIBS
759         if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
760                 char path[strlen(lib_dir) + sizeof("/libipt_.so")
761                          + strlen(name)];
762                 sprintf(path, "%s/libipt_%s.so", lib_dir, name);
763                 if (dlopen(path, RTLD_NOW)) {
764                         /* Found library.  If it didn't register itself,
765                            maybe they specified target as match. */
766                         ptr = find_match(name, DONT_LOAD, NULL);
767
768                         if (!ptr)
769                                 exit_error(PARAMETER_PROBLEM,
770                                            "Couldn't load match `%s'\n",
771                                            name);
772                 } else if (tryload == LOAD_MUST_SUCCEED)
773                         exit_error(PARAMETER_PROBLEM,
774                                    "Couldn't load match `%s':%s\n",
775                                    name, dlerror());
776         }
777 #else
778         if (ptr && !ptr->loaded) {
779                 if (tryload != DONT_LOAD)
780                         ptr->loaded = 1;
781                 else
782                         ptr = NULL;
783         }
784         if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
785                 exit_error(PARAMETER_PROBLEM,
786                            "Couldn't find match `%s'\n", name);
787         }
788 #endif
789
790         if (ptr && matches) {
791                 struct iptables_rule_match **i;
792                 struct iptables_rule_match *newentry;
793
794                 newentry = fw_malloc(sizeof(struct iptables_rule_match));
795
796                 for (i = matches; *i; i = &(*i)->next) {
797                         if (strcmp(name, (*i)->match->name) == 0)
798                                 (*i)->completed = 1;
799                 }
800                 newentry->match = ptr;
801                 newentry->completed = 0;
802                 newentry->next = NULL;
803                 *i = newentry;
804         }
805
806         return ptr;
807 }
808
809 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
810 static struct iptables_match *
811 find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches)
812 {
813         unsigned int proto;
814
815         if (string_to_number(pname, 0, 255, &proto) != -1) {
816                 char *protoname = proto_to_name(proto, nolookup);
817
818                 if (protoname)
819                         return find_match(protoname, tryload, matches);
820         } else
821                 return find_match(pname, tryload, matches);
822
823         return NULL;
824 }
825
826 u_int16_t
827 parse_protocol(const char *s)
828 {
829         unsigned int proto;
830
831         if (string_to_number(s, 0, 255, &proto) == -1) {
832                 struct protoent *pent;
833
834                 /* first deal with the special case of 'all' to prevent
835                  * people from being able to redefine 'all' in nsswitch
836                  * and/or provoke expensive [not working] ldap/nis/... 
837                  * lookups */
838                 if (!strcmp(s, "all"))
839                         return 0;
840
841                 if ((pent = getprotobyname(s)))
842                         proto = pent->p_proto;
843                 else {
844                         unsigned int i;
845                         for (i = 0;
846                              i < sizeof(chain_protos)/sizeof(struct pprot);
847                              i++) {
848                                 if (strcmp(s, chain_protos[i].name) == 0) {
849                                         proto = chain_protos[i].num;
850                                         break;
851                                 }
852                         }
853                         if (i == sizeof(chain_protos)/sizeof(struct pprot))
854                                 exit_error(PARAMETER_PROBLEM,
855                                            "unknown protocol `%s' specified",
856                                            s);
857                 }
858         }
859
860         return (u_int16_t)proto;
861 }
862
863 void parse_interface(const char *arg, char *vianame, unsigned char *mask)
864 {
865         int vialen = strlen(arg);
866         unsigned int i;
867
868         memset(mask, 0, IFNAMSIZ);
869         memset(vianame, 0, IFNAMSIZ);
870
871         if (vialen + 1 > IFNAMSIZ)
872                 exit_error(PARAMETER_PROBLEM,
873                            "interface name `%s' must be shorter than IFNAMSIZ"
874                            " (%i)", arg, IFNAMSIZ-1);
875
876         strcpy(vianame, arg);
877         if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
878                 memset(mask, 0, IFNAMSIZ);
879         else if (vianame[vialen - 1] == '+') {
880                 memset(mask, 0xFF, vialen - 1);
881                 memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
882                 /* Don't remove `+' here! -HW */
883         } else {
884                 /* Include nul-terminator in match */
885                 memset(mask, 0xFF, vialen + 1);
886                 memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
887                 for (i = 0; vianame[i]; i++) {
888                         if (vianame[i] == ':' ||
889                             vianame[i] == '!' ||
890                             vianame[i] == '*') {
891                                 printf("Warning: weird character in interface"
892                                        " `%s' (No aliases, :, ! or *).\n",
893                                        vianame);
894                                 break;
895                         }
896                 }
897         }
898 }
899
900 /* Can't be zero. */
901 static int
902 parse_rulenumber(const char *rule)
903 {
904         unsigned int rulenum;
905
906         if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
907                 exit_error(PARAMETER_PROBLEM,
908                            "Invalid rule number `%s'", rule);
909
910         return rulenum;
911 }
912
913 static const char *
914 parse_target(const char *targetname)
915 {
916         const char *ptr;
917
918         if (strlen(targetname) < 1)
919                 exit_error(PARAMETER_PROBLEM,
920                            "Invalid target name (too short)");
921
922         if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
923                 exit_error(PARAMETER_PROBLEM,
924                            "Invalid target name `%s' (%u chars max)",
925                            targetname, (unsigned int)sizeof(ipt_chainlabel)-1);
926
927         for (ptr = targetname; *ptr; ptr++)
928                 if (isspace(*ptr))
929                         exit_error(PARAMETER_PROBLEM,
930                                    "Invalid target name `%s'", targetname);
931         return targetname;
932 }
933
934 static char *
935 addr_to_network(const struct in_addr *addr)
936 {
937         struct netent *net;
938
939         if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
940                 return (char *) net->n_name;
941
942         return (char *) NULL;
943 }
944
945 char *
946 addr_to_dotted(const struct in_addr *addrp)
947 {
948         static char buf[20];
949         const unsigned char *bytep;
950
951         bytep = (const unsigned char *) &(addrp->s_addr);
952         sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
953         return buf;
954 }
955
956 char *
957 addr_to_anyname(const struct in_addr *addr)
958 {
959         char *name;
960
961         if ((name = addr_to_host(addr)) != NULL ||
962             (name = addr_to_network(addr)) != NULL)
963                 return name;
964
965         return addr_to_dotted(addr);
966 }
967
968 char *
969 mask_to_dotted(const struct in_addr *mask)
970 {
971         int i;
972         static char buf[20];
973         u_int32_t maskaddr, bits;
974
975         maskaddr = ntohl(mask->s_addr);
976
977         if (maskaddr == 0xFFFFFFFFL)
978                 /* we don't want to see "/32" */
979                 return "";
980
981         i = 32;
982         bits = 0xFFFFFFFEL;
983         while (--i >= 0 && maskaddr != bits)
984                 bits <<= 1;
985         if (i >= 0)
986                 sprintf(buf, "/%d", i);
987         else
988                 /* mask was not a decent combination of 1's and 0's */
989                 sprintf(buf, "/%s", addr_to_dotted(mask));
990
991         return buf;
992 }
993
994 int
995 string_to_number_ll(const char *s, unsigned long long min, unsigned long long max,
996                  unsigned long long *ret)
997 {
998         unsigned long long number;
999         char *end;
1000
1001         /* Handle hex, octal, etc. */
1002         errno = 0;
1003         number = strtoull(s, &end, 0);
1004         if (*end == '\0' && end != s) {
1005                 /* we parsed a number, let's see if we want this */
1006                 if (errno != ERANGE && min <= number && (!max || number <= max)) {
1007                         *ret = number;
1008                         return 0;
1009                 }
1010         }
1011         return -1;
1012 }
1013
1014 int
1015 string_to_number_l(const char *s, unsigned long min, unsigned long max,
1016                  unsigned long *ret)
1017 {
1018         int result;
1019         unsigned long long number;
1020
1021         result = string_to_number_ll(s, min, max, &number);
1022         *ret = (unsigned long)number;
1023
1024         return result;
1025 }
1026
1027 int string_to_number(const char *s, unsigned int min, unsigned int max,
1028                 unsigned int *ret)
1029 {
1030         int result;
1031         unsigned long number;
1032
1033         result = string_to_number_l(s, min, max, &number);
1034         *ret = (unsigned int)number;
1035
1036         return result;
1037 }
1038
1039 static void
1040 set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
1041            int invert)
1042 {
1043         if (*options & option)
1044                 exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
1045                            opt2char(option));
1046         *options |= option;
1047
1048         if (invert) {
1049                 unsigned int i;
1050                 for (i = 0; 1 << i != option; i++);
1051
1052                 if (!inverse_for_options[i])
1053                         exit_error(PARAMETER_PROBLEM,
1054                                    "cannot have ! before -%c",
1055                                    opt2char(option));
1056                 *invflg |= inverse_for_options[i];
1057         }
1058 }
1059
1060 struct iptables_target *
1061 find_target(const char *name, enum ipt_tryload tryload)
1062 {
1063         struct iptables_target *ptr;
1064
1065         /* Standard target? */
1066         if (strcmp(name, "") == 0
1067             || strcmp(name, IPTC_LABEL_ACCEPT) == 0
1068             || strcmp(name, IPTC_LABEL_DROP) == 0
1069             || strcmp(name, IPTC_LABEL_QUEUE) == 0
1070             || strcmp(name, IPTC_LABEL_RETURN) == 0)
1071                 name = "standard";
1072
1073         for (ptr = iptables_targets; ptr; ptr = ptr->next) {
1074                 if (strcmp(name, ptr->name) == 0)
1075                         break;
1076         }
1077
1078 #ifndef NO_SHARED_LIBS
1079         if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
1080                 char path[strlen(lib_dir) + sizeof("/libipt_.so")
1081                          + strlen(name)];
1082                 sprintf(path, "%s/libipt_%s.so", lib_dir, name);
1083                 if (dlopen(path, RTLD_NOW)) {
1084                         /* Found library.  If it didn't register itself,
1085                            maybe they specified match as a target. */
1086                         ptr = find_target(name, DONT_LOAD);
1087                         if (!ptr)
1088                                 exit_error(PARAMETER_PROBLEM,
1089                                            "Couldn't load target `%s'\n",
1090                                            name);
1091                 } else if (tryload == LOAD_MUST_SUCCEED)
1092                         exit_error(PARAMETER_PROBLEM,
1093                                    "Couldn't load target `%s':%s\n",
1094                                    name, dlerror());
1095         }
1096 #else
1097         if (ptr && !ptr->loaded) {
1098                 if (tryload != DONT_LOAD)
1099                         ptr->loaded = 1;
1100                 else
1101                         ptr = NULL;
1102         }
1103         if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
1104                 exit_error(PARAMETER_PROBLEM,
1105                            "Couldn't find target `%s'\n", name);
1106         }
1107 #endif
1108
1109         if (ptr)
1110                 ptr->used = 1;
1111
1112         return ptr;
1113 }
1114
1115 static struct option *
1116 merge_options(struct option *oldopts, const struct option *newopts,
1117               unsigned int *option_offset)
1118 {
1119         unsigned int num_old, num_new, i;
1120         struct option *merge;
1121
1122         for (num_old = 0; oldopts[num_old].name; num_old++);
1123         for (num_new = 0; newopts[num_new].name; num_new++);
1124
1125         global_option_offset += OPTION_OFFSET;
1126         *option_offset = global_option_offset;
1127
1128         merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
1129         memcpy(merge, oldopts, num_old * sizeof(struct option));
1130         free_opts(0); /* Release previous options merged if any */
1131         for (i = 0; i < num_new; i++) {
1132                 merge[num_old + i] = newopts[i];
1133                 merge[num_old + i].val += *option_offset;
1134         }
1135         memset(merge + num_old + num_new, 0, sizeof(struct option));
1136
1137         return merge;
1138 }
1139
1140 static int compatible_revision(const char *name, u_int8_t revision, int opt)
1141 {
1142         struct ipt_get_revision rev;
1143         socklen_t s = sizeof(rev);
1144         int max_rev, sockfd;
1145
1146         sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
1147         if (sockfd < 0) {
1148                 fprintf(stderr, "Could not open socket to kernel: %s\n",
1149                         strerror(errno));
1150                 exit(1);
1151         }
1152
1153         load_iptables_ko(modprobe, 1);
1154
1155         strcpy(rev.name, name);
1156         rev.revision = revision;
1157
1158         max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
1159         if (max_rev < 0) {
1160                 /* Definitely don't support this? */
1161                 if (errno == EPROTONOSUPPORT) {
1162                         close(sockfd);
1163                         return 0;
1164                 } else if (errno == ENOPROTOOPT) {
1165                         close(sockfd);
1166                         /* Assume only revision 0 support (old kernel) */
1167                         return (revision == 0);
1168                 } else {
1169                         fprintf(stderr, "getsockopt failed strangely: %s\n",
1170                                 strerror(errno));
1171                         exit(1);
1172                 }
1173         }
1174         close(sockfd);
1175         return 1;
1176 }
1177
1178 static int compatible_match_revision(const char *name, u_int8_t revision)
1179 {
1180         return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
1181 }
1182
1183 static int compatible_target_revision(const char *name, u_int8_t revision)
1184 {
1185         return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
1186 }
1187
1188 void
1189 register_match(struct iptables_match *me)
1190 {
1191         struct iptables_match **i, *old;
1192
1193         if (strcmp(me->version, program_version) != 0) {
1194                 fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
1195                         program_name, me->name, me->version, program_version);
1196                 exit(1);
1197         }
1198
1199         /* Revision field stole a char from name. */
1200         if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
1201                 fprintf(stderr, "%s: target `%s' has invalid name\n",
1202                         program_name, me->name);
1203                 exit(1);
1204         }
1205
1206         old = find_match(me->name, DURING_LOAD, NULL);
1207         if (old) {
1208                 if (old->revision == me->revision) {
1209                         fprintf(stderr,
1210                                 "%s: match `%s' already registered.\n",
1211                                 program_name, me->name);
1212                         exit(1);
1213                 }
1214
1215                 /* Now we have two (or more) options, check compatibility. */
1216                 if (compatible_match_revision(old->name, old->revision)
1217                     && old->revision > me->revision)
1218                         return;
1219
1220                 /* Replace if compatible. */
1221                 if (!compatible_match_revision(me->name, me->revision))
1222                         return;
1223
1224                 /* Delete old one. */
1225                 for (i = &iptables_matches; *i!=old; i = &(*i)->next);
1226                 *i = old->next;
1227         }
1228
1229         if (me->size != IPT_ALIGN(me->size)) {
1230                 fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
1231                         program_name, me->name, (unsigned int)me->size);
1232                 exit(1);
1233         }
1234
1235         /* Append to list. */
1236         for (i = &iptables_matches; *i; i = &(*i)->next);
1237         me->next = NULL;
1238         *i = me;
1239
1240         me->m = NULL;
1241         me->mflags = 0;
1242 }
1243
1244 void
1245 register_target(struct iptables_target *me)
1246 {
1247         struct iptables_target *old;
1248
1249         if (strcmp(me->version, program_version) != 0) {
1250                 fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
1251                         program_name, me->name, me->version, program_version);
1252                 exit(1);
1253         }
1254
1255         /* Revision field stole a char from name. */
1256         if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
1257                 fprintf(stderr, "%s: target `%s' has invalid name\n",
1258                         program_name, me->name);
1259                 exit(1);
1260         }
1261
1262         old = find_target(me->name, DURING_LOAD);
1263         if (old) {
1264                 struct iptables_target **i;
1265
1266                 if (old->revision == me->revision) {
1267                         fprintf(stderr,
1268                                 "%s: target `%s' already registered.\n",
1269                                 program_name, me->name);
1270                         exit(1);
1271                 }
1272
1273                 /* Now we have two (or more) options, check compatibility. */
1274                 if (compatible_target_revision(old->name, old->revision)
1275                     && old->revision > me->revision)
1276                         return;
1277
1278                 /* Replace if compatible. */
1279                 if (!compatible_target_revision(me->name, me->revision))
1280                         return;
1281
1282                 /* Delete old one. */
1283                 for (i = &iptables_targets; *i!=old; i = &(*i)->next);
1284                 *i = old->next;
1285         }
1286
1287         if (me->size != IPT_ALIGN(me->size)) {
1288                 fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
1289                         program_name, me->name, (unsigned int)me->size);
1290                 exit(1);
1291         }
1292
1293         /* Prepend to list. */
1294         me->next = iptables_targets;
1295         iptables_targets = me;
1296         me->t = NULL;
1297         me->tflags = 0;
1298 }
1299
1300 static void
1301 print_num(u_int64_t number, unsigned int format)
1302 {
1303         if (format & FMT_KILOMEGAGIGA) {
1304                 if (number > 99999) {
1305                         number = (number + 500) / 1000;
1306                         if (number > 9999) {
1307                                 number = (number + 500) / 1000;
1308                                 if (number > 9999) {
1309                                         number = (number + 500) / 1000;
1310                                         if (number > 9999) {
1311                                                 number = (number + 500) / 1000;
1312                                                 printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
1313                                         }
1314                                         else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
1315                                 }
1316                                 else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
1317                         } else
1318                                 printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
1319                 } else
1320                         printf(FMT("%5llu ","%llu "), (unsigned long long)number);
1321         } else
1322                 printf(FMT("%8llu ","%llu "), (unsigned long long)number);
1323 }
1324
1325
1326 static void
1327 print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
1328 {
1329         struct ipt_counters counters;
1330         const char *pol = iptc_get_policy(chain, &counters, handle);
1331         printf("Chain %s", chain);
1332         if (pol) {
1333                 printf(" (policy %s", pol);
1334                 if (!(format & FMT_NOCOUNTS)) {
1335                         fputc(' ', stdout);
1336                         print_num(counters.pcnt, (format|FMT_NOTABLE));
1337                         fputs("packets, ", stdout);
1338                         print_num(counters.bcnt, (format|FMT_NOTABLE));
1339                         fputs("bytes", stdout);
1340                 }
1341                 printf(")\n");
1342         } else {
1343                 unsigned int refs;
1344                 if (!iptc_get_references(&refs, chain, handle))
1345                         printf(" (ERROR obtaining refs)\n");
1346                 else
1347                         printf(" (%u references)\n", refs);
1348         }
1349
1350         if (format & FMT_LINENUMBERS)
1351                 printf(FMT("%-4s ", "%s "), "num");
1352         if (!(format & FMT_NOCOUNTS)) {
1353                 if (format & FMT_KILOMEGAGIGA) {
1354                         printf(FMT("%5s ","%s "), "pkts");
1355                         printf(FMT("%5s ","%s "), "bytes");
1356                 } else {
1357                         printf(FMT("%8s ","%s "), "pkts");
1358                         printf(FMT("%10s ","%s "), "bytes");
1359                 }
1360         }
1361         if (!(format & FMT_NOTARGET))
1362                 printf(FMT("%-9s ","%s "), "target");
1363         fputs(" prot ", stdout);
1364         if (format & FMT_OPTIONS)
1365                 fputs("opt", stdout);
1366         if (format & FMT_VIA) {
1367                 printf(FMT(" %-6s ","%s "), "in");
1368                 printf(FMT("%-6s ","%s "), "out");
1369         }
1370         printf(FMT(" %-19s ","%s "), "source");
1371         printf(FMT(" %-19s "," %s "), "destination");
1372         printf("\n");
1373 }
1374
1375
1376 static int
1377 print_match(const struct ipt_entry_match *m,
1378             const struct ipt_ip *ip,
1379             int numeric)
1380 {
1381         struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL);
1382
1383         if (match) {
1384                 if (match->print)
1385                         match->print(ip, m, numeric);
1386                 else
1387                         printf("%s ", match->name);
1388         } else {
1389                 if (m->u.user.name[0])
1390                         printf("UNKNOWN match `%s' ", m->u.user.name);
1391         }
1392         /* Don't stop iterating. */
1393         return 0;
1394 }
1395
1396 /* e is called `fw' here for hysterical raisins */
1397 static void
1398 print_firewall(const struct ipt_entry *fw,
1399                const char *targname,
1400                unsigned int num,
1401                unsigned int format,
1402                const iptc_handle_t handle)
1403 {
1404         struct iptables_target *target = NULL;
1405         const struct ipt_entry_target *t;
1406         u_int8_t flags;
1407         char buf[BUFSIZ];
1408
1409         if (!iptc_is_chain(targname, handle))
1410                 target = find_target(targname, TRY_LOAD);
1411         else
1412                 target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
1413
1414         t = ipt_get_target((struct ipt_entry *)fw);
1415         flags = fw->ip.flags;
1416
1417         if (format & FMT_LINENUMBERS)
1418                 printf(FMT("%-4u ", "%u "), num+1);
1419
1420         if (!(format & FMT_NOCOUNTS)) {
1421                 print_num(fw->counters.pcnt, format);
1422                 print_num(fw->counters.bcnt, format);
1423         }
1424
1425         if (!(format & FMT_NOTARGET))
1426                 printf(FMT("%-9s ", "%s "), targname);
1427
1428         fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
1429         {
1430                 char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
1431                 if (pname)
1432                         printf(FMT("%-5s", "%s "), pname);
1433                 else
1434                         printf(FMT("%-5hu", "%hu "), fw->ip.proto);
1435         }
1436
1437         if (format & FMT_OPTIONS) {
1438                 if (format & FMT_NOTABLE)
1439                         fputs("opt ", stdout);
1440                 fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
1441                 fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
1442                 fputc(' ', stdout);
1443         }
1444
1445         if (format & FMT_VIA) {
1446                 char iface[IFNAMSIZ+2];
1447
1448                 if (fw->ip.invflags & IPT_INV_VIA_IN) {
1449                         iface[0] = '!';
1450                         iface[1] = '\0';
1451                 }
1452                 else iface[0] = '\0';
1453
1454                 if (fw->ip.iniface[0] != '\0') {
1455                         strcat(iface, fw->ip.iniface);
1456                 }
1457                 else if (format & FMT_NUMERIC) strcat(iface, "*");
1458                 else strcat(iface, "any");
1459                 printf(FMT(" %-6s ","in %s "), iface);
1460
1461                 if (fw->ip.invflags & IPT_INV_VIA_OUT) {
1462                         iface[0] = '!';
1463                         iface[1] = '\0';
1464                 }
1465                 else iface[0] = '\0';
1466
1467                 if (fw->ip.outiface[0] != '\0') {
1468                         strcat(iface, fw->ip.outiface);
1469                 }
1470                 else if (format & FMT_NUMERIC) strcat(iface, "*");
1471                 else strcat(iface, "any");
1472                 printf(FMT("%-6s ","out %s "), iface);
1473         }
1474
1475         fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
1476         if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
1477                 printf(FMT("%-19s ","%s "), "anywhere");
1478         else {
1479                 if (format & FMT_NUMERIC)
1480                         sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
1481                 else
1482                         sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
1483                 strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
1484                 printf(FMT("%-19s ","%s "), buf);
1485         }
1486
1487         fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
1488         if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
1489                 printf(FMT("%-19s ","-> %s"), "anywhere");
1490         else {
1491                 if (format & FMT_NUMERIC)
1492                         sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
1493                 else
1494                         sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
1495                 strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
1496                 printf(FMT("%-19s ","-> %s"), buf);
1497         }
1498
1499         if (format & FMT_NOTABLE)
1500                 fputs("  ", stdout);
1501
1502 #ifdef IPT_F_GOTO
1503         if(fw->ip.flags & IPT_F_GOTO)
1504                 printf("[goto] ");
1505 #endif
1506
1507         IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
1508
1509         if (target) {
1510                 if (target->print)
1511                         /* Print the target information. */
1512                         target->print(&fw->ip, t, format & FMT_NUMERIC);
1513         } else if (t->u.target_size != sizeof(*t))
1514                 printf("[%u bytes of unknown target data] ",
1515                        (unsigned int)(t->u.target_size - sizeof(*t)));
1516
1517         if (!(format & FMT_NONEWLINE))
1518                 fputc('\n', stdout);
1519 }
1520
1521 static void
1522 print_firewall_line(const struct ipt_entry *fw,
1523                     const iptc_handle_t h)
1524 {
1525         struct ipt_entry_target *t;
1526
1527         t = ipt_get_target((struct ipt_entry *)fw);
1528         print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
1529 }
1530
1531 static int
1532 append_entry(const ipt_chainlabel chain,
1533              struct ipt_entry *fw,
1534              unsigned int nsaddrs,
1535              const struct in_addr saddrs[],
1536              unsigned int ndaddrs,
1537              const struct in_addr daddrs[],
1538              int verbose,
1539              iptc_handle_t *handle)
1540 {
1541         unsigned int i, j;
1542         int ret = 1;
1543
1544         for (i = 0; i < nsaddrs; i++) {
1545                 fw->ip.src.s_addr = saddrs[i].s_addr;
1546                 for (j = 0; j < ndaddrs; j++) {
1547                         fw->ip.dst.s_addr = daddrs[j].s_addr;
1548                         if (verbose)
1549                                 print_firewall_line(fw, *handle);
1550                         ret &= iptc_append_entry(chain, fw, handle);
1551                 }
1552         }
1553
1554         return ret;
1555 }
1556
1557 static int
1558 replace_entry(const ipt_chainlabel chain,
1559               struct ipt_entry *fw,
1560               unsigned int rulenum,
1561               const struct in_addr *saddr,
1562               const struct in_addr *daddr,
1563               int verbose,
1564               iptc_handle_t *handle)
1565 {
1566         fw->ip.src.s_addr = saddr->s_addr;
1567         fw->ip.dst.s_addr = daddr->s_addr;
1568
1569         if (verbose)
1570                 print_firewall_line(fw, *handle);
1571         return iptc_replace_entry(chain, fw, rulenum, handle);
1572 }
1573
1574 static int
1575 insert_entry(const ipt_chainlabel chain,
1576              struct ipt_entry *fw,
1577              unsigned int rulenum,
1578              unsigned int nsaddrs,
1579              const struct in_addr saddrs[],
1580              unsigned int ndaddrs,
1581              const struct in_addr daddrs[],
1582              int verbose,
1583              iptc_handle_t *handle)
1584 {
1585         unsigned int i, j;
1586         int ret = 1;
1587
1588         for (i = 0; i < nsaddrs; i++) {
1589                 fw->ip.src.s_addr = saddrs[i].s_addr;
1590                 for (j = 0; j < ndaddrs; j++) {
1591                         fw->ip.dst.s_addr = daddrs[j].s_addr;
1592                         if (verbose)
1593                                 print_firewall_line(fw, *handle);
1594                         ret &= iptc_insert_entry(chain, fw, rulenum, handle);
1595                 }
1596         }
1597
1598         return ret;
1599 }
1600
1601 static unsigned char *
1602 make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches)
1603 {
1604         /* Establish mask for comparison */
1605         unsigned int size;
1606         struct iptables_rule_match *matchp;
1607         unsigned char *mask, *mptr;
1608
1609         size = sizeof(struct ipt_entry);
1610         for (matchp = matches; matchp; matchp = matchp->next)
1611                 size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
1612
1613         mask = fw_calloc(1, size
1614                          + IPT_ALIGN(sizeof(struct ipt_entry_target))
1615                          + iptables_targets->size);
1616
1617         memset(mask, 0xFF, sizeof(struct ipt_entry));
1618         mptr = mask + sizeof(struct ipt_entry);
1619
1620         for (matchp = matches; matchp; matchp = matchp->next) {
1621                 memset(mptr, 0xFF,
1622                        IPT_ALIGN(sizeof(struct ipt_entry_match))
1623                        + matchp->match->userspacesize);
1624                 mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
1625         }
1626
1627         memset(mptr, 0xFF,
1628                IPT_ALIGN(sizeof(struct ipt_entry_target))
1629                + iptables_targets->userspacesize);
1630
1631         return mask;
1632 }
1633
1634 static int
1635 delete_entry(const ipt_chainlabel chain,
1636              struct ipt_entry *fw,
1637              unsigned int nsaddrs,
1638              const struct in_addr saddrs[],
1639              unsigned int ndaddrs,
1640              const struct in_addr daddrs[],
1641              int verbose,
1642              iptc_handle_t *handle,
1643              struct iptables_rule_match *matches)
1644 {
1645         unsigned int i, j;
1646         int ret = 1;
1647         unsigned char *mask;
1648
1649         mask = make_delete_mask(fw, matches);
1650         for (i = 0; i < nsaddrs; i++) {
1651                 fw->ip.src.s_addr = saddrs[i].s_addr;
1652                 for (j = 0; j < ndaddrs; j++) {
1653                         fw->ip.dst.s_addr = daddrs[j].s_addr;
1654                         if (verbose)
1655                                 print_firewall_line(fw, *handle);
1656                         ret &= iptc_delete_entry(chain, fw, mask, handle);
1657                 }
1658         }
1659         free(mask);
1660
1661         return ret;
1662 }
1663
1664 int
1665 for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
1666                int verbose, int builtinstoo, iptc_handle_t *handle)
1667 {
1668         int ret = 1;
1669         const char *chain;
1670         char *chains;
1671         unsigned int i, chaincount = 0;
1672
1673         chain = iptc_first_chain(handle);
1674         while (chain) {
1675                 chaincount++;
1676                 chain = iptc_next_chain(handle);
1677         }
1678
1679         chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
1680         i = 0;
1681         chain = iptc_first_chain(handle);
1682         while (chain) {
1683                 strcpy(chains + i*sizeof(ipt_chainlabel), chain);
1684                 i++;
1685                 chain = iptc_next_chain(handle);
1686         }
1687
1688         for (i = 0; i < chaincount; i++) {
1689                 if (!builtinstoo
1690                     && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
1691                                     *handle) == 1)
1692                         continue;
1693                 ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
1694         }
1695
1696         free(chains);
1697         return ret;
1698 }
1699
1700 int
1701 flush_entries(const ipt_chainlabel chain, int verbose,
1702               iptc_handle_t *handle)
1703 {
1704         if (!chain)
1705                 return for_each_chain(flush_entries, verbose, 1, handle);
1706
1707         if (verbose)
1708                 fprintf(stdout, "Flushing chain `%s'\n", chain);
1709         return iptc_flush_entries(chain, handle);
1710 }
1711
1712 static int
1713 zero_entries(const ipt_chainlabel chain, int verbose,
1714              iptc_handle_t *handle)
1715 {
1716         if (!chain)
1717                 return for_each_chain(zero_entries, verbose, 1, handle);
1718
1719         if (verbose)
1720                 fprintf(stdout, "Zeroing chain `%s'\n", chain);
1721         return iptc_zero_entries(chain, handle);
1722 }
1723
1724 int
1725 delete_chain(const ipt_chainlabel chain, int verbose,
1726              iptc_handle_t *handle)
1727 {
1728         if (!chain)
1729                 return for_each_chain(delete_chain, verbose, 0, handle);
1730
1731         if (verbose)
1732                 fprintf(stdout, "Deleting chain `%s'\n", chain);
1733         return iptc_delete_chain(chain, handle);
1734 }
1735
1736 static int
1737 list_entries(const ipt_chainlabel chain, int verbose, int numeric,
1738              int expanded, int linenumbers, iptc_handle_t *handle)
1739 {
1740         int found = 0;
1741         unsigned int format;
1742         const char *this;
1743
1744         format = FMT_OPTIONS;
1745         if (!verbose)
1746                 format |= FMT_NOCOUNTS;
1747         else
1748                 format |= FMT_VIA;
1749
1750         if (numeric)
1751                 format |= FMT_NUMERIC;
1752
1753         if (!expanded)
1754                 format |= FMT_KILOMEGAGIGA;
1755
1756         if (linenumbers)
1757                 format |= FMT_LINENUMBERS;
1758
1759         for (this = iptc_first_chain(handle);
1760              this;
1761              this = iptc_next_chain(handle)) {
1762                 const struct ipt_entry *i;
1763                 unsigned int num;
1764
1765                 if (chain && strcmp(chain, this) != 0)
1766                         continue;
1767
1768                 if (found) printf("\n");
1769
1770                 print_header(format, this, handle);
1771                 i = iptc_first_rule(this, handle);
1772
1773                 num = 0;
1774                 while (i) {
1775                         print_firewall(i,
1776                                        iptc_get_target(i, handle),
1777                                        num++,
1778                                        format,
1779                                        *handle);
1780                         i = iptc_next_rule(i, handle);
1781                 }
1782                 found = 1;
1783         }
1784
1785         errno = ENOENT;
1786         return found;
1787 }
1788
1789 static char *get_modprobe(void)
1790 {
1791         int procfile;
1792         char *ret;
1793
1794 #define PROCFILE_BUFSIZ 1024
1795         procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
1796         if (procfile < 0)
1797                 return NULL;
1798
1799         ret = (char *) malloc(PROCFILE_BUFSIZ);
1800         if (ret) {
1801                 memset(ret, 0, PROCFILE_BUFSIZ);
1802                 switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
1803                 case -1: goto fail;
1804                 case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
1805                 }
1806                 if (ret[strlen(ret)-1]=='\n') 
1807                         ret[strlen(ret)-1]=0;
1808                 close(procfile);
1809                 return ret;
1810         }
1811  fail:
1812         free(ret);
1813         close(procfile);
1814         return NULL;
1815 }
1816
1817 int iptables_insmod(const char *modname, const char *modprobe, int quiet)
1818 {
1819         char *buf = NULL;
1820         char *argv[4];
1821         int status;
1822
1823         /* If they don't explicitly set it, read out of kernel */
1824         if (!modprobe) {
1825                 buf = get_modprobe();
1826                 if (!buf)
1827                         return -1;
1828                 modprobe = buf;
1829         }
1830
1831         switch (fork()) {
1832         case 0:
1833                 argv[0] = (char *)modprobe;
1834                 argv[1] = (char *)modname;
1835                 if (quiet) {
1836                         argv[2] = "-q";
1837                         argv[3] = NULL;
1838                 } else {
1839                         argv[2] = NULL;
1840                         argv[3] = NULL;
1841                 }
1842                 execv(argv[0], argv);
1843
1844                 /* not usually reached */
1845                 exit(1);
1846         case -1:
1847                 return -1;
1848
1849         default: /* parent */
1850                 wait(&status);
1851         }
1852
1853         free(buf);
1854         if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
1855                 return 0;
1856         return -1;
1857 }
1858
1859 int load_iptables_ko(const char *modprobe, int quiet)
1860 {
1861         static int loaded = 0;
1862         static int ret = -1;
1863
1864         if (!loaded) {
1865                 ret = iptables_insmod("ip_tables", modprobe, quiet);
1866                 loaded = (ret == 0);
1867         }
1868
1869         return ret;
1870 }
1871
1872 static struct ipt_entry *
1873 generate_entry(const struct ipt_entry *fw,
1874                struct iptables_rule_match *matches,
1875                struct ipt_entry_target *target)
1876 {
1877         unsigned int size;
1878         struct iptables_rule_match *matchp;
1879         struct ipt_entry *e;
1880
1881         size = sizeof(struct ipt_entry);
1882         for (matchp = matches; matchp; matchp = matchp->next)
1883                 size += matchp->match->m->u.match_size;
1884
1885         e = fw_malloc(size + target->u.target_size);
1886         *e = *fw;
1887         e->target_offset = size;
1888         e->next_offset = size + target->u.target_size;
1889
1890         size = 0;
1891         for (matchp = matches; matchp; matchp = matchp->next) {
1892                 memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
1893                 size += matchp->match->m->u.match_size;
1894         }
1895         memcpy(e->elems + size, target, target->u.target_size);
1896
1897         return e;
1898 }
1899
1900 void clear_rule_matches(struct iptables_rule_match **matches)
1901 {
1902         struct iptables_rule_match *matchp, *tmp;
1903
1904         for (matchp = *matches; matchp;) {
1905                 tmp = matchp->next;
1906                 if (matchp->match->m) {
1907                         free(matchp->match->m);
1908                         matchp->match->m = NULL;
1909                 }
1910                 if (matchp->match == matchp->match->next) {
1911                         free(matchp->match);
1912                         matchp->match = NULL;
1913                 }
1914                 free(matchp);
1915                 matchp = tmp;
1916         }
1917
1918         *matches = NULL;
1919 }
1920
1921 static void set_revision(char *name, u_int8_t revision)
1922 {
1923         /* Old kernel sources don't have ".revision" field,
1924            but we stole a byte from name. */
1925         name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
1926         name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
1927 }
1928
1929 void
1930 get_kernel_version(void) {
1931         static struct utsname uts;
1932         int x = 0, y = 0, z = 0;
1933
1934         if (uname(&uts) == -1) {
1935                 fprintf(stderr, "Unable to retrieve kernel version.\n");
1936                 free_opts(1);
1937                 exit(1); 
1938         }
1939
1940         sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
1941         kernel_version = LINUX_VERSION(x, y, z);
1942 }
1943
1944 int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
1945 {
1946         struct ipt_entry fw, *e = NULL;
1947         int invert = 0;
1948         unsigned int nsaddrs = 0, ndaddrs = 0;
1949         struct in_addr *saddrs = NULL, *daddrs = NULL;
1950
1951         int c, verbose = 0;
1952         const char *chain = NULL;
1953         const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
1954         const char *policy = NULL, *newname = NULL;
1955         unsigned int rulenum = 0, options = 0, command = 0;
1956         const char *pcnt = NULL, *bcnt = NULL;
1957         int ret = 1;
1958         struct iptables_match *m;
1959         struct iptables_rule_match *matches = NULL;
1960         struct iptables_rule_match *matchp;
1961         struct iptables_target *target = NULL;
1962         struct iptables_target *t;
1963         const char *jumpto = "";
1964         char *protocol = NULL;
1965         int proto_used = 0;
1966
1967         memset(&fw, 0, sizeof(fw));
1968
1969         /* re-set optind to 0 in case do_command gets called
1970          * a second time */
1971         optind = 0;
1972
1973         /* clear mflags in case do_command gets called a second time
1974          * (we clear the global list of all matches for security)*/
1975         for (m = iptables_matches; m; m = m->next)
1976                 m->mflags = 0;
1977
1978         for (t = iptables_targets; t; t = t->next) {
1979                 t->tflags = 0;
1980                 t->used = 0;
1981         }
1982
1983         /* Suppress error messages: we may add new options if we
1984            demand-load a protocol. */
1985         opterr = 0;
1986
1987         while ((c = getopt_long(argc, argv,
1988            "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:",
1989                                            opts, NULL)) != -1) {
1990                 switch (c) {
1991                         /*
1992                          * Command selection
1993                          */
1994                 case 'A':
1995                         add_command(&command, CMD_APPEND, CMD_NONE,
1996                                     invert);
1997                         chain = optarg;
1998                         break;
1999
2000                 case 'D':
2001                         add_command(&command, CMD_DELETE, CMD_NONE,
2002                                     invert);
2003                         chain = optarg;
2004                         if (optind < argc && argv[optind][0] != '-'
2005                             && argv[optind][0] != '!') {
2006                                 rulenum = parse_rulenumber(argv[optind++]);
2007                                 command = CMD_DELETE_NUM;
2008                         }
2009                         break;
2010
2011                 case 'R':
2012                         add_command(&command, CMD_REPLACE, CMD_NONE,
2013                                     invert);
2014                         chain = optarg;
2015                         if (optind < argc && argv[optind][0] != '-'
2016                             && argv[optind][0] != '!')
2017                                 rulenum = parse_rulenumber(argv[optind++]);
2018                         else
2019                                 exit_error(PARAMETER_PROBLEM,
2020                                            "-%c requires a rule number",
2021                                            cmd2char(CMD_REPLACE));
2022                         break;
2023
2024                 case 'I':
2025                         add_command(&command, CMD_INSERT, CMD_NONE,
2026                                     invert);
2027                         chain = optarg;
2028                         if (optind < argc && argv[optind][0] != '-'
2029                             && argv[optind][0] != '!')
2030                                 rulenum = parse_rulenumber(argv[optind++]);
2031                         else rulenum = 1;
2032                         break;
2033
2034                 case 'L':
2035                         add_command(&command, CMD_LIST, CMD_ZERO,
2036                                     invert);
2037                         if (optarg) chain = optarg;
2038                         else if (optind < argc && argv[optind][0] != '-'
2039                                  && argv[optind][0] != '!')
2040                                 chain = argv[optind++];
2041                         break;
2042
2043                 case 'F':
2044                         add_command(&command, CMD_FLUSH, CMD_NONE,
2045                                     invert);
2046                         if (optarg) chain = optarg;
2047                         else if (optind < argc && argv[optind][0] != '-'
2048                                  && argv[optind][0] != '!')
2049                                 chain = argv[optind++];
2050                         break;
2051
2052                 case 'Z':
2053                         add_command(&command, CMD_ZERO, CMD_LIST,
2054                                     invert);
2055                         if (optarg) chain = optarg;
2056                         else if (optind < argc && argv[optind][0] != '-'
2057                                 && argv[optind][0] != '!')
2058                                 chain = argv[optind++];
2059                         break;
2060
2061                 case 'N':
2062                         if (optarg && (*optarg == '-' || *optarg == '!'))
2063                                 exit_error(PARAMETER_PROBLEM,
2064                                            "chain name not allowed to start "
2065                                            "with `%c'\n", *optarg);
2066                         if (find_target(optarg, TRY_LOAD))
2067                                 exit_error(PARAMETER_PROBLEM,
2068                                            "chain name may not clash "
2069                                            "with target name\n");
2070                         add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
2071                                     invert);
2072                         chain = optarg;
2073                         break;
2074
2075                 case 'X':
2076                         add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
2077                                     invert);
2078                         if (optarg) chain = optarg;
2079                         else if (optind < argc && argv[optind][0] != '-'
2080                                  && argv[optind][0] != '!')
2081                                 chain = argv[optind++];
2082                         break;
2083
2084                 case 'E':
2085                         add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
2086                                     invert);
2087                         chain = optarg;
2088                         if (optind < argc && argv[optind][0] != '-'
2089                             && argv[optind][0] != '!')
2090                                 newname = argv[optind++];
2091                         else
2092                                 exit_error(PARAMETER_PROBLEM,
2093                                            "-%c requires old-chain-name and "
2094                                            "new-chain-name",
2095                                             cmd2char(CMD_RENAME_CHAIN));
2096                         break;
2097
2098                 case 'P':
2099                         add_command(&command, CMD_SET_POLICY, CMD_NONE,
2100                                     invert);
2101                         chain = optarg;
2102                         if (optind < argc && argv[optind][0] != '-'
2103                             && argv[optind][0] != '!')
2104                                 policy = argv[optind++];
2105                         else
2106                                 exit_error(PARAMETER_PROBLEM,
2107                                            "-%c requires a chain and a policy",
2108                                            cmd2char(CMD_SET_POLICY));
2109                         break;
2110
2111                 case 'h':
2112                         if (!optarg)
2113                                 optarg = argv[optind];
2114
2115                         /* iptables -p icmp -h */
2116                         if (!matches && protocol)
2117                                 find_match(protocol, TRY_LOAD, &matches);
2118
2119                         exit_printhelp(matches);
2120
2121                         /*
2122                          * Option selection
2123                          */
2124                 case 'p':
2125                         check_inverse(optarg, &invert, &optind, argc);
2126                         set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
2127                                    invert);
2128
2129                         /* Canonicalize into lower case */
2130                         for (protocol = argv[optind-1]; *protocol; protocol++)
2131                                 *protocol = tolower(*protocol);
2132
2133                         protocol = argv[optind-1];
2134                         fw.ip.proto = parse_protocol(protocol);
2135
2136                         if (fw.ip.proto == 0
2137                             && (fw.ip.invflags & IPT_INV_PROTO))
2138                                 exit_error(PARAMETER_PROBLEM,
2139                                            "rule would never match protocol");
2140                         break;
2141
2142                 case 's':
2143                         check_inverse(optarg, &invert, &optind, argc);
2144                         set_option(&options, OPT_SOURCE, &fw.ip.invflags,
2145                                    invert);
2146                         shostnetworkmask = argv[optind-1];
2147                         break;
2148
2149                 case 'd':
2150                         check_inverse(optarg, &invert, &optind, argc);
2151                         set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
2152                                    invert);
2153                         dhostnetworkmask = argv[optind-1];
2154                         break;
2155
2156 #ifdef IPT_F_GOTO
2157                 case 'g':
2158                         set_option(&options, OPT_JUMP, &fw.ip.invflags,
2159                                    invert);
2160                         fw.ip.flags |= IPT_F_GOTO;
2161                         jumpto = parse_target(optarg);
2162                         break;
2163 #endif
2164
2165                 case 'j':
2166                         set_option(&options, OPT_JUMP, &fw.ip.invflags,
2167                                    invert);
2168                         jumpto = parse_target(optarg);
2169                         /* TRY_LOAD (may be chain name) */
2170                         target = find_target(jumpto, TRY_LOAD);
2171
2172                         if (target) {
2173                                 size_t size;
2174
2175                                 size = IPT_ALIGN(sizeof(struct ipt_entry_target))
2176                                         + target->size;
2177
2178                                 target->t = fw_calloc(1, size);
2179                                 target->t->u.target_size = size;
2180                                 strcpy(target->t->u.user.name, jumpto);
2181                                 set_revision(target->t->u.user.name,
2182                                              target->revision);
2183                                 if (target->init != NULL)
2184                                         target->init(target->t, &fw.nfcache);
2185                                 opts = merge_options(opts, target->extra_opts, &target->option_offset);
2186                         }
2187                         break;
2188
2189
2190                 case 'i':
2191                         check_inverse(optarg, &invert, &optind, argc);
2192                         set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
2193                                    invert);
2194                         parse_interface(argv[optind-1],
2195                                         fw.ip.iniface,
2196                                         fw.ip.iniface_mask);
2197                         break;
2198
2199                 case 'o':
2200                         check_inverse(optarg, &invert, &optind, argc);
2201                         set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
2202                                    invert);
2203                         parse_interface(argv[optind-1],
2204                                         fw.ip.outiface,
2205                                         fw.ip.outiface_mask);
2206                         break;
2207
2208                 case 'f':
2209                         set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
2210                                    invert);
2211                         fw.ip.flags |= IPT_F_FRAG;
2212                         break;
2213
2214                 case 'v':
2215                         if (!verbose)
2216                                 set_option(&options, OPT_VERBOSE,
2217                                            &fw.ip.invflags, invert);
2218                         verbose++;
2219                         break;
2220
2221                 case 'm': {
2222                         size_t size;
2223
2224                         if (invert)
2225                                 exit_error(PARAMETER_PROBLEM,
2226                                            "unexpected ! flag before --match");
2227
2228                         m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);
2229                         size = IPT_ALIGN(sizeof(struct ipt_entry_match))
2230                                          + m->size;
2231                         m->m = fw_calloc(1, size);
2232                         m->m->u.match_size = size;
2233                         strcpy(m->m->u.user.name, m->name);
2234                         set_revision(m->m->u.user.name, m->revision);
2235                         if (m->init != NULL)
2236                                 m->init(m->m, &fw.nfcache);
2237                         if (m != m->next)
2238                                 /* Merge options for non-cloned matches */
2239                                 opts = merge_options(opts, m->extra_opts, &m->option_offset);
2240                 }
2241                 break;
2242
2243                 case 'n':
2244                         set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
2245                                    invert);
2246                         break;
2247
2248                 case 't':
2249                         if (invert)
2250                                 exit_error(PARAMETER_PROBLEM,
2251                                            "unexpected ! flag before --table");
2252                         *table = argv[optind-1];
2253                         break;
2254
2255                 case 'x':
2256                         set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
2257                                    invert);
2258                         break;
2259
2260                 case 'V':
2261                         if (invert)
2262                                 printf("Not %s ;-)\n", program_version);
2263                         else
2264                                 printf("%s v%s\n",
2265                                        program_name, program_version);
2266                         exit(0);
2267
2268                 case '0':
2269                         set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
2270                                    invert);
2271                         break;
2272
2273                 case 'M':
2274                         modprobe = optarg;
2275                         break;
2276
2277                 case 'c':
2278
2279                         set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
2280                                    invert);
2281                         pcnt = optarg;
2282                         if (optind < argc && argv[optind][0] != '-'
2283                             && argv[optind][0] != '!')
2284                                 bcnt = argv[optind++];
2285                         else
2286                                 exit_error(PARAMETER_PROBLEM,
2287                                         "-%c requires packet and byte counter",
2288                                         opt2char(OPT_COUNTERS));
2289
2290                         if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
2291                                 exit_error(PARAMETER_PROBLEM,
2292                                         "-%c packet counter not numeric",
2293                                         opt2char(OPT_COUNTERS));
2294
2295                         if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
2296                                 exit_error(PARAMETER_PROBLEM,
2297                                         "-%c byte counter not numeric",
2298                                         opt2char(OPT_COUNTERS));
2299                         
2300                         break;
2301
2302
2303                 case 1: /* non option */
2304                         if (optarg[0] == '!' && optarg[1] == '\0') {
2305                                 if (invert)
2306                                         exit_error(PARAMETER_PROBLEM,
2307                                                    "multiple consecutive ! not"
2308                                                    " allowed");
2309                                 invert = TRUE;
2310                                 optarg[0] = '\0';
2311                                 continue;
2312                         }
2313                         printf("Bad argument `%s'\n", optarg);
2314                         exit_tryhelp(2);
2315
2316                 default:
2317                         if (!target
2318                             || !(target->parse(c - target->option_offset,
2319                                                argv, invert,
2320                                                &target->tflags,
2321                                                &fw, &target->t))) {
2322                                 for (matchp = matches; matchp; matchp = matchp->next) {
2323                                         if (matchp->completed) 
2324                                                 continue;
2325                                         if (matchp->match->parse(c - matchp->match->option_offset,
2326                                                      argv, invert,
2327                                                      &matchp->match->mflags,
2328                                                      &fw,
2329                                                      &fw.nfcache,
2330                                                      &matchp->match->m))
2331                                                 break;
2332                                 }
2333                                 m = matchp ? matchp->match : NULL;
2334
2335                                 /* If you listen carefully, you can
2336                                    actually hear this code suck. */
2337
2338                                 /* some explanations (after four different bugs
2339                                  * in 3 different releases): If we encounter a
2340                                  * parameter, that has not been parsed yet,
2341                                  * it's not an option of an explicitly loaded
2342                                  * match or a target.  However, we support
2343                                  * implicit loading of the protocol match
2344                                  * extension.  '-p tcp' means 'l4 proto 6' and
2345                                  * at the same time 'load tcp protocol match on
2346                                  * demand if we specify --dport'.
2347                                  *
2348                                  * To make this work, we need to make sure:
2349                                  * - the parameter has not been parsed by
2350                                  *   a match (m above)
2351                                  * - a protocol has been specified
2352                                  * - the protocol extension has not been
2353                                  *   loaded yet, or is loaded and unused
2354                                  *   [think of iptables-restore!]
2355                                  * - the protocol extension can be successively
2356                                  *   loaded
2357                                  */
2358                                 if (m == NULL
2359                                     && protocol
2360                                     && (!find_proto(protocol, DONT_LOAD,
2361                                                    options&OPT_NUMERIC, NULL) 
2362                                         || (find_proto(protocol, DONT_LOAD,
2363                                                         options&OPT_NUMERIC, NULL)
2364                                             && (proto_used == 0))
2365                                        )
2366                                     && (m = find_proto(protocol, TRY_LOAD,
2367                                                        options&OPT_NUMERIC, &matches))) {
2368                                         /* Try loading protocol */
2369                                         size_t size;
2370                                         
2371                                         proto_used = 1;
2372
2373                                         size = IPT_ALIGN(sizeof(struct ipt_entry_match))
2374                                                          + m->size;
2375
2376                                         m->m = fw_calloc(1, size);
2377                                         m->m->u.match_size = size;
2378                                         strcpy(m->m->u.user.name, m->name);
2379                                         set_revision(m->m->u.user.name,
2380                                                      m->revision);
2381                                         if (m->init != NULL)
2382                                                 m->init(m->m, &fw.nfcache);
2383
2384                                         opts = merge_options(opts,
2385                                             m->extra_opts, &m->option_offset);
2386
2387                                         optind--;
2388                                         continue;
2389                                 }
2390                                 if (!m)
2391                                         exit_error(PARAMETER_PROBLEM,
2392                                                    "Unknown arg `%s'",
2393                                                    argv[optind-1]);
2394                         }
2395                 }
2396                 invert = FALSE;
2397         }
2398
2399         for (matchp = matches; matchp; matchp = matchp->next)
2400                 matchp->match->final_check(matchp->match->mflags);
2401
2402         if (target)
2403                 target->final_check(target->tflags);
2404
2405         /* Fix me: must put inverse options checking here --MN */
2406
2407         if (optind < argc)
2408                 exit_error(PARAMETER_PROBLEM,
2409                            "unknown arguments found on commandline");
2410         if (!command)
2411                 exit_error(PARAMETER_PROBLEM, "no command specified");
2412         if (invert)
2413                 exit_error(PARAMETER_PROBLEM,
2414                            "nothing appropriate following !");
2415
2416         if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
2417                 if (!(options & OPT_DESTINATION))
2418                         dhostnetworkmask = "0.0.0.0/0";
2419                 if (!(options & OPT_SOURCE))
2420                         shostnetworkmask = "0.0.0.0/0";
2421         }
2422
2423         if (shostnetworkmask)
2424                 parse_hostnetworkmask(shostnetworkmask, &saddrs,
2425                                       &(fw.ip.smsk), &nsaddrs);
2426
2427         if (dhostnetworkmask)
2428                 parse_hostnetworkmask(dhostnetworkmask, &daddrs,
2429                                       &(fw.ip.dmsk), &ndaddrs);
2430
2431         if ((nsaddrs > 1 || ndaddrs > 1) &&
2432             (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
2433                 exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
2434                            " source or destination IP addresses");
2435
2436         if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
2437                 exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
2438                            "specify a unique address");
2439
2440         generic_opt_check(command, options);
2441
2442         if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
2443                 exit_error(PARAMETER_PROBLEM,
2444                            "chain name `%s' too long (must be under %i chars)",
2445                            chain, IPT_FUNCTION_MAXNAMELEN);
2446
2447         /* only allocate handle if we weren't called with a handle */
2448         if (!*handle)
2449                 *handle = iptc_init(*table);
2450
2451         /* try to insmod the module if iptc_init failed */
2452         if (!*handle && load_iptables_ko(modprobe, 0) != -1)
2453                 *handle = iptc_init(*table);
2454
2455         if (!*handle)
2456                 exit_error(VERSION_PROBLEM,
2457                            "can't initialize iptables table `%s': %s",
2458                            *table, iptc_strerror(errno));
2459
2460         if (command == CMD_APPEND
2461             || command == CMD_DELETE
2462             || command == CMD_INSERT
2463             || command == CMD_REPLACE) {
2464                 if (strcmp(chain, "PREROUTING") == 0
2465                     || strcmp(chain, "INPUT") == 0) {
2466                         /* -o not valid with incoming packets. */
2467                         if (options & OPT_VIANAMEOUT)
2468                                 exit_error(PARAMETER_PROBLEM,
2469                                            "Can't use -%c with %s\n",
2470                                            opt2char(OPT_VIANAMEOUT),
2471                                            chain);
2472                 }
2473
2474                 if (strcmp(chain, "POSTROUTING") == 0
2475                     || strcmp(chain, "OUTPUT") == 0) {
2476                         /* -i not valid with outgoing packets */
2477                         if (options & OPT_VIANAMEIN)
2478                                 exit_error(PARAMETER_PROBLEM,
2479                                            "Can't use -%c with %s\n",
2480                                            opt2char(OPT_VIANAMEIN),
2481                                            chain);
2482                 }
2483
2484                 if (target && iptc_is_chain(jumpto, *handle)) {
2485                         printf("Warning: using chain %s, not extension\n",
2486                                jumpto);
2487
2488                         if (target->t)
2489                                 free(target->t);
2490
2491                         target = NULL;
2492                 }
2493
2494                 /* If they didn't specify a target, or it's a chain
2495                    name, use standard. */
2496                 if (!target
2497                     && (strlen(jumpto) == 0
2498                         || iptc_is_chain(jumpto, *handle))) {
2499                         size_t size;
2500
2501                         target = find_target(IPT_STANDARD_TARGET,
2502                                              LOAD_MUST_SUCCEED);
2503
2504                         size = sizeof(struct ipt_entry_target)
2505                                 + target->size;
2506                         target->t = fw_calloc(1, size);
2507                         target->t->u.target_size = size;
2508                         strcpy(target->t->u.user.name, jumpto);
2509                         if (!iptc_is_chain(jumpto, *handle))
2510                                 set_revision(target->t->u.user.name,
2511                                              target->revision);
2512                         if (target->init != NULL)
2513                                 target->init(target->t, &fw.nfcache);
2514                 }
2515
2516                 if (!target) {
2517                         /* it is no chain, and we can't load a plugin.
2518                          * We cannot know if the plugin is corrupt, non
2519                          * existant OR if the user just misspelled a
2520                          * chain. */
2521 #ifdef IPT_F_GOTO
2522                         if (fw.ip.flags & IPT_F_GOTO)
2523                                 exit_error(PARAMETER_PROBLEM,
2524                                            "goto '%s' is not a chain\n", jumpto);
2525 #endif
2526                         find_target(jumpto, LOAD_MUST_SUCCEED);
2527                 } else {
2528                         e = generate_entry(&fw, matches, target->t);
2529                         free(target->t);
2530                 }
2531         }
2532
2533         switch (command) {
2534         case CMD_APPEND:
2535                 ret = append_entry(chain, e,
2536                                    nsaddrs, saddrs, ndaddrs, daddrs,
2537                                    options&OPT_VERBOSE,
2538                                    handle);
2539                 break;
2540         case CMD_DELETE:
2541                 ret = delete_entry(chain, e,
2542                                    nsaddrs, saddrs, ndaddrs, daddrs,
2543                                    options&OPT_VERBOSE,
2544                                    handle, matches);
2545                 break;
2546         case CMD_DELETE_NUM:
2547                 ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
2548                 break;
2549         case CMD_REPLACE:
2550                 ret = replace_entry(chain, e, rulenum - 1,
2551                                     saddrs, daddrs, options&OPT_VERBOSE,
2552                                     handle);
2553                 break;
2554         case CMD_INSERT:
2555                 ret = insert_entry(chain, e, rulenum - 1,
2556                                    nsaddrs, saddrs, ndaddrs, daddrs,
2557                                    options&OPT_VERBOSE,
2558                                    handle);
2559                 break;
2560         case CMD_LIST:
2561                 ret = list_entries(chain,
2562                                    options&OPT_VERBOSE,
2563                                    options&OPT_NUMERIC,
2564                                    options&OPT_EXPANDED,
2565                                    options&OPT_LINENUMBERS,
2566                                    handle);
2567                 break;
2568         case CMD_FLUSH:
2569                 ret = flush_entries(chain, options&OPT_VERBOSE, handle);
2570                 break;
2571         case CMD_ZERO:
2572                 ret = zero_entries(chain, options&OPT_VERBOSE, handle);
2573                 break;
2574         case CMD_LIST|CMD_ZERO:
2575                 ret = list_entries(chain,
2576                                    options&OPT_VERBOSE,
2577                                    options&OPT_NUMERIC,
2578                                    options&OPT_EXPANDED,
2579                                    options&OPT_LINENUMBERS,
2580                                    handle);
2581                 if (ret)
2582                         ret = zero_entries(chain,
2583                                            options&OPT_VERBOSE, handle);
2584                 break;
2585         case CMD_NEW_CHAIN:
2586                 ret = iptc_create_chain(chain, handle);
2587                 break;
2588         case CMD_DELETE_CHAIN:
2589                 ret = delete_chain(chain, options&OPT_VERBOSE, handle);
2590                 break;
2591         case CMD_RENAME_CHAIN:
2592                 ret = iptc_rename_chain(chain, newname, handle);
2593                 break;
2594         case CMD_SET_POLICY:
2595                 ret = iptc_set_policy(chain, policy, NULL, handle);
2596                 break;
2597         default:
2598                 /* We should never reach this... */
2599                 exit_tryhelp(2);
2600         }
2601
2602         if (verbose > 1)
2603                 dump_entries(*handle);
2604
2605         clear_rule_matches(&matches);
2606
2607         if (e != NULL) {
2608                 free(e);
2609                 e = NULL;
2610         }
2611
2612         free(saddrs);
2613         free(daddrs);
2614         free_opts(1);
2615
2616         return ret;
2617 }