Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/nptv6/nptv6.c
Show All 25 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/counter.h> | #include <sys/counter.h> | ||||
#include <sys/eventhandler.h> | |||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.h> | ||||
#include <sys/rwlock.h> | #include <sys/rwlock.h> | ||||
Show All 18 Lines | |||||
#include <netpfil/ipfw/ip_fw_private.h> | #include <netpfil/ipfw/ip_fw_private.h> | ||||
#include <netpfil/ipfw/nptv6/nptv6.h> | #include <netpfil/ipfw/nptv6/nptv6.h> | ||||
VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0; | VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0; | ||||
#define V_nptv6_eid VNET(nptv6_eid) | #define V_nptv6_eid VNET(nptv6_eid) | ||||
#define IPFW_TLV_NPTV6_NAME IPFW_TLV_EACTION_NAME(V_nptv6_eid) | #define IPFW_TLV_NPTV6_NAME IPFW_TLV_EACTION_NAME(V_nptv6_eid) | ||||
static eventhandler_tag nptv6_ifaddr_event; | |||||
static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set); | static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set); | ||||
static void nptv6_free_config(struct nptv6_cfg *cfg); | static void nptv6_free_config(struct nptv6_cfg *cfg); | ||||
static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni, | static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni, | ||||
const char *name, uint8_t set); | const char *name, uint8_t set); | ||||
static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, | static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, | ||||
int offset); | int offset); | ||||
static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, | static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, | ||||
int offset); | int offset); | ||||
▲ Show 20 Lines • Show All 276 Lines • ▼ Show 20 Lines | ipfw_nptv6(struct ip_fw_chain *chain, struct ip_fw_args *args, | ||||
int ret; | int ret; | ||||
*done = 0; /* try next rule if not matched */ | *done = 0; /* try next rule if not matched */ | ||||
ret = IP_FW_DENY; | ret = IP_FW_DENY; | ||||
icmd = cmd + 1; | icmd = cmd + 1; | ||||
if (cmd->opcode != O_EXTERNAL_ACTION || | if (cmd->opcode != O_EXTERNAL_ACTION || | ||||
cmd->arg1 != V_nptv6_eid || | cmd->arg1 != V_nptv6_eid || | ||||
icmd->opcode != O_EXTERNAL_INSTANCE || | icmd->opcode != O_EXTERNAL_INSTANCE || | ||||
(cfg = NPTV6_LOOKUP(chain, icmd)) == NULL) | (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL || | ||||
(cfg->flags & NPTV6_READY) == 0) | |||||
return (ret); | return (ret); | ||||
/* | /* | ||||
* We need act as router, so when forwarding is disabled - | * We need act as router, so when forwarding is disabled - | ||||
* do nothing. | * do nothing. | ||||
*/ | */ | ||||
if (V_ip6_forwarding == 0 || args->f_id.addr_type != 6) | if (V_ip6_forwarding == 0 || args->f_id.addr_type != 6) | ||||
return (ret); | return (ret); | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
nptv6_export_config(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, | nptv6_export_config(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, | ||||
ipfw_nptv6_cfg *uc) | ipfw_nptv6_cfg *uc) | ||||
{ | { | ||||
uc->internal = cfg->internal; | uc->internal = cfg->internal; | ||||
if (cfg->flags & NPTV6_DYNAMIC_PREFIX) | |||||
memcpy(uc->if_name, cfg->if_name, IF_NAMESIZE); | |||||
else | |||||
uc->external = cfg->external; | uc->external = cfg->external; | ||||
uc->plen = cfg->plen; | uc->plen = cfg->plen; | ||||
uc->flags = cfg->flags & NPTV6_FLAGSMASK; | uc->flags = cfg->flags & NPTV6_FLAGSMASK; | ||||
uc->set = cfg->no.set; | uc->set = cfg->no.set; | ||||
strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); | strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); | ||||
} | } | ||||
struct nptv6_dump_arg { | struct nptv6_dump_arg { | ||||
struct ip_fw_chain *ch; | struct ip_fw_chain *ch; | ||||
Show All 38 Lines | nptv6_calculate_adjustment(struct nptv6_cfg *cfg) | ||||
for (e = 0, p = (uint16_t *)&cfg->external; | for (e = 0, p = (uint16_t *)&cfg->external; | ||||
p < (uint16_t *)(&cfg->external + 1); p++) | p < (uint16_t *)(&cfg->external + 1); p++) | ||||
e = cksum_add(e, *p); | e = cksum_add(e, *p); | ||||
/* Adjustment value for Int->Ext direction */ | /* Adjustment value for Int->Ext direction */ | ||||
cfg->adjustment = cksum_add(~e, i); | cfg->adjustment = cksum_add(~e, i); | ||||
} | } | ||||
static int | |||||
nptv6_check_prefix(const struct in6_addr *addr) | |||||
{ | |||||
if (IN6_IS_ADDR_MULTICAST(addr) || | |||||
IN6_IS_ADDR_LINKLOCAL(addr) || | |||||
IN6_IS_ADDR_LOOPBACK(addr) || | |||||
IN6_IS_ADDR_UNSPECIFIED(addr)) | |||||
return (EINVAL); | |||||
return (0); | |||||
} | |||||
static void | |||||
nptv6_set_external(struct nptv6_cfg *cfg, struct in6_addr *addr) | |||||
{ | |||||
cfg->external = *addr; | |||||
IN6_MASK_ADDR(&cfg->external, &cfg->mask); | |||||
nptv6_calculate_adjustment(cfg); | |||||
cfg->flags |= NPTV6_READY; | |||||
} | |||||
/* | /* | ||||
* Try to determine what prefix to use as external for | |||||
* configured interface name. | |||||
*/ | |||||
static void | |||||
nptv6_find_prefix(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, | |||||
struct ifnet *ifp) | |||||
{ | |||||
struct ifaddr *ifa; | |||||
struct in6_ifaddr *ia; | |||||
MPASS(cfg->flags & NPTV6_DYNAMIC_PREFIX); | |||||
IPFW_UH_WLOCK_ASSERT(ch); | |||||
if (ifp == NULL) { | |||||
ifp = ifunit_ref(cfg->if_name); | |||||
if (ifp == NULL) | |||||
return; | |||||
} | |||||
if_addr_rlock(ifp); | |||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |||||
if (ifa->ifa_addr->sa_family != AF_INET6) | |||||
continue; | |||||
ia = (struct in6_ifaddr *)ifa; | |||||
if (nptv6_check_prefix(&ia->ia_addr.sin6_addr) || | |||||
IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr, | |||||
&cfg->internal, &cfg->mask)) | |||||
continue; | |||||
/* Suitable address is found. */ | |||||
nptv6_set_external(cfg, &ia->ia_addr.sin6_addr); | |||||
break; | |||||
} | |||||
if_addr_runlock(ifp); | |||||
if_rele(ifp); | |||||
} | |||||
struct ifaddr_event_args { | |||||
struct ifnet *ifp; | |||||
const struct in6_addr *addr; | |||||
int event; | |||||
}; | |||||
static int | |||||
ifaddr_cb(struct namedobj_instance *ni, struct named_object *no, | |||||
void *arg) | |||||
{ | |||||
struct ifaddr_event_args *args; | |||||
struct ip_fw_chain *ch; | |||||
struct nptv6_cfg *cfg; | |||||
ch = &V_layer3_chain; | |||||
cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx); | |||||
if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0) | |||||
return (0); | |||||
args = arg; | |||||
/* If interface name doesn't match, ignore */ | |||||
if (strncmp(args->ifp->if_xname, cfg->if_name, IF_NAMESIZE)) | |||||
return (0); | |||||
if (args->ifp->if_flags & IFF_DYING) { /* XXX: is it possible? */ | |||||
cfg->flags &= ~NPTV6_READY; | |||||
return (0); | |||||
} | |||||
if (args->event == IFADDR_EVENT_DEL) { | |||||
/* If instance is not ready, ignore */ | |||||
if ((cfg->flags & NPTV6_READY) == 0) | |||||
return (0); | |||||
/* If address does not match the external prefix, ignore */ | |||||
if (IN6_ARE_MASKED_ADDR_EQUAL(&cfg->external, args->addr, | |||||
&cfg->mask) != 0) | |||||
return (0); | |||||
/* Otherwise clear READY flag */ | |||||
cfg->flags &= ~NPTV6_READY; | |||||
} else {/* IFADDR_EVENT_ADD */ | |||||
/* If instance is already ready, ignore */ | |||||
if (cfg->flags & NPTV6_READY) | |||||
return (0); | |||||
/* If address is not suitable for prefix, ignore */ | |||||
if (nptv6_check_prefix(args->addr) || | |||||
IN6_ARE_MASKED_ADDR_EQUAL(args->addr, &cfg->internal, | |||||
&cfg->mask)) | |||||
return (0); | |||||
/* FALLTHROUGH */ | |||||
} | |||||
MPASS(!(cfg->flags & NPTV6_READY)); | |||||
/* Try to determine the prefix */ | |||||
if_ref(args->ifp); | |||||
nptv6_find_prefix(ch, cfg, args->ifp); | |||||
return (0); | |||||
} | |||||
static void | |||||
nptv6_ifaddrevent_handler(void *arg __unused, struct ifnet *ifp, | |||||
struct ifaddr *ifa, int event) | |||||
{ | |||||
struct ifaddr_event_args args; | |||||
struct ip_fw_chain *ch; | |||||
if (ifa->ifa_addr->sa_family != AF_INET6) | |||||
return; | |||||
args.ifp = ifp; | |||||
args.addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; | |||||
args.event = event; | |||||
ch = &V_layer3_chain; | |||||
IPFW_UH_WLOCK(ch); | |||||
ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), ifaddr_cb, &args, | |||||
IPFW_TLV_NPTV6_NAME); | |||||
IPFW_UH_WUNLOCK(ch); | |||||
} | |||||
/* | |||||
* Creates new NPTv6 instance. | * Creates new NPTv6 instance. | ||||
* Data layout (v0)(current): | * Data layout (v0)(current): | ||||
* Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] | * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] | ||||
* | * | ||||
* Returns 0 on success | * Returns 0 on success | ||||
*/ | */ | ||||
static int | static int | ||||
nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, | nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, | ||||
Show All 9 Lines | if (sd->valsize != sizeof(*olh) + sizeof(*uc)) | ||||
return (EINVAL); | return (EINVAL); | ||||
olh = (ipfw_obj_lheader *)sd->kbuf; | olh = (ipfw_obj_lheader *)sd->kbuf; | ||||
uc = (ipfw_nptv6_cfg *)(olh + 1); | uc = (ipfw_nptv6_cfg *)(olh + 1); | ||||
if (ipfw_check_object_name_generic(uc->name) != 0) | if (ipfw_check_object_name_generic(uc->name) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS) | if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (IN6_IS_ADDR_MULTICAST(&uc->internal) || | if (nptv6_check_prefix(&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); | return (EINVAL); | ||||
in6_prefixlen2mask(&mask, uc->plen); | in6_prefixlen2mask(&mask, uc->plen); | ||||
if (IN6_ARE_MASKED_ADDR_EQUAL(&uc->internal, &uc->external, &mask)) | if ((uc->flags & NPTV6_DYNAMIC_PREFIX) == 0 && ( | ||||
nptv6_check_prefix(&uc->external) || | |||||
IN6_ARE_MASKED_ADDR_EQUAL(&uc->external, &uc->internal, &mask))) | |||||
return (EINVAL); | return (EINVAL); | ||||
ni = CHAIN_TO_SRV(ch); | ni = CHAIN_TO_SRV(ch); | ||||
IPFW_UH_RLOCK(ch); | IPFW_UH_RLOCK(ch); | ||||
if (nptv6_find(ni, uc->name, uc->set) != NULL) { | if (nptv6_find(ni, uc->name, uc->set) != NULL) { | ||||
IPFW_UH_RUNLOCK(ch); | IPFW_UH_RUNLOCK(ch); | ||||
return (EEXIST); | return (EEXIST); | ||||
} | } | ||||
IPFW_UH_RUNLOCK(ch); | IPFW_UH_RUNLOCK(ch); | ||||
cfg = nptv6_alloc_config(uc->name, uc->set); | cfg = nptv6_alloc_config(uc->name, uc->set); | ||||
cfg->plen = uc->plen; | cfg->plen = uc->plen; | ||||
cfg->flags = uc->flags & NPTV6_FLAGSMASK; | |||||
if (cfg->plen <= 48) | if (cfg->plen <= 48) | ||||
cfg->flags |= NPTV6_48PLEN; | cfg->flags |= NPTV6_48PLEN; | ||||
cfg->internal = uc->internal; | |||||
cfg->external = uc->external; | |||||
cfg->mask = mask; | cfg->mask = mask; | ||||
cfg->internal = uc->internal; | |||||
IN6_MASK_ADDR(&cfg->internal, &mask); | IN6_MASK_ADDR(&cfg->internal, &mask); | ||||
IN6_MASK_ADDR(&cfg->external, &mask); | if (cfg->flags & NPTV6_DYNAMIC_PREFIX) | ||||
nptv6_calculate_adjustment(cfg); | memcpy(cfg->if_name, uc->if_name, IF_NAMESIZE); | ||||
else | |||||
nptv6_set_external(cfg, &uc->external); | |||||
if ((uc->flags & NPTV6_DYNAMIC_PREFIX) != 0 && | |||||
nptv6_ifaddr_event == NULL) | |||||
nptv6_ifaddr_event = EVENTHANDLER_REGISTER( | |||||
ifaddr_event_ext, nptv6_ifaddrevent_handler, NULL, | |||||
EVENTHANDLER_PRI_ANY); | |||||
IPFW_UH_WLOCK(ch); | IPFW_UH_WLOCK(ch); | ||||
if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { | if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { | ||||
IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
nptv6_free_config(cfg); | nptv6_free_config(cfg); | ||||
return (ENOSPC); | return (ENOSPC); | ||||
} | } | ||||
ipfw_objhash_add(ni, &cfg->no); | ipfw_objhash_add(ni, &cfg->no); | ||||
SRV_OBJECT(ch, cfg->no.kidx) = cfg; | SRV_OBJECT(ch, cfg->no.kidx) = cfg; | ||||
if (cfg->flags & NPTV6_DYNAMIC_PREFIX) | |||||
nptv6_find_prefix(ch, cfg, NULL); | |||||
IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Destroys NPTv6 instance. | * Destroys NPTv6 instance. | ||||
* Data layout (v0)(current): | * Data layout (v0)(current): | ||||
* Request: [ ipfw_obj_header ] | * Request: [ ipfw_obj_header ] | ||||
* | * | ||||
▲ Show 20 Lines • Show All 292 Lines • ▼ Show 20 Lines | nptv6_init(struct ip_fw_chain *ch, int first) | ||||
IPFW_ADD_OBJ_REWRITER(first, opcodes); | IPFW_ADD_OBJ_REWRITER(first, opcodes); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
nptv6_uninit(struct ip_fw_chain *ch, int last) | nptv6_uninit(struct ip_fw_chain *ch, int last) | ||||
{ | { | ||||
if (last && nptv6_ifaddr_event != NULL) | |||||
EVENTHANDLER_DEREGISTER(ifaddr_event_ext, nptv6_ifaddr_event); | |||||
IPFW_DEL_OBJ_REWRITER(last, opcodes); | IPFW_DEL_OBJ_REWRITER(last, opcodes); | ||||
IPFW_DEL_SOPT_HANDLER(last, scodes); | IPFW_DEL_SOPT_HANDLER(last, scodes); | ||||
ipfw_del_eaction(ch, V_nptv6_eid); | ipfw_del_eaction(ch, V_nptv6_eid); | ||||
/* | /* | ||||
* Since we already have deregistered external action, | * Since we already have deregistered external action, | ||||
* our named objects become unaccessible via rules, because | * our named objects become unaccessible via rules, because | ||||
* all rules were truncated by ipfw_del_eaction(). | * all rules were truncated by ipfw_del_eaction(). | ||||
* So, we can unlink and destroy our named objects without holding | * So, we can unlink and destroy our named objects without holding | ||||
Show All 9 Lines |