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