net/if: Call if_ioctl() if the protocol didn't handle the ioctl
authorAaron LI <aly@aaronly.me>
Sun, 4 Nov 2018 09:55:53 +0000 (17:55 +0800)
committerAaron LI <aly@aaronly.me>
Mon, 5 Nov 2018 03:29:14 +0000 (11:29 +0800)
This allows for a non-interface socket (like AF_LOCAL which ifconfig(8)
now uses since commit d4e856128472e2e79172aebf65f19f527ac0010f) to use
a broader class of interface ioctls.

This fixes the regression in ifconfig(8) that is introduced by the above
mentioned commit, which changed ifconfig(8) to use AF_LOCAL socket by
default and thus caused some ioctls failures.  For example:

dfly# ifconfig tap0 create
dfly# ifconfig tap0 up
dfly# ifconfig bridge0 create
dfly# ifconfig bridge0 addm tap0

[before]
dfly# ifconfig bridge0
bridge0: flags=8002<BROADCAST,MULTICAST> mtu 1500
        ether be:48:6d:6d:a6:1f
        groups: bridge

[after]
dfly# ifconfig bridge0
bridge0: flags=8002<BROADCAST,MULTICAST> mtu 1500
        ether be:48:6d:6d:a6:1f
        priority 32768 hellotime 2 fwddelay 15 maxage 20
        member: tap0 flags=3<LEARNING,DISCOVER>
        groups: bridge

Do not pass the SIOCSIF{ADDR,BRDADDR,DSTADDR,NETMASK} iotcls to the
drivers because they may assume these ioctls come from an already
privileged layer and thus skip credentials check and input validation.

While there, improve a NULL check.

Taken-from: FreeBSD (r190151, r255442)
Reviewed-by: dillon, sephe
sys/net/if.c

index 4a6af4e..f1e5172 100644 (file)
@@ -2223,7 +2223,7 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
                error = priv_check_cred(cred, PRIV_ROOT, 0);
                if (error)
                        break;
-               if (ifp->if_ioctl == 0) {
+               if (ifp->if_ioctl == NULL) {
                        error = EOPNOTSUPP;
                        break;
                }
@@ -2291,6 +2291,26 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
                }
                error = so_pru_control_direct(so, cmd, data, ifp);
 
+               /*
+                * If the socket control method returns EOPNOTSUPP, pass the
+                * request directly to the interface.
+                *
+                * Exclude the SIOCSIF{ADDR,BRDADDR,DSTADDR,NETMASK} ioctls,
+                * because drivers may trust these ioctls to come from an
+                * already privileged layer and thus do not perform credentials
+                * checks or input validation.
+                */
+               if (error == EOPNOTSUPP &&
+                   ifp->if_ioctl != NULL &&
+                   cmd != SIOCSIFADDR &&
+                   cmd != SIOCSIFBRDADDR &&
+                   cmd != SIOCSIFDSTADDR &&
+                   cmd != SIOCSIFNETMASK) {
+                       ifnet_serialize_all(ifp);
+                       error = ifp->if_ioctl(ifp, cmd, data, cred);
+                       ifnet_deserialize_all(ifp);
+               }
+
                if ((oif_flags ^ ifp->if_flags) & IFF_UP) {
 #ifdef INET6
                        DELAY(100);/* XXX: temporary workaround for fxp issue*/