diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-03-25 23:10:56 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:23:51 -0400 |
commit | 759e5d006462d53fb708daa8284b4ad909415da1 (patch) | |
tree | edcc4e9d975199b3fe5e2aadc3d1e06824755e75 | |
parent | 1ab6eb62b02e0949a392fb19bf31ba59ae1022b1 (diff) |
[UDP]: Clean up UDP-Lite receive checksum
This patch eliminates some duplicate code for the verification of
receive checksums between UDP-Lite and UDP. It does this by
introducing __skb_checksum_complete_head which is identical to
__skb_checksum_complete_head apart from the fact that it takes
a length parameter rather than computing the first skb->len bytes.
As a result UDP-Lite will be able to use hardware checksum offload
for packets which do not use partial coverage checksums. It also
means that UDP-Lite loopback no longer does unnecessary checksum
verification.
If any NICs start support UDP-Lite this would also start working
automatically.
This patch removes the assumption that msg_flags has MSG_TRUNC clear
upon entry in recvmsg.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/skbuff.h | 1 | ||||
-rw-r--r-- | include/net/udp.h | 5 | ||||
-rw-r--r-- | include/net/udplite.h | 39 | ||||
-rw-r--r-- | net/core/datagram.c | 10 | ||||
-rw-r--r-- | net/ipv4/udp.c | 96 | ||||
-rw-r--r-- | net/ipv4/udplite.c | 2 | ||||
-rw-r--r-- | net/ipv6/udp.c | 74 | ||||
-rw-r--r-- | net/ipv6/udplite.c | 2 |
8 files changed, 109 insertions, 120 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 30089adb2e78..df229bd5f1a9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -1372,6 +1372,7 @@ static inline void __net_timestamp(struct sk_buff *skb) | |||
1372 | } | 1372 | } |
1373 | 1373 | ||
1374 | 1374 | ||
1375 | extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); | ||
1375 | extern __sum16 __skb_checksum_complete(struct sk_buff *skb); | 1376 | extern __sum16 __skb_checksum_complete(struct sk_buff *skb); |
1376 | 1377 | ||
1377 | /** | 1378 | /** |
diff --git a/include/net/udp.h b/include/net/udp.h index 1b921fa81474..4a9699f79281 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -72,10 +72,7 @@ struct sk_buff; | |||
72 | */ | 72 | */ |
73 | static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb) | 73 | static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb) |
74 | { | 74 | { |
75 | if (! UDP_SKB_CB(skb)->partial_cov) | 75 | return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov); |
76 | return __skb_checksum_complete(skb); | ||
77 | return csum_fold(skb_checksum(skb, 0, UDP_SKB_CB(skb)->cscov, | ||
78 | skb->csum)); | ||
79 | } | 76 | } |
80 | 77 | ||
81 | static inline int udp_lib_checksum_complete(struct sk_buff *skb) | 78 | static inline int udp_lib_checksum_complete(struct sk_buff *skb) |
diff --git a/include/net/udplite.h b/include/net/udplite.h index 67ac51424307..d99df75fe54c 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h | |||
@@ -47,11 +47,10 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) | |||
47 | return 1; | 47 | return 1; |
48 | } | 48 | } |
49 | 49 | ||
50 | UDP_SKB_CB(skb)->partial_cov = 0; | ||
51 | cscov = ntohs(uh->len); | 50 | cscov = ntohs(uh->len); |
52 | 51 | ||
53 | if (cscov == 0) /* Indicates that full coverage is required. */ | 52 | if (cscov == 0) /* Indicates that full coverage is required. */ |
54 | cscov = skb->len; | 53 | ; |
55 | else if (cscov < 8 || cscov > skb->len) { | 54 | else if (cscov < 8 || cscov > skb->len) { |
56 | /* | 55 | /* |
57 | * Coverage length violates RFC 3828: log and discard silently. | 56 | * Coverage length violates RFC 3828: log and discard silently. |
@@ -60,42 +59,16 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) | |||
60 | cscov, skb->len); | 59 | cscov, skb->len); |
61 | return 1; | 60 | return 1; |
62 | 61 | ||
63 | } else if (cscov < skb->len) | 62 | } else if (cscov < skb->len) { |
64 | UDP_SKB_CB(skb)->partial_cov = 1; | 63 | UDP_SKB_CB(skb)->partial_cov = 1; |
65 | 64 | UDP_SKB_CB(skb)->cscov = cscov; | |
66 | UDP_SKB_CB(skb)->cscov = cscov; | 65 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
67 | 66 | skb->ip_summed = CHECKSUM_NONE; | |
68 | /* | 67 | } |
69 | * There is no known NIC manufacturer supporting UDP-Lite yet, | ||
70 | * hence ip_summed is always (re-)set to CHECKSUM_NONE. | ||
71 | */ | ||
72 | skb->ip_summed = CHECKSUM_NONE; | ||
73 | 68 | ||
74 | return 0; | 69 | return 0; |
75 | } | 70 | } |
76 | 71 | ||
77 | static __inline__ int udplite4_csum_init(struct sk_buff *skb, struct udphdr *uh) | ||
78 | { | ||
79 | int rc = udplite_checksum_init(skb, uh); | ||
80 | |||
81 | if (!rc) | ||
82 | skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, | ||
83 | skb->nh.iph->daddr, | ||
84 | skb->len, IPPROTO_UDPLITE, 0); | ||
85 | return rc; | ||
86 | } | ||
87 | |||
88 | static __inline__ int udplite6_csum_init(struct sk_buff *skb, struct udphdr *uh) | ||
89 | { | ||
90 | int rc = udplite_checksum_init(skb, uh); | ||
91 | |||
92 | if (!rc) | ||
93 | skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
94 | &skb->nh.ipv6h->daddr, | ||
95 | skb->len, IPPROTO_UDPLITE, 0)); | ||
96 | return rc; | ||
97 | } | ||
98 | |||
99 | static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh) | 72 | static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh) |
100 | { | 73 | { |
101 | int cscov = up->len; | 74 | int cscov = up->len; |
diff --git a/net/core/datagram.c b/net/core/datagram.c index 186212b5b7da..cb056f476126 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -411,11 +411,11 @@ fault: | |||
411 | return -EFAULT; | 411 | return -EFAULT; |
412 | } | 412 | } |
413 | 413 | ||
414 | __sum16 __skb_checksum_complete(struct sk_buff *skb) | 414 | __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len) |
415 | { | 415 | { |
416 | __sum16 sum; | 416 | __sum16 sum; |
417 | 417 | ||
418 | sum = csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); | 418 | sum = csum_fold(skb_checksum(skb, 0, len, skb->csum)); |
419 | if (likely(!sum)) { | 419 | if (likely(!sum)) { |
420 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) | 420 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) |
421 | netdev_rx_csum_fault(skb->dev); | 421 | netdev_rx_csum_fault(skb->dev); |
@@ -423,6 +423,12 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb) | |||
423 | } | 423 | } |
424 | return sum; | 424 | return sum; |
425 | } | 425 | } |
426 | EXPORT_SYMBOL(__skb_checksum_complete_head); | ||
427 | |||
428 | __sum16 __skb_checksum_complete(struct sk_buff *skb) | ||
429 | { | ||
430 | return __skb_checksum_complete_head(skb, skb->len); | ||
431 | } | ||
426 | EXPORT_SYMBOL(__skb_checksum_complete); | 432 | EXPORT_SYMBOL(__skb_checksum_complete); |
427 | 433 | ||
428 | /** | 434 | /** |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index fc620a7c1db4..86368832d481 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -810,7 +810,9 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
810 | struct inet_sock *inet = inet_sk(sk); | 810 | struct inet_sock *inet = inet_sk(sk); |
811 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; | 811 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; |
812 | struct sk_buff *skb; | 812 | struct sk_buff *skb; |
813 | int copied, err, copy_only, is_udplite = IS_UDPLITE(sk); | 813 | unsigned int ulen, copied; |
814 | int err; | ||
815 | int is_udplite = IS_UDPLITE(sk); | ||
814 | 816 | ||
815 | /* | 817 | /* |
816 | * Check any passed addresses | 818 | * Check any passed addresses |
@@ -826,28 +828,25 @@ try_again: | |||
826 | if (!skb) | 828 | if (!skb) |
827 | goto out; | 829 | goto out; |
828 | 830 | ||
829 | copied = skb->len - sizeof(struct udphdr); | 831 | ulen = skb->len - sizeof(struct udphdr); |
830 | if (copied > len) { | 832 | copied = len; |
831 | copied = len; | 833 | if (copied > ulen) |
834 | copied = ulen; | ||
835 | else if (copied < ulen) | ||
832 | msg->msg_flags |= MSG_TRUNC; | 836 | msg->msg_flags |= MSG_TRUNC; |
833 | } | ||
834 | 837 | ||
835 | /* | 838 | /* |
836 | * Decide whether to checksum and/or copy data. | 839 | * If checksum is needed at all, try to do it while copying the |
837 | * | 840 | * data. If the data is truncated, or if we only want a partial |
838 | * UDP: checksum may have been computed in HW, | 841 | * coverage checksum (UDP-Lite), do it before the copy. |
839 | * (re-)compute it if message is truncated. | ||
840 | * UDP-Lite: always needs to checksum, no HW support. | ||
841 | */ | 842 | */ |
842 | copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY); | ||
843 | 843 | ||
844 | if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) { | 844 | if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { |
845 | if (__udp_lib_checksum_complete(skb)) | 845 | if (udp_lib_checksum_complete(skb)) |
846 | goto csum_copy_err; | 846 | goto csum_copy_err; |
847 | copy_only = 1; | ||
848 | } | 847 | } |
849 | 848 | ||
850 | if (copy_only) | 849 | if (skb->ip_summed == CHECKSUM_UNNECESSARY) |
851 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), | 850 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), |
852 | msg->msg_iov, copied ); | 851 | msg->msg_iov, copied ); |
853 | else { | 852 | else { |
@@ -875,7 +874,7 @@ try_again: | |||
875 | 874 | ||
876 | err = copied; | 875 | err = copied; |
877 | if (flags & MSG_TRUNC) | 876 | if (flags & MSG_TRUNC) |
878 | err = skb->len - sizeof(struct udphdr); | 877 | err = ulen; |
879 | 878 | ||
880 | out_free: | 879 | out_free: |
881 | skb_free_datagram(sk, skb); | 880 | skb_free_datagram(sk, skb); |
@@ -1095,10 +1094,9 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | |||
1095 | } | 1094 | } |
1096 | } | 1095 | } |
1097 | 1096 | ||
1098 | if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) { | 1097 | if (sk->sk_filter) { |
1099 | if (__udp_lib_checksum_complete(skb)) | 1098 | if (udp_lib_checksum_complete(skb)) |
1100 | goto drop; | 1099 | goto drop; |
1101 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1102 | } | 1100 | } |
1103 | 1101 | ||
1104 | if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { | 1102 | if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { |
@@ -1166,25 +1164,36 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb, | |||
1166 | * Otherwise, csum completion requires chacksumming packet body, | 1164 | * Otherwise, csum completion requires chacksumming packet body, |
1167 | * including udp header and folding it to skb->csum. | 1165 | * including udp header and folding it to skb->csum. |
1168 | */ | 1166 | */ |
1169 | static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh) | 1167 | static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, |
1168 | int proto) | ||
1170 | { | 1169 | { |
1170 | int err; | ||
1171 | |||
1172 | UDP_SKB_CB(skb)->partial_cov = 0; | ||
1173 | UDP_SKB_CB(skb)->cscov = skb->len; | ||
1174 | |||
1175 | if (proto == IPPROTO_UDPLITE) { | ||
1176 | err = udplite_checksum_init(skb, uh); | ||
1177 | if (err) | ||
1178 | return err; | ||
1179 | } | ||
1180 | |||
1171 | if (uh->check == 0) { | 1181 | if (uh->check == 0) { |
1172 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1182 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1173 | } else if (skb->ip_summed == CHECKSUM_COMPLETE) { | 1183 | } else if (skb->ip_summed == CHECKSUM_COMPLETE) { |
1174 | if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr, | 1184 | if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr, |
1175 | skb->len, IPPROTO_UDP, skb->csum )) | 1185 | skb->len, proto, skb->csum)) |
1176 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1186 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1177 | } | 1187 | } |
1178 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | 1188 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) |
1179 | skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, | 1189 | skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, |
1180 | skb->nh.iph->daddr, | 1190 | skb->nh.iph->daddr, |
1181 | skb->len, IPPROTO_UDP, 0); | 1191 | skb->len, proto, 0); |
1182 | /* Probably, we should checksum udp header (it should be in cache | 1192 | /* Probably, we should checksum udp header (it should be in cache |
1183 | * in any case) and data in tiny packets (< rx copybreak). | 1193 | * in any case) and data in tiny packets (< rx copybreak). |
1184 | */ | 1194 | */ |
1185 | 1195 | ||
1186 | /* UDP = UDP-Lite with a non-partial checksum coverage */ | 1196 | return 0; |
1187 | UDP_SKB_CB(skb)->partial_cov = 0; | ||
1188 | } | 1197 | } |
1189 | 1198 | ||
1190 | /* | 1199 | /* |
@@ -1192,7 +1201,7 @@ static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh) | |||
1192 | */ | 1201 | */ |
1193 | 1202 | ||
1194 | int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | 1203 | int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], |
1195 | int is_udplite) | 1204 | int proto) |
1196 | { | 1205 | { |
1197 | struct sock *sk; | 1206 | struct sock *sk; |
1198 | struct udphdr *uh = skb->h.uh; | 1207 | struct udphdr *uh = skb->h.uh; |
@@ -1211,19 +1220,16 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | |||
1211 | if (ulen > skb->len) | 1220 | if (ulen > skb->len) |
1212 | goto short_packet; | 1221 | goto short_packet; |
1213 | 1222 | ||
1214 | if(! is_udplite ) { /* UDP validates ulen. */ | 1223 | if (proto == IPPROTO_UDP) { |
1215 | 1224 | /* UDP validates ulen. */ | |
1216 | if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) | 1225 | if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) |
1217 | goto short_packet; | 1226 | goto short_packet; |
1218 | uh = skb->h.uh; | 1227 | uh = skb->h.uh; |
1219 | |||
1220 | udp4_csum_init(skb, uh); | ||
1221 | |||
1222 | } else { /* UDP-Lite validates cscov. */ | ||
1223 | if (udplite4_csum_init(skb, uh)) | ||
1224 | goto csum_error; | ||
1225 | } | 1228 | } |
1226 | 1229 | ||
1230 | if (udp4_csum_init(skb, uh, proto)) | ||
1231 | goto csum_error; | ||
1232 | |||
1227 | if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) | 1233 | if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) |
1228 | return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); | 1234 | return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); |
1229 | 1235 | ||
@@ -1250,7 +1256,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | |||
1250 | if (udp_lib_checksum_complete(skb)) | 1256 | if (udp_lib_checksum_complete(skb)) |
1251 | goto csum_error; | 1257 | goto csum_error; |
1252 | 1258 | ||
1253 | UDP_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite); | 1259 | UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); |
1254 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | 1260 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
1255 | 1261 | ||
1256 | /* | 1262 | /* |
@@ -1262,7 +1268,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | |||
1262 | 1268 | ||
1263 | short_packet: | 1269 | short_packet: |
1264 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n", | 1270 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n", |
1265 | is_udplite? "-Lite" : "", | 1271 | proto == IPPROTO_UDPLITE ? "-Lite" : "", |
1266 | NIPQUAD(saddr), | 1272 | NIPQUAD(saddr), |
1267 | ntohs(uh->source), | 1273 | ntohs(uh->source), |
1268 | ulen, | 1274 | ulen, |
@@ -1277,21 +1283,21 @@ csum_error: | |||
1277 | * the network is concerned, anyway) as per 4.1.3.4 (MUST). | 1283 | * the network is concerned, anyway) as per 4.1.3.4 (MUST). |
1278 | */ | 1284 | */ |
1279 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n", | 1285 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n", |
1280 | is_udplite? "-Lite" : "", | 1286 | proto == IPPROTO_UDPLITE ? "-Lite" : "", |
1281 | NIPQUAD(saddr), | 1287 | NIPQUAD(saddr), |
1282 | ntohs(uh->source), | 1288 | ntohs(uh->source), |
1283 | NIPQUAD(daddr), | 1289 | NIPQUAD(daddr), |
1284 | ntohs(uh->dest), | 1290 | ntohs(uh->dest), |
1285 | ulen); | 1291 | ulen); |
1286 | drop: | 1292 | drop: |
1287 | UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite); | 1293 | UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); |
1288 | kfree_skb(skb); | 1294 | kfree_skb(skb); |
1289 | return(0); | 1295 | return(0); |
1290 | } | 1296 | } |
1291 | 1297 | ||
1292 | __inline__ int udp_rcv(struct sk_buff *skb) | 1298 | __inline__ int udp_rcv(struct sk_buff *skb) |
1293 | { | 1299 | { |
1294 | return __udp4_lib_rcv(skb, udp_hash, 0); | 1300 | return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP); |
1295 | } | 1301 | } |
1296 | 1302 | ||
1297 | int udp_destroy_sock(struct sock *sk) | 1303 | int udp_destroy_sock(struct sock *sk) |
@@ -1486,15 +1492,11 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) | |||
1486 | struct sk_buff *skb; | 1492 | struct sk_buff *skb; |
1487 | 1493 | ||
1488 | spin_lock_bh(&rcvq->lock); | 1494 | spin_lock_bh(&rcvq->lock); |
1489 | while ((skb = skb_peek(rcvq)) != NULL) { | 1495 | while ((skb = skb_peek(rcvq)) != NULL && |
1490 | if (udp_lib_checksum_complete(skb)) { | 1496 | udp_lib_checksum_complete(skb)) { |
1491 | UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite); | 1497 | UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite); |
1492 | __skb_unlink(skb, rcvq); | 1498 | __skb_unlink(skb, rcvq); |
1493 | kfree_skb(skb); | 1499 | kfree_skb(skb); |
1494 | } else { | ||
1495 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1496 | break; | ||
1497 | } | ||
1498 | } | 1500 | } |
1499 | spin_unlock_bh(&rcvq->lock); | 1501 | spin_unlock_bh(&rcvq->lock); |
1500 | 1502 | ||
diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index b28fe1edf98b..f34fd686a8f1 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c | |||
@@ -31,7 +31,7 @@ static int udplite_v4_get_port(struct sock *sk, unsigned short snum) | |||
31 | 31 | ||
32 | static int udplite_rcv(struct sk_buff *skb) | 32 | static int udplite_rcv(struct sk_buff *skb) |
33 | { | 33 | { |
34 | return __udp4_lib_rcv(skb, udplite_hash, 1); | 34 | return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE); |
35 | } | 35 | } |
36 | 36 | ||
37 | static void udplite_err(struct sk_buff *skb, u32 info) | 37 | static void udplite_err(struct sk_buff *skb, u32 info) |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3413fc22ce4a..733371689795 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -120,8 +120,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
120 | struct ipv6_pinfo *np = inet6_sk(sk); | 120 | struct ipv6_pinfo *np = inet6_sk(sk); |
121 | struct inet_sock *inet = inet_sk(sk); | 121 | struct inet_sock *inet = inet_sk(sk); |
122 | struct sk_buff *skb; | 122 | struct sk_buff *skb; |
123 | size_t copied; | 123 | unsigned int ulen, copied; |
124 | int err, copy_only, is_udplite = IS_UDPLITE(sk); | 124 | int err; |
125 | int is_udplite = IS_UDPLITE(sk); | ||
125 | 126 | ||
126 | if (addr_len) | 127 | if (addr_len) |
127 | *addr_len=sizeof(struct sockaddr_in6); | 128 | *addr_len=sizeof(struct sockaddr_in6); |
@@ -134,24 +135,25 @@ try_again: | |||
134 | if (!skb) | 135 | if (!skb) |
135 | goto out; | 136 | goto out; |
136 | 137 | ||
137 | copied = skb->len - sizeof(struct udphdr); | 138 | ulen = skb->len - sizeof(struct udphdr); |
138 | if (copied > len) { | 139 | copied = len; |
139 | copied = len; | 140 | if (copied > ulen) |
141 | copied = ulen; | ||
142 | else if (copied < ulen) | ||
140 | msg->msg_flags |= MSG_TRUNC; | 143 | msg->msg_flags |= MSG_TRUNC; |
141 | } | ||
142 | 144 | ||
143 | /* | 145 | /* |
144 | * Decide whether to checksum and/or copy data. | 146 | * If checksum is needed at all, try to do it while copying the |
147 | * data. If the data is truncated, or if we only want a partial | ||
148 | * coverage checksum (UDP-Lite), do it before the copy. | ||
145 | */ | 149 | */ |
146 | copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY); | ||
147 | 150 | ||
148 | if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) { | 151 | if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { |
149 | if (__udp_lib_checksum_complete(skb)) | 152 | if (udp_lib_checksum_complete(skb)) |
150 | goto csum_copy_err; | 153 | goto csum_copy_err; |
151 | copy_only = 1; | ||
152 | } | 154 | } |
153 | 155 | ||
154 | if (copy_only) | 156 | if (skb->ip_summed == CHECKSUM_UNNECESSARY) |
155 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), | 157 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), |
156 | msg->msg_iov, copied ); | 158 | msg->msg_iov, copied ); |
157 | else { | 159 | else { |
@@ -194,7 +196,7 @@ try_again: | |||
194 | 196 | ||
195 | err = copied; | 197 | err = copied; |
196 | if (flags & MSG_TRUNC) | 198 | if (flags & MSG_TRUNC) |
197 | err = skb->len - sizeof(struct udphdr); | 199 | err = ulen; |
198 | 200 | ||
199 | out_free: | 201 | out_free: |
200 | skb_free_datagram(sk, skb); | 202 | skb_free_datagram(sk, skb); |
@@ -368,9 +370,20 @@ out: | |||
368 | return 0; | 370 | return 0; |
369 | } | 371 | } |
370 | 372 | ||
371 | static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh) | 373 | static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, |
372 | 374 | int proto) | |
373 | { | 375 | { |
376 | int err; | ||
377 | |||
378 | UDP_SKB_CB(skb)->partial_cov = 0; | ||
379 | UDP_SKB_CB(skb)->cscov = skb->len; | ||
380 | |||
381 | if (proto == IPPROTO_UDPLITE) { | ||
382 | err = udplite_checksum_init(skb, uh); | ||
383 | if (err) | ||
384 | return err; | ||
385 | } | ||
386 | |||
374 | if (uh->check == 0) { | 387 | if (uh->check == 0) { |
375 | /* RFC 2460 section 8.1 says that we SHOULD log | 388 | /* RFC 2460 section 8.1 says that we SHOULD log |
376 | this error. Well, it is reasonable. | 389 | this error. Well, it is reasonable. |
@@ -380,20 +393,19 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh) | |||
380 | } | 393 | } |
381 | if (skb->ip_summed == CHECKSUM_COMPLETE && | 394 | if (skb->ip_summed == CHECKSUM_COMPLETE && |
382 | !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, | 395 | !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, |
383 | skb->len, IPPROTO_UDP, skb->csum )) | 396 | skb->len, proto, skb->csum)) |
384 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 397 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
385 | 398 | ||
386 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | 399 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) |
387 | skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, | 400 | skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, |
388 | &skb->nh.ipv6h->daddr, | 401 | &skb->nh.ipv6h->daddr, |
389 | skb->len, IPPROTO_UDP, | 402 | skb->len, proto, 0)); |
390 | 0)); | ||
391 | 403 | ||
392 | return (UDP_SKB_CB(skb)->partial_cov = 0); | 404 | return 0; |
393 | } | 405 | } |
394 | 406 | ||
395 | int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | 407 | int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], |
396 | int is_udplite) | 408 | int proto) |
397 | { | 409 | { |
398 | struct sk_buff *skb = *pskb; | 410 | struct sk_buff *skb = *pskb; |
399 | struct sock *sk; | 411 | struct sock *sk; |
@@ -413,7 +425,8 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
413 | if (ulen > skb->len) | 425 | if (ulen > skb->len) |
414 | goto short_packet; | 426 | goto short_packet; |
415 | 427 | ||
416 | if(! is_udplite ) { /* UDP validates ulen. */ | 428 | if (proto == IPPROTO_UDP) { |
429 | /* UDP validates ulen. */ | ||
417 | 430 | ||
418 | /* Check for jumbo payload */ | 431 | /* Check for jumbo payload */ |
419 | if (ulen == 0) | 432 | if (ulen == 0) |
@@ -429,15 +442,11 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
429 | daddr = &skb->nh.ipv6h->daddr; | 442 | daddr = &skb->nh.ipv6h->daddr; |
430 | uh = skb->h.uh; | 443 | uh = skb->h.uh; |
431 | } | 444 | } |
432 | |||
433 | if (udp6_csum_init(skb, uh)) | ||
434 | goto discard; | ||
435 | |||
436 | } else { /* UDP-Lite validates cscov. */ | ||
437 | if (udplite6_csum_init(skb, uh)) | ||
438 | goto discard; | ||
439 | } | 445 | } |
440 | 446 | ||
447 | if (udp6_csum_init(skb, uh, proto)) | ||
448 | goto discard; | ||
449 | |||
441 | /* | 450 | /* |
442 | * Multicast receive code | 451 | * Multicast receive code |
443 | */ | 452 | */ |
@@ -459,7 +468,7 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
459 | 468 | ||
460 | if (udp_lib_checksum_complete(skb)) | 469 | if (udp_lib_checksum_complete(skb)) |
461 | goto discard; | 470 | goto discard; |
462 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite); | 471 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); |
463 | 472 | ||
464 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); | 473 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); |
465 | 474 | ||
@@ -475,17 +484,18 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
475 | 484 | ||
476 | short_packet: | 485 | short_packet: |
477 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n", | 486 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n", |
478 | is_udplite? "-Lite" : "", ulen, skb->len); | 487 | proto == IPPROTO_UDPLITE ? "-Lite" : "", |
488 | ulen, skb->len); | ||
479 | 489 | ||
480 | discard: | 490 | discard: |
481 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite); | 491 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); |
482 | kfree_skb(skb); | 492 | kfree_skb(skb); |
483 | return(0); | 493 | return(0); |
484 | } | 494 | } |
485 | 495 | ||
486 | static __inline__ int udpv6_rcv(struct sk_buff **pskb) | 496 | static __inline__ int udpv6_rcv(struct sk_buff **pskb) |
487 | { | 497 | { |
488 | return __udp6_lib_rcv(pskb, udp_hash, 0); | 498 | return __udp6_lib_rcv(pskb, udp_hash, IPPROTO_UDP); |
489 | } | 499 | } |
490 | 500 | ||
491 | /* | 501 | /* |
diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 629f97162fbc..f54016a55004 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c | |||
@@ -19,7 +19,7 @@ DEFINE_SNMP_STAT(struct udp_mib, udplite_stats_in6) __read_mostly; | |||
19 | 19 | ||
20 | static int udplitev6_rcv(struct sk_buff **pskb) | 20 | static int udplitev6_rcv(struct sk_buff **pskb) |
21 | { | 21 | { |
22 | return __udp6_lib_rcv(pskb, udplite_hash, 1); | 22 | return __udp6_lib_rcv(pskb, udplite_hash, IPPROTO_UDPLITE); |
23 | } | 23 | } |
24 | 24 | ||
25 | static void udplitev6_err(struct sk_buff *skb, | 25 | static void udplitev6_err(struct sk_buff *skb, |