ifnet: Properly protect if_multiaddrs using ifnet serializers
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 13 May 2014 13:59:18 +0000 (21:59 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 18 May 2014 08:22:03 +0000 (16:22 +0800)
- Protect ifnet.if_multiaddrs using ifnet serializers.  Add some
  comment in the places, where only main serailizer is necessary.
- Fix if_delallmulti().  Using TAILQ_FOREACH_MUTABLE is incorrect for
  deleting an ifmultiaddr from ifnet.if_multiaddrs.  Since deleting one
  ifmultiaddr may cause additional ifmultiaddr deletion (e.g. the AF_LINK
  ifmultiaddr for AF_INET ifmultiaddr).
- Change IN_LOOKUP_MULTI and IN6_LOOKUP_MULTI macros into inline
  functions.
- Redispatch multicast IP packets to netisr0 for further processing.
  Software based IP packet hash function is changed.  And hash value
  fixup for multicast IP packets is added to the beginning of ip_input();
  this is mainly for IP packets, whose hash is calculated by hardware.
- For wlan's multicast hardware filter updating, we no longer need to
  release wlan serializer and mess up w/ the if_ioctl setting.

In netisr0, read and test ifma_refcount for AF_INET ifmultiaddr is MPSAFE
w/o ifnet serializers, since its ifma_refcount is only altered in netisr0.

In netisr0, any operation on in_multi, which is obtained from the
corresponding ifmuliaddr's ifma_protospec, is MPSAFE w/o ifnet
serializers, since ifmultiaddr for AF_INET is only set and cleared in
netisr0.

While I'm here also redispatch IP packets w/o hash to the proper netisrs,
on ip_input() path.  And unnecessary critical sections in
in_{add,del}multi() are removed.

19 files changed:
sys/net/if.c
sys/net/if_var.h
sys/net/vlan/if_vlan.c
sys/netinet/igmp.c
sys/netinet/in.c
sys/netinet/in_var.h
sys/netinet/ip_demux.c
sys/netinet/ip_input.c
sys/netinet/ip_output.c
sys/netinet6/in6.c
sys/netinet6/in6_ifattach.c
sys/netinet6/in6_var.h
sys/netinet6/ip6_mroute.c
sys/netinet6/ip6_output.c
sys/netinet6/mld6.c
sys/netinet6/nd6.c
sys/netproto/802_11/wlan/ieee80211_ioctl.c
sys/netproto/ipsec/key.c
sys/netproto/key/key.c

index 612fe69..3f55699 100644 (file)
@@ -116,6 +116,7 @@ static int  if_rtdel(struct radix_node *, void *);
 
 /* Helper functions */
 static void    ifsq_watchdog_reset(struct ifsubq_watchdog *);
+static int     if_delmulti_serialized(struct ifnet *, struct sockaddr *);
 
 #ifdef INET6
 /*
@@ -2152,15 +2153,15 @@ if_allmulti(struct ifnet *ifp, int onswitch)
  * The link layer provides a routine which converts
  */
 int
-if_addmulti(
-       struct ifnet *ifp,      /* interface to manipulate */
-       struct sockaddr *sa,    /* address to add */
-       struct ifmultiaddr **retifma)
+if_addmulti_serialized(struct ifnet *ifp, struct sockaddr *sa,
+    struct ifmultiaddr **retifma)
 {
        struct sockaddr *llsa, *dupsa;
        int error;
        struct ifmultiaddr *ifma;
 
+       ASSERT_IFNET_SERIALIZED_ALL(ifp);
+
        /*
         * If the matching multicast address already exists
         * then don't add a new one, just add a reference
@@ -2180,10 +2181,8 @@ if_addmulti(
         * already.
         */
        if (ifp->if_resolvemulti) {
-               ifnet_serialize_all(ifp);
                error = ifp->if_resolvemulti(ifp, &llsa, sa);
-               ifnet_deserialize_all(ifp);
-               if (error) 
+               if (error)
                        return error;
        } else {
                llsa = NULL;
@@ -2200,13 +2199,7 @@ if_addmulti(
        ifma->ifma_protospec = NULL;
        rt_newmaddrmsg(RTM_NEWMADDR, ifma);
 
-       /*
-        * Some network interfaces can scan the address list at
-        * interrupt time; lock them out.
-        */
-       crit_enter();
        TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
-       crit_exit();
        if (retifma)
                *retifma = ifma;
 
@@ -2224,34 +2217,43 @@ if_addmulti(
                        ifma->ifma_addr = dupsa;
                        ifma->ifma_ifp = ifp;
                        ifma->ifma_refcount = 1;
-                       crit_enter();
                        TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
-                       crit_exit();
                }
        }
        /*
         * We are certain we have added something, so call down to the
         * interface to let them know about it.
         */
-       crit_enter();
-       ifnet_serialize_all(ifp);
        if (ifp->if_ioctl)
                ifp->if_ioctl(ifp, SIOCADDMULTI, 0, NULL);
-       ifnet_deserialize_all(ifp);
-       crit_exit();
 
        return 0;
 }
 
+int
+if_addmulti(struct ifnet *ifp, struct sockaddr *sa,
+    struct ifmultiaddr **retifma)
+{
+       int error;
+
+       ifnet_serialize_all(ifp);
+       error = if_addmulti_serialized(ifp, sa, retifma);
+       ifnet_deserialize_all(ifp);
+
+       return error;
+}
+
 /*
  * Remove a reference to a multicast address on this interface.  Yell
  * if the request does not match an existing membership.
  */
-int
-if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
+static int
+if_delmulti_serialized(struct ifnet *ifp, struct sockaddr *sa)
 {
        struct ifmultiaddr *ifma;
 
+       ASSERT_IFNET_SERIALIZED_ALL(ifp);
+
        TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
                if (sa_equal(sa, ifma->ifma_addr))
                        break;
@@ -2265,18 +2267,13 @@ if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
 
        rt_newmaddrmsg(RTM_DELMADDR, ifma);
        sa = ifma->ifma_lladdr;
-       crit_enter();
        TAILQ_REMOVE(&ifp->if_multiaddrs, 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 == NULL) {
-               ifnet_serialize_all(ifp);
+       if (ifma->ifma_addr->sa_family == AF_LINK && sa == NULL)
                ifp->if_ioctl(ifp, SIOCDELMULTI, 0, NULL);
-               ifnet_deserialize_all(ifp);
-       }
-       crit_exit();
        kfree(ifma->ifma_addr, M_IFMADDR);
        kfree(ifma, M_IFMADDR);
        if (sa == NULL)
@@ -2304,12 +2301,8 @@ if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
                return 0;
        }
 
-       crit_enter();
-       ifnet_serialize_all(ifp);
        TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
        ifp->if_ioctl(ifp, SIOCDELMULTI, 0, NULL);
-       ifnet_deserialize_all(ifp);
-       crit_exit();
        kfree(ifma->ifma_addr, M_IFMADDR);
        kfree(sa, M_IFMADDR);
        kfree(ifma, M_IFMADDR);
@@ -2317,18 +2310,49 @@ if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
        return 0;
 }
 
+int
+if_delmulti(struct ifnet *ifp, struct sockaddr *sa)
+{
+       int error;
+
+       ifnet_serialize_all(ifp);
+       error = if_delmulti_serialized(ifp, sa);
+       ifnet_deserialize_all(ifp);
+
+       return error;
+}
+
 /*
  * Delete all multicast group membership for an interface.
  * Should be used to quickly flush all multicast filters.
  */
 void
-if_delallmulti(struct ifnet *ifp)
+if_delallmulti_serialized(struct ifnet *ifp)
 {
-       struct ifmultiaddr *ifma;
-       struct ifmultiaddr *next;
+       struct ifmultiaddr *ifma, mark;
+       struct sockaddr sa;
+
+       ASSERT_IFNET_SERIALIZED_ALL(ifp);
+
+       bzero(&sa, sizeof(sa));
+       sa.sa_family = AF_UNSPEC;
+       sa.sa_len = sizeof(sa);
+
+       bzero(&mark, sizeof(mark));
+       mark.ifma_addr = &sa;
 
-       TAILQ_FOREACH_MUTABLE(ifma, &ifp->if_multiaddrs, ifma_link, next)
-               if_delmulti(ifp, ifma->ifma_addr);
+       TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, &mark, ifma_link);
+
+       while ((ifma = TAILQ_NEXT(&mark, ifma_link)) != NULL) {
+               TAILQ_REMOVE(&ifp->if_multiaddrs, &mark, ifma_link);
+               TAILQ_INSERT_AFTER(&ifp->if_multiaddrs, ifma, &mark,
+                   ifma_link);
+
+               if (ifma->ifma_addr->sa_family == AF_UNSPEC)
+                       continue;
+
+               if_delmulti_serialized(ifp, ifma->ifma_addr);
+       }
 }
 
 
@@ -2403,9 +2427,12 @@ ifmaof_ifpforaddr(struct sockaddr *sa, struct ifnet *ifp)
 {
        struct ifmultiaddr *ifma;
 
+       /* TODO: need ifnet_serialize_main */
+       ifnet_serialize_all(ifp);
        TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
                if (sa_equal(ifma->ifma_addr, sa))
                        break;
+       ifnet_deserialize_all(ifp);
 
        return ifma;
 }
index 5695e80..608b562 100644 (file)
@@ -898,10 +898,12 @@ uint32_t  ether_crc32_le(const uint8_t *, size_t);
 uint32_t       ether_crc32_be(const uint8_t *, size_t);
 
 int    if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **);
+int    if_addmulti_serialized(struct ifnet *, struct sockaddr *,
+           struct ifmultiaddr **);
 int    if_allmulti(struct ifnet *, int);
 void   if_attach(struct ifnet *, struct lwkt_serialize *);
 int    if_delmulti(struct ifnet *, struct sockaddr *);
-void   if_delallmulti(struct ifnet *ifp);
+void   if_delallmulti_serialized(struct ifnet *ifp);
 void   if_purgeaddrs_nolink(struct ifnet *);
 void   if_detach(struct ifnet *);
 void   if_down(struct ifnet *);
index 8a8fb08..75d164c 100644 (file)
@@ -285,7 +285,7 @@ vlan_setflag(struct ifvlan *ifv, struct ifnet *ifp_p, int flag, int set,
 static int
 vlan_setmulti(struct ifvlan *ifv, struct ifnet *ifp_p)
 {
-       struct ifmultiaddr *ifma, *rifma = NULL;
+       struct ifmultiaddr *ifma;
        struct vlan_mc_entry *mc = NULL;
        struct sockaddr_dl sdl;
        struct ifnet *ifp = &ifv->ifv_if;
@@ -298,18 +298,12 @@ vlan_setmulti(struct ifvlan *ifv, struct ifnet *ifp_p)
        vlan_clrmulti(ifv, ifp_p);
 
        /*
-        * Now program new ones.
+        * Save the filter entries to be added to parent.
+        *
+        * TODO: need ifnet_serialize_main
         */
-       bzero(&sdl, sizeof(sdl));
-       sdl.sdl_len = sizeof(sdl);
-       sdl.sdl_family = AF_LINK;
-       sdl.sdl_index = ifp_p->if_index;
-       sdl.sdl_type = IFT_ETHER;
-       sdl.sdl_alen = ETHER_ADDR_LEN;
-
+       ifnet_serialize_all(ifp);
        TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
-               int error;
-
                if (ifma->ifma_addr->sa_family != AF_LINK)
                        continue;
 
@@ -318,13 +312,31 @@ vlan_setmulti(struct ifvlan *ifv, struct ifnet *ifp_p)
                bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
                      &mc->mc_addr, ETHER_ADDR_LEN);
                SLIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
+       }
+       ifnet_deserialize_all(ifp);
 
-               /* Program the parent multicast filter */
-               bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
-                     LLADDR(&sdl), ETHER_ADDR_LEN);
-               error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
-               if (error)
+       /*
+        * Now program new ones.
+        */
+       bzero(&sdl, sizeof(sdl));
+       sdl.sdl_len = sizeof(sdl);
+       sdl.sdl_family = AF_LINK;
+       sdl.sdl_index = ifp_p->if_index;
+       sdl.sdl_type = IFT_ETHER;
+       sdl.sdl_alen = ETHER_ADDR_LEN;
+
+       /*
+        * Program the parent multicast filter
+        */
+       SLIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
+               int error;
+
+               bcopy(&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
+               error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, NULL);
+               if (error) {
+                       /* XXX probably should keep going */
                        return error;
+               }
        }
        return 0;
 }
index 4a1da28..c0e4e7a 100644 (file)
@@ -332,7 +332,7 @@ igmp_input(struct mbuf **mp, int *offp, int proto)
                 * If we belong to the group being reported, stop
                 * our timer for that group.
                 */
-               IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
+               inm = IN_LOOKUP_MULTI(&igmp->igmp_group, ifp);
 
                if (inm != NULL) {
                        inm->inm_timer = 0;
index 1a66c1b..d2e41b5 100644 (file)
@@ -1352,24 +1352,17 @@ in_addmulti(struct in_addr *ap, struct ifnet *ifp)
        sin.sin_family = AF_INET;
        sin.sin_len = sizeof sin;
        sin.sin_addr = *ap;
-       crit_enter();
        error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma);
-       if (error) {
-               crit_exit();
-               return 0;
-       }
+       if (error)
+               return NULL;
 
        /*
         * If ifma->ifma_protospec is null, then if_addmulti() created
         * a new record.  Otherwise, we are done.
         */
-       if (ifma->ifma_protospec != NULL) {
-               crit_exit();
+       if (ifma->ifma_protospec != NULL)
                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 = kmalloc(sizeof *inm, M_IPMADDR, M_WAITOK | M_ZERO);
        inm->inm_addr = *ap;
        inm->inm_ifp = ifp;
@@ -1381,8 +1374,7 @@ in_addmulti(struct in_addr *ap, struct ifnet *ifp)
         * Let IGMP know that we have joined a new IP multicast group.
         */
        igmp_joingroup(inm);
-       crit_exit();
-       return (inm);
+       return inm;
 }
 
 /*
@@ -1397,7 +1389,6 @@ in_delmulti(struct in_multi *inm)
        KASSERT(&curthread->td_msgport == netisr_cpuport(0),
            ("in_delmulti is not called in netisr0"));
 
-       crit_enter();
        ifma = inm->inm_ifma;
        my_inm.inm_ifp = NULL ; /* don't send the leave msg */
        if (ifma->ifma_refcount == 1) {
@@ -1416,7 +1407,6 @@ in_delmulti(struct in_multi *inm)
        if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
        if (my_inm.inm_ifp != NULL)
                igmp_leavegroup(&my_inm);
-       crit_exit();
 }
 
 static void
index 946bc98..cb5d067 100644 (file)
@@ -194,24 +194,29 @@ struct in_multistep {
 };
 
 /*
- * Macro for looking up the in_multi record for a given IP multicast address
- * on a given interface.  If no matching record is found, "inm" is set null.
+ * Look up the in_multi record for a given IP multicast address on a given
+ * interface.  If no matching record is found, NULL is returned.
  */
-#define IN_LOOKUP_MULTI(addr, ifp, inm) \
-       /* struct in_addr addr; */ \
-       /* struct ifnet *ifp; */ \
-       /* struct in_multi *inm; */ \
-do { \
-       struct ifmultiaddr *ifma; \
-\
-       TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { \
-               if (ifma->ifma_addr->sa_family == AF_INET \
-                   && ((struct sockaddr_in *)ifma->ifma_addr)->sin_addr.s_addr == \
-                   (addr).s_addr) \
-                       break; \
-       } \
-       (inm) = ifma ? ifma->ifma_protospec : NULL; \
-} while(0)
+static __inline struct in_multi *
+IN_LOOKUP_MULTI(const struct in_addr *_addr, struct ifnet *_ifp)
+{
+       const struct ifmultiaddr *_ifma;
+       struct in_multi *_inm = NULL;
+
+       /* TODO: need ifnet_serialize_main */
+       ifnet_serialize_all(_ifp);
+       TAILQ_FOREACH(_ifma, &_ifp->if_multiaddrs, ifma_link) {
+               if (_ifma->ifma_addr->sa_family == AF_INET &&
+                   ((struct sockaddr_in *)_ifma->ifma_addr)->sin_addr.s_addr ==
+                   _addr->s_addr) {
+                       _inm = _ifma->ifma_protospec;
+                       break;
+               }
+       }
+       ifnet_deserialize_all(_ifp);
+
+       return _inm;
+}
 
 /*
  * Macro to step through all of the in_multi records, one at a time.
index 9280be7..9db38ad 100644 (file)
@@ -294,6 +294,11 @@ ip_hashfn(struct mbuf **mptr, int hoff, int dir)
                break;
 
        case IPPROTO_UDP:
+               if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+                       /* XXX handle multicast on CPU0 for now */
+                       hash = 0;
+                       break;
+               }
                uh = (struct udphdr *)((caddr_t)ip + iphlen);
                hash = INP_MPORT_HASH_UDP(ip->ip_src.s_addr, ip->ip_dst.s_addr,
                    uh->uh_sport, uh->uh_dport);
index 3387c43..29dc548 100644 (file)
@@ -425,6 +425,7 @@ ip_input(struct mbuf *m)
        int hlen, checkif;
        u_short sum;
        struct in_addr pkt_dst;
+       boolean_t check_msgport = FALSE;
        boolean_t using_srcrt = FALSE;          /* forward (by PFIL_HOOKS) */
        struct in_addr odst;                    /* original dst address(NAT) */
        struct m_tag *mtag;
@@ -448,9 +449,29 @@ ip_input(struct mbuf *m)
                if (m == NULL)
                        return;
                KKASSERT(m->m_flags & M_HASH);
+               check_msgport = TRUE;
        }
        ip = mtod(m, struct ip *);
 
+       if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+               /*
+                * XXX handle multicast on CPU0 for now.
+                *
+                * This could happen for packets hashed by hardware
+                * using RSS, which does not differentiate multicast
+                * packets from unicast packets.
+                */
+               m->m_pkthdr.hash = 0;
+               check_msgport = TRUE;
+       }
+
+       if (check_msgport &&
+           &curthread->td_msgport != netisr_hashport(m->m_pkthdr.hash)) {
+               netisr_queue(NETISR_IP, m);
+               /* Requeued to other netisr msgport; done */
+               return;
+       }
+
        /*
         * Pull out certain tags
         */
@@ -743,7 +764,7 @@ pass:
                 * See if we belong to the destination multicast group on the
                 * arrival interface.
                 */
-               IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
+               inm = IN_LOOKUP_MULTI(&ip->ip_dst, m->m_pkthdr.rcvif);
                if (inm == NULL) {
                        rel_mplock();
                        ipstat.ips_notmember++;
index 69d682f..e7522c1 100644 (file)
@@ -413,7 +413,7 @@ reroute:
                if (ip->ip_src.s_addr != INADDR_ANY) {
                        struct in_multi *inm;
 
-                       IN_LOOKUP_MULTI(pkt_dst, ifp, inm);
+                       inm = IN_LOOKUP_MULTI(&pkt_dst, ifp);
                        if (inm != NULL &&
                            (imo == NULL || imo->imo_multicast_loop)) {
                                /*
index 903ba56..cf5bc6c 100644 (file)
@@ -1084,7 +1084,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
                mltaddr.sin6_addr = kin6addr_linklocal_allnodes;
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
 
-               IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&mltaddr.sin6_addr, ifp);
                if (in6m == NULL) {
                        rtrequest_global(RTM_ADD,
                                  (struct sockaddr *)&mltaddr,
@@ -1107,7 +1107,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 #define hostnamelen    strlen(hostname)
                if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
                    == 0) {
-                       IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+                       in6m = IN6_LOOKUP_MULTI(&mltaddr.sin6_addr, ifp);
                        if (in6m == NULL && ia != NULL) {
                                in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
                                if (error != 0) {
@@ -1135,7 +1135,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 
                        mltaddr.sin6_addr = kin6addr_nodelocal_allnodes;
 
-                       IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+                       in6m = IN6_LOOKUP_MULTI(&mltaddr.sin6_addr, ifp);
                        if (in6m == NULL && ia_loop != NULL) {
                                rtrequest_global(RTM_ADD,
                                          (struct sockaddr *)&mltaddr,
@@ -1240,7 +1240,7 @@ in6_purgeaddr(struct ifaddr *ifa)
                        ia->ia_addr.sin6_addr.s6_addr32[3];
                llsol.s6_addr8[12] = 0xff;
 
-               IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&llsol, ifp);
                if (in6m)
                        in6_delmulti(in6m);
        }
index 770eda4..09d77ab 100644 (file)
@@ -643,7 +643,7 @@ in6_nigroup_attach(const char *name, int namelen)
 
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-               IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&mltaddr.sin6_addr, ifp);
                if (!in6m) {
                        if (!in6_addmulti(&mltaddr.sin6_addr, ifp, &error)) {
                                nd6log((LOG_ERR, "%s: failed to join %s "
@@ -670,7 +670,7 @@ in6_nigroup_detach(const char *name, int namelen)
 
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-               IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&mltaddr.sin6_addr, ifp);
                if (in6m)
                        in6_delmulti(in6m);
        }
index ffd8c96..7c7ae4c 100644 (file)
@@ -554,25 +554,30 @@ struct    in6_multistep {
 };
 
 /*
- * Macros for looking up the in6_multi record for a given IP6 multicast
- * address on a given interface. If no matching record is found, "in6m"
- * returns NLL.
+ * Look up the in6_multi record for a given IP6 multicast address on a given
+ * interface.  If no matching record is found, NULL is returned.
  */
-
-#define IN6_LOOKUP_MULTI(addr, ifp, in6m)                      \
-/* struct in6_addr addr; */                                    \
-/* struct ifnet *ifp; */                                       \
-/* struct in6_multi *in6m; */                                  \
-do { \
-       struct ifmultiaddr *ifma; \
-       TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { \
-               if (ifma->ifma_addr->sa_family == AF_INET6 \
-                   && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \
-                                         &(addr))) \
-                       break; \
-       } \
-       (in6m) = (struct in6_multi *)(ifma ? ifma->ifma_protospec : NULL); \
-} while(0)
+static __inline struct in6_multi *
+IN6_LOOKUP_MULTI(const struct in6_addr *_addr, struct ifnet *_ifp)
+{
+       const struct ifmultiaddr *_ifma;
+       struct in6_multi *_in6m = NULL;
+
+       /* TODO: need ifnet_serialize_main */
+       ifnet_serialize_all(_ifp);
+       TAILQ_FOREACH(_ifma, &_ifp->if_multiaddrs, ifma_link) {
+               if (_ifma->ifma_addr->sa_family == AF_INET6 &&
+                   IN6_ARE_ADDR_EQUAL(
+                   &((struct sockaddr_in6 *)_ifma->ifma_addr)->sin6_addr,
+                   _addr)) {
+                       _in6m = _ifma->ifma_protospec;
+                       break;
+               }
+       }
+       ifnet_deserialize_all(_ifp);
+
+       return _in6m;
+}
 
 /*
  * Macro to step through all of the in6_multi records, one at a time.
index 544ba46..cb762d0 100644 (file)
@@ -1429,7 +1429,7 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m)
         * on the outgoing interface, loop back a copy.
         */
        dst6 = (struct sockaddr_in6 *)&ro.ro_dst;
-       IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+       in6m = IN6_LOOKUP_MULTI(&ip6->ip6_dst, ifp);
        if (in6m != NULL) {
                dst6->sin6_len = sizeof(struct sockaddr_in6);
                dst6->sin6_family = AF_INET6;
index 1203ec3..8710888 100644 (file)
@@ -695,7 +695,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro,
                        error = ENETUNREACH;
                        goto bad;
                }
-               IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&ip6->ip6_dst, ifp);
                if (in6m != NULL &&
                   (im6o == NULL || im6o->im6o_multicast_loop)) {
                        /*
index e92d068..04b9811 100644 (file)
@@ -184,7 +184,8 @@ mld6_input(struct mbuf *m, int off)
        struct ifnet *ifp = m->m_pkthdr.rcvif;
        struct in6_multi *in6m;
        struct in6_ifaddr *ia;
-       struct ifmultiaddr *ifma;
+       struct ifmultiaddr *ifma, mark;
+       struct sockaddr sa;
        int timer;              /* timer value in the MLD query header */
 
 #ifndef PULLDOWN_TEST
@@ -265,9 +266,23 @@ mld6_input(struct mbuf *m, int off)
                        timer = 1;
                mld6_all_nodes_linklocal.s6_addr16[1] =
                        htons(ifp->if_index); /* XXX */
-               
-               TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
-               {
+
+               bzero(&sa, sizeof(sa));
+               sa.sa_family = AF_UNSPEC;
+               sa.sa_len = sizeof(sa);
+
+               bzero(&mark, sizeof(mark));
+               mark.ifma_addr = &sa;
+
+               /* TODO: need ifnet_serialize_main */
+               ifnet_serialize_all(ifp);
+
+               TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, &mark, ifma_link);
+               while ((ifma = TAILQ_NEXT(&mark, ifma_link)) != NULL) {
+                       TAILQ_REMOVE(&ifp->if_multiaddrs, &mark, ifma_link);
+                       TAILQ_INSERT_AFTER(&ifp->if_multiaddrs, ifma, &mark,
+                           ifma_link);
+
                        if (ifma->ifma_addr->sa_family != AF_INET6)
                                continue;
                        in6m = (struct in6_multi *)ifma->ifma_protospec;
@@ -282,9 +297,15 @@ mld6_input(struct mbuf *m, int off)
                                                &in6m->in6m_addr))
                        {
                                if (timer == 0) {
+                                       /*
+                                        * Release serializer(s) temporarily,
+                                        * before sending report.
+                                        */
+                                       ifnet_deserialize_all(ifp);
                                        /* send a report immediately */
                                        mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
                                                NULL);
+                                       ifnet_serialize_all(ifp);
                                        in6m->in6m_timer = 0; /* reset timer */
                                        in6m->in6m_state = MLD6_IREPORTEDLAST;
                                }
@@ -297,6 +318,8 @@ mld6_input(struct mbuf *m, int off)
                        }
                }
 
+               ifnet_deserialize_all(ifp);
+
                if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
                        mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
                break;
@@ -323,7 +346,7 @@ mld6_input(struct mbuf *m, int off)
                 * If we belong to the group being reported, stop
                 * our timer for that group.
                 */
-               IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&mldh->mld_addr, ifp);
                if (in6m) {
                        in6m->in6m_timer = 0; /* transit to idle state */
                        in6m->in6m_state = MLD6_OTHERLISTENER; /* clear flag */
index 75ce398..3c9d52e 100644 (file)
@@ -1282,7 +1282,7 @@ nd6_rtrequest(int req, struct rtentry *rt)
                        llsol.s6_addr32[2] = htonl(1);
                        llsol.s6_addr8[12] = 0xff;
 
-                       IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+                       in6m = IN6_LOOKUP_MULTI(&llsol, ifp);
                        if (in6m)
                                in6_delmulti(in6m);
                }
index 2cadd9b..80e98a3 100644 (file)
@@ -3181,12 +3181,8 @@ ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
 {
        struct ifnet *parent = ic->ic_ifp;
        struct ieee80211vap *vap;
-       void *ioctl;
 
-       wlan_serialize_exit();
-       if_delallmulti(parent);
-       ioctl = parent->if_ioctl;       /* XXX WAR if_allmulti */
-       parent->if_ioctl = NULL;
+       if_delallmulti_serialized(parent);
        TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
                struct ifnet *ifp = vap->iv_ifp;
                struct ifmultiaddr *ifma;
@@ -3194,12 +3190,10 @@ ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
                TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
                        if (ifma->ifma_addr->sa_family != AF_LINK)
                                continue;
-                       (void) if_addmulti(parent, ifma->ifma_addr, NULL);
+                       if_addmulti_serialized(parent, ifma->ifma_addr, NULL);
                }
        }
-       parent->if_ioctl = ioctl;
        ieee80211_runtask(ic, &ic->ic_mcast_task);
-       wlan_serialize_enter();
 }
 
 int
index ffafcf0..a6c9039 100644 (file)
@@ -3561,8 +3561,7 @@ key_ismyaddr6(struct sockaddr_in6 *sin6)
                 * about IPv4 multicast??
                 * XXX scope
                 */
-               in6m = NULL;
-               IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&sin6->sin6_addr, ia->ia_ifp);
                if (in6m)
                        return 1;
        }
index 14aa905..fc9068d 100644 (file)
@@ -3804,8 +3804,7 @@ key_ismyaddr6(struct sockaddr_in6 *sin6)
                 * about IPv4 multicast??
                 * XXX scope
                 */
-               in6m = NULL;
-               IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m);
+               in6m = IN6_LOOKUP_MULTI(&sin6->sin6_addr, ia->ia_ifp);
                if (in6m)
                        return 1;
        }