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

📄 ip_output.c

📁 linux 内核源代码
💻 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 *		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/mm.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/highmem.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/xfrm.h>#include <linux/skbuff.h>#include <net/sock.h>#include <net/arp.h>#include <net/icmp.h>#include <net/checksum.h>#include <net/inetpeer.h>#include <linux/igmp.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_bridge.h>#include <linux/mroute.h>#include <linux/netlink.h>#include <linux/tcp.h>int sysctl_ip_default_ttl __read_mostly = 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){	skb_reset_mac_header(newskb);	__skb_pull(newskb, skb_network_offset(newskb));	newskb->pkt_type = PACKET_LOOPBACK;	newskb->ip_summed = CHECKSUM_UNNECESSARY;	BUG_TRAP(newskb->dst);	netif_rx(newskb);	return 0;}static inline int ip_select_ttl(struct inet_sock *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,			  __be32 saddr, __be32 daddr, struct ip_options *opt){	struct inet_sock *inet = inet_sk(sk);	struct rtable *rt = (struct rtable *)skb->dst;	struct iphdr *iph;	/* Build the IP header. */	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));	skb_reset_network_header(skb);	iph = ip_hdr(skb);	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);	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);}EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);static inline int ip_finish_output2(struct sk_buff *skb){	struct dst_entry *dst = skb->dst;	struct rtable *rt = (struct rtable *)dst;	struct net_device *dev = dst->dev;	unsigned int hh_len = LL_RESERVED_SPACE(dev);	if (rt->rt_type == RTN_MULTICAST)		IP_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);	else if (rt->rt_type == RTN_BROADCAST)		IP_INC_STATS(IPSTATS_MIB_OUTBCASTPKTS);	/* Be paranoid, rather than too clever. */	if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {		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;	}	if (dst->hh)		return neigh_hh_output(dst->hh, 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;}static inline int ip_skb_dst_mtu(struct sk_buff *skb){	struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;	return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?	       skb->dst->dev->mtu : dst_mtu(skb->dst);}static int ip_finish_output(struct sk_buff *skb){#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)	/* Policy lookup after SNAT yielded a new policy */	if (skb->dst->xfrm != NULL) {		IPCB(skb)->flags |= IPSKB_REROUTED;		return dst_output(skb);	}#endif	if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))		return ip_fragment(skb, ip_finish_output2);	else		return ip_finish_output2(skb);}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(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 (ip_hdr(skb)->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 NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dev,			    ip_finish_output,			    !(IPCB(skb)->flags & IPSKB_REROUTED));}int ip_output(struct sk_buff *skb){	struct net_device *dev = skb->dst->dev;	IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);	skb->dev = dev;	skb->protocol = htons(ETH_P_IP);	return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,			    ip_finish_output,			    !(IPCB(skb)->flags & IPSKB_REROUTED));}int ip_queue_xmit(struct sk_buff *skb, int ipfragok){	struct sock *sk = skb->sk;	struct inet_sock *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) {		__be32 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.			 */			security_sk_classify_flow(sk, &fl);			if (ip_route_output_flow(&rt, &fl, sk, 0))				goto no_route;		}		sk_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. */	skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));	skb_reset_network_header(skb);	iph = ip_hdr(skb);	*((__be16 *)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;	/* 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)->gso_segs ?: 1) - 1);	/* 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;	dst_release(to->dst);	to->dst = dst_clone(from->dst);	to->dev = from->dev;	to->mark = from->mark;	/* Copy the flags to each fragment. */	IPCB(to)->flags = IPCB(from)->flags;#ifdef CONFIG_NET_SCHED	to->tc_index = from->tc_index;#endif	nf_copy(to, from);#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)	to->nf_trace = from->nf_trace;#endif#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)	to->ipvs_property = from->ipvs_property;#endif	skb_copy_secmark(to, from);}/* *	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, pad;	int offset;	__be16 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 = ip_hdr(skb);	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,			  htonl(ip_skb_dst_mtu(skb)));		kfree_skb(skb);		return -EMSGSIZE;	}	/*	 *	Setup starting values.	 */	hlen = iph->ihl * 4;	mtu = dst_mtu(&rt->u.dst) - hlen;	/* Size of data space */	IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;	/* When frag_list is given, use it. First, check its validity:	 * some transformers could create wrong frag_list or break existing	 * one, it is not prohibited. In this case fall back to copying.	 *	 * LATER: this step can be merged to real generation of fragments,	 * we can switch to copy when see the first bad fragment.	 */	if (skb_shinfo(skb)->frag_list) {		struct sk_buff *frag;		int first_len = skb_pagelen(skb);		if (first_len - hlen > mtu ||		    ((first_len - hlen) & 7) ||		    (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||		    skb_cloned(skb))			goto slow_path;		for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {			/* Correct geometry. */

⌨️ 快捷键说明

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