xref: /freebsd/sbin/ipfw/ipv6.c (revision b3e76948)
1d17aef79SPedro F. Giffuni /*-
2ead75a59SLuigi Rizzo  * Copyright (c) 2002-2003 Luigi Rizzo
3ead75a59SLuigi Rizzo  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4ead75a59SLuigi Rizzo  * Copyright (c) 1994 Ugen J.S.Antsilevich
5ead75a59SLuigi Rizzo  *
6ead75a59SLuigi Rizzo  * Idea and grammar partially left from:
7ead75a59SLuigi Rizzo  * Copyright (c) 1993 Daniel Boulet
8ead75a59SLuigi Rizzo  *
9ead75a59SLuigi Rizzo  * Redistribution and use in source forms, with and without modification,
10ead75a59SLuigi Rizzo  * are permitted provided that this entire comment appears intact.
11ead75a59SLuigi Rizzo  *
12ead75a59SLuigi Rizzo  * Redistribution in binary form may occur without any restrictions.
13ead75a59SLuigi Rizzo  * Obviously, it would be nice if you gave credit where credit is due
14ead75a59SLuigi Rizzo  * but requiring it would be too onerous.
15ead75a59SLuigi Rizzo  *
16ead75a59SLuigi Rizzo  * This software is provided ``AS IS'' without any warranties of any kind.
17ead75a59SLuigi Rizzo  *
18ead75a59SLuigi Rizzo  * NEW command line interface for IP firewall facility
19ead75a59SLuigi Rizzo  *
20ead75a59SLuigi Rizzo  * ipv6 support
21ead75a59SLuigi Rizzo  */
22ead75a59SLuigi Rizzo 
23ead75a59SLuigi Rizzo #include <sys/types.h>
24ead75a59SLuigi Rizzo #include <sys/socket.h>
25ead75a59SLuigi Rizzo 
26ead75a59SLuigi Rizzo #include "ipfw2.h"
27ead75a59SLuigi Rizzo 
28ead75a59SLuigi Rizzo #include <err.h>
29ead75a59SLuigi Rizzo #include <netdb.h>
30ead75a59SLuigi Rizzo #include <stdio.h>
31ead75a59SLuigi Rizzo #include <stdlib.h>
32ead75a59SLuigi Rizzo #include <string.h>
33ead75a59SLuigi Rizzo #include <sysexits.h>
34ead75a59SLuigi Rizzo 
35ead75a59SLuigi Rizzo #include <net/if.h>
36ead75a59SLuigi Rizzo #include <netinet/in.h>
37ead75a59SLuigi Rizzo #include <netinet/in_systm.h>
38ead75a59SLuigi Rizzo #include <netinet/ip.h>
39ead75a59SLuigi Rizzo #include <netinet/icmp6.h>
40ead75a59SLuigi Rizzo #include <netinet/ip_fw.h>
41ead75a59SLuigi Rizzo #include <arpa/inet.h>
42ead75a59SLuigi Rizzo 
43579ed7bdSAlexander V. Chernikov #define	CHECK_LENGTH(v, len) do {			\
44579ed7bdSAlexander V. Chernikov 	if ((v) < (len))				\
45579ed7bdSAlexander V. Chernikov 		errx(EX_DATAERR, "Rule too long");	\
46579ed7bdSAlexander V. Chernikov 	} while (0)
47579ed7bdSAlexander V. Chernikov 
48ead75a59SLuigi Rizzo static struct _s_x icmp6codes[] = {
49ead75a59SLuigi Rizzo 	{ "no-route",		ICMP6_DST_UNREACH_NOROUTE },
50ead75a59SLuigi Rizzo 	{ "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
51ead75a59SLuigi Rizzo 	{ "address",		ICMP6_DST_UNREACH_ADDR },
52ead75a59SLuigi Rizzo 	{ "port",			ICMP6_DST_UNREACH_NOPORT },
53ead75a59SLuigi Rizzo 	{ NULL, 0 }
54ead75a59SLuigi Rizzo };
55ead75a59SLuigi Rizzo 
562b5dd8b8SAlexander V. Chernikov uint16_t
get_unreach6_code(const char * str)572b5dd8b8SAlexander V. Chernikov get_unreach6_code(const char *str)
58ead75a59SLuigi Rizzo {
59ead75a59SLuigi Rizzo 	int val;
60ead75a59SLuigi Rizzo 	char *s;
61ead75a59SLuigi Rizzo 
62ead75a59SLuigi Rizzo 	val = strtoul(str, &s, 0);
63ead75a59SLuigi Rizzo 	if (s == str || *s != '\0' || val >= 0x100)
64ead75a59SLuigi Rizzo 		val = match_token(icmp6codes, str);
65ead75a59SLuigi Rizzo 	if (val < 0)
66ead75a59SLuigi Rizzo 		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
672b5dd8b8SAlexander V. Chernikov 	return (val);
68ead75a59SLuigi Rizzo }
69ead75a59SLuigi Rizzo 
70ead75a59SLuigi Rizzo void
print_unreach6_code(struct buf_pr * bp,uint16_t code)717b34dbe4SAndrey V. Elsukov print_unreach6_code(struct buf_pr *bp, uint16_t code)
72ead75a59SLuigi Rizzo {
73ead75a59SLuigi Rizzo 	char const *s = match_value(icmp6codes, code);
74ead75a59SLuigi Rizzo 
75ead75a59SLuigi Rizzo 	if (s != NULL)
767b34dbe4SAndrey V. Elsukov 		bprintf(bp, "unreach6 %s", s);
77ead75a59SLuigi Rizzo 	else
787b34dbe4SAndrey V. Elsukov 		bprintf(bp, "unreach6 %u", code);
79ead75a59SLuigi Rizzo }
80ead75a59SLuigi Rizzo 
81ead75a59SLuigi Rizzo /*
82ead75a59SLuigi Rizzo  * Print the ip address contained in a command.
83ead75a59SLuigi Rizzo  */
84ead75a59SLuigi Rizzo void
print_ip6(struct buf_pr * bp,const ipfw_insn_ip6 * cmd)8556707beeSMark Johnston print_ip6(struct buf_pr *bp, const ipfw_insn_ip6 *cmd)
86ead75a59SLuigi Rizzo {
87ead75a59SLuigi Rizzo 	char trad[255];
880f71e509SAndrey V. Elsukov 	struct hostent *he = NULL;
8956707beeSMark Johnston 	const struct in6_addr *a = &(cmd->addr6);
900f71e509SAndrey V. Elsukov 	int len, mb;
91ead75a59SLuigi Rizzo 
9256707beeSMark Johnston 	len = F_LEN((const ipfw_insn *)cmd) - 1;
93ead75a59SLuigi Rizzo 	if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
94912430f6SAlexander V. Chernikov 		bprintf(bp, " me6");
95ead75a59SLuigi Rizzo 		return;
96ead75a59SLuigi Rizzo 	}
97ead75a59SLuigi Rizzo 	if (cmd->o.opcode == O_IP6) {
98912430f6SAlexander V. Chernikov 		bprintf(bp, " ip6");
99ead75a59SLuigi Rizzo 		return;
100ead75a59SLuigi Rizzo 	}
101ead75a59SLuigi Rizzo 
102ead75a59SLuigi Rizzo 	/*
103ead75a59SLuigi Rizzo 	 * len == 4 indicates a single IP, whereas lists of 1 or more
104ead75a59SLuigi Rizzo 	 * addr/mask pairs have len = (2n+1). We convert len to n so we
105ead75a59SLuigi Rizzo 	 * use that to count the number of entries.
106ead75a59SLuigi Rizzo 	 */
107bd32e335SAndrey V. Elsukov 	bprintf(bp, " ");
108ead75a59SLuigi Rizzo 	for (len = len / 4; len > 0; len -= 2, a += 2) {
1090f71e509SAndrey V. Elsukov 		/* mask length */
1100f71e509SAndrey V. Elsukov 		mb = (cmd->o.opcode == O_IP6_SRC ||
1110f71e509SAndrey V. Elsukov 		    cmd->o.opcode == O_IP6_DST) ?  128:
11256707beeSMark Johnston 		    contigmask((const uint8_t *)&(a[1]), 128);
113ead75a59SLuigi Rizzo 
11456707beeSMark Johnston 		if (mb == 128 && g_co.do_resolv)
11556707beeSMark Johnston 			he = gethostbyaddr((const char *)a, sizeof(*a),
11656707beeSMark Johnston 			    AF_INET6);
1170f71e509SAndrey V. Elsukov 
118ead75a59SLuigi Rizzo 		if (he != NULL)	     /* resolved to name */
119912430f6SAlexander V. Chernikov 			bprintf(bp, "%s", he->h_name);
120ead75a59SLuigi Rizzo 		else if (mb == 0)	   /* any */
121912430f6SAlexander V. Chernikov 			bprintf(bp, "any");
122ead75a59SLuigi Rizzo 		else {	  /* numeric IP followed by some kind of mask */
1230f71e509SAndrey V. Elsukov 			if (inet_ntop(AF_INET6,  a, trad,
1240f71e509SAndrey V. Elsukov 			    sizeof(trad)) == NULL)
125912430f6SAlexander V. Chernikov 				bprintf(bp, "Error ntop in print_ip6\n");
126912430f6SAlexander V. Chernikov 			bprintf(bp, "%s",  trad );
12723b93085SAndrey V. Elsukov 			if (mb < 0) /* mask not contiguous */
1280f71e509SAndrey V. Elsukov 				bprintf(bp, "/%s", inet_ntop(AF_INET6, &a[1],
1290f71e509SAndrey V. Elsukov 				    trad, sizeof(trad)));
130ead75a59SLuigi Rizzo 			else if (mb < 128)
131912430f6SAlexander V. Chernikov 				bprintf(bp, "/%d", mb);
132ead75a59SLuigi Rizzo 		}
133ead75a59SLuigi Rizzo 		if (len > 2)
134912430f6SAlexander V. Chernikov 			bprintf(bp, ",");
135ead75a59SLuigi Rizzo 	}
136ead75a59SLuigi Rizzo }
137ead75a59SLuigi Rizzo 
138ead75a59SLuigi Rizzo void
fill_icmp6types(ipfw_insn_icmp6 * cmd,char * av,int cblen)139579ed7bdSAlexander V. Chernikov fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
140ead75a59SLuigi Rizzo {
141ead75a59SLuigi Rizzo        uint8_t type;
142ead75a59SLuigi Rizzo 
14356707beeSMark Johnston        CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn_icmp6));
14451b15930SAndrey V. Elsukov        memset(cmd, 0, sizeof(*cmd));
145ead75a59SLuigi Rizzo        while (*av) {
146ead75a59SLuigi Rizzo 	       if (*av == ',')
147ead75a59SLuigi Rizzo 		       av++;
148ead75a59SLuigi Rizzo 	       type = strtoul(av, &av, 0);
149ead75a59SLuigi Rizzo 	       if (*av != ',' && *av != '\0')
150ead75a59SLuigi Rizzo 		       errx(EX_DATAERR, "invalid ICMP6 type");
151ead75a59SLuigi Rizzo 	       /*
152ead75a59SLuigi Rizzo 		* XXX: shouldn't this be 0xFF?  I can't see any reason why
153ead75a59SLuigi Rizzo 		* we shouldn't be able to filter all possiable values
154ead75a59SLuigi Rizzo 		* regardless of the ability of the rest of the kernel to do
155ead75a59SLuigi Rizzo 		* anything useful with them.
156ead75a59SLuigi Rizzo 		*/
157ead75a59SLuigi Rizzo 	       if (type > ICMP6_MAXTYPE)
158ead75a59SLuigi Rizzo 		       errx(EX_DATAERR, "ICMP6 type out of range");
159ead75a59SLuigi Rizzo 	       cmd->d[type / 32] |= ( 1 << (type % 32));
160ead75a59SLuigi Rizzo        }
161ead75a59SLuigi Rizzo        cmd->o.opcode = O_ICMP6TYPE;
162ead75a59SLuigi Rizzo        cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
163ead75a59SLuigi Rizzo }
164ead75a59SLuigi Rizzo 
165ead75a59SLuigi Rizzo void
print_icmp6types(struct buf_pr * bp,const ipfw_insn_u32 * cmd)16656707beeSMark Johnston print_icmp6types(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
167ead75a59SLuigi Rizzo {
168ead75a59SLuigi Rizzo 	int i, j;
169ead75a59SLuigi Rizzo 	char sep= ' ';
170ead75a59SLuigi Rizzo 
1718f47ad01SAndrey V. Elsukov 	bprintf(bp, " icmp6types");
172ead75a59SLuigi Rizzo 	for (i = 0; i < 7; i++)
173ead75a59SLuigi Rizzo 		for (j=0; j < 32; ++j) {
174ead75a59SLuigi Rizzo 			if ( (cmd->d[i] & (1 << (j))) == 0)
175ead75a59SLuigi Rizzo 				continue;
176912430f6SAlexander V. Chernikov 			bprintf(bp, "%c%d", sep, (i*32 + j));
177ead75a59SLuigi Rizzo 			sep = ',';
178ead75a59SLuigi Rizzo 		}
179ead75a59SLuigi Rizzo }
180ead75a59SLuigi Rizzo 
181ead75a59SLuigi Rizzo void
print_flow6id(struct buf_pr * bp,const ipfw_insn_u32 * cmd)18256707beeSMark Johnston print_flow6id(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
183ead75a59SLuigi Rizzo {
184ead75a59SLuigi Rizzo 	uint16_t i, limit = cmd->o.arg1;
185ead75a59SLuigi Rizzo 	char sep = ',';
186ead75a59SLuigi Rizzo 
187912430f6SAlexander V. Chernikov 	bprintf(bp, " flow-id ");
188ead75a59SLuigi Rizzo 	for( i=0; i < limit; ++i) {
189ead75a59SLuigi Rizzo 		if (i == limit - 1)
190ead75a59SLuigi Rizzo 			sep = ' ';
191912430f6SAlexander V. Chernikov 		bprintf(bp, "%d%c", cmd->d[i], sep);
192ead75a59SLuigi Rizzo 	}
193ead75a59SLuigi Rizzo }
194ead75a59SLuigi Rizzo 
195ead75a59SLuigi Rizzo /* structure and define for the extension header in ipv6 */
196ead75a59SLuigi Rizzo static struct _s_x ext6hdrcodes[] = {
197ead75a59SLuigi Rizzo 	{ "frag",       EXT_FRAGMENT },
198ead75a59SLuigi Rizzo 	{ "hopopt",     EXT_HOPOPTS },
199ead75a59SLuigi Rizzo 	{ "route",      EXT_ROUTING },
200ead75a59SLuigi Rizzo 	{ "dstopt",     EXT_DSTOPTS },
201ead75a59SLuigi Rizzo 	{ "ah",	 EXT_AH },
202ead75a59SLuigi Rizzo 	{ "esp",	EXT_ESP },
203ead75a59SLuigi Rizzo 	{ "rthdr0",     EXT_RTHDR0 },
204ead75a59SLuigi Rizzo 	{ "rthdr2",     EXT_RTHDR2 },
205ead75a59SLuigi Rizzo 	{ NULL,	 0 }
206ead75a59SLuigi Rizzo };
207ead75a59SLuigi Rizzo 
208ead75a59SLuigi Rizzo /* fills command for the extension header filtering */
209ead75a59SLuigi Rizzo int
fill_ext6hdr(ipfw_insn * cmd,char * av)210ead75a59SLuigi Rizzo fill_ext6hdr( ipfw_insn *cmd, char *av)
211ead75a59SLuigi Rizzo {
212ead75a59SLuigi Rizzo 	int tok;
213ead75a59SLuigi Rizzo 	char *s = av;
214ead75a59SLuigi Rizzo 
215ead75a59SLuigi Rizzo 	cmd->arg1 = 0;
216ead75a59SLuigi Rizzo 	while(s) {
217ead75a59SLuigi Rizzo 		av = strsep( &s, ",") ;
218ead75a59SLuigi Rizzo 		tok = match_token(ext6hdrcodes, av);
219ead75a59SLuigi Rizzo 		switch (tok) {
220ead75a59SLuigi Rizzo 		case EXT_FRAGMENT:
221ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_FRAGMENT;
222ead75a59SLuigi Rizzo 			break;
223ead75a59SLuigi Rizzo 		case EXT_HOPOPTS:
224ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_HOPOPTS;
225ead75a59SLuigi Rizzo 			break;
226ead75a59SLuigi Rizzo 		case EXT_ROUTING:
227ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_ROUTING;
228ead75a59SLuigi Rizzo 			break;
229ead75a59SLuigi Rizzo 		case EXT_DSTOPTS:
230ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_DSTOPTS;
231ead75a59SLuigi Rizzo 			break;
232ead75a59SLuigi Rizzo 		case EXT_AH:
233ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_AH;
234ead75a59SLuigi Rizzo 			break;
235ead75a59SLuigi Rizzo 		case EXT_ESP:
236ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_ESP;
237ead75a59SLuigi Rizzo 			break;
238ead75a59SLuigi Rizzo 		case EXT_RTHDR0:
239ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_RTHDR0;
240ead75a59SLuigi Rizzo 			break;
241ead75a59SLuigi Rizzo 		case EXT_RTHDR2:
242ead75a59SLuigi Rizzo 			cmd->arg1 |= EXT_RTHDR2;
243ead75a59SLuigi Rizzo 			break;
244ead75a59SLuigi Rizzo 		default:
2450f71e509SAndrey V. Elsukov 			errx(EX_DATAERR,
2460f71e509SAndrey V. Elsukov 			    "invalid option for ipv6 exten header");
247ead75a59SLuigi Rizzo 			break;
248ead75a59SLuigi Rizzo 		}
249ead75a59SLuigi Rizzo 	}
250ead75a59SLuigi Rizzo 	if (cmd->arg1 == 0)
2510f71e509SAndrey V. Elsukov 		return (0);
252ead75a59SLuigi Rizzo 	cmd->opcode = O_EXT_HDR;
253ead75a59SLuigi Rizzo 	cmd->len |= F_INSN_SIZE(ipfw_insn);
2540f71e509SAndrey V. Elsukov 	return (1);
255ead75a59SLuigi Rizzo }
256ead75a59SLuigi Rizzo 
257ead75a59SLuigi Rizzo void
print_ext6hdr(struct buf_pr * bp,const ipfw_insn * cmd)25856707beeSMark Johnston print_ext6hdr(struct buf_pr *bp, const ipfw_insn *cmd )
259ead75a59SLuigi Rizzo {
260ead75a59SLuigi Rizzo 	char sep = ' ';
261ead75a59SLuigi Rizzo 
262912430f6SAlexander V. Chernikov 	bprintf(bp, " extension header:");
263ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_FRAGMENT) {
264912430f6SAlexander V. Chernikov 		bprintf(bp, "%cfragmentation", sep);
265ead75a59SLuigi Rizzo 		sep = ',';
266ead75a59SLuigi Rizzo 	}
267ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_HOPOPTS) {
268912430f6SAlexander V. Chernikov 		bprintf(bp, "%chop options", sep);
269ead75a59SLuigi Rizzo 		sep = ',';
270ead75a59SLuigi Rizzo 	}
271ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_ROUTING) {
272912430f6SAlexander V. Chernikov 		bprintf(bp, "%crouting options", sep);
273ead75a59SLuigi Rizzo 		sep = ',';
274ead75a59SLuigi Rizzo 	}
275ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_RTHDR0) {
276912430f6SAlexander V. Chernikov 		bprintf(bp, "%crthdr0", sep);
277ead75a59SLuigi Rizzo 		sep = ',';
278ead75a59SLuigi Rizzo 	}
279ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_RTHDR2) {
280912430f6SAlexander V. Chernikov 		bprintf(bp, "%crthdr2", sep);
281ead75a59SLuigi Rizzo 		sep = ',';
282ead75a59SLuigi Rizzo 	}
283ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_DSTOPTS) {
284912430f6SAlexander V. Chernikov 		bprintf(bp, "%cdestination options", sep);
285ead75a59SLuigi Rizzo 		sep = ',';
286ead75a59SLuigi Rizzo 	}
287ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_AH) {
288912430f6SAlexander V. Chernikov 		bprintf(bp, "%cauthentication header", sep);
289ead75a59SLuigi Rizzo 		sep = ',';
290ead75a59SLuigi Rizzo 	}
291ead75a59SLuigi Rizzo 	if (cmd->arg1 & EXT_ESP) {
292912430f6SAlexander V. Chernikov 		bprintf(bp, "%cencapsulated security payload", sep);
293ead75a59SLuigi Rizzo 	}
294ead75a59SLuigi Rizzo }
295ead75a59SLuigi Rizzo 
296ead75a59SLuigi Rizzo /* Try to find ipv6 address by hostname */
297ead75a59SLuigi Rizzo static int
lookup_host6(char * host,struct in6_addr * ip6addr)298ead75a59SLuigi Rizzo lookup_host6 (char *host, struct in6_addr *ip6addr)
299ead75a59SLuigi Rizzo {
300ead75a59SLuigi Rizzo 	struct hostent *he;
301ead75a59SLuigi Rizzo 
302ead75a59SLuigi Rizzo 	if (!inet_pton(AF_INET6, host, ip6addr)) {
303ead75a59SLuigi Rizzo 		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
304ead75a59SLuigi Rizzo 			return(-1);
305ead75a59SLuigi Rizzo 		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
306ead75a59SLuigi Rizzo 	}
307ead75a59SLuigi Rizzo 	return (0);
308ead75a59SLuigi Rizzo }
309ead75a59SLuigi Rizzo 
310ead75a59SLuigi Rizzo 
311ead75a59SLuigi Rizzo /*
312ead75a59SLuigi Rizzo  * fill the addr and mask fields in the instruction as appropriate from av.
313ead75a59SLuigi Rizzo  * Update length as appropriate.
314ead75a59SLuigi Rizzo  * The following formats are allowed:
315ead75a59SLuigi Rizzo  *     any     matches any IP6. Actually returns an empty instruction.
316ead75a59SLuigi Rizzo  *     me      returns O_IP6_*_ME
317ead75a59SLuigi Rizzo  *
318b68ac800SPedro F. Giffuni  *     03f1::234:123:0342			single IP6 address
31923b93085SAndrey V. Elsukov  *     03f1::234:123:0342/24			address/masklen
32023b93085SAndrey V. Elsukov  *     03f1::234:123:0342/ffff::ffff:ffff	address/mask
321ead75a59SLuigi Rizzo  *     03f1::234:123:0342/24,03f1::234:123:0343/	List of address
322ead75a59SLuigi Rizzo  *
323ead75a59SLuigi Rizzo  * Set of address (as in ipv6) not supported because ipv6 address
324ead75a59SLuigi Rizzo  * are typically random past the initial prefix.
325ead75a59SLuigi Rizzo  * Return 1 on success, 0 on failure.
326ead75a59SLuigi Rizzo  */
327ead75a59SLuigi Rizzo static int
fill_ip6(ipfw_insn_ip6 * cmd,char * av,int cblen,struct tidx * tstate)328757b5d87SAndrey V. Elsukov fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen, struct tidx *tstate)
329ead75a59SLuigi Rizzo {
330ead75a59SLuigi Rizzo 	int len = 0;
331ead75a59SLuigi Rizzo 	struct in6_addr *d = &(cmd->addr6);
332247cea8fSMarius Strobl 	char *oav;
333ead75a59SLuigi Rizzo 	/*
334ead75a59SLuigi Rizzo 	 * Needed for multiple address.
335ead75a59SLuigi Rizzo 	 * Note d[1] points to struct in6_add r mask6 of cmd
336ead75a59SLuigi Rizzo 	 */
337ead75a59SLuigi Rizzo 
338ead75a59SLuigi Rizzo 	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
339ead75a59SLuigi Rizzo 
340ead75a59SLuigi Rizzo 	if (strcmp(av, "any") == 0)
341ead75a59SLuigi Rizzo 		return (1);
342ead75a59SLuigi Rizzo 
34327b3db97SMark Johnston 	/* Set the data for "me" opt */
34427b3db97SMark Johnston 	if (strcmp(av, "me") == 0 || strcmp(av, "me6") == 0) {
345ead75a59SLuigi Rizzo 		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
346ead75a59SLuigi Rizzo 		return (1);
347ead75a59SLuigi Rizzo 	}
348ead75a59SLuigi Rizzo 
3497e00325dSAlexander V. Chernikov 	if (strncmp(av, "table(", 6) == 0) {
350757b5d87SAndrey V. Elsukov 		fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
3517e00325dSAlexander V. Chernikov 		return (1);
3527e00325dSAlexander V. Chernikov 	}
3537e00325dSAlexander V. Chernikov 
354247cea8fSMarius Strobl 	oav = av = strdup(av);
355ead75a59SLuigi Rizzo 	while (av) {
356ead75a59SLuigi Rizzo 		/*
357ead75a59SLuigi Rizzo 		 * After the address we can have '/' indicating a mask,
358ead75a59SLuigi Rizzo 		 * or ',' indicating another address follows.
359ead75a59SLuigi Rizzo 		 */
360ead75a59SLuigi Rizzo 
36123b93085SAndrey V. Elsukov 		char *p, *q;
362ead75a59SLuigi Rizzo 		int masklen;
363ead75a59SLuigi Rizzo 		char md = '\0';
364ead75a59SLuigi Rizzo 
36556707beeSMark Johnston 		CHECK_LENGTH(cblen,
36656707beeSMark Johnston 		    1 + len + 2 * (int)F_INSN_SIZE(struct in6_addr));
367579ed7bdSAlexander V. Chernikov 
36823b93085SAndrey V. Elsukov 		if ((q = strchr(av, ',')) ) {
36923b93085SAndrey V. Elsukov 			*q = '\0';
37023b93085SAndrey V. Elsukov 			q++;
37123b93085SAndrey V. Elsukov 		}
37223b93085SAndrey V. Elsukov 
37323b93085SAndrey V. Elsukov 		if ((p = strchr(av, '/')) ) {
374ead75a59SLuigi Rizzo 			md = *p;	/* save the separator */
375ead75a59SLuigi Rizzo 			*p = '\0';	/* terminate address string */
376ead75a59SLuigi Rizzo 			p++;		/* and skip past it */
377ead75a59SLuigi Rizzo 		}
378ead75a59SLuigi Rizzo 		/* now p points to NULL, mask or next entry */
379ead75a59SLuigi Rizzo 
380ead75a59SLuigi Rizzo 		/* lookup stores address in *d as a side effect */
381ead75a59SLuigi Rizzo 		if (lookup_host6(av, d) != 0) {
382ead75a59SLuigi Rizzo 			/* XXX: failed. Free memory and go */
383ead75a59SLuigi Rizzo 			errx(EX_DATAERR, "bad address \"%s\"", av);
384ead75a59SLuigi Rizzo 		}
385ead75a59SLuigi Rizzo 		/* next, look at the mask, if any */
38623b93085SAndrey V. Elsukov 		if (md == '/' && strchr(p, ':')) {
38723b93085SAndrey V. Elsukov 			if (!inet_pton(AF_INET6, p, &d[1]))
38823b93085SAndrey V. Elsukov 				errx(EX_DATAERR, "bad mask \"%s\"", p);
38923b93085SAndrey V. Elsukov 
39023b93085SAndrey V. Elsukov 			masklen = contigmask((uint8_t *)&(d[1]), 128);
39123b93085SAndrey V. Elsukov 		} else {
392ead75a59SLuigi Rizzo 			masklen = (md == '/') ? atoi(p) : 128;
393ead75a59SLuigi Rizzo 			if (masklen > 128 || masklen < 0)
394ead75a59SLuigi Rizzo 				errx(EX_DATAERR, "bad width \"%s\''", p);
395ead75a59SLuigi Rizzo 			else
396ead75a59SLuigi Rizzo 				n2mask(&d[1], masklen);
39723b93085SAndrey V. Elsukov 		}
398ead75a59SLuigi Rizzo 
3995786c6b9SAndrey V. Elsukov 		APPLY_MASK(d, &d[1]);   /* mask base address with mask */
400ead75a59SLuigi Rizzo 
40123b93085SAndrey V. Elsukov 		av = q;
402ead75a59SLuigi Rizzo 
403ead75a59SLuigi Rizzo 		/* Check this entry */
404ead75a59SLuigi Rizzo 		if (masklen == 0) {
405ead75a59SLuigi Rizzo 			/*
406ead75a59SLuigi Rizzo 			 * 'any' turns the entire list into a NOP.
407ead75a59SLuigi Rizzo 			 * 'not any' never matches, so it is removed from the
408ead75a59SLuigi Rizzo 			 * list unless it is the only item, in which case we
409ead75a59SLuigi Rizzo 			 * report an error.
410ead75a59SLuigi Rizzo 			 */
411ead75a59SLuigi Rizzo 			if (cmd->o.len & F_NOT && av == NULL && len == 0)
412ead75a59SLuigi Rizzo 				errx(EX_DATAERR, "not any never matches");
413ead75a59SLuigi Rizzo 			continue;
414ead75a59SLuigi Rizzo 		}
415ead75a59SLuigi Rizzo 
416ead75a59SLuigi Rizzo 		/*
417ead75a59SLuigi Rizzo 		 * A single IP can be stored alone
418ead75a59SLuigi Rizzo 		 */
419ead75a59SLuigi Rizzo 		if (masklen == 128 && av == NULL && len == 0) {
420ead75a59SLuigi Rizzo 			len = F_INSN_SIZE(struct in6_addr);
421ead75a59SLuigi Rizzo 			break;
422ead75a59SLuigi Rizzo 		}
423ead75a59SLuigi Rizzo 
424ead75a59SLuigi Rizzo 		/* Update length and pointer to arguments */
425ead75a59SLuigi Rizzo 		len += F_INSN_SIZE(struct in6_addr)*2;
426ead75a59SLuigi Rizzo 		d += 2;
427ead75a59SLuigi Rizzo 	} /* end while */
428ead75a59SLuigi Rizzo 
429ead75a59SLuigi Rizzo 	/*
430ead75a59SLuigi Rizzo 	 * Total length of the command, remember that 1 is the size of
431ead75a59SLuigi Rizzo 	 * the base command.
432ead75a59SLuigi Rizzo 	 */
433ead75a59SLuigi Rizzo 	if (len + 1 > F_LEN_MASK)
434ead75a59SLuigi Rizzo 		errx(EX_DATAERR, "address list too long");
435ead75a59SLuigi Rizzo 	cmd->o.len |= len+1;
436247cea8fSMarius Strobl 	free(oav);
437ead75a59SLuigi Rizzo 	return (1);
438ead75a59SLuigi Rizzo }
439ead75a59SLuigi Rizzo 
440ead75a59SLuigi Rizzo /*
441ead75a59SLuigi Rizzo  * fills command for ipv6 flow-id filtering
442ead75a59SLuigi Rizzo  * note that the 20 bit flow number is stored in a array of u_int32_t
443ead75a59SLuigi Rizzo  * it's supported lists of flow-id, so in the o.arg1 we store how many
444ead75a59SLuigi Rizzo  * additional flow-id we want to filter, the basic is 1
445ead75a59SLuigi Rizzo  */
446ead75a59SLuigi Rizzo void
fill_flow6(ipfw_insn_u32 * cmd,char * av,int cblen)447579ed7bdSAlexander V. Chernikov fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen)
448ead75a59SLuigi Rizzo {
449ead75a59SLuigi Rizzo 	u_int32_t type;	 /* Current flow number */
450ead75a59SLuigi Rizzo 	u_int16_t nflow = 0;    /* Current flow index */
451ead75a59SLuigi Rizzo 	char *s = av;
452ead75a59SLuigi Rizzo 	cmd->d[0] = 0;	  /* Initializing the base number*/
453ead75a59SLuigi Rizzo 
454ead75a59SLuigi Rizzo 	while (s) {
45556707beeSMark Johnston 		CHECK_LENGTH(cblen,
45656707beeSMark Johnston 		    (int)F_INSN_SIZE(ipfw_insn_u32) + nflow + 1);
457579ed7bdSAlexander V. Chernikov 
458ead75a59SLuigi Rizzo 		av = strsep( &s, ",") ;
459ead75a59SLuigi Rizzo 		type = strtoul(av, &av, 0);
460ead75a59SLuigi Rizzo 		if (*av != ',' && *av != '\0')
461ead75a59SLuigi Rizzo 			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
462ead75a59SLuigi Rizzo 		if (type > 0xfffff)
463ead75a59SLuigi Rizzo 			errx(EX_DATAERR, "flow number out of range %s", av);
464ead75a59SLuigi Rizzo 		cmd->d[nflow] |= type;
465ead75a59SLuigi Rizzo 		nflow++;
466ead75a59SLuigi Rizzo 	}
467ead75a59SLuigi Rizzo 	if( nflow > 0 ) {
468ead75a59SLuigi Rizzo 		cmd->o.opcode = O_FLOW6ID;
469ead75a59SLuigi Rizzo 		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
470ead75a59SLuigi Rizzo 		cmd->o.arg1 = nflow;
471ead75a59SLuigi Rizzo 	}
472ead75a59SLuigi Rizzo 	else {
473ead75a59SLuigi Rizzo 		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
474ead75a59SLuigi Rizzo 	}
475ead75a59SLuigi Rizzo }
476ead75a59SLuigi Rizzo 
477ead75a59SLuigi Rizzo ipfw_insn *
add_srcip6(ipfw_insn * cmd,char * av,int cblen,struct tidx * tstate)478757b5d87SAndrey V. Elsukov add_srcip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
479ead75a59SLuigi Rizzo {
480ead75a59SLuigi Rizzo 
481757b5d87SAndrey V. Elsukov 	fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate);
4827e00325dSAlexander V. Chernikov 	if (cmd->opcode == O_IP_DST_SET)			/* set */
4837e00325dSAlexander V. Chernikov 		cmd->opcode = O_IP_SRC_SET;
4847e00325dSAlexander V. Chernikov 	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4857e00325dSAlexander V. Chernikov 		cmd->opcode = O_IP_SRC_LOOKUP;
4867e00325dSAlexander V. Chernikov 	else if (F_LEN(cmd) == 0) {				/* any */
487ead75a59SLuigi Rizzo 	} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
488ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_SRC_ME;
489ead75a59SLuigi Rizzo 	} else if (F_LEN(cmd) ==
490ead75a59SLuigi Rizzo 	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
491ead75a59SLuigi Rizzo 		/* single IP, no mask*/
492ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_SRC;
493ead75a59SLuigi Rizzo 	} else {					/* addr/mask opt */
494ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_SRC_MASK;
495ead75a59SLuigi Rizzo 	}
496ead75a59SLuigi Rizzo 	return cmd;
497ead75a59SLuigi Rizzo }
498ead75a59SLuigi Rizzo 
499ead75a59SLuigi Rizzo ipfw_insn *
add_dstip6(ipfw_insn * cmd,char * av,int cblen,struct tidx * tstate)500757b5d87SAndrey V. Elsukov add_dstip6(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
501ead75a59SLuigi Rizzo {
502ead75a59SLuigi Rizzo 
503757b5d87SAndrey V. Elsukov 	fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen, tstate);
5047e00325dSAlexander V. Chernikov 	if (cmd->opcode == O_IP_DST_SET)			/* set */
5057e00325dSAlexander V. Chernikov 		;
5067e00325dSAlexander V. Chernikov 	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
5077e00325dSAlexander V. Chernikov 		;
5087e00325dSAlexander V. Chernikov 	else if (F_LEN(cmd) == 0) {				/* any */
509ead75a59SLuigi Rizzo 	} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
510ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_DST_ME;
511ead75a59SLuigi Rizzo 	} else if (F_LEN(cmd) ==
512ead75a59SLuigi Rizzo 	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
513ead75a59SLuigi Rizzo 		/* single IP, no mask*/
514ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_DST;
515ead75a59SLuigi Rizzo 	} else {					/* addr/mask opt */
516ead75a59SLuigi Rizzo 		cmd->opcode = O_IP6_DST_MASK;
517ead75a59SLuigi Rizzo 	}
518ead75a59SLuigi Rizzo 	return cmd;
519ead75a59SLuigi Rizzo }
520