[747] in linux-net channel archive

home help back first fref pref prev next nref lref last post

eth_copy_and_sum() comments + patch.

daemon@ATHENA.MIT.EDU (Tom May)
Fri Jul 21 14:48:32 1995

Date: Thu, 20 Jul 1995 19:04:21 -0700
From: ftom@netcom.com (Tom May)
To: linux-net@vger.rutgers.edu

Some notes on eth_copy_and_sum() from 1.3.11.  But first my apologies
for being such a micro-optimizer.  I do get carried away sometimes.
But I think note #1 at least is fairly worthwhile.

1. Assuming that the source and destination of the copy are 4-byte
aligned (which I think is true), it is best not to break the copy of
non-IP packets into two pieces because the second piece will not be
aligned.  (For the IP packet case, csum_partial_copy() can deal with
2-byte alignment so that's not a problem.)  The following patch fixes
this.

2. I added a new variable ip_length because gcc (2.7.0) was not
eliminating the common subexpression completely and making some really
funny code as a result.  (In general, using inline functions seems to
result in suboptimal register usage on x86.)  I know that's sort of a
bad reason, but I think the code also gets slightly easier to read in
the process . . . (now if only dest were allocated in a variable that
would live across the call to csum_partial_copy()).

3. It scares me to trust the value in iph->tot_len so readily.
Suppose it were equal to 10 because we got a broken packet from
somewhere.  Then 10 - 20 = -10 which is certainly < length, but if we
handed it to csum_partial_copy() it would be treated as unsigned and
the result would be a disaster.  In this routine we are saved by the
fact that ntohs()-sizeof() is unsigned, so the comparison is unsigned,
and since -10 looks like a huge positive unsigned number it will not
be < length.  Maybe all packet size variables should be declared as
unsigned?  (I recall hearing a while back that NT would crash if it
were sent certain malformed packets.  I certainly wouldn't want that
to happen to Linux.)

4. This isn't a comment about eth_copy_and_sum(), but I decided to
toss it in anyway. There seems to be some schizophrenia in eth.c
(and maybe elsewhere) between using ETH_ALEN and using dev->addr_len.
ETH_ALEN is certainly easier for the compiler to deal with.  Since
this is ethernet-specific code it shouldn't hurt to use ETH_ALEN
for everything for consistency and compilability.

This patch implements 1 & 2 above.  I also expanded the comment so
nobody else would get suckered into thinking they could just set
length = ntohs()-sizeof() and go for it :-)

--- linux/net/ethernet/eth.c.0	Thu Jul 20 15:05:08 1995
+++ linux/net/ethernet/eth.c	Thu Jul 20 16:54:09 1995
@@ -239,23 +239,27 @@
 {
 	struct ethhdr *eth;
 	struct iphdr *iph;
+	int ip_length;
 
 	IS_SKB(dest);
 	eth=(struct ethhdr *)dest->data;
-	memcpy(dest->data,src,34);	/* ethernet is always >= 60 */
-	length-=34;
 	if(eth->h_proto!=htons(ETH_P_IP))
 	{
-		memcpy(dest->data+34,src+34,length);
+		memcpy(dest->data,src,length);
 		return;
 	}
 	/*
 	 * We have to watch for padded packets. The csum doesn't include the
 	 * padding, and there is no point in copying the padding anyway.
+	 * We have to use the smaller of length and ip_length because it
+	 * can happen that ip_length > length.
 	 */
+	memcpy(dest->data,src,34);	/* ethernet is always >= 34 */
+	length -= 34;
 	iph=(struct iphdr*)(src+14);	/* 14 = Rx_addr+Tx_addr+type_field */
-	if (ntohs(iph->tot_len)-sizeof(struct iphdr) <= length)
-		length=ntohs(iph->tot_len)-sizeof(struct iphdr);
+	ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr);
+	if (ip_length <= length)
+		length=ip_length;
 
 	dest->csum=csum_partial_copy(src+34,dest->data+34,length,base);
 	dest->ip_summed=1;

Tom.

home help back first fref pref prev next nref lref last post