hs-test: fixed timed out tests passing in the CI
[vpp.git] / src / plugins / igmp / igmp_pkt.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 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_pkt.h>
19 #include <vnet/fib/fib_sas.h>
20
21 static void
22 vlib_buffer_append (vlib_buffer_t * b, uword l)
23 {
24   b->current_data += l;
25   b->current_length += l;
26 }
27
28 static vlib_buffer_t *
29 igmp_pkt_get_buffer (igmp_pkt_build_t * bk)
30 {
31   vlib_main_t *vm;
32   vlib_buffer_t *b;
33   u32 bi;
34
35   vm = vlib_get_main ();
36
37   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
38     return (NULL);
39
40   b = vlib_get_buffer (vm, bi);
41
42   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
43   b->flags |= VLIB_BUFFER_IS_TRACED;
44
45   /* clear out stale data */
46   vnet_buffer (b)->sw_if_index[VLIB_RX] = ~0;
47
48   /*
49    * save progress in the builder
50    */
51   vec_add1 (bk->buffers, bi);
52   bk->n_avail = vnet_sw_interface_get_mtu (vnet_get_main (),
53                                            bk->sw_if_index, VNET_MTU_IP4);
54
55   return (b);
56 }
57
58 static vlib_buffer_t *
59 igmp_pkt_build_ip_header (igmp_pkt_build_t * bk,
60                           igmp_msg_type_t msg_type,
61                           const igmp_group_t * group)
62 {
63   ip4_header_t *ip4;
64   vlib_buffer_t *b;
65   u8 *option;
66
67   b = igmp_pkt_get_buffer (bk);
68
69   if (NULL == b)
70     return (NULL);
71
72   ip4 = vlib_buffer_get_current (b);
73   clib_memset (ip4, 0, sizeof (ip4_header_t));
74   ip4->ip_version_and_header_length = 0x46;
75   ip4->ttl = 1;
76   ip4->protocol = IP_PROTOCOL_IGMP;
77   ip4->tos = 0xc0;
78
79   fib_sas4_get (bk->sw_if_index, NULL, &ip4->src_address);
80
81   vlib_buffer_append (b, sizeof (*ip4));
82   bk->n_avail -= sizeof (*ip4);
83
84   switch (msg_type)
85     {
86     case IGMP_MSG_REPORT:
87       ip4->dst_address.as_u32 = IGMP_MEMBERSHIP_REPORT_ADDRESS;
88       break;
89     case IGMP_MSG_QUERY:
90       if (group != NULL)
91         clib_memcpy_fast (&ip4->dst_address, &group->key->ip4,
92                           sizeof (ip4_address_t));
93       else
94         ip4->dst_address.as_u32 = IGMP_GENERAL_QUERY_ADDRESS;
95       break;
96     }
97
98   /* add the router alert options */
99   option = vlib_buffer_get_current (b);
100   option[0] = 0x80 | 20;        // IP4_ROUTER_ALERT_OPTION;
101   option[1] = 4;                // length
102   option[2] = option[3] = 0;
103
104   vlib_buffer_append (b, 4);
105   bk->n_avail -= 4;
106
107   return (b);
108 }
109
110 static vlib_buffer_t *
111 igmp_pkt_build_report_v3 (igmp_pkt_build_report_t * br,
112                           const igmp_group_t * group)
113 {
114   igmp_membership_report_v3_t *report;
115   vlib_buffer_t *b;
116
117   b = igmp_pkt_build_ip_header (&br->base, IGMP_MSG_REPORT, group);
118
119   if (NULL == b)
120     return (NULL);
121
122   report = vlib_buffer_get_current (b);
123   report->header.type = IGMP_TYPE_membership_report_v3;
124   report->header.code = 0;
125   report->header.checksum = 0;
126   report->unused = 0;
127
128   vlib_buffer_append (b, sizeof (igmp_membership_report_v3_t));
129   br->base.n_avail -= sizeof (igmp_membership_report_v3_t);
130   br->base.n_bytes += sizeof (igmp_membership_report_v3_t);
131
132   return (b);
133 }
134
135 static void
136 igmp_pkt_tx (igmp_pkt_build_t * bk)
137 {
138   const igmp_config_t *config;
139   vlib_buffer_t *b;
140   vlib_main_t *vm;
141   vlib_frame_t *f;
142   u32 *to_next;
143   u32 ii;
144
145   vm = vlib_get_main ();
146   config = igmp_config_lookup (bk->sw_if_index);
147
148   if (NULL == config)
149     return;
150
151   f = vlib_get_frame_to_node (vm, ip4_rewrite_mcast_node.index);
152   to_next = vlib_frame_vector_args (f);
153
154   vec_foreach_index (ii, bk->buffers)
155   {
156     b = vlib_get_buffer (vm, bk->buffers[ii]);
157     vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
158     to_next[ii] = bk->buffers[ii];
159     f->n_vectors++;
160   }
161
162   vlib_put_frame_to_node (vm, ip4_rewrite_mcast_node.index, f);
163
164   IGMP_DBG ("  ..tx: %U", format_vnet_sw_if_index_name,
165             vnet_get_main (), bk->sw_if_index);
166
167   vec_free (bk->buffers);
168   bk->buffers = 0;
169 }
170
171 static vlib_buffer_t *
172 igmp_pkt_build_report_get_active (igmp_pkt_build_report_t * br)
173 {
174   if (NULL == br->base.buffers)
175     return (NULL);
176
177   return (vlib_get_buffer (vlib_get_main (),
178                            br->base.buffers[vec_len (br->base.buffers) - 1]));
179 }
180
181 static void
182 igmp_pkt_build_report_bake (igmp_pkt_build_report_t * br)
183 {
184   igmp_membership_report_v3_t *igmp;
185   ip4_header_t *ip4;
186   vlib_buffer_t *b;
187
188   b = igmp_pkt_build_report_get_active (br);
189
190   b->current_data = 0;
191
192   ip4 = vlib_buffer_get_current (b);
193   igmp = (igmp_membership_report_v3_t *) (((u32 *) ip4) + 6);
194
195   igmp->n_groups = clib_host_to_net_u16 (br->n_groups);
196
197   igmp->header.checksum =
198     ~ip_csum_fold (ip_incremental_checksum (0, igmp, br->base.n_bytes));
199
200   ip4->length = clib_host_to_net_u16 (b->current_length);
201   ip4->checksum = ip4_header_checksum (ip4);
202
203   br->base.n_bytes = br->base.n_avail = br->n_groups = 0;
204 }
205
206 void
207 igmp_pkt_report_v3_send (igmp_pkt_build_report_t * br)
208 {
209   if (NULL == br->base.buffers)
210     return;
211
212   igmp_pkt_build_report_bake (br);
213   igmp_pkt_tx (&br->base);
214 }
215
216 static u32
217 igmp_pkt_report_v3_get_size (const igmp_group_t * group)
218 {
219   ASSERT (IGMP_FILTER_MODE_INCLUDE == group->router_filter_mode);
220
221   return ((hash_elts (group->igmp_src_by_key[IGMP_FILTER_MODE_INCLUDE]) *
222            sizeof (ip4_address_t)) + sizeof (igmp_membership_group_v3_t));
223 }
224
225 static igmp_membership_group_v3_t *
226 igmp_pkt_report_v3_append_group (igmp_pkt_build_report_t * br,
227                                  const ip46_address_t * grp,
228                                  igmp_membership_group_v3_type_t type)
229 {
230   igmp_membership_group_v3_t *igmp_group;
231   vlib_buffer_t *b;
232
233   b = igmp_pkt_build_report_get_active (br);
234
235   if (br->base.n_avail < sizeof (igmp_membership_group_v3_t))
236     {
237       igmp_pkt_build_report_bake (br);
238       b = igmp_pkt_build_report_v3 (br, NULL);
239       if (NULL == b)
240         return (NULL);
241     }
242   br->base.n_avail -= sizeof (igmp_membership_group_v3_t);
243   br->base.n_bytes += sizeof (igmp_membership_group_v3_t);
244   br->n_groups++;
245   br->n_srcs = 0;
246
247   igmp_group = vlib_buffer_get_current (b);
248   vlib_buffer_append (b, sizeof (igmp_membership_group_v3_t));
249
250   igmp_group->type = type;
251   igmp_group->n_aux_u32s = 0;
252   igmp_group->n_src_addresses = 0;
253   igmp_group->group_address.as_u32 = grp->ip4.as_u32;
254
255   return (igmp_group);
256 }
257
258 /**
259  * 4.2.16
260  "   If the set of Group Records required in a Report does not fit within
261  *   the size limit of a single Report message (as determined by the MTU
262  *   of the network on which it will be sent), the Group Records are sent
263  *   in as many Report messages as needed to report the entire set.
264
265  *   If a single Group Record contains so many source addresses that it
266  *   does not fit within the size limit of a single Report message, if its
267  *   Type is not MODE_IS_EXCLUDE or CHANGE_TO_EXCLUDE_MODE, it is split
268  *   into multiple Group Records, each containing a different subset of
269  *   the source addresses and each sent in a separate Report message.  If
270  *   its Type is MODE_IS_EXCLUDE or CHANGE_TO_EXCLUDE_MODE, a single Group
271  *   Record is sent, containing as many source addresses as can fit, and
272  *  the remaining source addresses are not reported; though the choice of
273  *   which sources to report is arbitrary, it is preferable to report the
274  *  same set of sources in each subsequent report, rather than reporting
275  *  different sources each time."
276   */
277 static igmp_membership_group_v3_t *
278 igmp_pkt_report_v3_append_src (igmp_pkt_build_report_t * br,
279                                igmp_membership_group_v3_t * igmp_group,
280                                const ip46_address_t * grp,
281                                igmp_membership_group_v3_type_t type,
282                                const ip46_address_t * src)
283 {
284   vlib_buffer_t *b;
285
286   b = igmp_pkt_build_report_get_active (br);
287
288   if (br->base.n_avail < sizeof (ip4_address_t))
289     {
290       igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
291       igmp_pkt_build_report_bake (br);
292       b = igmp_pkt_build_report_v3 (br, NULL);
293       if (NULL == b)
294         return (NULL);
295       igmp_group = igmp_pkt_report_v3_append_group (br, grp, type);
296     }
297
298   igmp_group->src_addresses[br->n_srcs].as_u32 = src->ip4.as_u32;
299   br->n_srcs++;
300   br->base.n_avail -= sizeof (ip4_address_t);
301   br->base.n_bytes += sizeof (ip4_address_t);
302   vlib_buffer_append (b, sizeof (ip4_address_t));
303
304   return (igmp_group);
305 }
306
307 void
308 igmp_pkt_report_v3_add_report (igmp_pkt_build_report_t * br,
309                                const ip46_address_t * grp,
310                                const ip46_address_t * srcs,
311                                igmp_membership_group_v3_type_t type)
312 {
313   igmp_membership_group_v3_t *igmp_group;
314   const ip46_address_t *s;
315   vlib_buffer_t *b;
316
317   b = igmp_pkt_build_report_get_active (br);
318
319   if (NULL == b)
320     {
321       b = igmp_pkt_build_report_v3 (br, NULL);
322       if (NULL == b)
323         /* failed to allocate buffer */
324         return;
325     }
326
327   igmp_group = igmp_pkt_report_v3_append_group (br, grp, type);
328
329   if (NULL == igmp_group)
330     return;
331
332   vec_foreach(s, srcs)
333     {
334       igmp_group = igmp_pkt_report_v3_append_src(br, igmp_group,
335                                                  grp, type, s);
336       if (NULL == igmp_group)
337         return;
338     };
339
340   igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
341
342   IGMP_DBG ("  ..add-group: %U", format_ip46_address, grp, IP46_TYPE_IP4);
343 }
344
345 void
346 igmp_pkt_report_v3_add_group (igmp_pkt_build_report_t * br,
347                               const igmp_group_t * group,
348                               igmp_membership_group_v3_type_t type)
349 {
350   igmp_membership_group_v3_t *igmp_group;
351   vlib_buffer_t *b;
352   igmp_src_t *src;
353
354   b = igmp_pkt_build_report_get_active (br);
355
356   if (NULL == b)
357     {
358       b = igmp_pkt_build_report_v3 (br, NULL);
359       if (NULL == b)
360         /* failed to allocate buffer */
361         return;
362     }
363
364   /*
365    * if the group won't fit in a partially full buffer, start again
366    */
367   if ((0 != br->n_groups) &&
368       (igmp_pkt_report_v3_get_size (group) > br->base.n_avail))
369     {
370       igmp_pkt_build_report_bake (br);
371       b = igmp_pkt_build_report_v3 (br, NULL);
372       if (NULL == b)
373         /* failed to allocate buffer */
374         return;
375     }
376
377   igmp_group = igmp_pkt_report_v3_append_group (br, group->key, type);
378
379   FOR_EACH_SRC (src, group, IGMP_FILTER_MODE_INCLUDE,
380     ({
381       igmp_group = igmp_pkt_report_v3_append_src(br, igmp_group,
382                                                  group->key, type,
383                                                  src->key);
384       if (NULL == igmp_group)
385         return;
386     }));
387   igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
388
389   IGMP_DBG ("  ..add-group: %U srcs:%d",
390             format_igmp_key, group->key,
391             hash_elts (group->igmp_src_by_key[IGMP_FILTER_MODE_INCLUDE]));
392 }
393
394 void
395 igmp_pkt_build_report_init (igmp_pkt_build_report_t * br, u32 sw_if_index)
396 {
397   clib_memset (br, 0, sizeof (*br));
398   br->base.sw_if_index = sw_if_index;
399 }
400
401 static vlib_buffer_t *
402 igmp_pkt_build_query_get_active (igmp_pkt_build_query_t * bq)
403 {
404   if (NULL == bq->base.buffers)
405     return (NULL);
406
407   return (vlib_get_buffer (vlib_get_main (),
408                            bq->base.buffers[vec_len (bq->base.buffers) - 1]));
409 }
410
411 static vlib_buffer_t *
412 igmp_pkt_build_query_v3 (igmp_pkt_build_query_t * bq,
413                          const igmp_group_t * group)
414 {
415   igmp_membership_query_v3_t *query;
416   vlib_buffer_t *b;
417
418   b = igmp_pkt_build_ip_header (&bq->base, IGMP_MSG_QUERY, group);
419
420   if (NULL == b)
421     return (NULL);
422
423   query = vlib_buffer_get_current (b);
424   query->header.type = IGMP_TYPE_membership_query;
425   query->header.code = 0;
426   query->header.checksum = 0;
427   query->qqi_code = 0;
428   query->resv_s_qrv = 0;
429
430   if (NULL != group)
431     query->group_address.as_u32 = group->key->ip4.as_u32;
432   else
433     query->group_address.as_u32 = 0;
434
435   vlib_buffer_append (b, sizeof (igmp_membership_query_v3_t));
436   bq->base.n_avail -= sizeof (igmp_membership_query_v3_t);
437   bq->base.n_bytes += sizeof (igmp_membership_query_v3_t);
438
439   return (b);
440 }
441
442 void
443 igmp_pkt_query_v3_add_group (igmp_pkt_build_query_t * bq,
444                              const igmp_group_t * group,
445                              const ip46_address_t * srcs)
446 {
447   vlib_buffer_t *b;
448
449   b = igmp_pkt_build_query_get_active (bq);
450
451   if (NULL == b)
452     {
453       b = igmp_pkt_build_query_v3 (bq, group);
454       if (NULL == b)
455         /* failed to allocate buffer */
456         return;
457     }
458
459   if (NULL != srcs)
460     {
461       igmp_membership_query_v3_t *query;
462       const ip46_address_t *src;
463
464       query = vlib_buffer_get_current (b);
465
466       vec_foreach (src, srcs)
467       {
468         query->src_addresses[bq->n_srcs++].as_u32 = src->ip4.as_u32;
469
470         vlib_buffer_append (b, sizeof (ip4_address_t));
471         bq->base.n_bytes += sizeof (ip4_address_t);
472         bq->base.n_avail += sizeof (ip4_address_t);
473       }
474     }
475   /*
476    * else
477    *   general query and we're done
478    */
479 }
480
481 static void
482 igmp_pkt_build_query_bake (igmp_pkt_build_query_t * bq)
483 {
484   igmp_membership_query_v3_t *igmp;
485   ip4_header_t *ip4;
486   vlib_buffer_t *b;
487
488   b = igmp_pkt_build_query_get_active (bq);
489
490   b->current_data = 0;
491
492   ip4 = vlib_buffer_get_current (b);
493   // account for options
494   igmp = (igmp_membership_query_v3_t *) (((u32 *) ip4) + 6);
495
496   igmp->n_src_addresses = clib_host_to_net_u16 (bq->n_srcs);
497
498   igmp->header.checksum =
499     ~ip_csum_fold (ip_incremental_checksum (0, igmp, bq->base.n_bytes));
500
501   ip4->length = clib_host_to_net_u16 (b->current_length);
502   ip4->checksum = ip4_header_checksum (ip4);
503
504   bq->base.n_bytes = bq->base.n_avail = bq->n_srcs = 0;
505 }
506
507 void
508 igmp_pkt_query_v3_send (igmp_pkt_build_query_t * bq)
509 {
510   if (NULL == bq->base.buffers)
511     return;
512
513   igmp_pkt_build_query_bake (bq);
514   igmp_pkt_tx (&bq->base);
515 }
516
517 void
518 igmp_pkt_build_query_init (igmp_pkt_build_query_t * bq, u32 sw_if_index)
519 {
520   clib_memset (bq, 0, sizeof (*bq));
521   bq->base.sw_if_index = sw_if_index;
522 }
523
524 /*
525  * fd.io coding-style-patch-verification: ON
526  *
527  * Local Variables:
528  * eval: (c-set-style "gnu")
529  * End:
530  */