On Sun, Jan 28, 2018 at 03:38:58AM -0800, Tonghao Zhang wrote:
> When using ioctl to get address of interface, we can't
> get it anymore. For example, the command is show as below.
> 
>       # ifconfig eth0
> 
> In the patch ("03aef17bb79b3"), the devinet_ioctl does not
> return a suitable value, even though we can find it in
> the kernel. Then fix it now.

D'oh...

Acked-by: Al Viro <v...@zeniv.linux.org.uk>

I really wonder how has that avoided loud screams at boot time...

Wouldn't it be better to move that ret = 0; in front of the
entire switch, though?  Look:
        ret = -EADDRNOTAVAIL;
        if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
                goto done;

        switch (cmd) {
[4 cases where we needed ret = 0; - those used to rely upon copy_to_user()]
                if (colon) {
                        ret = -EADDRNOTAVAIL;
                        if (!ifa)
                                break;
                        ret = 0;
                        if (!(ifr->ifr_flags & IFF_UP))
                                inet_del_ifa(in_dev, ifap, 1);
                        break;
                }
                ret = dev_change_flags(dev, ifr->ifr_flags);
                break;
always overwrites ret 
        case SIOCSIFADDR:       /* Set interface address (and family) */
                ret = -EINVAL;
...
immediately overwrites ret
        case SIOCSIFBRDADDR:    /* Set the broadcast address */
                ret = 0;
...
        case SIOCSIFDSTADDR:    /* Set the destination address */
                ret = 0;
...
        case SIOCSIFNETMASK:    /* Set the netmask for the interface */

                /*
                 *      The mask we set must be legal.
                 */
                ret = -EINVAL;
                if (bad_mask(sin->sin_addr.s_addr, 0))
                        break;
                ret = 0;
...
        }
and we have no cases not dealt with - switch in the beginning has taken care of
validating cmd.

I mean something like this (completely untested)

diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index e056c0067f2c..617b55558591 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1046,6 +1046,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
        if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
                goto done;
 
+       ret = 0;
        switch (cmd) {
        case SIOCGIFADDR:       /* Get interface address */
                sin->sin_addr.s_addr = ifa->ifa_local;
@@ -1065,10 +1066,10 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
 
        case SIOCSIFFLAGS:
                if (colon) {
-                       ret = -EADDRNOTAVAIL;
-                       if (!ifa)
+                       if (unlikely(!ifa)) {
+                               ret = -EADDRNOTAVAIL;
                                break;
-                       ret = 0;
+                       }
                        if (!(ifr->ifr_flags & IFF_UP))
                                inet_del_ifa(in_dev, ifap, 1);
                        break;
@@ -1077,22 +1078,23 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
                break;
 
        case SIOCSIFADDR:       /* Set interface address (and family) */
-               ret = -EINVAL;
-               if (inet_abc_len(sin->sin_addr.s_addr) < 0)
+               if (unlikely(inet_abc_len(sin->sin_addr.s_addr) < 0)) {
+                       ret = -EINVAL;
                        break;
+               }
 
                if (!ifa) {
-                       ret = -ENOBUFS;
                        ifa = inet_alloc_ifa();
-                       if (!ifa)
+                       if (unlikely(!ifa)) {
+                               ret = -ENOBUFS;
                                break;
+                       }
                        INIT_HLIST_NODE(&ifa->hash);
                        if (colon)
                                memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
                        else
                                memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
                } else {
-                       ret = 0;
                        if (ifa->ifa_local == sin->sin_addr.s_addr)
                                break;
                        inet_del_ifa(in_dev, ifap, 0);
@@ -1118,7 +1120,6 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
                break;
 
        case SIOCSIFBRDADDR:    /* Set the broadcast address */
-               ret = 0;
                if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
                        inet_del_ifa(in_dev, ifap, 0);
                        ifa->ifa_broadcast = sin->sin_addr.s_addr;
@@ -1127,13 +1128,12 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
                break;
 
        case SIOCSIFDSTADDR:    /* Set the destination address */
-               ret = 0;
                if (ifa->ifa_address == sin->sin_addr.s_addr)
                        break;
-               ret = -EINVAL;
-               if (inet_abc_len(sin->sin_addr.s_addr) < 0)
+               if (unlikely(inet_abc_len(sin->sin_addr.s_addr) < 0)) {
+                       ret = -EINVAL;
                        break;
-               ret = 0;
+               }
                inet_del_ifa(in_dev, ifap, 0);
                ifa->ifa_address = sin->sin_addr.s_addr;
                inet_insert_ifa(ifa);
@@ -1144,10 +1144,10 @@ int devinet_ioctl(struct net *net, unsigned int cmd, 
struct ifreq *ifr)
                /*
                 *      The mask we set must be legal.
                 */
-               ret = -EINVAL;
-               if (bad_mask(sin->sin_addr.s_addr, 0))
+               if (unlikely(bad_mask(sin->sin_addr.s_addr, 0))) {
+                       ret = -EINVAL;
                        break;
-               ret = 0;
+               }
                if (ifa->ifa_mask != sin->sin_addr.s_addr) {
                        __be32 old_mask = ifa->ifa_mask;
                        inet_del_ifa(in_dev, ifap, 0);

Reply via email to