Skip to content

Commit 4878809

Browse files
author
David S. Miller
committed
[NET]: Fix two issues wrt. SO_BINDTODEVICE.
1) Comments suggest that setting optlen to zero will unbind the socket from whatever device it might be attached to. This hasn't been the case since at least 2.2.x because the first thing this function does is return -EINVAL if 'optlen' is less than sizeof(int). This check also means that passing in a two byte string doesn't work so well. It's almost as if this code was testing with "eth?" patterned strings and nothing else :-) Fix this by breaking the logic of this facility out into a seperate function which validates optlen more appropriately. The optlen==0 and small string cases now work properly. 2) We should reset the cached route of the socket after we have made the device binding changes, not before. Reported by Ben Greear. Signed-off-by: David S. Miller <[email protected]>
1 parent 19299b1 commit 4878809

File tree

1 file changed

+58
-48
lines changed

1 file changed

+58
-48
lines changed

net/core/sock.c

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,61 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
362362
}
363363
EXPORT_SYMBOL(sk_dst_check);
364364

365+
static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
366+
{
367+
int ret = -ENOPROTOOPT;
368+
#ifdef CONFIG_NETDEVICES
369+
char devname[IFNAMSIZ];
370+
int index;
371+
372+
/* Sorry... */
373+
ret = -EPERM;
374+
if (!capable(CAP_NET_RAW))
375+
goto out;
376+
377+
ret = -EINVAL;
378+
if (optlen < 0)
379+
goto out;
380+
381+
/* Bind this socket to a particular device like "eth0",
382+
* as specified in the passed interface name. If the
383+
* name is "" or the option length is zero the socket
384+
* is not bound.
385+
*/
386+
if (optlen > IFNAMSIZ - 1)
387+
optlen = IFNAMSIZ - 1;
388+
memset(devname, 0, sizeof(devname));
389+
390+
ret = -EFAULT;
391+
if (copy_from_user(devname, optval, optlen))
392+
goto out;
393+
394+
if (devname[0] == '\0') {
395+
index = 0;
396+
} else {
397+
struct net_device *dev = dev_get_by_name(devname);
398+
399+
ret = -ENODEV;
400+
if (!dev)
401+
goto out;
402+
403+
index = dev->ifindex;
404+
dev_put(dev);
405+
}
406+
407+
lock_sock(sk);
408+
sk->sk_bound_dev_if = index;
409+
sk_dst_reset(sk);
410+
release_sock(sk);
411+
412+
ret = 0;
413+
414+
out:
415+
#endif
416+
417+
return ret;
418+
}
419+
365420
/*
366421
* This is meant for all protocols to use and covers goings on
367422
* at the socket level. Everything here is generic.
@@ -390,6 +445,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
390445
}
391446
#endif
392447

448+
if (optname == SO_BINDTODEVICE)
449+
return sock_bindtodevice(sk, optval, optlen);
450+
393451
if (optlen < sizeof(int))
394452
return -EINVAL;
395453

@@ -578,54 +636,6 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
578636
ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
579637
break;
580638

581-
#ifdef CONFIG_NETDEVICES
582-
case SO_BINDTODEVICE:
583-
{
584-
char devname[IFNAMSIZ];
585-
586-
/* Sorry... */
587-
if (!capable(CAP_NET_RAW)) {
588-
ret = -EPERM;
589-
break;
590-
}
591-
592-
/* Bind this socket to a particular device like "eth0",
593-
* as specified in the passed interface name. If the
594-
* name is "" or the option length is zero the socket
595-
* is not bound.
596-
*/
597-
598-
if (!valbool) {
599-
sk->sk_bound_dev_if = 0;
600-
} else {
601-
if (optlen > IFNAMSIZ - 1)
602-
optlen = IFNAMSIZ - 1;
603-
memset(devname, 0, sizeof(devname));
604-
if (copy_from_user(devname, optval, optlen)) {
605-
ret = -EFAULT;
606-
break;
607-
}
608-
609-
/* Remove any cached route for this socket. */
610-
sk_dst_reset(sk);
611-
612-
if (devname[0] == '\0') {
613-
sk->sk_bound_dev_if = 0;
614-
} else {
615-
struct net_device *dev = dev_get_by_name(devname);
616-
if (!dev) {
617-
ret = -ENODEV;
618-
break;
619-
}
620-
sk->sk_bound_dev_if = dev->ifindex;
621-
dev_put(dev);
622-
}
623-
}
624-
break;
625-
}
626-
#endif
627-
628-
629639
case SO_ATTACH_FILTER:
630640
ret = -EINVAL;
631641
if (optlen == sizeof(struct sock_fprog)) {

0 commit comments

Comments
 (0)