OSDN Git Service

[NET]: Fix two issues wrt. SO_BINDTODEVICE.
[uclinux-h8/linux.git] / net / core / sock.c
index cfed7d4..190de61 100644 (file)
@@ -362,6 +362,61 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
 }
 EXPORT_SYMBOL(sk_dst_check);
 
+static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
+{
+       int ret = -ENOPROTOOPT;
+#ifdef CONFIG_NETDEVICES
+       char devname[IFNAMSIZ];
+       int index;
+
+       /* Sorry... */
+       ret = -EPERM;
+       if (!capable(CAP_NET_RAW))
+               goto out;
+
+       ret = -EINVAL;
+       if (optlen < 0)
+               goto out;
+
+       /* Bind this socket to a particular device like "eth0",
+        * as specified in the passed interface name. If the
+        * name is "" or the option length is zero the socket
+        * is not bound.
+        */
+       if (optlen > IFNAMSIZ - 1)
+               optlen = IFNAMSIZ - 1;
+       memset(devname, 0, sizeof(devname));
+
+       ret = -EFAULT;
+       if (copy_from_user(devname, optval, optlen))
+               goto out;
+
+       if (devname[0] == '\0') {
+               index = 0;
+       } else {
+               struct net_device *dev = dev_get_by_name(devname);
+
+               ret = -ENODEV;
+               if (!dev)
+                       goto out;
+
+               index = dev->ifindex;
+               dev_put(dev);
+       }
+
+       lock_sock(sk);
+       sk->sk_bound_dev_if = index;
+       sk_dst_reset(sk);
+       release_sock(sk);
+
+       ret = 0;
+
+out:
+#endif
+
+       return ret;
+}
+
 /*
  *     This is meant for all protocols to use and covers goings on
  *     at the socket level. Everything here is generic.
@@ -390,6 +445,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
        }
 #endif
 
+       if (optname == SO_BINDTODEVICE)
+               return sock_bindtodevice(sk, optval, optlen);
+
        if (optlen < sizeof(int))
                return -EINVAL;
 
@@ -578,54 +636,6 @@ set_rcvbuf:
                ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
                break;
 
-#ifdef CONFIG_NETDEVICES
-       case SO_BINDTODEVICE:
-       {
-               char devname[IFNAMSIZ];
-
-               /* Sorry... */
-               if (!capable(CAP_NET_RAW)) {
-                       ret = -EPERM;
-                       break;
-               }
-
-               /* Bind this socket to a particular device like "eth0",
-                * as specified in the passed interface name. If the
-                * name is "" or the option length is zero the socket
-                * is not bound.
-                */
-
-               if (!valbool) {
-                       sk->sk_bound_dev_if = 0;
-               } else {
-                       if (optlen > IFNAMSIZ - 1)
-                               optlen = IFNAMSIZ - 1;
-                       memset(devname, 0, sizeof(devname));
-                       if (copy_from_user(devname, optval, optlen)) {
-                               ret = -EFAULT;
-                               break;
-                       }
-
-                       /* Remove any cached route for this socket. */
-                       sk_dst_reset(sk);
-
-                       if (devname[0] == '\0') {
-                               sk->sk_bound_dev_if = 0;
-                       } else {
-                               struct net_device *dev = dev_get_by_name(devname);
-                               if (!dev) {
-                                       ret = -ENODEV;
-                                       break;
-                               }
-                               sk->sk_bound_dev_if = dev->ifindex;
-                               dev_put(dev);
-                       }
-               }
-               break;
-       }
-#endif
-
-
        case SO_ATTACH_FILTER:
                ret = -EINVAL;
                if (optlen == sizeof(struct sock_fprog)) {