06bf26bbc17c2b30077ad19a0d384093bf99f5cf
[vpp.git] / src / plugins / igmp / igmp_api.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.h>
19
20 #include <vlibapi/api.h>
21 #include <vlibmemory/api.h>
22 #include <vnet/ip/ip_types_api.h>
23 #include <igmp/igmp_ssm_range.h>
24
25 /* define message IDs */
26 #include <igmp/igmp_msg_enum.h>
27
28 /* define message structures */
29 #define vl_typedefs
30 #include <igmp/igmp_all_api_h.h>
31 #undef vl_typedefs
32
33 /* define generated endian-swappers */
34 #define vl_endianfun
35 #include <igmp/igmp_all_api_h.h>
36 #undef vl_endianfun
37
38 /* instantiate all the print functions we know about */
39 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
40 #define vl_printfun
41 #include <igmp/igmp_all_api_h.h>
42 #undef vl_printfun
43
44 /* Get the API version number */
45 #define vl_api_version(n,v) static u32 api_version=(v);
46 #include <igmp/igmp_all_api_h.h>
47 #undef vl_api_version
48
49 #include <vlibapi/api_helper_macros.h>
50
51 #define IGMP_MSG_ID(_id) (_id + igmp_main.msg_id_base)
52
53 #define foreach_igmp_plugin_api_msg                      \
54 _(IGMP_LISTEN, igmp_listen)                              \
55 _(IGMP_ENABLE_DISABLE, igmp_enable_disable)              \
56 _(IGMP_DUMP, igmp_dump)                                  \
57 _(IGMP_CLEAR_INTERFACE, igmp_clear_interface)            \
58 _(IGMP_CLEAR_INTERFACE, igmp_clear_interface)            \
59 _(IGMP_GROUP_PREFIX_SET, igmp_group_prefix_set)          \
60 _(IGMP_GROUP_PREFIX_DUMP, igmp_group_prefix_dump)        \
61 _(WANT_IGMP_EVENTS, want_igmp_events)                    \
62
63 static void
64 vl_api_igmp_listen_t_handler (vl_api_igmp_listen_t * mp)
65 {
66   vlib_main_t *vm = vlib_get_main ();
67   vnet_main_t *vnm = vnet_get_main ();
68   vl_api_igmp_listen_reply_t *rmp;
69   int ii, rv = 0;
70   ip46_address_t gaddr, *saddrs = NULL;
71
72   VALIDATE_SW_IF_INDEX (&mp->group);
73
74   if ((vnet_sw_interface_get_flags (vnm, ntohl (mp->group.sw_if_index)) &&
75        VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
76     {
77       // FIXME - don't we clear this state on interface down ...
78       rv = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
79       goto done;
80     }
81
82   memset (&gaddr, 0, sizeof (gaddr));
83   clib_memcpy (&gaddr.ip4, &mp->group.gaddr, sizeof (ip4_address_t));
84
85   vec_validate (saddrs, mp->group.n_srcs - 1);
86
87   vec_foreach_index (ii, saddrs)
88   {
89     clib_memcpy (&saddrs[ii].ip4,
90                  &mp->group.saddrs[ii], sizeof (ip4_address_t));
91   }
92
93   rv = igmp_listen (vm,
94                     (mp->group.filter ?
95                      IGMP_FILTER_MODE_INCLUDE :
96                      IGMP_FILTER_MODE_EXCLUDE),
97                     ntohl (mp->group.sw_if_index), saddrs, &gaddr);
98
99   vec_free (saddrs);
100
101   BAD_SW_IF_INDEX_LABEL;
102 done:;
103   REPLY_MACRO (IGMP_MSG_ID (VL_API_IGMP_LISTEN_REPLY));
104 }
105
106 static void
107 vl_api_igmp_enable_disable_t_handler (vl_api_igmp_enable_disable_t * mp)
108 {
109   vl_api_igmp_enable_disable_reply_t *rmp;
110   int rv = 0;
111
112   VALIDATE_SW_IF_INDEX (mp);
113
114   rv = igmp_enable_disable (ntohl (mp->sw_if_index),
115                             mp->enable,
116                             (mp->mode ? IGMP_MODE_HOST : IGMP_MODE_ROUTER));
117
118   BAD_SW_IF_INDEX_LABEL;
119
120   REPLY_MACRO (IGMP_MSG_ID (VL_API_IGMP_ENABLE_DISABLE_REPLY));
121 }
122
123 static void
124 send_igmp_details (unix_shared_memory_queue_t * q, igmp_main_t * im,
125                    igmp_config_t * config, igmp_group_t * group,
126                    igmp_src_t * src, u32 context)
127 {
128   vl_api_igmp_details_t *mp;
129
130   mp = vl_msg_api_alloc (sizeof (*mp));
131   memset (mp, 0, sizeof (*mp));
132
133   mp->_vl_msg_id = htons (IGMP_MSG_ID (VL_API_IGMP_DETAILS));
134   mp->context = context;
135   mp->sw_if_index = htonl (config->sw_if_index);
136   clib_memcpy (mp->saddr.address, &src->key->ip4, sizeof (src->key->ip4));
137   clib_memcpy (mp->gaddr.address, &group->key->ip4, sizeof (group->key->ip4));
138
139   vl_msg_api_send_shmem (q, (u8 *) & mp);
140 }
141
142 static void
143 igmp_config_dump (igmp_main_t * im,
144                   unix_shared_memory_queue_t * q,
145                   u32 context, igmp_config_t * config)
146 {
147   igmp_group_t *group;
148   igmp_src_t *src;
149
150   /* *INDENT-OFF* */
151   FOR_EACH_GROUP (group, config,
152     ({
153       FOR_EACH_SRC (src, group, IGMP_FILTER_MODE_INCLUDE,
154         ({
155           send_igmp_details (q, im, config, group, src, context);
156         }));
157     }));
158   /* *INDENT-ON* */
159 }
160
161 static void
162 vl_api_igmp_dump_t_handler (vl_api_igmp_dump_t * mp)
163 {
164   unix_shared_memory_queue_t *q;
165   igmp_main_t *im = &igmp_main;
166   igmp_config_t *config;
167   u32 sw_if_index;
168
169   q = vl_api_client_index_to_input_queue (mp->client_index);
170   if (!q)
171     return;
172
173   sw_if_index = ntohl (mp->sw_if_index);
174   if (~0 == sw_if_index)
175     {
176       /* *INDENT-OFF* */
177       pool_foreach (config, im->configs,
178         ({
179           igmp_config_dump(im, q, mp->context, config);
180         }));
181       /* *INDENT-ON* */
182     }
183   else
184     {
185       config = igmp_config_lookup (sw_if_index);
186       if (config)
187         {
188           igmp_config_dump (im, q, mp->context, config);
189         }
190     }
191 }
192
193 static void
194 vl_api_igmp_clear_interface_t_handler (vl_api_igmp_clear_interface_t * mp)
195 {
196   vl_api_igmp_clear_interface_reply_t *rmp;
197   igmp_config_t *config;
198   int rv = 0;
199
200   config = igmp_config_lookup (ntohl (mp->sw_if_index));
201   if (config)
202     igmp_clear_config (config);
203
204   REPLY_MACRO (IGMP_MSG_ID (VL_API_IGMP_CLEAR_INTERFACE_REPLY));
205 }
206
207 static vl_api_group_prefix_type_t
208 igmp_group_type_int_to_api (igmp_group_prefix_type_t t)
209 {
210   switch (t)
211     {
212     case IGMP_GROUP_PREFIX_TYPE_ASM:
213       return (htonl (ASM));
214     case IGMP_GROUP_PREFIX_TYPE_SSM:
215       return (htonl (SSM));
216     }
217
218   return (SSM);
219 }
220
221 static igmp_group_prefix_type_t
222 igmp_group_type_api_to_int (vl_api_group_prefix_type_t t)
223 {
224   switch (htonl (t))
225     {
226     case ASM:
227       return (IGMP_GROUP_PREFIX_TYPE_ASM);
228     case SSM:
229       return (IGMP_GROUP_PREFIX_TYPE_SSM);
230     }
231
232   return (IGMP_GROUP_PREFIX_TYPE_SSM);
233 }
234
235 static void
236 vl_api_igmp_group_prefix_set_t_handler (vl_api_igmp_group_prefix_set_t * mp)
237 {
238   vl_api_igmp_group_prefix_set_reply_t *rmp;
239   fib_prefix_t pfx;
240   int rv = 0;
241
242   ip_prefix_decode (&mp->gp.prefix, &pfx);
243   igmp_group_prefix_set (&pfx, igmp_group_type_api_to_int (mp->gp.type));
244
245   REPLY_MACRO (IGMP_MSG_ID (VL_API_IGMP_GROUP_PREFIX_SET_REPLY));
246 }
247
248 typedef struct igmp_ssm_range_walk_ctx_t_
249 {
250   unix_shared_memory_queue_t *q;
251   u32 context;
252 } igmp_ssm_range_walk_ctx_t;
253
254 static walk_rc_t
255 igmp_ssm_range_walk_dump (const fib_prefix_t * pfx,
256                           igmp_group_prefix_type_t type, void *args)
257 {
258   igmp_ssm_range_walk_ctx_t *ctx = args;
259   vl_api_igmp_group_prefix_details_t *mp;
260
261   mp = vl_msg_api_alloc (sizeof (*mp));
262   memset (mp, 0, sizeof (*mp));
263
264   mp->_vl_msg_id = htons (IGMP_MSG_ID (VL_API_IGMP_DETAILS));
265   mp->context = ctx->context;
266   mp->gp.type = igmp_group_type_int_to_api (type);
267   ip_prefix_encode (pfx, &mp->gp.prefix);
268
269   vl_msg_api_send_shmem (ctx->q, (u8 *) & mp);
270
271   return (WALK_CONTINUE);
272 }
273
274 static void
275 vl_api_igmp_group_prefix_dump_t_handler (vl_api_igmp_dump_t * mp)
276 {
277   unix_shared_memory_queue_t *q;
278
279   q = vl_api_client_index_to_input_queue (mp->client_index);
280   if (!q)
281     return;
282
283   igmp_ssm_range_walk_ctx_t ctx = {
284     .q = q,
285     .context = mp->context,
286   };
287
288   igmp_ssm_range_walk (igmp_ssm_range_walk_dump, &ctx);
289 }
290
291 static vpe_client_registration_t *
292 igmp_api_client_lookup (igmp_main_t * im, u32 client_index)
293 {
294   uword *p;
295   vpe_client_registration_t *api_client = NULL;
296
297   p = hash_get (im->igmp_api_client_by_client_index, client_index);
298   if (p)
299     api_client = vec_elt_at_index (im->api_clients, p[0]);
300
301   return api_client;
302 }
303
304 static void
305 vl_api_want_igmp_events_t_handler (vl_api_want_igmp_events_t * mp)
306 {
307   igmp_main_t *im = &igmp_main;
308   vpe_client_registration_t *api_client;
309   vl_api_want_igmp_events_reply_t *rmp;
310   int rv = 0;
311
312   api_client = igmp_api_client_lookup (im, mp->client_index);
313   if (api_client)
314     {
315       if (mp->enable)
316         {
317           rv = VNET_API_ERROR_INVALID_REGISTRATION;
318           goto done;
319         }
320       hash_unset (im->igmp_api_client_by_client_index,
321                   api_client->client_index);
322       pool_put (im->api_clients, api_client);
323       goto done;
324     }
325   if (mp->enable)
326     {
327       pool_get (im->api_clients, api_client);
328       memset (api_client, 0, sizeof (vpe_client_registration_t));
329       api_client->client_index = mp->client_index;
330       api_client->client_pid = mp->pid;
331       hash_set (im->igmp_api_client_by_client_index,
332                 mp->client_index, api_client - im->api_clients);
333       goto done;
334     }
335   rv = VNET_API_ERROR_INVALID_REGISTRATION;
336
337 done:;
338   REPLY_MACRO (VL_API_WANT_IGMP_EVENTS_REPLY + im->msg_id_base);
339 }
340
341 static clib_error_t *
342 want_igmp_events_reaper (u32 client_index)
343 {
344   igmp_main_t *im = &igmp_main;
345   vpe_client_registration_t *api_client;
346   uword *p;
347
348   p = hash_get (im->igmp_api_client_by_client_index, client_index);
349
350   if (p)
351     {
352       api_client = pool_elt_at_index (im->api_clients, p[0]);
353       pool_put (im->api_clients, api_client);
354       hash_unset (im->igmp_api_client_by_client_index, client_index);
355     }
356   return (NULL);
357 }
358
359 VL_MSG_API_REAPER_FUNCTION (want_igmp_events_reaper);
360
361 void
362 send_igmp_event (unix_shared_memory_queue_t * q,
363                  u32 context,
364                  igmp_filter_mode_t filter,
365                  u32 sw_if_index,
366                  const ip46_address_t * saddr, const ip46_address_t * gaddr)
367 {
368   vl_api_igmp_event_t *mp = vl_msg_api_alloc (sizeof (*mp));
369   memset (mp, 0, sizeof (*mp));
370
371   mp->_vl_msg_id = ntohs ((VL_API_IGMP_EVENT) + igmp_main.msg_id_base);
372   mp->context = context;
373   mp->sw_if_index = htonl (sw_if_index);
374   mp->filter = htonl (filter);
375   clib_memcpy (&mp->saddr, &saddr->ip4, sizeof (ip4_address_t));
376   clib_memcpy (&mp->gaddr, &gaddr->ip4, sizeof (ip4_address_t));
377
378   vl_msg_api_send_shmem (q, (u8 *) & mp);
379 }
380
381 void
382 igmp_event (igmp_filter_mode_t filter,
383             u32 sw_if_index,
384             const ip46_address_t * saddr, const ip46_address_t * gaddr)
385 {
386   vpe_client_registration_t *api_client;
387   unix_shared_memory_queue_t *q;
388   igmp_main_t *im;
389
390   im = &igmp_main;
391
392   IGMP_DBG ("event: (%U, %U) %U %U",
393             format_ip46_address, saddr, IP46_TYPE_ANY,
394             format_ip46_address, saddr, IP46_TYPE_ANY,
395             format_vnet_sw_if_index_name,
396             vnet_get_main (), sw_if_index, format_igmp_filter_mode, filter);
397
398
399   /* *INDENT-OFF* */
400   pool_foreach (api_client, im->api_clients,
401     ({
402       q = vl_api_client_index_to_input_queue (api_client->client_index);
403       if (q)
404         send_igmp_event (q, 0, filter, sw_if_index, saddr, gaddr);
405     }));
406   /* *INDENT-ON* */
407 }
408
409 #define vl_msg_name_crc_list
410 #include <igmp/igmp_all_api_h.h>
411 #undef vl_msg_name_crc_list
412
413 static void
414 setup_message_id_table (igmp_main_t * im, api_main_t * am)
415 {
416 #define _(id,n,crc) \
417   vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + im->msg_id_base);
418   foreach_vl_msg_name_crc_igmp;
419 #undef _
420 }
421
422 /* Set up the API message handling tables */
423 static clib_error_t *
424 igmp_plugin_api_hookup (vlib_main_t * vm)
425 {
426   igmp_main_t *im = &igmp_main;
427   api_main_t *am = &api_main;
428   u8 *name;
429
430   /* Construct the API name */
431   name = format (0, "igmp_%08x%c", api_version, 0);
432
433   /* Ask for a correctly-sized block of API message decode slots */
434   im->msg_id_base = vl_msg_api_get_msg_ids
435     ((char *) name, VL_MSG_FIRST_AVAILABLE);
436
437 #define _(N,n)                                                  \
438     vl_msg_api_set_handlers((VL_API_##N + im->msg_id_base),     \
439                            #n,                                  \
440                            vl_api_##n##_t_handler,              \
441                            vl_noop_handler,                     \
442                            vl_api_##n##_t_endian,               \
443                            vl_api_##n##_t_print,                \
444                            sizeof(vl_api_##n##_t), 1);
445   foreach_igmp_plugin_api_msg;
446 #undef _
447
448   /*
449    * Set up the (msg_name, crc, message-id) table
450    */
451   setup_message_id_table (im, am);
452
453   vec_free (name);
454   return 0;
455 }
456
457 VLIB_API_INIT_FUNCTION (igmp_plugin_api_hookup);
458
459 /*
460  * fd.io coding-style-patch-verification: ON
461  *
462  * Local Variables:
463  * eval: (c-set-style "gnu")
464  * End:
465  */