diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2006-11-03 03:28:23 -0500 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2006-11-05 17:11:25 -0500 |
| commit | 02e60370d4dac83f22d5ae75d5512bcb9a3f24b7 (patch) | |
| tree | 3952d784c9d7c061a5ff7c0e23277783f10e55d2 /net/ipx | |
| parent | 4833ed094097323f5f219820f6ebdc8dd66f501f (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>
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 c272a38af325..76c661566dfd 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c | |||
| @@ -1234,27 +1234,27 @@ static int ipxitf_ioctl(unsigned int cmd, void __user *arg) | |||
| 1234 | /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */ | 1234 | /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */ |
| 1235 | /* This functions should *not* mess with packet contents */ | 1235 | /* This functions should *not* mess with packet contents */ |
| 1236 | 1236 | ||
| 1237 | __u16 ipx_cksum(struct ipxhdr *packet, int length) | 1237 | __be16 ipx_cksum(struct ipxhdr *packet, int length) |
| 1238 | { | 1238 | { |
| 1239 | /* | 1239 | /* |
| 1240 | * NOTE: sum is a net byte order quantity, which optimizes the | 1240 | * NOTE: sum is a net byte order quantity, which optimizes the |
| 1241 | * loop. This only works on big and little endian machines. (I | 1241 | * loop. This only works on big and little endian machines. (I |
| 1242 | * don't know of a machine that isn't.) | 1242 | * don't know of a machine that isn't.) |
| 1243 | */ | 1243 | */ |
| 1244 | /* start at ipx_dest - We skip the checksum field and start with | 1244 | /* handle the first 3 words separately; checksum should be skipped |
| 1245 | * ipx_type before the loop, not considering ipx_tctrl in the calc */ | 1245 | * and ipx_tctrl masked out */ |
| 1246 | __u16 *p = (__u16 *)&packet->ipx_dest; | 1246 | __u16 *p = (__u16 *)packet; |
| 1247 | __u32 i = (length >> 1) - 1; /* Number of complete words */ | 1247 | __u32 sum = p[1] + (p[2] & (__force u16)htons(0x00ff)); |
| 1248 | __u32 sum = packet->ipx_type << sizeof(packet->ipx_tctrl); | 1248 | __u32 i = (length >> 1) - 3; /* Number of remaining complete words */ |
| 1249 | 1249 | ||
| 1250 | /* Loop through all complete words except the checksum field, | 1250 | /* Loop through them */ |
| 1251 | * ipx_type (accounted above) and ipx_tctrl (not used in the cksum) */ | 1251 | p += 3; |
| 1252 | while (--i) | 1252 | while (i--) |
| 1253 | sum += *p++; | 1253 | sum += *p++; |
| 1254 | 1254 | ||
| 1255 | /* Add on the last part word if it exists */ | 1255 | /* Add on the last part word if it exists */ |
| 1256 | if (packet->ipx_pktsize & htons(1)) | 1256 | if (packet->ipx_pktsize & htons(1)) |
| 1257 | sum += ntohs(0xff00) & *p; | 1257 | sum += (__force u16)htons(0xff00) & *p; |
| 1258 | 1258 | ||
| 1259 | /* Do final fixup */ | 1259 | /* Do final fixup */ |
| 1260 | sum = (sum & 0xffff) + (sum >> 16); | 1260 | sum = (sum & 0xffff) + (sum >> 16); |
| @@ -1263,7 +1263,14 @@ __u16 ipx_cksum(struct ipxhdr *packet, int length) | |||
| 1263 | if (sum >= 0x10000) | 1263 | if (sum >= 0x10000) |
| 1264 | sum++; | 1264 | sum++; |
| 1265 | 1265 | ||
| 1266 | return ~sum; | 1266 | /* |
| 1267 | * Leave 0 alone; we don't want 0xffff here. Note that we can't get | ||
| 1268 | * here with 0x10000, so this check is the same as ((__u16)sum) | ||
| 1269 | */ | ||
| 1270 | if (sum) | ||
| 1271 | sum = ~sum; | ||
| 1272 | |||
| 1273 | return (__force __be16)sum; | ||
| 1267 | } | 1274 | } |
| 1268 | 1275 | ||
| 1269 | const char *ipx_frame_name(__be16 frame) | 1276 | const char *ipx_frame_name(__be16 frame) |
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index 8bfaefaf8841..68560ee0d797 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c | |||
| @@ -19,7 +19,7 @@ DEFINE_RWLOCK(ipx_routes_lock); | |||
| 19 | 19 | ||
| 20 | extern struct ipx_interface *ipx_internal_net; | 20 | extern struct ipx_interface *ipx_internal_net; |
| 21 | 21 | ||
| 22 | extern __u16 ipx_cksum(struct ipxhdr *packet, int length); | 22 | extern __be16 ipx_cksum(struct ipxhdr *packet, int length); |
| 23 | extern struct ipx_interface *ipxitf_find_using_net(__be32 net); | 23 | extern struct ipx_interface *ipxitf_find_using_net(__be32 net); |
| 24 | extern int ipxitf_demux_socket(struct ipx_interface *intrfc, | 24 | extern int ipxitf_demux_socket(struct ipx_interface *intrfc, |
| 25 | struct sk_buff *skb, int copy); | 25 | struct sk_buff *skb, int copy); |
| @@ -238,7 +238,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, | |||
| 238 | 238 | ||
| 239 | /* Apply checksum. Not allowed on 802.3 links. */ | 239 | /* Apply checksum. Not allowed on 802.3 links. */ |
| 240 | if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) | 240 | if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) |
| 241 | ipx->ipx_checksum = 0xFFFF; | 241 | ipx->ipx_checksum = htons(0xFFFF); |
| 242 | else | 242 | else |
| 243 | ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); | 243 | ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); |
| 244 | 244 | ||
