xref: /dflybsd-src/lib/libipfw3/basic/ipfw3_basic.c (revision 23832f75edc9855492226d612851679a00de2f9c)
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <grp.h>
39 #include <limits.h>
40 #include <netdb.h>
41 #include <pwd.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <sysexits.h>
48 #include <timeconv.h>
49 #include <unistd.h>
50 
51 #include <netinet/in.h>
52 
53 #include <arpa/inet.h>
54 #include <net/if.h>
55 #include <net/route.h>
56 #include <net/pfil.h>
57 
58 #include "../../../sys/net/ipfw3/ip_fw3.h"
59 #include "../../../sbin/ipfw3/ipfw.h"
60 #include "ipfw3_basic.h"
61 
62 
63 #define	IP_MASK_ALL	0xffffffff
64 /*
65  * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
66  * This is only used in this code.
67  */
68 #define IPPROTO_ETHERTYPE	0x1000
69 
70 
71 struct char_int_map limit_types[] = {
72 	{ "src-addr", 	1 },
73 	{ "src-port", 	2 },
74 	{ "dst-addr", 	3 },
75 	{ "dst-port", 	4 },
76 	{ NULL, 	0 }
77 };
78 
79 static struct char_int_map ether_types[] = {
80 	{ "ip", 	0x0800 },
81 	{ "ipv4", 	0x0800 },
82 	{ "ipv6", 	0x86dd },
83 	{ "arp", 	0x0806 },
84 	{ "rarp", 	0x8035 },
85 	{ "vlan", 	0x8100 },
86 	{ "loop", 	0x9000 },
87 	{ "trail", 	0x1000 },
88 	{ "pppoe_disc", 0x8863 },
89 	{ "pppoe_sess", 0x8864 },
90 	{ "ipx_8022", 	0x00E0 },
91 	{ "ipx_8023", 	0x0000 },
92 	{ "ipx_ii", 	0x8137 },
93 	{ "ipx_snap", 	0x8137 },
94 	{ "ipx", 	0x8137 },
95 	{ "ns", 	0x0600 },
96 	{ NULL, 	0 }
97 };
98 
99 /**
100  * match_token takes a table and a string, returns the value associated
101  * with the string (0 meaning an error in most cases)
102  */
103 static int
104 match_token(struct char_int_map *table, char *string)
105 {
106 	while (table->key) {
107 		if (strcmp(table->key, string) == 0)
108 			return table->val;
109 
110 		table++;
111 	}
112 	return 0;
113 };
114 
115 static char *
116 match_token2(struct char_int_map *table, int val)
117 {
118 	while (table->val) {
119 		if (table->val == val)
120 			return table->key;
121 
122 		table++;
123 	}
124 	return NULL;
125 };
126 
127 static void
128 fill_iface(ipfw_insn_if *cmd, char *arg)
129 {
130 	cmd->name[0] = '\0';
131 	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
132 
133 	/* Parse the interface or address */
134 	if (!strcmp(arg, "any")){
135 		cmd->o.len = 0;
136 	} else if (!isdigit(*arg)) {
137 		strlcpy(cmd->name, arg, sizeof(cmd->name));
138 		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
139 	} else if (!inet_aton(arg, &cmd->p.ip))
140 		errx(EX_DATAERR, "bad ip address ``%s''", arg);
141 }
142 
143 static int
144 lookup_host (char *host, struct in_addr *ipaddr)
145 {
146 	struct hostent *he;
147 
148 	if (!inet_aton(host, ipaddr)) {
149 		if ((he = gethostbyname(host)) == NULL)
150 			return(-1);
151 		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
152 	}
153 	return(0);
154 }
155 
156 /*
157  * Like strtol, but also translates service names into port numbers
158  * for some protocols.
159  * In particular:
160  *	proto == -1 disables the protocol check;
161  *	proto == IPPROTO_ETHERTYPE looks up an internal table
162  *	proto == <some value in /etc/protocols> matches the values there.
163  * Returns *end == s in case the parameter is not found.
164  */
165 static int
166 strtoport(char *s, char **end, int base, int proto)
167 {
168 	char *p, *buf;
169 	char *s1;
170 	int i;
171 
172 	*end = s; 		/* default - not found */
173 	if ( *s == '\0')
174 		return 0; 	/* not found */
175 
176 	if (isdigit(*s))
177 		return strtol(s, end, base);
178 
179 	/*
180 	 * find separator. '\\' escapes the next char.
181 	 */
182 	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) {
183 		if (*s1 == '\\' && s1[1] != '\0')
184 			s1++;
185 	}
186 
187 	buf = malloc(s1 - s + 1);
188 	if (buf == NULL)
189 		return 0;
190 
191 	/*
192 	 * copy into a buffer skipping backslashes
193 	 */
194 	for (p = s, i = 0; p != s1 ; p++)
195 		if ( *p != '\\')
196 			buf[i++] = *p;
197 	buf[i++] = '\0';
198 
199 	if (proto == IPPROTO_ETHERTYPE) {
200 		i = match_token(ether_types, buf);
201 		free(buf);
202 		if (i != -1) {	/* found */
203 			*end = s1;
204 			return i;
205 		}
206 	} else {
207 		struct protoent *pe = NULL;
208 		struct servent *se;
209 
210 		if (proto != 0)
211 			pe = getprotobynumber(proto);
212 		setservent(1);
213 		se = getservbyname(buf, pe ? pe->p_name : NULL);
214 		free(buf);
215 		if (se != NULL) {
216 			*end = s1;
217 			return ntohs(se->s_port);
218 		}
219 	}
220 	return 0; 	/* not found */
221 }
222 
223 static int
224 contigmask(u_char *p, int len)
225 {
226 	int i, n;
227 	for (i=0; i<len ; i++) {
228 		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
229 			break;
230 	}
231 	for (n=i+1; n < len; n++) {
232 		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
233 			return -1; /* mask not contiguous */
234 	}
235 	return i;
236 }
237 
238 static void
239 fill_ip(ipfw_insn_ip *cmd, char *av)
240 {
241 	char *p = NULL, md = 0;
242 	u_int32_t i;
243 
244 	cmd->o.len &= ~F_LEN_MASK; 	/* zero len */
245 
246 	if (!strncmp(av, "any", strlen(av)))
247 		return;
248 
249 	if (!strncmp(av, "me", strlen(av))) {
250 		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
251 		return;
252 	}
253 
254 	p = strchr(av, '/');
255 	if (!p)
256 		p = strchr(av, ':');
257 
258 	if (p) {
259 		md = *p;
260 		*p++ = '\0';
261 	}
262 
263 	if (lookup_host(av, &cmd->addr) != 0)
264 		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
265 	switch (md) {
266 	case ':':
267 		if (!inet_aton(p, &cmd->mask))
268 			errx(EX_DATAERR, "bad netmask ``%s''", p);
269 		break;
270 	case '/':
271 		i = atoi(p);
272 		if (i == 0)
273 			cmd->mask.s_addr = htonl(0);
274 		else if (i > 32)
275 			errx(EX_DATAERR, "bad width ``%s''", p);
276 		else
277 			cmd->mask.s_addr = htonl(~0 << (32 - i));
278 		break;
279 	default:
280 		cmd->mask.s_addr = htonl(~0);
281 		break;
282 	}
283 	cmd->addr.s_addr &= cmd->mask.s_addr;
284 
285 	if (p)
286 		p = strchr(p, '{');
287 	if (p) {
288 		u_int32_t *d;
289 		int low, high;
290 		int i = contigmask((u_char *)&(cmd->mask), 32);
291 
292 		if (i < 24 || i > 31) {
293 			fprintf(stderr, "invalid set with mask %d\n", i);
294 			exit(0);
295 		}
296 		cmd->o.arg1 = 1<<(32-i);
297 		cmd->addr.s_addr = ntohl(cmd->addr.s_addr);
298 		d = (u_int32_t *)&cmd->mask;
299 		cmd->o.opcode = O_BASIC_IP_DST_SET; 	/* default */
300 		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
301 		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
302 			d[i] = 0; 	/* clear masks */
303 
304 		av = p+1;
305 		low = cmd->addr.s_addr & 0xff;
306 		high = low + cmd->o.arg1 - 1;
307 		while (isdigit(*av)) {
308 			char *s;
309 			u_int16_t a = strtol(av, &s, 0);
310 
311 			if (s == av) /* no parameter */
312 				break;
313 			if (a < low || a > high) {
314 				fprintf(stderr,
315 					"addr %d out of range [%d-%d]\n",
316 						a, low, high);
317 				exit(0);
318 			}
319 			a -= low;
320 			d[ a/32] |= 1<<(a & 31);
321 			if (*s != ',')
322 				break;
323 			av = s+1;
324 		}
325 		return;
326 	}
327 
328 	if (cmd->mask.s_addr == 0) {
329 		if (cmd->o.len & F_NOT)
330 			errx(EX_DATAERR, "not any never matches");
331 		else	/* useless, nuke it */
332 			return;
333 	} else if (cmd->mask.s_addr == IP_MASK_ALL) {
334 		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
335 	} else {						/* addr/mask */
336 		cmd->o.len |= F_INSN_SIZE(ipfw_insn_ip);
337 		printf("cmd->o.len=%d\n", cmd->o.len);
338 	}
339 }
340 
341 
342 static ipfw_insn *add_srcip(ipfw_insn *cmd, char *av)
343 {
344 	fill_ip((ipfw_insn_ip *)cmd, av);
345 	if ((int)cmd->opcode == O_BASIC_IP_DST_SET)		/* set */
346 		cmd->opcode = O_BASIC_IP_SRC_SET;
347 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
348 		cmd->opcode = O_BASIC_IP_SRC_ME;
349 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
350 		cmd->opcode = O_BASIC_IP_SRC;
351 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip))	/* addr/mask */
352 		cmd->opcode = O_BASIC_IP_SRC_MASK;
353 	return cmd;
354 }
355 
356 
357 static ipfw_insn *add_dstip(ipfw_insn *cmd, char *av)
358 {
359 	fill_ip((ipfw_insn_ip *)cmd, av);
360 	if ((int)cmd->opcode == O_BASIC_IP_DST_SET)
361 		cmd->opcode = O_BASIC_IP_DST_SET;
362 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))
363 		cmd->opcode = O_BASIC_IP_DST_ME;
364 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
365 		cmd->opcode = O_BASIC_IP_DST;
366 	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip))
367 		cmd->opcode = O_BASIC_IP_DST_MASK;
368 	return cmd;
369 }
370 
371 
372 static ipfw_insn *add_proto(ipfw_insn *cmd, char *av)
373 {
374 	struct protoent *pe;
375 	u_char proto = 0;
376 	if (!strncmp(av, "all", strlen(av))) {
377 		;
378 	} else if ((proto = atoi(av)) > 0) {
379 		;
380 	} else if ((pe = getprotobyname(av)) != NULL) {
381 		proto = pe->p_proto;
382 	} else {
383 		errx(EX_USAGE, "protocol `%s' not recognizable\n", av);
384 	}
385 	if (proto != IPPROTO_IP) {
386 		cmd->opcode = O_BASIC_PROTO;
387 		cmd->module = MODULE_BASIC_ID;
388 		cmd->len = cmd->len|LEN_OF_IPFWINSN;
389 		cmd->arg1 = proto;
390 	}
391 	return cmd;
392 }
393 
394 void
395 parse_count(ipfw_insn **cmd, int *ac, char **av[])
396 {
397 	(*cmd)->opcode = O_BASIC_COUNT;
398 	(*cmd)->module = MODULE_BASIC_ID;
399 	(*cmd)->len = LEN_OF_IPFWINSN;
400 	NEXT_ARG1;
401 }
402 
403 void
404 parse_skipto(ipfw_insn **cmd, int *ac, char **av[])
405 {
406 	NEXT_ARG1;
407 	(*cmd)->opcode = O_BASIC_SKIPTO;
408 	(*cmd)->module = MODULE_BASIC_ID;
409 	(*cmd)->len = LEN_OF_IPFWINSN;
410 	(*cmd)->arg1 = strtoul(**av, NULL, 10);
411 	NEXT_ARG1;
412 }
413 
414 /*
415  * cmd->arg3 is count of the destination
416  * cmd->arg1 is the type, random 0, round-robin 1, sticky 2
417  */
418 void
419 parse_forward(ipfw_insn **cmd, int *ac, char **av[])
420 {
421 	ipfw_insn_sa *p = (ipfw_insn_sa *)(*cmd);
422 	struct sockaddr_in *sa;
423 	char *tok, *end = '\0';
424 	char *str;
425 	int count, port;
426 
427 	(*cmd)->opcode = O_BASIC_FORWARD;
428 	NEXT_ARG1;
429 	/*
430 	 * multiple forward destinations are seperated by colon
431 	 * ip address and port are seperated by comma
432 	 * e.g. 192.168.1.1:80,192.168.1.2:8080
433 	 *      192.168.1.1,192.168.1.2 or keep the port the same
434 	 */
435 	tok = strtok(**av, ",");
436 	sa = &p->sa;
437 	count = 0;
438 	while (tok != NULL) {
439 		sa->sin_len = sizeof(struct sockaddr_in);
440 		sa->sin_family = AF_INET;
441 		sa->sin_port = 0;
442 		str = strchr(tok,':');
443 		if (str != NULL) {
444 			*(str++) = '\0';
445 			port = strtoport(str, &end, 0, 0);
446 			sa->sin_port = (u_short)port;
447 		}
448 		lookup_host(tok, &(sa->sin_addr));
449 		tok = strtok (NULL, ",");
450 		sa++;
451 		count++;
452 	}
453 	(*cmd)->arg3 = count;
454 	if (count == 0) {
455 		errx(EX_DATAERR, "forward `%s' not recognizable", **av);
456 	}
457 	NEXT_ARG1;
458 	if (count > 1) {
459 		if (strcmp(**av, "round-robin") == 0) {
460 			NEXT_ARG1;
461 			(*cmd)->arg1 = 1;
462 		} else if (strcmp(**av, "sticky") == 0) {
463 			NEXT_ARG1;
464 			(*cmd)->arg1 = 2;
465 		} else {
466 			/* random */
467 			(*cmd)->arg1 = 0;
468 		}
469 	}
470 	(*cmd)->len = LEN_OF_IPFWINSN + count * sizeof(struct sockaddr_in);
471 }
472 
473 void
474 parse_in(ipfw_insn **cmd, int *ac, char **av[])
475 {
476 	(*cmd)->opcode = O_BASIC_IN;
477 	(*cmd)->module = MODULE_BASIC_ID;
478 	(*cmd)->len = LEN_OF_IPFWINSN;
479 	(*cmd)->arg1 = 0;
480 	NEXT_ARG1;
481 }
482 
483 void
484 parse_out(ipfw_insn **cmd, int *ac, char **av[])
485 {
486 	(*cmd)->opcode = O_BASIC_OUT;
487 	(*cmd)->module = MODULE_BASIC_ID;
488 	(*cmd)->len = LEN_OF_IPFWINSN;
489 	(*cmd)->arg1 = 0;
490 	NEXT_ARG1;
491 }
492 
493 
494 void
495 parse_via(ipfw_insn **cmd, int *ac, char **av[])
496 {
497 	(*cmd)->module = MODULE_BASIC_ID;
498 	(*cmd)->len = LEN_OF_IPFWINSN;
499 	if (strcmp(*av[0], "via")==0) {
500 		(*cmd)->opcode = O_BASIC_VIA;
501 	} else if (strcmp(*av[0], "xmit")==0) {
502 		(*cmd)->opcode = O_BASIC_XMIT;
503 	} else if (strcmp(*av[0], "recv")==0) {
504 		(*cmd)->opcode = O_BASIC_RECV;
505 	}
506 	NEXT_ARG1;
507 	fill_iface((ipfw_insn_if *)(*cmd), *av[0]);
508 	NEXT_ARG1;
509 }
510 
511 void
512 parse_from(ipfw_insn **cmd, int *ac, char **av[])
513 {
514 	(*cmd)->module = MODULE_BASIC_ID;
515 	NEXT_ARG1;
516 	add_srcip(*cmd, **av);
517 	NEXT_ARG1;
518 }
519 
520 void
521 parse_to(ipfw_insn **cmd, int *ac, char **av[])
522 {
523 	(*cmd)->module = MODULE_BASIC_ID;
524 	NEXT_ARG1;
525 	add_dstip(*cmd, **av);
526 	NEXT_ARG1;
527 }
528 
529 void
530 parse_proto(ipfw_insn **cmd, int *ac, char **av[])
531 {
532 	add_proto(*cmd, **av);
533 	NEXT_ARG1;
534 }
535 
536 void
537 parse_prob(ipfw_insn **cmd, int *ac, char **av[])
538 {
539 	NEXT_ARG1;
540 	(*cmd)->opcode = O_BASIC_PROB;
541 	(*cmd)->module = MODULE_BASIC_ID;
542 	(*cmd)->len = LEN_OF_IPFWINSN;
543 	(*cmd)->arg1 = strtoul(**av, NULL, 10);
544 	NEXT_ARG1;
545 }
546 
547 void
548 parse_keep_state(ipfw_insn **cmd, int *ac, char **av[])
549 {
550 	NEXT_ARG1;
551 	(*cmd)->opcode = O_BASIC_KEEP_STATE;
552 	(*cmd)->module = MODULE_BASIC_ID;
553 	(*cmd)->len = LEN_OF_IPFWINSN;
554 	if (strcmp(**av, "limit") == 0) {
555 		NEXT_ARG1;
556 		(*cmd)->arg3 = match_token(limit_types, **av);
557 		if ((*cmd)->arg3 == 0)
558 			errx(EX_DATAERR, "limit `%s' not recognizable", **av);
559 
560 		NEXT_ARG1;
561 		(*cmd)->arg1 = strtoul(**av, NULL, 10);
562 		if ((*cmd)->arg1 == 0)
563 			errx(EX_DATAERR, "bad limit `%s'", **av);
564 
565 		NEXT_ARG1;
566 	}
567 	if (strcmp(**av, "live") == 0) {
568 		NEXT_ARG1;
569 		(*cmd)->arg2 = strtoul(**av, NULL, 10);
570 		NEXT_ARG1;
571 	}
572 }
573 
574 void
575 parse_check_state(ipfw_insn **cmd, int *ac, char **av[])
576 {
577 	NEXT_ARG1;
578 	(*cmd)->opcode = O_BASIC_CHECK_STATE;
579 	(*cmd)->module = MODULE_BASIC_ID;
580 	(*cmd)->len = LEN_OF_IPFWINSN;
581 }
582 
583 void
584 parse_tagged(ipfw_insn **cmd, int *ac, char **av[])
585 {
586 	NEXT_ARG1;
587 	(*cmd)->opcode = O_BASIC_TAGGED;
588 	(*cmd)->module = MODULE_BASIC_ID;
589 	(*cmd)->len = LEN_OF_IPFWINSN;
590 	(*cmd)->arg1 = strtoul(**av, NULL, 10);
591 	NEXT_ARG1;
592 }
593 
594 void
595 parse_comment(ipfw_insn **cmd, int *ac, char **av[])
596 {
597 	int l = 0;
598 	char *p = (char *)((*cmd) + 1);
599 
600 	NEXT_ARG1;
601 	(*cmd)->opcode = O_BASIC_COMMENT;
602 	(*cmd)->module = MODULE_BASIC_ID;
603 
604 	while (*ac > 0) {
605 		l += strlen(**av) + 1;
606 		if (l > 84) {
607 			errx(EX_DATAERR, "comment too long (max 80 chars)");
608 		}
609 		strcpy(p, **av);
610 		p += strlen(**av);
611 		*p++ = ' ';
612 		NEXT_ARG1;
613 	}
614 	l = 1 + (l + 3) / 4;
615 	(*cmd)->len = l;
616 	*(--p) = '\0';
617 }
618 
619 void
620 parse_tag(ipfw_insn **cmd, int *ac, char **av[])
621 {
622 	NEXT_ARG1;
623 	(*cmd)->opcode = O_BASIC_TAG;
624 	(*cmd)->module = MODULE_BASIC_ID;
625 	(*cmd)->len = LEN_OF_IPFWINSN;
626 	(*cmd)->arg1 = strtoul(**av, NULL, 10);
627 	NEXT_ARG1;
628 }
629 
630 void
631 parse_untag(ipfw_insn **cmd, int *ac, char **av[])
632 {
633 	NEXT_ARG1;
634 	(*cmd)->opcode = O_BASIC_UNTAG;
635 	(*cmd)->module = MODULE_BASIC_ID;
636 	(*cmd)->len = LEN_OF_IPFWINSN;
637 	(*cmd)->arg1 = strtoul(**av, NULL, 10);
638 	NEXT_ARG1;
639 }
640 
641 void
642 show_count(ipfw_insn *cmd)
643 {
644 	printf(" count");
645 }
646 
647 void
648 show_skipto(ipfw_insn *cmd)
649 {
650 	printf(" skipto %u", cmd->arg1);
651 }
652 
653 void
654 show_forward(ipfw_insn *cmd)
655 {
656 	struct sockaddr_in *sa;
657 	int i;
658 
659 	ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
660 	sa = &s->sa;
661 	printf(" forward");
662 	for (i = 0; i < cmd->arg3; i++){
663 		if (i > 0)
664 			printf(",");
665 		else
666 			printf(" ");
667 
668 		printf("%s", inet_ntoa(sa->sin_addr));
669 		if (sa->sin_port != 0)
670 			printf(":%d", sa->sin_port);
671 
672 		sa++;
673 	}
674 	if (cmd->arg1 == 1)
675 		printf(" round-robin");
676 	else if (cmd->arg1 == 2)
677 		printf(" sticky");
678 
679 }
680 
681 void
682 show_in(ipfw_insn *cmd)
683 {
684 	printf(" in");
685 }
686 
687 void
688 show_out(ipfw_insn *cmd)
689 {
690 	printf(" out");
691 }
692 
693 void
694 show_via(ipfw_insn *cmd)
695 {
696 	char *s;
697 	ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
698 
699 	if ((int)cmd->opcode == O_BASIC_XMIT)
700 		s = "xmit";
701 	else if ((int)cmd->opcode == O_BASIC_RECV)
702 		s = "recv";
703 	else if ((int)cmd->opcode == O_BASIC_VIA)
704 		s = "via";
705 	else
706 		s = "?huh?";
707 
708 	if (cmdif->name[0] == '\0')
709 		printf(" %s %s", s, inet_ntoa(cmdif->p.ip));
710 
711 	printf(" %s %s", s, cmdif->name);
712 }
713 
714 void
715 show_from(ipfw_insn *cmd)
716 {
717 	printf(" from %s", inet_ntoa(((ipfw_insn_ip *)cmd)->addr));
718 }
719 
720 void
721 show_to(ipfw_insn *cmd)
722 {
723 	printf(" to %s", inet_ntoa(((ipfw_insn_ip *)cmd)->addr));
724 }
725 
726 void
727 show_proto(ipfw_insn *cmd)
728 {
729 	struct protoent *pe;
730 	u_char proto = 0;
731 	proto = cmd->arg1;
732 	pe = getprotobynumber(cmd->arg1);
733 	printf(" %s", pe->p_name);
734 }
735 
736 void
737 show_prob(ipfw_insn *cmd)
738 {
739 	printf(" prob %d%%", cmd->arg1);
740 }
741 
742 void
743 show_keep_state(ipfw_insn *cmd)
744 {
745 	printf(" keep-state");
746 	if (cmd->arg1 != 0) {
747 		char *type=match_token2(limit_types, cmd->arg3);
748 		printf(" limit %s %d", type, cmd->arg1);
749 	}
750 	if (cmd->arg2 != 0) {
751 		printf(" live %d", cmd->arg2);
752 	}
753 }
754 
755 void
756 show_check_state(ipfw_insn *cmd)
757 {
758 	printf(" check-state");
759 }
760 
761 void
762 show_tagged(ipfw_insn *cmd)
763 {
764 	printf(" tagged %d", cmd->arg1);
765 }
766 
767 void
768 show_comment(ipfw_insn *cmd)
769 {
770 	printf(" // %s", (char *)(cmd + 1));
771 }
772 
773 void
774 show_tag(ipfw_insn *cmd)
775 {
776 	printf(" tag %d", cmd->arg1);
777 }
778 
779 void
780 show_untag(ipfw_insn *cmd)
781 {
782 	printf(" untag %d", cmd->arg1);
783 }
784 
785 void
786 load_module(register_func function, register_keyword keyword)
787 {
788 	keyword(MODULE_BASIC_ID, O_BASIC_COUNT, "count",
789 			IPFW_KEYWORD_TYPE_ACTION);
790 	function(MODULE_BASIC_ID, O_BASIC_COUNT,
791 			(parser_func)parse_count, (shower_func)show_count);
792 
793 	keyword(MODULE_BASIC_ID, O_BASIC_SKIPTO, "skipto",
794 			IPFW_KEYWORD_TYPE_ACTION);
795 	function(MODULE_BASIC_ID, O_BASIC_SKIPTO,
796 			(parser_func)parse_skipto, (shower_func)show_skipto);
797 
798 	keyword(MODULE_BASIC_ID, O_BASIC_FORWARD, "forward",
799 			IPFW_KEYWORD_TYPE_ACTION);
800 	function(MODULE_BASIC_ID, O_BASIC_FORWARD,
801 			(parser_func)parse_forward, (shower_func)show_forward);
802 
803 	keyword(MODULE_BASIC_ID, O_BASIC_IN, "in", IPFW_KEYWORD_TYPE_FILTER);
804 	function(MODULE_BASIC_ID, O_BASIC_IN,
805 			(parser_func)parse_in, (shower_func)show_in);
806 
807 	keyword(MODULE_BASIC_ID, O_BASIC_OUT, "out", IPFW_KEYWORD_TYPE_FILTER);
808 	function(MODULE_BASIC_ID, O_BASIC_OUT,
809 			(parser_func)parse_out, (shower_func)show_out);
810 
811 	keyword(MODULE_BASIC_ID, O_BASIC_VIA, "via", IPFW_KEYWORD_TYPE_FILTER);
812 	function(MODULE_BASIC_ID, O_BASIC_VIA,
813 			(parser_func)parse_via, (shower_func)show_via);
814 
815 	keyword(MODULE_BASIC_ID, O_BASIC_XMIT, "xmit",
816 			IPFW_KEYWORD_TYPE_FILTER);
817 	function(MODULE_BASIC_ID, O_BASIC_XMIT,
818 			(parser_func)parse_via, (shower_func)show_via);
819 
820 	keyword(MODULE_BASIC_ID, O_BASIC_RECV, "recv",
821 			IPFW_KEYWORD_TYPE_FILTER);
822 	function(MODULE_BASIC_ID, O_BASIC_RECV,
823 			(parser_func)parse_via, (shower_func)show_via);
824 
825 	keyword(MODULE_BASIC_ID, O_BASIC_IP_SRC, "from",
826 			IPFW_KEYWORD_TYPE_FILTER);
827 	function(MODULE_BASIC_ID, O_BASIC_IP_SRC,
828 			(parser_func)parse_from, (shower_func)show_from);
829 
830 	keyword(MODULE_BASIC_ID, O_BASIC_IP_DST, "to",
831 			IPFW_KEYWORD_TYPE_FILTER);
832 	function(MODULE_BASIC_ID, O_BASIC_IP_DST,
833 			(parser_func)parse_to, (shower_func)show_to);
834 
835 	keyword(MODULE_BASIC_ID, O_BASIC_PROTO, "proto",
836 			IPFW_KEYWORD_TYPE_FILTER);
837 	function(MODULE_BASIC_ID, O_BASIC_PROTO,
838 			(parser_func)parse_proto, (shower_func)show_proto);
839 
840 	keyword(MODULE_BASIC_ID, O_BASIC_PROB, "prob",
841 			IPFW_KEYWORD_TYPE_FILTER);
842 	function(MODULE_BASIC_ID, O_BASIC_PROB,
843 			(parser_func)parse_prob, (shower_func)show_prob);
844 
845 	keyword(MODULE_BASIC_ID, O_BASIC_KEEP_STATE, "keep-state",
846 			IPFW_KEYWORD_TYPE_FILTER);
847 	function(MODULE_BASIC_ID, O_BASIC_KEEP_STATE,
848 			(parser_func)parse_keep_state,
849 			(shower_func)show_keep_state);
850 
851 	keyword(MODULE_BASIC_ID, O_BASIC_CHECK_STATE, "check-state",
852 			IPFW_KEYWORD_TYPE_OTHERS);
853 	function(MODULE_BASIC_ID, O_BASIC_CHECK_STATE,
854 			(parser_func)parse_check_state,
855 			(shower_func)show_check_state);
856 
857 	keyword(MODULE_BASIC_ID, O_BASIC_TAG, "tag", IPFW_KEYWORD_TYPE_OTHERS);
858 	function(MODULE_BASIC_ID, O_BASIC_TAG,
859 			(parser_func)parse_tag, (shower_func)show_tag);
860 
861 	keyword(MODULE_BASIC_ID, O_BASIC_UNTAG, "untag",
862 			IPFW_KEYWORD_TYPE_OTHERS);
863 	function(MODULE_BASIC_ID, O_BASIC_UNTAG,
864 			(parser_func)parse_untag, (shower_func)show_untag);
865 
866 	keyword(MODULE_BASIC_ID, O_BASIC_TAGGED, "tagged",
867 			IPFW_KEYWORD_TYPE_FILTER);
868 	function(MODULE_BASIC_ID, O_BASIC_TAGGED,
869 			(parser_func)parse_tagged, (shower_func)show_tagged);
870 
871 	keyword(MODULE_BASIC_ID, O_BASIC_COMMENT, "//",
872 			IPFW_KEYWORD_TYPE_FILTER);
873 	function(MODULE_BASIC_ID, O_BASIC_COMMENT,
874 			(parser_func)parse_comment, (shower_func)show_comment);
875 }
876