* Atomically update the global in_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 space
| 1025 | * and return a non-zero value. |
| 1026 | */ |
| 1027 | static int |
| 1028 | inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) |
| 1029 | { |
| 1030 | struct ip_msource *ims, *nims; |
| 1031 | struct in_msource *lims; |
| 1032 | int schanged, error; |
| 1033 | int nsrc0, nsrc1; |
| 1034 | |
| 1035 | schanged = 0; |
| 1036 | error = 0; |
| 1037 | nsrc1 = nsrc0 = 0; |
| 1038 | IN_MULTI_LIST_LOCK_ASSERT(); |
| 1039 | |
| 1040 | /* |
| 1041 | * Update the source filters first, as this may fail. |
| 1042 | * Maintain count of in-mode filters at t0, t1. These are |
| 1043 | * used to work out if we transition into ASM mode or not. |
| 1044 | * Maintain a count of source filters whose state was |
| 1045 | * actually modified by this operation. |
| 1046 | */ |
| 1047 | RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { |
| 1048 | lims = (struct in_msource *)ims; |
| 1049 | if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++; |
| 1050 | if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++; |
| 1051 | if (lims->imsl_st[0] == lims->imsl_st[1]) continue; |
| 1052 | error = inm_get_source(inm, lims->ims_haddr, 0, &nims); |
| 1053 | ++schanged; |
| 1054 | if (error) |
| 1055 | break; |
| 1056 | ims_merge(nims, lims, 0); |
| 1057 | } |
| 1058 | if (error) { |
| 1059 | struct ip_msource *bims; |
| 1060 | |
| 1061 | RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) { |
| 1062 | lims = (struct in_msource *)ims; |
| 1063 | if (lims->imsl_st[0] == lims->imsl_st[1]) |
| 1064 | continue; |
| 1065 | (void)inm_get_source(inm, lims->ims_haddr, 1, &bims); |
| 1066 | if (bims == NULL) |
| 1067 | continue; |
| 1068 | ims_merge(bims, lims, 1); |
| 1069 | } |
| 1070 | goto out_reap; |
| 1071 | } |
| 1072 | |
| 1073 | CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1", |
| 1074 | __func__, nsrc0, nsrc1); |
| 1075 | |
| 1076 | /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ |
| 1077 | if (imf->imf_st[0] == imf->imf_st[1] && |
| 1078 | imf->imf_st[1] == MCAST_INCLUDE) { |
| 1079 | if (nsrc1 == 0) { |
| 1080 | CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__); |
| 1081 | --inm->inm_st[1].iss_in; |
| 1082 | } |
| 1083 | } |
| 1084 |
no test coverage detected