ip_encap.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,059 行 · 第 1/2 页

C
1,059
字号
		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 intencap_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 intencap_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#endifvoidencap6_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);}#endifintencap_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_RADIXstatic 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 intmask_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_RADIXstatic intmask_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;}#endifstatic voidencap_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 + =
减小字号Ctrl + -
显示快捷键?