Line data Source code
1 : /*
2 : *------------------------------------------------------------------
3 : * Copyright (c) 2018 Cisco and/or its affiliates.
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at:
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : *------------------------------------------------------------------
16 : */
17 :
18 : #include <igmp/igmp_report.h>
19 : #include <igmp/igmp_pkt.h>
20 :
21 : static ip46_address_t *
22 13 : igmp_group_mk_source_list (const igmp_membership_group_v3_t * r)
23 : {
24 13 : ip46_address_t *srcs = NULL;
25 : const ip4_address_t *s;
26 : u16 ii, n;
27 :
28 : /*
29 : * we validated this packet when we accepted it in the DP, so
30 : * this number is safe to use
31 : */
32 13 : n = clib_net_to_host_u16 (r->n_src_addresses);
33 :
34 13 : if (0 == n)
35 : {
36 : /* a (*,G) join has no source address specified */
37 5 : vec_validate (srcs, 0);
38 5 : srcs[0].ip4.as_u32 = 0;
39 : }
40 : else
41 : {
42 8 : vec_validate (srcs, n - 1);
43 8 : s = r->src_addresses;
44 :
45 23 : for (ii = 0; ii < n; ii++)
46 : {
47 15 : srcs[ii].ip4 = *s;
48 15 : s++;
49 : }
50 : }
51 :
52 13 : return (srcs);
53 : }
54 :
55 : static void
56 2 : igmp_handle_group_exclude (igmp_config_t * config,
57 : const igmp_membership_group_v3_t * igmp_group)
58 : {
59 2 : ip46_address_t key = {
60 : .ip4 = igmp_group->group_address,
61 : };
62 : u16 n;
63 :
64 : /*
65 : * treat an exclude all sources as a *,G join
66 : */
67 2 : n = clib_net_to_host_u16 (igmp_group->n_src_addresses);
68 :
69 2 : if (0 == n)
70 : {
71 : ip46_address_t *src, *srcs;
72 : igmp_group_t *group;
73 :
74 2 : group = igmp_group_lookup (config, &key);
75 2 : srcs = igmp_group_mk_source_list (igmp_group);
76 :
77 2 : IGMP_DBG (" ..group-update: %U (*, %U)",
78 : format_vnet_sw_if_index_name,
79 : vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
80 :
81 2 : if (NULL == group)
82 : {
83 2 : group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
84 : }
85 4 : vec_foreach (src, srcs)
86 : {
87 2 : igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
88 : }
89 :
90 2 : vec_free (srcs);
91 : }
92 : else
93 : {
94 0 : IGMP_DBG (" ..group-update: %U (*, %U) source exclude ignored",
95 : format_vnet_sw_if_index_name,
96 : vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
97 : }
98 2 : }
99 :
100 : static void
101 5 : igmp_handle_group_block (igmp_config_t * config,
102 : const igmp_membership_group_v3_t * igmp_group)
103 : {
104 : ip46_address_t *s, *srcs;
105 : igmp_pkt_build_query_t bq;
106 : igmp_group_t *group;
107 5 : ip46_address_t key = {
108 : .ip4 = igmp_group->group_address,
109 : };
110 :
111 5 : srcs = igmp_group_mk_source_list (igmp_group);
112 5 : group = igmp_group_lookup (config, &key);
113 :
114 5 : IGMP_DBG (" ..group-block: %U (%U, %U)",
115 : format_vnet_sw_if_index_name,
116 : vnet_get_main (), config->sw_if_index,
117 : format_igmp_key, &key, format_igmp_src_addr_list, srcs);
118 :
119 5 : if (group)
120 : {
121 : igmp_src_t *src;
122 : /*
123 : * send a group+source specific query
124 : */
125 4 : igmp_pkt_build_query_init (&bq, config->sw_if_index);
126 4 : igmp_pkt_query_v3_add_group (&bq, group, srcs);
127 4 : igmp_pkt_query_v3_send (&bq);
128 :
129 : /*
130 : * for each source left/blocked drop the source expire timer to the leave
131 : * latency timer
132 : */
133 9 : vec_foreach (s, srcs)
134 : {
135 5 : src = igmp_src_lookup (group, s);
136 5 : if (NULL != src)
137 5 : igmp_src_blocked (src);
138 : }
139 : }
140 : /*
141 : * a block/leave from a group for which we have no state
142 : */
143 :
144 5 : vec_free (srcs);
145 5 : }
146 :
147 : static void
148 9 : igmp_handle_group_update (igmp_config_t * config,
149 : const igmp_membership_group_v3_t * igmp_group)
150 : {
151 : ip46_address_t *src, *srcs;
152 : igmp_group_t *group;
153 9 : ip46_address_t key = {
154 : .ip4 = igmp_group->group_address,
155 : };
156 :
157 : /*
158 : * treat a TO_INC({}) as a (*,G) leave
159 : */
160 9 : if (0 == clib_net_to_host_u16 (igmp_group->n_src_addresses))
161 : {
162 3 : return (igmp_handle_group_block (config, igmp_group));
163 : }
164 :
165 6 : srcs = igmp_group_mk_source_list (igmp_group);
166 6 : group = igmp_group_lookup (config, &key);
167 :
168 6 : IGMP_DBG (" ..group-update: %U (%U, %U)",
169 : format_vnet_sw_if_index_name,
170 : vnet_get_main (), config->sw_if_index,
171 : format_igmp_key, &key, format_igmp_src_addr_list, srcs);
172 :
173 6 : if (NULL == group)
174 : {
175 5 : group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
176 : }
177 :
178 : /* create or update all sources */
179 18 : vec_foreach (src, srcs)
180 : {
181 12 : igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
182 : }
183 :
184 6 : vec_free (srcs);
185 : }
186 :
187 : static void
188 13 : igmp_handle_group (igmp_config_t * config,
189 : const igmp_membership_group_v3_t * igmp_group)
190 : {
191 13 : IGMP_DBG ("rx-group-report: %U",
192 : format_vnet_sw_if_index_name,
193 : vnet_get_main (), config->sw_if_index);
194 :
195 13 : switch (igmp_group->type)
196 : {
197 9 : case IGMP_MEMBERSHIP_GROUP_mode_is_include:
198 : case IGMP_MEMBERSHIP_GROUP_change_to_include:
199 : case IGMP_MEMBERSHIP_GROUP_allow_new_sources:
200 9 : igmp_handle_group_update (config, igmp_group);
201 9 : break;
202 2 : case IGMP_MEMBERSHIP_GROUP_block_old_sources:
203 2 : igmp_handle_group_block (config, igmp_group);
204 2 : break;
205 2 : case IGMP_MEMBERSHIP_GROUP_mode_is_exclude:
206 : case IGMP_MEMBERSHIP_GROUP_change_to_exclude:
207 2 : igmp_handle_group_exclude (config, igmp_group);
208 2 : break;
209 : /*
210 : * all other types ignored
211 : */
212 : }
213 13 : }
214 :
215 : void
216 13 : igmp_handle_report (const igmp_report_args_t * args)
217 : {
218 : const igmp_membership_group_v3_t *igmp_group;
219 : igmp_config_t *config;
220 : u16 n_groups, ii;
221 :
222 13 : config = igmp_config_lookup (args->sw_if_index);
223 :
224 13 : if (!config)
225 : /*
226 : * no IGMP config on the interface. quit
227 : */
228 0 : return;
229 :
230 13 : if (IGMP_MODE_HOST == config->mode)
231 : {
232 : /*
233 : * Hosts need not listen to the reports of other hosts.
234 : * we're done here
235 : */
236 0 : return;
237 : }
238 :
239 : /*
240 : * we validated this packet when we accepted it in the DP, so
241 : * this number is safe to use
242 : */
243 13 : n_groups = clib_net_to_host_u16 (args->report[0].n_groups);
244 13 : igmp_group = args->report[0].groups;
245 :
246 26 : for (ii = 0; ii < n_groups; ii++)
247 : {
248 13 : igmp_handle_group (config, igmp_group);
249 :
250 13 : igmp_group = group_cptr (igmp_group,
251 : igmp_membership_group_v3_length (igmp_group));
252 : }
253 :
254 13 : igmp_proxy_device_merge_config (config, 0);
255 : }
256 :
257 : /*
258 : * fd.io coding-style-patch-verification: ON
259 : *
260 : * Local Variables:
261 : * eval: (c-set-style "gnu")
262 : * End:
263 : */
|