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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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 *------------------------------------------------------------------
18 #include <vlib/vlib.h>
19 #include <vnet/mfib/mfib_entry.h>
20 #include <vnet/mfib/mfib_table.h>
22 #include <igmp/igmp_proxy.h>
23 #include <igmp/igmp.h>
24 #include <igmp/igmp_pkt.h>
27 igmp_proxy_device_mfib_path_add_del (igmp_group_t * group, u8 add)
29 igmp_config_t *config;
32 config = igmp_config_get (group->config);
34 mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
38 mfib_prefix_t mpfx_group_addr = {
39 .fp_proto = FIB_PROTOCOL_IP4,
42 .ip4 = (*group->key).ip4,
45 fib_route_path_t via_itf_path =
47 .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
48 .frp_addr = zero_addr,
49 .frp_sw_if_index = config->sw_if_index,
52 .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
57 mfib_table_entry_path_update (mfib_index, &mpfx_group_addr,
58 MFIB_SOURCE_IGMP, MFIB_ENTRY_FLAG_NONE,
61 mfib_table_entry_path_remove (mfib_index, &mpfx_group_addr,
62 MFIB_SOURCE_IGMP, &via_itf_path);
66 igmp_proxy_device_lookup (u32 vrf_id)
68 igmp_main_t *im = &igmp_main;
70 if (vec_len (im->igmp_proxy_device_by_vrf_id) > vrf_id)
73 index = im->igmp_proxy_device_by_vrf_id[vrf_id];
75 return (vec_elt_at_index (im->proxy_devices, index));
81 igmp_proxy_device_add_del (u32 vrf_id, u32 sw_if_index, u8 add)
83 igmp_main_t *im = &igmp_main;
84 igmp_proxy_device_t *proxy_device;
85 igmp_config_t *config;
90 mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
92 return VNET_API_ERROR_INVALID_INTERFACE;
93 if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
94 return VNET_API_ERROR_INVALID_INTERFACE;
96 /* check IGMP configuration */
97 config = igmp_config_lookup (sw_if_index);
99 return VNET_API_ERROR_INVALID_INTERFACE;
100 if (config->mode != IGMP_MODE_HOST)
101 return VNET_API_ERROR_INVALID_INTERFACE;
103 proxy_device = igmp_proxy_device_lookup (vrf_id);
104 if (!proxy_device && add)
106 vec_validate_init_empty (im->igmp_proxy_device_by_vrf_id, vrf_id, ~0);
107 pool_get (im->proxy_devices, proxy_device);
108 im->igmp_proxy_device_by_vrf_id[vrf_id] =
109 proxy_device - im->proxy_devices;
110 clib_memset (proxy_device, 0, sizeof (igmp_proxy_device_t));
111 proxy_device->vrf_id = vrf_id;
112 proxy_device->upstream_if = sw_if_index;
113 config->proxy_device_id = vrf_id;
114 /* lock mfib table */
115 mfib_table_lock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
117 else if (proxy_device && !add)
119 while (vec_len (proxy_device->downstream_ifs) > 0)
121 igmp_proxy_device_add_del_interface (vrf_id,
122 proxy_device->downstream_ifs
125 vec_free (proxy_device->downstream_ifs);
126 proxy_device->downstream_ifs = NULL;
127 im->igmp_proxy_device_by_vrf_id[vrf_id] = ~0;
128 pool_put (im->proxy_devices, proxy_device);
129 config->proxy_device_id = ~0;
130 /* clear proxy database */
131 igmp_clear_config (config);
132 /* unlock mfib table */
133 mfib_table_unlock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
142 igmp_proxy_device_add_del_interface (u32 vrf_id, u32 sw_if_index, u8 add)
144 igmp_proxy_device_t *proxy_device;
148 proxy_device = igmp_proxy_device_lookup (vrf_id);
154 mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
155 if (mfib_index == ~0)
156 return VNET_API_ERROR_INVALID_INTERFACE;
157 if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
158 return VNET_API_ERROR_INVALID_INTERFACE;
160 /* check IGMP configuration */
161 igmp_config_t *config;
162 config = igmp_config_lookup (sw_if_index);
164 return VNET_API_ERROR_INVALID_INTERFACE;
165 if (config->mode != IGMP_MODE_ROUTER)
166 return VNET_API_ERROR_INVALID_INTERFACE;
170 if (proxy_device->downstream_ifs)
172 index = vec_search (proxy_device->downstream_ifs, sw_if_index);
176 vec_add1 (proxy_device->downstream_ifs, sw_if_index);
177 config->proxy_device_id = vrf_id;
181 if (!proxy_device->downstream_ifs)
183 index = vec_search (proxy_device->downstream_ifs, sw_if_index);
186 /* remove (S,G)s belonging to this interface from proxy database */
187 igmp_proxy_device_merge_config (config, /* block */ 1);
188 vec_del1 (proxy_device->downstream_ifs, index);
189 config->proxy_device_id = ~0;
196 igmp_proxy_device_block_src (igmp_config_t * config, igmp_group_t * group,
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;
205 proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
209 proxy_config = igmp_config_lookup (proxy_device->upstream_if);
210 ASSERT (proxy_config);
212 proxy_group = igmp_group_lookup (proxy_config, group->key);
213 if (proxy_group == NULL)
216 proxy_src = igmp_src_lookup (proxy_group, src->key);
217 if (proxy_src == NULL)
220 if (vec_len (proxy_src->referance_by_config_index) <= group->config)
222 IGMP_DBG ("proxy block src: invalid config %u", group->config);
225 proxy_src->referance_by_config_index[group->config] = 0;
226 vec_foreach (ref, proxy_src->referance_by_config_index)
232 /* build "Block Old Sources" report */
233 igmp_pkt_build_report_t br;
234 ip46_address_t *srcaddrs = NULL;
236 igmp_pkt_build_report_init (&br, proxy_config->sw_if_index);
237 vec_add1 (srcaddrs, *proxy_src->key);
238 igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
239 IGMP_MEMBERSHIP_GROUP_block_old_sources);
240 igmp_pkt_report_v3_send (&br);
243 igmp_group_src_remove (proxy_group, proxy_src);
244 igmp_src_free (proxy_src);
246 if (igmp_group_n_srcs (proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
248 igmp_proxy_device_mfib_path_add_del (proxy_group, 0);
249 igmp_proxy_device_mfib_path_add_del (group, 0);
250 igmp_group_clear (&proxy_group);
255 igmp_proxy_device_merge_src (igmp_group_t ** proxy_group, igmp_src_t * src,
256 ip46_address_t ** srcaddrs, u8 block)
258 igmp_src_t *proxy_src;
261 proxy_src = igmp_src_lookup (*proxy_group, src->key);
263 if (proxy_src == NULL)
267 /* store downstream config index */
268 d_config = igmp_group_get (src->group)->config;
271 igmp_src_alloc (igmp_group_index (*proxy_group), src->key,
274 hash_set_mem ((*proxy_group)->igmp_src_by_key
275 [(*proxy_group)->router_filter_mode], proxy_src->key,
276 igmp_src_index (proxy_src));
278 vec_validate_init_empty (proxy_src->referance_by_config_index, d_config,
280 proxy_src->referance_by_config_index[d_config] = 1;
281 vec_add1 (*srcaddrs, *proxy_src->key);
287 d_config = igmp_group_get (src->group)->config;
288 if (vec_len (proxy_src->referance_by_config_index) <= d_config)
290 IGMP_DBG ("proxy block src: invalid config %u", d_config);
293 proxy_src->referance_by_config_index[d_config] = 0;
295 vec_foreach (ref, proxy_src->referance_by_config_index)
301 vec_add1 (*srcaddrs, *proxy_src->key);
303 igmp_group_src_remove (*proxy_group, proxy_src);
304 igmp_src_free (proxy_src);
306 if (igmp_group_n_srcs (*proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
308 igmp_proxy_device_mfib_path_add_del (*proxy_group, 0);
309 igmp_group_clear (proxy_group);
313 d_config = igmp_group_get (src->group)->config;
314 vec_validate (proxy_src->referance_by_config_index, d_config);
315 proxy_src->referance_by_config_index[d_config] = 1;
320 always_inline igmp_group_t *
321 igmp_proxy_device_merge_group (igmp_proxy_device_t * proxy_device,
322 igmp_group_t * group,
323 ip46_address_t ** srcaddrs, u8 block)
325 igmp_config_t *proxy_config;
326 igmp_group_t *proxy_group;
329 proxy_config = igmp_config_lookup (proxy_device->upstream_if);
330 ALWAYS_ASSERT (proxy_config);
332 proxy_group = igmp_group_lookup (proxy_config, group->key);
337 u32 tmp = igmp_group_index (group);
339 igmp_group_alloc (proxy_config, group->key,
340 group->router_filter_mode);
341 igmp_proxy_device_mfib_path_add_del (proxy_group, 1);
342 group = igmp_group_get (tmp);
346 igmp_proxy_device_mfib_path_add_del (group, 0);
350 FOR_EACH_SRC (src, group, group->router_filter_mode,
352 igmp_proxy_device_merge_src (&proxy_group, src, srcaddrs, block);
359 igmp_proxy_device_merge_config (igmp_config_t * config, u8 block)
361 igmp_proxy_device_t *proxy_device;
363 igmp_group_t *proxy_group;
364 ip46_address_t *srcaddrs = NULL;
365 igmp_pkt_build_report_t br;
367 proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
371 igmp_pkt_build_report_init (&br, proxy_device->upstream_if);
374 FOR_EACH_GROUP(group, config,
376 proxy_group = igmp_proxy_device_merge_group (proxy_device, group, &srcaddrs, block);
378 if ((vec_len(srcaddrs) > 0) && proxy_group)
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);
388 igmp_pkt_report_v3_send (&br);
393 * fd.io coding-style-patch-verification: ON
396 * eval: (c-set-style "gnu")