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

📄 icmp.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	NET3:	Implementation of the ICMP protocol layer.  *	 *		Alan Cox, <alan@redhat.com> * *	Version: $Id: icmp.c,v 1.82.2.1 2001/12/13 08:59:27 davem Exp $ * *	This program is free software; you can redistribute it and/or *	modify it under the terms of the GNU General Public License *	as published by the Free Software Foundation; either version *	2 of the License, or (at your option) any later version. * *	Some of the function names and the icmp unreach table for this *	module were derived from [icmp.c 1.0.11 06/02/93] by *	Ross Biro, Fred N. van Kempen, Mark Evans, Alan Cox, Gerhard Koerting. *	Other than that this module is a complete rewrite. * *	Fixes: *	Clemens Fruhwirth	:	introduce global icmp rate limiting *					with icmp type masking ability instead *					of broken per type icmp timeouts. *		Mike Shaver	:	RFC1122 checks. *		Alan Cox	:	Multicast ping reply as self. *		Alan Cox	:	Fix atomicity lockup in ip_build_xmit  *					call. *		Alan Cox	:	Added 216,128 byte paths to the MTU  *					code. *		Martin Mares	:	RFC1812 checks. *		Martin Mares	:	Can be configured to follow redirects  *					if acting as a router _without_ a *					routing protocol (RFC 1812). *		Martin Mares	:	Echo requests may be configured to  *					be ignored (RFC 1812). *		Martin Mares	:	Limitation of ICMP error message  *					transmit rate (RFC 1812). *		Martin Mares	:	TOS and Precedence set correctly  *					(RFC 1812). *		Martin Mares	:	Now copying as much data from the  *					original packet as we can without *					exceeding 576 bytes (RFC 1812). *	Willy Konynenberg	:	Transparent proxying support. *		Keith Owens	:	RFC1191 correction for 4.2BSD based  *					path MTU bug. *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are *					valid (RFC 1812). *		Andi Kleen	:	Check all packet lengths properly *					and moved all kfree_skb() up to *					icmp_rcv. *		Andi Kleen	:	Move the rate limit bookkeeping *					into the dest entry and use a token *					bucket filter (thanks to ANK). Make *					the rates sysctl configurable. *		Yu Tianli	:	Fixed two ugly bugs in icmp_send *					- IP option length was accounted wrongly *					- ICMP header length was not accounted at all. *              Tristan Greaves :       Added sysctl option to ignore bogus broadcast *                                      responses from broken routers. * * To Fix: * *	- Should use skb_pull() instead of all the manual checking. *	  This would also greatly simply some upper layer error handlers. --AK * */#include <linux/config.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/fcntl.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/string.h>#include <linux/netfilter_ipv4.h>#include <net/snmp.h>#include <net/ip.h>#include <net/route.h>#include <net/protocol.h>#include <net/icmp.h>#include <net/tcp.h>#include <net/udp.h>#include <net/raw.h>#include <linux/skbuff.h>#include <net/sock.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/init.h>#include <asm/system.h>#include <asm/uaccess.h>#include <net/checksum.h>/* *	Build xmit assembly blocks */struct icmp_bxm{	struct sk_buff *skb;	int offset;	int data_len;	unsigned int csum;	struct {		struct icmphdr icmph;		__u32	       times[3];	} data;	int head_len;	struct ip_options replyopts;	unsigned char  optbuf[40];};/* *	Statistics */ struct icmp_mib icmp_statistics[NR_CPUS*2];/* An array of errno for error messages from dest unreach. *//* RFC 1122: 3.2.2.1 States that NET_UNREACH, HOS_UNREACH and SR_FAIELD MUST be considered 'transient errs'. */struct icmp_err icmp_err_convert[] = {  { ENETUNREACH,	0 },	/*	ICMP_NET_UNREACH	*/  { EHOSTUNREACH,	0 },	/*	ICMP_HOST_UNREACH	*/  { ENOPROTOOPT,	1 },	/*	ICMP_PROT_UNREACH	*/  { ECONNREFUSED,	1 },	/*	ICMP_PORT_UNREACH	*/  { EMSGSIZE,		0 },	/*	ICMP_FRAG_NEEDED	*/  { EOPNOTSUPP,		0 },	/*	ICMP_SR_FAILED		*/  { ENETUNREACH,	1 },	/* 	ICMP_NET_UNKNOWN	*/  { EHOSTDOWN,		1 },	/*	ICMP_HOST_UNKNOWN	*/  { ENONET,		1 },	/*	ICMP_HOST_ISOLATED	*/  { ENETUNREACH,	1 },	/*	ICMP_NET_ANO		*/  { EHOSTUNREACH,	1 },	/*	ICMP_HOST_ANO		*/  { ENETUNREACH,	0 },	/*	ICMP_NET_UNR_TOS	*/  { EHOSTUNREACH,	0 },	/*	ICMP_HOST_UNR_TOS	*/  { EHOSTUNREACH,	1 },	/*	ICMP_PKT_FILTERED	*/  { EHOSTUNREACH,	1 },	/*	ICMP_PREC_VIOLATION	*/  { EHOSTUNREACH,	1 }	/*	ICMP_PREC_CUTOFF	*/};/* Control parameters for ECHO replies. */int sysctl_icmp_echo_ignore_all;int sysctl_icmp_echo_ignore_broadcasts;/* Control parameter - ignore bogus broadcast responses? */int sysctl_icmp_ignore_bogus_error_responses;/*  * 	Configurable global rate limit. * *	ratelimit defines tokens/packet consumed for dst->rate_token bucket *	ratemask defines which icmp types are ratelimited by setting * 	it's bit position. * *	default:  *	dest unreachable (3), source quench (4), *	time exceeded (11), parameter problem (12) */int sysctl_icmp_ratelimit = 1*HZ;int sysctl_icmp_ratemask = 0x1818;/* *	ICMP control array. This specifies what to do with each ICMP. */struct icmp_control{	unsigned long *output;		/* Address to increment on output */	unsigned long *input;		/* Address to increment on input */	void (*handler)(struct sk_buff *skb);	short	error;		/* This ICMP is classed as an error message */};static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];/* *	The ICMP socket. This is the most convenient way to flow control *	our ICMP output as well as maintain a clean interface throughout *	all layers. All Socketless IP sends will soon be gone. */	struct inode icmp_inode;struct socket *icmp_socket = &icmp_inode.u.socket_i;/* ICMPv4 socket is only a bit non-reenterable (unlike ICMPv6,   which is strongly non-reenterable). A bit later it will be made   reenterable and the lock may be removed then. */static int icmp_xmit_holder = -1;static int icmp_xmit_lock_bh(void){	if (!spin_trylock(&icmp_socket->sk->lock.slock)) {		if (icmp_xmit_holder == smp_processor_id())			return -EAGAIN;		spin_lock(&icmp_socket->sk->lock.slock);	}	icmp_xmit_holder = smp_processor_id();	return 0;}static __inline__ int icmp_xmit_lock(void){	int ret;	local_bh_disable();	ret = icmp_xmit_lock_bh();	if (ret)		local_bh_enable();	return ret;}static void icmp_xmit_unlock_bh(void){	icmp_xmit_holder = -1;	spin_unlock(&icmp_socket->sk->lock.slock);}static __inline__ void icmp_xmit_unlock(void){	icmp_xmit_unlock_bh();	local_bh_enable();}/* *	Send an ICMP frame. *//* *	Check transmit rate limitation for given message. *	The rate information is held in the destination cache now. *	This function is generic and could be used for other purposes *	too. It uses a Token bucket filter as suggested by Alexey Kuznetsov. * *	Note that the same dst_entry fields are modified by functions in  *	route.c too, but these work for packet destinations while xrlim_allow *	works for icmp destinations. This means the rate limiting information *	for one "ip object" is shared - and these ICMPs are twice limited: *	by source and by destination. * *	RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate *			  SHOULD allow setting of rate limits  * * 	Shared between ICMPv4 and ICMPv6. */#define XRLIM_BURST_FACTOR 6int xrlim_allow(struct dst_entry *dst, int timeout){	unsigned long now;	now = jiffies;	dst->rate_tokens += now - dst->rate_last;	dst->rate_last = now;	if (dst->rate_tokens > XRLIM_BURST_FACTOR*timeout)        	dst->rate_tokens = XRLIM_BURST_FACTOR*timeout;	if (dst->rate_tokens >= timeout) {		dst->rate_tokens -= timeout;		return 1;	}	return 0; }static inline int icmpv4_xrlim_allow(struct rtable *rt, int type, int code){	struct dst_entry *dst = &rt->u.dst; 	if (type > NR_ICMP_TYPES)		return 1;	/* Don't limit PMTU discovery. */	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)		return 1;	/* No rate limit on loopback */	if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) 		return 1;	/* Limit if icmp type is enabled in ratemask. */	if((1 << type) & sysctl_icmp_ratemask)		return xrlim_allow(dst, sysctl_icmp_ratelimit);	else		return 1;}/* *	Maintain the counters used in the SNMP statistics for outgoing ICMP */ static void icmp_out_count(int type){	if (type>NR_ICMP_TYPES)		return;	(icmp_pointers[type].output)[(smp_processor_id()*2+!in_softirq())*sizeof(struct icmp_mib)/sizeof(unsigned long)]++;	ICMP_INC_STATS(IcmpOutMsgs);} /* *	Checksum each fragment, and on the first include the headers and final checksum. */ static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen){	struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;	struct icmphdr *icmph;	unsigned int csum;	if (offset) {		icmp_param->csum=skb_copy_and_csum_bits(icmp_param->skb,							icmp_param->offset+(offset-icmp_param->head_len), 							to, fraglen,icmp_param->csum);		return 0;	}	/*	 *	First fragment includes header. Note that we've done	 *	the other fragments first, so that we get the checksum	 *	for the whole packet here.	 */	csum = csum_partial_copy_nocheck((void *)&icmp_param->data,		to, icmp_param->head_len,		icmp_param->csum);	csum=skb_copy_and_csum_bits(icmp_param->skb,				    icmp_param->offset, 				    to+icmp_param->head_len,				    fraglen-icmp_param->head_len,				    csum);	icmph=(struct icmphdr *)to;	icmph->checksum = csum_fold(csum);	return 0;}/* *	Driving logic for building and sending ICMP messages. */static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb){	struct sock *sk=icmp_socket->sk;	struct ipcm_cookie ipc;	struct rtable *rt = (struct rtable*)skb->dst;	u32 daddr;	if (ip_options_echo(&icmp_param->replyopts, skb))		return;	if (icmp_xmit_lock_bh())		return;	icmp_param->data.icmph.checksum=0;	icmp_param->csum=0;	icmp_out_count(icmp_param->data.icmph.type);	sk->protinfo.af_inet.tos = skb->nh.iph->tos;	daddr = ipc.addr = rt->rt_src;	ipc.opt = NULL;	if (icmp_param->replyopts.optlen) {		ipc.opt = &icmp_param->replyopts;		if (ipc.opt->srr)			daddr = icmp_param->replyopts.faddr;	}	if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))		goto out;	if (icmpv4_xrlim_allow(rt, icmp_param->data.icmph.type, 			       icmp_param->data.icmph.code)) { 		ip_build_xmit(sk, icmp_glue_bits, icmp_param, 			      icmp_param->data_len+icmp_param->head_len,			      &ipc, rt, MSG_DONTWAIT);	}	ip_rt_put(rt);out:	icmp_xmit_unlock_bh();}/* *	Send an ICMP message in response to a situation * *	RFC 1122: 3.2.2	MUST send at least the IP header and 8 bytes of header. MAY send more (we do). *			MUST NOT change this header information. *			MUST NOT reply to a multicast/broadcast IP address. *			MUST NOT reply to a multicast/broadcast MAC address. *			MUST reply to only the first fragment. */void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info){	struct iphdr *iph;	int room;	struct icmp_bxm icmp_param;	struct rtable *rt = (struct rtable*)skb_in->dst;	struct ipcm_cookie ipc;	u32 saddr;	u8  tos;	if (!rt)		return;	/*	 *	Find the original header. It is expected to be valid, of course.	 *	Check this, icmp_send is called from the most obscure devices	 *	sometimes.	 */	iph = skb_in->nh.iph;	if ((u8*)iph < skb_in->head || (u8*)(iph+1) > skb_in->tail)		return;	/*	 *	No replies to physical multicast/broadcast	 */	if (skb_in->pkt_type!=PACKET_HOST)		return;	/*	 *	Now check at the protocol level	 */	if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))		return;	/*	 *	Only reply to fragment 0. We byte re-order the constant	 *	mask for efficiency.	 */	if (iph->frag_off&htons(IP_OFFSET))		return;	/* 	 *	If we send an ICMP error to an ICMP error a mess would result..	 */	if (icmp_pointers[type].error) {		/*		 *	We are an error, check if we are replying to an ICMP error		 */		if (iph->protocol==IPPROTO_ICMP) {			u8 inner_type;			if (skb_copy_bits(skb_in,					  skb_in->nh.raw + (iph->ihl<<2)					  + offsetof(struct icmphdr, type)					  - skb_in->data,					  &inner_type, 1))				return;			/*			 *	Assume any unknown ICMP type is an error. This isn't			 *	specified by the RFC, but think about it..			 */			if (inner_type>NR_ICMP_TYPES || icmp_pointers[inner_type].error)				return;		}	}	if (icmp_xmit_lock())		return;	/*	 *	Construct source address and options.	 */#ifdef CONFIG_IP_ROUTE_NAT		/*	 *	Restore original addresses if packet has been translated.	 */	if (rt->rt_flags&RTCF_NAT && IPCB(skb_in)->flags&IPSKB_TRANSLATED) {		iph->daddr = rt->key.dst;		iph->saddr = rt->key.src;	}#endif	saddr = iph->daddr;	if (!(rt->rt_flags & RTCF_LOCAL))		saddr = 0;	tos = icmp_pointers[type].error ?		((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :			iph->tos;	if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))		goto out;	if (ip_options_echo(&icmp_param.replyopts, skb_in)) 		goto ende;	/*	 *	Prepare data for ICMP header.	 */	icmp_param.data.icmph.type=type;	icmp_param.data.icmph.code=code;	icmp_param.data.icmph.un.gateway = info;	icmp_param.data.icmph.checksum=0;	icmp_param.csum=0;	icmp_param.skb=skb_in;	icmp_param.offset=skb_in->nh.raw - skb_in->data;	icmp_out_count(icmp_param.data.icmph.type);	icmp_socket->sk->protinfo.af_inet.tos = tos;	ipc.addr = iph->saddr;	ipc.opt = &icmp_param.replyopts;	if (icmp_param.replyopts.srr) {		ip_rt_put(rt);

⌨️ 快捷键说明

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