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