|
@@ -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)) {
|