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