aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorBrian Haley <brian.haley@hp.com>2012-11-26 00:21:08 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-26 17:22:14 -0500
commitc91f6df2db4972d3cc983e6988b9abf1ad02f5f9 (patch)
treecd29f1a068154e6136e54449ed7caa0b2f922318 /net/core
parent513777b2435a4b2a551ef57a345d3e6f99ae1508 (diff)
sockopt: Change getsockopt() of SO_BINDTODEVICE to return an interface 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>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c21
-rw-r--r--net/core/sock.c64
2 files changed, 79 insertions, 6 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 7304ea8a1f13..2a5f55866429 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -203,6 +203,8 @@ static struct list_head offload_base __read_mostly;
203DEFINE_RWLOCK(dev_base_lock); 203DEFINE_RWLOCK(dev_base_lock);
204EXPORT_SYMBOL(dev_base_lock); 204EXPORT_SYMBOL(dev_base_lock);
205 205
206DEFINE_SEQLOCK(devnet_rename_seq);
207
206static inline void dev_base_seq_inc(struct net *net) 208static inline void dev_base_seq_inc(struct net *net)
207{ 209{
208 while (++net->dev_base_seq == 0); 210 while (++net->dev_base_seq == 0);
@@ -1091,22 +1093,31 @@ int dev_change_name(struct net_device *dev, const char *newname)
1091 if (dev->flags & IFF_UP) 1093 if (dev->flags & IFF_UP)
1092 return -EBUSY; 1094 return -EBUSY;
1093 1095
1094 if (strncmp(newname, dev->name, IFNAMSIZ) == 0) 1096 write_seqlock(&devnet_rename_seq);
1097
1098 if (strncmp(newname, dev->name, IFNAMSIZ) == 0) {
1099 write_sequnlock(&devnet_rename_seq);
1095 return 0; 1100 return 0;
1101 }
1096 1102
1097 memcpy(oldname, dev->name, IFNAMSIZ); 1103 memcpy(oldname, dev->name, IFNAMSIZ);
1098 1104
1099 err = dev_get_valid_name(net, dev, newname); 1105 err = dev_get_valid_name(net, dev, newname);
1100 if (err < 0) 1106 if (err < 0) {
1107 write_sequnlock(&devnet_rename_seq);
1101 return err; 1108 return err;
1109 }
1102 1110
1103rollback: 1111rollback:
1104 ret = device_rename(&dev->dev, dev->name); 1112 ret = device_rename(&dev->dev, dev->name);
1105 if (ret) { 1113 if (ret) {
1106 memcpy(dev->name, oldname, IFNAMSIZ); 1114 memcpy(dev->name, oldname, IFNAMSIZ);
1115 write_sequnlock(&devnet_rename_seq);
1107 return ret; 1116 return ret;
1108 } 1117 }
1109 1118
1119 write_sequnlock(&devnet_rename_seq);
1120
1110 write_lock_bh(&dev_base_lock); 1121 write_lock_bh(&dev_base_lock);
1111 hlist_del_rcu(&dev->name_hlist); 1122 hlist_del_rcu(&dev->name_hlist);
1112 write_unlock_bh(&dev_base_lock); 1123 write_unlock_bh(&dev_base_lock);
@@ -1124,6 +1135,7 @@ rollback:
1124 /* err >= 0 after dev_alloc_name() or stores the first errno */ 1135 /* err >= 0 after dev_alloc_name() or stores the first errno */
1125 if (err >= 0) { 1136 if (err >= 0) {
1126 err = ret; 1137 err = ret;
1138 write_seqlock(&devnet_rename_seq);
1127 memcpy(dev->name, oldname, IFNAMSIZ); 1139 memcpy(dev->name, oldname, IFNAMSIZ);
1128 goto rollback; 1140 goto rollback;
1129 } else { 1141 } else {
@@ -4148,6 +4160,7 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
4148{ 4160{
4149 struct net_device *dev; 4161 struct net_device *dev;
4150 struct ifreq ifr; 4162 struct ifreq ifr;
4163 unsigned seq;
4151 4164
4152 /* 4165 /*
4153 * Fetch the caller's info block. 4166 * Fetch the caller's info block.
@@ -4156,6 +4169,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
4156 if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 4169 if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
4157 return -EFAULT; 4170 return -EFAULT;
4158 4171
4172retry:
4173 seq = read_seqbegin(&devnet_rename_seq);
4159 rcu_read_lock(); 4174 rcu_read_lock();
4160 dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); 4175 dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex);
4161 if (!dev) { 4176 if (!dev) {
@@ -4165,6 +4180,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
4165 4180
4166 strcpy(ifr.ifr_name, dev->name); 4181 strcpy(ifr.ifr_name, dev->name);
4167 rcu_read_unlock(); 4182 rcu_read_unlock();
4183 if (read_seqretry(&devnet_rename_seq, seq))
4184 goto retry;
4168 4185
4169 if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) 4186 if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
4170 return -EFAULT; 4187 return -EFAULT;
diff --git a/net/core/sock.c b/net/core/sock.c
index d4f7b58b3866..a692ef49c9bb 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -505,7 +505,8 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
505} 505}
506EXPORT_SYMBOL(sk_dst_check); 506EXPORT_SYMBOL(sk_dst_check);
507 507
508static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen) 508static int sock_setbindtodevice(struct sock *sk, char __user *optval,
509 int optlen)
509{ 510{
510 int ret = -ENOPROTOOPT; 511 int ret = -ENOPROTOOPT;
511#ifdef CONFIG_NETDEVICES 512#ifdef CONFIG_NETDEVICES
@@ -562,6 +563,59 @@ out:
562 return ret; 563 return ret;
563} 564}
564 565
566static int sock_getbindtodevice(struct sock *sk, char __user *optval,
567 int __user *optlen, int len)
568{
569 int ret = -ENOPROTOOPT;
570#ifdef CONFIG_NETDEVICES
571 struct net *net = sock_net(sk);
572 struct net_device *dev;
573 char devname[IFNAMSIZ];
574 unsigned seq;
575
576 if (sk->sk_bound_dev_if == 0) {
577 len = 0;
578 goto zero;
579 }
580
581 ret = -EINVAL;
582 if (len < IFNAMSIZ)
583 goto out;
584
585retry:
586 seq = read_seqbegin(&devnet_rename_seq);
587 rcu_read_lock();
588 dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
589 ret = -ENODEV;
590 if (!dev) {
591 rcu_read_unlock();
592 goto out;
593 }
594
595 strcpy(devname, dev->name);
596 rcu_read_unlock();
597 if (read_seqretry(&devnet_rename_seq, seq))
598 goto retry;
599
600 len = strlen(devname) + 1;
601
602 ret = -EFAULT;
603 if (copy_to_user(optval, devname, len))
604 goto out;
605
606zero:
607 ret = -EFAULT;
608 if (put_user(len, optlen))
609 goto out;
610
611 ret = 0;
612
613out:
614#endif
615
616 return ret;
617}
618
565static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool) 619static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
566{ 620{
567 if (valbool) 621 if (valbool)
@@ -589,7 +643,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
589 */ 643 */
590 644
591 if (optname == SO_BINDTODEVICE) 645 if (optname == SO_BINDTODEVICE)
592 return sock_bindtodevice(sk, optval, optlen); 646 return sock_setbindtodevice(sk, optval, optlen);
593 647
594 if (optlen < sizeof(int)) 648 if (optlen < sizeof(int))
595 return -EINVAL; 649 return -EINVAL;
@@ -1075,15 +1129,17 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
1075 case SO_NOFCS: 1129 case SO_NOFCS:
1076 v.val = sock_flag(sk, SOCK_NOFCS); 1130 v.val = sock_flag(sk, SOCK_NOFCS);
1077 break; 1131 break;
1132
1078 case SO_BINDTODEVICE: 1133 case SO_BINDTODEVICE:
1079 v.val = sk->sk_bound_dev_if; 1134 return sock_getbindtodevice(sk, optval, optlen, len);
1080 break; 1135
1081 case SO_GET_FILTER: 1136 case SO_GET_FILTER:
1082 len = sk_get_filter(sk, (struct sock_filter __user *)optval, len); 1137 len = sk_get_filter(sk, (struct sock_filter __user *)optval, len);
1083 if (len < 0) 1138 if (len < 0)
1084 return len; 1139 return len;
1085 1140
1086 goto lenout; 1141 goto lenout;
1142
1087 default: 1143 default:
1088 return -ENOPROTOOPT; 1144 return -ENOPROTOOPT;
1089 } 1145 }