Skip to content

Commit

Permalink
sockopt: Change getsockopt() of SO_BINDTODEVICE to return an interfac…
Browse files Browse the repository at this point in the history
…e name

Instead of having the getsockopt() of SO_BINDTODEVICE return an index, which
will then require another call like if_indextoname() to get the actual interface
name, have it return the name directly.

This also matches the existing man page description on socket(7) which mentions
the argument being an interface name.

If the value has not been set, zero is returned and optlen will be set to zero
to indicate there is no interface name present.

Added a seqlock to protect this code path, and dev_ifname(), from someone
changing the device name via dev_change_name().

v2: Added seqlock protection while copying device name.

v3: Fixed word wrap in patch.

Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Brian Haley authored and David S. Miller committed Nov 26, 2012
1 parent 513777b commit c91f6df
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 6 deletions.
2 changes: 2 additions & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,8 @@ extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);

extern rwlock_t dev_base_lock; /* Device list lock */

extern seqlock_t devnet_rename_seq; /* Device rename lock */


#define for_each_netdev(net, d) \
list_for_each_entry(d, &(net)->dev_base_head, dev_list)
Expand Down
21 changes: 19 additions & 2 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ static struct list_head offload_base __read_mostly;
DEFINE_RWLOCK(dev_base_lock);
EXPORT_SYMBOL(dev_base_lock);

DEFINE_SEQLOCK(devnet_rename_seq);

static inline void dev_base_seq_inc(struct net *net)
{
while (++net->dev_base_seq == 0);
Expand Down Expand Up @@ -1091,22 +1093,31 @@ int dev_change_name(struct net_device *dev, const char *newname)
if (dev->flags & IFF_UP)
return -EBUSY;

if (strncmp(newname, dev->name, IFNAMSIZ) == 0)
write_seqlock(&devnet_rename_seq);

if (strncmp(newname, dev->name, IFNAMSIZ) == 0) {
write_sequnlock(&devnet_rename_seq);
return 0;
}

memcpy(oldname, dev->name, IFNAMSIZ);

err = dev_get_valid_name(net, dev, newname);
if (err < 0)
if (err < 0) {
write_sequnlock(&devnet_rename_seq);
return err;
}

rollback:
ret = device_rename(&dev->dev, dev->name);
if (ret) {
memcpy(dev->name, oldname, IFNAMSIZ);
write_sequnlock(&devnet_rename_seq);
return ret;
}

write_sequnlock(&devnet_rename_seq);

write_lock_bh(&dev_base_lock);
hlist_del_rcu(&dev->name_hlist);
write_unlock_bh(&dev_base_lock);
Expand All @@ -1124,6 +1135,7 @@ int dev_change_name(struct net_device *dev, const char *newname)
/* err >= 0 after dev_alloc_name() or stores the first errno */
if (err >= 0) {
err = ret;
write_seqlock(&devnet_rename_seq);
memcpy(dev->name, oldname, IFNAMSIZ);
goto rollback;
} else {
Expand Down Expand Up @@ -4148,6 +4160,7 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
{
struct net_device *dev;
struct ifreq ifr;
unsigned seq;

/*
* Fetch the caller's info block.
Expand All @@ -4156,6 +4169,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;

retry:
seq = read_seqbegin(&devnet_rename_seq);
rcu_read_lock();
dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex);
if (!dev) {
Expand All @@ -4165,6 +4180,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)

strcpy(ifr.ifr_name, dev->name);
rcu_read_unlock();
if (read_seqretry(&devnet_rename_seq, seq))
goto retry;

if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
return -EFAULT;
Expand Down
64 changes: 60 additions & 4 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,8 @@ 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)
static int sock_setbindtodevice(struct sock *sk, char __user *optval,
int optlen)
{
int ret = -ENOPROTOOPT;
#ifdef CONFIG_NETDEVICES
Expand Down Expand Up @@ -562,6 +563,59 @@ static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
return ret;
}

static int sock_getbindtodevice(struct sock *sk, char __user *optval,
int __user *optlen, int len)
{
int ret = -ENOPROTOOPT;
#ifdef CONFIG_NETDEVICES
struct net *net = sock_net(sk);
struct net_device *dev;
char devname[IFNAMSIZ];
unsigned seq;

if (sk->sk_bound_dev_if == 0) {
len = 0;
goto zero;
}

ret = -EINVAL;
if (len < IFNAMSIZ)
goto out;

retry:
seq = read_seqbegin(&devnet_rename_seq);
rcu_read_lock();
dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
ret = -ENODEV;
if (!dev) {
rcu_read_unlock();
goto out;
}

strcpy(devname, dev->name);
rcu_read_unlock();
if (read_seqretry(&devnet_rename_seq, seq))
goto retry;

len = strlen(devname) + 1;

ret = -EFAULT;
if (copy_to_user(optval, devname, len))
goto out;

zero:
ret = -EFAULT;
if (put_user(len, optlen))
goto out;

ret = 0;

out:
#endif

return ret;
}

static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
{
if (valbool)
Expand Down Expand Up @@ -589,7 +643,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
*/

if (optname == SO_BINDTODEVICE)
return sock_bindtodevice(sk, optval, optlen);
return sock_setbindtodevice(sk, optval, optlen);

if (optlen < sizeof(int))
return -EINVAL;
Expand Down Expand Up @@ -1075,15 +1129,17 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
case SO_NOFCS:
v.val = sock_flag(sk, SOCK_NOFCS);
break;

case SO_BINDTODEVICE:
v.val = sk->sk_bound_dev_if;
break;
return sock_getbindtodevice(sk, optval, optlen, len);

case SO_GET_FILTER:
len = sk_get_filter(sk, (struct sock_filter __user *)optval, len);
if (len < 0)
return len;

goto lenout;

default:
return -ENOPROTOOPT;
}
Expand Down

0 comments on commit c91f6df

Please sign in to comment.