]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if.c
xnu-517.7.21.tar.gz
[apple/xnu.git] / bsd / net / if.c
index 1f10ed96f61bf5cfe87b263e32166bf35bc58cb1..e5f361d70f635fe0dc1c01f7f18ac9d93757655b 100644 (file)
@@ -3,22 +3,19 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 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.
  * 
- * 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
+ * This 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
@@ -102,11 +99,18 @@ static int ifconf __P((u_long, caddr_t));
 static void if_qflush __P((struct ifqueue *));
 static void link_rtrequest __P((int, struct rtentry *, struct sockaddr *));
 
+static struct  if_clone *if_clone_lookup(const char *, int *);
+static int     if_clone_list(struct if_clonereq *);
+
 MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
 MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
 
 int    ifqmaxlen = IFQ_MAXLEN;
-struct ifnethead ifnet;        /* depend on static init XXX */
+struct ifnethead ifnet = TAILQ_HEAD_INITIALIZER(ifnet);
+struct ifmultihead ifma_lostlist = LIST_HEAD_INITIALIZER(ifma_lostlist);
+
+static int     if_cloners_count;
+LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
 
 #if INET6
 /*
@@ -114,9 +118,10 @@ struct     ifnethead ifnet;        /* depend on static init XXX */
  * should be more generalized?
  */
 extern void    nd6_setmtu __P((struct ifnet *));
-extern int ip6_auto_on;
 #endif
 
+#define M_CLONE                M_IFADDR
+
 /*
  * Network interface utility routines.
  *
@@ -124,10 +129,86 @@ extern int ip6_auto_on;
  * parameters.
  */
 
-int if_index = 0;
+int if_index;
 struct ifaddr **ifnet_addrs;
-struct ifnet **ifindex2ifnet = NULL;
+struct ifnet **ifindex2ifnet;
+
+#define INITIAL_IF_INDEXLIM    8
+
+/*
+ * Function: if_next_index
+ * Purpose:
+ *   Return the next available interface index.  
+ *   Grow the ifnet_addrs[] and ifindex2ifnet[] arrays to accomodate the 
+ *   added entry when necessary.
+ *
+ * Note:
+ *   ifnet_addrs[] is indexed by (if_index - 1), whereas
+ *   ifindex2ifnet[] is indexed by ifp->if_index.  That requires us to
+ *   always allocate one extra element to hold ifindex2ifnet[0], which
+ *   is unused.
+ */
+static int
+if_next_index(void)
+{
+       static int      if_indexlim = 0;
+       static int      if_list_growing = 0;
+       int             new_index;
+
+       while (if_list_growing) {
+               /* wait until list is done growing */
+               (void)tsleep((caddr_t)&ifnet_addrs, PZERO, "if_next_index", 0);
+       }
+       new_index = ++if_index;
+       if (if_index > if_indexlim) {
+               unsigned        n;
+               int             new_if_indexlim;
+               caddr_t         new_ifnet_addrs;
+               caddr_t         new_ifindex2ifnet;
+               caddr_t         old_ifnet_addrs;
+
+               /* mark list as growing */
+               if_list_growing = 1;
+
+               old_ifnet_addrs = (caddr_t)ifnet_addrs;
+               if (ifnet_addrs == NULL) {
+                       new_if_indexlim = INITIAL_IF_INDEXLIM;
+               } else {
+                       new_if_indexlim = if_indexlim << 1;
+               }
 
+               /* allocate space for the larger arrays */
+               n = (2 * new_if_indexlim + 1) * sizeof(caddr_t);
+               new_ifnet_addrs = _MALLOC(n, M_IFADDR, M_WAITOK);
+               new_ifindex2ifnet = new_ifnet_addrs 
+                       + new_if_indexlim * sizeof(caddr_t);
+               bzero(new_ifnet_addrs, n);
+               if (ifnet_addrs != NULL) {
+                       /* copy the existing data */
+                       bcopy((caddr_t)ifnet_addrs, new_ifnet_addrs,
+                             if_indexlim * sizeof(caddr_t));
+                       bcopy((caddr_t)ifindex2ifnet,
+                             new_ifindex2ifnet,
+                             (if_indexlim + 1) * sizeof(caddr_t));
+               }
+
+               /* switch to the new tables and size */
+               ifnet_addrs = (struct ifaddr **)new_ifnet_addrs;
+               ifindex2ifnet = (struct ifnet **)new_ifindex2ifnet;
+               if_indexlim = new_if_indexlim;
+
+               /* release the old data */
+               if (old_ifnet_addrs != NULL) {
+                       _FREE((caddr_t)old_ifnet_addrs, M_IFADDR);
+               }
+
+               /* wake up others that might be blocked */
+               if_list_growing = 0;
+               wakeup((caddr_t)&ifnet_addrs);
+       }
+       return (new_index);
+
+}
 
 /*
  * Attach an interface to the
@@ -142,19 +223,10 @@ old_if_attach(ifp)
        char workbuf[64];
        register struct sockaddr_dl *sdl;
        register struct ifaddr *ifa;
-       static int if_indexlim = 8;
-       static int inited;
 
        if (ifp->if_snd.ifq_maxlen == 0)
            ifp->if_snd.ifq_maxlen = ifqmaxlen;
 
-       if (!inited) {
-               TAILQ_INIT(&ifnet);
-               inited = 1;
-       }
-
-       TAILQ_INSERT_TAIL(&ifnet, ifp, if_link);
-       ifp->if_index = ++if_index;
        /*
         * XXX -
         * The old code would work if the interface passed a pre-existing
@@ -166,66 +238,315 @@ old_if_attach(ifp)
        TAILQ_INIT(&ifp->if_prefixhead);
        LIST_INIT(&ifp->if_multiaddrs);
        getmicrotime(&ifp->if_lastchange);
-       if (ifnet_addrs == 0 || if_index >= if_indexlim) {
-               unsigned n = (if_indexlim <<= 1) * sizeof(ifa);
-               struct ifaddr **q = (struct ifaddr **)
-                                       _MALLOC(n, M_IFADDR, M_WAITOK);
-               bzero((caddr_t)q, n);
-               if (ifnet_addrs) {
-                       bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
-                       FREE((caddr_t)ifnet_addrs, M_IFADDR);
+
+       if ((ifp->if_eflags & IFEF_REUSE) == 0 || ifp->if_index == 0) {
+               /* allocate a new entry */
+               ifp->if_index = if_next_index();
+               ifindex2ifnet[ifp->if_index] = ifp;
+
+               /*
+                * create a Link Level name for this device
+                */
+               namelen = snprintf(workbuf, sizeof(workbuf),
+                                  "%s%d", ifp->if_name, ifp->if_unit);
+#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
+               masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
+               socksize = masklen + ifp->if_addrlen;
+#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
+               if (socksize < sizeof(*sdl))
+                       socksize = sizeof(*sdl);
+               socksize = ROUNDUP(socksize);
+               ifasize = sizeof(*ifa) + 2 * socksize;
+               ifa = (struct ifaddr *) _MALLOC(ifasize, M_IFADDR, M_WAITOK);
+               if (ifa) {
+                       bzero((caddr_t)ifa, ifasize);
+                       sdl = (struct sockaddr_dl *)(ifa + 1);
+                       sdl->sdl_len = socksize;
+                       sdl->sdl_family = AF_LINK;
+                       bcopy(workbuf, sdl->sdl_data, namelen);
+                       sdl->sdl_nlen = namelen;
+                       sdl->sdl_index = ifp->if_index;
+                       sdl->sdl_type = ifp->if_type;
+                       ifnet_addrs[ifp->if_index - 1] = ifa;
+                       ifa->ifa_ifp = ifp;
+                       ifa->ifa_rtrequest = link_rtrequest;
+                       ifa->ifa_addr = (struct sockaddr *)sdl;
+                       sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
+                       ifa->ifa_netmask = (struct sockaddr *)sdl;
+                       sdl->sdl_len = masklen;
+                       while (namelen != 0)
+                               sdl->sdl_data[--namelen] = 0xff;
+               }
+       } else {
+               ifa = ifnet_addrs[ifp->if_index - 1];
+       }
+       if (ifa != NULL) {
+               TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link);
+       }
+       TAILQ_INSERT_TAIL(&ifnet, ifp, if_link);
+}
+
+/*
+ * Create a clone network interface.
+ */
+static int
+if_clone_create(char *name, int len)
+{
+       struct if_clone *ifc;
+       char *dp;
+       int wildcard, bytoff, bitoff;
+       int unit;
+       int err;
+
+       ifc = if_clone_lookup(name, &unit);
+       if (ifc == NULL)
+               return (EINVAL);
+
+       if (ifunit(name) != NULL)
+               return (EEXIST);
+
+       bytoff = bitoff = 0;
+       wildcard = (unit < 0);
+       /*
+        * Find a free unit if none was given.
+        */
+       if (wildcard) {
+               while ((bytoff < ifc->ifc_bmlen)
+                   && (ifc->ifc_units[bytoff] == 0xff))
+                       bytoff++;
+               if (bytoff >= ifc->ifc_bmlen)
+                       return (ENOSPC);
+               while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0)
+                       bitoff++;
+               unit = (bytoff << 3) + bitoff;
+       }
+
+       if (unit > ifc->ifc_maxunit)
+               return (ENXIO);
+
+       err = (*ifc->ifc_create)(ifc, unit);
+       if (err != 0)
+               return (err);
+
+       if (!wildcard) {
+               bytoff = unit >> 3;
+               bitoff = unit - (bytoff << 3);
+       }
+
+       /*
+        * Allocate the unit in the bitmap.
+        */
+       KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0,
+           ("%s: bit is already set", __func__));
+       ifc->ifc_units[bytoff] |= (1 << bitoff);
+
+       /* In the wildcard case, we need to update the name. */
+       if (wildcard) {
+               for (dp = name; *dp != '\0'; dp++);
+               if (snprintf(dp, len - (dp-name), "%d", unit) >
+                   len - (dp-name) - 1) {
+                       /*
+                        * This can only be a programmer error and
+                        * there's no straightforward way to recover if
+                        * it happens.
+                        */
+                       panic("if_clone_create(): interface name too long");
                }
-               ifnet_addrs = (struct ifaddr **)q;
-
-               /* grow ifindex2ifnet */
-               n = if_indexlim * sizeof(struct ifaddr *);
-               q = (struct ifaddr **)_MALLOC(n, M_IFADDR, M_WAITOK);
-               bzero(q, n);
-               if (ifindex2ifnet) {
-                       bcopy((caddr_t)ifindex2ifnet, q, n/2);
-                       _FREE((caddr_t)ifindex2ifnet, M_IFADDR);
+
+       }
+
+       return (0);
+}
+
+/*
+ * Destroy a clone network interface.
+ */
+int
+if_clone_destroy(const char *name)
+{
+       struct if_clone *ifc;
+       struct ifnet *ifp;
+       int bytoff, bitoff;
+       int unit;
+
+       ifc = if_clone_lookup(name, &unit);
+       if (ifc == NULL)
+               return (EINVAL);
+
+       if (unit < ifc->ifc_minifs)
+               return (EINVAL);
+
+       ifp = ifunit(name);
+       if (ifp == NULL)
+               return (ENXIO);
+
+       if (ifc->ifc_destroy == NULL)
+               return (EOPNOTSUPP);
+
+       (*ifc->ifc_destroy)(ifp);
+
+       /*
+        * Compute offset in the bitmap and deallocate the unit.
+        */
+       bytoff = unit >> 3;
+       bitoff = unit - (bytoff << 3);
+       KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0,
+           ("%s: bit is already cleared", __func__));
+       ifc->ifc_units[bytoff] &= ~(1 << bitoff);
+       return (0);
+}
+
+/*
+ * Look up a network interface cloner.
+ */
+
+static struct if_clone *
+if_clone_lookup(const char *name, int *unitp)
+{
+       struct if_clone *ifc;
+       const char *cp;
+       int i;
+
+       for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) {
+               for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) {
+                       if (ifc->ifc_name[i] != *cp)
+                               goto next_ifc;
                }
-               ifindex2ifnet = (struct ifnet **)q;
+               goto found_name;
+ next_ifc:
+               ifc = LIST_NEXT(ifc, ifc_list);
        }
 
-       ifindex2ifnet[if_index] = ifp;
+       /* No match. */
+       return ((struct if_clone *)NULL);
 
+ found_name:
+       if (*cp == '\0') {
+               i = -1;
+       } else {
+               for (i = 0; *cp != '\0'; cp++) {
+                       if (*cp < '0' || *cp > '9') {
+                               /* Bogus unit number. */
+                               return (NULL);
+                       }
+                       i = (i * 10) + (*cp - '0');
+               }
+       }
+
+       if (unitp != NULL)
+               *unitp = i;
+       return (ifc);
+}
+
+/*
+ * Register a network interface cloner.
+ */
+void
+if_clone_attach(struct if_clone *ifc)
+{
+       int bytoff, bitoff;
+       int err;
+       int len, maxclone;
+       int unit;
+
+       KASSERT(ifc->ifc_minifs - 1 <= ifc->ifc_maxunit,
+           ("%s: %s requested more units then allowed (%d > %d)",
+           __func__, ifc->ifc_name, ifc->ifc_minifs,
+           ifc->ifc_maxunit + 1));
        /*
-        * create a Link Level name for this device
+        * Compute bitmap size and allocate it.
         */
-       namelen = snprintf(workbuf, sizeof(workbuf),
-           "%s%d", ifp->if_name, ifp->if_unit);
-#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
-       masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
-       socksize = masklen + ifp->if_addrlen;
-#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
-       if (socksize < sizeof(*sdl))
-               socksize = sizeof(*sdl);
-       socksize = ROUNDUP(socksize);
-       ifasize = sizeof(*ifa) + 2 * socksize;
-       ifa = (struct ifaddr *) _MALLOC(ifasize, M_IFADDR, M_WAITOK);
-       if (ifa) {
-               bzero((caddr_t)ifa, ifasize);
-               sdl = (struct sockaddr_dl *)(ifa + 1);
-               sdl->sdl_len = socksize;
-               sdl->sdl_family = AF_LINK;
-               bcopy(workbuf, sdl->sdl_data, namelen);
-               sdl->sdl_nlen = namelen;
-               sdl->sdl_index = ifp->if_index;
-               sdl->sdl_type = ifp->if_type;
-               ifnet_addrs[if_index - 1] = ifa;
-               ifa->ifa_ifp = ifp;
-               ifa->ifa_rtrequest = link_rtrequest;
-               ifa->ifa_addr = (struct sockaddr *)sdl;
-               sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
-               ifa->ifa_netmask = (struct sockaddr *)sdl;
-               sdl->sdl_len = masklen;
-               while (namelen != 0)
-                       sdl->sdl_data[--namelen] = 0xff;
-               TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link);
+       maxclone = ifc->ifc_maxunit + 1;
+       len = maxclone >> 3;
+       if ((len << 3) < maxclone)
+               len++;
+       ifc->ifc_units = _MALLOC(len, M_CLONE, M_WAITOK | M_ZERO);
+       bzero(ifc->ifc_units, len);
+       ifc->ifc_bmlen = len;
+
+       LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
+       if_cloners_count++;
+
+       for (unit = 0; unit < ifc->ifc_minifs; unit++) {
+               err = (*ifc->ifc_create)(ifc, unit);
+               KASSERT(err == 0,
+                   ("%s: failed to create required interface %s%d",
+                   __func__, ifc->ifc_name, unit));
+
+               /* Allocate the unit in the bitmap. */
+               bytoff = unit >> 3;
+               bitoff = unit - (bytoff << 3);
+               ifc->ifc_units[bytoff] |= (1 << bitoff);
        }
 }
 
+/*
+ * Unregister a network interface cloner.
+ */
+void
+if_clone_detach(struct if_clone *ifc)
+{
+
+       LIST_REMOVE(ifc, ifc_list);
+       FREE(ifc->ifc_units, M_CLONE);
+       if_cloners_count--;
+}
+
+/*
+ * Provide list of interface cloners to userspace.
+ */
+static int
+if_clone_list(struct if_clonereq *ifcr)
+{
+       char outbuf[IFNAMSIZ], *dst;
+       struct if_clone *ifc;
+       int count, error = 0;
+
+       ifcr->ifcr_total = if_cloners_count;
+       if ((dst = ifcr->ifcr_buffer) == NULL) {
+               /* Just asking how many there are. */
+               return (0);
+       }
+
+       if (ifcr->ifcr_count < 0)
+               return (EINVAL);
+
+       count = (if_cloners_count < ifcr->ifcr_count) ?
+           if_cloners_count : ifcr->ifcr_count;
+
+       for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
+            ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
+               strncpy(outbuf, ifc->ifc_name, IFNAMSIZ - 1);
+               error = copyout(outbuf, dst, IFNAMSIZ);
+               if (error)
+                       break;
+       }
+
+       return (error);
+}
+
+__private_extern__ int
+ifa_foraddr(addr)
+       unsigned int addr;
+{
+       register struct ifnet *ifp;
+       register struct ifaddr *ifa;
+       register unsigned int addr2;
+       
+
+       for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next)
+           for (ifa = ifp->if_addrhead.tqh_first; ifa;
+                ifa = ifa->ifa_link.tqe_next) {
+               if (ifa->ifa_addr->sa_family != AF_INET)
+                       continue;
+               addr2 = IA_SIN(ifa)->sin_addr.s_addr;
+
+               if (addr == addr2)
+                       return (1);
+       }
+       return (0);
+}
+
 /*
  * Locate an interface based on a complete address.
  */
@@ -498,10 +819,6 @@ if_route(ifp, flag, fam)
                        pfctlinput(PRC_IFUP, ifa->ifa_addr);
        rt_ifmsg(ifp);
 
-#if INET6
-       if (ip6_auto_on) /* Only if IPv6 is on on configured on on all ifs */
-               in6_if_up(ifp);
-#endif
 }
 
 /*
@@ -650,6 +967,22 @@ ifioctl(so, cmd, data, p)
                return (ifconf(cmd, data));
        }
        ifr = (struct ifreq *)data;
+
+       switch (cmd) {
+       case SIOCIFCREATE:
+       case SIOCIFDESTROY:
+               error = suser(p->p_ucred, &p->p_acflag);
+               if (error)
+                       return (error);
+               return ((cmd == SIOCIFCREATE) ?
+                       if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) :
+                       if_clone_destroy(ifr->ifr_name));
+#if 0
+       case SIOCIFGCLONERS:
+               return (if_clone_list((struct if_clonereq *)data));
+#endif 0
+       }
+
        ifp = ifunit(ifr->ifr_name);
        if (ifp == 0)
                return (ENXIO);
@@ -848,6 +1181,10 @@ ifioctl(so, cmd, data, p)
                }
                return error;
 
+       case SIOCSETVLAN:
+               if (ifp->if_type != IFT_L2VLAN) {
+                       return (EOPNOTSUPP);
+               }
        case SIOCSIFPHYADDR:
        case SIOCDIFPHYADDR:
 #ifdef INET6
@@ -880,6 +1217,12 @@ ifioctl(so, cmd, data, p)
 
                return dlil_ioctl(so->so_proto->pr_domain->dom_family, 
                                   ifp, cmd, (caddr_t) data);
+       case SIOCGETVLAN:
+               if (ifp->if_type != IFT_L2VLAN) {
+                       return (EOPNOTSUPP);
+               }
+               return dlil_ioctl(so->so_proto->pr_domain->dom_family, 
+                                  ifp, cmd, (caddr_t) data);
 
        default:
                oif_flags = ifp->if_flags;
@@ -1220,41 +1563,65 @@ if_addmulti(ifp, sa, retifma)
        return 0;
 }
 
-/*
- * Remove a reference to a multicast address on this interface.  Yell
- * if the request does not match an existing membership.
- */
 int
-if_delmulti(ifp, sa)
-       struct ifnet *ifp;
-       struct sockaddr *sa;
+if_delmultiaddr(struct ifmultiaddr *ifma)
 {
-       struct ifmultiaddr *ifma;
-       int s;
-
-       for (ifma = ifp->if_multiaddrs.lh_first; ifma;
-            ifma = ifma->ifma_link.le_next)
-               if (equal(sa, ifma->ifma_addr))
-                       break;
-       if (ifma == 0)
-               return ENOENT;
-
+       struct sockaddr *sa;
+       struct ifnet *ifp;
+       
+       /* Verify ifma is valid */
+       {
+               struct ifmultiaddr *match = NULL;
+               for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) {
+                       for (match = ifp->if_multiaddrs.lh_first; match; match = match->ifma_link.le_next) {
+                               if (match->ifma_ifp != ifp) {
+                                       printf("if_delmultiaddr: ifma (%x) on ifp i(%s) is stale\n",
+                                                       match, if_name(ifp));
+                                       return (0) ; /* swallow error ? */
+                               }
+                               if (match == ifma)
+                                       break;
+                       }
+                       if (match == ifma)
+                               break;
+               }
+               if (match != ifma) {
+                       for (match = ifma_lostlist.lh_first; match; match = match->ifma_link.le_next) {
+                               if (match->ifma_ifp != NULL) {
+                                       printf("if_delmultiaddr: item on lost list (%x) contains non-null ifp=%s\n",
+                                                       match, if_name(match->ifma_ifp));
+                                       return (0) ; /* swallow error ? */
+                               }
+                               if (match == ifma)
+                                       break;
+                       }
+               }
+               
+               if (match != ifma) {
+                       printf("if_delmultiaddr: ifma 0x%X is invalid\n", ifma);
+                       return 0;
+               }
+       }
+       
        if (ifma->ifma_refcount > 1) {
                ifma->ifma_refcount--;
                return 0;
        }
 
-       rt_newmaddrmsg(RTM_DELMADDR, ifma);
        sa = ifma->ifma_lladdr;
-       s = splimp();
+
+       if (sa) /* send a routing msg for network addresses only */
+               rt_newmaddrmsg(RTM_DELMADDR, ifma);
+
+       ifp = ifma->ifma_ifp;
+       
        LIST_REMOVE(ifma, ifma_link);
        /*
         * Make sure the interface driver is notified
         * in the case of a link layer mcast group being left.
         */
-       if (ifma->ifma_addr->sa_family == AF_LINK && sa == 0)
+       if (ifp && ifma->ifma_addr->sa_family == AF_LINK && sa == 0)
                dlil_ioctl(0, ifp, SIOCDELMULTI, 0);
-       splx(s);
        FREE(ifma->ifma_addr, M_IFMADDR);
        FREE(ifma, M_IFMADDR);
        if (sa == 0)
@@ -1271,27 +1638,41 @@ if_delmulti(ifp, sa)
         * in the record for the link-layer address.  (So we don't complain
         * in that case.)
         */
-       for (ifma = ifp->if_multiaddrs.lh_first; ifma;
-            ifma = ifma->ifma_link.le_next)
+       if (ifp)
+               ifma = ifp->if_multiaddrs.lh_first;
+       else
+               ifma = ifma_lostlist.lh_first;
+       for (; ifma; ifma = ifma->ifma_link.le_next)
                if (equal(sa, ifma->ifma_addr))
                        break;
-       if (ifma == 0)
-               return 0;
-
-       if (ifma->ifma_refcount > 1) {
-               ifma->ifma_refcount--;
+       
+       FREE(sa, M_IFMADDR);
+       if (ifma == 0) {
                return 0;
        }
 
-       s = splimp();
-       LIST_REMOVE(ifma, ifma_link);
-       dlil_ioctl(0, ifp, SIOCDELMULTI, (caddr_t) 0);
-       splx(s);
-       FREE(ifma->ifma_addr, M_IFMADDR);
-       FREE(sa, M_IFMADDR);
-       FREE(ifma, M_IFMADDR);
+       return if_delmultiaddr(ifma);
+}
 
-       return 0;
+/*
+ * Remove a reference to a multicast address on this interface.  Yell
+ * if the request does not match an existing membership.
+ */
+int
+if_delmulti(ifp, sa)
+       struct ifnet *ifp;
+       struct sockaddr *sa;
+{
+       struct ifmultiaddr *ifma;
+
+       for (ifma = ifp->if_multiaddrs.lh_first; ifma;
+            ifma = ifma->ifma_link.le_next)
+               if (equal(sa, ifma->ifma_addr))
+                       break;
+       if (ifma == 0)
+               return ENOENT;
+       
+       return if_delmultiaddr(ifma);
 }