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 + -
显示快捷键?