• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Code to take an arptables-style command line and do it. */
2 
3 /*
4  * arptables:
5  * Author: Bart De Schuymer <bdschuym@pandora.be>, but
6  * almost all code is from the iptables userspace program, which has main
7  * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
8  *
9  *	This program is free software; you can redistribute it and/or modify
10  *	it under the terms of the GNU General Public License as published by
11  *	the Free Software Foundation; either version 2 of the License, or
12  *	(at your option) any later version.
13  *
14  *	This program is distributed in the hope that it will be useful,
15  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *	GNU General Public License for more details.
18  *
19  *	You should have received a copy of the GNU General Public License
20  *	along with this program; if not, write to the Free Software
21  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 /*
25   Currently, only support for specifying hardware addresses for Ethernet
26   is available.
27   This tool is not luser-proof: you can specify an Ethernet source address
28   and set hardware length to something different than 6, f.e.
29 */
30 
31 #include <getopt.h>
32 #include <string.h>
33 #include <netdb.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <inttypes.h>
38 #include <dlfcn.h>
39 #include <ctype.h>
40 #include <stdarg.h>
41 #include <limits.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <sys/wait.h>
45 #include <net/if.h>
46 #include <netinet/ether.h>
47 #include <iptables.h>
48 #include <xtables.h>
49 
50 #include "xshared.h"
51 
52 #include "nft.h"
53 #include "nft-arp.h"
54 #include <linux/netfilter_arp/arp_tables.h>
55 
56 typedef char arpt_chainlabel[32];
57 
58 #ifndef TRUE
59 #define TRUE 1
60 #endif
61 #ifndef FALSE
62 #define FALSE 0
63 #endif
64 
65 /* XXX: command defined by nft-shared.h do not overlap with these two */
66 #undef CMD_CHECK
67 #undef CMD_RENAME_CHAIN
68 
69 #define CMD_NONE		0x0000U
70 #define CMD_INSERT		0x0001U
71 #define CMD_DELETE		0x0002U
72 #define CMD_DELETE_NUM		0x0004U
73 #define CMD_REPLACE		0x0008U
74 #define CMD_APPEND		0x0010U
75 #define CMD_LIST		0x0020U
76 #define CMD_FLUSH		0x0040U
77 #define CMD_ZERO		0x0080U
78 #define CMD_NEW_CHAIN		0x0100U
79 #define CMD_DELETE_CHAIN	0x0200U
80 #define CMD_SET_POLICY		0x0400U
81 #define CMD_CHECK		0x0800U
82 #define CMD_RENAME_CHAIN	0x1000U
83 #define NUMBER_OF_CMD	13
84 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
85 				 'N', 'X', 'P', 'E' };
86 
87 #define OPTION_OFFSET 256
88 
89 #define OPT_NONE	0x00000U
90 #define OPT_NUMERIC	0x00001U
91 #define OPT_S_IP	0x00002U
92 #define OPT_D_IP	0x00004U
93 #define OPT_S_MAC	0x00008U
94 #define OPT_D_MAC	0x00010U
95 #define OPT_H_LENGTH	0x00020U
96 #define OPT_P_LENGTH	0x00040U
97 #define OPT_OPCODE	0x00080U
98 #define OPT_H_TYPE	0x00100U
99 #define OPT_P_TYPE	0x00200U
100 #define OPT_JUMP	0x00400U
101 #define OPT_VERBOSE	0x00800U
102 #define OPT_VIANAMEIN	0x01000U
103 #define OPT_VIANAMEOUT	0x02000U
104 #define OPT_LINENUMBERS 0x04000U
105 #define OPT_COUNTERS	0x08000U
106 #define NUMBER_OF_OPT	16
107 static const char optflags[NUMBER_OF_OPT]
108 = { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};
109 
110 static struct option original_opts[] = {
111 	{ "append", 1, 0, 'A' },
112 	{ "delete", 1, 0,  'D' },
113 	{ "insert", 1, 0,  'I' },
114 	{ "replace", 1, 0,  'R' },
115 	{ "list", 2, 0,  'L' },
116 	{ "flush", 2, 0,  'F' },
117 	{ "zero", 2, 0,  'Z' },
118 	{ "new-chain", 1, 0,  'N' },
119 	{ "delete-chain", 2, 0,  'X' },
120 	{ "rename-chain", 1, 0,  'E' },
121 	{ "policy", 1, 0,  'P' },
122 	{ "source-ip", 1, 0, 's' },
123 	{ "destination-ip", 1, 0,  'd' },
124 	{ "src-ip", 1, 0,  's' },
125 	{ "dst-ip", 1, 0,  'd' },
126 	{ "source-mac", 1, 0, 2},
127 	{ "destination-mac", 1, 0, 3},
128 	{ "src-mac", 1, 0, 2},
129 	{ "dst-mac", 1, 0, 3},
130 	{ "h-length", 1, 0,  'l' },
131 	{ "p-length", 1, 0,  8 },
132 	{ "opcode", 1, 0,  4 },
133 	{ "h-type", 1, 0,  5 },
134 	{ "proto-type", 1, 0,  6 },
135 	{ "in-interface", 1, 0, 'i' },
136 	{ "jump", 1, 0, 'j' },
137 	{ "table", 1, 0, 't' },
138 	{ "match", 1, 0, 'm' },
139 	{ "numeric", 0, 0, 'n' },
140 	{ "out-interface", 1, 0, 'o' },
141 	{ "verbose", 0, 0, 'v' },
142 	{ "exact", 0, 0, 'x' },
143 	{ "version", 0, 0, 'V' },
144 	{ "help", 2, 0, 'h' },
145 	{ "line-numbers", 0, 0, '0' },
146 	{ "modprobe", 1, 0, 'M' },
147 	{ 0 }
148 };
149 
150 int RUNTIME_NF_ARP_NUMHOOKS = 3;
151 
152 static struct option *opts = original_opts;
153 static unsigned int global_option_offset = 0;
154 
155 extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
156 struct xtables_globals arptables_globals = {
157 	.option_offset		= 0,
158 	.program_version	= IPTABLES_VERSION,
159 	.orig_opts		= original_opts,
160 	.exit_err		= xtables_exit_error,
161 	.compat_rev		= nft_compatible_revision,
162 };
163 
164 /* Table of legal combinations of commands and options.  If any of the
165  * given commands make an option legal, that option is legal (applies to
166  * CMD_LIST and CMD_ZERO only).
167  * Key:
168  *  +  compulsory
169  *  x  illegal
170  *     optional
171  */
172 
173 static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
174 /* Well, it's better than "Re: Linux vs FreeBSD" */
175 {
176 	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
177 /*INSERT*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
178 /*DELETE*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
179 /*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
180 /*REPLACE*/   {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
181 /*APPEND*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
182 /*LIST*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
183 /*FLUSH*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
184 /*ZERO*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
185 /*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
186 /*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
187 /*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
188 /*CHECK*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
189 /*RENAME*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}
190 };
191 
192 static int inverse_for_options[NUMBER_OF_OPT] =
193 {
194 /* -n */ 0,
195 /* -s */ ARPT_INV_SRCIP,
196 /* -d */ ARPT_INV_TGTIP,
197 /* 2 */ ARPT_INV_SRCDEVADDR,
198 /* 3 */ ARPT_INV_TGTDEVADDR,
199 /* -l */ ARPT_INV_ARPHLN,
200 /* 8 */ 0,
201 /* 4 */ ARPT_INV_ARPOP,
202 /* 5 */ ARPT_INV_ARPHRD,
203 /* 6 */ ARPT_INV_ARPPRO,
204 /* -j */ 0,
205 /* -v */ 0,
206 /* -i */ ARPT_INV_VIA_IN,
207 /* -o */ ARPT_INV_VIA_OUT,
208 /*--line*/ 0,
209 /* -c */ 0,
210 };
211 
212 const char *program_version = XTABLES_VERSION;
213 const char *program_name = "arptables";
214 
215 /* A few hardcoded protocols for 'all' and in case the user has no
216    /etc/protocols */
217 struct pprot {
218 	char *name;
219 	u_int8_t num;
220 };
221 
222 /* Primitive headers... */
223 /* defined in netinet/in.h */
224 #if 0
225 #ifndef IPPROTO_ESP
226 #define IPPROTO_ESP 50
227 #endif
228 #ifndef IPPROTO_AH
229 #define IPPROTO_AH 51
230 #endif
231 #endif
232 
233 /***********************************************/
234 /* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
235 /***********************************************/
236 
237 unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
238 unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
239 unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
240 unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
241 unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
242 unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
243 
244 /*
245  * put the mac address into 6 (ETH_ALEN) bytes
246  */
getmac_and_mask(char * from,char * to,char * mask)247 static int getmac_and_mask(char *from, char *to, char *mask)
248 {
249 	char *p;
250 	int i;
251 	struct ether_addr *addr;
252 
253 	if (strcasecmp(from, "Unicast") == 0) {
254 		memcpy(to, mac_type_unicast, ETH_ALEN);
255 		memcpy(mask, msk_type_unicast, ETH_ALEN);
256 		return 0;
257 	}
258 	if (strcasecmp(from, "Multicast") == 0) {
259 		memcpy(to, mac_type_multicast, ETH_ALEN);
260 		memcpy(mask, msk_type_multicast, ETH_ALEN);
261 		return 0;
262 	}
263 	if (strcasecmp(from, "Broadcast") == 0) {
264 		memcpy(to, mac_type_broadcast, ETH_ALEN);
265 		memcpy(mask, msk_type_broadcast, ETH_ALEN);
266 		return 0;
267 	}
268 	if ( (p = strrchr(from, '/')) != NULL) {
269 		*p = '\0';
270 		if (!(addr = ether_aton(p + 1)))
271 			return -1;
272 		memcpy(mask, addr, ETH_ALEN);
273 	} else
274 		memset(mask, 0xff, ETH_ALEN);
275 	if (!(addr = ether_aton(from)))
276 		return -1;
277 	memcpy(to, addr, ETH_ALEN);
278 	for (i = 0; i < ETH_ALEN; i++)
279 		to[i] &= mask[i];
280 	return 0;
281 }
282 
getlength_and_mask(char * from,uint8_t * to,uint8_t * mask)283 static int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask)
284 {
285 	char *p, *buffer;
286 	int i;
287 
288 	if ( (p = strrchr(from, '/')) != NULL) {
289 		*p = '\0';
290 		i = strtol(p+1, &buffer, 10);
291 		if (*buffer != '\0' || i < 0 || i > 255)
292 			return -1;
293 		*mask = (uint8_t)i;
294 	} else
295 		*mask = 255;
296 	i = strtol(from, &buffer, 10);
297 	if (*buffer != '\0' || i < 0 || i > 255)
298 		return -1;
299 	*to = (uint8_t)i;
300 	return 0;
301 }
302 
get16_and_mask(char * from,uint16_t * to,uint16_t * mask,int base)303 static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
304 {
305 	char *p, *buffer;
306 	int i;
307 
308 	if ( (p = strrchr(from, '/')) != NULL) {
309 		*p = '\0';
310 		i = strtol(p+1, &buffer, base);
311 		if (*buffer != '\0' || i < 0 || i > 65535)
312 			return -1;
313 		*mask = htons((uint16_t)i);
314 	} else
315 		*mask = 65535;
316 	i = strtol(from, &buffer, base);
317 	if (*buffer != '\0' || i < 0 || i > 65535)
318 		return -1;
319 	*to = htons((uint16_t)i);
320 	return 0;
321 }
322 
323 static int
string_to_number(const char * s,unsigned int min,unsigned int max,unsigned int * ret)324 string_to_number(const char *s, unsigned int min, unsigned int max,
325 		 unsigned int *ret)
326 {
327 	long number;
328 	char *end;
329 
330 	/* Handle hex, octal, etc. */
331 	errno = 0;
332 	number = strtol(s, &end, 0);
333 	if (*end == '\0' && end != s) {
334 		/* we parsed a number, let's see if we want this */
335 		if (errno != ERANGE && min <= number && number <= max) {
336 			*ret = number;
337 			return 0;
338 		}
339 	}
340 	return -1;
341 }
342 
343 /*********************************************/
344 /* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
345 /*********************************************/
346 
347 static struct in_addr *
dotted_to_addr(const char * dotted)348 dotted_to_addr(const char *dotted)
349 {
350 	static struct in_addr addr;
351 	unsigned char *addrp;
352 	char *p, *q;
353 	unsigned int onebyte;
354 	int i;
355 	char buf[20];
356 
357 	/* copy dotted string, because we need to modify it */
358 	strncpy(buf, dotted, sizeof(buf) - 1);
359 	addrp = (unsigned char *) &(addr.s_addr);
360 
361 	p = buf;
362 	for (i = 0; i < 3; i++) {
363 		if ((q = strchr(p, '.')) == NULL)
364 			return (struct in_addr *) NULL;
365 
366 		*q = '\0';
367 		if (string_to_number(p, 0, 255, &onebyte) == -1)
368 			return (struct in_addr *) NULL;
369 
370 		addrp[i] = (unsigned char) onebyte;
371 		p = q + 1;
372 	}
373 
374 	/* we've checked 3 bytes, now we check the last one */
375 	if (string_to_number(p, 0, 255, &onebyte) == -1)
376 		return (struct in_addr *) NULL;
377 
378 	addrp[3] = (unsigned char) onebyte;
379 
380 	return &addr;
381 }
382 
383 static struct in_addr *
network_to_addr(const char * name)384 network_to_addr(const char *name)
385 {
386 	struct netent *net;
387 	static struct in_addr addr;
388 
389 	if ((net = getnetbyname(name)) != NULL) {
390 		if (net->n_addrtype != AF_INET)
391 			return (struct in_addr *) NULL;
392 		addr.s_addr = htonl((unsigned long) net->n_net);
393 		return &addr;
394 	}
395 
396 	return (struct in_addr *) NULL;
397 }
398 
399 static void
inaddrcpy(struct in_addr * dst,struct in_addr * src)400 inaddrcpy(struct in_addr *dst, struct in_addr *src)
401 {
402 	/* memcpy(dst, src, sizeof(struct in_addr)); */
403 	dst->s_addr = src->s_addr;
404 }
405 
406 static void
exit_tryhelp(int status)407 exit_tryhelp(int status)
408 {
409 	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
410 			program_name, program_name );
411 	exit(status);
412 }
413 
414 static void
exit_printhelp(void)415 exit_printhelp(void)
416 {
417 	struct xtables_target *t = NULL;
418 	int i;
419 
420 	printf("%s v%s\n\n"
421 "Usage: %s -[AD] chain rule-specification [options]\n"
422 "       %s -[RI] chain rulenum rule-specification [options]\n"
423 "       %s -D chain rulenum [options]\n"
424 "       %s -[LFZ] [chain] [options]\n"
425 "       %s -[NX] chain\n"
426 "       %s -E old-chain-name new-chain-name\n"
427 "       %s -P chain target [options]\n"
428 "       %s -h (print this help information)\n\n",
429 	       program_name, program_version, program_name, program_name,
430 	       program_name, program_name, program_name, program_name,
431 	       program_name, program_name);
432 
433 	printf(
434 "Commands:\n"
435 "Either long or short options are allowed.\n"
436 "  --append  -A chain		Append to chain\n"
437 "  --delete  -D chain		Delete matching rule from chain\n"
438 "  --delete  -D chain rulenum\n"
439 "				Delete rule rulenum (1 = first) from chain\n"
440 "  --insert  -I chain [rulenum]\n"
441 "				Insert in chain as rulenum (default 1=first)\n"
442 "  --replace -R chain rulenum\n"
443 "				Replace rule rulenum (1 = first) in chain\n"
444 "  --list    -L [chain]		List the rules in a chain or all chains\n"
445 "  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
446 "  --zero    -Z [chain]		Zero counters in chain or all chains\n"
447 "  --new     -N chain		Create a new user-defined chain\n"
448 "  --delete-chain\n"
449 "            -X [chain]		Delete a user-defined chain\n"
450 "  --policy  -P chain target\n"
451 "				Change policy on chain to target\n"
452 "  --rename-chain\n"
453 "            -E old-chain new-chain\n"
454 "				Change chain name, (moving any references)\n"
455 
456 "Options:\n"
457 "  --source-ip	-s [!] address[/mask]\n"
458 "				source specification\n"
459 "  --destination-ip -d [!] address[/mask]\n"
460 "				destination specification\n"
461 "  --source-mac [!] address[/mask]\n"
462 "  --destination-mac [!] address[/mask]\n"
463 "  --h-length   -l   length[/mask] hardware length (nr of bytes)\n"
464 "  --opcode code[/mask] operation code (2 bytes)\n"
465 "  --h-type   type[/mask]  hardware type (2 bytes, hexadecimal)\n"
466 "  --proto-type   type[/mask]  protocol type (2 bytes)\n"
467 "  --in-interface -i [!] input name[+]\n"
468 "				network interface name ([+] for wildcard)\n"
469 "  --out-interface -o [!] output name[+]\n"
470 "				network interface name ([+] for wildcard)\n"
471 "  --jump	-j target\n"
472 "				target for rule (may load target extension)\n"
473 "  --match	-m match\n"
474 "				extended match (may load extension)\n"
475 "  --numeric	-n		numeric output of addresses and ports\n"
476 "  --table	-t table	table to manipulate (default: `filter')\n"
477 "  --verbose	-v		verbose mode\n"
478 "  --line-numbers		print line numbers when listing\n"
479 "  --exact	-x		expand numbers (display exact values)\n"
480 "  --modprobe=<command>		try to insert modules using this command\n"
481 "  --set-counters PKTS BYTES	set the counter during insert/append\n"
482 "[!] --version	-V		print package version.\n");
483 	printf(" opcode strings: \n");
484         for (i = 0; i < NUMOPCODES; i++)
485                 printf(" %d = %s\n", i + 1, opcodes[i]);
486         printf(
487 " hardware type string: 1 = Ethernet\n"
488 " protocol type string: 0x800 = IPv4\n");
489 
490 	/* Print out any special helps. A user might like to be able
491 		to add a --help to the commandline, and see expected
492 		results. So we call help for all matches & targets */
493 	for (t = xtables_targets; t; t = t->next) {
494 		if (strcmp(t->name, "CLASSIFY") && strcmp(t->name, "mangle"))
495 			continue;
496 		printf("\n");
497 		t->help();
498 	}
499 	exit(0);
500 }
501 
502 static void
generic_opt_check(int command,int options)503 generic_opt_check(int command, int options)
504 {
505 	int i, j, legal = 0;
506 
507 	/* Check that commands are valid with options.  Complicated by the
508 	 * fact that if an option is legal with *any* command given, it is
509 	 * legal overall (ie. -z and -l).
510 	 */
511 	for (i = 0; i < NUMBER_OF_OPT; i++) {
512 		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
513 
514 		for (j = 0; j < NUMBER_OF_CMD; j++) {
515 			if (!(command & (1<<j)))
516 				continue;
517 
518 			if (!(options & (1<<i))) {
519 				if (commands_v_options[j][i] == '+')
520 					xtables_error(PARAMETER_PROBLEM,
521 						      "You need to supply the `-%c' "
522 						      "option for this command\n",
523 						      optflags[i]);
524 			} else {
525 				if (commands_v_options[j][i] != 'x')
526 					legal = 1;
527 				else if (legal == 0)
528 					legal = -1;
529 			}
530 		}
531 		if (legal == -1)
532 			xtables_error(PARAMETER_PROBLEM,
533 				      "Illegal option `-%c' with this command\n",
534 				      optflags[i]);
535 	}
536 }
537 
538 static char
opt2char(int option)539 opt2char(int option)
540 {
541 	const char *ptr;
542 	for (ptr = optflags; option > 1; option >>= 1, ptr++);
543 
544 	return *ptr;
545 }
546 
547 static char
cmd2char(int option)548 cmd2char(int option)
549 {
550 	const char *ptr;
551 	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
552 
553 	return *ptr;
554 }
555 
556 static void
add_command(unsigned int * cmd,const int newcmd,const unsigned int othercmds,int invert)557 add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, int invert)
558 {
559 	if (invert)
560 		xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
561 	if (*cmd & (~othercmds))
562 		xtables_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
563 			      cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
564 	*cmd |= newcmd;
565 }
566 
567 static int
check_inverse(const char option[],int * invert,int * optidx,int argc)568 check_inverse(const char option[], int *invert, int *optidx, int argc)
569 {
570 	if (option && strcmp(option, "!") == 0) {
571 		if (*invert)
572 			xtables_error(PARAMETER_PROBLEM,
573 				      "Multiple `!' flags not allowed");
574 		*invert = TRUE;
575 		if (optidx) {
576 			*optidx = *optidx+1;
577 			if (argc && *optidx > argc)
578 				xtables_error(PARAMETER_PROBLEM,
579 					      "no argument following `!'");
580 		}
581 
582 		return TRUE;
583 	}
584 	return FALSE;
585 }
586 
587 static struct in_addr *
host_to_addr(const char * name,unsigned int * naddr)588 host_to_addr(const char *name, unsigned int *naddr)
589 {
590 	struct in_addr *addr;
591 	struct addrinfo hints;
592 	struct addrinfo *res, *p;
593 	int err;
594 	unsigned int i;
595 
596 	memset(&hints, 0, sizeof(hints));
597 	hints.ai_flags	  = AI_CANONNAME;
598 	hints.ai_family	  = AF_INET;
599 	hints.ai_socktype = SOCK_RAW;
600 
601 	*naddr = 0;
602 	err = getaddrinfo(name, NULL, &hints, &res);
603 	if (err != 0)
604 		return NULL;
605 	else {
606 		for (p = res; p != NULL; p = p->ai_next)
607 			(*naddr)++;
608 		addr = xtables_calloc(*naddr, sizeof(struct in_addr));
609 		for (i = 0, p = res; p != NULL; p = p->ai_next)
610 			memcpy(&addr[i++],
611 			       &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
612 			       sizeof(struct in_addr));
613 		freeaddrinfo(res);
614 		return addr;
615 	}
616 
617 	return (struct in_addr *) NULL;
618 }
619 
620 /*
621  *	All functions starting with "parse" should succeed, otherwise
622  *	the program fails.
623  *	Most routines return pointers to static data that may change
624  *	between calls to the same or other routines with a few exceptions:
625  *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
626  *	return global static data.
627 */
628 
629 static struct in_addr *
parse_hostnetwork(const char * name,unsigned int * naddrs)630 parse_hostnetwork(const char *name, unsigned int *naddrs)
631 {
632 	struct in_addr *addrp, *addrptmp;
633 
634 	if ((addrptmp = dotted_to_addr(name)) != NULL ||
635 	    (addrptmp = network_to_addr(name)) != NULL) {
636 		addrp = xtables_malloc(sizeof(struct in_addr));
637 		inaddrcpy(addrp, addrptmp);
638 		*naddrs = 1;
639 		return addrp;
640 	}
641 	if ((addrp = host_to_addr(name, naddrs)) != NULL)
642 		return addrp;
643 
644 	xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
645 }
646 
647 static struct in_addr *
parse_mask(char * mask)648 parse_mask(char *mask)
649 {
650 	static struct in_addr maskaddr;
651 	struct in_addr *addrp;
652 	unsigned int bits;
653 
654 	if (mask == NULL) {
655 		/* no mask at all defaults to 32 bits */
656 		maskaddr.s_addr = 0xFFFFFFFF;
657 		return &maskaddr;
658 	}
659 	if ((addrp = dotted_to_addr(mask)) != NULL)
660 		/* dotted_to_addr already returns a network byte order addr */
661 		return addrp;
662 	if (string_to_number(mask, 0, 32, &bits) == -1)
663 		xtables_error(PARAMETER_PROBLEM,
664 			      "invalid mask `%s' specified", mask);
665 	if (bits != 0) {
666 		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
667 		return &maskaddr;
668 	}
669 
670 	maskaddr.s_addr = 0L;
671 	return &maskaddr;
672 }
673 
674 static void
parse_hostnetworkmask(const char * name,struct in_addr ** addrpp,struct in_addr * maskp,unsigned int * naddrs)675 parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
676 		      struct in_addr *maskp, unsigned int *naddrs)
677 {
678 	struct in_addr *addrp;
679 	char buf[256];
680 	char *p;
681 	int i, j, k, n;
682 
683 	strncpy(buf, name, sizeof(buf) - 1);
684 	if ((p = strrchr(buf, '/')) != NULL) {
685 		*p = '\0';
686 		addrp = parse_mask(p + 1);
687 	} else
688 		addrp = parse_mask(NULL);
689 	inaddrcpy(maskp, addrp);
690 
691 	/* if a null mask is given, the name is ignored, like in "any/0" */
692 	if (maskp->s_addr == 0L)
693 		strcpy(buf, "0.0.0.0");
694 
695 	addrp = *addrpp = parse_hostnetwork(buf, naddrs);
696 	n = *naddrs;
697 	for (i = 0, j = 0; i < n; i++) {
698 		addrp[j++].s_addr &= maskp->s_addr;
699 		for (k = 0; k < j - 1; k++) {
700 			if (addrp[k].s_addr == addrp[j - 1].s_addr) {
701 				(*naddrs)--;
702 				j--;
703 				break;
704 			}
705 		}
706 	}
707 }
708 
709 static void
parse_interface(const char * arg,char * vianame,unsigned char * mask)710 parse_interface(const char *arg, char *vianame, unsigned char *mask)
711 {
712 	int vialen = strlen(arg);
713 	unsigned int i;
714 
715 	memset(mask, 0, IFNAMSIZ);
716 	memset(vianame, 0, IFNAMSIZ);
717 
718 	if (vialen + 1 > IFNAMSIZ)
719 		xtables_error(PARAMETER_PROBLEM,
720 			      "interface name `%s' must be shorter than IFNAMSIZ"
721 			      " (%i)", arg, IFNAMSIZ-1);
722 
723 	strcpy(vianame, arg);
724 	if (vialen == 0)
725 		memset(mask, 0, IFNAMSIZ);
726 	else if (vianame[vialen - 1] == '+') {
727 		memset(mask, 0xFF, vialen - 1);
728 		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
729 		/* Don't remove `+' here! -HW */
730 	} else {
731 		/* Include nul-terminator in match */
732 		memset(mask, 0xFF, vialen + 1);
733 		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
734 		for (i = 0; vianame[i]; i++) {
735 			if (!isalnum(vianame[i])
736 			    && vianame[i] != '_'
737 			    && vianame[i] != '.') {
738 				printf("Warning: weird character in interface"
739 				       " `%s' (No aliases, :, ! or *).\n",
740 				       vianame);
741 				break;
742 			}
743 		}
744 	}
745 }
746 
747 /* Can't be zero. */
748 static int
parse_rulenumber(const char * rule)749 parse_rulenumber(const char *rule)
750 {
751 	unsigned int rulenum;
752 
753 	if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
754 		xtables_error(PARAMETER_PROBLEM,
755 			      "Invalid rule number `%s'", rule);
756 
757 	return rulenum;
758 }
759 
760 static const char *
parse_target(const char * targetname)761 parse_target(const char *targetname)
762 {
763 	const char *ptr;
764 
765 	if (strlen(targetname) < 1)
766 		xtables_error(PARAMETER_PROBLEM,
767 			      "Invalid target name (too short)");
768 
769 	if (strlen(targetname)+1 > sizeof(arpt_chainlabel))
770 		xtables_error(PARAMETER_PROBLEM,
771 			      "Invalid target name `%s' (%zu chars max)",
772 			      targetname, sizeof(arpt_chainlabel)-1);
773 
774 	for (ptr = targetname; *ptr; ptr++)
775 		if (isspace(*ptr))
776 			xtables_error(PARAMETER_PROBLEM,
777 				      "Invalid target name `%s'", targetname);
778 	return targetname;
779 }
780 
781 static void
set_option(unsigned int * options,unsigned int option,u_int16_t * invflg,int invert)782 set_option(unsigned int *options, unsigned int option, u_int16_t *invflg,
783 	   int invert)
784 {
785 	if (*options & option)
786 		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
787 			      opt2char(option));
788 	*options |= option;
789 
790 	if (invert) {
791 		unsigned int i;
792 		for (i = 0; 1 << i != option; i++);
793 
794 		if (!inverse_for_options[i])
795 			xtables_error(PARAMETER_PROBLEM,
796 				      "cannot have ! before -%c",
797 				      opt2char(option));
798 		*invflg |= inverse_for_options[i];
799 	}
800 }
801 
802 static int
list_entries(struct nft_handle * h,const char * chain,const char * table,int rulenum,int verbose,int numeric,int expanded,int linenumbers)803 list_entries(struct nft_handle *h, const char *chain, const char *table,
804 	     int rulenum, int verbose, int numeric, int expanded,
805 	     int linenumbers)
806 {
807 	unsigned int format;
808 
809 	format = FMT_OPTIONS;
810 	if (!verbose)
811 		format |= FMT_NOCOUNTS;
812 	else
813 		format |= FMT_VIA;
814 
815 	if (numeric)
816 		format |= FMT_NUMERIC;
817 
818 	if (!expanded)
819 		format |= FMT_KILOMEGAGIGA;
820 
821 	if (linenumbers)
822 		format |= FMT_LINENUMBERS;
823 
824 	return nft_rule_list(h, chain, table, rulenum, format);
825 }
826 
command_jump(struct arpt_entry * fw,const char * jumpto)827 static struct xtables_target *command_jump(struct arpt_entry *fw,
828 					   const char *jumpto)
829 {
830 	struct xtables_target *target;
831 	size_t size;
832 
833 	/* XTF_TRY_LOAD (may be chain name) */
834 	target = xtables_find_target(jumpto, XTF_TRY_LOAD);
835 
836 	if (!target)
837 		return NULL;
838 
839 	size = XT_ALIGN(sizeof(struct xt_entry_target))
840 		+ target->size;
841 
842 	target->t = xtables_calloc(1, size);
843 	target->t->u.target_size = size;
844 	strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
845 	target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
846 	target->t->u.user.revision = target->revision;
847 
848 	xs_init_target(target);
849 
850 	if (target->x6_options != NULL)
851 		opts = xtables_options_xfrm(arptables_globals.orig_opts,
852 					    opts, target->x6_options,
853 					    &target->option_offset);
854 	else
855 		opts = xtables_merge_options(arptables_globals.orig_opts,
856 					     opts, target->extra_opts,
857 					     &target->option_offset);
858 
859 	return target;
860 }
861 
862 static int
append_entry(struct nft_handle * h,const char * chain,const char * table,struct arptables_command_state * cs,int rulenum,unsigned int nsaddrs,const struct in_addr saddrs[],unsigned int ndaddrs,const struct in_addr daddrs[],bool verbose,bool append)863 append_entry(struct nft_handle *h,
864 	     const char *chain,
865 	     const char *table,
866 	     struct arptables_command_state *cs,
867 	     int rulenum,
868 	     unsigned int nsaddrs,
869 	     const struct in_addr saddrs[],
870 	     unsigned int ndaddrs,
871 	     const struct in_addr daddrs[],
872 	     bool verbose, bool append)
873 {
874 	unsigned int i, j;
875 	int ret = 1;
876 
877 	for (i = 0; i < nsaddrs; i++) {
878 		cs->fw.arp.src.s_addr = saddrs[i].s_addr;
879 		for (j = 0; j < ndaddrs; j++) {
880 			cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
881 			if (append) {
882 				ret = nft_rule_append(h, chain, table, cs, 0,
883 						      verbose);
884 			} else {
885 				ret = nft_rule_insert(h, chain, table, cs,
886 						      rulenum, verbose);
887 			}
888 		}
889 	}
890 
891 	return ret;
892 }
893 
894 static int
replace_entry(const char * chain,const char * table,struct arptables_command_state * cs,unsigned int rulenum,const struct in_addr * saddr,const struct in_addr * daddr,bool verbose,struct nft_handle * h)895 replace_entry(const char *chain,
896 	      const char *table,
897 	      struct arptables_command_state *cs,
898 	      unsigned int rulenum,
899 	      const struct in_addr *saddr,
900 	      const struct in_addr *daddr,
901 	      bool verbose, struct nft_handle *h)
902 {
903 	cs->fw.arp.src.s_addr = saddr->s_addr;
904 	cs->fw.arp.tgt.s_addr = daddr->s_addr;
905 
906 	return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
907 }
908 
909 static int
delete_entry(const char * chain,const char * table,struct arptables_command_state * cs,unsigned int nsaddrs,const struct in_addr saddrs[],unsigned int ndaddrs,const struct in_addr daddrs[],bool verbose,struct nft_handle * h)910 delete_entry(const char *chain,
911 	     const char *table,
912 	     struct arptables_command_state *cs,
913 	     unsigned int nsaddrs,
914 	     const struct in_addr saddrs[],
915 	     unsigned int ndaddrs,
916 	     const struct in_addr daddrs[],
917 	     bool verbose, struct nft_handle *h)
918 {
919 	unsigned int i, j;
920 	int ret = 1;
921 
922 	for (i = 0; i < nsaddrs; i++) {
923 		cs->fw.arp.src.s_addr = saddrs[i].s_addr;
924 		for (j = 0; j < ndaddrs; j++) {
925 			cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
926 			ret = nft_rule_delete(h, chain, table, cs, verbose);
927 		}
928 	}
929 
930 	return ret;
931 }
932 
do_commandarp(struct nft_handle * h,int argc,char * argv[],char ** table)933 int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
934 {
935 	struct arptables_command_state cs;
936 	int invert = 0;
937 	unsigned int nsaddrs = 0, ndaddrs = 0;
938 	struct in_addr *saddrs = NULL, *daddrs = NULL;
939 
940 	int c, verbose = 0;
941 	const char *chain = NULL;
942 	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
943 	const char *policy = NULL, *newname = NULL;
944 	unsigned int rulenum = 0, options = 0, command = 0;
945 	const char *pcnt = NULL, *bcnt = NULL;
946 	int ret = 1;
947 	struct xtables_target *t;
948 
949 	memset(&cs, 0, sizeof(cs));
950 	cs.jumpto = "";
951 
952 	opts = original_opts;
953 	global_option_offset = 0;
954 
955 	xtables_globals.orig_opts = original_opts;
956 
957 	/* re-set optind to 0 in case do_command gets called
958 	 * a second time */
959 	optind = 0;
960 
961 	for (t = xtables_targets; t; t = t->next) {
962 		t->tflags = 0;
963 		t->used = 0;
964 	}
965 
966 	/* Suppress error messages: we may add new options if we
967 	    demand-load a protocol. */
968 	opterr = 0;
969 
970 	while ((c = getopt_long(argc, argv,
971 	   "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
972 					   opts, NULL)) != -1) {
973 		switch (c) {
974 			/*
975 			 * Command selection
976 			 */
977 		case 'A':
978 			add_command(&command, CMD_APPEND, CMD_NONE,
979 				    invert);
980 			chain = optarg;
981 			break;
982 
983 		case 'D':
984 			add_command(&command, CMD_DELETE, CMD_NONE,
985 				    invert);
986 			chain = optarg;
987 			if (xs_has_arg(argc, argv)) {
988 				rulenum = parse_rulenumber(argv[optind++]);
989 				command = CMD_DELETE_NUM;
990 			}
991 			break;
992 
993 		case 'R':
994 			add_command(&command, CMD_REPLACE, CMD_NONE,
995 				    invert);
996 			chain = optarg;
997 			if (xs_has_arg(argc, argv))
998 				rulenum = parse_rulenumber(argv[optind++]);
999 			else
1000 				xtables_error(PARAMETER_PROBLEM,
1001 					      "-%c requires a rule number",
1002 					      cmd2char(CMD_REPLACE));
1003 			break;
1004 
1005 		case 'I':
1006 			add_command(&command, CMD_INSERT, CMD_NONE,
1007 				    invert);
1008 			chain = optarg;
1009 			if (xs_has_arg(argc, argv))
1010 				rulenum = parse_rulenumber(argv[optind++]);
1011 			else rulenum = 1;
1012 			break;
1013 
1014 		case 'L':
1015 			add_command(&command, CMD_LIST, CMD_ZERO,
1016 				    invert);
1017 			if (optarg) chain = optarg;
1018 			else if (xs_has_arg(argc, argv))
1019 				chain = argv[optind++];
1020 			break;
1021 
1022 		case 'F':
1023 			add_command(&command, CMD_FLUSH, CMD_NONE,
1024 				    invert);
1025 			if (optarg) chain = optarg;
1026 			else if (xs_has_arg(argc, argv))
1027 				chain = argv[optind++];
1028 			break;
1029 
1030 		case 'Z':
1031 			add_command(&command, CMD_ZERO, CMD_LIST,
1032 				    invert);
1033 			if (optarg) chain = optarg;
1034 			else if (xs_has_arg(argc, argv))
1035 				chain = argv[optind++];
1036 			break;
1037 
1038 		case 'N':
1039 			if (optarg && *optarg == '-')
1040 				xtables_error(PARAMETER_PROBLEM,
1041 					      "chain name not allowed to start "
1042 					      "with `-'\n");
1043 			if (xtables_find_target(optarg, XTF_TRY_LOAD))
1044 				xtables_error(PARAMETER_PROBLEM,
1045 						"chain name may not clash "
1046 						"with target name\n");
1047 			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
1048 				    invert);
1049 			chain = optarg;
1050 			break;
1051 
1052 		case 'X':
1053 			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
1054 				    invert);
1055 			if (optarg) chain = optarg;
1056 			else if (xs_has_arg(argc, argv))
1057 				chain = argv[optind++];
1058 			break;
1059 
1060 		case 'E':
1061 			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
1062 				    invert);
1063 			chain = optarg;
1064 			if (xs_has_arg(argc, argv))
1065 				newname = argv[optind++];
1066 			else
1067 				xtables_error(PARAMETER_PROBLEM,
1068 					      "-%c requires old-chain-name and "
1069 					      "new-chain-name",
1070 					      cmd2char(CMD_RENAME_CHAIN));
1071 			break;
1072 
1073 		case 'P':
1074 			add_command(&command, CMD_SET_POLICY, CMD_NONE,
1075 				    invert);
1076 			chain = optarg;
1077 			if (xs_has_arg(argc, argv))
1078 				policy = argv[optind++];
1079 			else
1080 				xtables_error(PARAMETER_PROBLEM,
1081 					      "-%c requires a chain and a policy",
1082 					      cmd2char(CMD_SET_POLICY));
1083 			break;
1084 
1085 		case 'h':
1086 			if (!optarg)
1087 				optarg = argv[optind];
1088 
1089 			exit_printhelp();
1090 			break;
1091 		case 's':
1092 			check_inverse(optarg, &invert, &optind, argc);
1093 			set_option(&options, OPT_S_IP, &cs.fw.arp.invflags,
1094 				   invert);
1095 			shostnetworkmask = argv[optind-1];
1096 			break;
1097 
1098 		case 'd':
1099 			check_inverse(optarg, &invert, &optind, argc);
1100 			set_option(&options, OPT_D_IP, &cs.fw.arp.invflags,
1101 				   invert);
1102 			dhostnetworkmask = argv[optind-1];
1103 			break;
1104 
1105 		case 2:/* src-mac */
1106 			check_inverse(optarg, &invert, &optind, argc);
1107 			set_option(&options, OPT_S_MAC, &cs.fw.arp.invflags,
1108 				   invert);
1109 			if (getmac_and_mask(argv[optind - 1],
1110 			    cs.fw.arp.src_devaddr.addr, cs.fw.arp.src_devaddr.mask))
1111 				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
1112 						"source mac");
1113 			break;
1114 
1115 		case 3:/* dst-mac */
1116 			check_inverse(optarg, &invert, &optind, argc);
1117 			set_option(&options, OPT_D_MAC, &cs.fw.arp.invflags,
1118 				   invert);
1119 
1120 			if (getmac_and_mask(argv[optind - 1],
1121 			    cs.fw.arp.tgt_devaddr.addr, cs.fw.arp.tgt_devaddr.mask))
1122 				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
1123 						"destination mac");
1124 			break;
1125 
1126 		case 'l':/* hardware length */
1127 			check_inverse(optarg, &invert, &optind, argc);
1128 			set_option(&options, OPT_H_LENGTH, &cs.fw.arp.invflags,
1129 				   invert);
1130 			getlength_and_mask(argv[optind - 1], &cs.fw.arp.arhln,
1131 					   &cs.fw.arp.arhln_mask);
1132 
1133 			if (cs.fw.arp.arhln != 6) {
1134 				xtables_error(PARAMETER_PROBLEM,
1135 					      "Only harware address length of"
1136 					      " 6 is supported currently.");
1137 			}
1138 
1139 			break;
1140 
1141 		case 8:/* protocol length */
1142 			xtables_error(PARAMETER_PROBLEM, "not supported");
1143 /*
1144 			check_inverse(optarg, &invert, &optind, argc);
1145 			set_option(&options, OPT_P_LENGTH, &cs.fw.arp.invflags,
1146 				   invert);
1147 
1148 			getlength_and_mask(argv[optind - 1], &cs.fw.arp.arpln,
1149 					   &cs.fw.arp.arpln_mask);
1150 			break;
1151 */
1152 
1153 		case 4:/* opcode */
1154 			check_inverse(optarg, &invert, &optind, argc);
1155 			set_option(&options, OPT_OPCODE, &cs.fw.arp.invflags,
1156 				   invert);
1157 			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpop,
1158 					   &cs.fw.arp.arpop_mask, 10)) {
1159 				int i;
1160 
1161 				for (i = 0; i < NUMOPCODES; i++)
1162 					if (!strcasecmp(opcodes[i], optarg))
1163 						break;
1164 				if (i == NUMOPCODES)
1165 					xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
1166 				cs.fw.arp.arpop = htons(i+1);
1167 			}
1168 			break;
1169 
1170 		case 5:/* h-type */
1171 			check_inverse(optarg, &invert, &optind, argc);
1172 			set_option(&options, OPT_H_TYPE, &cs.fw.arp.invflags,
1173 				   invert);
1174 			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arhrd,
1175 					   &cs.fw.arp.arhrd_mask, 16)) {
1176 				if (strcasecmp(argv[optind-1], "Ethernet"))
1177 					xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
1178 				cs.fw.arp.arhrd = htons(1);
1179 			}
1180 			break;
1181 
1182 		case 6:/* proto-type */
1183 			check_inverse(optarg, &invert, &optind, argc);
1184 			set_option(&options, OPT_P_TYPE, &cs.fw.arp.invflags,
1185 				   invert);
1186 			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpro,
1187 					   &cs.fw.arp.arpro_mask, 0)) {
1188 				if (strcasecmp(argv[optind-1], "ipv4"))
1189 					xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
1190 				cs.fw.arp.arpro = htons(0x800);
1191 			}
1192 			break;
1193 
1194 		case 'j':
1195 			set_option(&options, OPT_JUMP, &cs.fw.arp.invflags,
1196 				   invert);
1197 			cs.jumpto = parse_target(optarg);
1198 			cs.target = command_jump(&cs.fw, cs.jumpto);
1199 			break;
1200 
1201 		case 'i':
1202 			check_inverse(optarg, &invert, &optind, argc);
1203 			set_option(&options, OPT_VIANAMEIN, &cs.fw.arp.invflags,
1204 				   invert);
1205 			parse_interface(argv[optind-1],
1206 					cs.fw.arp.iniface,
1207 					cs.fw.arp.iniface_mask);
1208 /*			cs.fw.nfcache |= NFC_IP_IF_IN; */
1209 			break;
1210 
1211 		case 'o':
1212 			check_inverse(optarg, &invert, &optind, argc);
1213 			set_option(&options, OPT_VIANAMEOUT, &cs.fw.arp.invflags,
1214 				   invert);
1215 			parse_interface(argv[optind-1],
1216 					cs.fw.arp.outiface,
1217 					cs.fw.arp.outiface_mask);
1218 			/* cs.fw.nfcache |= NFC_IP_IF_OUT; */
1219 			break;
1220 
1221 		case 'v':
1222 			if (!verbose)
1223 				set_option(&options, OPT_VERBOSE,
1224 					   &cs.fw.arp.invflags, invert);
1225 			verbose++;
1226 			break;
1227 
1228 		case 'm': /*{
1229 			size_t size;
1230 
1231 			if (invert)
1232 				exit_error(PARAMETER_PROBLEM,
1233 					   "unexpected ! flag before --match");
1234 
1235 			m = find_match(optarg, LOAD_MUST_SUCCEED);
1236 			size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
1237 					 + m->size;
1238 			m->m = fw_calloc(1, size);
1239 			m->m->u.match_size = size;
1240 			strcpy(m->m->u.user.name, m->name);
1241 			m->init(m->m, &fw.nfcache);
1242 			opts = merge_options(opts, m->extra_opts, &m->option_offset);
1243 		}*/
1244 		break;
1245 
1246 		case 'n':
1247 			set_option(&options, OPT_NUMERIC, &cs.fw.arp.invflags,
1248 				   invert);
1249 			break;
1250 
1251 		case 't':
1252 			if (invert)
1253 				xtables_error(PARAMETER_PROBLEM,
1254 					      "unexpected ! flag before --table");
1255 			*table = argv[optind-1];
1256 			break;
1257 
1258 		case 'V':
1259 			if (invert)
1260 				printf("Not %s ;-)\n", program_version);
1261 			else
1262 				printf("%s v%s\n",
1263 				       program_name, program_version);
1264 			exit(0);
1265 
1266 		case '0':
1267 			set_option(&options, OPT_LINENUMBERS, &cs.fw.arp.invflags,
1268 				   invert);
1269 			break;
1270 
1271 		case 'M':
1272 			//modprobe = optarg;
1273 			break;
1274 
1275 		case 'c':
1276 
1277 			set_option(&options, OPT_COUNTERS, &cs.fw.arp.invflags,
1278 				   invert);
1279 			pcnt = optarg;
1280 			if (xs_has_arg(argc, argv))
1281 				bcnt = argv[optind++];
1282 			else
1283 				xtables_error(PARAMETER_PROBLEM,
1284 					      "-%c requires packet and byte counter",
1285 					      opt2char(OPT_COUNTERS));
1286 
1287 			if (sscanf(pcnt, "%llu", &cs.fw.counters.pcnt) != 1)
1288 			xtables_error(PARAMETER_PROBLEM,
1289 				"-%c packet counter not numeric",
1290 				opt2char(OPT_COUNTERS));
1291 
1292 			if (sscanf(bcnt, "%llu", &cs.fw.counters.bcnt) != 1)
1293 				xtables_error(PARAMETER_PROBLEM,
1294 					      "-%c byte counter not numeric",
1295 					      opt2char(OPT_COUNTERS));
1296 
1297 			break;
1298 
1299 
1300 		case 1: /* non option */
1301 			if (optarg[0] == '!' && optarg[1] == '\0') {
1302 				if (invert)
1303 					xtables_error(PARAMETER_PROBLEM,
1304 						      "multiple consecutive ! not"
1305 						      " allowed");
1306 				invert = TRUE;
1307 				optarg[0] = '\0';
1308 				continue;
1309 			}
1310 			printf("Bad argument `%s'\n", optarg);
1311 			exit_tryhelp(2);
1312 
1313 		default:
1314 			if (cs.target) {
1315 				xtables_option_tpcall(c, argv,
1316 						      invert, cs.target, &cs.fw);
1317 			}
1318 			break;
1319 		}
1320 		invert = FALSE;
1321 	}
1322 
1323 	if (cs.target)
1324 		xtables_option_tfcall(cs.target);
1325 
1326 	if (optind < argc)
1327 		xtables_error(PARAMETER_PROBLEM,
1328 			      "unknown arguments found on commandline");
1329 	if (!command)
1330 		xtables_error(PARAMETER_PROBLEM, "no command specified");
1331 	if (invert)
1332 		xtables_error(PARAMETER_PROBLEM,
1333 			      "nothing appropriate following !");
1334 
1335 	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
1336 		if (!(options & OPT_D_IP))
1337 			dhostnetworkmask = "0.0.0.0/0";
1338 		if (!(options & OPT_S_IP))
1339 			shostnetworkmask = "0.0.0.0/0";
1340 	}
1341 
1342 	if (shostnetworkmask)
1343 		parse_hostnetworkmask(shostnetworkmask, &saddrs,
1344 				      &(cs.fw.arp.smsk), &nsaddrs);
1345 
1346 	if (dhostnetworkmask)
1347 		parse_hostnetworkmask(dhostnetworkmask, &daddrs,
1348 				      &(cs.fw.arp.tmsk), &ndaddrs);
1349 
1350 	if ((nsaddrs > 1 || ndaddrs > 1) &&
1351 	    (cs.fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
1352 		xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
1353 				" source or destination IP addresses");
1354 
1355 	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
1356 		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
1357 						 "specify a unique address");
1358 
1359 	generic_opt_check(command, options);
1360 
1361 	if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
1362 		xtables_error(PARAMETER_PROBLEM,
1363 				"chain name `%s' too long (must be under %i chars)",
1364 				chain, ARPT_FUNCTION_MAXNAMELEN);
1365 
1366 	if (nft_init(h, xtables_arp) < 0)
1367 		xtables_error(OTHER_PROBLEM,
1368 			      "Could not initialize nftables layer.");
1369 
1370 	h->ops = nft_family_ops_lookup(h->family);
1371 	if (h->ops == NULL)
1372 		xtables_error(PARAMETER_PROBLEM, "Unknown family");
1373 
1374 	if (command == CMD_APPEND
1375 	    || command == CMD_DELETE
1376 	    || command == CMD_INSERT
1377 	    || command == CMD_REPLACE) {
1378 		if (strcmp(chain, "PREROUTING") == 0
1379 		    || strcmp(chain, "INPUT") == 0) {
1380 			/* -o not valid with incoming packets. */
1381 			if (options & OPT_VIANAMEOUT)
1382 				xtables_error(PARAMETER_PROBLEM,
1383 					      "Can't use -%c with %s\n",
1384 					      opt2char(OPT_VIANAMEOUT),
1385 					      chain);
1386 		}
1387 
1388 		if (strcmp(chain, "POSTROUTING") == 0
1389 		    || strcmp(chain, "OUTPUT") == 0) {
1390 			/* -i not valid with outgoing packets */
1391 			if (options & OPT_VIANAMEIN)
1392 				xtables_error(PARAMETER_PROBLEM,
1393 						"Can't use -%c with %s\n",
1394 						opt2char(OPT_VIANAMEIN),
1395 						chain);
1396 		}
1397 
1398 		if (!cs.target && strlen(cs.jumpto) != 0) {
1399 			size_t size;
1400 
1401 			cs.target = xtables_find_target(XT_STANDARD_TARGET,
1402 							XTF_LOAD_MUST_SUCCEED);
1403 			size = sizeof(struct arpt_entry_target) + cs.target->size;
1404 			cs.target->t = xtables_calloc(1, size);
1405 			cs.target->t->u.target_size = size;
1406 			strcpy(cs.target->t->u.user.name, cs.jumpto);
1407 		}
1408 	}
1409 
1410 	switch (command) {
1411 	case CMD_APPEND:
1412 		ret = append_entry(h, chain, *table, &cs, 0,
1413 				   nsaddrs, saddrs, ndaddrs, daddrs,
1414 				   options&OPT_VERBOSE, true);
1415 		break;
1416 	case CMD_DELETE:
1417 		ret = delete_entry(chain, *table, &cs,
1418 				   nsaddrs, saddrs, ndaddrs, daddrs,
1419 				   options&OPT_VERBOSE, h);
1420 		break;
1421 	case CMD_DELETE_NUM:
1422 		ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
1423 		break;
1424 	case CMD_REPLACE:
1425 		ret = replace_entry(chain, *table, &cs, rulenum - 1,
1426 				    saddrs, daddrs, options&OPT_VERBOSE, h);
1427 		break;
1428 	case CMD_INSERT:
1429 		ret = append_entry(h, chain, *table, &cs, rulenum - 1,
1430 				   nsaddrs, saddrs, ndaddrs, daddrs,
1431 				   options&OPT_VERBOSE, false);
1432 		break;
1433 	case CMD_LIST:
1434 		ret = list_entries(h, chain, *table,
1435 				   rulenum,
1436 				   options&OPT_VERBOSE,
1437 				   options&OPT_NUMERIC,
1438 				   /*options&OPT_EXPANDED*/0,
1439 				   options&OPT_LINENUMBERS);
1440 		break;
1441 	case CMD_FLUSH:
1442 		ret = nft_rule_flush(h, chain, *table);
1443 		break;
1444 	case CMD_ZERO:
1445 		ret = nft_chain_zero_counters(h, chain, *table);
1446 		break;
1447 	case CMD_LIST|CMD_ZERO:
1448 		ret = list_entries(h, chain, *table, rulenum,
1449 				   options&OPT_VERBOSE,
1450 				   options&OPT_NUMERIC,
1451 				   /*options&OPT_EXPANDED*/0,
1452 				   options&OPT_LINENUMBERS);
1453 		if (ret)
1454 			ret = nft_chain_zero_counters(h, chain, *table);
1455 		break;
1456 	case CMD_NEW_CHAIN:
1457 		ret = nft_chain_user_add(h, chain, *table);
1458 		break;
1459 	case CMD_DELETE_CHAIN:
1460 		ret = nft_chain_user_del(h, chain, *table);
1461 		break;
1462 	case CMD_RENAME_CHAIN:
1463 		ret = nft_chain_user_rename(h, chain, *table, newname);
1464 		break;
1465 	case CMD_SET_POLICY:
1466 		ret = nft_chain_set(h, *table, chain, policy, NULL);
1467 		if (ret < 0)
1468 			xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
1469 				      policy);
1470 		break;
1471 	default:
1472 		/* We should never reach this... */
1473 		exit_tryhelp(2);
1474 	}
1475 
1476 /*	if (verbose > 1)
1477 		dump_entries(*handle);*/
1478 
1479 	return ret;
1480 }
1481