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