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