xtables-arp.c 27.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* Code to take an arptables-style command line and do it. */

/*
 * arptables:
 * Author: Bart De Schuymer <bdschuym@pandora.be>, but
 * almost all code is from the iptables userspace program, which has main
 * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
  Currently, only support for specifying hardware addresses for Ethernet
  is available.
  This tool is not luser-proof: you can specify an Ethernet source address
  and set hardware length to something different than 6, f.e.
*/
30
#include "config.h"
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <getopt.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <iptables.h>
#include <xtables.h>

#include "xshared.h"

#include "nft.h"
#include "nft-arp.h"
#include <linux/netfilter_arp/arp_tables.h>

#define NUMBER_OF_OPT	16
static const char optflags[NUMBER_OF_OPT]
= { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};

static struct option original_opts[] = {
	{ "append", 1, 0, 'A' },
	{ "delete", 1, 0,  'D' },
	{ "insert", 1, 0,  'I' },
	{ "replace", 1, 0,  'R' },
	{ "list", 2, 0,  'L' },
	{ "flush", 2, 0,  'F' },
	{ "zero", 2, 0,  'Z' },
	{ "new-chain", 1, 0,  'N' },
	{ "delete-chain", 2, 0,  'X' },
	{ "rename-chain", 1, 0,  'E' },
	{ "policy", 1, 0,  'P' },
	{ "source-ip", 1, 0, 's' },
	{ "destination-ip", 1, 0,  'd' },
	{ "src-ip", 1, 0,  's' },
	{ "dst-ip", 1, 0,  'd' },
	{ "source-mac", 1, 0, 2},
	{ "destination-mac", 1, 0, 3},
	{ "src-mac", 1, 0, 2},
	{ "dst-mac", 1, 0, 3},
	{ "h-length", 1, 0,  'l' },
	{ "p-length", 1, 0,  8 },
	{ "opcode", 1, 0,  4 },
	{ "h-type", 1, 0,  5 },
	{ "proto-type", 1, 0,  6 },
	{ "in-interface", 1, 0, 'i' },
	{ "jump", 1, 0, 'j' },
	{ "table", 1, 0, 't' },
	{ "match", 1, 0, 'm' },
	{ "numeric", 0, 0, 'n' },
	{ "out-interface", 1, 0, 'o' },
	{ "verbose", 0, 0, 'v' },
	{ "exact", 0, 0, 'x' },
	{ "version", 0, 0, 'V' },
	{ "help", 2, 0, 'h' },
	{ "line-numbers", 0, 0, '0' },
	{ "modprobe", 1, 0, 'M' },
97
	{ "set-counters", 1, 0, 'c' },
98
99
100
	{ 0 }
};

101
#define opts xt_params->opts
102
103
104
105

extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
struct xtables_globals arptables_globals = {
	.option_offset		= 0,
106
	.program_version	= PACKAGE_VERSION,
107
108
109
110
111
	.orig_opts		= original_opts,
	.exit_err		= xtables_exit_error,
	.compat_rev		= nft_compatible_revision,
};

112
113
/* index relates to bit of each OPT_* value */
static int inverse_for_options[] =
114
115
116
117
{
/* -n */ 0,
/* -s */ ARPT_INV_SRCIP,
/* -d */ ARPT_INV_TGTIP,
118
/* -p */ 0,
119
120
/* -j */ 0,
/* -v */ 0,
121
/* -x */ 0,
122
123
124
125
/* -i */ ARPT_INV_VIA_IN,
/* -o */ ARPT_INV_VIA_OUT,
/*--line*/ 0,
/* -c */ 0,
126
127
128
129
130
131
/* 2 */ ARPT_INV_SRCDEVADDR,
/* 3 */ ARPT_INV_TGTDEVADDR,
/* -l */ ARPT_INV_ARPHLN,
/* 4 */ ARPT_INV_ARPOP,
/* 5 */ ARPT_INV_ARPHRD,
/* 6 */ ARPT_INV_ARPPRO,
132
133
134
135
136
137
};

/***********************************************/
/* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
/***********************************************/

138
139
140
141
142
143
static unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
static unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
static unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
static unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
static unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
static unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

/*
 * put the mac address into 6 (ETH_ALEN) bytes
 */
static int getmac_and_mask(char *from, char *to, char *mask)
{
	char *p;
	int i;
	struct ether_addr *addr;

	if (strcasecmp(from, "Unicast") == 0) {
		memcpy(to, mac_type_unicast, ETH_ALEN);
		memcpy(mask, msk_type_unicast, ETH_ALEN);
		return 0;
	}
	if (strcasecmp(from, "Multicast") == 0) {
		memcpy(to, mac_type_multicast, ETH_ALEN);
		memcpy(mask, msk_type_multicast, ETH_ALEN);
		return 0;
	}
	if (strcasecmp(from, "Broadcast") == 0) {
		memcpy(to, mac_type_broadcast, ETH_ALEN);
		memcpy(mask, msk_type_broadcast, ETH_ALEN);
		return 0;
	}
	if ( (p = strrchr(from, '/')) != NULL) {
		*p = '\0';
		if (!(addr = ether_aton(p + 1)))
			return -1;
		memcpy(mask, addr, ETH_ALEN);
	} else
		memset(mask, 0xff, ETH_ALEN);
	if (!(addr = ether_aton(from)))
		return -1;
	memcpy(to, addr, ETH_ALEN);
	for (i = 0; i < ETH_ALEN; i++)
		to[i] &= mask[i];
	return 0;
}

static int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask)
{
	char *p, *buffer;
	int i;

	if ( (p = strrchr(from, '/')) != NULL) {
		*p = '\0';
		i = strtol(p+1, &buffer, 10);
		if (*buffer != '\0' || i < 0 || i > 255)
			return -1;
		*mask = (uint8_t)i;
	} else
		*mask = 255;
	i = strtol(from, &buffer, 10);
	if (*buffer != '\0' || i < 0 || i > 255)
		return -1;
	*to = (uint8_t)i;
	return 0;
}

static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
{
	char *p, *buffer;
	int i;

	if ( (p = strrchr(from, '/')) != NULL) {
		*p = '\0';
		i = strtol(p+1, &buffer, base);
		if (*buffer != '\0' || i < 0 || i > 65535)
			return -1;
		*mask = htons((uint16_t)i);
	} else
		*mask = 65535;
	i = strtol(from, &buffer, base);
	if (*buffer != '\0' || i < 0 || i > 65535)
		return -1;
	*to = htons((uint16_t)i);
	return 0;
}

/*********************************************/
/* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
/*********************************************/

static void
exit_tryhelp(int status)
{
	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
232
233
		arptables_globals.program_name,
		arptables_globals.program_version);
234
235
236
237
	exit(status);
}

static void
238
printhelp(void)
239
240
241
242
243
244
245
246
247
248
249
250
251
{
	struct xtables_target *t = NULL;
	int i;

	printf("%s v%s\n\n"
"Usage: %s -[AD] chain rule-specification [options]\n"
"       %s -[RI] chain rulenum rule-specification [options]\n"
"       %s -D chain rulenum [options]\n"
"       %s -[LFZ] [chain] [options]\n"
"       %s -[NX] chain\n"
"       %s -E old-chain-name new-chain-name\n"
"       %s -P chain target [options]\n"
"       %s -h (print this help information)\n\n",
252
253
254
255
256
257
258
259
260
261
	       arptables_globals.program_name,
	       arptables_globals.program_version,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name,
	       arptables_globals.program_name);
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
	printf(
"Commands:\n"
"Either long or short options are allowed.\n"
"  --append  -A chain		Append to chain\n"
"  --delete  -D chain		Delete matching rule from chain\n"
"  --delete  -D chain rulenum\n"
"				Delete rule rulenum (1 = first) from chain\n"
"  --insert  -I chain [rulenum]\n"
"				Insert in chain as rulenum (default 1=first)\n"
"  --replace -R chain rulenum\n"
"				Replace rule rulenum (1 = first) in chain\n"
"  --list    -L [chain]		List the rules in a chain or all chains\n"
"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
"  --zero    -Z [chain]		Zero counters in chain or all chains\n"
"  --new     -N chain		Create a new user-defined chain\n"
"  --delete-chain\n"
"            -X [chain]		Delete a user-defined chain\n"
"  --policy  -P chain target\n"
"				Change policy on chain to target\n"
"  --rename-chain\n"
"            -E old-chain new-chain\n"
"				Change chain name, (moving any references)\n"

"Options:\n"
"  --source-ip	-s [!] address[/mask]\n"
"				source specification\n"
"  --destination-ip -d [!] address[/mask]\n"
"				destination specification\n"
"  --source-mac [!] address[/mask]\n"
"  --destination-mac [!] address[/mask]\n"
"  --h-length   -l   length[/mask] hardware length (nr of bytes)\n"
"  --opcode code[/mask] operation code (2 bytes)\n"
"  --h-type   type[/mask]  hardware type (2 bytes, hexadecimal)\n"
"  --proto-type   type[/mask]  protocol type (2 bytes)\n"
"  --in-interface -i [!] input name[+]\n"
"				network interface name ([+] for wildcard)\n"
"  --out-interface -o [!] output name[+]\n"
"				network interface name ([+] for wildcard)\n"
"  --jump	-j target\n"
"				target for rule (may load target extension)\n"
"  --match	-m match\n"
"				extended match (may load extension)\n"
"  --numeric	-n		numeric output of addresses and ports\n"
"  --table	-t table	table to manipulate (default: `filter')\n"
"  --verbose	-v		verbose mode\n"
"  --line-numbers		print line numbers when listing\n"
"  --exact	-x		expand numbers (display exact values)\n"
"  --modprobe=<command>		try to insert modules using this command\n"
310
"  --set-counters -c PKTS BYTES	set the counter during insert/append\n"
311
312
313
"[!] --version	-V		print package version.\n");
	printf(" opcode strings: \n");
        for (i = 0; i < NUMOPCODES; i++)
314
                printf(" %d = %s\n", i + 1, arp_opcodes[i]);
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
        printf(
" hardware type string: 1 = Ethernet\n"
" protocol type string: 0x800 = IPv4\n");

	/* Print out any special helps. A user might like to be able
		to add a --help to the commandline, and see expected
		results. So we call help for all matches & targets */
	for (t = xtables_targets; t; t = t->next) {
		if (strcmp(t->name, "CLASSIFY") && strcmp(t->name, "mangle"))
			continue;
		printf("\n");
		t->help();
	}
}

static char
opt2char(int option)
{
	const char *ptr;
	for (ptr = optflags; option > 1; option >>= 1, ptr++);

	return *ptr;
}

static int
check_inverse(const char option[], int *invert, int *optidx, int argc)
{
	if (option && strcmp(option, "!") == 0) {
		if (*invert)
			xtables_error(PARAMETER_PROBLEM,
				      "Multiple `!' flags not allowed");
346
		*invert = true;
347
348
349
350
351
352
353
		if (optidx) {
			*optidx = *optidx+1;
			if (argc && *optidx > argc)
				xtables_error(PARAMETER_PROBLEM,
					      "no argument following `!'");
		}

354
		return true;
355
	}
356
	return false;
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
}

static void
set_option(unsigned int *options, unsigned int option, u_int16_t *invflg,
	   int invert)
{
	if (*options & option)
		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
			      opt2char(option));
	*options |= option;

	if (invert) {
		unsigned int i;
		for (i = 0; 1 << i != option; i++);

		if (!inverse_for_options[i])
			xtables_error(PARAMETER_PROBLEM,
				      "cannot have ! before -%c",
				      opt2char(option));
		*invflg |= inverse_for_options[i];
	}
}

static int
list_entries(struct nft_handle *h, const char *chain, const char *table,
	     int rulenum, int verbose, int numeric, int expanded,
	     int linenumbers)
{
	unsigned int format;

	format = FMT_OPTIONS;
	if (!verbose)
		format |= FMT_NOCOUNTS;
	else
		format |= FMT_VIA;

	if (numeric)
		format |= FMT_NUMERIC;

	if (!expanded)
		format |= FMT_KILOMEGAGIGA;

	if (linenumbers)
		format |= FMT_LINENUMBERS;

402
	return nft_cmd_rule_list(h, chain, table, rulenum, format);
403
404
405
406
407
408
}

static int
append_entry(struct nft_handle *h,
	     const char *chain,
	     const char *table,
409
	     struct iptables_command_state *cs,
410
411
412
	     int rulenum,
	     unsigned int nsaddrs,
	     const struct in_addr saddrs[],
413
	     const struct in_addr smasks[],
414
415
	     unsigned int ndaddrs,
	     const struct in_addr daddrs[],
416
	     const struct in_addr dmasks[],
417
418
419
420
421
422
	     bool verbose, bool append)
{
	unsigned int i, j;
	int ret = 1;

	for (i = 0; i < nsaddrs; i++) {
423
		cs->arp.arp.src.s_addr = saddrs[i].s_addr;
424
		cs->arp.arp.smsk.s_addr = smasks[i].s_addr;
425
		for (j = 0; j < ndaddrs; j++) {
426
			cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
427
			cs->arp.arp.tmsk.s_addr = dmasks[j].s_addr;
428
			if (append) {
429
				ret = nft_cmd_rule_append(h, chain, table, cs, NULL,
430
431
						      verbose);
			} else {
432
				ret = nft_cmd_rule_insert(h, chain, table, cs,
433
434
435
436
437
438
439
440
441
442
443
						      rulenum, verbose);
			}
		}
	}

	return ret;
}

static int
replace_entry(const char *chain,
	      const char *table,
444
	      struct iptables_command_state *cs,
445
446
	      unsigned int rulenum,
	      const struct in_addr *saddr,
447
	      const struct in_addr *smask,
448
	      const struct in_addr *daddr,
449
	      const struct in_addr *dmask,
450
451
	      bool verbose, struct nft_handle *h)
{
452
453
	cs->arp.arp.src.s_addr = saddr->s_addr;
	cs->arp.arp.tgt.s_addr = daddr->s_addr;
454
455
	cs->arp.arp.smsk.s_addr = smask->s_addr;
	cs->arp.arp.tmsk.s_addr = dmask->s_addr;
456

457
	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
458
459
460
461
462
}

static int
delete_entry(const char *chain,
	     const char *table,
463
	     struct iptables_command_state *cs,
464
465
	     unsigned int nsaddrs,
	     const struct in_addr saddrs[],
466
	     const struct in_addr smasks[],
467
468
	     unsigned int ndaddrs,
	     const struct in_addr daddrs[],
469
	     const struct in_addr dmasks[],
470
471
472
473
474
475
	     bool verbose, struct nft_handle *h)
{
	unsigned int i, j;
	int ret = 1;

	for (i = 0; i < nsaddrs; i++) {
476
		cs->arp.arp.src.s_addr = saddrs[i].s_addr;
477
		cs->arp.arp.smsk.s_addr = smasks[i].s_addr;
478
		for (j = 0; j < ndaddrs; j++) {
479
			cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
480
			cs->arp.arp.tmsk.s_addr = dmasks[j].s_addr;
481
			ret = nft_cmd_rule_delete(h, chain, table, cs, verbose);
482
483
484
485
486
487
		}
	}

	return ret;
}

488
int nft_init_arp(struct nft_handle *h, const char *pname)
489
{
490
491
492
493
494
495
496
497
498
499
500
501
	arptables_globals.program_name = pname;
	if (xtables_init_all(&arptables_globals, NFPROTO_ARP) < 0) {
		fprintf(stderr, "%s/%s Failed to initialize arptables-compat\n",
			arptables_globals.program_name,
			arptables_globals.program_version);
		exit(1);
	}

#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
	init_extensionsa();
#endif

502
	if (nft_init(h, NFPROTO_ARP, xtables_arp) < 0)
503
504
505
506
507
508
509
510
511
512
513
		xtables_error(OTHER_PROBLEM,
			      "Could not initialize nftables layer.");

	return 0;
}

int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
		  bool restore)
{
	struct iptables_command_state cs = {
		.jumpto = "",
514
515
516
517
518
519
		.arp.arp = {
			.arhln = 6,
			.arhln_mask = 255,
			.arhrd = htons(ARPHRD_ETHER),
			.arhrd_mask = 65535,
		},
520
	};
521
522
	int invert = 0;
	unsigned int nsaddrs = 0, ndaddrs = 0;
523
524
	struct in_addr *saddrs = NULL, *smasks = NULL;
	struct in_addr *daddrs = NULL, *dmasks = NULL;
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

	int c, verbose = 0;
	const char *chain = NULL;
	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
	const char *policy = NULL, *newname = NULL;
	unsigned int rulenum = 0, options = 0, command = 0;
	const char *pcnt = NULL, *bcnt = NULL;
	int ret = 1;
	struct xtables_target *t;

	/* re-set optind to 0 in case do_command gets called
	 * a second time */
	optind = 0;

	for (t = xtables_targets; t; t = t->next) {
		t->tflags = 0;
		t->used = 0;
	}

	/* Suppress error messages: we may add new options if we
	    demand-load a protocol. */
	opterr = 0;

548
	opts = xt_params->orig_opts;
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
	while ((c = getopt_long(argc, argv,
	   "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
					   opts, NULL)) != -1) {
		switch (c) {
			/*
			 * Command selection
			 */
		case 'A':
			add_command(&command, CMD_APPEND, CMD_NONE,
				    invert);
			chain = optarg;
			break;

		case 'D':
			add_command(&command, CMD_DELETE, CMD_NONE,
				    invert);
			chain = optarg;
566
			if (xs_has_arg(argc, argv)) {
567
568
569
570
571
572
573
574
575
				rulenum = parse_rulenumber(argv[optind++]);
				command = CMD_DELETE_NUM;
			}
			break;

		case 'R':
			add_command(&command, CMD_REPLACE, CMD_NONE,
				    invert);
			chain = optarg;
576
			if (xs_has_arg(argc, argv))
577
578
579
580
581
582
583
584
585
586
587
				rulenum = parse_rulenumber(argv[optind++]);
			else
				xtables_error(PARAMETER_PROBLEM,
					      "-%c requires a rule number",
					      cmd2char(CMD_REPLACE));
			break;

		case 'I':
			add_command(&command, CMD_INSERT, CMD_NONE,
				    invert);
			chain = optarg;
588
			if (xs_has_arg(argc, argv))
589
590
591
592
593
594
595
596
				rulenum = parse_rulenumber(argv[optind++]);
			else rulenum = 1;
			break;

		case 'L':
			add_command(&command, CMD_LIST, CMD_ZERO,
				    invert);
			if (optarg) chain = optarg;
597
			else if (xs_has_arg(argc, argv))
598
599
600
601
602
603
604
				chain = argv[optind++];
			break;

		case 'F':
			add_command(&command, CMD_FLUSH, CMD_NONE,
				    invert);
			if (optarg) chain = optarg;
605
			else if (xs_has_arg(argc, argv))
606
607
608
609
610
611
612
				chain = argv[optind++];
			break;

		case 'Z':
			add_command(&command, CMD_ZERO, CMD_LIST,
				    invert);
			if (optarg) chain = optarg;
613
			else if (xs_has_arg(argc, argv))
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
				chain = argv[optind++];
			break;

		case 'N':
			if (optarg && *optarg == '-')
				xtables_error(PARAMETER_PROBLEM,
					      "chain name not allowed to start "
					      "with `-'\n");
			if (xtables_find_target(optarg, XTF_TRY_LOAD))
				xtables_error(PARAMETER_PROBLEM,
						"chain name may not clash "
						"with target name\n");
			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
				    invert);
			chain = optarg;
			break;

		case 'X':
			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
				    invert);
			if (optarg) chain = optarg;
635
			else if (xs_has_arg(argc, argv))
636
637
638
639
640
641
642
				chain = argv[optind++];
			break;

		case 'E':
			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
				    invert);
			chain = optarg;
643
			if (xs_has_arg(argc, argv))
644
645
646
647
648
649
650
651
652
653
654
655
				newname = argv[optind++];
			else
				xtables_error(PARAMETER_PROBLEM,
					      "-%c requires old-chain-name and "
					      "new-chain-name",
					      cmd2char(CMD_RENAME_CHAIN));
			break;

		case 'P':
			add_command(&command, CMD_SET_POLICY, CMD_NONE,
				    invert);
			chain = optarg;
656
			if (xs_has_arg(argc, argv))
657
658
659
660
661
662
663
664
665
666
667
				policy = argv[optind++];
			else
				xtables_error(PARAMETER_PROBLEM,
					      "-%c requires a chain and a policy",
					      cmd2char(CMD_SET_POLICY));
			break;

		case 'h':
			if (!optarg)
				optarg = argv[optind];

668
669
			printhelp();
			command = CMD_NONE;
670
671
672
			break;
		case 's':
			check_inverse(optarg, &invert, &optind, argc);
673
			set_option(&options, OPT_SOURCE, &cs.arp.arp.invflags,
674
675
676
677
678
679
				   invert);
			shostnetworkmask = argv[optind-1];
			break;

		case 'd':
			check_inverse(optarg, &invert, &optind, argc);
680
			set_option(&options, OPT_DESTINATION, &cs.arp.arp.invflags,
681
682
683
684
685
686
				   invert);
			dhostnetworkmask = argv[optind-1];
			break;

		case 2:/* src-mac */
			check_inverse(optarg, &invert, &optind, argc);
687
			set_option(&options, OPT_S_MAC, &cs.arp.arp.invflags,
688
689
				   invert);
			if (getmac_and_mask(argv[optind - 1],
690
			    cs.arp.arp.src_devaddr.addr, cs.arp.arp.src_devaddr.mask))
691
692
693
694
695
696
				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
						"source mac");
			break;

		case 3:/* dst-mac */
			check_inverse(optarg, &invert, &optind, argc);
697
			set_option(&options, OPT_D_MAC, &cs.arp.arp.invflags,
698
699
700
				   invert);

			if (getmac_and_mask(argv[optind - 1],
701
			    cs.arp.arp.tgt_devaddr.addr, cs.arp.arp.tgt_devaddr.mask))
702
703
704
705
706
707
				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
						"destination mac");
			break;

		case 'l':/* hardware length */
			check_inverse(optarg, &invert, &optind, argc);
708
			set_option(&options, OPT_H_LENGTH, &cs.arp.arp.invflags,
709
				   invert);
710
711
			getlength_and_mask(argv[optind - 1], &cs.arp.arp.arhln,
					   &cs.arp.arp.arhln_mask);
712

713
			if (cs.arp.arp.arhln != 6) {
714
715
716
717
718
719
720
				xtables_error(PARAMETER_PROBLEM,
					      "Only harware address length of"
					      " 6 is supported currently.");
			}

			break;

721
		case 8: /* was never supported, not even in arptables-legacy */
722
723
724
			xtables_error(PARAMETER_PROBLEM, "not supported");
		case 4:/* opcode */
			check_inverse(optarg, &invert, &optind, argc);
725
			set_option(&options, OPT_OPCODE, &cs.arp.arp.invflags,
726
				   invert);
727
728
			if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arpop,
					   &cs.arp.arp.arpop_mask, 10)) {
729
730
731
				int i;

				for (i = 0; i < NUMOPCODES; i++)
732
					if (!strcasecmp(arp_opcodes[i], optarg))
733
734
735
						break;
				if (i == NUMOPCODES)
					xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
736
				cs.arp.arp.arpop = htons(i+1);
737
738
739
740
741
			}
			break;

		case 5:/* h-type */
			check_inverse(optarg, &invert, &optind, argc);
742
			set_option(&options, OPT_H_TYPE, &cs.arp.arp.invflags,
743
				   invert);
744
745
			if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arhrd,
					   &cs.arp.arp.arhrd_mask, 16)) {
746
747
				if (strcasecmp(argv[optind-1], "Ethernet"))
					xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
748
				cs.arp.arp.arhrd = htons(1);
749
750
751
752
753
			}
			break;

		case 6:/* proto-type */
			check_inverse(optarg, &invert, &optind, argc);
754
			set_option(&options, OPT_P_TYPE, &cs.arp.arp.invflags,
755
				   invert);
756
757
			if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arpro,
					   &cs.arp.arp.arpro_mask, 0)) {
758
759
				if (strcasecmp(argv[optind-1], "ipv4"))
					xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
760
				cs.arp.arp.arpro = htons(0x800);
761
762
763
764
			}
			break;

		case 'j':
765
			set_option(&options, OPT_JUMP, &cs.arp.arp.invflags,
766
				   invert);
767
			command_jump(&cs, optarg);
768
769
770
771
			break;

		case 'i':
			check_inverse(optarg, &invert, &optind, argc);
772
			set_option(&options, OPT_VIANAMEIN, &cs.arp.arp.invflags,
773
				   invert);
774
775
776
			xtables_parse_interface(argv[optind-1],
						cs.arp.arp.iniface,
						cs.arp.arp.iniface_mask);
777
778
779
780
			break;

		case 'o':
			check_inverse(optarg, &invert, &optind, argc);
781
			set_option(&options, OPT_VIANAMEOUT, &cs.arp.arp.invflags,
782
				   invert);
783
784
785
			xtables_parse_interface(argv[optind-1],
						cs.arp.arp.outiface,
						cs.arp.arp.outiface_mask);
786
787
788
789
790
			break;

		case 'v':
			if (!verbose)
				set_option(&options, OPT_VERBOSE,
791
					   &cs.arp.arp.invflags, invert);
792
793
794
			verbose++;
			break;

795
796
		case 'm': /* ignored by arptables-legacy */
			break;
797
		case 'n':
798
			set_option(&options, OPT_NUMERIC, &cs.arp.arp.invflags,
799
800
801
802
803
804
805
				   invert);
			break;

		case 't':
			if (invert)
				xtables_error(PARAMETER_PROBLEM,
					      "unexpected ! flag before --table");
806
807
808
809
			/* ignore this option.
			 * arptables-legacy parses it, but libarptc doesn't use it.
			 * arptables only has a 'filter' table anyway.
			 */
810
811
812
813
			break;

		case 'V':
			if (invert)
814
				printf("Not %s ;-)\n", arptables_globals.program_version);
815
			else
816
				printf("%s v%s (nf_tables)\n",
817
818
				       arptables_globals.program_name,
				       arptables_globals.program_version);
819
820
821
			exit(0);

		case '0':
822
			set_option(&options, OPT_LINENUMBERS, &cs.arp.arp.invflags,
823
824
825
826
827
828
829
830
831
				   invert);
			break;

		case 'M':
			//modprobe = optarg;
			break;

		case 'c':

832
			set_option(&options, OPT_COUNTERS, &cs.arp.arp.invflags,
833
834
				   invert);
			pcnt = optarg;
835
			if (xs_has_arg(argc, argv))
836
837
838
839
840
841
				bcnt = argv[optind++];
			else
				xtables_error(PARAMETER_PROBLEM,
					      "-%c requires packet and byte counter",
					      opt2char(OPT_COUNTERS));

842
			if (sscanf(pcnt, "%llu", &cs.arp.counters.pcnt) != 1)
843
844
845
846
			xtables_error(PARAMETER_PROBLEM,
				"-%c packet counter not numeric",
				opt2char(OPT_COUNTERS));

847
			if (sscanf(bcnt, "%llu", &cs.arp.counters.bcnt) != 1)
848
849
850
851
852
853
854
855
856
857
858
859
860
				xtables_error(PARAMETER_PROBLEM,
					      "-%c byte counter not numeric",
					      opt2char(OPT_COUNTERS));

			break;


		case 1: /* non option */
			if (optarg[0] == '!' && optarg[1] == '\0') {
				if (invert)
					xtables_error(PARAMETER_PROBLEM,
						      "multiple consecutive ! not"
						      " allowed");
861
				invert = true;
862
863
864
865
866
867
868
869
870
				optarg[0] = '\0';
				continue;
			}
			printf("Bad argument `%s'\n", optarg);
			exit_tryhelp(2);

		default:
			if (cs.target) {
				xtables_option_tpcall(c, argv,
871
						      invert, cs.target, &cs.arp);
872
873
874
			}
			break;
		}
875
		invert = false;
876
877
878
879
880
881
882
883
884
885
886
887
888
	}

	if (cs.target)
		xtables_option_tfcall(cs.target);

	if (optind < argc)
		xtables_error(PARAMETER_PROBLEM,
			      "unknown arguments found on commandline");
	if (invert)
		xtables_error(PARAMETER_PROBLEM,
			      "nothing appropriate following !");

	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
889
		if (!(options & OPT_DESTINATION))
890
			dhostnetworkmask = "0.0.0.0/0";
891
		if (!(options & OPT_SOURCE))
892
893
894
895
			shostnetworkmask = "0.0.0.0/0";
	}

	if (shostnetworkmask)
896
897
		xtables_ipparse_multiple(shostnetworkmask, &saddrs,
					 &smasks, &nsaddrs);
898
899

	if (dhostnetworkmask)
900
901
		xtables_ipparse_multiple(dhostnetworkmask, &daddrs,
					 &dmasks, &ndaddrs);
902
903

	if ((nsaddrs > 1 || ndaddrs > 1) &&
904
	    (cs.arp.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
		xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
				" source or destination IP addresses");

	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
						 "specify a unique address");

	if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
		xtables_error(PARAMETER_PROBLEM,
				"chain name `%s' too long (must be under %i chars)",
				chain, ARPT_FUNCTION_MAXNAMELEN);

	if (command == CMD_APPEND
	    || command == CMD_DELETE
	    || command == CMD_INSERT
	    || command == CMD_REPLACE) {
		if (strcmp(chain, "PREROUTING") == 0
		    || strcmp(chain, "INPUT") == 0) {
			/* -o not valid with incoming packets. */
			if (options & OPT_VIANAMEOUT)
				xtables_error(PARAMETER_PROBLEM,
					      "Can't use -%c with %s\n",
					      opt2char(OPT_VIANAMEOUT),
					      chain);
		}

		if (strcmp(chain, "POSTROUTING") == 0
		    || strcmp(chain, "OUTPUT") == 0) {
			/* -i not valid with outgoing packets */
			if (options & OPT_VIANAMEIN)
				xtables_error(PARAMETER_PROBLEM,
						"Can't use -%c with %s\n",
						opt2char(OPT_VIANAMEIN),
						chain);
		}
	}

	switch (command) {
	case CMD_APPEND:
		ret = append_entry(h, chain, *table, &cs, 0,
945
946
				   nsaddrs, saddrs, smasks,
				   ndaddrs, daddrs, dmasks,
947
948
949
950
				   options&OPT_VERBOSE, true);
		break;
	case CMD_DELETE:
		ret = delete_entry(chain, *table, &cs,
951
952
				   nsaddrs, saddrs, smasks,
				   ndaddrs, daddrs, dmasks,
953
954
955
				   options&OPT_VERBOSE, h);
		break;
	case CMD_DELETE_NUM:
956
		ret = nft_cmd_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
957
958
959
		break;
	case CMD_REPLACE:
		ret = replace_entry(chain, *table, &cs, rulenum - 1,
960
961
				    saddrs, smasks, daddrs, dmasks,
				    options&OPT_VERBOSE, h);
962
963
964
		break;
	case CMD_INSERT:
		ret = append_entry(h, chain, *table, &cs, rulenum - 1,
965
966
				   nsaddrs, saddrs, smasks,
				   ndaddrs, daddrs, dmasks,
967
968
969
970
971
972
973
974
975
976
977
				   options&OPT_VERBOSE, false);
		break;
	case CMD_LIST:
		ret = list_entries(h, chain, *table,
				   rulenum,
				   options&OPT_VERBOSE,
				   options&OPT_NUMERIC,
				   /*options&OPT_EXPANDED*/0,
				   options&OPT_LINENUMBERS);
		break;
	case CMD_FLUSH:
978
		ret = nft_cmd_rule_flush(h, chain, *table, options & OPT_VERBOSE);
979
980
		break;
	case CMD_ZERO:
981
		ret = nft_cmd_chain_zero_counters(h, chain, *table,
982
					      options & OPT_VERBOSE);
983
984
985
986
987
988
989
990
		break;
	case CMD_LIST|CMD_ZERO:
		ret = list_entries(h, chain, *table, rulenum,
				   options&OPT_VERBOSE,
				   options&OPT_NUMERIC,
				   /*options&OPT_EXPANDED*/0,
				   options&OPT_LINENUMBERS);
		if (ret)
991
			ret = nft_cmd_chain_zero_counters(h, chain, *table,
992
						      options & OPT_VERBOSE);
993
994
		break;
	case CMD_NEW_CHAIN:
995
		ret = nft_cmd_chain_user_add(h, chain, *table);
996
997
		break;
	case CMD_DELETE_CHAIN:
998
		ret = nft_cmd_chain_user_del(h, chain, *table,
999
					 options & OPT_VERBOSE);
1000
1001
		break;
	case CMD_RENAME_CHAIN:
1002
		ret = nft_cmd_chain_user_rename(h, chain, *table, newname);
1003
1004
		break;
	case CMD_SET_POLICY:
1005
		ret = nft_cmd_chain_set(h, *table, chain, policy, NULL);
1006
1007
1008
1009
		if (ret < 0)
			xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
				      policy);
		break;
1010
1011
	case CMD_NONE:
		break;
1012
1013
1014
1015
1016
	default:
		/* We should never reach this... */
		exit_tryhelp(2);
	}

1017
1018
1019
1020
	free(saddrs);
	free(smasks);
	free(daddrs);
	free(dmasks);
1021

1022
	nft_clear_iptables_command_state(&cs);
1023
1024
	xtables_free_opts(1);

1025
1026
1027
1028
1029
/*	if (verbose > 1)
		dump_entries(*handle);*/

	return ret;
}