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