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