aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r--net/ipv6/ip6_output.c43
1 files changed, 27 insertions, 16 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 58900c21e4e4..e22339fad10b 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -571,7 +571,10 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
571 int ptr, offset = 0, err = 0; 571 int ptr, offset = 0, err = 0;
572 u8 *prevhdr, nexthdr = 0; 572 u8 *prevhdr, nexthdr = 0;
573 573
574 hlen = ip6_find_1stfragopt(skb, &prevhdr); 574 err = ip6_find_1stfragopt(skb, &prevhdr);
575 if (err < 0)
576 goto fail;
577 hlen = err;
575 nexthdr = *prevhdr; 578 nexthdr = *prevhdr;
576 579
577 mtu = ip6_skb_dst_mtu(skb); 580 mtu = ip6_skb_dst_mtu(skb);
@@ -644,8 +647,6 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
644 *prevhdr = NEXTHDR_FRAGMENT; 647 *prevhdr = NEXTHDR_FRAGMENT;
645 tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC); 648 tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
646 if (!tmp_hdr) { 649 if (!tmp_hdr) {
647 IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
648 IPSTATS_MIB_FRAGFAILS);
649 err = -ENOMEM; 650 err = -ENOMEM;
650 goto fail; 651 goto fail;
651 } 652 }
@@ -742,13 +743,14 @@ slow_path:
742 * Fragment the datagram. 743 * Fragment the datagram.
743 */ 744 */
744 745
745 *prevhdr = NEXTHDR_FRAGMENT;
746 troom = rt->dst.dev->needed_tailroom; 746 troom = rt->dst.dev->needed_tailroom;
747 747
748 /* 748 /*
749 * Keep copying data until we run out. 749 * Keep copying data until we run out.
750 */ 750 */
751 while (left > 0) { 751 while (left > 0) {
752 u8 *fragnexthdr_offset;
753
752 len = left; 754 len = left;
753 /* IF: it doesn't fit, use 'mtu' - the data space left */ 755 /* IF: it doesn't fit, use 'mtu' - the data space left */
754 if (len > mtu) 756 if (len > mtu)
@@ -763,8 +765,6 @@ slow_path:
763 frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) + 765 frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
764 hroom + troom, GFP_ATOMIC); 766 hroom + troom, GFP_ATOMIC);
765 if (!frag) { 767 if (!frag) {
766 IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
767 IPSTATS_MIB_FRAGFAILS);
768 err = -ENOMEM; 768 err = -ENOMEM;
769 goto fail; 769 goto fail;
770 } 770 }
@@ -793,6 +793,10 @@ slow_path:
793 */ 793 */
794 skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); 794 skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
795 795
796 fragnexthdr_offset = skb_network_header(frag);
797 fragnexthdr_offset += prevhdr - skb_network_header(skb);
798 *fragnexthdr_offset = NEXTHDR_FRAGMENT;
799
796 /* 800 /*
797 * Build fragment header. 801 * Build fragment header.
798 */ 802 */
@@ -996,6 +1000,11 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
996 } 1000 }
997 } 1001 }
998#endif 1002#endif
1003 if (ipv6_addr_v4mapped(&fl6->saddr) &&
1004 !(ipv6_addr_v4mapped(&fl6->daddr) || ipv6_addr_any(&fl6->daddr))) {
1005 err = -EAFNOSUPPORT;
1006 goto out_err_release;
1007 }
999 1008
1000 return 0; 1009 return 0;
1001 1010
@@ -1348,11 +1357,12 @@ emsgsize:
1348 */ 1357 */
1349 1358
1350 cork->length += length; 1359 cork->length += length;
1351 if (((length > mtu) || 1360 if ((skb && skb_is_gso(skb)) ||
1352 (skb && skb_is_gso(skb))) && 1361 (((length + (skb ? skb->len : headersize)) > mtu) &&
1362 (skb_queue_len(queue) <= 1) &&
1353 (sk->sk_protocol == IPPROTO_UDP) && 1363 (sk->sk_protocol == IPPROTO_UDP) &&
1354 (rt->dst.dev->features & NETIF_F_UFO) && 1364 (rt->dst.dev->features & NETIF_F_UFO) &&
1355 (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) { 1365 (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk))) {
1356 err = ip6_ufo_append_data(sk, queue, getfrag, from, length, 1366 err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
1357 hh_len, fragheaderlen, exthdrlen, 1367 hh_len, fragheaderlen, exthdrlen,
1358 transhdrlen, mtu, flags, fl6); 1368 transhdrlen, mtu, flags, fl6);
@@ -1424,6 +1434,11 @@ alloc_new_skb:
1424 */ 1434 */
1425 alloclen += sizeof(struct frag_hdr); 1435 alloclen += sizeof(struct frag_hdr);
1426 1436
1437 copy = datalen - transhdrlen - fraggap;
1438 if (copy < 0) {
1439 err = -EINVAL;
1440 goto error;
1441 }
1427 if (transhdrlen) { 1442 if (transhdrlen) {
1428 skb = sock_alloc_send_skb(sk, 1443 skb = sock_alloc_send_skb(sk,
1429 alloclen + hh_len, 1444 alloclen + hh_len,
@@ -1473,13 +1488,9 @@ alloc_new_skb:
1473 data += fraggap; 1488 data += fraggap;
1474 pskb_trim_unique(skb_prev, maxfraglen); 1489 pskb_trim_unique(skb_prev, maxfraglen);
1475 } 1490 }
1476 copy = datalen - transhdrlen - fraggap; 1491 if (copy > 0 &&
1477 1492 getfrag(from, data + transhdrlen, offset,
1478 if (copy < 0) { 1493 copy, fraggap, skb) < 0) {
1479 err = -EINVAL;
1480 kfree_skb(skb);
1481 goto error;
1482 } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
1483 err = -EFAULT; 1494 err = -EFAULT;
1484 kfree_skb(skb); 1495 kfree_skb(skb);
1485 goto error; 1496 goto error;