*
* @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@
*/
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
/*
* 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.
*
* 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
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
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.
*/
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
}
/*
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);
}
return error;
+ case SIOCSETVLAN:
+ if (ifp->if_type != IFT_L2VLAN) {
+ return (EOPNOTSUPP);
+ }
case SIOCSIFPHYADDR:
case SIOCDIFPHYADDR:
#ifdef INET6
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;
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)
* 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);
}