IGMP improvements
[vpp.git] / src / plugins / igmp / igmp_report.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 <igmp/igmp_report.h>
19 #include <igmp/igmp_pkt.h>
20
21 static ip46_address_t *
22 igmp_group_mk_source_list (const igmp_membership_group_v3_t * r)
23 {
24   ip46_address_t *srcs = NULL;
25   const ip4_address_t *s;
26   u16 ii, n;
27
28   n = clib_net_to_host_u16 (r->n_src_addresses);
29
30   if (0 == n)
31     return (NULL);
32
33   vec_validate (srcs, n - 1);
34   s = r->src_addresses;
35
36   for (ii = 0; ii < n; ii++)
37     {
38       srcs[ii].ip4 = *s;
39       s++;
40     }
41
42   return (srcs);
43 }
44
45 static void
46 igmp_handle_group_update (igmp_config_t * config,
47                           const igmp_membership_group_v3_t * igmp_group)
48 {
49   ip46_address_t *src, *srcs;
50   igmp_group_t *group;
51   ip46_address_t key = {
52     .ip4 = igmp_group->group_address,
53   };
54
55   srcs = igmp_group_mk_source_list (igmp_group);
56   group = igmp_group_lookup (config, &key);
57
58   IGMP_DBG (" ..group-update: %U (%U, %U)",
59             format_vnet_sw_if_index_name,
60             vnet_get_main (), config->sw_if_index,
61             format_igmp_key, &key, format_igmp_src_addr_list, srcs);
62
63   if (NULL == group)
64     {
65       group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
66     }
67
68   /* create or update all sources */
69   vec_foreach (src, srcs)
70   {
71     igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
72   }
73
74   vec_free (srcs);
75 }
76
77 static void
78 igmp_handle_group_block (igmp_config_t * config,
79                          const igmp_membership_group_v3_t * igmp_group)
80 {
81   ip46_address_t *s, *srcs;
82   igmp_pkt_build_query_t bq;
83   igmp_group_t *group;
84   ip46_address_t key = {
85     .ip4 = igmp_group->group_address,
86   };
87
88   srcs = igmp_group_mk_source_list (igmp_group);
89   group = igmp_group_lookup (config, &key);
90
91   IGMP_DBG (" ..group-block: %U (%U, %U)",
92             format_vnet_sw_if_index_name,
93             vnet_get_main (), config->sw_if_index,
94             format_igmp_key, &key, format_igmp_src_addr_list, srcs);
95
96   if (group)
97     {
98       igmp_src_t *src;
99       /*
100        * sned a group+source specific query
101        */
102       igmp_pkt_build_query_init (&bq, config->sw_if_index);
103       igmp_pkt_query_v3_add_group (&bq, group, srcs);
104       igmp_pkt_query_v3_send (&bq);
105
106       /*
107        * for each source left/blocked drop the source expire timer to the leave
108        * latency timer
109        */
110       vec_foreach (s, srcs)
111       {
112         src = igmp_src_lookup (group, s);
113         if (NULL != src)
114           igmp_src_blocked (src);
115       }
116     }
117   /*
118    * a block/leave from a group for which we have no state
119    */
120
121   vec_free (srcs);
122 }
123
124 static void
125 igmp_handle_group (igmp_config_t * config,
126                    const igmp_membership_group_v3_t * igmp_group)
127 {
128   IGMP_DBG ("rx-group-report: %U",
129             format_vnet_sw_if_index_name,
130             vnet_get_main (), config->sw_if_index);
131
132   switch (igmp_group->type)
133     {
134     case IGMP_MEMBERSHIP_GROUP_mode_is_include:
135     case IGMP_MEMBERSHIP_GROUP_change_to_include:
136     case IGMP_MEMBERSHIP_GROUP_allow_new_sources:
137       igmp_handle_group_update (config, igmp_group);
138       break;
139     case IGMP_MEMBERSHIP_GROUP_block_old_sources:
140       igmp_handle_group_block (config, igmp_group);
141       break;
142     case IGMP_MEMBERSHIP_GROUP_mode_is_exclude:
143     case IGMP_MEMBERSHIP_GROUP_change_to_exclude:
144       break;
145       /*
146        * all other types ignored
147        */
148     }
149 }
150
151 void
152 igmp_handle_report (const igmp_report_args_t * args)
153 {
154   const igmp_membership_group_v3_t *igmp_group;
155   igmp_config_t *config;
156   u16 n_groups, ii;
157
158   config = igmp_config_lookup (args->sw_if_index);
159
160   if (!config)
161     /*
162      * no IGMP config on the interface. quit
163      */
164     return;
165
166   if (IGMP_MODE_HOST == config->mode)
167     {
168       /*
169        * Hosts need not listen to the reports of other hosts.
170        * we're done here
171        */
172       return;
173     }
174
175   n_groups = clib_net_to_host_u16 (args->report[0].n_groups);
176   igmp_group = args->report[0].groups;
177
178   for (ii = 0; ii < n_groups; ii++)
179     {
180       igmp_handle_group (config, igmp_group);
181
182       igmp_group = group_cptr (igmp_group,
183                                igmp_membership_group_v3_length (igmp_group));
184     }
185 }
186
187 /*
188  * fd.io coding-style-patch-verification: ON
189  *
190  * Local Variables:
191  * eval: (c-set-style "gnu")
192  * End:
193  */