/* *------------------------------------------------------------------ * Copyright (c) 2017 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------ */ #include <igmp/igmp.h> #include <vlibapi/api.h> #include <vlibmemory/api.h> #include <vnet/ip/ip_types_api.h> #include <igmp/igmp_ssm_range.h> /* define message IDs */ #include <igmp/igmp.api_enum.h> #include <igmp/igmp.api_types.h> #include <vnet/format_fns.h> #include <vlibapi/api_helper_macros.h> #define IGMP_MSG_ID(_id) (_id + igmp_main.msg_id_base) static void vl_api_igmp_listen_t_handler (vl_api_igmp_listen_t * mp) { vlib_main_t *vm = vlib_get_main (); vnet_main_t *vnm = vnet_get_main (); vl_api_igmp_listen_reply_t *rmp; int ii, rv = 0; ip46_address_t gaddr, *saddrs = NULL; VALIDATE_SW_IF_INDEX (&mp->group); if ((vnet_sw_interface_get_flags (vnm, ntohl (mp->group.sw_if_index)) && VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0) { // FIXME - don't we clear this state on interface down ... rv = VNET_API_ERROR_UNEXPECTED_INTF_STATE; goto done; } clib_memset (&gaddr, 0, sizeof (gaddr)); clib_memcpy (&gaddr.ip4, &mp->group.gaddr, sizeof (ip4_address_t)); vec_validate (saddrs, mp->group.n_srcs - 1); vec_foreach_index (ii, saddrs) { clib_memcpy (&saddrs[ii].ip4, &mp->group.saddrs[ii], sizeof (ip4_address_t)); } rv = igmp_listen (vm, (mp->group.filter ? IGMP_FILTER_MODE_INCLUDE : IGMP_FILTER_MODE_EXCLUDE), ntohl (mp->group.sw_if_index), saddrs, &gaddr); vec_free (saddrs); BAD_SW_IF_INDEX_LABEL; done:; REPLY_MACRO (VL_API_IGMP_LISTEN_REPLY); } static void vl_api_igmp_enable_disable_t_handler (vl_api_igmp_enable_disable_t * mp) { vl_api_igmp_enable_disable_reply_t *rmp; int rv = 0; VALIDATE_SW_IF_INDEX (mp); rv = igmp_enable_disable (ntohl (mp->sw_if_index), mp->enable, (mp->mode ? IGMP_MODE_HOST : IGMP_MODE_ROUTER)); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_IGMP_ENABLE_DISABLE_REPLY); } static void vl_api_igmp_proxy_device_add_del_t_handler (vl_api_igmp_proxy_device_add_del_t * mp) { vl_api_igmp_proxy_device_add_del_reply_t *rmp; int rv = 0; VALIDATE_SW_IF_INDEX (mp); rv = igmp_proxy_device_add_del (ntohl (mp->vrf_id), ntohl (mp->sw_if_index), mp->add); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_IGMP_PROXY_DEVICE_ADD_DEL_REPLY); } static void vl_api_igmp_proxy_device_add_del_interface_t_handler (vl_api_igmp_proxy_device_add_del_interface_t * mp) { vl_api_igmp_proxy_device_add_del_interface_reply_t *rmp; int rv = 0; VALIDATE_SW_IF_INDEX (mp); rv = igmp_proxy_device_add_del_interface (ntohl (mp->vrf_id), ntohl (mp->sw_if_index), mp->add); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_IGMP_PROXY_DEVICE_ADD_DEL_INTERFACE_REPLY); } static void send_igmp_details (vl_api_registration_t * rp, igmp_main_t * im, igmp_config_t * config, igmp_group_t * group, igmp_src_t * src, u32 context) { vl_api_igmp_details_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); clib_memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = htons (IGMP_MSG_ID (VL_API_IGMP_DETAILS)); mp->context = context; mp->sw_if_index = htonl (config->sw_if_index); clib_memcpy (&mp->saddr, &src->key->ip4, sizeof (src->key->ip4)); clib_memcpy (&mp->gaddr, &group->key->ip4, sizeof (group->key->ip4)); vl_api_send_msg (rp, (u8 *) mp); } static void igmp_config_dump (igmp_main_t * im, vl_api_registration_t * rp, u32 context, igmp_config_t * config) { igmp_group_t *group; igmp_src_t *src; /* *INDENT-OFF* */ FOR_EACH_GROUP (group, config, ({ FOR_EACH_SRC (src, group, IGMP_FILTER_MODE_INCLUDE, ({ send_igmp_details (rp, im, config, group, src, context); })); })); /* *INDENT-ON* */ } static void vl_api_igmp_dump_t_handler (vl_api_igmp_dump_t * mp) { igmp_main_t *im = &igmp_main; igmp_config_t *config; u32 sw_if_index; vl_api_registration_t *rp; rp = vl_api_client_index_to_registration (mp->client_index); if (rp == 0) return; sw_if_index = ntohl (mp->sw_if_index); if (~0 == sw_if_index) { /* *INDENT-OFF* */ pool_foreach (config, im->configs) { igmp_config_dump(im, rp, mp->context, config); } /* *INDENT-ON* */ } else { config = igmp_config_lookup (sw_if_index); if (config) { igmp_config_dump (im, rp, mp->context, config); } } } static void vl_api_igmp_clear_interface_t_handler (vl_api_igmp_clear_interface_t * mp) { vl_api_igmp_clear_interface_reply_t *rmp; igmp_config_t *config; int rv = 0; config = igmp_config_lookup (ntohl (mp->sw_if_index)); if (config) igmp_clear_config (config); REPLY_MACRO (VL_API_IGMP_CLEAR_INTERFACE_REPLY); } static vl_api_group_prefix_type_t igmp_group_type_int_to_api (igmp_group_prefix_type_t t) { switch (t) { case IGMP_GROUP_PREFIX_TYPE_ASM: return (htonl (ASM)); case IGMP_GROUP_PREFIX_TYPE_SSM: return (htonl (SSM)); } return (SSM); } static igmp_group_prefix_type_t igmp_group_type_api_to_int (vl_api_group_prefix_type_t t) { switch (htonl (t)) { case ASM: return (IGMP_GROUP_PREFIX_TYPE_ASM); case SSM: return (IGMP_GROUP_PREFIX_TYPE_SSM); } return (IGMP_GROUP_PREFIX_TYPE_SSM); } static void vl_api_igmp_group_prefix_set_t_handler (vl_api_igmp_group_prefix_set_t * mp) { vl_api_igmp_group_prefix_set_reply_t *rmp; fib_prefix_t pfx; int rv = 0; ip_prefix_decode (&mp->gp.prefix, &pfx); igmp_group_prefix_set (&pfx, igmp_group_type_api_to_int (mp->gp.type)); REPLY_MACRO (VL_API_IGMP_GROUP_PREFIX_SET_REPLY); } typedef struct igmp_ssm_range_walk_ctx_t_ { vl_api_registration_t *rp; u32 context; } igmp_ssm_range_walk_ctx_t; static walk_rc_t igmp_ssm_range_walk_dump (const fib_prefix_t * pfx, igmp_group_prefix_type_t type, void *args) { igmp_ssm_range_walk_ctx_t *ctx = args; vl_api_igmp_group_prefix_details_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); clib_memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = htons (IGMP_MSG_ID (VL_API_IGMP_DETAILS)); mp->context = ctx->context; mp->gp.type = igmp_group_type_int_to_api (type); ip_prefix_encode (pfx, &mp->gp.prefix); vl_api_send_msg (ctx->rp, (u8 *) mp); return (WALK_CONTINUE); } static void vl_api_igmp_group_prefix_dump_t_handler (vl_api_igmp_dump_t * mp) { vl_api_registration_t *rp; rp = vl_api_client_index_to_registration (mp->client_index); if (rp == 0) return; igmp_ssm_range_walk_ctx_t ctx = { .rp = rp, .context = mp->context, }; igmp_ssm_range_walk (igmp_ssm_range_walk_dump, &ctx); } static vpe_client_registration_t * igmp_api_client_lookup (igmp_main_t * im, u32 client_index) { uword *p; vpe_client_registration_t *api_client = NULL; p = hash_get (im->igmp_api_client_by_client_index, client_index); if (p) api_client = vec_elt_at_index (im->api_clients, p[0]); return api_client; } static void vl_api_want_igmp_events_t_handler (vl_api_want_igmp_events_t * mp) { igmp_main_t *im = &igmp_main; vpe_client_registration_t *api_client; vl_api_want_igmp_events_reply_t *rmp; int rv = 0; api_client = igmp_api_client_lookup (im, mp->client_index); if (api_client) { if (mp->enable) { rv = VNET_API_ERROR_INVALID_REGISTRATION; goto done; } hash_unset (im->igmp_api_client_by_client_index, api_client->client_index); pool_put (im->api_clients, api_client); goto done; } if (mp->enable) { pool_get (im->api_clients, api_client); clib_memset (api_client, 0, sizeof (vpe_client_registration_t)); api_client->client_index = mp->client_index; api_client->client_pid = mp->pid; hash_set (im->igmp_api_client_by_client_index, mp->client_index, api_client - im->api_clients); goto done; } rv = VNET_API_ERROR_INVALID_REGISTRATION; done: REPLY_MACRO (VL_API_WANT_IGMP_EVENTS_REPLY); } static clib_error_t * want_igmp_events_reaper (u32 client_index) { igmp_main_t *im = &igmp_main; vpe_client_registration_t *api_client; uword *p; p = hash_get (im->igmp_api_client_by_client_index, client_index); if (p) { api_client = pool_elt_at_index (im->api_clients, p[0]); pool_put (im->api_clients, api_client); hash_unset (im->igmp_api_client_by_client_index, client_index); } return (NULL); } VL_MSG_API_REAPER_FUNCTION (want_igmp_events_reaper); void send_igmp_event (vl_api_registration_t * rp, igmp_filter_mode_t filter, u32 sw_if_index, const ip46_address_t * saddr, const ip46_address_t * gaddr) { vl_api_igmp_event_t *mp = vl_msg_api_alloc (sizeof (*mp)); clib_memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs ((VL_API_IGMP_EVENT) + igmp_main.msg_id_base); mp->sw_if_index = htonl (sw_if_index); mp->filter = htonl (filter); clib_memcpy (&mp->saddr, &saddr->ip4, sizeof (ip4_address_t)); clib_memcpy (&mp->gaddr, &gaddr->ip4, sizeof (ip4_address_t)); vl_api_send_msg (rp, (u8 *) mp); } void igmp_event (igmp_filter_mode_t filter, u32 sw_if_index, const ip46_address_t * saddr, const ip46_address_t * gaddr) { vpe_client_registration_t *api_client; vl_api_registration_t *rp; igmp_main_t *im; im = &igmp_main; IGMP_DBG ("event: (%U, %U) %U %U", format_ip46_address, saddr, IP46_TYPE_ANY, format_ip46_address, saddr, IP46_TYPE_ANY, format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index, format_igmp_filter_mode, filter); /* *INDENT-OFF* */ pool_foreach (api_client, im->api_clients) { rp = vl_api_client_index_to_registration (api_client->client_index); if (rp) send_igmp_event (rp, filter, sw_if_index, saddr, gaddr); } /* *INDENT-ON* */ } /* Set up the API message handling tables */ #include <igmp/igmp.api.c> static clib_error_t * igmp_plugin_api_hookup (vlib_main_t * vm) { igmp_main_t *im = &igmp_main; /* Ask for a correctly-sized block of API message decode slots */ im->msg_id_base = setup_message_id_table (); return 0; } VLIB_API_INIT_FUNCTION (igmp_plugin_api_hookup); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */