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

📄 ip_output.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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.100 2002/02/01 22:01:03 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> *		Hirokazu Takahashi, <taka@valinux.co.jp> * *	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. *		Hirokazu Takahashi:	HW checksumming for outgoing UDP *					datagrams. *		Hirokazu Takahashi:	sendfile() on UDP works now. */#include <asm/uaccess.h>#include <asm/system.h>#include <linux/module.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 <net/checksum.h>#include <linux/igmp.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_bridge.h>#include <linux/mroute.h>#include <linux/netlink.h>/* *      Shall we try to damage output packets if routing dev changes? */int sysctl_ip_dynaddr;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;}static inline int ip_select_ttl(struct inet_opt *inet, struct dst_entry *dst){	int ttl = inet->uc_ttl;	if (ttl < 0)		ttl = dst_metric(dst, RTAX_HOPLIMIT);	return ttl;}/*  *		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 inet_opt *inet = inet_sk(sk);	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      = inet->tos;	if (ip_dont_fragment(sk, &rt->u.dst))		iph->frag_off = htons(IP_DF);	else		iph->frag_off = 0;	iph->ttl      = ip_select_ttl(inet, &rt->u.dst);	iph->daddr    = rt->rt_dst;	iph->saddr    = rt->rt_src;	iph->protocol = sk->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);	skb->priority = sk->sk_priority;	/* Send it out. */	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,		       dst_output);}static inline int ip_finish_output2(struct sk_buff *skb){	struct dst_entry *dst = skb->dst;	struct hh_cache *hh = dst->hh;	struct net_device *dev = dst->dev;	int hh_len = LL_RESERVED_SPACE(dev);	/* Be paranoid, rather than too clever. */	if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {		struct sk_buff *skb2;		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));		if (skb2 == NULL) {			kfree_skb(skb);			return -ENOMEM;		}		if (skb->sk)			skb_set_owner_w(skb2, skb->sk);		kfree_skb(skb);		skb = skb2;	}#ifdef CONFIG_NETFILTER_DEBUG	nf_debug_ip_finish_output2(skb);#endif /*CONFIG_NETFILTER_DEBUG*/	if (hh) {		int hh_alen;		read_lock_bh(&hh->hh_lock);		hh_alen = HH_DATA_ALIGN(hh->hh_len);  		memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);		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;}int ip_finish_output(struct sk_buff *skb){	struct net_device *dev = skb->dst->dev;	skb->dev = dev;	skb->protocol = 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 **pskb){	struct sk_buff *skb = *pskb;	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(IPSTATS_MIB_OUTREQUESTS);	skb->dev = dev;	skb->protocol = htons(ETH_P_IP);	/*	 *	Multicasts are looped back for other local users	 */	if (rt->rt_flags&RTCF_MULTICAST) {		if ((!sk || inet_sk(sk)->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);	}	if (skb->len > dst_pmtu(&rt->u.dst) || skb_shinfo(skb)->frag_list)		return ip_fragment(skb, ip_finish_output);	else		return ip_finish_output(skb);}int ip_output(struct sk_buff **pskb){	struct sk_buff *skb = *pskb;	IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);	if ((skb->len > dst_pmtu(skb->dst) || skb_shinfo(skb)->frag_list) &&	    !skb_shinfo(skb)->tso_size)		return ip_fragment(skb, ip_finish_output);	else		return ip_finish_output(skb);}int ip_queue_xmit(struct sk_buff *skb, int ipfragok){	struct sock *sk = skb->sk;	struct inet_opt *inet = inet_sk(sk);	struct ip_options *opt = 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 = inet->daddr;		if(opt && opt->srr)			daddr = opt->faddr;		{			struct flowi fl = { .oif = sk->sk_bound_dev_if,					    .nl_u = { .ip4_u =						      { .daddr = daddr,							.saddr = inet->saddr,							.tos = RT_CONN_FLAGS(sk) } },					    .proto = sk->sk_protocol,					    .uli_u = { .ports =						       { .sport = inet->sport,							 .dport = inet->dport } } };			/* If this fails, retransmit mechanism of transport layer will			 * keep trying until route appears or the connection times			 * itself out.			 */			if (ip_route_output_flow(&rt, &fl, sk, 0))				goto no_route;		}		__sk_dst_set(sk, &rt->u.dst);		tcp_v4_setup_caps(sk, &rt->u.dst);	}	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) | (inet->tos & 0xff));	iph->tot_len = htons(skb->len);	if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)		iph->frag_off = htons(IP_DF);	else		iph->frag_off = 0;	iph->ttl      = ip_select_ttl(inet, &rt->u.dst);	iph->protocol = sk->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, inet->daddr, rt, 0);	}	ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);	/* Add an IP checksum. */	ip_send_check(iph);	skb->priority = sk->sk_priority;	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,		       dst_output);no_route:	IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES);	kfree_skb(skb);	return -EHOSTUNREACH;}static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from){	to->pkt_type = from->pkt_type;	to->priority = from->priority;	to->protocol = from->protocol;	to->security = from->security;	to->dst = dst_clone(from->dst);	to->dev = from->dev;	/* Copy the flags to each fragment. */	IPCB(to)->flags = IPCB(from)->flags;#ifdef CONFIG_NET_SCHED	to->tc_index = from->tc_index;#endif#ifdef CONFIG_NETFILTER	to->nfmark = from->nfmark;	to->nfcache = from->nfcache;	/* Connection association is same as pre-frag packet */	nf_conntrack_put(to->nfct);	to->nfct = from->nfct;	nf_conntrack_get(to->nfct);	to->nfctinfo = from->nfctinfo;#ifdef CONFIG_BRIDGE_NETFILTER	nf_bridge_put(to->nf_bridge);	to->nf_bridge = from->nf_bridge;	nf_bridge_get(to->nf_bridge);#endif#ifdef CONFIG_NETFILTER_DEBUG	to->nf_debug = from->nf_debug;#endif#endif}/* *	This IP datagram is too large to be sent in one piece.  Break it up into *	smaller pieces (each of size equal to IP header plus *	a block of the data of the original IP data part) that will yet fit in a *	single device frame, and queue such a frame for sending. */int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)){	struct iphdr *iph;	int raw = 0;	int ptr;	struct net_device *dev;	struct sk_buff *skb2;	unsigned int mtu, hlen, left, len, ll_rs;	int offset;	int not_last_frag;	struct rtable *rt = (struct rtable*)skb->dst;	int err = 0;	dev = rt->u.dst.dev;	/*	 *	Point into the IP datagram header.	 */	iph = skb->nh.iph;	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,			  htonl(dst_pmtu(&rt->u.dst)));

⌨️ 快捷键说明

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