patch-2.4.4 linux/net/ipv4/icmp.c

Next file: linux/net/ipv4/igmp.c
Previous file: linux/net/ipv4/arp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.71 2000/08/02 06:01:48 davem Exp $
+ *	Version: $Id: icmp.c,v 1.74 2001/04/16 23:58:51 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -58,197 +58,6 @@
  *	- Should use skb_pull() instead of all the manual checking.
  *	  This would also greatly simply some upper layer error handlers. --AK
  *
- * RFC1122 (Host Requirements -- Comm. Layer) Status:
- * (boy, are there a lot of rules for ICMP)
- *  3.2.2 (Generic ICMP stuff)
- *   MUST discard messages of unknown type. (OK)
- *   MUST copy at least the first 8 bytes from the offending packet
- *     when sending ICMP errors. (OBSOLETE -- see RFC1812)
- *   MUST pass received ICMP errors up to protocol level. (OK)
- *   SHOULD send ICMP errors with TOS == 0. (OBSOLETE -- see RFC1812)
- *   MUST NOT send ICMP errors in reply to:
- *     ICMP errors (OK)
- *     Broadcast/multicast datagrams (OK)
- *     MAC broadcasts (OK)
- *     Non-initial fragments (OK)
- *     Datagram with a source address that isn't a single host. (OK)
- *  3.2.2.1 (Destination Unreachable)
- *   All the rules govern the IP layer, and are dealt with in ip.c, not here.
- *  3.2.2.2 (Redirect)
- *   Host SHOULD NOT send ICMP_REDIRECTs.  (OK)
- *   MUST update routing table in response to host or network redirects.
- *     (host OK, network OBSOLETE)
- *   SHOULD drop redirects if they're not from directly connected gateway
- *     (OK -- we drop it if it's not from our old gateway, which is close
- *      enough)
- * 3.2.2.3 (Source Quench)
- *   MUST pass incoming SOURCE_QUENCHs to transport layer (OK)
- *   Other requirements are dealt with at the transport layer.
- * 3.2.2.4 (Time Exceeded)
- *   MUST pass TIME_EXCEEDED to transport layer (OK)
- *   Other requirements dealt with at IP (generating TIME_EXCEEDED).
- * 3.2.2.5 (Parameter Problem)
- *   SHOULD generate these (OK)
- *   MUST pass received PARAMPROBLEM to transport layer (NOT YET)
- *   	[Solaris 2.X seems to assert EPROTO when this occurs] -- AC
- * 3.2.2.6 (Echo Request/Reply)
- *   MUST reply to ECHO_REQUEST, and give app to do ECHO stuff (OK, OK)
- *   MAY discard broadcast ECHO_REQUESTs. (Configurable with a sysctl.)
- *   MUST reply using same source address as the request was sent to.
- *     We're OK for unicast ECHOs, and it doesn't say anything about
- *     how to handle broadcast ones, since it's optional.
- *   MUST copy data from REQUEST to REPLY (OK)
- *     unless it would require illegal fragmentation (OK)
- *   MUST pass REPLYs to transport/user layer (OK)
- *   MUST use any provided source route (reversed) for REPLY. (NOT YET)
- * 3.2.2.7 (Information Request/Reply)
- *   MUST NOT implement this. (I guess that means silently discard...?) (OK)
- * 3.2.2.8 (Timestamp Request/Reply)
- *   MAY implement (OK)
- *   SHOULD be in-kernel for "minimum variability" (OK)
- *   MAY discard broadcast REQUESTs.  (OK, but see source for inconsistency)
- *   MUST reply using same source address as the request was sent to. (OK)
- *   MUST reverse source route, as per ECHO (NOT YET)
- *   MUST pass REPLYs to transport/user layer (requires RAW, just like 
- *	ECHO) (OK)
- *   MUST update clock for timestamp at least 15 times/sec (OK)
- *   MUST be "correct within a few minutes" (OK)
- * 3.2.2.9 (Address Mask Request/Reply)
- *   MAY implement (OK)
- *   MUST send a broadcast REQUEST if using this system to set netmask
- *     (OK... we don't use it)
- *   MUST discard received REPLYs if not using this system (OK)
- *   MUST NOT send replies unless specifically made agent for this sort
- *     of thing. (OK)
- *
- *
- * RFC 1812 (IPv4 Router Requirements) Status (even longer):
- *  4.3.2.1 (Unknown Message Types)
- *   MUST pass messages of unknown type to ICMP user iface or silently discard
- *     them (OK)
- *  4.3.2.2 (ICMP Message TTL)
- *   MUST initialize TTL when originating an ICMP message (OK)
- *  4.3.2.3 (Original Message Header)
- *   SHOULD copy as much data from the offending packet as possible without
- *     the length of the ICMP datagram exceeding 576 bytes (OK)
- *   MUST leave original IP header of the offending packet, but we're not
- *     required to undo modifications made (OK)
- *  4.3.2.4 (Original Message Source Address)
- *   MUST use one of addresses for the interface the orig. packet arrived as
- *     source address (OK)
- *  4.3.2.5 (TOS and Precedence)
- *   SHOULD leave TOS set to the same value unless the packet would be 
- *     discarded for that reason (OK)
- *   MUST use TOS=0 if not possible to leave original value (OK)
- *   MUST leave IP Precedence for Source Quench messages (OK -- not sent 
- *	at all)
- *   SHOULD use IP Precedence = 6 (Internetwork Control) or 7 (Network Control)
- *     for all other error messages (OK, we use 6)
- *   MAY allow configuration of IP Precedence (OK -- not done)
- *   MUST leave IP Precedence and TOS for reply messages (OK)
- *  4.3.2.6 (Source Route)
- *   SHOULD use reverse source route UNLESS sending Parameter Problem on source
- *     routing and UNLESS the packet would be immediately discarded (NOT YET)
- *  4.3.2.7 (When Not to Send ICMP Errors)
- *   MUST NOT send ICMP errors in reply to:
- *     ICMP errors (OK)
- *     Packets failing IP header validation tests unless otherwise noted (OK)
- *     Broadcast/multicast datagrams (OK)
- *     MAC broadcasts (OK)
- *     Non-initial fragments (OK)
- *     Datagram with a source address that isn't a single host. (OK)
- *  4.3.2.8 (Rate Limiting)
- *   SHOULD be able to limit error message rate (OK)
- *   SHOULD allow setting of rate limits (OK, in the source)
- *  4.3.3.1 (Destination Unreachable)
- *   All the rules govern the IP layer, and are dealt with in ip.c, not here.
- *  4.3.3.2 (Redirect)
- *   MAY ignore ICMP Redirects if running a routing protocol or if forwarding
- *     is enabled on the interface (OK -- ignores)
- *  4.3.3.3 (Source Quench)
- *   SHOULD NOT originate SQ messages (OK)
- *   MUST be able to limit SQ rate if originates them (OK as we don't 
- *	send them)
- *   MAY ignore SQ messages it receives (OK -- we don't)
- *  4.3.3.4 (Time Exceeded)
- *   Requirements dealt with at IP (generating TIME_EXCEEDED).
- *  4.3.3.5 (Parameter Problem)
- *   MUST generate these for all errors not covered by other messages (OK)
- *   MUST include original value of the value pointed by (OK)
- *  4.3.3.6 (Echo Request)
- *   MUST implement echo server function (OK)
- *   MUST process at ER of at least max(576, MTU) (OK)
- *   MAY reject broadcast/multicast ER's (We don't, but that's OK)
- *   SHOULD have a config option for silently ignoring ER's (OK)
- *   MUST have a default value for the above switch = NO (OK)
- *   MUST have application layer interface for Echo Request/Reply (OK)
- *   MUST reply using same source address as the request was sent to.
- *     We're OK for unicast ECHOs, and it doesn't say anything about
- *     how to handle broadcast ones, since it's optional.
- *   MUST copy data from Request to Reply (OK)
- *   SHOULD update Record Route / Timestamp options (??)
- *   MUST use reversed Source Route for Reply if possible (NOT YET)
- *  4.3.3.7 (Information Request/Reply)
- *   SHOULD NOT originate or respond to these (OK)
- *  4.3.3.8 (Timestamp / Timestamp Reply)
- *   MAY implement (OK)
- *   MUST reply to every Timestamp message received (OK)
- *   MAY discard broadcast REQUESTs.  (OK, but see source for inconsistency)
- *   MUST reply using same source address as the request was sent to. (OK)
- *   MUST use reversed Source Route if possible (NOT YET)
- *   SHOULD update Record Route / Timestamp options (??)
- *   MUST pass REPLYs to transport/user layer (requires RAW, just like 
- *	ECHO) (OK)
- *   MUST update clock for timestamp at least 16 times/sec (OK)
- *   MUST be "correct within a few minutes" (OK)
- * 4.3.3.9 (Address Mask Request/Reply)
- *   MUST have support for receiving AMRq and responding with AMRe (OK, 
- *	but only as a compile-time option)
- *   SHOULD have option for each interface for AMRe's, MUST default to 
- *	NO (NOT YET)
- *   MUST NOT reply to AMRq before knows the correct AM (OK)
- *   MUST NOT respond to AMRq with source address 0.0.0.0 on physical
- *    	interfaces having multiple logical i-faces with different masks
- *	(NOT YET)
- *   SHOULD examine all AMRe's it receives and check them (NOT YET)
- *   SHOULD log invalid AMRe's (AM+sender) (NOT YET)
- *   MUST NOT use contents of AMRe to determine correct AM (OK)
- *   MAY broadcast AMRe's after having configured address masks (OK -- doesn't)
- *   MUST NOT do broadcast AMRe's if not set by extra option (OK, no option)
- *   MUST use the { <NetPrefix>, -1 } form of broadcast addresses (OK)
- * 4.3.3.10 (Router Advertisement and Solicitations)
- *   MUST support router part of Router Discovery Protocol on all networks we
- *     support broadcast or multicast addressing. (OK -- done by gated)
- *   MUST have all config parameters with the respective defaults (OK)
- * 5.2.7.1 (Destination Unreachable)
- *   MUST generate DU's (OK)
- *   SHOULD choose a best-match response code (OK)
- *   SHOULD NOT generate Host Isolated codes (OK)
- *   SHOULD use Communication Administratively Prohibited when administratively
- *     filtering packets (NOT YET -- bug-to-bug compatibility)
- *   MAY include config option for not generating the above and silently
- *	discard the packets instead (OK)
- *   MAY include config option for not generating Precedence Violation and
- *     Precedence Cutoff messages (OK as we don't generate them at all)
- *   MUST use Host Unreachable or Dest. Host Unknown codes whenever other hosts
- *     on the same network might be reachable (OK -- no net unreach's at all)
- *   MUST use new form of Fragmentation Needed and DF Set messages (OK)
- * 5.2.7.2 (Redirect)
- *   MUST NOT generate network redirects (OK)
- *   MUST be able to generate host redirects (OK)
- *   SHOULD be able to generate Host+TOS redirects (NO as we don't use TOS)
- *   MUST have an option to use Host redirects instead of Host+TOS ones (OK as
- *     no Host+TOS Redirects are used)
- *   MUST NOT generate redirects unless forwarding to the same i-face and the
- *     dest. address is on the same subnet as the src. address and no source
- *     routing is in use. (OK)
- *   MUST NOT follow redirects when using a routing protocol (OK)
- *   MAY use redirects if not using a routing protocol (OK, compile-time option)
- *   MUST comply to Host Requirements when not acting as a router (OK)
- *  5.2.7.3 (Time Exceeded)
- *   MUST generate Time Exceeded Code 0 when discarding packet due to TTL=0 (OK)
- *   MAY have a per-interface option to disable origination of TE messages, but
- *     it MUST default to "originate" (OK -- we don't support it)
  */
 
 #include <linux/config.h>
@@ -282,6 +91,26 @@
 #define min(a,b)	((a)<(b)?(a):(b))
 
 /*
+ *	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
  */
  
@@ -324,7 +153,7 @@
 {
 	unsigned long *output;		/* Address to increment on output */
 	unsigned long *input;		/* Address to increment on input */
-	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len);
+	void (*handler)(struct sk_buff *skb);
 	short	error;		/* This ICMP is classed as an error message */
 	int *timeout; /* Rate limit */
 };
@@ -466,11 +295,12 @@
 {
 	struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
 	struct icmphdr *icmph;
-	unsigned long csum;
+	unsigned int csum;
 
 	if (offset) {
-		icmp_param->csum=csum_partial_copy_nocheck(icmp_param->data_ptr+offset-sizeof(struct icmphdr), 
-				to, fraglen,icmp_param->csum);
+		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;
 	}
 
@@ -479,22 +309,24 @@
 	 *	the other fragments first, so that we get the checksum
 	 *	for the whole packet here.
 	 */
-	csum = csum_partial_copy_nocheck((void *)&icmp_param->icmph,
-		to, sizeof(struct icmphdr), 
+	csum = csum_partial_copy_nocheck((void *)&icmp_param->data,
+		to, icmp_param->head_len,
 		icmp_param->csum);
-	csum = csum_partial_copy_nocheck(icmp_param->data_ptr,
-		to+sizeof(struct icmphdr),
-		fraglen-sizeof(struct icmphdr), 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.
  */
 
-void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
+static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
 {
 	struct sock *sk=icmp_socket->sk;
 	struct ipcm_cookie ipc;
@@ -507,9 +339,9 @@
 	if (icmp_xmit_lock_bh())
 		return;
 
-	icmp_param->icmph.checksum=0;
+	icmp_param->data.icmph.checksum=0;
 	icmp_param->csum=0;
-	icmp_out_count(icmp_param->icmph.type);
+	icmp_out_count(icmp_param->data.icmph.type);
 
 	sk->protinfo.af_inet.tos = skb->nh.iph->tos;
 	daddr = ipc.addr = rt->rt_src;
@@ -521,10 +353,10 @@
 	}
 	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->icmph.type, 
-			       icmp_param->icmph.code)) { 
+	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+sizeof(struct icmphdr),
+			      icmp_param->data_len+icmp_param->head_len,
 			      &ipc, rt, MSG_DONTWAIT);
 	}
 	ip_rt_put(rt);
@@ -543,10 +375,9 @@
  *			MUST reply to only the first fragment.
  */
 
-void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info)
+void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info)
 {
 	struct iphdr *iph;
-	struct icmphdr *icmph;
 	int room;
 	struct icmp_bxm icmp_param;
 	struct rtable *rt = (struct rtable*)skb_in->dst;
@@ -558,10 +389,15 @@
 		return;
 
 	/*
-	 *	Find the original header
+	 *	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
 	 */
@@ -589,17 +425,24 @@
 		 *	We are an error, check if we are replying to an ICMP error
 		 */
 		if (iph->protocol==IPPROTO_ICMP) {
-			icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+			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 (icmph->type>NR_ICMP_TYPES || icmp_pointers[icmph->type].error)
+			if (inner_type>NR_ICMP_TYPES || icmp_pointers[inner_type].error)
 				return;
 		}
 	}
 
-
 	if (icmp_xmit_lock())
 		return;
 
@@ -625,12 +468,6 @@
 		((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :
 			iph->tos;
 
-	/* XXX: use a more aggressive expire for routes created by 
-	 * this call (not longer than the rate limit timeout). 
-	 * It could be also worthwhile to not put them into ipv4
-	 * fast routing cache at first. Otherwise an attacker can
-	 * grow the routing table.
-	 */
 	if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
 		goto out;
 
@@ -642,13 +479,14 @@
 	 *	Prepare data for ICMP header.
 	 */
 
-	icmp_param.icmph.type=type;
-	icmp_param.icmph.code=code;
-	icmp_param.icmph.un.gateway = info;
-	icmp_param.icmph.checksum=0;
+	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.data_ptr=iph;
-	icmp_out_count(icmp_param.icmph.type);
+	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;
@@ -669,10 +507,11 @@
 	room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
 	room -= sizeof(struct icmphdr);
 
-	icmp_param.data_len=(skb_in->tail-(u8*)iph);
+	icmp_param.data_len=skb_in->len-icmp_param.offset;
 	if (icmp_param.data_len > room)
 		icmp_param.data_len = room;
-	
+	icmp_param.head_len = sizeof(struct icmphdr);
+
 	ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param, 
 		icmp_param.data_len+sizeof(struct icmphdr),
 		&ipc, rt, MSG_DONTWAIT);
@@ -688,28 +527,35 @@
  *	Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH. 
  */
 
-static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_unreach(struct sk_buff *skb)
 {
 	struct iphdr *iph;
-	int hash;
+	struct icmphdr *icmph;
+	int hash, protocol;
 	struct inet_protocol *ipprot;
-	unsigned char *dp;
 	struct sock *raw_sk;
-	
+	u32 info = 0;
+
 	/*
 	 *	Incomplete header ?
 	 * 	Only checks for the IP header, there should be an
 	 *	additional check for longer headers in upper levels.
 	 */
 
-	if(len<sizeof(struct iphdr)) {
+	if (!pskb_may_pull(skb, sizeof(struct iphdr))) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return;
 	}
-		
-	iph = (struct iphdr *) (icmph + 1);
-	dp = (unsigned char*)iph;
-	
+
+	icmph = skb->h.icmph;
+	iph = (struct iphdr *) skb->data;
+
+	if (iph->ihl<5) {
+		/* Mangled header, drop. */
+		ICMP_INC_STATS_BH(IcmpInErrors);
+		return;
+	}
+
 	if(icmph->type==ICMP_DEST_UNREACH) {
 		switch(icmph->code & 15) {
 			case ICMP_NET_UNREACH:
@@ -726,11 +572,9 @@
 						printk(KERN_INFO "ICMP: %u.%u.%u.%u: fragmentation needed and DF set.\n",
 						       NIPQUAD(iph->daddr));
 				} else {
-					unsigned short new_mtu;
-					new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
-					if (!new_mtu) 
-						return;
-					icmph->un.frag.mtu = htons(new_mtu);
+					info = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
+					if (!info) 
+						goto out;
 				}
 				break;
 			case ICMP_SR_FAILED:
@@ -740,10 +584,12 @@
 			default:
 				break;
 		}
-		if (icmph->code>NR_ICMP_UNREACH) 
-			return;
+		if (icmph->code>NR_ICMP_UNREACH)
+			goto out;
+	} else if (icmph->type == ICMP_PARAMETERPROB) {
+		info = ntohl(icmph->un.gateway)>>24;
 	}
-	
+
 	/*
 	 *	Throw it at our lower layers
 	 *
@@ -751,7 +597,7 @@
 	 *	RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer.
 	 *	RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
 	 */
-	 
+
 	/*
 	 *	Check the other end isnt violating RFC 1122. Some routers send
 	 *	bogus responses to broadcast frames. If you see this message
@@ -767,23 +613,33 @@
 			if (net_ratelimit())
 				printk(KERN_WARNING "%u.%u.%u.%u sent an invalid ICMP error to a broadcast.\n",
 			       	NIPQUAD(skb->nh.iph->saddr));
-			return; 
+			goto out;
 		}
 	}
 
+	/* Checkin full IP header plus 8 bytes of protocol to
+	 * avoid additional coding at protocol handlers.
+	 */
+	if (!pskb_may_pull(skb, iph->ihl*4+8))
+		goto out;
+
+	iph = (struct iphdr *) skb->data;
+	protocol = iph->protocol;
+
 	/*
 	 *	Deliver ICMP message to raw sockets. Pretty useless feature?
 	 */
 
 	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-	hash = iph->protocol & (MAX_INET_PROTOS - 1);
+	hash = protocol & (MAX_INET_PROTOS - 1);
 	read_lock(&raw_v4_lock);
 	if ((raw_sk = raw_v4_htable[hash]) != NULL) 
 	{
-		while ((raw_sk = __raw_v4_lookup(raw_sk, iph->protocol, iph->saddr,
+		while ((raw_sk = __raw_v4_lookup(raw_sk, protocol, iph->saddr,
 						 iph->daddr, skb->dev->ifindex)) != NULL) {
-			raw_err(raw_sk, skb);
+			raw_err(raw_sk, skb, info);
 			raw_sk = raw_sk->next;
+			iph = (struct iphdr *)skb->data;
 		}
 	}
 	read_unlock(&raw_v4_lock);
@@ -807,11 +663,12 @@
 		/* RFC1122: OK. Passes appropriate ICMP errors to the */
 		/* appropriate protocol layer (MUST), as per 3.2.2. */
 
-		if (iph->protocol == ipprot->protocol && ipprot->err_handler)
- 			ipprot->err_handler(skb, dp, len);
+		if (protocol == ipprot->protocol && ipprot->err_handler)
+ 			ipprot->err_handler(skb, info);
 
 		ipprot = nextip;
   	}
+out:;
 }
 
 
@@ -819,24 +676,26 @@
  *	Handle ICMP_REDIRECT. 
  */
 
-static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_redirect(struct sk_buff *skb)
 {
 	struct iphdr *iph;
 	unsigned long ip;
 
-	if (len < sizeof(struct iphdr)) {
+	if (skb->len < sizeof(struct iphdr)) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return; 
 	}
-		
+
 	/*
 	 *	Get the copied header of the packet that caused the redirect
 	 */
-	 
-	iph = (struct iphdr *) (icmph + 1);
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		return;
+
+	iph = (struct iphdr *) skb->data;
 	ip = iph->daddr;
 
-	switch(icmph->code & 7) {
+	switch(skb->h.icmph->code & 7) {
 		case ICMP_REDIR_NET:
 		case ICMP_REDIR_NETTOS:
 			/*
@@ -846,7 +705,7 @@
 			 
 		case ICMP_REDIR_HOST:
 		case ICMP_REDIR_HOSTTOS:
-			ip_rt_redirect(skb->nh.iph->saddr, ip, icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
+			ip_rt_redirect(skb->nh.iph->saddr, ip, skb->h.icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
 			break;
 		default:
 			break;
@@ -862,15 +721,17 @@
  *	See also WRT handling of options once they are done and working.
  */
 
-static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_echo(struct sk_buff *skb)
 {
 	if (!sysctl_icmp_echo_ignore_all) {
 		struct icmp_bxm icmp_param;
 
-		icmp_param.icmph=*icmph;
-		icmp_param.icmph.type=ICMP_ECHOREPLY;
-		icmp_param.data_ptr=(icmph+1);
-		icmp_param.data_len=len;
+		icmp_param.data.icmph=*skb->h.icmph;
+		icmp_param.data.icmph.type=ICMP_ECHOREPLY;
+		icmp_param.skb=skb;
+		icmp_param.offset=0;
+		icmp_param.data_len=skb->len;
+		icmp_param.head_len=sizeof(struct icmphdr);
 		icmp_reply(&icmp_param, skb);
 	}
 }
@@ -883,34 +744,35 @@
  *		  MUST be updated at least at 15Hz.
  */
  
-static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_timestamp(struct sk_buff *skb)
 {
 	struct timeval tv;
-	__u32 times[3];		/* So the new timestamp works on ALPHA's.. */
 	struct icmp_bxm icmp_param;
 	
 	/*
 	 *	Too short.
 	 */
 	 
-	if(len<12) {
+	if(skb->len<4) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return;
 	}
-	
+
 	/*
 	 *	Fill in the current time as ms since midnight UT: 
 	 */
-	 
 	do_gettimeofday(&tv);
-	times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-	times[2] = times[1];
-	memcpy((void *)&times[0], icmph+1, 4);		/* Incoming stamp */
-	icmp_param.icmph=*icmph;
-	icmp_param.icmph.type=ICMP_TIMESTAMPREPLY;
-	icmp_param.icmph.code=0;
-	icmp_param.data_ptr=&times;
-	icmp_param.data_len=12;
+	icmp_param.data.times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+	icmp_param.data.times[2] = icmp_param.data.times[1];
+	if (skb_copy_bits(skb, 0, &icmp_param.data.times[0], 4))
+		BUG();
+	icmp_param.data.icmph=*skb->h.icmph;
+	icmp_param.data.icmph.type=ICMP_TIMESTAMPREPLY;
+	icmp_param.data.icmph.code=0;
+	icmp_param.skb=skb;
+	icmp_param.offset=0;
+	icmp_param.data_len=0;
+	icmp_param.head_len=sizeof(struct icmphdr)+12;
 	icmp_reply(&icmp_param, skb);
 }
 
@@ -948,7 +810,7 @@
  * anyway...
  */
 
-static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_address(struct sk_buff *skb)
 {
 #if 0
 	if (net_ratelimit())
@@ -961,7 +823,7 @@
  *			loudly if an inconsistency is found.
  */
 
-static void icmp_address_reply(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_address_reply(struct sk_buff *skb)
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
 	struct net_device *dev = skb->dev;
@@ -969,7 +831,7 @@
 	struct in_ifaddr *ifa;
 	u32 mask;
 
-	if (len < 4 || !(rt->rt_flags&RTCF_DIRECTSRC))
+	if (skb->len < 4 || !(rt->rt_flags&RTCF_DIRECTSRC))
 		return;
 
 	in_dev = in_dev_get(dev);
@@ -979,8 +841,8 @@
 	if (in_dev->ifa_list &&
 	    IN_DEV_LOG_MARTIANS(in_dev) &&
 	    IN_DEV_FORWARD(in_dev)) {
-
-		mask = *(u32*)&icmph[1];
+		if (skb_copy_bits(skb, 0, &mask, 4))
+			BUG();
 		for (ifa=in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
 			if (mask == ifa->ifa_mask && inet_ifa_match(rt->rt_src, ifa))
 				break;
@@ -994,7 +856,7 @@
 	in_dev_put(in_dev);
 }
 
-static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_discard(struct sk_buff *skb)
 {
 }
 
@@ -1002,23 +864,36 @@
  *	Deal with incoming ICMP packets.
  */
  
-int icmp_rcv(struct sk_buff *skb, unsigned short len)
+int icmp_rcv(struct sk_buff *skb)
 {
 	struct icmphdr *icmph = skb->h.icmph;
 	struct rtable *rt = (struct rtable*)skb->dst;
 
 	ICMP_INC_STATS_BH(IcmpInMsgs);
 
+	switch (skb->ip_summed) {
+	case CHECKSUM_HW:
+		if ((u16)csum_fold(skb->csum) == 0)
+			break;
+		NETDEBUG(printk(KERN_DEBUG "icmp v4 hw csum failure\n"));
+	case CHECKSUM_NONE:
+		if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0)))
+			goto error;
+	default:;
+	}
+
+	if (!pskb_pull(skb, sizeof(struct icmphdr)))
+		goto error;
+
 	/*
 	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
 	 *
 	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently discarded.
 	 */
-	if(len < sizeof(struct icmphdr) ||
-	   ip_compute_csum((unsigned char *) icmph, len) ||
-	   icmph->type > NR_ICMP_TYPES)
+	if (icmph->type > NR_ICMP_TYPES)
 		goto error;
-	 
+
+
 	/*
 	 *	Parse the ICMP message 
 	 */
@@ -1042,9 +917,8 @@
   		}
 	}
 
-	len -= sizeof(struct icmphdr);
 	icmp_pointers[icmph->type].input[smp_processor_id()*2*sizeof(struct icmp_mib)/sizeof(unsigned long)]++;
-	(icmp_pointers[icmph->type].handler)(icmph, skb, len);
+	(icmp_pointers[icmph->type].handler)(skb);
 
 drop:
 	kfree_skb(skb);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)