aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWillem de Bruijn <willemb@google.com>2017-08-10 12:29:19 -0400
committerDavid S. Miller <davem@davemloft.net>2017-08-10 12:52:12 -0400
commit85f1bd9a7b5a79d5baa8bf44af19658f7bf77bfa (patch)
treedcf8a90eb6ae097aa5c962f8bec97e1ce4a5aab7
parent96d9703050a0036a3360ec98bb41e107c90664fe (diff)
udp: consistently apply ufo or fragmentation
When iteratively building a UDP datagram with MSG_MORE and that datagram exceeds MTU, consistently choose UFO or fragmentation. Once skb_is_gso, always apply ufo. Conversely, once a datagram is split across multiple skbs, do not consider ufo. Sendpage already maintains the first invariant, only add the second. IPv6 does not have a sendpage implementation to modify. A gso skb must have a partial checksum, do not follow sk_no_check_tx in udp_send_skb. Found by syzkaller. Fixes: e89e9cf539a2 ("[IPv4/IPv6]: UFO Scatter-gather approach") Reported-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/ip_output.c8
-rw-r--r--net/ipv4/udp.c2
-rw-r--r--net/ipv6/ip6_output.c7
3 files changed, 10 insertions, 7 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 50c74cd890bc..e153c40c2436 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -965,11 +965,12 @@ static int __ip_append_data(struct sock *sk,
965 csummode = CHECKSUM_PARTIAL; 965 csummode = CHECKSUM_PARTIAL;
966 966
967 cork->length += length; 967 cork->length += length;
968 if ((((length + (skb ? skb->len : fragheaderlen)) > mtu) || 968 if ((skb && skb_is_gso(skb)) ||
969 (skb && skb_is_gso(skb))) && 969 (((length + (skb ? skb->len : fragheaderlen)) > mtu) &&
970 (skb_queue_len(queue) <= 1) &&
970 (sk->sk_protocol == IPPROTO_UDP) && 971 (sk->sk_protocol == IPPROTO_UDP) &&
971 (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && 972 (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) &&
972 (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) { 973 (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx)) {
973 err = ip_ufo_append_data(sk, queue, getfrag, from, length, 974 err = ip_ufo_append_data(sk, queue, getfrag, from, length,
974 hh_len, fragheaderlen, transhdrlen, 975 hh_len, fragheaderlen, transhdrlen,
975 maxfraglen, flags); 976 maxfraglen, flags);
@@ -1288,6 +1289,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
1288 return -EINVAL; 1289 return -EINVAL;
1289 1290
1290 if ((size + skb->len > mtu) && 1291 if ((size + skb->len > mtu) &&
1292 (skb_queue_len(&sk->sk_write_queue) == 1) &&
1291 (sk->sk_protocol == IPPROTO_UDP) && 1293 (sk->sk_protocol == IPPROTO_UDP) &&
1292 (rt->dst.dev->features & NETIF_F_UFO)) { 1294 (rt->dst.dev->features & NETIF_F_UFO)) {
1293 if (skb->ip_summed != CHECKSUM_PARTIAL) 1295 if (skb->ip_summed != CHECKSUM_PARTIAL)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index e6276fa3750b..a7c804f73990 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -802,7 +802,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
802 if (is_udplite) /* UDP-Lite */ 802 if (is_udplite) /* UDP-Lite */
803 csum = udplite_csum(skb); 803 csum = udplite_csum(skb);
804 804
805 else if (sk->sk_no_check_tx) { /* UDP csum disabled */ 805 else if (sk->sk_no_check_tx && !skb_is_gso(skb)) { /* UDP csum off */
806 806
807 skb->ip_summed = CHECKSUM_NONE; 807 skb->ip_summed = CHECKSUM_NONE;
808 goto send; 808 goto send;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 162efba0d0cd..2dfe50d8d609 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1381,11 +1381,12 @@ emsgsize:
1381 */ 1381 */
1382 1382
1383 cork->length += length; 1383 cork->length += length;
1384 if ((((length + (skb ? skb->len : headersize)) > mtu) || 1384 if ((skb && skb_is_gso(skb)) ||
1385 (skb && skb_is_gso(skb))) && 1385 (((length + (skb ? skb->len : headersize)) > mtu) &&
1386 (skb_queue_len(queue) <= 1) &&
1386 (sk->sk_protocol == IPPROTO_UDP) && 1387 (sk->sk_protocol == IPPROTO_UDP) &&
1387 (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && 1388 (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) &&
1388 (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) { 1389 (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk))) {
1389 err = ip6_ufo_append_data(sk, queue, getfrag, from, length, 1390 err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
1390 hh_len, fragheaderlen, exthdrlen, 1391 hh_len, fragheaderlen, exthdrlen,
1391 transhdrlen, mtu, flags, fl6); 1392 transhdrlen, mtu, flags, fl6);