aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2007-09-14 19:41:03 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-09-14 19:41:03 -0400
commit4878809f711981a602cc562eb47994fc81ea0155 (patch)
tree17e84e056331e90514fe1a5069d1fba9cdaab274 /net
parent19299b1a722198830e39264a0f2edadd3fde74c2 (diff)
[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 <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/sock.c106
1 files changed, 58 insertions, 48 deletions
diff --git a/net/core/sock.c b/net/core/sock.c
index cfed7d42c485..190de61cd648 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -362,6 +362,61 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
362} 362}
363EXPORT_SYMBOL(sk_dst_check); 363EXPORT_SYMBOL(sk_dst_check);
364 364
365static 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
414out:
415#endif
416
417 return ret;
418}
419
365/* 420/*
366 * This is meant for all protocols to use and covers goings on 421 * This is meant for all protocols to use and covers goings on
367 * at the socket level. Everything here is generic. 422 * at the socket level. Everything here is generic.
@@ -390,6 +445,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
390 } 445 }
391#endif 446#endif
392 447
448 if (optname == SO_BINDTODEVICE)
449 return sock_bindtodevice(sk, optval, optlen);
450
393 if (optlen < sizeof(int)) 451 if (optlen < sizeof(int))
394 return -EINVAL; 452 return -EINVAL;
395 453
@@ -578,54 +636,6 @@ set_rcvbuf:
578 ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen); 636 ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
579 break; 637 break;
580 638
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
629 case SO_ATTACH_FILTER: 639 case SO_ATTACH_FILTER:
630 ret = -EINVAL; 640 ret = -EINVAL;
631 if (optlen == sizeof(struct sock_fprog)) { 641 if (optlen == sizeof(struct sock_fprog)) {