ip_encap.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,059 行 · 第 1/2 页
C
1,059 行
//==========================================================================//// src/sys/netinet/ip_encap.c////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD, // FreeBSD or other sources, and are covered by the appropriate// copyright disclaimers included herein.//// Portions created by Red Hat are// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================/* $KAME: ip_encap.c,v 1.73 2001/10/02 08:30:58 itojun Exp $ *//* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. *//* * My grandfather said that there's a devil inside tunnelling technology... * * We have surprisingly many protocols that want packets with IP protocol * #4 or #41. Here's a list of protocols that want protocol #41: * RFC1933 configured tunnel * RFC1933 automatic tunnel * RFC2401 IPsec tunnel * RFC2473 IPv6 generic packet tunnelling * RFC2529 6over4 tunnel * RFC3056 6to4 tunnel * isatap tunnel * mobile-ip6 (uses RFC2473) * Here's a list of protocol that want protocol #4: * RFC1853 IPv4-in-IPv4 tunnelling * RFC2003 IPv4 encapsulation within IPv4 * RFC2344 reverse tunnelling for mobile-ip4 * RFC2401 IPsec tunnel * Well, what can I say. They impose different en/decapsulation mechanism * from each other, so they need separate protocol handler. The only one * we can easily determine by protocol # is IPsec, which always has * AH/ESP/IPComp header right after outer IP header. * * So, clearly good old protosw does not work for protocol #4 and #41. * The code will let you match protocol via src/dst address pair. *//* XXX is M_NETADDR correct? *//* * With USE_RADIX the code will use radix table for tunnel lookup, for * tunnels registered with encap_attach() with a addr/mask pair. * Faster on machines with thousands of tunnel registerations (= interfaces). * * The code assumes that radix table code can handle non-continuous netmask, * as it will pass radix table memory region with (src + dst) sockaddr pair. * * FreeBSD is excluded here as they make max_keylen a static variable, and * thus forbid definition of radix table other than proper domains. */#include <sys/param.h>#include <sys/socket.h>#include <sys/sockio.h>#include <sys/mbuf.h>#include <sys/errno.h>#include <sys/protosw.h>#include <sys/queue.h>#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/ip_var.h>#include <netinet/ip_encap.h>#ifdef MROUTING#include <netinet/ip_mroute.h>#endif /* MROUTING */#ifdef __OpenBSD__#include <netinet/ip_ipsp.h>#endif#ifdef INET6#include <netinet/ip6.h>#include <netinet6/ip6_var.h>#include <netinet6/ip6protosw.h>#include <netinet6/in6_var.h>#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)#include <netinet/in_pcb.h>#else#include <netinet6/in6_pcb.h>#endif#include <netinet/icmp6.h>#endif#include <stdarg.h>#include <sys/malloc.h>/* to lookup a pair of address using radix tree */struct sockaddr_pack { u_int8_t sp_len; u_int8_t sp_family; /* not really used */ /* followed by variable-length data */} __attribute__((__packed__));struct pack4 { struct sockaddr_pack p; struct sockaddr_in mine; struct sockaddr_in yours;} __attribute__((__packed__));struct pack6 { struct sockaddr_pack p; struct sockaddr_in6 mine; struct sockaddr_in6 yours;} __attribute__((__packed__));enum direction { INBOUND, OUTBOUND };#ifdef INETstatic struct encaptab *encap4_lookup __P((struct mbuf *, int, int, enum direction));#endif#ifdef INET6static struct encaptab *encap6_lookup __P((struct mbuf *, int, int, enum direction));#endifstatic int encap_add __P((struct encaptab *));static int encap_remove __P((struct encaptab *));static int encap_afcheck __P((int, const struct sockaddr *, const struct sockaddr *));#ifdef USE_RADIXstatic struct radix_node_head *encap_rnh __P((int));static int mask_matchlen __P((const struct sockaddr *));#endif#ifndef USE_RADIXstatic int mask_match __P((const struct encaptab *, const struct sockaddr *, const struct sockaddr *));#endifstatic void encap_fillarg __P((struct mbuf *, const struct encaptab *));#ifndef LIST_HEAD_INITIALIZER/* rely upon BSS initialization */LIST_HEAD(, encaptab) encaptab;#elseLIST_HEAD(, encaptab) encaptab = LIST_HEAD_INITIALIZER(&encaptab);#endif#ifdef USE_RADIXextern int max_keylen; /* radix.c */struct radix_node_head *encap_head[2]; /* 0 for AF_INET, 1 for AF_INET6 */#endifvoidencap_init(){ static int initialized = 0; if (initialized) return; initialized++;#if 0 /* * we cannot use LIST_INIT() here, since drivers may want to call * encap_attach(), on driver attach. encap_init() will be called * on AF_INET{,6} initialization, which happens after driver * initialization - using LIST_INIT() here can nuke encap_attach() * from drivers. */ LIST_INIT(&encaptab);#endif#ifdef USE_RADIX /* * initialize radix lookup table. * max_keylen initialization should happen before the call to rn_init(). */ rn_inithead((void **)&encap_head[0], sizeof(struct sockaddr_pack) << 3); if (sizeof(struct pack4) > max_keylen) max_keylen = sizeof(struct pack4);#ifdef INET6 rn_inithead((void **)&encap_head[1], sizeof(struct sockaddr_pack) << 3); if (sizeof(struct pack6) > max_keylen) max_keylen = sizeof(struct pack6);#endif#endif}#ifdef INETstatic struct encaptab *encap4_lookup(m, off, proto, dir) struct mbuf *m; int off; int proto; enum direction dir;{ struct ip *ip; struct pack4 pack; struct encaptab *ep, *match; int prio, matchprio;#ifdef USE_RADIX struct radix_node_head *rnh = encap_rnh(AF_INET); struct radix_node *rn;#endif#ifdef DIAGNOSTIC if (m->m_len < sizeof(*ip)) panic("encap4_lookup");#endif ip = mtod(m, struct ip *); bzero(&pack, sizeof(pack)); pack.p.sp_len = sizeof(pack); pack.mine.sin_family = pack.yours.sin_family = AF_INET; pack.mine.sin_len = pack.yours.sin_len = sizeof(struct sockaddr_in); if (dir == INBOUND) { pack.mine.sin_addr = ip->ip_dst; pack.yours.sin_addr = ip->ip_src; } else { pack.mine.sin_addr = ip->ip_src; pack.yours.sin_addr = ip->ip_dst; } match = NULL; matchprio = 0;#ifdef USE_RADIX rn = rnh->rnh_matchaddr((caddr_t)&pack, rnh); if (rn && (rn->rn_flags & RNF_ROOT) == 0) { match = (struct encaptab *)rn; matchprio = mask_matchlen(match->srcmask) + mask_matchlen(match->dstmask); }#endif for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { if (ep->af != AF_INET) continue; if (ep->proto >= 0 && ep->proto != proto) continue; if (ep->func) prio = (*ep->func)(m, off, proto, ep->arg); else {#ifdef USE_RADIX continue;#else prio = mask_match(ep, (struct sockaddr *)&pack.mine, (struct sockaddr *)&pack.yours);#endif } /* * We prioritize the matches by using bit length of the * matches. mask_match() and user-supplied matching function * should return the bit length of the matches (for example, * if both src/dst are matched for IPv4, 64 should be returned). * 0 or negative return value means "it did not match". * * The question is, since we have two "mask" portion, we * cannot really define total order between entries. * For example, which of these should be preferred? * mask_match() returns 48 (32 + 16) for both of them. * src=3ffe::/16, dst=3ffe:501::/32 * src=3ffe:501::/32, dst=3ffe::/16 * * We need to loop through all the possible candidates * to get the best match - the search takes O(n) for * n attachments (i.e. interfaces). * * For radix-based lookup, I guess source takes precedence. * See rn_{refines,lexobetter} for the correct answer. */ if (prio <= 0) continue; if (prio > matchprio) { matchprio = prio; match = ep; } } return match;#undef s#undef d}void#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)encap4_input(struct mbuf *m, int off)#else#if __STDC__encap4_input(struct mbuf *m, ...)#elseencap4_input(m, va_alist) struct mbuf *m; va_dcl#endif#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */{#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4) int off, proto; va_list ap;#else int proto;#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */ const struct protosw *psw; struct encaptab *match;#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4) va_start(ap, m); off = va_arg(ap, int);#if !defined(__OpenBSD__) proto = va_arg(ap, int);#endif va_end(ap);#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 4) proto = mtod(m, struct ip *)->ip_p;#endif match = encap4_lookup(m, off, proto, INBOUND); if (match) { /* found a match, "match" has the best one */ psw = match->psw; if (psw && psw->pr_input) { encap_fillarg(m, match);#if defined(__FreeBSD__) && __FreeBSD__ >= 4 (*psw->pr_input)(m, off);#else (*psw->pr_input)(m, off, proto);#endif } else m_freem(m); return; } /* for backward compatibility - messy... */#if defined(__NetBSD__) /* last resort: inject to raw socket */ rip_input(m, off, proto);#elif defined(__OpenBSD__)# if defined(MROUTING) || defined(IPSEC) if (proto == IPPROTO_IPV4) { ip4_input(m, off, proto); return; }# endif /* last resort: inject to raw socket */ rip_input(m, off, proto);#elif (defined(__FreeBSD__) && __FreeBSD__ >= 5) /* last resort: inject to raw socket */ rip_input(m, off);#elif defined(__FreeBSD__) && __FreeBSD__ >= 4#ifdef MROUTING if (proto == IPPROTO_IPV4) { ipip_input(m, off); return; }#endif /* last resort: inject to raw socket */ rip_input(m, off);#else#ifdef MROUTING if (proto == IPPROTO_IPV4) { ipip_input(m, off, proto); return; }#endif /* last resort: inject to raw socket */ rip_input(m, off, proto);#endif}#endif#ifdef INET6static struct encaptab *encap6_lookup(m, off, proto, dir) struct mbuf *m; int off; int proto; enum direction dir;{ struct ip6_hdr *ip6; struct pack6 pack; int prio, matchprio; struct encaptab *ep, *match;#ifdef USE_RADIX struct radix_node_head *rnh = encap_rnh(AF_INET6); struct radix_node *rn;#endif#ifdef DIAGNOSTIC if (m->m_len < sizeof(*ip6)) panic("encap6_lookup");#endif ip6 = mtod(m, struct ip6_hdr *); bzero(&pack, sizeof(pack)); pack.p.sp_len = sizeof(pack); pack.mine.sin6_family = pack.yours.sin6_family = AF_INET6; pack.mine.sin6_len = pack.yours.sin6_len = sizeof(struct sockaddr_in6); if (dir == INBOUND) { pack.mine.sin6_addr = ip6->ip6_dst; pack.yours.sin6_addr = ip6->ip6_src; } else { pack.mine.sin6_addr = ip6->ip6_src; pack.yours.sin6_addr = ip6->ip6_dst; } match = NULL; matchprio = 0;#ifdef USE_RADIX rn = rnh->rnh_matchaddr((caddr_t)&pack, rnh); if (rn && (rn->rn_flags & RNF_ROOT) == 0) { match = (struct encaptab *)rn; matchprio = mask_matchlen(match->srcmask) + mask_matchlen(match->dstmask); }#endif for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { if (ep->af != AF_INET6) continue; if (ep->proto >= 0 && ep->proto != proto) continue; if (ep->func) prio = (*ep->func)(m, off, proto, ep->arg); else {#ifdef USE_RADIX continue;#else prio = mask_match(ep, (struct sockaddr *)&pack.mine, (struct sockaddr *)&pack.yours);#endif } /* see encap4_lookup() for issues here */ if (prio <= 0) continue; if (prio > matchprio) { matchprio = prio; match = ep; } } return match;#undef s#undef d}intencap6_input(mp, offp, proto) struct mbuf **mp; int *offp; int proto;{ struct mbuf *m = *mp; const struct ip6protosw *psw; struct encaptab *match; match = encap6_lookup(m, *offp, proto, INBOUND); if (match) { /* found a match */ psw = (const struct ip6protosw *)match->psw; if (psw && psw->pr_input) { encap_fillarg(m, match); return (*psw->pr_input)(mp, offp, proto); } else { m_freem(m); return IPPROTO_DONE; } }#ifdef __OpenBSD__ /* last resort */ return ip4_input6(mp, offp, 0); /* XXX last argument ignored */#else /* last resort: inject to raw socket */ return rip6_input(mp, offp, proto);#endif}#endifstatic intencap_add(ep) struct encaptab *ep;{#ifdef USE_RADIX struct radix_node_head *rnh = encap_rnh(ep->af);#endif int error = 0; LIST_INSERT_HEAD(&encaptab, ep, chain);#ifdef USE_RADIX if (!ep->func && rnh) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?