Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / net / ipv4 / ip_sockglue.c
index 6ddde89..bc14c5b 100644 (file)
@@ -98,7 +98,7 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 }
 
 static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
-                                 int offset)
+                                 int tlen, int offset)
 {
        __wsum csum = skb->csum;
 
@@ -106,7 +106,9 @@ static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
                return;
 
        if (offset != 0)
-               csum = csum_sub(csum, csum_partial(skb->data, offset, 0));
+               csum = csum_sub(csum,
+                               csum_partial(skb->data + tlen,
+                                            offset, 0));
 
        put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
 }
@@ -152,7 +154,7 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
 }
 
 void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
-                        int offset)
+                        int tlen, int offset)
 {
        struct inet_sock *inet = inet_sk(skb->sk);
        unsigned int flags = inet->cmsg_flags;
@@ -215,7 +217,7 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
        }
 
        if (flags & IP_CMSG_CHECKSUM)
-               ip_cmsg_recv_checksum(msg, skb, offset);
+               ip_cmsg_recv_checksum(msg, skb, tlen, offset);
 }
 EXPORT_SYMBOL(ip_cmsg_recv_offset);
 
@@ -249,6 +251,8 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
                switch (cmsg->cmsg_type) {
                case IP_RETOPTS:
                        err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
+
+                       /* Our caller is responsible for freeing ipc->opt */
                        err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
                                             err < 40 ? err : 40);
                        if (err)
@@ -591,6 +595,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
        case IP_TRANSPARENT:
        case IP_MINTTL:
        case IP_NODEFRAG:
+       case IP_BIND_ADDRESS_NO_PORT:
        case IP_UNICAST_IF:
        case IP_MULTICAST_TTL:
        case IP_MULTICAST_ALL:
@@ -741,6 +746,9 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                }
                inet->nodefrag = val ? 1 : 0;
                break;
+       case IP_BIND_ADDRESS_NO_PORT:
+               inet->bind_address_no_port = val ? 1 : 0;
+               break;
        case IP_MTU_DISCOVER:
                if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
                        goto e_inval;
@@ -1184,7 +1192,14 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
                pktinfo->ipi_ifindex = 0;
                pktinfo->ipi_spec_dst.s_addr = 0;
        }
-       skb_dst_drop(skb);
+       /* We need to keep the dst for __ip_options_echo()
+        * We could restrict the test to opt.ts_needtime || opt.srr,
+        * but the following is good enough as IP options are not often used.
+        */
+       if (unlikely(IPCB(skb)->opt.optlen))
+               skb_dst_force(skb);
+       else
+               skb_dst_drop(skb);
 }
 
 int ip_setsockopt(struct sock *sk, int level,
@@ -1247,11 +1262,22 @@ EXPORT_SYMBOL(compat_ip_setsockopt);
  *     the _received_ ones. The set sets the _sent_ ones.
  */
 
+static bool getsockopt_needs_rtnl(int optname)
+{
+       switch (optname) {
+       case IP_MSFILTER:
+       case MCAST_MSFILTER:
+               return true;
+       }
+       return false;
+}
+
 static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                            char __user *optval, int __user *optlen, unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
-       int val;
+       bool needs_rtnl = getsockopt_needs_rtnl(optname);
+       int val, err = 0;
        int len;
 
        if (level != SOL_IP)
@@ -1265,6 +1291,8 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        if (len < 0)
                return -EINVAL;
 
+       if (needs_rtnl)
+               rtnl_lock();
        lock_sock(sk);
 
        switch (optname) {
@@ -1333,6 +1361,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_NODEFRAG:
                val = inet->nodefrag;
                break;
+       case IP_BIND_ADDRESS_NO_PORT:
+               val = inet->bind_address_no_port;
+               break;
        case IP_MTU_DISCOVER:
                val = inet->pmtudisc;
                break;
@@ -1379,39 +1410,35 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_MSFILTER:
        {
                struct ip_msfilter msf;
-               int err;
 
                if (len < IP_MSFILTER_SIZE(0)) {
-                       release_sock(sk);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto out;
                }
                if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
-                       release_sock(sk);
-                       return -EFAULT;
+                       err = -EFAULT;
+                       goto out;
                }
                err = ip_mc_msfget(sk, &msf,
                                   (struct ip_msfilter __user *)optval, optlen);
-               release_sock(sk);
-               return err;
+               goto out;
        }
        case MCAST_MSFILTER:
        {
                struct group_filter gsf;
-               int err;
 
                if (len < GROUP_FILTER_SIZE(0)) {
-                       release_sock(sk);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto out;
                }
                if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
-                       release_sock(sk);
-                       return -EFAULT;
+                       err = -EFAULT;
+                       goto out;
                }
                err = ip_mc_gsfget(sk, &gsf,
                                   (struct group_filter __user *)optval,
                                   optlen);
-               release_sock(sk);
-               return err;
+               goto out;
        }
        case IP_MULTICAST_ALL:
                val = inet->mc_all;
@@ -1478,6 +1505,12 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
                        return -EFAULT;
        }
        return 0;
+
+out:
+       release_sock(sk);
+       if (needs_rtnl)
+               rtnl_unlock();
+       return err;
 }
 
 int ip_getsockopt(struct sock *sk, int level,