* Atomically update the global in6_multi state, when a membership's * filter list is being updated in any way. * * imf is the per-inpcb-membership group filter pointer. * A fake imf may be passed for in-kernel consumers. * * XXX This is a candidate for a set-symmetric-difference style loop * which would eliminate the repeated lookup from root of ims nodes, * as they share the same key spac
| 995 | * and return a non-zero value. |
| 996 | */ |
| 997 | static int |
| 998 | in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) |
| 999 | { |
| 1000 | struct ip6_msource *ims, *nims; |
| 1001 | struct in6_msource *lims; |
| 1002 | int schanged, error; |
| 1003 | int nsrc0, nsrc1; |
| 1004 | |
| 1005 | schanged = 0; |
| 1006 | error = 0; |
| 1007 | nsrc1 = nsrc0 = 0; |
| 1008 | IN6_MULTI_LIST_LOCK_ASSERT(); |
| 1009 | |
| 1010 | /* |
| 1011 | * Update the source filters first, as this may fail. |
| 1012 | * Maintain count of in-mode filters at t0, t1. These are |
| 1013 | * used to work out if we transition into ASM mode or not. |
| 1014 | * Maintain a count of source filters whose state was |
| 1015 | * actually modified by this operation. |
| 1016 | */ |
| 1017 | RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { |
| 1018 | lims = (struct in6_msource *)ims; |
| 1019 | if (lims->im6sl_st[0] == imf->im6f_st[0]) nsrc0++; |
| 1020 | if (lims->im6sl_st[1] == imf->im6f_st[1]) nsrc1++; |
| 1021 | if (lims->im6sl_st[0] == lims->im6sl_st[1]) continue; |
| 1022 | error = in6m_get_source(inm, &lims->im6s_addr, 0, &nims); |
| 1023 | ++schanged; |
| 1024 | if (error) |
| 1025 | break; |
| 1026 | im6s_merge(nims, lims, 0); |
| 1027 | } |
| 1028 | if (error) { |
| 1029 | struct ip6_msource *bims; |
| 1030 | |
| 1031 | RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) { |
| 1032 | lims = (struct in6_msource *)ims; |
| 1033 | if (lims->im6sl_st[0] == lims->im6sl_st[1]) |
| 1034 | continue; |
| 1035 | (void)in6m_get_source(inm, &lims->im6s_addr, 1, &bims); |
| 1036 | if (bims == NULL) |
| 1037 | continue; |
| 1038 | im6s_merge(bims, lims, 1); |
| 1039 | } |
| 1040 | goto out_reap; |
| 1041 | } |
| 1042 | |
| 1043 | CTR3(KTR_MLD, "%s: imf filters in-mode: %d at t0, %d at t1", |
| 1044 | __func__, nsrc0, nsrc1); |
| 1045 | |
| 1046 | /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ |
| 1047 | if (imf->im6f_st[0] == imf->im6f_st[1] && |
| 1048 | imf->im6f_st[1] == MCAST_INCLUDE) { |
| 1049 | if (nsrc1 == 0) { |
| 1050 | CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); |
| 1051 | --inm->in6m_st[1].iss_in; |
| 1052 | } |
| 1053 | } |
| 1054 |
no test coverage detected