From 72659ed0ae74f217471476f10385924812a91186 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Tue, 13 May 2014 21:59:18 +0800 Subject: [PATCH] ifnet: Properly protect if_multiaddrs using ifnet serializers - 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. --- sys/net/if.c | 99 ++++++++++++++-------- sys/net/if_var.h | 4 +- sys/net/vlan/if_vlan.c | 44 ++++++---- sys/netinet/igmp.c | 2 +- sys/netinet/in.c | 18 +--- sys/netinet/in_var.h | 39 +++++---- sys/netinet/ip_demux.c | 5 ++ sys/netinet/ip_input.c | 23 ++++- sys/netinet/ip_output.c | 2 +- sys/netinet6/in6.c | 8 +- sys/netinet6/in6_ifattach.c | 4 +- sys/netinet6/in6_var.h | 41 +++++---- sys/netinet6/ip6_mroute.c | 2 +- sys/netinet6/ip6_output.c | 2 +- sys/netinet6/mld6.c | 33 ++++++-- sys/netinet6/nd6.c | 2 +- sys/netproto/802_11/wlan/ieee80211_ioctl.c | 10 +-- sys/netproto/ipsec/key.c | 3 +- sys/netproto/key/key.c | 3 +- 19 files changed, 213 insertions(+), 131 deletions(-) diff --git a/sys/net/if.c b/sys/net/if.c index 612fe69df0..3f556996f8 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -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; } diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 5695e8043d..608b5627cb 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -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 *); diff --git a/sys/net/vlan/if_vlan.c b/sys/net/vlan/if_vlan.c index 8a8fb08ff2..75d164cdac 100644 --- a/sys/net/vlan/if_vlan.c +++ b/sys/net/vlan/if_vlan.c @@ -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; } diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c index 4a1da28ce2..c0e4e7a098 100644 --- a/sys/netinet/igmp.c +++ b/sys/netinet/igmp.c @@ -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; diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 1a66c1b4e7..d2e41b5e47 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -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 diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 946bc98f86..cb5d0679de 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -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. diff --git a/sys/netinet/ip_demux.c b/sys/netinet/ip_demux.c index 9280be770f..9db38adf28 100644 --- a/sys/netinet/ip_demux.c +++ b/sys/netinet/ip_demux.c @@ -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); diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 3387c43bc0..29dc548586 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -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++; diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 69d682f68e..e7522c15a1 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -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)) { /* diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 903ba56d5a..cf5bc6c993 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -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); } diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 770eda4dc8..09d77abbe6 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -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); } diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index ffd8c96a7e..7c7ae4c141 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -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. diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index 544ba46079..cb762d0544 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -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; diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 1203ec39f3..8710888037 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -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)) { /* diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index e92d068314..04b981124d 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -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 */ diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 75ce3986c4..3c9d52ef8e 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -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); } diff --git a/sys/netproto/802_11/wlan/ieee80211_ioctl.c b/sys/netproto/802_11/wlan/ieee80211_ioctl.c index 2cadd9b6b4..80e98a3791 100644 --- a/sys/netproto/802_11/wlan/ieee80211_ioctl.c +++ b/sys/netproto/802_11/wlan/ieee80211_ioctl.c @@ -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 diff --git a/sys/netproto/ipsec/key.c b/sys/netproto/ipsec/key.c index ffafcf0326..a6c9039b48 100644 --- a/sys/netproto/ipsec/key.c +++ b/sys/netproto/ipsec/key.c @@ -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; } diff --git a/sys/netproto/key/key.c b/sys/netproto/key/key.c index 14aa905f71..fc9068dad2 100644 --- a/sys/netproto/key/key.c +++ b/sys/netproto/key/key.c @@ -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; } -- 2.41.0