summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2006-11-24 03:03:34 +0100
committerAdrian Bunk <bunk@stusta.de>2006-11-24 03:03:34 +0100
commitd6042b2ec9446e7955d72a2155a4dff473ccc511 (patch)
tree20ace8632910704d6162227fd17f55a458bb4866
parent635cc29c54bd88c69a2bbd357401cfaf67190aef (diff)
[IPX]: Annotate and fix IPX checksum
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> Signed-off-by: Adrian Bunk <bunk@stusta.de>
-rw-r--r--include/net/ipx.h4
-rw-r--r--net/ipx/af_ipx.c31
-rw-r--r--net/ipx/ipx_route.c4
3 files changed, 23 insertions, 16 deletions
diff --git a/include/net/ipx.h b/include/net/ipx.h
index 5c0cf33826c5..0447b3cddb74 100644
--- a/include/net/ipx.h
+++ b/include/net/ipx.h
@@ -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)
__u16 ipx_pktsize __attribute__ ((packed));
__u8 ipx_tctrl;
__u8 ipx_type;
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index c55b2a954b9e..8b0c25f74a43 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1235,27 +1235,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);
@@ -1264,7 +1264,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(unsigned short frame)
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c
index 67774448efd9..5d4ddf713016 100644
--- a/net/ipx/ipx_route.c
+++ b/net/ipx/ipx_route.c
@@ -20,7 +20,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(__u32 net);
extern int ipxitf_demux_socket(struct ipx_interface *intrfc,
struct sk_buff *skb, int copy);
@@ -239,7 +239,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 == IPX_FRAME_8023)
- ipx->ipx_checksum = 0xFFFF;
+ ipx->ipx_checksum = htons(0xFFFF);
else
ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));