]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/in.c
xnu-517.tar.gz
[apple/xnu.git] / bsd / netinet / in.c
index 1389cf3a2a6391dbcec9e43295e7d997bcaf3a9f..ac87e8ac5d8a5d35a0c9da457f99e1ece754a198 100644 (file)
@@ -3,19 +3,22 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
@@ -52,6 +55,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)in.c        8.4 (Berkeley) 1/9/95
+ * $FreeBSD: src/sys/netinet/in.c,v 1.44.2.5 2001/08/13 16:26:17 ume Exp $
  */
 
 #include <sys/param.h>
 #include <sys/kern_event.h>
 
 #include <net/if.h>
-#include <net/route.h>
-#if NGIF > 0
-#include "gif.h"
 #include <net/if_types.h>
-#include <net/if_gif.h>
-#endif
+#include <net/route.h>
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
@@ -106,7 +106,6 @@ struct in_multihead in_multihead; /* XXX BSS initialization */
 
 extern void arp_rtrequest();
 
-
 /*
  * Return 1 if an internet address is for a ``local'' host
  * (one to which we have a connection).  If subnetsarelocal
@@ -238,41 +237,12 @@ in_control(so, cmd, data, ifp, p)
        struct kev_msg        ev_msg;
        struct kev_in_data    in_event_data;
 
-#if NGIF > 0
-        if (ifp && ifp->if_type == IFT_GIF) {
-                switch (cmd) {
-                case SIOCSIFPHYADDR:
-#if 1
-                       if (p &&
-                           (error = suser(p->p_ucred, &p->p_acflag)) != 0)
-                               return(error);
-#else
-               if ((so->so_state & SS_PRIV) == 0)
-                       return (EPERM);
-#endif
-                case SIOCGIFPSRCADDR:
-                case SIOCGIFPDSTADDR:
-                       if (strcmp(ifp->if_name, "gif") == 0)
-                           dl_tag = gif_attach_inet(ifp);
-                        return gif_ioctl(ifp, cmd, data);
-                }
-        }
-#endif
-#if NFAITH > 0
-        if (ifp && ifp->if_type == IFT_FAITH) 
-               dl_tag = faith_attach_inet(ifp);
-#endif
 
        switch (cmd) {
        case SIOCALIFADDR:
        case SIOCDLIFADDR:
-#if 1
                if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
                        return error;
-#else
-               if ((so->so_state & SS_PRIV) == 0)
-                       return (EPERM);
-#endif
                /*fall through*/
        case SIOCGLIFADDR:
                if (!ifp)
@@ -302,6 +272,10 @@ in_control(so, cmd, data, ifp, p)
                        }
 
        switch (cmd) {
+       case SIOCAUTOADDR:
+               if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
+                       return error;
+               break;
 
        case SIOCAIFADDR:
        case SIOCDIFADDR:
@@ -321,23 +295,26 @@ in_control(so, cmd, data, ifp, p)
                                return EDESTADDRREQ;
                        }
                }
+        else if (cmd == SIOCAIFADDR)
+            return (EINVAL);
                if (cmd == SIOCDIFADDR && ia == 0)
                        return (EADDRNOTAVAIL);
                /* FALLTHROUGH */
        case SIOCSIFADDR:
        case SIOCSIFNETMASK:
        case SIOCSIFDSTADDR:
-
-#if ISFB31
-               if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
-                       return error;
-#else
+#ifdef __APPLE__
                if ((so->so_state & SS_PRIV) == 0)
                        return (EPERM);
+#else
+               if (p && (error = suser(p)) != 0)
+                       return error;
 #endif
 
                if (ifp == 0)
                        return (EADDRNOTAVAIL);
+        if (ifra->ifra_addr.sin_family != AF_INET && cmd == SIOCSIFADDR)
+            return (EINVAL);
                if (ia == (struct in_ifaddr *)0) {
                        ia = (struct in_ifaddr *)
                                _MALLOC(sizeof *ia, M_IFADDR, M_WAITOK);
@@ -358,14 +335,15 @@ in_control(so, cmd, data, ifp, p)
  * Temorary code for protocol attachment XXX
  */
                        
-                       if (strcmp(ifp->if_name, "en") == 0)
-                           dl_tag = ether_attach_inet(ifp);
-                       
-                       if (strcmp(ifp->if_name, "lo") == 0)
-                           dl_tag = lo_attach_inet(ifp);
+                       /* Generic protocol plumbing */
+
+                       if (error = dlil_plumb_protocol(PF_INET, ifp, &dl_tag)) {
+                               kprintf("in.c: warning can't plumb proto if=%s%n type %d error=%d\n",
+                                       ifp->if_name, ifp->if_unit, ifp->if_type, error);
+                               error = 0; /*discard error, can be cold with unsupported interfaces */
+                       }
 /* End of temp code */
 
-                       ifa->ifa_dlt = dl_tag;
                        ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
                        ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
                        ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
@@ -381,13 +359,21 @@ in_control(so, cmd, data, ifp, p)
                }
                break;
 
-       case SIOCSIFBRDADDR:
-#if ISFB31
+       case SIOCPROTOATTACH:
+       case SIOCPROTODETACH:
                if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
                        return error;
-#else
+               if (ifp == 0)
+                       return (EADDRNOTAVAIL);
+               break;
+                
+       case SIOCSIFBRDADDR:
+#ifdef __APPLE__
                if ((so->so_state & SS_PRIV) == 0)
                        return (EPERM);
+#else
+               if (p && (error = suser(p)) != 0)
+                       return error;
 #endif
                /* FALLTHROUGH */
 
@@ -400,6 +386,14 @@ in_control(so, cmd, data, ifp, p)
                break;
        }
        switch (cmd) {
+       case SIOCAUTOADDR:
+               if (ifp == 0)
+                       return (EADDRNOTAVAIL);
+               if (ifr->ifr_data)
+                       ifp->if_eflags |= IFEF_AUTOCONFIGURING;
+               else
+                       ifp->if_eflags &= ~IFEF_AUTOCONFIGURING;
+               break;
 
        case SIOCGIFADDR:
                *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
@@ -512,6 +506,24 @@ in_control(so, cmd, data, ifp, p)
                return (in_ifinit(ifp, ia,
                    (struct sockaddr_in *) &ifr->ifr_addr, 1));
 
+       case SIOCPROTOATTACH:
+               error = dlil_plumb_protocol(PF_INET, ifp, &dl_tag);
+               if (error)
+                       return(error);
+                break;
+                
+       case SIOCPROTODETACH:
+                // if an ip address is still present, refuse to detach
+               TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 
+                if (ifa->ifa_addr->sa_family == AF_INET)
+                   return EBUSY;
+
+               error = dlil_unplumb_protocol(PF_INET, ifp);
+               if (error)
+                       return(error);
+               break;
+               
+
        case SIOCSIFNETMASK:
                i = ifra->ifra_addr.sin_addr.s_addr;
                ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i);
@@ -582,7 +594,7 @@ in_control(so, cmd, data, ifp, p)
                 * Report event.
                 */
 
-               if (error == 0) {
+               if ((error == 0) || (error == EEXIST)) {
                     ev_msg.vendor_code    = KEV_VENDOR_APPLE;
                     ev_msg.kev_class      = KEV_NETWORK_CLASS;
                     ev_msg.kev_subclass   = KEV_INET_SUBCLASS;
@@ -618,6 +630,12 @@ in_control(so, cmd, data, ifp, p)
                return (error);
 
        case SIOCDIFADDR:
+                error = dlil_ioctl(PF_INET, ifp, SIOCDIFADDR, (caddr_t)ia);
+                if (error == EOPNOTSUPP)
+                   error = 0;
+               if (error)
+                   return error;
+
                ev_msg.vendor_code    = KEV_VENDOR_APPLE;
                ev_msg.kev_class      = KEV_NETWORK_CLASS;
                ev_msg.kev_subclass   = KEV_INET_SUBCLASS;
@@ -646,7 +664,28 @@ in_control(so, cmd, data, ifp, p)
 
                kev_post_msg(&ev_msg);
 
+               /*
+                * in_ifscrub kills the interface route.
+                */
                in_ifscrub(ifp, ia);
+#ifndef __APPLE__
+               /*
+                * in_ifadown gets rid of all the rest of
+                * the routes.  This is not quite the right
+                * thing to do, but at least if we are running
+                * a routing process they will come back.
+                */
+               in_ifadown(&ia->ia_ifa, 1);
+               /*
+                * XXX horrible hack to detect that we are being called
+                * from if_detach()
+                */
+               if (!ifnet_addrs[ifp->if_index - 1]) {
+                       in_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp);
+                       in_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp);
+               }
+#endif
+
                /*
                 * Protect from ipintr() traversing address list
                 * while we're modifying it.
@@ -657,13 +696,37 @@ in_control(so, cmd, data, ifp, p)
                TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
                oia = ia;
                TAILQ_REMOVE(&in_ifaddrhead, oia, ia_link);
-               IFAFREE(&oia->ia_ifa);
+               ifafree(&oia->ia_ifa);
+        
+#ifdef __APPLE__
+        /*
+        * If the interface supports multicast, and no address is left,
+        * remove the "all hosts" multicast group from that interface.
+        */
+               if (ifp->if_flags & IFF_MULTICAST) {
+                       struct in_addr addr;
+                       struct in_multi *inm;
+
+                       TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 
+                               if (ifa->ifa_addr->sa_family == AF_INET)
+                                       break;
+
+                       if (ifa == 0) {
+                               addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+                               IN_LOOKUP_MULTI(addr, ifp, inm);
+                               if (inm)
+                                       in_delmulti(inm);
+                       }
+               }
+#endif
                splx(s);
                break;
 
+#ifdef __APPLE__
     case SIOCSETOT: {
         /*
          * Inspiration from tcp_ctloutput() and ip_ctloutput()
+         * Special ioctl for OpenTransport sockets
          */
         struct inpcb   *inp, *cloned_inp;
         int                    error = 0;
@@ -736,23 +799,37 @@ in_control(so, cmd, data, ifp, p)
                     imo->imo_membership[i] =
                     in_addmulti(&cloned_imo->imo_membership[i]->inm_addr,
                                 cloned_imo->imo_membership[i]->inm_ifp);
+                                       if (imo->imo_membership[i] == NULL) {
+                                               error = ENOBUFS;
+                                               break;
+                                       }
+                }
+                if (i < cloned_imo->imo_num_memberships) {
+                       /* Failed, perform cleanup */
+                       for (i--; i >= 0; i--)
+                               in_delmulti(imo->imo_membership[i]);
+                       break;
                 }
             }
         }
         splx(s);
         break;
     }
+#endif /* __APPLE__ */
 
        default:
-            return EOPNOTSUPP;
-
+               return EOPNOTSUPP;
+       /* Darwin: dlil_ioctl called from ifioctl */
+#ifndef __APPLE__
+                return ((*ifp->if_ioctl)(ifp, cmd, data));
+#endif
        }
        return (0);
 }
 
 /*
  * SIOC[GAD]LIFADDR.
- *     SIOCGLIFADDR: get first address. (???)
+ *     SIOCGLIFADDR: get first address. (?!?)
  *     SIOCGLIFADDR with IFLR_PREFIX:
  *             get first address that matches the specified prefix.
  *     SIOCALIFADDR: add the specified address.
@@ -805,12 +882,7 @@ in_lifaddr_ioctl(so, cmd, data, ifp, p)
                        return EINVAL;
                break;
        default: /*shouldn't happen*/
-#if 0
-               panic("invalid cmd to in_lifaddr_ioctl");
-               /*NOTREACHED*/
-#else
                return EOPNOTSUPP;
-#endif
        }
        if (sizeof(struct in_addr) * 8 < iflr->prefixlen)
                return EINVAL;
@@ -965,24 +1037,23 @@ in_ifinit(ifp, ia, sin, scrub)
        register u_long i = ntohl(sin->sin_addr.s_addr);
        struct sockaddr_in oldaddr;
        int s = splimp(), flags = RTF_UP, error;
-       u_long      dl_tag;
-
-
 
        oldaddr = ia->ia_addr;
        ia->ia_addr = *sin;
 
-
+       /*
+        * Give the interface a chance to initialize
+        * if this is its first address,
+        * and to validate the address if necessary.
+        */
        error = dlil_ioctl(PF_INET, ifp, SIOCSIFADDR, (caddr_t)ia);
        if (error == EOPNOTSUPP)
             error = 0;
-
        if (error) {
                splx(s);
                ia->ia_addr = oldaddr;
                return (error);
        }
-
        splx(s);
        if (scrub) {
                ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
@@ -1027,16 +1098,22 @@ in_ifinit(ifp, ia, sin, scrub)
        }
        if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
                ia->ia_flags |= IFA_ROUTE;
+       /* XXX check if the subnet route points to the same interface */
+       if (error == EEXIST)
+               error = 0;
 
        /*
         * If the interface supports multicast, join the "all hosts"
         * multicast group on that interface.
         */
        if (ifp->if_flags & IFF_MULTICAST) {
+               struct in_multi *inm;
                struct in_addr addr;
 
                addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
-               in_addmulti(&addr, ifp);
+               IN_LOOKUP_MULTI(addr, ifp, inm);
+               if (inm == 0)
+                       in_addmulti(&addr, ifp);
        }
        return (error);
 }
@@ -1065,7 +1142,9 @@ in_broadcast(in, ifp)
         */
 #define ia ((struct in_ifaddr *)ifa)
        for (ifa = ifp->if_addrhead.tqh_first; ifa; 
-            ifa = ifa->ifa_link.tqe_next)
+                ifa = ifa->ifa_link.tqe_next) {
+               if (ifa->ifa_addr == NULL)
+                       return (0);
                if (ifa->ifa_addr->sa_family == AF_INET &&
                    (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
                     in.s_addr == ia->ia_netbroadcast.s_addr ||
@@ -1080,6 +1159,7 @@ in_broadcast(in, ifp)
                      */
                     ia->ia_subnetmask != (u_long)0xffffffff)
                            return 1;
+       }
        return (0);
 #undef ia
 }
@@ -1116,12 +1196,12 @@ in_addmulti(ap, ifp)
         * If ifma->ifma_protospec is null, then if_addmulti() created
         * a new record.  Otherwise, we are done.
         */
-       if (ifma->ifma_protospec != 0)
+       if (ifma->ifma_protospec != 0) {
+               splx(s);
                return ifma->ifma_protospec;
+       }
 
-       /* XXX - if_addmulti uses M_WAITOK.  Can this really be called
-          at interrupt time?  If so, need to fix if_addmulti. XXX */
-       inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_NOWAIT);
+       inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_WAITOK);
        if (inm == NULL) {
                splx(s);
                return (NULL);
@@ -1137,7 +1217,13 @@ in_addmulti(ap, ifp)
        /*
         * Let IGMP know that we have joined a new IP multicast group.
         */
-       igmp_joingroup(inm);
+       error = igmp_joingroup(inm);
+       if (error) {
+               if_delmultiaddr(ifma);
+               LIST_REMOVE(inm, inm_link);
+               _FREE(inm, M_IPMADDR);
+               inm = NULL;
+       }
        splx(s);
        return (inm);
 }
@@ -1151,8 +1237,10 @@ in_delmulti(inm)
 {
        struct ifmultiaddr *ifma = inm->inm_ifma;
        int s = splnet();
+       
+       /* We intentionally do this a bit differently than BSD */
 
-       if (ifma->ifma_refcount == 1) {
+       if (ifma && ifma->ifma_refcount == 1) {
                /*
                 * No remaining claims to this record; let IGMP know that
                 * we are leaving the multicast group.
@@ -1163,8 +1251,7 @@ in_delmulti(inm)
                FREE(inm, M_IPMADDR);
        }
        /* XXX - should be separate API for when we have an ifma? */
-       if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
+       if (ifma)
+               if_delmultiaddr(ifma);
        splx(s);
-
-
 }