📄 ip_encap.c
字号:
if (!rnh->rnh_addaddr((caddr_t)ep->addrpack,
(caddr_t)ep->maskpack, rnh, ep->nodes)) {
error = EEXIST;
goto fail;
}
}
#endif
return error;
#ifdef USE_RADIX
fail:
LIST_REMOVE(ep, chain);
return error;
#endif
}
static int
encap_remove(ep)
struct encaptab *ep;
{
#ifdef USE_RADIX
struct radix_node_head *rnh = encap_rnh(ep->af);
#endif
int error = 0;
LIST_REMOVE(ep, chain);
#ifdef USE_RADIX
if (!ep->func && rnh) {
if (!rnh->rnh_deladdr((caddr_t)ep->addrpack,
(caddr_t)ep->maskpack, rnh))
error = ESRCH;
}
#endif
return error;
}
static int
encap_afcheck(af, sp, dp)
int af;
const struct sockaddr *sp;
const struct sockaddr *dp;
{
if (sp && dp) {
if (sp->sa_len != dp->sa_len)
return EINVAL;
if (af != sp->sa_family || af != dp->sa_family)
return EINVAL;
} else if (!sp && !dp)
;
else
return EINVAL;
switch (af) {
case AF_INET:
if (sp && sp->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
if (dp && dp->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
break;
#ifdef INET6
case AF_INET6:
if (sp && sp->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
if (dp && dp->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
break;
#endif
default:
return EAFNOSUPPORT;
}
return 0;
}
/*
* sp (src ptr) is always my side, and dp (dst ptr) is always remote side.
* length of mask (sm and dm) is assumed to be same as sp/dp.
* Return value will be necessary as input (cookie) for encap_detach().
*/
const struct encaptab *
encap_attach(af, proto, sp, sm, dp, dm, psw, arg)
int af;
int proto;
const struct sockaddr *sp, *sm;
const struct sockaddr *dp, *dm;
const struct protosw *psw;
void *arg;
{
struct encaptab *ep;
int error;
int s;
size_t l;
struct pack4 *pack4;
#ifdef INET6
struct pack6 *pack6;
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
s = splsoftnet();
#else
s = splnet();
#endif
/* sanity check on args */
error = encap_afcheck(af, sp, dp);
if (error)
goto fail;
/* check if anyone have already attached with exactly same config */
for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) {
if (ep->af != af)
continue;
if (ep->proto != proto)
continue;
if (ep->func)
continue;
#ifdef DIAGNOSTIC
if (!ep->src || !ep->dst || !ep->srcmask || !ep->dstmask)
panic("null pointers in encaptab");
#endif
if (ep->src->sa_len != sp->sa_len ||
bcmp(ep->src, sp, sp->sa_len) != 0 ||
bcmp(ep->srcmask, sm, sp->sa_len) != 0)
continue;
if (ep->dst->sa_len != dp->sa_len ||
bcmp(ep->dst, dp, dp->sa_len) != 0 ||
bcmp(ep->dstmask, dm, dp->sa_len) != 0)
continue;
error = EEXIST;
goto fail;
}
switch (af) {
case AF_INET:
l = sizeof(*pack4);
break;
#ifdef INET6
case AF_INET6:
l = sizeof(*pack6);
break;
#endif
default:
goto fail;
}
#ifdef DIAGNOSTIC
/* if l exceeds the value sa_len can possibly express, it's wrong. */
if (l > (1 << (8 * sizeof(ep->addrpack->sa_len)))) {
error = EINVAL;
goto fail;
}
#endif
ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /* M_NETADDR ok? */
if (ep == NULL) {
error = ENOBUFS;
goto fail;
}
bzero(ep, sizeof(*ep));
ep->addrpack = malloc(l, M_NETADDR, M_NOWAIT);
if (ep->addrpack == NULL) {
error = ENOBUFS;
goto gc;
}
ep->maskpack = malloc(l, M_NETADDR, M_NOWAIT);
if (ep->maskpack == NULL) {
error = ENOBUFS;
goto gc;
}
ep->af = af;
ep->proto = proto;
ep->addrpack->sa_len = l & 0xff;
ep->maskpack->sa_len = l & 0xff;
switch (af) {
case AF_INET:
pack4 = (struct pack4 *)ep->addrpack;
ep->src = (struct sockaddr *)&pack4->mine;
ep->dst = (struct sockaddr *)&pack4->yours;
pack4 = (struct pack4 *)ep->maskpack;
ep->srcmask = (struct sockaddr *)&pack4->mine;
ep->dstmask = (struct sockaddr *)&pack4->yours;
break;
#ifdef INET6
case AF_INET6:
pack6 = (struct pack6 *)ep->addrpack;
ep->src = (struct sockaddr *)&pack6->mine;
ep->dst = (struct sockaddr *)&pack6->yours;
pack6 = (struct pack6 *)ep->maskpack;
ep->srcmask = (struct sockaddr *)&pack6->mine;
ep->dstmask = (struct sockaddr *)&pack6->yours;
break;
#endif
}
bcopy(sp, ep->src, sp->sa_len);
bcopy(sm, ep->srcmask, sp->sa_len);
bcopy(dp, ep->dst, dp->sa_len);
bcopy(dm, ep->dstmask, dp->sa_len);
ep->psw = psw;
ep->arg = arg;
error = encap_add(ep);
if (error)
goto gc;
error = 0;
splx(s);
return ep;
gc:
if (ep->addrpack)
free(ep->addrpack, M_NETADDR);
if (ep->maskpack)
free(ep->maskpack, M_NETADDR);
if (ep)
free(ep, M_NETADDR);
fail:
splx(s);
return NULL;
}
const struct encaptab *
encap_attach_func(af, proto, func, psw, arg)
int af;
int proto;
int (*func) __P((const struct mbuf *, int, int, void *));
const struct protosw *psw;
void *arg;
{
struct encaptab *ep;
int error;
int s;
#if defined(__NetBSD__) || defined(__OpenBSD__)
s = splsoftnet();
#else
s = splnet();
#endif
/* sanity check on args */
if (!func) {
error = EINVAL;
goto fail;
}
error = encap_afcheck(af, NULL, NULL);
if (error)
goto fail;
ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /*XXX*/
if (ep == NULL) {
error = ENOBUFS;
goto fail;
}
bzero(ep, sizeof(*ep));
ep->af = af;
ep->proto = proto;
ep->func = func;
ep->psw = psw;
ep->arg = arg;
error = encap_add(ep);
if (error)
goto fail;
error = 0;
splx(s);
return ep;
fail:
splx(s);
return NULL;
}
/* XXX encap4_ctlinput() is necessary if we set DF=1 on outer IPv4 header */
#ifdef INET6
#if defined(HAVE_NRL_INPCB) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
#undef in6_rtchange
#define in6_rtchange in_rtchange
#define in6pcb inpcb
#endif
void
encap6_ctlinput(cmd, sa, d0)
int cmd;
struct sockaddr *sa;
void *d0;
{
void *d = d0;
struct ip6_hdr *ip6;
struct mbuf *m;
int off;
struct ip6ctlparam *ip6cp = NULL;
const struct sockaddr_in6 *sa6_src = NULL;
void *cmdarg;
int nxt;
struct encaptab *ep;
const struct ip6protosw *psw;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
if (cmd == PRC_HOSTDEAD)
d = NULL;
#if defined(__NetBSD__) || defined(__OpenBSD__)
else if (cmd == PRC_MSGSIZE)
; /* special code is present, see below */
#endif
else if (inet6ctlerrmap[cmd] == 0)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
cmdarg = ip6cp->ip6c_cmdarg;
sa6_src = ip6cp->ip6c_src;
nxt = ip6cp->ip6c_nxt;
} else {
m = NULL;
ip6 = NULL;
cmdarg = NULL;
sa6_src = &sa6_any;
nxt = -1;
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
if (ip6 && cmd == PRC_MSGSIZE) {
int valid = 0;
struct encaptab *match;
/*
* Check to see if we have a valid encap configuration.
*/
match = encap6_lookup(m, off, nxt, OUTBOUND);
if (match)
valid++;
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalcurate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
}
#endif
/* inform all listeners */
for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) {
if (ep->af != AF_INET6)
continue;
if (ep->proto >= 0 && ep->proto != nxt)
continue;
/* should optimize by looking at address pairs */
/* XXX need to pass ep->arg or ep itself to listeners */
psw = (const struct ip6protosw *)ep->psw;
if (psw && psw->pr_ctlinput)
(*psw->pr_ctlinput)(cmd, sa, d);
}
rip6_ctlinput(cmd, sa, d0);
}
#endif
int
encap_detach(cookie)
const struct encaptab *cookie;
{
const struct encaptab *ep = cookie;
struct encaptab *p;
int error;
for (p = LIST_FIRST(&encaptab); p; p = LIST_NEXT(p, chain)) {
if (p == ep) {
error = encap_remove(p);
if (error)
return error;
if (!ep->func) {
free(p->addrpack, M_NETADDR);
free(p->maskpack, M_NETADDR);
}
free(p, M_NETADDR); /*XXX*/
return 0;
}
}
return ENOENT;
}
#ifdef USE_RADIX
static struct radix_node_head *
encap_rnh(af)
int af;
{
switch (af) {
case AF_INET:
return encap_head[0];
#ifdef INET6
case AF_INET6:
return encap_head[1];
#endif
default:
return NULL;
}
}
static int
mask_matchlen(sa)
const struct sockaddr *sa;
{
const char *p, *ep;
int l;
p = (const char *)sa;
ep = p + sa->sa_len;
p += 2; /* sa_len + sa_family */
l = 0;
while (p < ep) {
l += (*p ? 8 : 0); /* estimate */
p++;
}
return l;
}
#endif
#ifndef USE_RADIX
static int
mask_match(ep, sp, dp)
const struct encaptab *ep;
const struct sockaddr *sp;
const struct sockaddr *dp;
{
struct sockaddr_storage s;
struct sockaddr_storage d;
int i;
const u_int8_t *p, *q;
u_int8_t *r;
int matchlen;
#ifdef DIAGNOSTIC
if (ep->func)
panic("wrong encaptab passed to mask_match");
#endif
if (sp->sa_len > sizeof(s) || dp->sa_len > sizeof(d))
return 0;
if (sp->sa_family != ep->af || dp->sa_family != ep->af)
return 0;
if (sp->sa_len != ep->src->sa_len || dp->sa_len != ep->dst->sa_len)
return 0;
matchlen = 0;
p = (const u_int8_t *)sp;
q = (const u_int8_t *)ep->srcmask;
r = (u_int8_t *)&s;
for (i = 0 ; i < sp->sa_len; i++) {
r[i] = p[i] & q[i];
/* XXX estimate */
matchlen += (q[i] ? 8 : 0);
}
p = (const u_int8_t *)dp;
q = (const u_int8_t *)ep->dstmask;
r = (u_int8_t *)&d;
for (i = 0 ; i < dp->sa_len; i++) {
r[i] = p[i] & q[i];
/* XXX rough estimate */
matchlen += (q[i] ? 8 : 0);
}
/* need to overwrite len/family portion as we don't compare them */
s.ss_len = sp->sa_len;
s.ss_family = sp->sa_family;
d.ss_len = dp->sa_len;
d.ss_family = dp->sa_family;
if (bcmp(&s, ep->src, ep->src->sa_len) == 0 &&
bcmp(&d, ep->dst, ep->dst->sa_len) == 0) {
return matchlen;
} else
return 0;
}
#endif
static void
encap_fillarg(m, ep)
struct mbuf *m;
const struct encaptab *ep;
{
struct mbuf *n;
n = m_aux_add(m, AF_INET, IPPROTO_IPV4);
if (n) {
*mtod(n, void **) = ep->arg;
n->m_len = sizeof(void *);
}
}
void *
encap_getarg(m)
struct mbuf *m;
{
void *p;
struct mbuf *n;
p = NULL;
n = m_aux_find(m, AF_INET, IPPROTO_IPV4);
if (n) {
if (n->m_len == sizeof(void *))
p = *mtod(n, void **);
m_aux_delete(m, n);
}
return p;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -