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 <vlib/vlib.h>
19 : #include <vnet/mfib/mfib_entry.h>
20 : #include <vnet/mfib/mfib_table.h>
21 :
22 : #include <igmp/igmp_proxy.h>
23 : #include <igmp/igmp.h>
24 : #include <igmp/igmp_pkt.h>
25 :
26 : void
27 18 : igmp_proxy_device_mfib_path_add_del (igmp_group_t * group, u8 add)
28 : {
29 : igmp_config_t *config;
30 : u32 mfib_index;
31 :
32 18 : config = igmp_config_get (group->config);
33 : mfib_index =
34 18 : mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
35 : config->sw_if_index);
36 :
37 : /* *INDENT-OFF* */
38 18 : mfib_prefix_t mpfx_group_addr = {
39 : .fp_proto = FIB_PROTOCOL_IP4,
40 : .fp_len = 32,
41 : .fp_grp_addr = {
42 18 : .ip4 = (*group->key).ip4,
43 : },
44 : };
45 36 : fib_route_path_t via_itf_path =
46 : {
47 18 : .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
48 : .frp_addr = zero_addr,
49 18 : .frp_sw_if_index = config->sw_if_index,
50 : .frp_fib_index = 0,
51 : .frp_weight = 1,
52 : .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
53 : };
54 : /* *INDENT-ON* */
55 :
56 18 : if (add)
57 8 : mfib_table_entry_path_update (mfib_index, &mpfx_group_addr,
58 : MFIB_SOURCE_IGMP, MFIB_ENTRY_FLAG_NONE,
59 : &via_itf_path);
60 : else
61 10 : mfib_table_entry_path_remove (mfib_index, &mpfx_group_addr,
62 : MFIB_SOURCE_IGMP, &via_itf_path);
63 18 : }
64 :
65 : igmp_proxy_device_t *
66 32 : igmp_proxy_device_lookup (u32 vrf_id)
67 : {
68 32 : igmp_main_t *im = &igmp_main;
69 :
70 32 : if (vec_len (im->igmp_proxy_device_by_vrf_id) > vrf_id)
71 : {
72 : u32 index;
73 11 : index = im->igmp_proxy_device_by_vrf_id[vrf_id];
74 11 : if (index != ~0)
75 11 : return (vec_elt_at_index (im->proxy_devices, index));
76 : }
77 21 : return NULL;
78 : }
79 :
80 : int
81 7 : igmp_proxy_device_add_del (u32 vrf_id, u32 sw_if_index, u8 add)
82 : {
83 7 : igmp_main_t *im = &igmp_main;
84 : igmp_proxy_device_t *proxy_device;
85 : igmp_config_t *config;
86 : u32 mfib_index;
87 :
88 : /* check VRF id */
89 : mfib_index =
90 7 : mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
91 7 : if (mfib_index == ~0)
92 0 : return VNET_API_ERROR_INVALID_INTERFACE;
93 7 : if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
94 5 : return VNET_API_ERROR_INVALID_INTERFACE;
95 :
96 : /* check IGMP configuration */
97 2 : config = igmp_config_lookup (sw_if_index);
98 2 : if (!config)
99 0 : return VNET_API_ERROR_INVALID_INTERFACE;
100 2 : if (config->mode != IGMP_MODE_HOST)
101 0 : return VNET_API_ERROR_INVALID_INTERFACE;
102 :
103 2 : proxy_device = igmp_proxy_device_lookup (vrf_id);
104 2 : if (!proxy_device && add)
105 : {
106 2 : vec_validate_init_empty (im->igmp_proxy_device_by_vrf_id, vrf_id, ~0);
107 1 : pool_get (im->proxy_devices, proxy_device);
108 1 : im->igmp_proxy_device_by_vrf_id[vrf_id] =
109 1 : proxy_device - im->proxy_devices;
110 1 : clib_memset (proxy_device, 0, sizeof (igmp_proxy_device_t));
111 1 : proxy_device->vrf_id = vrf_id;
112 1 : proxy_device->upstream_if = sw_if_index;
113 1 : config->proxy_device_id = vrf_id;
114 : /* lock mfib table */
115 1 : mfib_table_lock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
116 : }
117 1 : else if (proxy_device && !add)
118 : {
119 2 : while (vec_len (proxy_device->downstream_ifs) > 0)
120 : {
121 1 : igmp_proxy_device_add_del_interface (vrf_id,
122 1 : proxy_device->downstream_ifs
123 : [0], 0);
124 : }
125 1 : vec_free (proxy_device->downstream_ifs);
126 1 : proxy_device->downstream_ifs = NULL;
127 1 : im->igmp_proxy_device_by_vrf_id[vrf_id] = ~0;
128 1 : pool_put (im->proxy_devices, proxy_device);
129 1 : config->proxy_device_id = ~0;
130 : /* clear proxy database */
131 1 : igmp_clear_config (config);
132 : /* unlock mfib table */
133 1 : mfib_table_unlock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
134 : }
135 : else
136 0 : return -1;
137 :
138 2 : return 0;
139 : }
140 :
141 : int
142 6 : igmp_proxy_device_add_del_interface (u32 vrf_id, u32 sw_if_index, u8 add)
143 : {
144 : igmp_proxy_device_t *proxy_device;
145 : u32 index;
146 : u32 mfib_index;
147 :
148 6 : proxy_device = igmp_proxy_device_lookup (vrf_id);
149 6 : if (!proxy_device)
150 2 : return -1;
151 :
152 : /* check VRF id */
153 : mfib_index =
154 4 : mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
155 4 : if (mfib_index == ~0)
156 0 : return VNET_API_ERROR_INVALID_INTERFACE;
157 4 : if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
158 0 : return VNET_API_ERROR_INVALID_INTERFACE;
159 :
160 : /* check IGMP configuration */
161 : igmp_config_t *config;
162 4 : config = igmp_config_lookup (sw_if_index);
163 4 : if (!config)
164 0 : return VNET_API_ERROR_INVALID_INTERFACE;
165 4 : if (config->mode != IGMP_MODE_ROUTER)
166 0 : return VNET_API_ERROR_INVALID_INTERFACE;
167 :
168 4 : if (add)
169 : {
170 2 : if (proxy_device->downstream_ifs)
171 : {
172 2 : index = vec_search (proxy_device->downstream_ifs, sw_if_index);
173 1 : if (index != ~0)
174 0 : return -1;
175 : }
176 2 : vec_add1 (proxy_device->downstream_ifs, sw_if_index);
177 2 : config->proxy_device_id = vrf_id;
178 : }
179 : else
180 : {
181 2 : if (!proxy_device->downstream_ifs)
182 0 : return -2;
183 2 : index = vec_search (proxy_device->downstream_ifs, sw_if_index);
184 2 : if (index == ~0)
185 0 : return -3;
186 : /* remove (S,G)s belonging to this interface from proxy database */
187 2 : igmp_proxy_device_merge_config (config, /* block */ 1);
188 2 : vec_del1 (proxy_device->downstream_ifs, index);
189 2 : config->proxy_device_id = ~0;
190 : }
191 :
192 4 : return 0;
193 : }
194 :
195 : void
196 9 : igmp_proxy_device_block_src (igmp_config_t * config, igmp_group_t * group,
197 : igmp_src_t * src)
198 : {
199 : igmp_proxy_device_t *proxy_device;
200 : igmp_config_t *proxy_config;
201 : igmp_group_t *proxy_group;
202 : igmp_src_t *proxy_src;
203 : u8 *ref;
204 :
205 9 : proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
206 9 : if (!proxy_device)
207 8 : return;
208 :
209 1 : proxy_config = igmp_config_lookup (proxy_device->upstream_if);
210 1 : ASSERT (proxy_config);
211 :
212 1 : proxy_group = igmp_group_lookup (proxy_config, group->key);
213 1 : if (proxy_group == NULL)
214 0 : return;
215 :
216 1 : proxy_src = igmp_src_lookup (proxy_group, src->key);
217 1 : if (proxy_src == NULL)
218 0 : return;
219 :
220 1 : if (vec_len (proxy_src->referance_by_config_index) <= group->config)
221 : {
222 0 : IGMP_DBG ("proxy block src: invalid config %u", group->config);
223 0 : return;
224 : }
225 1 : proxy_src->referance_by_config_index[group->config] = 0;
226 3 : vec_foreach (ref, proxy_src->referance_by_config_index)
227 : {
228 2 : if ((*ref) > 0)
229 0 : return;
230 : }
231 :
232 : /* build "Block Old Sources" report */
233 : igmp_pkt_build_report_t br;
234 1 : ip46_address_t *srcaddrs = NULL;
235 :
236 1 : igmp_pkt_build_report_init (&br, proxy_config->sw_if_index);
237 1 : vec_add1 (srcaddrs, *proxy_src->key);
238 1 : igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
239 : IGMP_MEMBERSHIP_GROUP_block_old_sources);
240 1 : igmp_pkt_report_v3_send (&br);
241 :
242 :
243 1 : igmp_group_src_remove (proxy_group, proxy_src);
244 1 : igmp_src_free (proxy_src);
245 :
246 1 : if (igmp_group_n_srcs (proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
247 : {
248 0 : igmp_proxy_device_mfib_path_add_del (proxy_group, 0);
249 0 : igmp_proxy_device_mfib_path_add_del (group, 0);
250 0 : igmp_group_clear (&proxy_group);
251 : }
252 : }
253 :
254 : always_inline void
255 9 : igmp_proxy_device_merge_src (igmp_group_t ** proxy_group, igmp_src_t * src,
256 : ip46_address_t ** srcaddrs, u8 block)
257 : {
258 : igmp_src_t *proxy_src;
259 : u32 d_config;
260 :
261 9 : proxy_src = igmp_src_lookup (*proxy_group, src->key);
262 :
263 9 : if (proxy_src == NULL)
264 : {
265 3 : if (block)
266 0 : return;
267 : /* store downstream config index */
268 3 : d_config = igmp_group_get (src->group)->config;
269 :
270 : proxy_src =
271 3 : igmp_src_alloc (igmp_group_index (*proxy_group), src->key,
272 : IGMP_MODE_HOST);
273 :
274 6 : hash_set_mem ((*proxy_group)->igmp_src_by_key
275 : [(*proxy_group)->router_filter_mode], proxy_src->key,
276 : igmp_src_index (proxy_src));
277 :
278 11 : vec_validate_init_empty (proxy_src->referance_by_config_index, d_config,
279 : 0);
280 3 : proxy_src->referance_by_config_index[d_config] = 1;
281 3 : vec_add1 (*srcaddrs, *proxy_src->key);
282 : }
283 : else
284 : {
285 6 : if (block)
286 : {
287 3 : d_config = igmp_group_get (src->group)->config;
288 3 : if (vec_len (proxy_src->referance_by_config_index) <= d_config)
289 : {
290 0 : IGMP_DBG ("proxy block src: invalid config %u", d_config);
291 0 : return;
292 : }
293 3 : proxy_src->referance_by_config_index[d_config] = 0;
294 : u8 *ref;
295 10 : vec_foreach (ref, proxy_src->referance_by_config_index)
296 : {
297 8 : if ((*ref) > 0)
298 1 : return;
299 : }
300 :
301 2 : vec_add1 (*srcaddrs, *proxy_src->key);
302 :
303 2 : igmp_group_src_remove (*proxy_group, proxy_src);
304 2 : igmp_src_free (proxy_src);
305 :
306 2 : if (igmp_group_n_srcs (*proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
307 : {
308 1 : igmp_proxy_device_mfib_path_add_del (*proxy_group, 0);
309 1 : igmp_group_clear (proxy_group);
310 : }
311 2 : return;
312 : }
313 3 : d_config = igmp_group_get (src->group)->config;
314 3 : vec_validate (proxy_src->referance_by_config_index, d_config);
315 3 : proxy_src->referance_by_config_index[d_config] = 1;
316 3 : return;
317 : }
318 : }
319 :
320 : always_inline igmp_group_t *
321 5 : igmp_proxy_device_merge_group (igmp_proxy_device_t * proxy_device,
322 : igmp_group_t * group,
323 : ip46_address_t ** srcaddrs, u8 block)
324 : {
325 : igmp_config_t *proxy_config;
326 : igmp_group_t *proxy_group;
327 : igmp_src_t *src;
328 :
329 5 : proxy_config = igmp_config_lookup (proxy_device->upstream_if);
330 5 : ALWAYS_ASSERT (proxy_config);
331 :
332 5 : proxy_group = igmp_group_lookup (proxy_config, group->key);
333 5 : if (!proxy_group)
334 : {
335 1 : if (block)
336 0 : return NULL;
337 1 : u32 tmp = igmp_group_index (group);
338 1 : proxy_group =
339 1 : igmp_group_alloc (proxy_config, group->key,
340 : group->router_filter_mode);
341 1 : igmp_proxy_device_mfib_path_add_del (proxy_group, 1);
342 1 : group = igmp_group_get (tmp);
343 : }
344 5 : if (block)
345 : {
346 2 : igmp_proxy_device_mfib_path_add_del (group, 0);
347 : }
348 :
349 : /* *INDENT-OFF* */
350 334 : FOR_EACH_SRC (src, group, group->router_filter_mode,
351 : ({
352 : igmp_proxy_device_merge_src (&proxy_group, src, srcaddrs, block);
353 : }));
354 : /* *INDENT-ON* */
355 5 : return proxy_group;
356 : }
357 :
358 : void
359 15 : igmp_proxy_device_merge_config (igmp_config_t * config, u8 block)
360 : {
361 : igmp_proxy_device_t *proxy_device;
362 : igmp_group_t *group;
363 : igmp_group_t *proxy_group;
364 15 : ip46_address_t *srcaddrs = NULL;
365 : igmp_pkt_build_report_t br;
366 :
367 15 : proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
368 15 : if (!proxy_device)
369 10 : return;
370 :
371 5 : igmp_pkt_build_report_init (&br, proxy_device->upstream_if);
372 :
373 : /* *INDENT-OFF* */
374 330 : FOR_EACH_GROUP(group, config,
375 : ({
376 : proxy_group = igmp_proxy_device_merge_group (proxy_device, group, &srcaddrs, block);
377 :
378 : if ((vec_len(srcaddrs) > 0) && proxy_group)
379 : {
380 : igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
381 : block ? IGMP_MEMBERSHIP_GROUP_block_old_sources :
382 : IGMP_MEMBERSHIP_GROUP_allow_new_sources);
383 : }
384 : vec_free (srcaddrs);
385 : }));
386 : /* *INDENT-ON* */
387 :
388 5 : igmp_pkt_report_v3_send (&br);
389 :
390 : }
391 :
392 : /*
393 : * fd.io coding-style-patch-verification: ON
394 : *
395 : * Local Variables:
396 : * eval: (c-set-style "gnu")
397 : * End:
398 : */
|