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