[IPX]: Annotate and fix IPX checksum
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 3 Nov 2006 08:28:23 +0000 (00:28 -0800)
committerDavid S. Miller <davem@sunset.davemloft.net>
Sun, 5 Nov 2006 22:11:25 +0000 (14:11 -0800)
Calculation of IPX checksum got buggered about 2.4.0.  The old variant
mangled the packet; that got fixed, but calculation itself got buggered.
Restored the correct logics, fixed a subtle breakage we used to have even
back then: if the sum is 0 mod 0xffff, we want to return 0, not 0xffff.
The latter has special meaning for IPX (cheksum disabled).  Observation
(and obvious fix) nicked from history of FreeBSD ipx_cksum.c...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ipx.h
net/ipx/af_ipx.c
net/ipx/ipx_route.c

index 4a423d2..c6b2ee6 100644 (file)
@@ -26,8 +26,8 @@ struct ipx_address {
 #define IPX_MAX_PPROP_HOPS 8
 
 struct ipxhdr {
-       __u16                   ipx_checksum __attribute__ ((packed));
-#define IPX_NO_CHECKSUM        0xFFFF
+       __be16                  ipx_checksum __attribute__ ((packed));
+#define IPX_NO_CHECKSUM        __constant_htons(0xFFFF)
        __be16                  ipx_pktsize __attribute__ ((packed));
        __u8                    ipx_tctrl;
        __u8                    ipx_type;
index c272a38..76c6615 100644 (file)
@@ -1234,27 +1234,27 @@ static int ipxitf_ioctl(unsigned int cmd, void __user *arg)
 /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
 /* This functions should *not* mess with packet contents */
 
-__u16 ipx_cksum(struct ipxhdr *packet, int length) 
+__be16 ipx_cksum(struct ipxhdr *packet, int length)
 {
        /* 
         *      NOTE: sum is a net byte order quantity, which optimizes the 
         *      loop. This only works on big and little endian machines. (I
         *      don't know of a machine that isn't.)
         */
-       /* start at ipx_dest - We skip the checksum field and start with
-        * ipx_type before the loop, not considering ipx_tctrl in the calc */
-       __u16 *p = (__u16 *)&packet->ipx_dest;
-       __u32 i = (length >> 1) - 1; /* Number of complete words */
-       __u32 sum = packet->ipx_type << sizeof(packet->ipx_tctrl); 
-
-       /* Loop through all complete words except the checksum field,
-        * ipx_type (accounted above) and ipx_tctrl (not used in the cksum) */
-       while (--i)
+       /* handle the first 3 words separately; checksum should be skipped
+        * and ipx_tctrl masked out */
+       __u16 *p = (__u16 *)packet;
+       __u32 sum = p[1] + (p[2] & (__force u16)htons(0x00ff));
+       __u32 i = (length >> 1) - 3; /* Number of remaining complete words */
+
+       /* Loop through them */
+       p += 3;
+       while (i--)
                sum += *p++;
 
        /* Add on the last part word if it exists */
        if (packet->ipx_pktsize & htons(1))
-               sum += ntohs(0xff00) & *p;
+               sum += (__force u16)htons(0xff00) & *p;
 
        /* Do final fixup */
        sum = (sum & 0xffff) + (sum >> 16);
@@ -1263,7 +1263,14 @@ __u16 ipx_cksum(struct ipxhdr *packet, int length)
        if (sum >= 0x10000)
                sum++;
 
-       return ~sum;
+       /*
+        * Leave 0 alone; we don't want 0xffff here.  Note that we can't get
+        * here with 0x10000, so this check is the same as ((__u16)sum)
+        */
+       if (sum)
+               sum = ~sum;
+
+       return (__force __be16)sum;
 }
 
 const char *ipx_frame_name(__be16 frame)
index 8bfaefa..68560ee 100644 (file)
@@ -19,7 +19,7 @@ DEFINE_RWLOCK(ipx_routes_lock);
 
 extern struct ipx_interface *ipx_internal_net;
 
-extern __u16 ipx_cksum(struct ipxhdr *packet, int length);
+extern __be16 ipx_cksum(struct ipxhdr *packet, int length);
 extern struct ipx_interface *ipxitf_find_using_net(__be32 net);
 extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
                               struct sk_buff *skb, int copy);
@@ -238,7 +238,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
 
        /* Apply checksum. Not allowed on 802.3 links. */
        if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023))
-               ipx->ipx_checksum = 0xFFFF;
+               ipx->ipx_checksum = htons(0xFFFF);
        else
                ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));