ip_output.c

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

C
1,972
字号
	ip->ip_sum = 0;	/*	 * XXX	 * delayed checksums are not currently compatible with IPsec	 */	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {		in_delayed_cksum(m);		m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;	}	HTONS(ip->ip_len);	HTONS(ip->ip_off);	error = ipsec4_output(&state, sp, flags);	m = state.m;	if (flags & IP_ROUTETOIF) {		/*		 * if we have tunnel mode SA, we may need to ignore		 * IP_ROUTETOIF.		 */		if (state.ro != &iproute || state.ro->ro_rt != NULL) {			flags &= ~IP_ROUTETOIF;			ro = state.ro;		}	} else		ro = state.ro;	dst = (struct sockaddr_in *)state.dst;	if (error) {		/* mbuf is already reclaimed in ipsec4_output. */		m0 = NULL;		switch (error) {		case EHOSTUNREACH:		case ENETUNREACH:		case EMSGSIZE:		case ENOBUFS:		case ENOMEM:			break;		default:			printf("ip4_output (ipsec): error code %d\n", error);			/*fall through*/		case ENOENT:			/* don't show these error codes to the user */			error = 0;			break;		}		goto bad;	}	/* be sure to update variables that are affected by ipsec4_output() */	ip = mtod(m, struct ip *);#ifdef _IP_VHL	hlen = IP_VHL_HL(ip->ip_vhl) << 2;#else	hlen = ip->ip_hl << 2;#endif	if (ro->ro_rt == NULL) {		if ((flags & IP_ROUTETOIF) == 0) {			printf("ip_output: "				"can't update route after IPsec processing\n");			error = EHOSTUNREACH;	/*XXX*/			goto bad;		}	} else {		if (state.encap) {			ia = ifatoia(ro->ro_rt->rt_ifa);			ifp = ro->ro_rt->rt_ifp;		}	}    }	/* make it flipped, again. */	NTOHS(ip->ip_len);	NTOHS(ip->ip_off);skip_ipsec:#endif /*IPSEC*/	/*	 * IpHack's section.	 * - Xlate: translate packet's addr/port (NAT).	 * - Firewall: deny/allow/etc.	 * - Wrap: fake packet's addr/port <unimpl.>	 * - Encapsulate: put it in another IP and send out. <unimp.>	 */ 	if (fr_checkp) {		struct  mbuf    *m1 = m;		if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1)			goto done;		ip = mtod(m = m1, struct ip *);	}	/*	 * Check with the firewall...	 */	if (fw_enable && ip_fw_chk_ptr) {		struct sockaddr_in *old = dst;		off = (*ip_fw_chk_ptr)(&ip,		    hlen, ifp, &divert_cookie, &m, &rule, &dst);                /*                 * On return we must do the following:                 * IP_FW_PORT_DENY_FLAG		-> drop the pkt (XXX new)                 * 1<=off<= 0xffff   -> DIVERT                 * (off & IP_FW_PORT_DYNT_FLAG)	-> send to a DUMMYNET pipe                 * (off & IP_FW_PORT_TEE_FLAG)	-> TEE the packet                 * dst != old        -> IPFIREWALL_FORWARD                 * off==0, dst==old  -> accept                 * If some of the above modules is not compiled in, then                 * we should't have to check the corresponding condition                 * (because the ipfw control socket should not accept                 * unsupported rules), but better play safe and drop                 * packets in case of doubt.                 */		if ( (off & IP_FW_PORT_DENY_FLAG) || m == NULL) {			if (m)				m_freem(m);			error = EACCES ;			goto done ;		}		ip = mtod(m, struct ip *);		if (off == 0 && dst == old) /* common case */			goto pass ;#ifdef DUMMYNET                if ((off & IP_FW_PORT_DYNT_FLAG) != 0) {                    /*                     * pass the pkt to dummynet. Need to include                     * pipe number, m, ifp, ro, dst because these are                     * not recomputed in the next pass.                     * All other parameters have been already used and                     * so they are not needed anymore.                      * XXX note: if the ifp or ro entry are deleted                     * while a pkt is in dummynet, we are in trouble!                     */ 		    error = dummynet_io(off & 0xffff, DN_TO_IP_OUT, m,				ifp,ro,dst,rule, flags);		    goto done;		}#endif   #ifdef IPDIVERT		if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) {			struct mbuf *clone = NULL;			/* Clone packet if we're doing a 'tee' */			if ((off & IP_FW_PORT_TEE_FLAG) != 0)				clone = m_dup(m, M_DONTWAIT);			/*			 * XXX			 * delayed checksums are not currently compatible			 * with divert sockets.			 */			if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {				in_delayed_cksum(m);				m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;			}			/* Restore packet header fields to original values */			HTONS(ip->ip_len);			HTONS(ip->ip_off);			/* Deliver packet to divert input routine */			ip_divert_cookie = divert_cookie;			divert_packet(m, 0, off & 0xffff);			/* If 'tee', continue with original packet */			if (clone != NULL) {				m = clone;				ip = mtod(m, struct ip *);				goto pass;			}			goto done;		}#endif#ifdef IPFIREWALL_FORWARD		/* Here we check dst to make sure it's directly reachable on the		 * interface we previously thought it was.		 * If it isn't (which may be likely in some situations) we have		 * to re-route it (ie, find a route for the next-hop and the		 * associated interface) and set them here. This is nested		 * forwarding which in most cases is undesirable, except where		 * such control is nigh impossible. So we do it here.		 * And I'm babbling.		 */		if (off == 0 && old != dst) {			struct in_ifaddr *ia;			/* It's changed... */			/* There must be a better way to do this next line... */			static struct route sro_fwd, *ro_fwd = &sro_fwd;#ifdef IPFIREWALL_FORWARD_DEBUG			printf("IPFIREWALL_FORWARD: New dst ip: ");			print_ip(dst->sin_addr);			printf("\n");#endif			/*			 * We need to figure out if we have been forwarded			 * to a local socket. If so then we should somehow 			 * "loop back" to ip_input, and get directed to the			 * PCB as if we had received this packet. This is			 * because it may be dificult to identify the packets			 * you want to forward until they are being output			 * and have selected an interface. (e.g. locally			 * initiated packets) If we used the loopback inteface,			 * we would not be able to control what happens 			 * as the packet runs through ip_input() as			 * it is done through a ISR.			 */			TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {				/*				 * If the addr to forward to is one				 * of ours, we pretend to				 * be the destination for this packet.				 */				if (IA_SIN(ia)->sin_addr.s_addr ==						 dst->sin_addr.s_addr)					break;			}			if (ia) {				/* tell ip_input "dont filter" */				ip_fw_fwd_addr = dst;				if (m->m_pkthdr.rcvif == NULL)					m->m_pkthdr.rcvif = ifunit("lo0");				if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {					m->m_pkthdr.csum_flags |=					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;					m0->m_pkthdr.csum_data = 0xffff;				}				m->m_pkthdr.csum_flags |=				    CSUM_IP_CHECKED | CSUM_IP_VALID;				HTONS(ip->ip_len);				HTONS(ip->ip_off);				ip_input(m);				goto done;			}			/* Some of the logic for this was			 * nicked from above.			 *			 * This rewrites the cached route in a local PCB.			 * Is this what we want to do?			 */			bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst));			ro_fwd->ro_rt = 0;			rtalloc_ign(ro_fwd, RTF_PRCLONING);			if (ro_fwd->ro_rt == 0) {				ipstat.ips_noroute++;				error = EHOSTUNREACH;				goto bad;			}			ia = ifatoia(ro_fwd->ro_rt->rt_ifa);			ifp = ro_fwd->ro_rt->rt_ifp;			ro_fwd->ro_rt->rt_use++;			if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY)				dst = (struct sockaddr_in *)ro_fwd->ro_rt->rt_gateway;			if (ro_fwd->ro_rt->rt_flags & RTF_HOST)				isbroadcast =				    (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST);			else				isbroadcast = in_broadcast(dst->sin_addr, ifp);			RTFREE(ro->ro_rt);			ro->ro_rt = ro_fwd->ro_rt;			dst = (struct sockaddr_in *)&ro_fwd->ro_dst;			/*			 * If we added a default src ip earlier,			 * which would have been gotten from the-then			 * interface, do it again, from the new one.			 */			if (fwd_rewrite_src)				ip->ip_src = IA_SIN(ia)->sin_addr;			goto pass ;		}#endif /* IPFIREWALL_FORWARD */                /*                 * if we get here, none of the above matches, and                  * we have to drop the pkt                 */		m_freem(m);                error = EACCES; /* not sure this is the right error msg */                goto done;	}pass:	m->m_pkthdr.csum_flags |= CSUM_IP;	sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;	if (sw_csum & CSUM_DELAY_DATA) {		in_delayed_cksum(m);		sw_csum &= ~CSUM_DELAY_DATA;	}	m->m_pkthdr.csum_flags &= ifp->if_hwassist;	/*	 * If small enough for interface, or the interface will take	 * care of the fragmentation for us, can just send directly.	 */	if ((u_short)ip->ip_len <= ifp->if_mtu ||	    ifp->if_hwassist & CSUM_FRAGMENT) {		HTONS(ip->ip_len);		HTONS(ip->ip_off);		ip->ip_sum = 0;		if (sw_csum & CSUM_DELAY_IP) {			if (ip->ip_vhl == IP_VHL_BORING) {				ip->ip_sum = in_cksum_hdr(ip);			} else {				ip->ip_sum = in_cksum(m, hlen);			}		}		/* Record statistics for this interface address. */		if (!(flags & IP_FORWARDING) && ia != NULL) {			ia->ia_ifa.if_opackets++;			ia->ia_ifa.if_obytes += m->m_pkthdr.len;		}#ifdef IPSEC		/* clean ipsec history once it goes out of the node */		ipsec_delaux(m);#endif		error = (*ifp->if_output)(ifp, m,				(struct sockaddr *)dst, ro->ro_rt);		goto done;	}	/*	 * Too large for interface; fragment if possible.	 * Must be able to put at least 8 bytes per fragment.	 */	if (ip->ip_off & IP_DF) {		error = EMSGSIZE;		/*		 * This case can happen if the user changed the MTU		 * of an interface after enabling IP on it.  Because		 * most netifs don't keep track of routes pointing to		 * them, there is no way for one to update all its		 * routes when the MTU is changed.		 */		if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST))		    && !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU)		    && (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) {			ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu;		}		ipstat.ips_cantfrag++;		goto bad;	}	len = (ifp->if_mtu - hlen) &~ 7;	if (len < 8) {		error = EMSGSIZE;		goto bad;	}	/*	 * if the interface will not calculate checksums on	 * fragmented packets, then do it here.	 */	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA &&	    (ifp->if_hwassist & CSUM_IP_FRAGS) == 0) {		in_delayed_cksum(m);		m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;	}    {	int mhlen, firstlen = len;	struct mbuf **mnext = &m->m_nextpkt;	int nfrags = 1;	/*	 * Loop through length of segment after first fragment,	 * make new header and copy data of each part and link onto chain.	 */	m0 = m;	mhlen = sizeof (struct ip);	for (off = hlen + len; off < (u_short)ip->ip_len; off += len) {		MGETHDR(m, M_DONTWAIT, MT_HEADER);		if (m == 0) {			error = ENOBUFS;			ipstat.ips_odropped++;			goto sendorfree;		}		m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG;		m->m_data += max_linkhdr;		mhip = mtod(m, struct ip *);		*mhip = *ip;		if (hlen > sizeof (struct ip)) {			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);			mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2);		}		m->m_len = mhlen;		mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);		if (ip->ip_off & IP_MF)			mhip->ip_off |= IP_MF;		if (off + len >= (u_short)ip->ip_len)			len = (u_short)ip->ip_len - off;		else			mhip->ip_off |= IP_MF;		mhip->ip_len = htons((u_short)(len + mhlen));		m->m_next = m_copy(m0, off, len);		if (m->m_next == 0) {			(void) m_free(m);			error = ENOBUFS;	/* ??? */			ipstat.ips_odropped++;			goto sendorfree;		}		m->m_pkthdr.len = mhlen + len;		m->m_pkthdr.rcvif = (struct ifnet *)0;		m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags;		HTONS(mhip->ip_off);		mhip->ip_sum = 0;		if (sw_csum & CSUM_DELAY_IP) {			if (mhip->ip_vhl == IP_VHL_BORING) {				mhip->ip_sum = in_cksum_hdr(mhip);			} else {				mhip->ip_sum = in_cksum(m, mhlen);			}		}		*mnext = m;		mnext = &m->m_nextpkt;		nfrags++;	}	ipstat.ips_ofragments += nfrags;	/* set first/last markers for fragment chain */	m->m_flags |= M_LASTFRAG;	m0->m_flags |= M_FIRSTFRAG | M_FRAG;	m0->m_pkthdr.csum_data = nfrags;	/*	 * Update first fragment by trimming what's been copied out	 * and updating header, then send each fragment (in order).	 */	m = m0;	m_adj(m, hlen + firstlen - (u_short)ip->ip_len);	m->m_pkthdr.len = hlen + firstlen;	ip->ip_len = htons((u_short)m->m_pkthdr.len);	ip->ip_off |= IP_MF;	HTONS(ip->ip_off);	ip->ip_sum = 0;	if (sw_csum & CSUM_DELAY_IP) {		if (ip->ip_vhl == IP_VHL_BORING) {			ip->ip_sum = in_cksum_hdr(ip);		} else {			ip->ip_sum = in_cksum(m, hlen);		}	}sendorfree:	for (m = m0; m; m = m0) {		m0 = m->m_nextpkt;		m->m_nextpkt = 0;#ifdef IPSEC		/* clean ipsec history once it goes out of the node */		ipsec_delaux(m);#endif		if (error == 0) {			/* Record statistics for this interface address. */			if (ia != NULL) {				ia->ia_ifa.if_opackets++;				ia->ia_ifa.if_obytes += m->m_pkthdr.len;			}						error = (*ifp->if_output)(ifp, m,			    (struct sockaddr *)dst, ro->ro_rt);		} else			m_freem(m);	}	if (error == 0)		ipstat.ips_fragmented++;    }done:#ifdef IPSEC	if (ro == &iproute && ro->ro_rt) {		RTFREE(ro->ro_rt);		ro->ro_rt = NULL;	}	if (sp != NULL) {		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,			printf("DP ip_output call free SP:%p\n", sp));		key_freesp(sp);	}#endif /* IPSEC */	return (error);bad:	m_freem(m0);	goto done;}voidin_delayed_cksum(struct mbuf *m){

⌨️ 快捷键说明

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