|
@@ -853,6 +853,43 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL(udp_push_pending_frames);
|
|
|
|
|
|
+static int __udp_cmsg_send(struct cmsghdr *cmsg, u16 *gso_size)
|
|
|
+{
|
|
|
+ switch (cmsg->cmsg_type) {
|
|
|
+ case UDP_SEGMENT:
|
|
|
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u16)))
|
|
|
+ return -EINVAL;
|
|
|
+ *gso_size = *(__u16 *)CMSG_DATA(cmsg);
|
|
|
+ return 0;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size)
|
|
|
+{
|
|
|
+ struct cmsghdr *cmsg;
|
|
|
+ bool need_ip = false;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for_each_cmsghdr(cmsg, msg) {
|
|
|
+ if (!CMSG_OK(msg, cmsg))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (cmsg->cmsg_level != SOL_UDP) {
|
|
|
+ need_ip = true;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = __udp_cmsg_send(cmsg, gso_size);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return need_ip;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(udp_cmsg_send);
|
|
|
+
|
|
|
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
|
{
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
@@ -941,8 +978,11 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
|
ipc.gso_size = up->gso_size;
|
|
|
|
|
|
if (msg->msg_controllen) {
|
|
|
- err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);
|
|
|
- if (unlikely(err)) {
|
|
|
+ err = udp_cmsg_send(sk, msg, &ipc.gso_size);
|
|
|
+ if (err > 0)
|
|
|
+ err = ip_cmsg_send(sk, msg, &ipc,
|
|
|
+ sk->sk_family == AF_INET6);
|
|
|
+ if (unlikely(err < 0)) {
|
|
|
kfree(ipc.opt);
|
|
|
return err;
|
|
|
}
|