diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2006-11-24 03:03:34 +0100 |
---|---|---|
committer | Adrian Bunk <bunk@stusta.de> | 2006-11-24 03:03:34 +0100 |
commit | d6042b2ec9446e7955d72a2155a4dff473ccc511 (patch) | |
tree | 20ace8632910704d6162227fd17f55a458bb4866 /net/ipx | |
parent | 635cc29c54bd88c69a2bbd357401cfaf67190aef (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>
Diffstat (limited to 'net/ipx')
-rw-r--r-- | net/ipx/af_ipx.c | 31 | ||||
-rw-r--r-- | net/ipx/ipx_route.c | 4 |
2 files changed, 21 insertions, 14 deletions
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index c55b2a954b9..8b0c25f74a4 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 67774448efd..5d4ddf71301 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)); |