xref: /freebsd/sys/netpfil/ipfw/nptv6/nptv6.c (revision fdafd315)
1b867e84eSAndrey V. Elsukov /*-
2b867e84eSAndrey V. Elsukov  * Copyright (c) 2016 Yandex LLC
3b867e84eSAndrey V. Elsukov  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
4b867e84eSAndrey V. Elsukov  * All rights reserved.
5b867e84eSAndrey V. Elsukov  *
6b867e84eSAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
7b867e84eSAndrey V. Elsukov  * modification, are permitted provided that the following conditions
8b867e84eSAndrey V. Elsukov  * are met:
9b867e84eSAndrey V. Elsukov  *
10b867e84eSAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
11b867e84eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
12b867e84eSAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
13b867e84eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
14b867e84eSAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
15b867e84eSAndrey V. Elsukov  *
16b867e84eSAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17b867e84eSAndrey V. Elsukov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18b867e84eSAndrey V. Elsukov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19b867e84eSAndrey V. Elsukov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20b867e84eSAndrey V. Elsukov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21b867e84eSAndrey V. Elsukov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22b867e84eSAndrey V. Elsukov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23b867e84eSAndrey V. Elsukov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24b867e84eSAndrey V. Elsukov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25b867e84eSAndrey V. Elsukov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26b867e84eSAndrey V. Elsukov  */
27b867e84eSAndrey V. Elsukov 
28b867e84eSAndrey V. Elsukov #include <sys/param.h>
29b867e84eSAndrey V. Elsukov #include <sys/systm.h>
30b867e84eSAndrey V. Elsukov #include <sys/counter.h>
31b2b56606SAndrey V. Elsukov #include <sys/eventhandler.h>
32b867e84eSAndrey V. Elsukov #include <sys/errno.h>
33b867e84eSAndrey V. Elsukov #include <sys/kernel.h>
34b867e84eSAndrey V. Elsukov #include <sys/lock.h>
35b867e84eSAndrey V. Elsukov #include <sys/malloc.h>
36b867e84eSAndrey V. Elsukov #include <sys/mbuf.h>
37b867e84eSAndrey V. Elsukov #include <sys/module.h>
38b867e84eSAndrey V. Elsukov #include <sys/rmlock.h>
39b867e84eSAndrey V. Elsukov #include <sys/rwlock.h>
40b867e84eSAndrey V. Elsukov #include <sys/socket.h>
41b867e84eSAndrey V. Elsukov #include <sys/queue.h>
42b867e84eSAndrey V. Elsukov #include <sys/syslog.h>
43b867e84eSAndrey V. Elsukov #include <sys/sysctl.h>
44b867e84eSAndrey V. Elsukov 
45b867e84eSAndrey V. Elsukov #include <net/if.h>
46b867e84eSAndrey V. Elsukov #include <net/if_var.h>
473d0d5b21SJustin Hibbits #include <net/if_private.h>
48b867e84eSAndrey V. Elsukov #include <net/netisr.h>
49b867e84eSAndrey V. Elsukov #include <net/pfil.h>
50b867e84eSAndrey V. Elsukov #include <net/vnet.h>
51b867e84eSAndrey V. Elsukov 
52b867e84eSAndrey V. Elsukov #include <netinet/in.h>
53b867e84eSAndrey V. Elsukov #include <netinet/ip_var.h>
54b867e84eSAndrey V. Elsukov #include <netinet/ip_fw.h>
55b867e84eSAndrey V. Elsukov #include <netinet/ip6.h>
56b867e84eSAndrey V. Elsukov #include <netinet/icmp6.h>
57b867e84eSAndrey V. Elsukov #include <netinet6/in6_var.h>
58b867e84eSAndrey V. Elsukov #include <netinet6/ip6_var.h>
59b867e84eSAndrey V. Elsukov 
60b867e84eSAndrey V. Elsukov #include <netpfil/ipfw/ip_fw_private.h>
61b867e84eSAndrey V. Elsukov #include <netpfil/ipfw/nptv6/nptv6.h>
62b867e84eSAndrey V. Elsukov 
635f901c92SAndrew Turner VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0;
64b867e84eSAndrey V. Elsukov #define	V_nptv6_eid	VNET(nptv6_eid)
65b867e84eSAndrey V. Elsukov #define	IPFW_TLV_NPTV6_NAME	IPFW_TLV_EACTION_NAME(V_nptv6_eid)
66b867e84eSAndrey V. Elsukov 
67b2b56606SAndrey V. Elsukov static eventhandler_tag nptv6_ifaddr_event;
68b2b56606SAndrey V. Elsukov 
69b867e84eSAndrey V. Elsukov static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set);
70b867e84eSAndrey V. Elsukov static void nptv6_free_config(struct nptv6_cfg *cfg);
71b867e84eSAndrey V. Elsukov static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni,
72b867e84eSAndrey V. Elsukov     const char *name, uint8_t set);
73b867e84eSAndrey V. Elsukov static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp,
74b867e84eSAndrey V. Elsukov     int offset);
75b867e84eSAndrey V. Elsukov static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp,
76b867e84eSAndrey V. Elsukov     int offset);
77b867e84eSAndrey V. Elsukov 
78b867e84eSAndrey V. Elsukov #define	NPTV6_LOOKUP(chain, cmd)	\
79b867e84eSAndrey V. Elsukov     (struct nptv6_cfg *)SRV_OBJECT((chain), (cmd)->arg1)
80b867e84eSAndrey V. Elsukov 
81b867e84eSAndrey V. Elsukov #ifndef IN6_MASK_ADDR
82b867e84eSAndrey V. Elsukov #define IN6_MASK_ADDR(a, m)	do { \
83b867e84eSAndrey V. Elsukov 	(a)->s6_addr32[0] &= (m)->s6_addr32[0]; \
84b867e84eSAndrey V. Elsukov 	(a)->s6_addr32[1] &= (m)->s6_addr32[1]; \
85b867e84eSAndrey V. Elsukov 	(a)->s6_addr32[2] &= (m)->s6_addr32[2]; \
86b867e84eSAndrey V. Elsukov 	(a)->s6_addr32[3] &= (m)->s6_addr32[3]; \
87b867e84eSAndrey V. Elsukov } while (0)
88b867e84eSAndrey V. Elsukov #endif
89b867e84eSAndrey V. Elsukov #ifndef IN6_ARE_MASKED_ADDR_EQUAL
90b867e84eSAndrey V. Elsukov #define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m)	(	\
91b867e84eSAndrey V. Elsukov 	(((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
92b867e84eSAndrey V. Elsukov 	(((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
93b867e84eSAndrey V. Elsukov 	(((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
94b867e84eSAndrey V. Elsukov 	(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
95b867e84eSAndrey V. Elsukov #endif
96b867e84eSAndrey V. Elsukov 
97b867e84eSAndrey V. Elsukov #if 0
98b867e84eSAndrey V. Elsukov #define	NPTV6_DEBUG(fmt, ...)	do {			\
99b867e84eSAndrey V. Elsukov 	printf("%s: " fmt "\n", __func__, ## __VA_ARGS__);	\
100b867e84eSAndrey V. Elsukov } while (0)
101b867e84eSAndrey V. Elsukov #define	NPTV6_IPDEBUG(fmt, ...)	do {			\
102b867e84eSAndrey V. Elsukov 	char _s[INET6_ADDRSTRLEN], _d[INET6_ADDRSTRLEN];	\
103b867e84eSAndrey V. Elsukov 	printf("%s: " fmt "\n", __func__, ## __VA_ARGS__);	\
104b867e84eSAndrey V. Elsukov } while (0)
105b867e84eSAndrey V. Elsukov #else
106b867e84eSAndrey V. Elsukov #define	NPTV6_DEBUG(fmt, ...)
107b867e84eSAndrey V. Elsukov #define	NPTV6_IPDEBUG(fmt, ...)
108b867e84eSAndrey V. Elsukov #endif
109b867e84eSAndrey V. Elsukov 
110b867e84eSAndrey V. Elsukov static int
nptv6_getlasthdr(struct nptv6_cfg * cfg,struct mbuf * m,int * offset)111b867e84eSAndrey V. Elsukov nptv6_getlasthdr(struct nptv6_cfg *cfg, struct mbuf *m, int *offset)
112b867e84eSAndrey V. Elsukov {
113b867e84eSAndrey V. Elsukov 	struct ip6_hdr *ip6;
114b867e84eSAndrey V. Elsukov 	struct ip6_hbh *hbh;
115b867e84eSAndrey V. Elsukov 	int proto, hlen;
116b867e84eSAndrey V. Elsukov 
117b867e84eSAndrey V. Elsukov 	hlen = (offset == NULL) ? 0: *offset;
118b867e84eSAndrey V. Elsukov 	if (m->m_len < hlen)
119b867e84eSAndrey V. Elsukov 		return (-1);
120b867e84eSAndrey V. Elsukov 	ip6 = mtodo(m, hlen);
121b867e84eSAndrey V. Elsukov 	hlen += sizeof(*ip6);
122b867e84eSAndrey V. Elsukov 	proto = ip6->ip6_nxt;
123b867e84eSAndrey V. Elsukov 	while (proto == IPPROTO_HOPOPTS || proto == IPPROTO_ROUTING ||
124b867e84eSAndrey V. Elsukov 	    proto == IPPROTO_DSTOPTS) {
125b867e84eSAndrey V. Elsukov 		hbh = mtodo(m, hlen);
126b867e84eSAndrey V. Elsukov 		if (m->m_len < hlen)
127b867e84eSAndrey V. Elsukov 			return (-1);
128b867e84eSAndrey V. Elsukov 		proto = hbh->ip6h_nxt;
129785c0d4dSAndrey V. Elsukov 		hlen += (hbh->ip6h_len + 1) << 3;
130b867e84eSAndrey V. Elsukov 	}
131b867e84eSAndrey V. Elsukov 	if (offset != NULL)
132b867e84eSAndrey V. Elsukov 		*offset = hlen;
133b867e84eSAndrey V. Elsukov 	return (proto);
134b867e84eSAndrey V. Elsukov }
135b867e84eSAndrey V. Elsukov 
136b867e84eSAndrey V. Elsukov static int
nptv6_translate_icmpv6(struct nptv6_cfg * cfg,struct mbuf ** mp,int offset)137b867e84eSAndrey V. Elsukov nptv6_translate_icmpv6(struct nptv6_cfg *cfg, struct mbuf **mp, int offset)
138b867e84eSAndrey V. Elsukov {
139b867e84eSAndrey V. Elsukov 	struct icmp6_hdr *icmp6;
140b867e84eSAndrey V. Elsukov 	struct ip6_hdr *ip6;
141b867e84eSAndrey V. Elsukov 	struct mbuf *m;
142b867e84eSAndrey V. Elsukov 
143b867e84eSAndrey V. Elsukov 	m = *mp;
144b867e84eSAndrey V. Elsukov 	if (offset > m->m_len)
145b867e84eSAndrey V. Elsukov 		return (-1);
146b867e84eSAndrey V. Elsukov 	icmp6 = mtodo(m, offset);
147b867e84eSAndrey V. Elsukov 	NPTV6_DEBUG("ICMPv6 type %d", icmp6->icmp6_type);
148b867e84eSAndrey V. Elsukov 	switch (icmp6->icmp6_type) {
149b867e84eSAndrey V. Elsukov 	case ICMP6_DST_UNREACH:
150b867e84eSAndrey V. Elsukov 	case ICMP6_PACKET_TOO_BIG:
151b867e84eSAndrey V. Elsukov 	case ICMP6_TIME_EXCEEDED:
152b867e84eSAndrey V. Elsukov 	case ICMP6_PARAM_PROB:
153b867e84eSAndrey V. Elsukov 		break;
154b867e84eSAndrey V. Elsukov 	case ICMP6_ECHO_REQUEST:
155b867e84eSAndrey V. Elsukov 	case ICMP6_ECHO_REPLY:
156b867e84eSAndrey V. Elsukov 		/* nothing to translate */
157b867e84eSAndrey V. Elsukov 		return (0);
158b867e84eSAndrey V. Elsukov 	default:
159b867e84eSAndrey V. Elsukov 		/*
160b867e84eSAndrey V. Elsukov 		 * XXX: We can add some checks to not translate NDP and MLD
161b867e84eSAndrey V. Elsukov 		 * messages. Currently user must explicitly allow these message
162b867e84eSAndrey V. Elsukov 		 * types, otherwise packets will be dropped.
163b867e84eSAndrey V. Elsukov 		 */
164b867e84eSAndrey V. Elsukov 		return (-1);
165b867e84eSAndrey V. Elsukov 	}
166b867e84eSAndrey V. Elsukov 	offset += sizeof(*icmp6);
167b867e84eSAndrey V. Elsukov 	if (offset + sizeof(*ip6) > m->m_pkthdr.len)
168b867e84eSAndrey V. Elsukov 		return (-1);
169b867e84eSAndrey V. Elsukov 	if (offset + sizeof(*ip6) > m->m_len)
170b867e84eSAndrey V. Elsukov 		*mp = m = m_pullup(m, offset + sizeof(*ip6));
171b867e84eSAndrey V. Elsukov 	if (m == NULL)
172b867e84eSAndrey V. Elsukov 		return (-1);
173b867e84eSAndrey V. Elsukov 	ip6 = mtodo(m, offset);
174b867e84eSAndrey V. Elsukov 	NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset,
175b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)),
176b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)),
177b867e84eSAndrey V. Elsukov 	    ip6->ip6_nxt);
178b867e84eSAndrey V. Elsukov 	if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src,
179b867e84eSAndrey V. Elsukov 	    &cfg->external, &cfg->mask))
180b867e84eSAndrey V. Elsukov 		return (nptv6_rewrite_external(cfg, mp, offset));
181b867e84eSAndrey V. Elsukov 	else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst,
182b867e84eSAndrey V. Elsukov 	    &cfg->internal, &cfg->mask))
183b867e84eSAndrey V. Elsukov 		return (nptv6_rewrite_internal(cfg, mp, offset));
184b867e84eSAndrey V. Elsukov 	/*
185b867e84eSAndrey V. Elsukov 	 * Addresses in the inner IPv6 header doesn't matched to
186b867e84eSAndrey V. Elsukov 	 * our prefixes.
187b867e84eSAndrey V. Elsukov 	 */
188b867e84eSAndrey V. Elsukov 	return (-1);
189b867e84eSAndrey V. Elsukov }
190b867e84eSAndrey V. Elsukov 
191b867e84eSAndrey V. Elsukov static int
nptv6_search_index(struct nptv6_cfg * cfg,struct in6_addr * a)192b867e84eSAndrey V. Elsukov nptv6_search_index(struct nptv6_cfg *cfg, struct in6_addr *a)
193b867e84eSAndrey V. Elsukov {
194b867e84eSAndrey V. Elsukov 	int idx;
195b867e84eSAndrey V. Elsukov 
196b867e84eSAndrey V. Elsukov 	if (cfg->flags & NPTV6_48PLEN)
197b867e84eSAndrey V. Elsukov 		return (3);
198b867e84eSAndrey V. Elsukov 
199b867e84eSAndrey V. Elsukov 	/* Search suitable word index for adjustment */
200b867e84eSAndrey V. Elsukov 	for (idx = 4; idx < 8; idx++)
201b867e84eSAndrey V. Elsukov 		if (a->s6_addr16[idx] != 0xffff)
202b867e84eSAndrey V. Elsukov 			break;
203b867e84eSAndrey V. Elsukov 	/*
204b867e84eSAndrey V. Elsukov 	 * RFC 6296 p3.7: If an NPTv6 Translator discovers a datagram with
205b867e84eSAndrey V. Elsukov 	 * an IID of all-zeros while performing address mapping, that
206b867e84eSAndrey V. Elsukov 	 * datagram MUST be dropped, and an ICMPv6 Parameter Problem error
207b867e84eSAndrey V. Elsukov 	 * SHOULD be generated.
208b867e84eSAndrey V. Elsukov 	 */
209b867e84eSAndrey V. Elsukov 	if (idx == 8 ||
210b867e84eSAndrey V. Elsukov 	    (a->s6_addr32[2] == 0 && a->s6_addr32[3] == 0))
211b867e84eSAndrey V. Elsukov 		return (-1);
212b867e84eSAndrey V. Elsukov 	return (idx);
213b867e84eSAndrey V. Elsukov }
214b867e84eSAndrey V. Elsukov 
215b867e84eSAndrey V. Elsukov static void
nptv6_copy_addr(struct in6_addr * src,struct in6_addr * dst,struct in6_addr * mask)216b867e84eSAndrey V. Elsukov nptv6_copy_addr(struct in6_addr *src, struct in6_addr *dst,
217b867e84eSAndrey V. Elsukov     struct in6_addr *mask)
218b867e84eSAndrey V. Elsukov {
219b867e84eSAndrey V. Elsukov 	int i;
220b867e84eSAndrey V. Elsukov 
221b867e84eSAndrey V. Elsukov 	for (i = 0; i < 8 && mask->s6_addr8[i] != 0; i++) {
222b867e84eSAndrey V. Elsukov 		dst->s6_addr8[i] &=  ~mask->s6_addr8[i];
223b867e84eSAndrey V. Elsukov 		dst->s6_addr8[i] |= src->s6_addr8[i] & mask->s6_addr8[i];
224b867e84eSAndrey V. Elsukov 	}
225b867e84eSAndrey V. Elsukov }
226b867e84eSAndrey V. Elsukov 
227b867e84eSAndrey V. Elsukov static int
nptv6_rewrite_internal(struct nptv6_cfg * cfg,struct mbuf ** mp,int offset)228b867e84eSAndrey V. Elsukov nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, int offset)
229b867e84eSAndrey V. Elsukov {
230b867e84eSAndrey V. Elsukov 	struct in6_addr *addr;
231b867e84eSAndrey V. Elsukov 	struct ip6_hdr *ip6;
232b867e84eSAndrey V. Elsukov 	int idx, proto;
233b867e84eSAndrey V. Elsukov 	uint16_t adj;
234b867e84eSAndrey V. Elsukov 
235b867e84eSAndrey V. Elsukov 	ip6 = mtodo(*mp, offset);
236b867e84eSAndrey V. Elsukov 	NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset,
237b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)),
238b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)),
239b867e84eSAndrey V. Elsukov 	    ip6->ip6_nxt);
240b867e84eSAndrey V. Elsukov 	if (offset == 0)
241b867e84eSAndrey V. Elsukov 		addr = &ip6->ip6_src;
242b867e84eSAndrey V. Elsukov 	else {
243b867e84eSAndrey V. Elsukov 		/*
244b867e84eSAndrey V. Elsukov 		 * When we rewriting inner IPv6 header, we need to rewrite
245b867e84eSAndrey V. Elsukov 		 * destination address back to external prefix. The datagram in
246b867e84eSAndrey V. Elsukov 		 * the ICMPv6 payload should looks like it was send from
247b867e84eSAndrey V. Elsukov 		 * external prefix.
248b867e84eSAndrey V. Elsukov 		 */
249b867e84eSAndrey V. Elsukov 		addr = &ip6->ip6_dst;
250b867e84eSAndrey V. Elsukov 	}
251b867e84eSAndrey V. Elsukov 	idx = nptv6_search_index(cfg, addr);
252b867e84eSAndrey V. Elsukov 	if (idx < 0) {
253b867e84eSAndrey V. Elsukov 		/*
254b867e84eSAndrey V. Elsukov 		 * Do not send ICMPv6 error when offset isn't zero.
255b867e84eSAndrey V. Elsukov 		 * This means we are rewriting inner IPv6 header in the
256b867e84eSAndrey V. Elsukov 		 * ICMPv6 error message.
257b867e84eSAndrey V. Elsukov 		 */
258b867e84eSAndrey V. Elsukov 		if (offset == 0) {
259b867e84eSAndrey V. Elsukov 			icmp6_error2(*mp, ICMP6_DST_UNREACH,
260b867e84eSAndrey V. Elsukov 			    ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif);
261b867e84eSAndrey V. Elsukov 			*mp = NULL;
262b867e84eSAndrey V. Elsukov 		}
263b867e84eSAndrey V. Elsukov 		return (IP_FW_DENY);
264b867e84eSAndrey V. Elsukov 	}
265b867e84eSAndrey V. Elsukov 	adj = addr->s6_addr16[idx];
266b867e84eSAndrey V. Elsukov 	nptv6_copy_addr(&cfg->external, addr, &cfg->mask);
267b867e84eSAndrey V. Elsukov 	adj = cksum_add(adj, cfg->adjustment);
268b867e84eSAndrey V. Elsukov 	if (adj == 0xffff)
269b867e84eSAndrey V. Elsukov 		adj = 0;
270b867e84eSAndrey V. Elsukov 	addr->s6_addr16[idx] = adj;
271b867e84eSAndrey V. Elsukov 	if (offset == 0) {
272b867e84eSAndrey V. Elsukov 		/*
273b867e84eSAndrey V. Elsukov 		 * We may need to translate addresses in the inner IPv6
274b867e84eSAndrey V. Elsukov 		 * header for ICMPv6 error messages.
275b867e84eSAndrey V. Elsukov 		 */
276b867e84eSAndrey V. Elsukov 		proto = nptv6_getlasthdr(cfg, *mp, &offset);
277b867e84eSAndrey V. Elsukov 		if (proto < 0 || (proto == IPPROTO_ICMPV6 &&
278b867e84eSAndrey V. Elsukov 		    nptv6_translate_icmpv6(cfg, mp, offset) != 0))
279b867e84eSAndrey V. Elsukov 			return (IP_FW_DENY);
280b867e84eSAndrey V. Elsukov 		NPTV6STAT_INC(cfg, in2ex);
281b867e84eSAndrey V. Elsukov 	}
282b867e84eSAndrey V. Elsukov 	return (0);
283b867e84eSAndrey V. Elsukov }
284b867e84eSAndrey V. Elsukov 
285b867e84eSAndrey V. Elsukov static int
nptv6_rewrite_external(struct nptv6_cfg * cfg,struct mbuf ** mp,int offset)286b867e84eSAndrey V. Elsukov nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, int offset)
287b867e84eSAndrey V. Elsukov {
288b867e84eSAndrey V. Elsukov 	struct in6_addr *addr;
289b867e84eSAndrey V. Elsukov 	struct ip6_hdr *ip6;
290b867e84eSAndrey V. Elsukov 	int idx, proto;
291b867e84eSAndrey V. Elsukov 	uint16_t adj;
292b867e84eSAndrey V. Elsukov 
293b867e84eSAndrey V. Elsukov 	ip6 = mtodo(*mp, offset);
294b867e84eSAndrey V. Elsukov 	NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset,
295b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)),
296b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)),
297b867e84eSAndrey V. Elsukov 	    ip6->ip6_nxt);
298b867e84eSAndrey V. Elsukov 	if (offset == 0)
299b867e84eSAndrey V. Elsukov 		addr = &ip6->ip6_dst;
300b867e84eSAndrey V. Elsukov 	else {
301b867e84eSAndrey V. Elsukov 		/*
302b867e84eSAndrey V. Elsukov 		 * When we rewriting inner IPv6 header, we need to rewrite
303b867e84eSAndrey V. Elsukov 		 * source address back to internal prefix. The datagram in
304b867e84eSAndrey V. Elsukov 		 * the ICMPv6 payload should looks like it was send from
305b867e84eSAndrey V. Elsukov 		 * internal prefix.
306b867e84eSAndrey V. Elsukov 		 */
307b867e84eSAndrey V. Elsukov 		addr = &ip6->ip6_src;
308b867e84eSAndrey V. Elsukov 	}
309b867e84eSAndrey V. Elsukov 	idx = nptv6_search_index(cfg, addr);
310b867e84eSAndrey V. Elsukov 	if (idx < 0) {
311b867e84eSAndrey V. Elsukov 		/*
312b867e84eSAndrey V. Elsukov 		 * Do not send ICMPv6 error when offset isn't zero.
313b867e84eSAndrey V. Elsukov 		 * This means we are rewriting inner IPv6 header in the
314b867e84eSAndrey V. Elsukov 		 * ICMPv6 error message.
315b867e84eSAndrey V. Elsukov 		 */
316b867e84eSAndrey V. Elsukov 		if (offset == 0) {
317b867e84eSAndrey V. Elsukov 			icmp6_error2(*mp, ICMP6_DST_UNREACH,
318b867e84eSAndrey V. Elsukov 			    ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif);
319b867e84eSAndrey V. Elsukov 			*mp = NULL;
320b867e84eSAndrey V. Elsukov 		}
321b867e84eSAndrey V. Elsukov 		return (IP_FW_DENY);
322b867e84eSAndrey V. Elsukov 	}
323b867e84eSAndrey V. Elsukov 	adj = addr->s6_addr16[idx];
324b867e84eSAndrey V. Elsukov 	nptv6_copy_addr(&cfg->internal, addr, &cfg->mask);
325b867e84eSAndrey V. Elsukov 	adj = cksum_add(adj, ~cfg->adjustment);
326b867e84eSAndrey V. Elsukov 	if (adj == 0xffff)
327b867e84eSAndrey V. Elsukov 		adj = 0;
328b867e84eSAndrey V. Elsukov 	addr->s6_addr16[idx] = adj;
329b867e84eSAndrey V. Elsukov 	if (offset == 0) {
330b867e84eSAndrey V. Elsukov 		/*
331b867e84eSAndrey V. Elsukov 		 * We may need to translate addresses in the inner IPv6
332b867e84eSAndrey V. Elsukov 		 * header for ICMPv6 error messages.
333b867e84eSAndrey V. Elsukov 		 */
334b867e84eSAndrey V. Elsukov 		proto = nptv6_getlasthdr(cfg, *mp, &offset);
335b867e84eSAndrey V. Elsukov 		if (proto < 0 || (proto == IPPROTO_ICMPV6 &&
336b867e84eSAndrey V. Elsukov 		    nptv6_translate_icmpv6(cfg, mp, offset) != 0))
337b867e84eSAndrey V. Elsukov 			return (IP_FW_DENY);
338b867e84eSAndrey V. Elsukov 		NPTV6STAT_INC(cfg, ex2in);
339b867e84eSAndrey V. Elsukov 	}
340b867e84eSAndrey V. Elsukov 	return (0);
341b867e84eSAndrey V. Elsukov }
342b867e84eSAndrey V. Elsukov 
343b867e84eSAndrey V. Elsukov /*
344b867e84eSAndrey V. Elsukov  * ipfw external action handler.
345b867e84eSAndrey V. Elsukov  */
346b867e84eSAndrey V. Elsukov static int
ipfw_nptv6(struct ip_fw_chain * chain,struct ip_fw_args * args,ipfw_insn * cmd,int * done)347b867e84eSAndrey V. Elsukov ipfw_nptv6(struct ip_fw_chain *chain, struct ip_fw_args *args,
348b867e84eSAndrey V. Elsukov     ipfw_insn *cmd, int *done)
349b867e84eSAndrey V. Elsukov {
350b867e84eSAndrey V. Elsukov 	struct ip6_hdr *ip6;
351b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
352b867e84eSAndrey V. Elsukov 	ipfw_insn *icmd;
353b867e84eSAndrey V. Elsukov 	int ret;
354b867e84eSAndrey V. Elsukov 
355b867e84eSAndrey V. Elsukov 	*done = 0; /* try next rule if not matched */
356576429f0SAndrey V. Elsukov 	ret = IP_FW_DENY;
357b867e84eSAndrey V. Elsukov 	icmd = cmd + 1;
358b867e84eSAndrey V. Elsukov 	if (cmd->opcode != O_EXTERNAL_ACTION ||
359b867e84eSAndrey V. Elsukov 	    cmd->arg1 != V_nptv6_eid ||
360b867e84eSAndrey V. Elsukov 	    icmd->opcode != O_EXTERNAL_INSTANCE ||
361b2b56606SAndrey V. Elsukov 	    (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL ||
362b2b56606SAndrey V. Elsukov 	    (cfg->flags & NPTV6_READY) == 0)
363576429f0SAndrey V. Elsukov 		return (ret);
364b867e84eSAndrey V. Elsukov 	/*
365b867e84eSAndrey V. Elsukov 	 * We need act as router, so when forwarding is disabled -
366b867e84eSAndrey V. Elsukov 	 * do nothing.
367b867e84eSAndrey V. Elsukov 	 */
368b867e84eSAndrey V. Elsukov 	if (V_ip6_forwarding == 0 || args->f_id.addr_type != 6)
369576429f0SAndrey V. Elsukov 		return (ret);
370b867e84eSAndrey V. Elsukov 	/*
371b867e84eSAndrey V. Elsukov 	 * NOTE: we expect ipfw_chk() did m_pullup() up to upper level
372b867e84eSAndrey V. Elsukov 	 * protocol's headers. Also we skip some checks, that ip6_input(),
373b867e84eSAndrey V. Elsukov 	 * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did.
374b867e84eSAndrey V. Elsukov 	 */
375b867e84eSAndrey V. Elsukov 	ip6 = mtod(args->m, struct ip6_hdr *);
376b867e84eSAndrey V. Elsukov 	NPTV6_IPDEBUG("eid %u, oid %u, %s -> %s %d",
377b867e84eSAndrey V. Elsukov 	    cmd->arg1, icmd->arg1,
378b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)),
379b867e84eSAndrey V. Elsukov 	    inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)),
380b867e84eSAndrey V. Elsukov 	    ip6->ip6_nxt);
381b867e84eSAndrey V. Elsukov 	if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src,
382b867e84eSAndrey V. Elsukov 	    &cfg->internal, &cfg->mask)) {
383b867e84eSAndrey V. Elsukov 		/*
384b867e84eSAndrey V. Elsukov 		 * XXX: Do not translate packets when both src and dst
385b867e84eSAndrey V. Elsukov 		 * are from internal prefix.
386b867e84eSAndrey V. Elsukov 		 */
387b867e84eSAndrey V. Elsukov 		if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst,
388b867e84eSAndrey V. Elsukov 		    &cfg->internal, &cfg->mask))
389576429f0SAndrey V. Elsukov 			return (ret);
390b867e84eSAndrey V. Elsukov 		ret = nptv6_rewrite_internal(cfg, &args->m, 0);
391b867e84eSAndrey V. Elsukov 	} else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst,
392b867e84eSAndrey V. Elsukov 	    &cfg->external, &cfg->mask))
393b867e84eSAndrey V. Elsukov 		ret = nptv6_rewrite_external(cfg, &args->m, 0);
394b867e84eSAndrey V. Elsukov 	else
395576429f0SAndrey V. Elsukov 		return (ret);
396b867e84eSAndrey V. Elsukov 	/*
397576429f0SAndrey V. Elsukov 	 * If address wasn't rewrited - free mbuf and terminate the search.
398b867e84eSAndrey V. Elsukov 	 */
399b867e84eSAndrey V. Elsukov 	if (ret != 0) {
400b867e84eSAndrey V. Elsukov 		if (args->m != NULL) {
401b867e84eSAndrey V. Elsukov 			m_freem(args->m);
402b867e84eSAndrey V. Elsukov 			args->m = NULL; /* mark mbuf as consumed */
403b867e84eSAndrey V. Elsukov 		}
404b867e84eSAndrey V. Elsukov 		NPTV6STAT_INC(cfg, dropped);
405576429f0SAndrey V. Elsukov 		*done = 1;
406576429f0SAndrey V. Elsukov 	} else {
407b867e84eSAndrey V. Elsukov 		/* Terminate the search if one_pass is set */
408b867e84eSAndrey V. Elsukov 		*done = V_fw_one_pass;
409b867e84eSAndrey V. Elsukov 		/* Update args->f_id when one_pass is off */
410576429f0SAndrey V. Elsukov 		if (*done == 0) {
411b867e84eSAndrey V. Elsukov 			ip6 = mtod(args->m, struct ip6_hdr *);
412b867e84eSAndrey V. Elsukov 			args->f_id.src_ip6 = ip6->ip6_src;
413b867e84eSAndrey V. Elsukov 			args->f_id.dst_ip6 = ip6->ip6_dst;
414b867e84eSAndrey V. Elsukov 		}
415576429f0SAndrey V. Elsukov 	}
416b867e84eSAndrey V. Elsukov 	return (ret);
417b867e84eSAndrey V. Elsukov }
418b867e84eSAndrey V. Elsukov 
419b867e84eSAndrey V. Elsukov static struct nptv6_cfg *
nptv6_alloc_config(const char * name,uint8_t set)420b867e84eSAndrey V. Elsukov nptv6_alloc_config(const char *name, uint8_t set)
421b867e84eSAndrey V. Elsukov {
422b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
423b867e84eSAndrey V. Elsukov 
424b867e84eSAndrey V. Elsukov 	cfg = malloc(sizeof(struct nptv6_cfg), M_IPFW, M_WAITOK | M_ZERO);
425b867e84eSAndrey V. Elsukov 	COUNTER_ARRAY_ALLOC(cfg->stats, NPTV6STATS, M_WAITOK);
426b867e84eSAndrey V. Elsukov 	cfg->no.name = cfg->name;
427b867e84eSAndrey V. Elsukov 	cfg->no.etlv = IPFW_TLV_NPTV6_NAME;
428b867e84eSAndrey V. Elsukov 	cfg->no.set = set;
429b867e84eSAndrey V. Elsukov 	strlcpy(cfg->name, name, sizeof(cfg->name));
430b867e84eSAndrey V. Elsukov 	return (cfg);
431b867e84eSAndrey V. Elsukov }
432b867e84eSAndrey V. Elsukov 
433b867e84eSAndrey V. Elsukov static void
nptv6_free_config(struct nptv6_cfg * cfg)434b867e84eSAndrey V. Elsukov nptv6_free_config(struct nptv6_cfg *cfg)
435b867e84eSAndrey V. Elsukov {
436b867e84eSAndrey V. Elsukov 
437b867e84eSAndrey V. Elsukov 	COUNTER_ARRAY_FREE(cfg->stats, NPTV6STATS);
438b867e84eSAndrey V. Elsukov 	free(cfg, M_IPFW);
439b867e84eSAndrey V. Elsukov }
440b867e84eSAndrey V. Elsukov 
441b867e84eSAndrey V. Elsukov static void
nptv6_export_config(struct ip_fw_chain * ch,struct nptv6_cfg * cfg,ipfw_nptv6_cfg * uc)442b867e84eSAndrey V. Elsukov nptv6_export_config(struct ip_fw_chain *ch, struct nptv6_cfg *cfg,
443b867e84eSAndrey V. Elsukov     ipfw_nptv6_cfg *uc)
444b867e84eSAndrey V. Elsukov {
445b867e84eSAndrey V. Elsukov 
446b867e84eSAndrey V. Elsukov 	uc->internal = cfg->internal;
447b2b56606SAndrey V. Elsukov 	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
448b2b56606SAndrey V. Elsukov 		memcpy(uc->if_name, cfg->if_name, IF_NAMESIZE);
449b2b56606SAndrey V. Elsukov 	else
450b867e84eSAndrey V. Elsukov 		uc->external = cfg->external;
451b867e84eSAndrey V. Elsukov 	uc->plen = cfg->plen;
452b867e84eSAndrey V. Elsukov 	uc->flags = cfg->flags & NPTV6_FLAGSMASK;
453b867e84eSAndrey V. Elsukov 	uc->set = cfg->no.set;
454b867e84eSAndrey V. Elsukov 	strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
455b867e84eSAndrey V. Elsukov }
456b867e84eSAndrey V. Elsukov 
457b867e84eSAndrey V. Elsukov struct nptv6_dump_arg {
458b867e84eSAndrey V. Elsukov 	struct ip_fw_chain *ch;
459b867e84eSAndrey V. Elsukov 	struct sockopt_data *sd;
460b867e84eSAndrey V. Elsukov };
461b867e84eSAndrey V. Elsukov 
462b867e84eSAndrey V. Elsukov static int
export_config_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)463b867e84eSAndrey V. Elsukov export_config_cb(struct namedobj_instance *ni, struct named_object *no,
464b867e84eSAndrey V. Elsukov     void *arg)
465b867e84eSAndrey V. Elsukov {
466b867e84eSAndrey V. Elsukov 	struct nptv6_dump_arg *da = (struct nptv6_dump_arg *)arg;
467b867e84eSAndrey V. Elsukov 	ipfw_nptv6_cfg *uc;
468b867e84eSAndrey V. Elsukov 
469b867e84eSAndrey V. Elsukov 	uc = (ipfw_nptv6_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
470b867e84eSAndrey V. Elsukov 	nptv6_export_config(da->ch, (struct nptv6_cfg *)no, uc);
471b867e84eSAndrey V. Elsukov 	return (0);
472b867e84eSAndrey V. Elsukov }
473b867e84eSAndrey V. Elsukov 
474b867e84eSAndrey V. Elsukov static struct nptv6_cfg *
nptv6_find(struct namedobj_instance * ni,const char * name,uint8_t set)475b867e84eSAndrey V. Elsukov nptv6_find(struct namedobj_instance *ni, const char *name, uint8_t set)
476b867e84eSAndrey V. Elsukov {
477b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
478b867e84eSAndrey V. Elsukov 
479b867e84eSAndrey V. Elsukov 	cfg = (struct nptv6_cfg *)ipfw_objhash_lookup_name_type(ni, set,
480b867e84eSAndrey V. Elsukov 	    IPFW_TLV_NPTV6_NAME, name);
481b867e84eSAndrey V. Elsukov 
482b867e84eSAndrey V. Elsukov 	return (cfg);
483b867e84eSAndrey V. Elsukov }
484b867e84eSAndrey V. Elsukov 
485b867e84eSAndrey V. Elsukov static void
nptv6_calculate_adjustment(struct nptv6_cfg * cfg)486b867e84eSAndrey V. Elsukov nptv6_calculate_adjustment(struct nptv6_cfg *cfg)
487b867e84eSAndrey V. Elsukov {
488b867e84eSAndrey V. Elsukov 	uint16_t i, e;
489b867e84eSAndrey V. Elsukov 	uint16_t *p;
490b867e84eSAndrey V. Elsukov 
491b867e84eSAndrey V. Elsukov 	/* Calculate checksum of internal prefix */
492b867e84eSAndrey V. Elsukov 	for (i = 0, p = (uint16_t *)&cfg->internal;
493b867e84eSAndrey V. Elsukov 	    p < (uint16_t *)(&cfg->internal + 1); p++)
494b867e84eSAndrey V. Elsukov 		i = cksum_add(i, *p);
495b867e84eSAndrey V. Elsukov 
496b867e84eSAndrey V. Elsukov 	/* Calculate checksum of external prefix */
497b867e84eSAndrey V. Elsukov 	for (e = 0, p = (uint16_t *)&cfg->external;
498b867e84eSAndrey V. Elsukov 	    p < (uint16_t *)(&cfg->external + 1); p++)
499b867e84eSAndrey V. Elsukov 		e = cksum_add(e, *p);
500b867e84eSAndrey V. Elsukov 
501b867e84eSAndrey V. Elsukov 	/* Adjustment value for Int->Ext direction */
502b867e84eSAndrey V. Elsukov 	cfg->adjustment = cksum_add(~e, i);
503b867e84eSAndrey V. Elsukov }
504b867e84eSAndrey V. Elsukov 
505b2b56606SAndrey V. Elsukov static int
nptv6_check_prefix(const struct in6_addr * addr)506b2b56606SAndrey V. Elsukov nptv6_check_prefix(const struct in6_addr *addr)
507b2b56606SAndrey V. Elsukov {
508b2b56606SAndrey V. Elsukov 
509b2b56606SAndrey V. Elsukov 	if (IN6_IS_ADDR_MULTICAST(addr) ||
510b2b56606SAndrey V. Elsukov 	    IN6_IS_ADDR_LINKLOCAL(addr) ||
511b2b56606SAndrey V. Elsukov 	    IN6_IS_ADDR_LOOPBACK(addr) ||
512b2b56606SAndrey V. Elsukov 	    IN6_IS_ADDR_UNSPECIFIED(addr))
513b2b56606SAndrey V. Elsukov 		return (EINVAL);
514b2b56606SAndrey V. Elsukov 	return (0);
515b2b56606SAndrey V. Elsukov }
516b2b56606SAndrey V. Elsukov 
517b2b56606SAndrey V. Elsukov static void
nptv6_set_external(struct nptv6_cfg * cfg,struct in6_addr * addr)518b2b56606SAndrey V. Elsukov nptv6_set_external(struct nptv6_cfg *cfg, struct in6_addr *addr)
519b2b56606SAndrey V. Elsukov {
520b2b56606SAndrey V. Elsukov 
521b2b56606SAndrey V. Elsukov 	cfg->external = *addr;
522b2b56606SAndrey V. Elsukov 	IN6_MASK_ADDR(&cfg->external, &cfg->mask);
523b2b56606SAndrey V. Elsukov 	nptv6_calculate_adjustment(cfg);
524b2b56606SAndrey V. Elsukov 	cfg->flags |= NPTV6_READY;
525b2b56606SAndrey V. Elsukov }
526b2b56606SAndrey V. Elsukov 
527b2b56606SAndrey V. Elsukov /*
528b2b56606SAndrey V. Elsukov  * Try to determine what prefix to use as external for
529b2b56606SAndrey V. Elsukov  * configured interface name.
530b2b56606SAndrey V. Elsukov  */
531b2b56606SAndrey V. Elsukov static void
nptv6_find_prefix(struct ip_fw_chain * ch,struct nptv6_cfg * cfg,struct ifnet * ifp)532b2b56606SAndrey V. Elsukov nptv6_find_prefix(struct ip_fw_chain *ch, struct nptv6_cfg *cfg,
533b2b56606SAndrey V. Elsukov     struct ifnet *ifp)
534b2b56606SAndrey V. Elsukov {
53516a72f53SGleb Smirnoff 	struct epoch_tracker et;
536b2b56606SAndrey V. Elsukov 	struct ifaddr *ifa;
537b2b56606SAndrey V. Elsukov 	struct in6_ifaddr *ia;
538b2b56606SAndrey V. Elsukov 
539b2b56606SAndrey V. Elsukov 	MPASS(cfg->flags & NPTV6_DYNAMIC_PREFIX);
540b2b56606SAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
541b2b56606SAndrey V. Elsukov 
542b2b56606SAndrey V. Elsukov 	if (ifp == NULL) {
543b2b56606SAndrey V. Elsukov 		ifp = ifunit_ref(cfg->if_name);
544b2b56606SAndrey V. Elsukov 		if (ifp == NULL)
545b2b56606SAndrey V. Elsukov 			return;
546b2b56606SAndrey V. Elsukov 	}
54716a72f53SGleb Smirnoff 	NET_EPOCH_ENTER(et);
548b2b56606SAndrey V. Elsukov 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
549b2b56606SAndrey V. Elsukov 		if (ifa->ifa_addr->sa_family != AF_INET6)
550b2b56606SAndrey V. Elsukov 			continue;
551b2b56606SAndrey V. Elsukov 		ia = (struct in6_ifaddr *)ifa;
552b2b56606SAndrey V. Elsukov 		if (nptv6_check_prefix(&ia->ia_addr.sin6_addr) ||
553b2b56606SAndrey V. Elsukov 		    IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
554b2b56606SAndrey V. Elsukov 		    &cfg->internal, &cfg->mask))
555b2b56606SAndrey V. Elsukov 			continue;
556b2b56606SAndrey V. Elsukov 		/* Suitable address is found. */
557b2b56606SAndrey V. Elsukov 		nptv6_set_external(cfg, &ia->ia_addr.sin6_addr);
558b2b56606SAndrey V. Elsukov 		break;
559b2b56606SAndrey V. Elsukov 	}
56016a72f53SGleb Smirnoff 	NET_EPOCH_EXIT(et);
561b2b56606SAndrey V. Elsukov 	if_rele(ifp);
562b2b56606SAndrey V. Elsukov }
563b2b56606SAndrey V. Elsukov 
564b2b56606SAndrey V. Elsukov struct ifaddr_event_args {
565b2b56606SAndrey V. Elsukov 	struct ifnet *ifp;
566b2b56606SAndrey V. Elsukov 	const struct in6_addr *addr;
567b2b56606SAndrey V. Elsukov 	int event;
568b2b56606SAndrey V. Elsukov };
569b2b56606SAndrey V. Elsukov 
570b2b56606SAndrey V. Elsukov static int
ifaddr_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)571b2b56606SAndrey V. Elsukov ifaddr_cb(struct namedobj_instance *ni, struct named_object *no,
572b2b56606SAndrey V. Elsukov     void *arg)
573b2b56606SAndrey V. Elsukov {
574b2b56606SAndrey V. Elsukov 	struct ifaddr_event_args *args;
575b2b56606SAndrey V. Elsukov 	struct ip_fw_chain *ch;
576b2b56606SAndrey V. Elsukov 	struct nptv6_cfg *cfg;
577b2b56606SAndrey V. Elsukov 
578b2b56606SAndrey V. Elsukov 	ch = &V_layer3_chain;
579b2b56606SAndrey V. Elsukov 	cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx);
580b2b56606SAndrey V. Elsukov 	if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0)
581b2b56606SAndrey V. Elsukov 		return (0);
582b2b56606SAndrey V. Elsukov 
583b2b56606SAndrey V. Elsukov 	args = arg;
584b2b56606SAndrey V. Elsukov 	/* If interface name doesn't match, ignore */
585b2b56606SAndrey V. Elsukov 	if (strncmp(args->ifp->if_xname, cfg->if_name, IF_NAMESIZE))
586b2b56606SAndrey V. Elsukov 		return (0);
587b2b56606SAndrey V. Elsukov 	if (args->ifp->if_flags & IFF_DYING) { /* XXX: is it possible? */
588b2b56606SAndrey V. Elsukov 		cfg->flags &= ~NPTV6_READY;
589b2b56606SAndrey V. Elsukov 		return (0);
590b2b56606SAndrey V. Elsukov 	}
591b2b56606SAndrey V. Elsukov 	if (args->event == IFADDR_EVENT_DEL) {
592b2b56606SAndrey V. Elsukov 		/* If instance is not ready, ignore */
593b2b56606SAndrey V. Elsukov 		if ((cfg->flags & NPTV6_READY) == 0)
594b2b56606SAndrey V. Elsukov 			return (0);
595b2b56606SAndrey V. Elsukov 		/* If address does not match the external prefix, ignore */
596b2b56606SAndrey V. Elsukov 		if (IN6_ARE_MASKED_ADDR_EQUAL(&cfg->external, args->addr,
597b2b56606SAndrey V. Elsukov 		    &cfg->mask) != 0)
598b2b56606SAndrey V. Elsukov 			return (0);
599b2b56606SAndrey V. Elsukov 		/* Otherwise clear READY flag */
600b2b56606SAndrey V. Elsukov 		cfg->flags &= ~NPTV6_READY;
601b2b56606SAndrey V. Elsukov 	} else {/* IFADDR_EVENT_ADD */
602b2b56606SAndrey V. Elsukov 		/* If instance is already ready, ignore */
603b2b56606SAndrey V. Elsukov 		if (cfg->flags & NPTV6_READY)
604b2b56606SAndrey V. Elsukov 			return (0);
605b2b56606SAndrey V. Elsukov 		/* If address is not suitable for prefix, ignore */
606b2b56606SAndrey V. Elsukov 		if (nptv6_check_prefix(args->addr) ||
607b2b56606SAndrey V. Elsukov 		    IN6_ARE_MASKED_ADDR_EQUAL(args->addr, &cfg->internal,
608b2b56606SAndrey V. Elsukov 		    &cfg->mask))
609b2b56606SAndrey V. Elsukov 			return (0);
610b2b56606SAndrey V. Elsukov 		/* FALLTHROUGH */
611b2b56606SAndrey V. Elsukov 	}
612b2b56606SAndrey V. Elsukov 	MPASS(!(cfg->flags & NPTV6_READY));
613b2b56606SAndrey V. Elsukov 	/* Try to determine the prefix */
614b2b56606SAndrey V. Elsukov 	if_ref(args->ifp);
615b2b56606SAndrey V. Elsukov 	nptv6_find_prefix(ch, cfg, args->ifp);
616b2b56606SAndrey V. Elsukov 	return (0);
617b2b56606SAndrey V. Elsukov }
618b2b56606SAndrey V. Elsukov 
619b2b56606SAndrey V. Elsukov static void
nptv6_ifaddrevent_handler(void * arg __unused,struct ifnet * ifp,struct ifaddr * ifa,int event)620b2b56606SAndrey V. Elsukov nptv6_ifaddrevent_handler(void *arg __unused, struct ifnet *ifp,
621b2b56606SAndrey V. Elsukov     struct ifaddr *ifa, int event)
622b2b56606SAndrey V. Elsukov {
623b2b56606SAndrey V. Elsukov 	struct ifaddr_event_args args;
624b2b56606SAndrey V. Elsukov 	struct ip_fw_chain *ch;
625b2b56606SAndrey V. Elsukov 
626b2b56606SAndrey V. Elsukov 	if (ifa->ifa_addr->sa_family != AF_INET6)
627b2b56606SAndrey V. Elsukov 		return;
628b2b56606SAndrey V. Elsukov 
629b2b56606SAndrey V. Elsukov 	args.ifp = ifp;
630b2b56606SAndrey V. Elsukov 	args.addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
631b2b56606SAndrey V. Elsukov 	args.event = event;
632b2b56606SAndrey V. Elsukov 
633b2b56606SAndrey V. Elsukov 	ch = &V_layer3_chain;
634b2b56606SAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
635b2b56606SAndrey V. Elsukov 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), ifaddr_cb, &args,
636b2b56606SAndrey V. Elsukov 	    IPFW_TLV_NPTV6_NAME);
637b2b56606SAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
638b2b56606SAndrey V. Elsukov }
639b2b56606SAndrey V. Elsukov 
640b867e84eSAndrey V. Elsukov /*
641b867e84eSAndrey V. Elsukov  * Creates new NPTv6 instance.
642b867e84eSAndrey V. Elsukov  * Data layout (v0)(current):
643b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
644b867e84eSAndrey V. Elsukov  *
645b867e84eSAndrey V. Elsukov  * Returns 0 on success
646b867e84eSAndrey V. Elsukov  */
647b867e84eSAndrey V. Elsukov static int
nptv6_create(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)648b867e84eSAndrey V. Elsukov nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
649b867e84eSAndrey V. Elsukov     struct sockopt_data *sd)
650b867e84eSAndrey V. Elsukov {
651b867e84eSAndrey V. Elsukov 	struct in6_addr mask;
652b867e84eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
653b867e84eSAndrey V. Elsukov 	ipfw_nptv6_cfg *uc;
654b867e84eSAndrey V. Elsukov 	struct namedobj_instance *ni;
655b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
656b867e84eSAndrey V. Elsukov 
657b867e84eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*olh) + sizeof(*uc))
658b867e84eSAndrey V. Elsukov 		return (EINVAL);
659b867e84eSAndrey V. Elsukov 
660b867e84eSAndrey V. Elsukov 	olh = (ipfw_obj_lheader *)sd->kbuf;
661b867e84eSAndrey V. Elsukov 	uc = (ipfw_nptv6_cfg *)(olh + 1);
662b867e84eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(uc->name) != 0)
663b867e84eSAndrey V. Elsukov 		return (EINVAL);
664b867e84eSAndrey V. Elsukov 	if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS)
665b867e84eSAndrey V. Elsukov 		return (EINVAL);
666b2b56606SAndrey V. Elsukov 	if (nptv6_check_prefix(&uc->internal))
667b867e84eSAndrey V. Elsukov 		return (EINVAL);
668b867e84eSAndrey V. Elsukov 	in6_prefixlen2mask(&mask, uc->plen);
669b2b56606SAndrey V. Elsukov 	if ((uc->flags & NPTV6_DYNAMIC_PREFIX) == 0 && (
670b2b56606SAndrey V. Elsukov 	    nptv6_check_prefix(&uc->external) ||
671b2b56606SAndrey V. Elsukov 	    IN6_ARE_MASKED_ADDR_EQUAL(&uc->external, &uc->internal, &mask)))
672b867e84eSAndrey V. Elsukov 		return (EINVAL);
673b867e84eSAndrey V. Elsukov 
674b867e84eSAndrey V. Elsukov 	ni = CHAIN_TO_SRV(ch);
675b867e84eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
676b867e84eSAndrey V. Elsukov 	if (nptv6_find(ni, uc->name, uc->set) != NULL) {
677b867e84eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
678b867e84eSAndrey V. Elsukov 		return (EEXIST);
679b867e84eSAndrey V. Elsukov 	}
680b867e84eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
681b867e84eSAndrey V. Elsukov 
682b867e84eSAndrey V. Elsukov 	cfg = nptv6_alloc_config(uc->name, uc->set);
683b867e84eSAndrey V. Elsukov 	cfg->plen = uc->plen;
684b2b56606SAndrey V. Elsukov 	cfg->flags = uc->flags & NPTV6_FLAGSMASK;
685b867e84eSAndrey V. Elsukov 	if (cfg->plen <= 48)
686b867e84eSAndrey V. Elsukov 		cfg->flags |= NPTV6_48PLEN;
687b867e84eSAndrey V. Elsukov 	cfg->mask = mask;
688b2b56606SAndrey V. Elsukov 	cfg->internal = uc->internal;
689b867e84eSAndrey V. Elsukov 	IN6_MASK_ADDR(&cfg->internal, &mask);
690b2b56606SAndrey V. Elsukov 	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
691b2b56606SAndrey V. Elsukov 		memcpy(cfg->if_name, uc->if_name, IF_NAMESIZE);
692b2b56606SAndrey V. Elsukov 	else
693b2b56606SAndrey V. Elsukov 		nptv6_set_external(cfg, &uc->external);
694b2b56606SAndrey V. Elsukov 
695b2b56606SAndrey V. Elsukov 	if ((uc->flags & NPTV6_DYNAMIC_PREFIX) != 0 &&
696b2b56606SAndrey V. Elsukov 	    nptv6_ifaddr_event == NULL)
697b2b56606SAndrey V. Elsukov 		nptv6_ifaddr_event = EVENTHANDLER_REGISTER(
698b2b56606SAndrey V. Elsukov 		    ifaddr_event_ext, nptv6_ifaddrevent_handler, NULL,
699b2b56606SAndrey V. Elsukov 		    EVENTHANDLER_PRI_ANY);
700b867e84eSAndrey V. Elsukov 
701b867e84eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
702b867e84eSAndrey V. Elsukov 	if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) {
703b867e84eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
704b867e84eSAndrey V. Elsukov 		nptv6_free_config(cfg);
705b867e84eSAndrey V. Elsukov 		return (ENOSPC);
706b867e84eSAndrey V. Elsukov 	}
707b867e84eSAndrey V. Elsukov 	ipfw_objhash_add(ni, &cfg->no);
708b867e84eSAndrey V. Elsukov 	SRV_OBJECT(ch, cfg->no.kidx) = cfg;
709b2b56606SAndrey V. Elsukov 	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
710b2b56606SAndrey V. Elsukov 		nptv6_find_prefix(ch, cfg, NULL);
711b867e84eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
712b2b56606SAndrey V. Elsukov 
713b867e84eSAndrey V. Elsukov 	return (0);
714b867e84eSAndrey V. Elsukov }
715b867e84eSAndrey V. Elsukov 
716b867e84eSAndrey V. Elsukov /*
717b867e84eSAndrey V. Elsukov  * Destroys NPTv6 instance.
718b867e84eSAndrey V. Elsukov  * Data layout (v0)(current):
719b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
720b867e84eSAndrey V. Elsukov  *
721b867e84eSAndrey V. Elsukov  * Returns 0 on success
722b867e84eSAndrey V. Elsukov  */
723b867e84eSAndrey V. Elsukov static int
nptv6_destroy(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)724b867e84eSAndrey V. Elsukov nptv6_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
725b867e84eSAndrey V. Elsukov     struct sockopt_data *sd)
726b867e84eSAndrey V. Elsukov {
727b867e84eSAndrey V. Elsukov 	ipfw_obj_header *oh;
728b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
729b867e84eSAndrey V. Elsukov 
730b867e84eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*oh))
731b867e84eSAndrey V. Elsukov 		return (EINVAL);
732b867e84eSAndrey V. Elsukov 
733b867e84eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)sd->kbuf;
734b867e84eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
735b867e84eSAndrey V. Elsukov 		return (EINVAL);
736b867e84eSAndrey V. Elsukov 
737b867e84eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
738b867e84eSAndrey V. Elsukov 	cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
739b867e84eSAndrey V. Elsukov 	if (cfg == NULL) {
740b867e84eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
741b867e84eSAndrey V. Elsukov 		return (ESRCH);
742b867e84eSAndrey V. Elsukov 	}
743b867e84eSAndrey V. Elsukov 	if (cfg->no.refcnt > 0) {
744b867e84eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
745b867e84eSAndrey V. Elsukov 		return (EBUSY);
746b867e84eSAndrey V. Elsukov 	}
747b867e84eSAndrey V. Elsukov 
748cefe3d67SAndrey V. Elsukov 	ipfw_reset_eaction_instance(ch, V_nptv6_eid, cfg->no.kidx);
749b867e84eSAndrey V. Elsukov 	SRV_OBJECT(ch, cfg->no.kidx) = NULL;
750b867e84eSAndrey V. Elsukov 	ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
751b867e84eSAndrey V. Elsukov 	ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
752b867e84eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
753b867e84eSAndrey V. Elsukov 
754b867e84eSAndrey V. Elsukov 	nptv6_free_config(cfg);
755b867e84eSAndrey V. Elsukov 	return (0);
756b867e84eSAndrey V. Elsukov }
757b867e84eSAndrey V. Elsukov 
758b867e84eSAndrey V. Elsukov /*
759b867e84eSAndrey V. Elsukov  * Get or change nptv6 instance config.
760b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_header [ ipfw_nptv6_cfg ] ]
761b867e84eSAndrey V. Elsukov  */
762b867e84eSAndrey V. Elsukov static int
nptv6_config(struct ip_fw_chain * chain,ip_fw3_opheader * op,struct sockopt_data * sd)763b867e84eSAndrey V. Elsukov nptv6_config(struct ip_fw_chain *chain, ip_fw3_opheader *op,
764b867e84eSAndrey V. Elsukov     struct sockopt_data *sd)
765b867e84eSAndrey V. Elsukov {
766b867e84eSAndrey V. Elsukov 
767b867e84eSAndrey V. Elsukov 	return (EOPNOTSUPP);
768b867e84eSAndrey V. Elsukov }
769b867e84eSAndrey V. Elsukov 
770b867e84eSAndrey V. Elsukov /*
771b867e84eSAndrey V. Elsukov  * Lists all NPTv6 instances currently available in kernel.
772b867e84eSAndrey V. Elsukov  * Data layout (v0)(current):
773b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ]
774b867e84eSAndrey V. Elsukov  * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ]
775b867e84eSAndrey V. Elsukov  *
776b867e84eSAndrey V. Elsukov  * Returns 0 on success
777b867e84eSAndrey V. Elsukov  */
778b867e84eSAndrey V. Elsukov static int
nptv6_list(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)779b867e84eSAndrey V. Elsukov nptv6_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
780b867e84eSAndrey V. Elsukov     struct sockopt_data *sd)
781b867e84eSAndrey V. Elsukov {
782b867e84eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
783b867e84eSAndrey V. Elsukov 	struct nptv6_dump_arg da;
784b867e84eSAndrey V. Elsukov 
785b867e84eSAndrey V. Elsukov 	/* Check minimum header size */
786b867e84eSAndrey V. Elsukov 	if (sd->valsize < sizeof(ipfw_obj_lheader))
787b867e84eSAndrey V. Elsukov 		return (EINVAL);
788b867e84eSAndrey V. Elsukov 
789b867e84eSAndrey V. Elsukov 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
790b867e84eSAndrey V. Elsukov 
791b867e84eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
792b867e84eSAndrey V. Elsukov 	olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
793b867e84eSAndrey V. Elsukov 	    IPFW_TLV_NPTV6_NAME);
794b867e84eSAndrey V. Elsukov 	olh->objsize = sizeof(ipfw_nptv6_cfg);
795b867e84eSAndrey V. Elsukov 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
796b867e84eSAndrey V. Elsukov 
797b867e84eSAndrey V. Elsukov 	if (sd->valsize < olh->size) {
798b867e84eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
799b867e84eSAndrey V. Elsukov 		return (ENOMEM);
800b867e84eSAndrey V. Elsukov 	}
801b867e84eSAndrey V. Elsukov 	memset(&da, 0, sizeof(da));
802b867e84eSAndrey V. Elsukov 	da.ch = ch;
803b867e84eSAndrey V. Elsukov 	da.sd = sd;
804b867e84eSAndrey V. Elsukov 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
805b867e84eSAndrey V. Elsukov 	    &da, IPFW_TLV_NPTV6_NAME);
806b867e84eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
807b867e84eSAndrey V. Elsukov 
808b867e84eSAndrey V. Elsukov 	return (0);
809b867e84eSAndrey V. Elsukov }
810b867e84eSAndrey V. Elsukov 
811b867e84eSAndrey V. Elsukov #define	__COPY_STAT_FIELD(_cfg, _stats, _field)	\
812b867e84eSAndrey V. Elsukov 	(_stats)->_field = NPTV6STAT_FETCH(_cfg, _field)
813b867e84eSAndrey V. Elsukov static void
export_stats(struct ip_fw_chain * ch,struct nptv6_cfg * cfg,struct ipfw_nptv6_stats * stats)814b867e84eSAndrey V. Elsukov export_stats(struct ip_fw_chain *ch, struct nptv6_cfg *cfg,
815b867e84eSAndrey V. Elsukov     struct ipfw_nptv6_stats *stats)
816b867e84eSAndrey V. Elsukov {
817b867e84eSAndrey V. Elsukov 
818b867e84eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, in2ex);
819b867e84eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, ex2in);
820b867e84eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, dropped);
821b867e84eSAndrey V. Elsukov }
822b867e84eSAndrey V. Elsukov 
823b867e84eSAndrey V. Elsukov /*
824b867e84eSAndrey V. Elsukov  * Get NPTv6 statistics.
825b867e84eSAndrey V. Elsukov  * Data layout (v0)(current):
826b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
827b867e84eSAndrey V. Elsukov  * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
828b867e84eSAndrey V. Elsukov  *
829b867e84eSAndrey V. Elsukov  * Returns 0 on success
830b867e84eSAndrey V. Elsukov  */
831b867e84eSAndrey V. Elsukov static int
nptv6_stats(struct ip_fw_chain * ch,ip_fw3_opheader * op,struct sockopt_data * sd)832b867e84eSAndrey V. Elsukov nptv6_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
833b867e84eSAndrey V. Elsukov     struct sockopt_data *sd)
834b867e84eSAndrey V. Elsukov {
835b867e84eSAndrey V. Elsukov 	struct ipfw_nptv6_stats stats;
836b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
837b867e84eSAndrey V. Elsukov 	ipfw_obj_header *oh;
838b867e84eSAndrey V. Elsukov 	ipfw_obj_ctlv *ctlv;
839b867e84eSAndrey V. Elsukov 	size_t sz;
840b867e84eSAndrey V. Elsukov 
841b867e84eSAndrey V. Elsukov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
842b867e84eSAndrey V. Elsukov 	if (sd->valsize % sizeof(uint64_t))
843b867e84eSAndrey V. Elsukov 		return (EINVAL);
844b867e84eSAndrey V. Elsukov 	if (sd->valsize < sz)
845b867e84eSAndrey V. Elsukov 		return (ENOMEM);
846b867e84eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
847b867e84eSAndrey V. Elsukov 	if (oh == NULL)
848b867e84eSAndrey V. Elsukov 		return (EINVAL);
84957fb3b7aSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
85057fb3b7aSAndrey V. Elsukov 	    oh->ntlv.set >= IPFW_MAX_SETS)
85157fb3b7aSAndrey V. Elsukov 		return (EINVAL);
852b867e84eSAndrey V. Elsukov 	memset(&stats, 0, sizeof(stats));
853b867e84eSAndrey V. Elsukov 
854b867e84eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
855b867e84eSAndrey V. Elsukov 	cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
856b867e84eSAndrey V. Elsukov 	if (cfg == NULL) {
857b867e84eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
858b867e84eSAndrey V. Elsukov 		return (ESRCH);
859b867e84eSAndrey V. Elsukov 	}
860b867e84eSAndrey V. Elsukov 	export_stats(ch, cfg, &stats);
861b867e84eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
862b867e84eSAndrey V. Elsukov 
863b867e84eSAndrey V. Elsukov 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
864b867e84eSAndrey V. Elsukov 	memset(ctlv, 0, sizeof(*ctlv));
865b867e84eSAndrey V. Elsukov 	ctlv->head.type = IPFW_TLV_COUNTERS;
866b867e84eSAndrey V. Elsukov 	ctlv->head.length = sz - sizeof(ipfw_obj_header);
867b867e84eSAndrey V. Elsukov 	ctlv->count = sizeof(stats) / sizeof(uint64_t);
868b867e84eSAndrey V. Elsukov 	ctlv->objsize = sizeof(uint64_t);
869b867e84eSAndrey V. Elsukov 	ctlv->version = 1;
870b867e84eSAndrey V. Elsukov 	memcpy(ctlv + 1, &stats, sizeof(stats));
871b867e84eSAndrey V. Elsukov 	return (0);
872b867e84eSAndrey V. Elsukov }
873b867e84eSAndrey V. Elsukov 
87457fb3b7aSAndrey V. Elsukov /*
87557fb3b7aSAndrey V. Elsukov  * Reset NPTv6 statistics.
87657fb3b7aSAndrey V. Elsukov  * Data layout (v0)(current):
87757fb3b7aSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
87857fb3b7aSAndrey V. Elsukov  *
87957fb3b7aSAndrey V. Elsukov  * Returns 0 on success
88057fb3b7aSAndrey V. Elsukov  */
88157fb3b7aSAndrey V. Elsukov static int
nptv6_reset_stats(struct ip_fw_chain * ch,ip_fw3_opheader * op,struct sockopt_data * sd)88257fb3b7aSAndrey V. Elsukov nptv6_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
88357fb3b7aSAndrey V. Elsukov     struct sockopt_data *sd)
88457fb3b7aSAndrey V. Elsukov {
88557fb3b7aSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
88657fb3b7aSAndrey V. Elsukov 	ipfw_obj_header *oh;
88757fb3b7aSAndrey V. Elsukov 
88857fb3b7aSAndrey V. Elsukov 	if (sd->valsize != sizeof(*oh))
88957fb3b7aSAndrey V. Elsukov 		return (EINVAL);
89057fb3b7aSAndrey V. Elsukov 	oh = (ipfw_obj_header *)sd->kbuf;
89157fb3b7aSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
89257fb3b7aSAndrey V. Elsukov 	    oh->ntlv.set >= IPFW_MAX_SETS)
89357fb3b7aSAndrey V. Elsukov 		return (EINVAL);
89457fb3b7aSAndrey V. Elsukov 
89557fb3b7aSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
89657fb3b7aSAndrey V. Elsukov 	cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
89757fb3b7aSAndrey V. Elsukov 	if (cfg == NULL) {
89857fb3b7aSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
89957fb3b7aSAndrey V. Elsukov 		return (ESRCH);
90057fb3b7aSAndrey V. Elsukov 	}
90157fb3b7aSAndrey V. Elsukov 	COUNTER_ARRAY_ZERO(cfg->stats, NPTV6STATS);
90257fb3b7aSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
90357fb3b7aSAndrey V. Elsukov 	return (0);
90457fb3b7aSAndrey V. Elsukov }
90557fb3b7aSAndrey V. Elsukov 
906b867e84eSAndrey V. Elsukov static struct ipfw_sopt_handler	scodes[] = {
907b867e84eSAndrey V. Elsukov 	{ IP_FW_NPTV6_CREATE, 0,	HDIR_SET,	nptv6_create },
908b867e84eSAndrey V. Elsukov 	{ IP_FW_NPTV6_DESTROY,0,	HDIR_SET,	nptv6_destroy },
909b867e84eSAndrey V. Elsukov 	{ IP_FW_NPTV6_CONFIG, 0,	HDIR_BOTH,	nptv6_config },
910b867e84eSAndrey V. Elsukov 	{ IP_FW_NPTV6_LIST,   0,	HDIR_GET,	nptv6_list },
911b867e84eSAndrey V. Elsukov 	{ IP_FW_NPTV6_STATS,  0,	HDIR_GET,	nptv6_stats },
91257fb3b7aSAndrey V. Elsukov 	{ IP_FW_NPTV6_RESET_STATS,0,	HDIR_SET,	nptv6_reset_stats },
913b867e84eSAndrey V. Elsukov };
914b867e84eSAndrey V. Elsukov 
915b867e84eSAndrey V. Elsukov static int
nptv6_classify(ipfw_insn * cmd,uint16_t * puidx,uint8_t * ptype)916b867e84eSAndrey V. Elsukov nptv6_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
917b867e84eSAndrey V. Elsukov {
918b867e84eSAndrey V. Elsukov 	ipfw_insn *icmd;
919b867e84eSAndrey V. Elsukov 
920b867e84eSAndrey V. Elsukov 	icmd = cmd - 1;
921b867e84eSAndrey V. Elsukov 	NPTV6_DEBUG("opcode %d, arg1 %d, opcode0 %d, arg1 %d",
922b867e84eSAndrey V. Elsukov 	    cmd->opcode, cmd->arg1, icmd->opcode, icmd->arg1);
923b867e84eSAndrey V. Elsukov 	if (icmd->opcode != O_EXTERNAL_ACTION ||
924b867e84eSAndrey V. Elsukov 	    icmd->arg1 != V_nptv6_eid)
925b867e84eSAndrey V. Elsukov 		return (1);
926b867e84eSAndrey V. Elsukov 
927b867e84eSAndrey V. Elsukov 	*puidx = cmd->arg1;
928b867e84eSAndrey V. Elsukov 	*ptype = 0;
929b867e84eSAndrey V. Elsukov 	return (0);
930b867e84eSAndrey V. Elsukov }
931b867e84eSAndrey V. Elsukov 
932b867e84eSAndrey V. Elsukov static void
nptv6_update_arg1(ipfw_insn * cmd,uint16_t idx)933b867e84eSAndrey V. Elsukov nptv6_update_arg1(ipfw_insn *cmd, uint16_t idx)
934b867e84eSAndrey V. Elsukov {
935b867e84eSAndrey V. Elsukov 
936b867e84eSAndrey V. Elsukov 	cmd->arg1 = idx;
937b867e84eSAndrey V. Elsukov 	NPTV6_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1);
938b867e84eSAndrey V. Elsukov }
939b867e84eSAndrey V. Elsukov 
940b867e84eSAndrey V. Elsukov static int
nptv6_findbyname(struct ip_fw_chain * ch,struct tid_info * ti,struct named_object ** pno)941b867e84eSAndrey V. Elsukov nptv6_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
942b867e84eSAndrey V. Elsukov     struct named_object **pno)
943b867e84eSAndrey V. Elsukov {
944b867e84eSAndrey V. Elsukov 	int err;
945b867e84eSAndrey V. Elsukov 
946b867e84eSAndrey V. Elsukov 	err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
947b867e84eSAndrey V. Elsukov 	    IPFW_TLV_NPTV6_NAME, pno);
948b867e84eSAndrey V. Elsukov 	NPTV6_DEBUG("uidx %u, type %u, err %d", ti->uidx, ti->type, err);
949b867e84eSAndrey V. Elsukov 	return (err);
950b867e84eSAndrey V. Elsukov }
951b867e84eSAndrey V. Elsukov 
952b867e84eSAndrey V. Elsukov static struct named_object *
nptv6_findbykidx(struct ip_fw_chain * ch,uint16_t idx)953b867e84eSAndrey V. Elsukov nptv6_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
954b867e84eSAndrey V. Elsukov {
955b867e84eSAndrey V. Elsukov 	struct namedobj_instance *ni;
956b867e84eSAndrey V. Elsukov 	struct named_object *no;
957b867e84eSAndrey V. Elsukov 
958b867e84eSAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
959b867e84eSAndrey V. Elsukov 	ni = CHAIN_TO_SRV(ch);
960b867e84eSAndrey V. Elsukov 	no = ipfw_objhash_lookup_kidx(ni, idx);
961b867e84eSAndrey V. Elsukov 	KASSERT(no != NULL, ("NPT with index %d not found", idx));
962b867e84eSAndrey V. Elsukov 
963b867e84eSAndrey V. Elsukov 	NPTV6_DEBUG("kidx %u -> %s", idx, no->name);
964b867e84eSAndrey V. Elsukov 	return (no);
965b867e84eSAndrey V. Elsukov }
966b867e84eSAndrey V. Elsukov 
967b867e84eSAndrey V. Elsukov static int
nptv6_manage_sets(struct ip_fw_chain * ch,uint16_t set,uint8_t new_set,enum ipfw_sets_cmd cmd)968b867e84eSAndrey V. Elsukov nptv6_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
969b867e84eSAndrey V. Elsukov     enum ipfw_sets_cmd cmd)
970b867e84eSAndrey V. Elsukov {
971b867e84eSAndrey V. Elsukov 
972b867e84eSAndrey V. Elsukov 	return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NPTV6_NAME,
973b867e84eSAndrey V. Elsukov 	    set, new_set, cmd));
974b867e84eSAndrey V. Elsukov }
975b867e84eSAndrey V. Elsukov 
976b867e84eSAndrey V. Elsukov static struct opcode_obj_rewrite opcodes[] = {
977b867e84eSAndrey V. Elsukov 	{
978b867e84eSAndrey V. Elsukov 		.opcode	= O_EXTERNAL_INSTANCE,
979b867e84eSAndrey V. Elsukov 		.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
980b867e84eSAndrey V. Elsukov 		.classifier = nptv6_classify,
981b867e84eSAndrey V. Elsukov 		.update = nptv6_update_arg1,
982b867e84eSAndrey V. Elsukov 		.find_byname = nptv6_findbyname,
983b867e84eSAndrey V. Elsukov 		.find_bykidx = nptv6_findbykidx,
984b867e84eSAndrey V. Elsukov 		.manage_sets = nptv6_manage_sets,
985b867e84eSAndrey V. Elsukov 	},
986b867e84eSAndrey V. Elsukov };
987b867e84eSAndrey V. Elsukov 
988b867e84eSAndrey V. Elsukov static int
destroy_config_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)989b867e84eSAndrey V. Elsukov destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
990b867e84eSAndrey V. Elsukov     void *arg)
991b867e84eSAndrey V. Elsukov {
992b867e84eSAndrey V. Elsukov 	struct nptv6_cfg *cfg;
993b867e84eSAndrey V. Elsukov 	struct ip_fw_chain *ch;
994b867e84eSAndrey V. Elsukov 
995b867e84eSAndrey V. Elsukov 	ch = (struct ip_fw_chain *)arg;
996b867e84eSAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
997b867e84eSAndrey V. Elsukov 
998b867e84eSAndrey V. Elsukov 	cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx);
999b867e84eSAndrey V. Elsukov 	SRV_OBJECT(ch, no->kidx) = NULL;
1000b867e84eSAndrey V. Elsukov 	ipfw_objhash_del(ni, &cfg->no);
1001b867e84eSAndrey V. Elsukov 	ipfw_objhash_free_idx(ni, cfg->no.kidx);
1002b867e84eSAndrey V. Elsukov 	nptv6_free_config(cfg);
1003b867e84eSAndrey V. Elsukov 	return (0);
1004b867e84eSAndrey V. Elsukov }
1005b867e84eSAndrey V. Elsukov 
1006b867e84eSAndrey V. Elsukov int
nptv6_init(struct ip_fw_chain * ch,int first)1007b867e84eSAndrey V. Elsukov nptv6_init(struct ip_fw_chain *ch, int first)
1008b867e84eSAndrey V. Elsukov {
1009b867e84eSAndrey V. Elsukov 
1010b867e84eSAndrey V. Elsukov 	V_nptv6_eid = ipfw_add_eaction(ch, ipfw_nptv6, "nptv6");
1011b867e84eSAndrey V. Elsukov 	if (V_nptv6_eid == 0)
1012b867e84eSAndrey V. Elsukov 		return (ENXIO);
1013b867e84eSAndrey V. Elsukov 	IPFW_ADD_SOPT_HANDLER(first, scodes);
1014b867e84eSAndrey V. Elsukov 	IPFW_ADD_OBJ_REWRITER(first, opcodes);
1015b867e84eSAndrey V. Elsukov 	return (0);
1016b867e84eSAndrey V. Elsukov }
1017b867e84eSAndrey V. Elsukov 
1018b867e84eSAndrey V. Elsukov void
nptv6_uninit(struct ip_fw_chain * ch,int last)1019b867e84eSAndrey V. Elsukov nptv6_uninit(struct ip_fw_chain *ch, int last)
1020b867e84eSAndrey V. Elsukov {
1021b867e84eSAndrey V. Elsukov 
1022b2b56606SAndrey V. Elsukov 	if (last && nptv6_ifaddr_event != NULL)
1023b2b56606SAndrey V. Elsukov 		EVENTHANDLER_DEREGISTER(ifaddr_event_ext, nptv6_ifaddr_event);
1024b867e84eSAndrey V. Elsukov 	IPFW_DEL_OBJ_REWRITER(last, opcodes);
1025b867e84eSAndrey V. Elsukov 	IPFW_DEL_SOPT_HANDLER(last, scodes);
1026b867e84eSAndrey V. Elsukov 	ipfw_del_eaction(ch, V_nptv6_eid);
1027b867e84eSAndrey V. Elsukov 	/*
1028b867e84eSAndrey V. Elsukov 	 * Since we already have deregistered external action,
1029b867e84eSAndrey V. Elsukov 	 * our named objects become unaccessible via rules, because
1030b867e84eSAndrey V. Elsukov 	 * all rules were truncated by ipfw_del_eaction().
1031b867e84eSAndrey V. Elsukov 	 * So, we can unlink and destroy our named objects without holding
1032b867e84eSAndrey V. Elsukov 	 * IPFW_WLOCK().
1033b867e84eSAndrey V. Elsukov 	 */
1034b867e84eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
1035b867e84eSAndrey V. Elsukov 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
1036b867e84eSAndrey V. Elsukov 	    IPFW_TLV_NPTV6_NAME);
1037b867e84eSAndrey V. Elsukov 	V_nptv6_eid = 0;
1038b867e84eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
1039b867e84eSAndrey V. Elsukov }
1040