diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/NOTES | 3 | ||||
-rw-r--r-- | sys/conf/files | 4 | ||||
-rw-r--r-- | sys/conf/options | 1 | ||||
-rw-r--r-- | sys/modules/Makefile | 5 | ||||
-rw-r--r-- | sys/modules/ipfw_nptv6/Makefile | 8 | ||||
-rw-r--r-- | sys/netinet/ip_fw.h | 8 | ||||
-rw-r--r-- | sys/netinet6/ip_fw_nptv6.h | 51 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw2.c | 7 | ||||
-rw-r--r-- | sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c | 99 | ||||
-rw-r--r-- | sys/netpfil/ipfw/nptv6/nptv6.c | 894 | ||||
-rw-r--r-- | sys/netpfil/ipfw/nptv6/nptv6.h | 65 |
11 files changed, 1145 insertions, 0 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 6c13640..c7d8822 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -953,6 +953,8 @@ device lagg # IPFIREWALL_NAT adds support for in kernel nat in ipfw, and it requires # LIBALIAS. # +# IPFIREWALL_NPTV6 adds support for in kernel NPTv6 in ipfw. +# # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the TTL). This can be useful to hide firewalls # from traceroute and similar tools. @@ -974,6 +976,7 @@ options IPFIREWALL_VERBOSE #enable logging to syslogd(8) options IPFIREWALL_VERBOSE_LIMIT=100 #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPFIREWALL_NAT #ipfw kernel nat support +options IPFIREWALL_NPTV6 #ipfw kernel IPv6 NPT support options IPDIVERT #divert sockets options IPFILTER #ipfilter support options IPFILTER_LOG #ipfilter logging diff --git a/sys/conf/files b/sys/conf/files index 57a2ead..afd2820 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3942,6 +3942,10 @@ netpfil/ipfw/ip_fw_table_algo.c optional inet ipfirewall netpfil/ipfw/ip_fw_table_value.c optional inet ipfirewall netpfil/ipfw/ip_fw_iface.c optional inet ipfirewall netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat +netpfil/ipfw/nptv6/ip_fw_nptv6.c optional inet inet6 ipfirewall \ + ipfirewall_nptv6 +netpfil/ipfw/nptv6/nptv6.c optional inet inet6 ipfirewall \ + ipfirewall_nptv6 netpfil/pf/if_pflog.c optional pflog pf inet netpfil/pf/if_pfsync.c optional pfsync pf inet netpfil/pf/pf.c optional pf inet diff --git a/sys/conf/options b/sys/conf/options index 5dd66e0..9341d58 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -419,6 +419,7 @@ IPFILTER_LOOKUP opt_ipfilter.h IPFIREWALL opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPFIREWALL_NAT opt_ipfw.h +IPFIREWALL_NPTV6 opt_ipfw.h IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPSEC opt_ipsec.h diff --git a/sys/modules/Makefile b/sys/modules/Makefile index a760663..78c9880 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -173,6 +173,7 @@ SUBDIR= \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ + ${_ipfw_nptv6} \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ @@ -474,6 +475,10 @@ _ipdivert= ipdivert _ipfw= ipfw .endif +.if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) +_ipfw_nptv6= ipfw_nptv6 +.endif + .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif diff --git a/sys/modules/ipfw_nptv6/Makefile b/sys/modules/ipfw_nptv6/Makefile new file mode 100644 index 0000000..a3cf004 --- /dev/null +++ b/sys/modules/ipfw_nptv6/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../netpfil/ipfw/nptv6 + +KMOD= ipfw_nptv6 +SRCS= ip_fw_nptv6.c nptv6.c opt_ipfw.h + +.include <bsd.kmod.mk> diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 9ee298b..aed2020 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -110,6 +110,13 @@ typedef struct _ip_fw3_opheader { #define IP_FW_DUMP_SOPTCODES 116 /* Dump available sopts/versions */ #define IP_FW_DUMP_SRVOBJECTS 117 /* Dump existing named objects */ +#define IP_FW_NPTV6_CREATE 150 /* Create NPTv6 instance */ +#define IP_FW_NPTV6_DESTROY 151 /* Destroy NPTv6 instance */ +#define IP_FW_NPTV6_CONFIG 152 /* Modify NPTv6 instance */ +#define IP_FW_NPTV6_LIST 153 /* List NPTv6 instances */ +#define IP_FW_NPTV6_STATS 154 /* Get NPTv6 instance statistics */ +#define IP_FW_NPTV6_RESET_STATS 155 /* Reset NPTv6 instance statistics */ + /* * The kernel representation of ipfw rules is made of a list of * 'instructions' (for all practical purposes equivalent to BPF @@ -785,6 +792,7 @@ typedef struct _ipfw_obj_tlv { #define IPFW_TLV_TBLENT_LIST 8 #define IPFW_TLV_RANGE 9 #define IPFW_TLV_EACTION 10 +#define IPFW_TLV_COUNTERS 11 #define IPFW_TLV_STATE_NAME 14 #define IPFW_TLV_EACTION_BASE 1000 diff --git a/sys/netinet6/ip_fw_nptv6.h b/sys/netinet6/ip_fw_nptv6.h new file mode 100644 index 0000000..e2357ef --- /dev/null +++ b/sys/netinet6/ip_fw_nptv6.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2016 Yandex LLC + * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET6_IP_FW_NPTV6_H_ +#define _NETINET6_IP_FW_NPTV6_H_ + +struct ipfw_nptv6_stats { + uint64_t in2ex; /* Int->Ext packets translated */ + uint64_t ex2in; /* Ext->Int packets translated */ + uint64_t dropped; /* dropped due to some errors */ + uint64_t reserved[5]; +}; + +typedef struct _ipfw_nptv6_cfg { + char name[64]; /* NPTv6 instance name */ + struct in6_addr internal; /* NPTv6 internal prefix */ + struct in6_addr external; /* NPTv6 external prefix */ + uint8_t plen; /* Prefix length */ + uint8_t set; /* Named instance set [0..31] */ + uint8_t spare[2]; + uint32_t flags; +} ipfw_nptv6_cfg; + +#endif /* _NETINET6_IP_FW_NPTV6_H_ */ + diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index aa30702..02c0c94 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2611,6 +2611,13 @@ do { \ l = 0; /* in any case exit inner loop */ retval = ipfw_run_eaction(chain, args, cmd, &done); + /* + * If both @retval and @done are zero, + * consider this as rule matching and + * update counters. + */ + if (retval == 0 && done == 0) + IPFW_INC_RULE_COUNTER(f, pktlen); break; default: diff --git a/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c b/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c new file mode 100644 index 0000000..ec63ad0 --- /dev/null +++ b/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2016 Yandex LLC + * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/rwlock.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/vnet.h> + +#include <netinet/in.h> +#include <netinet/ip_var.h> +#include <netinet/ip_fw.h> + +#include <netpfil/ipfw/ip_fw_private.h> +#include <netpfil/ipfw/nptv6/nptv6.h> + +static int +vnet_ipfw_nptv6_init(const void *arg __unused) +{ + + return (nptv6_init(&V_layer3_chain, IS_DEFAULT_VNET(curvnet))); +} + +static int +vnet_ipfw_nptv6_uninit(const void *arg __unused) +{ + + nptv6_uninit(&V_layer3_chain, IS_DEFAULT_VNET(curvnet)); + return (0); +} + +static int +ipfw_nptv6_modevent(module_t mod, int type, void *unused) +{ + + switch (type) { + case MOD_LOAD: + case MOD_UNLOAD: + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static moduledata_t ipfw_nptv6_mod = { + "ipfw_nptv6", + ipfw_nptv6_modevent, + 0 +}; + +/* Define startup order. */ +#define IPFW_NPTV6_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN +#define IPFW_NPTV6_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ +#define IPFW_NPTV6_MODULE_ORDER (IPFW_NPTV6_MODEVENT_ORDER + 1) +#define IPFW_NPTV6_VNET_ORDER (IPFW_NPTV6_MODEVENT_ORDER + 2) + +DECLARE_MODULE(ipfw_nptv6, ipfw_nptv6_mod, IPFW_NPTV6_SI_SUB_FIREWALL, + IPFW_NPTV6_MODULE_ORDER); +MODULE_DEPEND(ipfw_nptv6, ipfw, 3, 3, 3); +MODULE_VERSION(ipfw_nptv6, 1); + +VNET_SYSINIT(vnet_ipfw_nptv6_init, IPFW_NPTV6_SI_SUB_FIREWALL, + IPFW_NPTV6_VNET_ORDER, vnet_ipfw_nptv6_init, NULL); +VNET_SYSUNINIT(vnet_ipfw_nptv6_uninit, IPFW_NPTV6_SI_SUB_FIREWALL, + IPFW_NPTV6_VNET_ORDER, vnet_ipfw_nptv6_uninit, NULL); diff --git a/sys/netpfil/ipfw/nptv6/nptv6.c b/sys/netpfil/ipfw/nptv6/nptv6.c new file mode 100644 index 0000000..da86975 --- /dev/null +++ b/sys/netpfil/ipfw/nptv6/nptv6.c @@ -0,0 +1,894 @@ +/*- + * Copyright (c) 2016 Yandex LLC + * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/counter.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/rmlock.h> +#include <sys/rwlock.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/syslog.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/netisr.h> +#include <net/pfil.h> +#include <net/vnet.h> + +#include <netinet/in.h> +#include <netinet/ip_var.h> +#include <netinet/ip_fw.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet6/in6_var.h> +#include <netinet6/ip6_var.h> + +#include <netpfil/ipfw/ip_fw_private.h> +#include <netpfil/ipfw/nptv6/nptv6.h> + +static VNET_DEFINE(uint16_t, nptv6_eid) = 0; +#define V_nptv6_eid VNET(nptv6_eid) +#define IPFW_TLV_NPTV6_NAME IPFW_TLV_EACTION_NAME(V_nptv6_eid) + +static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set); +static void nptv6_free_config(struct nptv6_cfg *cfg); +static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni, + const char *name, uint8_t set); +static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, + int offset); +static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, + int offset); + +#define NPTV6_LOOKUP(chain, cmd) \ + (struct nptv6_cfg *)SRV_OBJECT((chain), (cmd)->arg1) + +#ifndef IN6_MASK_ADDR +#define IN6_MASK_ADDR(a, m) do { \ + (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ + (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ + (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ + (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ +} while (0) +#endif +#ifndef IN6_ARE_MASKED_ADDR_EQUAL +#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ + (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ + (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ + (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ + (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) +#endif + +#if 0 +#define NPTV6_DEBUG(fmt, ...) do { \ + printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ +} while (0) +#define NPTV6_IPDEBUG(fmt, ...) do { \ + char _s[INET6_ADDRSTRLEN], _d[INET6_ADDRSTRLEN]; \ + printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ +} while (0) +#else +#define NPTV6_DEBUG(fmt, ...) +#define NPTV6_IPDEBUG(fmt, ...) +#endif + +static int +nptv6_getlasthdr(struct nptv6_cfg *cfg, struct mbuf *m, int *offset) +{ + struct ip6_hdr *ip6; + struct ip6_hbh *hbh; + int proto, hlen; + + hlen = (offset == NULL) ? 0: *offset; + if (m->m_len < hlen) + return (-1); + ip6 = mtodo(m, hlen); + hlen += sizeof(*ip6); + proto = ip6->ip6_nxt; + while (proto == IPPROTO_HOPOPTS || proto == IPPROTO_ROUTING || + proto == IPPROTO_DSTOPTS) { + hbh = mtodo(m, hlen); + if (m->m_len < hlen) + return (-1); + proto = hbh->ip6h_nxt; + hlen += hbh->ip6h_len << 3; + } + if (offset != NULL) + *offset = hlen; + return (proto); +} + +static int +nptv6_translate_icmpv6(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) +{ + struct icmp6_hdr *icmp6; + struct ip6_hdr *ip6; + struct mbuf *m; + + m = *mp; + if (offset > m->m_len) + return (-1); + icmp6 = mtodo(m, offset); + NPTV6_DEBUG("ICMPv6 type %d", icmp6->icmp6_type); + switch (icmp6->icmp6_type) { + case ICMP6_DST_UNREACH: + case ICMP6_PACKET_TOO_BIG: + case ICMP6_TIME_EXCEEDED: + case ICMP6_PARAM_PROB: + break; + case ICMP6_ECHO_REQUEST: + case ICMP6_ECHO_REPLY: + /* nothing to translate */ + return (0); + default: + /* + * XXX: We can add some checks to not translate NDP and MLD + * messages. Currently user must explicitly allow these message + * types, otherwise packets will be dropped. + */ + return (-1); + } + offset += sizeof(*icmp6); + if (offset + sizeof(*ip6) > m->m_pkthdr.len) + return (-1); + if (offset + sizeof(*ip6) > m->m_len) + *mp = m = m_pullup(m, offset + sizeof(*ip6)); + if (m == NULL) + return (-1); + ip6 = mtodo(m, offset); + NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, + inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), + inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), + ip6->ip6_nxt); + if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src, + &cfg->external, &cfg->mask)) + return (nptv6_rewrite_external(cfg, mp, offset)); + else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, + &cfg->internal, &cfg->mask)) + return (nptv6_rewrite_internal(cfg, mp, offset)); + /* + * Addresses in the inner IPv6 header doesn't matched to + * our prefixes. + */ + return (-1); +} + +static int +nptv6_search_index(struct nptv6_cfg *cfg, struct in6_addr *a) +{ + int idx; + + if (cfg->flags & NPTV6_48PLEN) + return (3); + + /* Search suitable word index for adjustment */ + for (idx = 4; idx < 8; idx++) + if (a->s6_addr16[idx] != 0xffff) + break; + /* + * RFC 6296 p3.7: If an NPTv6 Translator discovers a datagram with + * an IID of all-zeros while performing address mapping, that + * datagram MUST be dropped, and an ICMPv6 Parameter Problem error + * SHOULD be generated. + */ + if (idx == 8 || + (a->s6_addr32[2] == 0 && a->s6_addr32[3] == 0)) + return (-1); + return (idx); +} + +static void +nptv6_copy_addr(struct in6_addr *src, struct in6_addr *dst, + struct in6_addr *mask) +{ + int i; + + for (i = 0; i < 8 && mask->s6_addr8[i] != 0; i++) { + dst->s6_addr8[i] &= ~mask->s6_addr8[i]; + dst->s6_addr8[i] |= src->s6_addr8[i] & mask->s6_addr8[i]; + } +} + +static int +nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) +{ + struct in6_addr *addr; + struct ip6_hdr *ip6; + int idx, proto; + uint16_t adj; + + ip6 = mtodo(*mp, offset); + NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, + inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), + inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), + ip6->ip6_nxt); + if (offset == 0) + addr = &ip6->ip6_src; + else { + /* + * When we rewriting inner IPv6 header, we need to rewrite + * destination address back to external prefix. The datagram in + * the ICMPv6 payload should looks like it was send from + * external prefix. + */ + addr = &ip6->ip6_dst; + } + idx = nptv6_search_index(cfg, addr); + if (idx < 0) { + /* + * Do not send ICMPv6 error when offset isn't zero. + * This means we are rewriting inner IPv6 header in the + * ICMPv6 error message. + */ + if (offset == 0) { + icmp6_error2(*mp, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif); + *mp = NULL; + } + return (IP_FW_DENY); + } + adj = addr->s6_addr16[idx]; + nptv6_copy_addr(&cfg->external, addr, &cfg->mask); + adj = cksum_add(adj, cfg->adjustment); + if (adj == 0xffff) + adj = 0; + addr->s6_addr16[idx] = adj; + if (offset == 0) { + /* + * We may need to translate addresses in the inner IPv6 + * header for ICMPv6 error messages. + */ + proto = nptv6_getlasthdr(cfg, *mp, &offset); + if (proto < 0 || (proto == IPPROTO_ICMPV6 && + nptv6_translate_icmpv6(cfg, mp, offset) != 0)) + return (IP_FW_DENY); + NPTV6STAT_INC(cfg, in2ex); + } + return (0); +} + +static int +nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) +{ + struct in6_addr *addr; + struct ip6_hdr *ip6; + int idx, proto; + uint16_t adj; + + ip6 = mtodo(*mp, offset); + NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, + inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), + inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), + ip6->ip6_nxt); + if (offset == 0) + addr = &ip6->ip6_dst; + else { + /* + * When we rewriting inner IPv6 header, we need to rewrite + * source address back to internal prefix. The datagram in + * the ICMPv6 payload should looks like it was send from + * internal prefix. + */ + addr = &ip6->ip6_src; + } + idx = nptv6_search_index(cfg, addr); + if (idx < 0) { + /* + * Do not send ICMPv6 error when offset isn't zero. + * This means we are rewriting inner IPv6 header in the + * ICMPv6 error message. + */ + if (offset == 0) { + icmp6_error2(*mp, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif); + *mp = NULL; + } + return (IP_FW_DENY); + } + adj = addr->s6_addr16[idx]; + nptv6_copy_addr(&cfg->internal, addr, &cfg->mask); + adj = cksum_add(adj, ~cfg->adjustment); + if (adj == 0xffff) + adj = 0; + addr->s6_addr16[idx] = adj; + if (offset == 0) { + /* + * We may need to translate addresses in the inner IPv6 + * header for ICMPv6 error messages. + */ + proto = nptv6_getlasthdr(cfg, *mp, &offset); + if (proto < 0 || (proto == IPPROTO_ICMPV6 && + nptv6_translate_icmpv6(cfg, mp, offset) != 0)) + return (IP_FW_DENY); + NPTV6STAT_INC(cfg, ex2in); + } + return (0); +} + +/* + * ipfw external action handler. + */ +static int +ipfw_nptv6(struct ip_fw_chain *chain, struct ip_fw_args *args, + ipfw_insn *cmd, int *done) +{ + struct ip6_hdr *ip6; + struct nptv6_cfg *cfg; + ipfw_insn *icmd; + int ret; + + *done = 0; /* try next rule if not matched */ + ret = IP_FW_DENY; + icmd = cmd + 1; + if (cmd->opcode != O_EXTERNAL_ACTION || + cmd->arg1 != V_nptv6_eid || + icmd->opcode != O_EXTERNAL_INSTANCE || + (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL) + return (ret); + /* + * We need act as router, so when forwarding is disabled - + * do nothing. + */ + if (V_ip6_forwarding == 0 || args->f_id.addr_type != 6) + return (ret); + /* + * NOTE: we expect ipfw_chk() did m_pullup() up to upper level + * protocol's headers. Also we skip some checks, that ip6_input(), + * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. + */ + ip6 = mtod(args->m, struct ip6_hdr *); + NPTV6_IPDEBUG("eid %u, oid %u, %s -> %s %d", + cmd->arg1, icmd->arg1, + inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), + inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), + ip6->ip6_nxt); + if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src, + &cfg->internal, &cfg->mask)) { + /* + * XXX: Do not translate packets when both src and dst + * are from internal prefix. + */ + if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, + &cfg->internal, &cfg->mask)) + return (ret); + ret = nptv6_rewrite_internal(cfg, &args->m, 0); + } else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, + &cfg->external, &cfg->mask)) + ret = nptv6_rewrite_external(cfg, &args->m, 0); + else + return (ret); + /* + * If address wasn't rewrited - free mbuf and terminate the search. + */ + if (ret != 0) { + if (args->m != NULL) { + m_freem(args->m); + args->m = NULL; /* mark mbuf as consumed */ + } + NPTV6STAT_INC(cfg, dropped); + *done = 1; + } else { + /* Terminate the search if one_pass is set */ + *done = V_fw_one_pass; + /* Update args->f_id when one_pass is off */ + if (*done == 0) { + ip6 = mtod(args->m, struct ip6_hdr *); + args->f_id.src_ip6 = ip6->ip6_src; + args->f_id.dst_ip6 = ip6->ip6_dst; + } + } + return (ret); +} + +static struct nptv6_cfg * +nptv6_alloc_config(const char *name, uint8_t set) +{ + struct nptv6_cfg *cfg; + + cfg = malloc(sizeof(struct nptv6_cfg), M_IPFW, M_WAITOK | M_ZERO); + COUNTER_ARRAY_ALLOC(cfg->stats, NPTV6STATS, M_WAITOK); + cfg->no.name = cfg->name; + cfg->no.etlv = IPFW_TLV_NPTV6_NAME; + cfg->no.set = set; + strlcpy(cfg->name, name, sizeof(cfg->name)); + return (cfg); +} + +static void +nptv6_free_config(struct nptv6_cfg *cfg) +{ + + COUNTER_ARRAY_FREE(cfg->stats, NPTV6STATS); + free(cfg, M_IPFW); +} + +static void +nptv6_export_config(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, + ipfw_nptv6_cfg *uc) +{ + + uc->internal = cfg->internal; + uc->external = cfg->external; + uc->plen = cfg->plen; + uc->flags = cfg->flags & NPTV6_FLAGSMASK; + uc->set = cfg->no.set; + strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); +} + +struct nptv6_dump_arg { + struct ip_fw_chain *ch; + struct sockopt_data *sd; +}; + +static int +export_config_cb(struct namedobj_instance *ni, struct named_object *no, + void *arg) +{ + struct nptv6_dump_arg *da = (struct nptv6_dump_arg *)arg; + ipfw_nptv6_cfg *uc; + + uc = (ipfw_nptv6_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); + nptv6_export_config(da->ch, (struct nptv6_cfg *)no, uc); + return (0); +} + +static struct nptv6_cfg * +nptv6_find(struct namedobj_instance *ni, const char *name, uint8_t set) +{ + struct nptv6_cfg *cfg; + + cfg = (struct nptv6_cfg *)ipfw_objhash_lookup_name_type(ni, set, + IPFW_TLV_NPTV6_NAME, name); + + return (cfg); +} + +static void +nptv6_calculate_adjustment(struct nptv6_cfg *cfg) +{ + uint16_t i, e; + uint16_t *p; + + /* Calculate checksum of internal prefix */ + for (i = 0, p = (uint16_t *)&cfg->internal; + p < (uint16_t *)(&cfg->internal + 1); p++) + i = cksum_add(i, *p); + + /* Calculate checksum of external prefix */ + for (e = 0, p = (uint16_t *)&cfg->external; + p < (uint16_t *)(&cfg->external + 1); p++) + e = cksum_add(e, *p); + + /* Adjustment value for Int->Ext direction */ + cfg->adjustment = cksum_add(~e, i); +} + +/* + * Creates new NPTv6 instance. + * Data layout (v0)(current): + * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] + * + * Returns 0 on success + */ +static int +nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + struct in6_addr mask; + ipfw_obj_lheader *olh; + ipfw_nptv6_cfg *uc; + struct namedobj_instance *ni; + struct nptv6_cfg *cfg; + + if (sd->valsize != sizeof(*olh) + sizeof(*uc)) + return (EINVAL); + + olh = (ipfw_obj_lheader *)sd->kbuf; + uc = (ipfw_nptv6_cfg *)(olh + 1); + if (ipfw_check_object_name_generic(uc->name) != 0) + return (EINVAL); + if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS) + return (EINVAL); + if (IN6_IS_ADDR_MULTICAST(&uc->internal) || + IN6_IS_ADDR_MULTICAST(&uc->external) || + IN6_IS_ADDR_UNSPECIFIED(&uc->internal) || + IN6_IS_ADDR_UNSPECIFIED(&uc->external) || + IN6_IS_ADDR_LINKLOCAL(&uc->internal) || + IN6_IS_ADDR_LINKLOCAL(&uc->external)) + return (EINVAL); + in6_prefixlen2mask(&mask, uc->plen); + if (IN6_ARE_MASKED_ADDR_EQUAL(&uc->internal, &uc->external, &mask)) + return (EINVAL); + + ni = CHAIN_TO_SRV(ch); + IPFW_UH_RLOCK(ch); + if (nptv6_find(ni, uc->name, uc->set) != NULL) { + IPFW_UH_RUNLOCK(ch); + return (EEXIST); + } + IPFW_UH_RUNLOCK(ch); + + cfg = nptv6_alloc_config(uc->name, uc->set); + cfg->plen = uc->plen; + if (cfg->plen <= 48) + cfg->flags |= NPTV6_48PLEN; + cfg->internal = uc->internal; + cfg->external = uc->external; + cfg->mask = mask; + IN6_MASK_ADDR(&cfg->internal, &mask); + IN6_MASK_ADDR(&cfg->external, &mask); + nptv6_calculate_adjustment(cfg); + + IPFW_UH_WLOCK(ch); + if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { + IPFW_UH_WUNLOCK(ch); + nptv6_free_config(cfg); + return (ENOSPC); + } + ipfw_objhash_add(ni, &cfg->no); + IPFW_WLOCK(ch); + SRV_OBJECT(ch, cfg->no.kidx) = cfg; + IPFW_WUNLOCK(ch); + IPFW_UH_WUNLOCK(ch); + return (0); +} + +/* + * Destroys NPTv6 instance. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ] + * + * Returns 0 on success + */ +static int +nptv6_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nptv6_cfg *cfg; + + if (sd->valsize != sizeof(*oh)) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + if (ipfw_check_object_name_generic(oh->ntlv.name) != 0) + return (EINVAL); + + IPFW_UH_WLOCK(ch); + cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (cfg == NULL) { + IPFW_UH_WUNLOCK(ch); + return (ESRCH); + } + if (cfg->no.refcnt > 0) { + IPFW_UH_WUNLOCK(ch); + return (EBUSY); + } + + IPFW_WLOCK(ch); + SRV_OBJECT(ch, cfg->no.kidx) = NULL; + IPFW_WUNLOCK(ch); + + ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); + ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); + IPFW_UH_WUNLOCK(ch); + + nptv6_free_config(cfg); + return (0); +} + +/* + * Get or change nptv6 instance config. + * Request: [ ipfw_obj_header [ ipfw_nptv6_cfg ] ] + */ +static int +nptv6_config(struct ip_fw_chain *chain, ip_fw3_opheader *op, + struct sockopt_data *sd) +{ + + return (EOPNOTSUPP); +} + +/* + * Lists all NPTv6 instances currently available in kernel. + * Data layout (v0)(current): + * Request: [ ipfw_obj_lheader ] + * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ] + * + * Returns 0 on success + */ +static int +nptv6_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_lheader *olh; + struct nptv6_dump_arg da; + + /* Check minimum header size */ + if (sd->valsize < sizeof(ipfw_obj_lheader)) + return (EINVAL); + + olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); + + IPFW_UH_RLOCK(ch); + olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), + IPFW_TLV_NPTV6_NAME); + olh->objsize = sizeof(ipfw_nptv6_cfg); + olh->size = sizeof(*olh) + olh->count * olh->objsize; + + if (sd->valsize < olh->size) { + IPFW_UH_RUNLOCK(ch); + return (ENOMEM); + } + memset(&da, 0, sizeof(da)); + da.ch = ch; + da.sd = sd; + ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, + &da, IPFW_TLV_NPTV6_NAME); + IPFW_UH_RUNLOCK(ch); + + return (0); +} + +#define __COPY_STAT_FIELD(_cfg, _stats, _field) \ + (_stats)->_field = NPTV6STAT_FETCH(_cfg, _field) +static void +export_stats(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, + struct ipfw_nptv6_stats *stats) +{ + + __COPY_STAT_FIELD(cfg, stats, in2ex); + __COPY_STAT_FIELD(cfg, stats, ex2in); + __COPY_STAT_FIELD(cfg, stats, dropped); +} + +/* + * Get NPTv6 statistics. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ] + * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]] + * + * Returns 0 on success + */ +static int +nptv6_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, + struct sockopt_data *sd) +{ + struct ipfw_nptv6_stats stats; + struct nptv6_cfg *cfg; + ipfw_obj_header *oh; + ipfw_obj_ctlv *ctlv; + size_t sz; + + sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); + if (sd->valsize % sizeof(uint64_t)) + return (EINVAL); + if (sd->valsize < sz) + return (ENOMEM); + oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); + if (oh == NULL) + return (EINVAL); + if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || + oh->ntlv.set >= IPFW_MAX_SETS) + return (EINVAL); + memset(&stats, 0, sizeof(stats)); + + IPFW_UH_RLOCK(ch); + cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (cfg == NULL) { + IPFW_UH_RUNLOCK(ch); + return (ESRCH); + } + export_stats(ch, cfg, &stats); + IPFW_UH_RUNLOCK(ch); + + ctlv = (ipfw_obj_ctlv *)(oh + 1); + memset(ctlv, 0, sizeof(*ctlv)); + ctlv->head.type = IPFW_TLV_COUNTERS; + ctlv->head.length = sz - sizeof(ipfw_obj_header); + ctlv->count = sizeof(stats) / sizeof(uint64_t); + ctlv->objsize = sizeof(uint64_t); + ctlv->version = 1; + memcpy(ctlv + 1, &stats, sizeof(stats)); + return (0); +} + +/* + * Reset NPTv6 statistics. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ] + * + * Returns 0 on success + */ +static int +nptv6_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, + struct sockopt_data *sd) +{ + struct nptv6_cfg *cfg; + ipfw_obj_header *oh; + + if (sd->valsize != sizeof(*oh)) + return (EINVAL); + oh = (ipfw_obj_header *)sd->kbuf; + if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || + oh->ntlv.set >= IPFW_MAX_SETS) + return (EINVAL); + + IPFW_UH_WLOCK(ch); + cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); + if (cfg == NULL) { + IPFW_UH_WUNLOCK(ch); + return (ESRCH); + } + COUNTER_ARRAY_ZERO(cfg->stats, NPTV6STATS); + IPFW_UH_WUNLOCK(ch); + return (0); +} + +static struct ipfw_sopt_handler scodes[] = { + { IP_FW_NPTV6_CREATE, 0, HDIR_SET, nptv6_create }, + { IP_FW_NPTV6_DESTROY,0, HDIR_SET, nptv6_destroy }, + { IP_FW_NPTV6_CONFIG, 0, HDIR_BOTH, nptv6_config }, + { IP_FW_NPTV6_LIST, 0, HDIR_GET, nptv6_list }, + { IP_FW_NPTV6_STATS, 0, HDIR_GET, nptv6_stats }, + { IP_FW_NPTV6_RESET_STATS,0, HDIR_SET, nptv6_reset_stats }, +}; + +static int +nptv6_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +{ + ipfw_insn *icmd; + + icmd = cmd - 1; + NPTV6_DEBUG("opcode %d, arg1 %d, opcode0 %d, arg1 %d", + cmd->opcode, cmd->arg1, icmd->opcode, icmd->arg1); + if (icmd->opcode != O_EXTERNAL_ACTION || + icmd->arg1 != V_nptv6_eid) + return (1); + + *puidx = cmd->arg1; + *ptype = 0; + return (0); +} + +static void +nptv6_update_arg1(ipfw_insn *cmd, uint16_t idx) +{ + + cmd->arg1 = idx; + NPTV6_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1); +} + +static int +nptv6_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, + struct named_object **pno) +{ + int err; + + err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, + IPFW_TLV_NPTV6_NAME, pno); + NPTV6_DEBUG("uidx %u, type %u, err %d", ti->uidx, ti->type, err); + return (err); +} + +static struct named_object * +nptv6_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +{ + struct namedobj_instance *ni; + struct named_object *no; + + IPFW_UH_WLOCK_ASSERT(ch); + ni = CHAIN_TO_SRV(ch); + no = ipfw_objhash_lookup_kidx(ni, idx); + KASSERT(no != NULL, ("NPT with index %d not found", idx)); + + NPTV6_DEBUG("kidx %u -> %s", idx, no->name); + return (no); +} + +static int +nptv6_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, + enum ipfw_sets_cmd cmd) +{ + + return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NPTV6_NAME, + set, new_set, cmd)); +} + +static struct opcode_obj_rewrite opcodes[] = { + { + .opcode = O_EXTERNAL_INSTANCE, + .etlv = IPFW_TLV_EACTION /* just show it isn't table */, + .classifier = nptv6_classify, + .update = nptv6_update_arg1, + .find_byname = nptv6_findbyname, + .find_bykidx = nptv6_findbykidx, + .manage_sets = nptv6_manage_sets, + }, +}; + +static int +destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, + void *arg) +{ + struct nptv6_cfg *cfg; + struct ip_fw_chain *ch; + + ch = (struct ip_fw_chain *)arg; + IPFW_UH_WLOCK_ASSERT(ch); + + cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx); + SRV_OBJECT(ch, no->kidx) = NULL; + ipfw_objhash_del(ni, &cfg->no); + ipfw_objhash_free_idx(ni, cfg->no.kidx); + nptv6_free_config(cfg); + return (0); +} + +int +nptv6_init(struct ip_fw_chain *ch, int first) +{ + + V_nptv6_eid = ipfw_add_eaction(ch, ipfw_nptv6, "nptv6"); + if (V_nptv6_eid == 0) + return (ENXIO); + IPFW_ADD_SOPT_HANDLER(first, scodes); + IPFW_ADD_OBJ_REWRITER(first, opcodes); + return (0); +} + +void +nptv6_uninit(struct ip_fw_chain *ch, int last) +{ + + IPFW_DEL_OBJ_REWRITER(last, opcodes); + IPFW_DEL_SOPT_HANDLER(last, scodes); + ipfw_del_eaction(ch, V_nptv6_eid); + /* + * Since we already have deregistered external action, + * our named objects become unaccessible via rules, because + * all rules were truncated by ipfw_del_eaction(). + * So, we can unlink and destroy our named objects without holding + * IPFW_WLOCK(). + */ + IPFW_UH_WLOCK(ch); + ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, + IPFW_TLV_NPTV6_NAME); + V_nptv6_eid = 0; + IPFW_UH_WUNLOCK(ch); +} + diff --git a/sys/netpfil/ipfw/nptv6/nptv6.h b/sys/netpfil/ipfw/nptv6/nptv6.h new file mode 100644 index 0000000..95b04bf --- /dev/null +++ b/sys/netpfil/ipfw/nptv6/nptv6.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2016 Yandex LLC + * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _IP_FW_NPTV6_H_ +#define _IP_FW_NPTV6_H_ + +#include <netinet6/ip_fw_nptv6.h> + +#ifdef _KERNEL +#define NPTV6STATS (sizeof(struct ipfw_nptv6_stats) / sizeof(uint64_t)) +#define NPTV6STAT_ADD(c, f, v) \ + counter_u64_add((c)->stats[ \ + offsetof(struct ipfw_nptv6_stats, f) / sizeof(uint64_t)], (v)) +#define NPTV6STAT_INC(c, f) NPTV6STAT_ADD(c, f, 1) +#define NPTV6STAT_FETCH(c, f) \ + counter_u64_fetch((c)->stats[ \ + offsetof(struct ipfw_nptv6_stats, f) / sizeof(uint64_t)]) + +struct nptv6_cfg { + struct named_object no; + + struct in6_addr internal; /* Internal IPv6 prefix */ + struct in6_addr external; /* External IPv6 prefix */ + struct in6_addr mask; /* IPv6 prefix mask */ + uint16_t adjustment; /* Checksum adjustment value */ + uint8_t plen; /* Prefix length */ + uint8_t flags; /* Flags for internal use */ +#define NPTV6_48PLEN 0x0001 + char name[64]; /* Instance name */ + counter_u64_t stats[NPTV6STATS]; /* Statistics counters */ +}; +#define NPTV6_FLAGSMASK 0 + +int nptv6_init(struct ip_fw_chain *ch, int first); +void nptv6_uninit(struct ip_fw_chain *ch, int last); +#endif /* _KERNEL */ + +#endif /* _IP_FW_NPTV6_H_ */ + |