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

📄 ip_output.c

📁 GNU Hurd 源代码
💻 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.67.2.1 1999/09/07 02:25:23 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. */#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 <linux/igmp.h>#include <linux/ip_fw.h>#include <linux/firewall.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 ip_id_count = 0;/* 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);}/*  *		Add an ip header to a skbuff and send it out. */void 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;	struct device *dev;		/* 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->ip_tos;	iph->frag_off = 0;	if (ip_dont_fragment(sk, &rt->u.dst))		iph->frag_off |= htons(IP_DF);	iph->ttl      = sk->ip_ttl;	iph->daddr    = rt->rt_dst;	iph->saddr    = rt->rt_src;	iph->protocol = sk->protocol;	iph->tot_len  = htons(skb->len);	iph->id       = htons(ip_id_count++);	skb->nh.iph   = iph;	if (opt && opt->optlen) {		iph->ihl += opt->optlen>>2;		ip_options_build(skb, opt, daddr, rt, 0);	}	dev = rt->u.dst.dev;#ifdef CONFIG_FIREWALL	/* Now we have no better mechanism to notify about error. */	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {	case FW_REJECT:		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);		/* Fall thru... */	case FW_BLOCK:	case FW_QUEUE:		kfree_skb(skb);		return;	}#endif	ip_send_check(iph);	/* Send it out. */	skb->dst->output(skb);	return;}int __ip_finish_output(struct sk_buff *skb){	return ip_finish_output(skb);}int ip_mc_output(struct sk_buff *skb){	struct sock *sk = skb->sk;	struct rtable *rt = (struct rtable*)skb->dst;	struct device *dev = rt->u.dst.dev;	/*	 *	If the indicated interface is up and running, send the packet.	 */	 	ip_statistics.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 && (!sk || sk->ip_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.		 */		if ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))#endif		dev_loopback_xmit(skb);		/* 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)		dev_loopback_xmit(skb);	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_statistics.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. */void ip_queue_xmit(struct sk_buff *skb){	struct sock *sk = skb->sk;	struct ip_options *opt = sk->opt;	struct rtable *rt;	struct device *dev;	struct iphdr *iph;	unsigned int tot_len;	/* Make sure we can route this packet. */	rt = (struct rtable *) sk->dst_cache;	if(rt == NULL || rt->u.dst.obsolete) {		u32 daddr;		sk->dst_cache = NULL;		ip_rt_put(rt);		/* 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_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,				   sk->bound_dev_if))			goto drop;		sk->dst_cache = &rt->u.dst;	}	if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)		goto no_route;	/* We have a route, so grab a reference. */	skb->dst = dst_clone(sk->dst_cache);	/* 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));	iph->version  = 4;	iph->ihl      = 5;	iph->tos      = sk->ip_tos;	iph->frag_off = 0;	iph->ttl      = sk->ip_ttl;	iph->daddr    = rt->rt_dst;	iph->saddr    = rt->rt_src;	iph->protocol = sk->protocol;	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);	}	tot_len = skb->len;	iph->tot_len = htons(tot_len);	iph->id = htons(ip_id_count++);	dev = rt->u.dst.dev;#ifdef CONFIG_FIREWALL	/* Now we have no better mechanism to notify about error. */	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {	case FW_REJECT:		start_bh_atomic();		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);		end_bh_atomic();		/* Fall thru... */	case FW_BLOCK:	case FW_QUEUE: 		goto drop;	}#endif	/* 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;		if (sk)			skb_set_owner_w(skb2, sk);		skb = skb2;		iph = skb->nh.iph;	}	/* Do we need to fragment.  Again this is inefficient.  We	 * need to somehow lock the original buffer and use bits of it.	 */	if (tot_len > rt->u.dst.pmtu)		goto fragment;	if (ip_dont_fragment(sk, &rt->u.dst))		iph->frag_off |= __constant_htons(IP_DF);	/* Add an IP checksum. */	ip_send_check(iph);	skb->priority = sk->priority;	skb->dst->output(skb);	return;fragment:	if (ip_dont_fragment(sk, &rt->u.dst) &&	    tot_len > (iph->ihl<<2) + sizeof(struct tcphdr)+16) {		/* Reject packet ONLY if TCP might fragment		   it itself, if were careful enough.		   Test is not precise (f.e. it does not take sacks		   into account). Actually, tcp should make it. --ANK (980801)		 */		iph->frag_off |= __constant_htons(IP_DF);		NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));		/* icmp_send is not reenterable, so that bh_atomic... --ANK */		start_bh_atomic();		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,			  htonl(rt->u.dst.pmtu));		end_bh_atomic();		goto drop;	}	ip_fragment(skb, skb->dst->output);	return;no_route:	sk->dst_cache = NULL;	ip_rt_put(rt);	ip_statistics.IpOutNoRoutes++;	/* Fall through... */drop:	kfree_skb(skb);}/* *	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. */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;	unsigned short 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 && df) { 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu); 		return(-EMSGSIZE);	}	/*	 *	Lock the device lists.	 */	dev_lock_list();		/*	 *	Get an identifier	 */	 	id = htons(ip_id_count++);	/*	 *	Begin outputting the bytes.	 */	 	do {		char *data;		struct sk_buff * skb;		/*		 *	Get the memory we require with some space left for alignment.		 */

⌨️ 快捷键说明

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