Author: rwatson
Date: Thu Apr 23 21:41:37 2009
New Revision: 191443
URL: http://svn.freebsd.org/changeset/base/191443

Log:
  Reorganize in_control() so that invariants are more obvious, and so
  that it is easier to lock:
  
  - Handle the unsupported ioctl case at the beginning of in_control(),
    handing off to ifp->if_ioctl, rather than looking up interfaces and
    addresses unnecessarily in this case.
  
  - Make it an invariant that ifp is always non-NULL when running
    in_control()-implemented ioctls, simplifying the code structure.
  
  MFC after:    3 weeks

Modified:
  head/sys/netinet/in.c

Modified: head/sys/netinet/in.c
==============================================================================
--- head/sys/netinet/in.c       Thu Apr 23 21:27:06 2009        (r191442)
+++ head/sys/netinet/in.c       Thu Apr 23 21:41:37 2009        (r191443)
@@ -202,7 +202,8 @@ in_len2mask(struct in_addr *mask, int le
 
 /*
  * Generic internet control operations (ioctl's).
- * Ifp is 0 if not an interface-specific ioctl.
+ *
+ * ifp is NULL if not an interface-specific ioctl.
  */
 /* ARGSUSED */
 int
@@ -227,7 +228,23 @@ in_control(struct socket *so, u_long cmd
        iaIsNew = 0;
        allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
 
+       /*
+        * Filter out ioctls we implement directly; forward the rest on to
+        * in_lifaddr_ioctl() and ifp->if_ioctl().
+        */
        switch (cmd) {
+       case SIOCAIFADDR:
+       case SIOCDIFADDR:
+       case SIOCGIFADDR:
+       case SIOCGIFBRDADDR:
+       case SIOCGIFDSTADDR:
+       case SIOCGIFNETMASK:
+       case SIOCSIFADDR:
+       case SIOCSIFBRDADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCSIFNETMASK:
+               break;
+
        case SIOCALIFADDR:
                if (td != NULL) {
                        error = priv_check(td, PRIV_NET_ADDIFADDR);
@@ -252,46 +269,51 @@ in_control(struct socket *so, u_long cmd
                if (ifp == NULL)
                        return (EINVAL);
                return in_lifaddr_ioctl(so, cmd, data, ifp, td);
+
+       default:
+               if (ifp == NULL || ifp->if_ioctl == NULL)
+                       return (EOPNOTSUPP);
+               return ((*ifp->if_ioctl)(ifp, cmd, data));
        }
 
+       if (ifp == NULL)
+               return (EADDRNOTAVAIL);
+
        /*
         * Find address for this interface, if it exists.
         *
-        * If an alias address was specified, find that one instead of
-        * the first one on the interface, if possible.
+        * If an alias address was specified, find that one instead of the
+        * first one on the interface, if possible.
         */
-       if (ifp != NULL) {
-               dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
-               LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash)
-                       if (iap->ia_ifp == ifp &&
-                           iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
-                               if (td == NULL || prison_check_ip4(
-                                   td->td_ucred, &dst) == 0)
-                                       ia = iap;
+       dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+       LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) {
+               if (iap->ia_ifp == ifp &&
+                   iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
+                       if (td == NULL || prison_check_ip4(td->td_ucred,
+                           &dst) == 0)
+                               ia = iap;
+                       break;
+               }
+       }
+       if (ia == NULL) {
+               TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+                       iap = ifatoia(ifa);
+                       if (iap->ia_addr.sin_family == AF_INET) {
+                               if (td != NULL &&
+                                   prison_check_ip4(td->td_ucred,
+                                   &iap->ia_addr.sin_addr) != 0)
+                                       continue;
+                               ia = iap;
                                break;
                        }
-               if (ia == NULL)
-                       TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
-                               iap = ifatoia(ifa);
-                               if (iap->ia_addr.sin_family == AF_INET) {
-                                       if (td != NULL &&
-                                           prison_check_ip4(td->td_ucred,
-                                           &iap->ia_addr.sin_addr) != 0)
-                                               continue;
-                                       ia = iap;
-                                       break;
-                               }
-                       }
-               if (ia == NULL)
-                       iaIsFirst = 1;
+               }
        }
+       if (ia == NULL)
+               iaIsFirst = 1;
 
        switch (cmd) {
-
        case SIOCAIFADDR:
        case SIOCDIFADDR:
-               if (ifp == NULL)
-                       return (EADDRNOTAVAIL);
                if (ifra->ifra_addr.sin_family == AF_INET) {
                        for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) {
                                if (ia->ia_ifp == ifp  &&
@@ -319,8 +341,6 @@ in_control(struct socket *so, u_long cmd
                                return (error);
                }
 
-               if (ifp == NULL)
-                       return (EADDRNOTAVAIL);
                if (ia == NULL) {
                        ia = (struct in_ifaddr *)
                                malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
@@ -505,9 +525,7 @@ in_control(struct socket *so, u_long cmd
                break;
 
        default:
-               if (ifp == NULL || ifp->if_ioctl == NULL)
-                       return (EOPNOTSUPP);
-               return ((*ifp->if_ioctl)(ifp, cmd, data));
+               panic("in_control: unsupported ioctl");
        }
 
        /*
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to