⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip_output.c

📁 ARM 嵌入式 系统 设计与实例开发 实验教材 二源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * INET		An implementation of the TCP/IP protocol suite for the LINUX *		operating system.  INET is implemented using the  BSD Socket *		interface as the means of communication with the user level. * *		The Internet Protocol (IP) output module. * * Version:	$Id: ip_output.c,v 1.99 2001/10/15 12:34:50 davem Exp $ * * Authors:	Ross Biro, <bir7@leland.Stanford.Edu> *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> *		Donald Becker, <becker@super.org> *		Alan Cox, <Alan.Cox@linux.org> *		Richard Underwood *		Stefan Becker, <stefanb@yello.ping.de> *		Jorge Cwik, <jorge@laser.satlink.net> *		Arnt Gulbrandsen, <agulbra@nvg.unit.no> * *	See ip_input.c for original log * *	Fixes: *		Alan Cox	:	Missing nonblock feature in ip_build_xmit. *		Mike Kilburn	:	htons() missing in ip_build_xmit. *		Bradford Johnson:	Fix faulty handling of some frames when  *					no route is found. *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit *					(in case if packet not accepted by *					output firewall rules) *		Mike McLagan	:	Routing by source *		Alexey Kuznetsov:	use new route cache *		Andi Kleen:		Fix broken PMTU recovery and remove *					some redundant tests. *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma. *		Andi Kleen	: 	Replace ip_reply with ip_send_reply. *		Andi Kleen	:	Split fast and slow ip_build_xmit path  *					for decreased register pressure on x86  *					and more readibility.  *		Marc Boucher	:	When call_out_firewall returns FW_QUEUE, *					silently drop skb instead of failing with -EPERM. *		Detlev Wengorz	:	Copy protocol for fragments. */#include <asm/uaccess.h>#include <asm/system.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/config.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <net/snmp.h>#include <net/ip.h>#include <net/protocol.h>#include <net/route.h>#include <net/tcp.h>#include <net/udp.h>#include <linux/skbuff.h>#include <net/sock.h>#include <net/arp.h>#include <net/icmp.h>#include <net/raw.h>#include <net/checksum.h>#include <net/inetpeer.h>#include <linux/igmp.h>#include <linux/netfilter_ipv4.h>#include <linux/mroute.h>#include <linux/netlink.h>/* *      Shall we try to damage output packets if routing dev changes? */int sysctl_ip_dynaddr = 0;int sysctl_ip_default_ttl = IPDEFTTL;/* Generate a checksum for an outgoing IP datagram. */__inline__ void ip_send_check(struct iphdr *iph){	iph->check = 0;	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);}/* dev_loopback_xmit for use with netfilter. */static int ip_dev_loopback_xmit(struct sk_buff *newskb){	newskb->mac.raw = newskb->data;	__skb_pull(newskb, newskb->nh.raw - newskb->data);	newskb->pkt_type = PACKET_LOOPBACK;	newskb->ip_summed = CHECKSUM_UNNECESSARY;	BUG_TRAP(newskb->dst);#ifdef CONFIG_NETFILTER_DEBUG	nf_debug_ip_loopback_xmit(newskb);#endif	netif_rx(newskb);	return 0;}/* Don't just hand NF_HOOK skb->dst->output, in case netfilter hook   changes route */static inline intoutput_maybe_reroute(struct sk_buff *skb){	return skb->dst->output(skb);}/*  *		Add an ip header to a skbuff and send it out. */int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,			  u32 saddr, u32 daddr, struct ip_options *opt){	struct rtable *rt = (struct rtable *)skb->dst;	struct iphdr *iph;	/* Build the IP header. */	if (opt)		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);	else		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));	iph->version  = 4;	iph->ihl      = 5;	iph->tos      = sk->protinfo.af_inet.tos;	iph->frag_off = 0;	if (ip_dont_fragment(sk, &rt->u.dst))		iph->frag_off |= htons(IP_DF);	iph->ttl      = sk->protinfo.af_inet.ttl;	iph->daddr    = rt->rt_dst;	iph->saddr    = rt->rt_src;	iph->protocol = sk->protocol;	iph->tot_len  = htons(skb->len);	ip_select_ident(iph, &rt->u.dst, sk);	skb->nh.iph   = iph;	if (opt && opt->optlen) {		iph->ihl += opt->optlen>>2;		ip_options_build(skb, opt, daddr, rt, 0);	}	ip_send_check(iph);	/* Send it out. */	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,		       output_maybe_reroute);}static inline int ip_finish_output2(struct sk_buff *skb){	struct dst_entry *dst = skb->dst;	struct hh_cache *hh = dst->hh;#ifdef CONFIG_NETFILTER_DEBUG	nf_debug_ip_finish_output2(skb);#endif /*CONFIG_NETFILTER_DEBUG*/	if (hh) {		read_lock_bh(&hh->hh_lock);  		memcpy(skb->data - 16, hh->hh_data, 16);		read_unlock_bh(&hh->hh_lock);	        skb_push(skb, hh->hh_len);		return hh->hh_output(skb);	} else if (dst->neighbour)		return dst->neighbour->output(skb);	if (net_ratelimit())		printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");	kfree_skb(skb);	return -EINVAL;}__inline__ int ip_finish_output(struct sk_buff *skb){	struct net_device *dev = skb->dst->dev;	skb->dev = dev;	skb->protocol = __constant_htons(ETH_P_IP);	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,		       ip_finish_output2);}int ip_mc_output(struct sk_buff *skb){	struct sock *sk = skb->sk;	struct rtable *rt = (struct rtable*)skb->dst;	struct net_device *dev = rt->u.dst.dev;	/*	 *	If the indicated interface is up and running, send the packet.	 */	IP_INC_STATS(IpOutRequests);#ifdef CONFIG_IP_ROUTE_NAT	if (rt->rt_flags & RTCF_NAT)		ip_do_nat(skb);#endif	skb->dev = dev;	skb->protocol = __constant_htons(ETH_P_IP);	/*	 *	Multicasts are looped back for other local users	 */	if (rt->rt_flags&RTCF_MULTICAST) {		if ((!sk || sk->protinfo.af_inet.mc_loop)#ifdef CONFIG_IP_MROUTE		/* Small optimization: do not loopback not local frames,		   which returned after forwarding; they will be  dropped		   by ip_mr_input in any case.		   Note, that local frames are looped back to be delivered		   to local recipients.		   This check is duplicated in ip_mr_input at the moment.		 */		    && ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))#endif		) {			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);			if (newskb)				NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,					newskb->dev, 					ip_dev_loopback_xmit);		}		/* Multicasts with ttl 0 must not go beyond the host */		if (skb->nh.iph->ttl == 0) {			kfree_skb(skb);			return 0;		}	}	if (rt->rt_flags&RTCF_BROADCAST) {		struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);		if (newskb)			NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,				newskb->dev, ip_dev_loopback_xmit);	}	return ip_finish_output(skb);}int ip_output(struct sk_buff *skb){#ifdef CONFIG_IP_ROUTE_NAT	struct rtable *rt = (struct rtable*)skb->dst;#endif	IP_INC_STATS(IpOutRequests);#ifdef CONFIG_IP_ROUTE_NAT	if (rt->rt_flags&RTCF_NAT)		ip_do_nat(skb);#endif	return ip_finish_output(skb);}/* Queues a packet to be sent, and starts the transmitter if necessary.   * This routine also needs to put in the total length and compute the  * checksum.  We use to do this in two stages, ip_build_header() then * this, but that scheme created a mess when routes disappeared etc. * So we do it all here, and the TCP send engine has been changed to * match. (No more unroutable FIN disasters, etc. wheee...)  This will * most likely make other reliable transport layers above IP easier * to implement under Linux. */static inline int ip_queue_xmit2(struct sk_buff *skb){	struct sock *sk = skb->sk;	struct rtable *rt = (struct rtable *)skb->dst;	struct net_device *dev;	struct iphdr *iph = skb->nh.iph;	dev = rt->u.dst.dev;	/* This can happen when the transport layer has segments queued	 * with a cached route, and by the time we get here things are	 * re-routed to a device with a different MTU than the original	 * device.  Sick, but we must cover it.	 */	if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {		struct sk_buff *skb2;		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);		kfree_skb(skb);		if (skb2 == NULL)			return -ENOMEM;		if (sk)			skb_set_owner_w(skb2, sk);		skb = skb2;		iph = skb->nh.iph;	}	if (skb->len > rt->u.dst.pmtu)		goto fragment;	if (ip_dont_fragment(sk, &rt->u.dst))		iph->frag_off |= __constant_htons(IP_DF);	ip_select_ident(iph, &rt->u.dst, sk);	/* Add an IP checksum. */	ip_send_check(iph);	skb->priority = sk->priority;	return skb->dst->output(skb);fragment:	if (ip_dont_fragment(sk, &rt->u.dst)) {		/* Reject packet ONLY if TCP might fragment		 * it itself, if were careful enough.		 */		iph->frag_off |= __constant_htons(IP_DF);		NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,			  htonl(rt->u.dst.pmtu));		kfree_skb(skb);		return -EMSGSIZE;	}	ip_select_ident(iph, &rt->u.dst, sk);	if (skb->ip_summed == CHECKSUM_HW &&	    (skb = skb_checksum_help(skb)) == NULL)		return -ENOMEM;	return ip_fragment(skb, skb->dst->output);}int ip_queue_xmit(struct sk_buff *skb){	struct sock *sk = skb->sk;	struct ip_options *opt = sk->protinfo.af_inet.opt;	struct rtable *rt;	struct iphdr *iph;	/* Skip all of this if the packet is already routed,	 * f.e. by something like SCTP.	 */	rt = (struct rtable *) skb->dst;	if (rt != NULL)		goto packet_routed;	/* Make sure we can route this packet. */	rt = (struct rtable *)__sk_dst_check(sk, 0);	if (rt == NULL) {		u32 daddr;		/* Use correct destination address if we have options. */		daddr = sk->daddr;		if(opt && opt->srr)			daddr = opt->faddr;		/* If this fails, retransmit mechanism of transport layer will		 * keep trying until route appears or the connection times itself		 * out.		 */		if (ip_route_output(&rt, daddr, sk->saddr,				    RT_CONN_FLAGS(sk),				    sk->bound_dev_if))			goto no_route;		__sk_dst_set(sk, &rt->u.dst);		sk->route_caps = rt->u.dst.dev->features;	}	skb->dst = dst_clone(&rt->u.dst);packet_routed:	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)		goto no_route;	/* OK, we know where to send it, allocate and build IP header. */	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff));	iph->tot_len = htons(skb->len);	iph->frag_off = 0;	iph->ttl      = sk->protinfo.af_inet.ttl;	iph->protocol = sk->protocol;	iph->saddr    = rt->rt_src;	iph->daddr    = rt->rt_dst;	skb->nh.iph   = iph;	/* Transport layer set skb->h.foo itself. */	if(opt && opt->optlen) {		iph->ihl += opt->optlen >> 2;		ip_options_build(skb, opt, sk->daddr, rt, 0);	}	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,		       ip_queue_xmit2);no_route:	IP_INC_STATS(IpOutNoRoutes);	kfree_skb(skb);	return -EHOSTUNREACH;}/* *	Build and send a packet, with as little as one copy * *	Doesn't care much about ip options... option length can be *	different for fragment at 0 and other fragments. * *	Note that the fragment at the highest offset is sent first, *	so the getfrag routine can fill in the TCP/UDP checksum header *	field in the last fragment it sends... actually it also helps * 	the reassemblers, they can put most packets in at the head of *	the fragment queue, and they know the total size in advance. This *	last feature will measurably improve the Linux fragment handler one *	day. * *	The callback has five args, an arbitrary pointer (copy of frag), *	the source IP address (may depend on the routing table), the  *	destination address (char *), the offset to copy from, and the *	length to be copied. */static int ip_build_xmit_slow(struct sock *sk,		  int getfrag (const void *,			       char *,			       unsigned int,				       unsigned int),		  const void *frag,		  unsigned length,		  struct ipcm_cookie *ipc,		  struct rtable *rt,		  int flags){	unsigned int fraglen, maxfraglen, fragheaderlen;	int err;	int offset, mf;	int mtu;	u16 id;	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;	int nfrags=0;	struct ip_options *opt = ipc->opt;	int df = 0;	mtu = rt->u.dst.pmtu;	if (ip_dont_fragment(sk, &rt->u.dst))		df = htons(IP_DF);	length -= sizeof(struct iphdr);	if (opt) {		fragheaderlen = sizeof(struct iphdr) + opt->optlen;		maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;	} else {		fragheaderlen = sizeof(struct iphdr);		/*		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work		 *	out the size of the frames to send.		 */		maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;	}	if (length + fragheaderlen > 0xFFFF) {		ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);		return -EMSGSIZE;	}	/*	 *	Start at the end of the frame by handling the remainder.	 */	offset = length - (length % (maxfraglen - fragheaderlen));	/*	 *	Amount of memory to allocate for final fragment.	 */	fraglen = length - offset + fragheaderlen;	if (length-offset==0) {		fraglen = maxfraglen;		offset -= maxfraglen-fragheaderlen;	}	/*	 *	The last fragment will not have MF (more fragments) set.	 */	mf = 0;	/*	 *	Don't fragment packets for path mtu discovery.	 */	if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) { 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu); 		return -EMSGSIZE;	}	if (flags&MSG_PROBE)		goto out;

⌨️ 快捷键说明

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