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>
47*3d0d5b21SJustin 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