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