|
@@ -757,7 +757,8 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb,
|
|
|
}
|
|
|
EXPORT_SYMBOL(udp_set_csum);
|
|
|
|
|
|
-static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
|
|
|
+static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
|
|
|
+ struct inet_cork *cork)
|
|
|
{
|
|
|
struct sock *sk = skb->sk;
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
@@ -777,6 +778,21 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
|
|
|
uh->len = htons(len);
|
|
|
uh->check = 0;
|
|
|
|
|
|
+ if (cork->gso_size) {
|
|
|
+ const int hlen = skb_network_header_len(skb) +
|
|
|
+ sizeof(struct udphdr);
|
|
|
+
|
|
|
+ if (hlen + cork->gso_size > cork->fragsize)
|
|
|
+ return -EINVAL;
|
|
|
+ if (skb->len > cork->gso_size * UDP_MAX_SEGMENTS)
|
|
|
+ return -EINVAL;
|
|
|
+ if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ skb_shinfo(skb)->gso_size = cork->gso_size;
|
|
|
+ skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4;
|
|
|
+ }
|
|
|
+
|
|
|
if (is_udplite) /* UDP-Lite */
|
|
|
csum = udplite_csum(skb);
|
|
|
|
|
@@ -828,7 +844,7 @@ int udp_push_pending_frames(struct sock *sk)
|
|
|
if (!skb)
|
|
|
goto out;
|
|
|
|
|
|
- err = udp_send_skb(skb, fl4);
|
|
|
+ err = udp_send_skb(skb, fl4, &inet->cork.base);
|
|
|
|
|
|
out:
|
|
|
up->len = 0;
|
|
@@ -922,6 +938,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
|
ipc.sockc.tsflags = sk->sk_tsflags;
|
|
|
ipc.addr = inet->inet_saddr;
|
|
|
ipc.oif = sk->sk_bound_dev_if;
|
|
|
+ ipc.gso_size = up->gso_size;
|
|
|
|
|
|
if (msg->msg_controllen) {
|
|
|
err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);
|
|
@@ -1037,7 +1054,7 @@ back_from_confirm:
|
|
|
&cork, msg->msg_flags);
|
|
|
err = PTR_ERR(skb);
|
|
|
if (!IS_ERR_OR_NULL(skb))
|
|
|
- err = udp_send_skb(skb, fl4);
|
|
|
+ err = udp_send_skb(skb, fl4, &cork);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -2367,6 +2384,12 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|
|
up->no_check6_rx = valbool;
|
|
|
break;
|
|
|
|
|
|
+ case UDP_SEGMENT:
|
|
|
+ if (val < 0 || val > USHRT_MAX)
|
|
|
+ return -EINVAL;
|
|
|
+ up->gso_size = val;
|
|
|
+ break;
|
|
|
+
|
|
|
/*
|
|
|
* UDP-Lite's partial checksum coverage (RFC 3828).
|
|
|
*/
|
|
@@ -2457,6 +2480,10 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
|
|
|
val = up->no_check6_rx;
|
|
|
break;
|
|
|
|
|
|
+ case UDP_SEGMENT:
|
|
|
+ val = up->gso_size;
|
|
|
+ break;
|
|
|
+
|
|
|
/* The following two cannot be changed on UDP sockets, the return is
|
|
|
* always 0 (which corresponds to the full checksum coverage of UDP). */
|
|
|
case UDPLITE_SEND_CSCOV:
|