ip_output.c

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

C
1,761
字号
//==========================================================================////      sys/netinet/ip_output.c////     ////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD or other sources,// and are covered by the appropriate copyright disclaimers included herein.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================//#####DESCRIPTIONBEGIN####//// Author(s):    gthomas// Contributors: gthomas// Date:         2000-01-10// Purpose:      // Description:  //              ////####DESCRIPTIONEND####////==========================================================================/*	$OpenBSD: ip_output.c,v 1.57 1999/12/10 08:55:23 angelos Exp $	*//*	$NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $	*//* * Copyright (c) 1982, 1986, 1988, 1990, 1993 *	The Regents of the University of California.  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. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * *	@(#)ip_output.c	8.3 (Berkeley) 1/21/94 */#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/errno.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/socketvar.h>#ifndef __ECOS#include <sys/systm.h>#endif#include <sys/kernel.h>#ifndef __ECOS#include <sys/proc.h>#include <vm/vm.h>#include <sys/proc.h>#endif#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/in_pcb.h>#include <netinet/in_var.h>#include <netinet/ip_var.h>#ifdef vax#include <machine/mtpr.h>#endif#include <machine/stdarg.h>#ifdef IPSEC#include <netinet/ip_ah.h>#include <netinet/ip_esp.h>#include <netinet/udp.h>#include <netinet/tcp.h>#include <net/pfkeyv2.h>#ifdef ENCDEBUG#define DPRINTF(x)    do { if (encdebug) printf x ; } while (0)#else#define DPRINTF(x)#endif#ifndef offsetof#define offsetof(s, e) ((int)&((s *)0)->e)#endifextern u_int8_t get_sa_require  __P((struct inpcb *));#endif /* IPSEC */static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));static void ip_mloopback	__P((struct ifnet *, struct mbuf *, struct sockaddr_in *));#if defined(IPFILTER) || defined(IPFILTER_LKM)int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **));#endif#ifdef IPSECextern int ipsec_auth_default_level;extern int ipsec_esp_trans_default_level;extern int ipsec_esp_network_default_level;extern int pfkeyv2_acquire(struct tdb *, int);#endif#ifndef RAMDOM_IP_IDu_short ip_id;#endif/* * IP output.  The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */int#if __STDC__ip_output(struct mbuf *m0, ...)#elseip_output(m0, va_alist)	struct mbuf *m0;	va_dcl#endif{	register struct ip *ip, *mhip;	register struct ifnet *ifp;	register struct mbuf *m = m0;	register int hlen = sizeof (struct ip);	int len, off, error = 0;	struct route iproute;	struct sockaddr_in *dst;	struct in_ifaddr *ia;	struct mbuf *opt;	struct route *ro;	int flags;	struct ip_moptions *imo;	va_list ap;#ifdef IPSEC	union sockaddr_union sunion;	struct mbuf *mp;	struct udphdr *udp;	struct tcphdr *tcp;	struct inpcb *inp;	struct route_enc re0, *re = &re0;	struct sockaddr_encap *ddst, *gw;	u_int8_t sa_require, sa_have = 0;	struct tdb *tdb, *t;	int s, ip6flag;#ifdef INET6	struct ip6_hdr *ip6;#endif /* INET6 */#endif /* IPSEC */	va_start(ap, m0);	opt = va_arg(ap, struct mbuf *);	ro = va_arg(ap, struct route *);	flags = va_arg(ap, int);	imo = va_arg(ap, struct ip_moptions *);#ifdef IPSEC	inp = va_arg(ap, struct inpcb *);#endif /* IPSEC */	va_end(ap);#ifdef	DIAGNOSTIC	if ((m->m_flags & M_PKTHDR) == 0)		panic("ip_output no HDR");#endif	if (opt) {		m = ip_insertoptions(m, opt, &len);		hlen = len;	}	ip = mtod(m, struct ip *);	/*	 * Fill in IP header.	 */	if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) {		ip->ip_v = IPVERSION;		ip->ip_off &= IP_DF;#ifdef RANDOM_IP_ID		ip->ip_id = ip_randomid();#else		ip->ip_id = htons(ip_id++);#endif		ip->ip_hl = hlen >> 2;		ipstat.ips_localout++;	} else {		hlen = ip->ip_hl << 2;	}	/*	 * Route packet.	 */	if (ro == 0) {		ro = &iproute;		bzero((caddr_t)ro, sizeof (*ro));	}	dst = satosin(&ro->ro_dst);	/*	 * If there is a cached route,	 * check that it is to the same destination	 * and is still up.  If not, free it and try again.	 */	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||	    dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {		RTFREE(ro->ro_rt);		ro->ro_rt = (struct rtentry *)0;	}	if (ro->ro_rt == 0) {		dst->sin_family = AF_INET;		dst->sin_len = sizeof(*dst);		dst->sin_addr = ip->ip_dst;	}	/*	 * If routing to interface only,	 * short circuit routing lookup.	 */	if (flags & IP_ROUTETOIF) {		if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 &&		    (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) {			ipstat.ips_noroute++;			error = ENETUNREACH;			goto bad;		}		ifp = ia->ia_ifp;		ip->ip_ttl = 1;	} else {		if (ro->ro_rt == 0)			rtalloc(ro);		if (ro->ro_rt == 0) {			ipstat.ips_noroute++;			error = EHOSTUNREACH;			goto bad;		}		ia = ifatoia(ro->ro_rt->rt_ifa);		ifp = ro->ro_rt->rt_ifp;		ro->ro_rt->rt_use++;		if (ro->ro_rt->rt_flags & RTF_GATEWAY)			dst = satosin(ro->ro_rt->rt_gateway);	}	if (IN_MULTICAST(ip->ip_dst.s_addr)) {		struct in_multi *inm;		m->m_flags |= M_MCAST;		/*		 * IP destination address is multicast.  Make sure "dst"		 * still points to the address in "ro".  (It may have been		 * changed to point to a gateway address, above.)		 */		dst = satosin(&ro->ro_dst);		/*		 * See if the caller provided any multicast options		 */		if (imo != NULL) {			ip->ip_ttl = imo->imo_multicast_ttl;			if (imo->imo_multicast_ifp != NULL)				ifp = imo->imo_multicast_ifp;		} else			ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;		/*		 * Confirm that the outgoing interface supports multicast.		 */		if ((ifp->if_flags & IFF_MULTICAST) == 0) {			ipstat.ips_noroute++;			error = ENETUNREACH;			goto bad;		}		/*		 * If source address not specified yet, use address		 * of outgoing interface.		 */		if (ip->ip_src.s_addr == INADDR_ANY) {			register struct in_ifaddr *ia;			for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next)				if (ia->ia_ifp == ifp) {					ip->ip_src = ia->ia_addr.sin_addr;					break;				}		}		IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);		if (inm != NULL &&		   (imo == NULL || imo->imo_multicast_loop)) {			/*			 * If we belong to the destination multicast group			 * on the outgoing interface, and the caller did not			 * forbid loopback, loop back a copy.			 */			ip_mloopback(ifp, m, dst);		}#ifdef MROUTING		else {			/*			 * If we are acting as a multicast router, perform			 * multicast forwarding as if the packet had just			 * arrived on the interface to which we are about			 * to send.  The multicast forwarding function			 * recursively calls this function, using the			 * IP_FORWARDING flag to prevent infinite recursion.			 *			 * Multicasts that are looped back by ip_mloopback(),			 * above, will be forwarded by the ip_input() routine,			 * if necessary.			 */			extern struct socket *ip_mrouter;			if (ip_mrouter && (flags & IP_FORWARDING) == 0) {				if (ip_mforward(m, ifp) != 0) {					m_freem(m);					goto done;				}			}		}#endif		/*		 * Multicasts with a time-to-live of zero may be looped-		 * back, above, but must not be transmitted on a network.		 * Also, multicasts addressed to the loopback interface		 * are not sent -- the above call to ip_mloopback() will		 * loop back a copy if this host actually belongs to the		 * destination group on the loopback interface.		 */		if (ip->ip_ttl == 0 || (ifp->if_flags & IFF_LOOPBACK) != 0) {			m_freem(m);			goto done;		}		goto sendit;	}#ifndef notdef	/*	 * If source address not specified yet, use address	 * of outgoing interface.	 */	if (ip->ip_src.s_addr == INADDR_ANY)		ip->ip_src = ia->ia_addr.sin_addr;#endif	/*	 * Look for broadcast address and	 * and verify user is allowed to send	 * such a packet.	 */	if (in_broadcast(dst->sin_addr, ifp)) {		if ((ifp->if_flags & IFF_BROADCAST) == 0) {			error = EADDRNOTAVAIL;			goto bad;		}		if ((flags & IP_ALLOWBROADCAST) == 0) {			error = EACCES;			goto bad;		}		/* don't allow broadcast messages to be fragmented */		if ((u_int16_t)ip->ip_len > ifp->if_mtu) {			error = EMSGSIZE;			goto bad;		}		m->m_flags |= M_BCAST;	} else		m->m_flags &= ~M_BCAST;sendit:#ifdef IPSEC	/*	 * Check if the packet needs encapsulation.	 */	if (!(flags & IP_ENCAPSULATED) &&	    (inp == NULL || 	     inp->inp_seclevel[SL_AUTH] != IPSEC_LEVEL_BYPASS ||	     inp->inp_seclevel[SL_ESP_TRANS] != IPSEC_LEVEL_BYPASS ||	     inp->inp_seclevel[SL_ESP_NETWORK] != IPSEC_LEVEL_BYPASS)) {		if (inp == NULL)			sa_require = get_sa_require(inp);		else			sa_require = inp->inp_secrequire;		bzero((caddr_t) re, sizeof(*re));		/*		 * splnet is chosen over spltdb because we are not allowed to		 * lower the level, and udp_output calls us in splnet().		 */		s = splnet();		/*		 * Check if there was an outgoing SA bound to the flow		 * from a transport protocol.		 */		if (inp && inp->inp_tdb &&		    (inp->inp_tdb->tdb_dst.sin.sin_addr.s_addr == INADDR_ANY ||		     !bcmp(&inp->inp_tdb->tdb_dst.sin.sin_addr,			   &ip->ip_dst, sizeof(ip->ip_dst)))) {			tdb = inp->inp_tdb;			goto have_tdb;		}		if (!ipsec_in_use) {			splx(s);			goto no_encap;		}		ddst = (struct sockaddr_encap *) &re->re_dst;		ddst->sen_family = PF_KEY;		ddst->sen_len = SENT_IP4_LEN;		ddst->sen_type = SENT_IP4;		ddst->sen_ip_src = ip->ip_src;		ddst->sen_ip_dst = ip->ip_dst;		ddst->sen_proto = ip->ip_p;		switch (ip->ip_p) {		case IPPROTO_UDP:			if (m->m_len < hlen + 2 * sizeof(u_int16_t)) {				if ((m = m_pullup(m, hlen + 2 *				    sizeof(u_int16_t))) == 0)					return ENOBUFS;				ip = mtod(m, struct ip *);			}			udp = (struct udphdr *) (mtod(m, u_char *) + hlen);			ddst->sen_sport = ntohs(udp->uh_sport);			ddst->sen_dport = ntohs(udp->uh_dport);			break;		case IPPROTO_TCP:			if (m->m_len < hlen + 2 * sizeof(u_int16_t)) {				if ((m = m_pullup(m, hlen + 2 *				    sizeof(u_int16_t))) == 0)					return ENOBUFS;				ip = mtod(m, struct ip *);			}			tcp = (struct tcphdr *) (mtod(m, u_char *) + hlen);			ddst->sen_sport = ntohs(tcp->th_sport);			ddst->sen_dport = ntohs(tcp->th_dport);			break;		default:			ddst->sen_sport = 0;			ddst->sen_dport = 0;		}		rtalloc((struct route *) re);		if (re->re_rt == NULL) {			splx(s);			goto no_encap;		}		gw = (struct sockaddr_encap *) (re->re_rt->rt_gateway);		/* Sanity check */		if (gw == NULL || ((gw->sen_type != SENT_IPSP) &&				   (gw->sen_type != SENT_IPSP6))) {			splx(s);		        DPRINTF(("ip_output(): no gw or gw data not IPSP\n"));			if (re->re_rt)				RTFREE(re->re_rt);			error = EHOSTUNREACH;			m_freem(m);			goto done;		}		/*		 * There might be a specific route, that tells us to avoid		 * doing IPsec; this is useful for specific routes that we		 * don't want to have IPsec applied on, like the key		 * management ports.		 */		if ((gw != NULL) && (gw->sen_ipsp_sproto == 0) &&		    (gw->sen_ipsp_spi == 0)) {		    if ((gw->sen_family == AF_INET) &&			(gw->sen_ipsp_dst.s_addr == 0)) {			splx(s);			goto no_encap;		    }#ifdef INET6		    if ((gw->sen_family == AF_INET6) &&			IN6_IS_ADDR_UNSPECIFIED(&gw->sen_ipsp6_dst)) {			splx(s);			goto no_encap;		    }#endif /* INET6 */		}		/*		 * At this point we have an IPSP "gateway" (tunnel) spec.		 * Use the destination of the tunnel and the SPI to		 * look up the necessary Tunnel Control Block. Look it up,		 * and then pass it, along with the packet and the gw,		 * to the appropriate transformation.		 */		bzero(&sunion, sizeof(sunion));		if (gw->sen_type == SENT_IPSP) {		    sunion.sin.sin_family = AF_INET;		    sunion.sin.sin_len = sizeof(struct sockaddr_in);		    sunion.sin.sin_addr = gw->sen_ipsp_dst;		}#ifdef INET6		if (gw->sen_type == SENT_IPSP6) {		    sunion.sin6.sin6_family = AF_INET6;		    sunion.sin6.sin6_len = sizeof(struct sockaddr_in6);		    sunion.sin6.sin6_addr = gw->sen_ipsp6_dst;		}#endif /* INET6 */		tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion,					    gw->sen_ipsp_sproto);		/* 		 * For VPNs a route with a reserved SPI is used to		 * indicate the need for an SA when none is established.		 */		if (((ntohl(gw->sen_ipsp_spi) == SPI_LOCAL_USE) &&		     (gw->sen_type == SENT_IPSP)) ||		    ((ntohl(gw->sen_ipsp6_spi) == SPI_LOCAL_USE) &&		     (gw->sen_type == SENT_IPSP6))) {		    if (tdb == NULL) {			/*			 * XXX We should construct a TDB from system			 * default (which should be tunable via sysctl).			 * For now, drop packet and ignore SPD entry.			 */			splx(s);			goto no_encap;		    }		    else {			if (tdb->tdb_authalgxform)			  sa_require = NOTIFY_SATYPE_AUTH;			if (tdb->tdb_encalgxform)			  sa_require |= NOTIFY_SATYPE_CONF;			if (tdb->tdb_flags & TDBF_TUNNELING)			  sa_require |= NOTIFY_SATYPE_TUNNEL;		    }		    /* PF_KEYv2 notification message */		    if (tdb && tdb->tdb_satype != SADB_X_SATYPE_BYPASS)		            if ((error = pfkeyv2_acquire(tdb, 0)) != 0)			            return error;		    splx(s);		    /* 		     * When sa_require is set, the packet will be dropped		     * at no_encap.		     */		    goto no_encap;		}	     have_tdb:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?