http: fix client send another request
[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   /*
29    * we validated this packet when we accepted it in the DP, so
30    * this number is safe to use
31    */
32   n = clib_net_to_host_u16 (r->n_src_addresses);
33
34   if (0 == n)
35     {
36       /* a (*,G) join has no source address specified */
37       vec_validate (srcs, 0);
38       srcs[0].ip4.as_u32 = 0;
39     }
40   else
41     {
42       vec_validate (srcs, n - 1);
43       s = r->src_addresses;
44
45       for (ii = 0; ii < n; ii++)
46         {
47           srcs[ii].ip4 = *s;
48           s++;
49         }
50     }
51
52   return (srcs);
53 }
54
55 static void
56 igmp_handle_group_exclude (igmp_config_t * config,
57                            const igmp_membership_group_v3_t * igmp_group)
58 {
59   ip46_address_t key = {
60     .ip4 = igmp_group->group_address,
61   };
62   u16 n;
63
64   /*
65    * treat an exclude all sources as a *,G join
66    */
67   n = clib_net_to_host_u16 (igmp_group->n_src_addresses);
68
69   if (0 == n)
70     {
71       ip46_address_t *src, *srcs;
72       igmp_group_t *group;
73
74       group = igmp_group_lookup (config, &key);
75       srcs = igmp_group_mk_source_list (igmp_group);
76
77       IGMP_DBG (" ..group-update: %U (*, %U)",
78                 format_vnet_sw_if_index_name,
79                 vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
80
81       if (NULL == group)
82         {
83           group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
84         }
85       vec_foreach (src, srcs)
86       {
87         igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
88       }
89
90       vec_free (srcs);
91     }
92   else
93     {
94       IGMP_DBG (" ..group-update: %U (*, %U) source exclude ignored",
95                 format_vnet_sw_if_index_name,
96                 vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
97     }
98 }
99
100 static void
101 igmp_handle_group_block (igmp_config_t * config,
102                          const igmp_membership_group_v3_t * igmp_group)
103 {
104   ip46_address_t *s, *srcs;
105   igmp_pkt_build_query_t bq;
106   igmp_group_t *group;
107   ip46_address_t key = {
108     .ip4 = igmp_group->group_address,
109   };
110
111   srcs = igmp_group_mk_source_list (igmp_group);
112   group = igmp_group_lookup (config, &key);
113
114   IGMP_DBG (" ..group-block: %U (%U, %U)",
115             format_vnet_sw_if_index_name,
116             vnet_get_main (), config->sw_if_index,
117             format_igmp_key, &key, format_igmp_src_addr_list, srcs);
118
119   if (group)
120     {
121       igmp_src_t *src;
122       /*
123        * send a group+source specific query
124        */
125       igmp_pkt_build_query_init (&bq, config->sw_if_index);
126       igmp_pkt_query_v3_add_group (&bq, group, srcs);
127       igmp_pkt_query_v3_send (&bq);
128
129       /*
130        * for each source left/blocked drop the source expire timer to the leave
131        * latency timer
132        */
133       vec_foreach (s, srcs)
134       {
135         src = igmp_src_lookup (group, s);
136         if (NULL != src)
137           igmp_src_blocked (src);
138       }
139     }
140   /*
141    * a block/leave from a group for which we have no state
142    */
143
144   vec_free (srcs);
145 }
146
147 static void
148 igmp_handle_group_update (igmp_config_t * config,
149                           const igmp_membership_group_v3_t * igmp_group)
150 {
151   ip46_address_t *src, *srcs;
152   igmp_group_t *group;
153   ip46_address_t key = {
154     .ip4 = igmp_group->group_address,
155   };
156
157   /*
158    * treat a TO_INC({}) as a (*,G) leave
159    */
160   if (0 == clib_net_to_host_u16 (igmp_group->n_src_addresses))
161     {
162       return (igmp_handle_group_block (config, igmp_group));
163     }
164
165   srcs = igmp_group_mk_source_list (igmp_group);
166   group = igmp_group_lookup (config, &key);
167
168   IGMP_DBG (" ..group-update: %U (%U, %U)",
169             format_vnet_sw_if_index_name,
170             vnet_get_main (), config->sw_if_index,
171             format_igmp_key, &key, format_igmp_src_addr_list, srcs);
172
173   if (NULL == group)
174     {
175       group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
176     }
177
178   /* create or update all sources */
179   vec_foreach (src, srcs)
180   {
181     igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
182   }
183
184   vec_free (srcs);
185 }
186
187 static void
188 igmp_handle_group (igmp_config_t * config,
189                    const igmp_membership_group_v3_t * igmp_group)
190 {
191   IGMP_DBG ("rx-group-report: %U",
192             format_vnet_sw_if_index_name,
193             vnet_get_main (), config->sw_if_index);
194
195   switch (igmp_group->type)
196     {
197     case IGMP_MEMBERSHIP_GROUP_mode_is_include:
198     case IGMP_MEMBERSHIP_GROUP_change_to_include:
199     case IGMP_MEMBERSHIP_GROUP_allow_new_sources:
200       igmp_handle_group_update (config, igmp_group);
201       break;
202     case IGMP_MEMBERSHIP_GROUP_block_old_sources:
203       igmp_handle_group_block (config, igmp_group);
204       break;
205     case IGMP_MEMBERSHIP_GROUP_mode_is_exclude:
206     case IGMP_MEMBERSHIP_GROUP_change_to_exclude:
207       igmp_handle_group_exclude (config, igmp_group);
208       break;
209       /*
210        * all other types ignored
211        */
212     }
213 }
214
215 void
216 igmp_handle_report (const igmp_report_args_t * args)
217 {
218   const igmp_membership_group_v3_t *igmp_group;
219   igmp_config_t *config;
220   u16 n_groups, ii;
221
222   config = igmp_config_lookup (args->sw_if_index);
223
224   if (!config)
225     /*
226      * no IGMP config on the interface. quit
227      */
228     return;
229
230   if (IGMP_MODE_HOST == config->mode)
231     {
232       /*
233        * Hosts need not listen to the reports of other hosts.
234        * we're done here
235        */
236       return;
237     }
238
239   /*
240    * we validated this packet when we accepted it in the DP, so
241    * this number is safe to use
242    */
243   n_groups = clib_net_to_host_u16 (args->report[0].n_groups);
244   igmp_group = args->report[0].groups;
245
246   for (ii = 0; ii < n_groups; ii++)
247     {
248       igmp_handle_group (config, igmp_group);
249
250       igmp_group = group_cptr (igmp_group,
251                                igmp_membership_group_v3_length (igmp_group));
252     }
253
254   igmp_proxy_device_merge_config (config, 0);
255 }
256
257 /*
258  * fd.io coding-style-patch-verification: ON
259  *
260  * Local Variables:
261  * eval: (c-set-style "gnu")
262  * End:
263  */