/* * Copyright (c) 2017-2018, Pelion and affiliates. * SPDX-License-Identifier: Apache-2.0 * * 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 "nsconfig.h" #include <string.h> #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ipv6_constants.h" #include "MPL/mpl.h" #include "multicast_api.h" #ifdef MULTICAST_FORWARDING int8_t multicast_fwd_add(int8_t interface_id, const uint8_t group[16], uint32_t lifetime) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } return addr_multicast_fwd_add(cur, group, lifetime) ? 0 : -1; } int8_t multicast_fwd_remove(int8_t interface_id, const uint8_t group[16]) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } return addr_multicast_fwd_remove(cur, group) ? 0 : -1; } int8_t multicast_fwd_full_for_scope(int8_t interface_id, uint_fast8_t min_scope) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } cur->ip_mcast_fwd_for_scope = min_scope; return 0; } int8_t multicast_fwd_set_forwarding(int8_t interface_id, bool enable) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } addr_multicast_fwd_set_forwarding(cur, enable); return 0; } int8_t multicast_fwd_set_proxy_upstream(int8_t interface_id) { protocol_interface_info_entry_t *upstream; if (interface_id < 0) { upstream = NULL; } else { upstream = protocol_stack_interface_info_get_by_id(interface_id); if (!upstream || !upstream->ip_multicast_forwarding) { return -1; } } protocol_interface_info_entry_t *old_upstream = protocol_core_multicast_upstream; protocol_core_multicast_upstream = upstream; if (upstream != old_upstream) { /* Try to maintain correct state */ addr_multicast_fwd_adjust_upstream_full(old_upstream, false); addr_multicast_fwd_adjust_upstream_full(upstream, true); } return 0; } #else // MULTICAST_FORWARDING int8_t multicast_fwd_add(int8_t interface_id, const uint8_t group[16], uint32_t lifetime) { (void) interface_id; (void) group; (void) lifetime; return -1; } int8_t multicast_fwd_remove(int8_t interface_id, const uint8_t group[16]) { (void) interface_id; (void) group; return -1; } int8_t multicast_fwd_full_for_scope(int8_t interface_id, uint_fast8_t min_scope) { (void) interface_id; (void) min_scope; return -1; } int8_t multicast_fwd_set_forwarding(int8_t interface_id, bool enable) { (void) interface_id; (void) enable; return -1; } int8_t multicast_fwd_set_proxy_upstream(int8_t interface_id) { (void) interface_id; return -1; } #endif // MULTICAST_FORWARDING #ifdef HAVE_MPL int8_t multicast_mpl_domain_subscribe(int8_t interface_id, const uint8_t address[16], multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id) { protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); if (!interface) { return -1; } return mpl_domain_create(interface, address, seed_id, seed_id_mode, -1, 0, NULL, NULL) ? 0 : -1; } int8_t multicast_mpl_domain_subscribe_with_parameters (int8_t interface_id, const uint8_t address[16], multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id, bool proactive_forwarding, uint16_t seed_set_entry_lifetime, uint32_t data_message_imin, uint32_t data_message_imax, uint8_t data_message_k, uint8_t data_message_timer_expirations, uint32_t control_message_imin, uint32_t control_message_imax, uint8_t control_message_k, uint8_t control_message_timer_expirations) { protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); if (!interface) { return -1; } const trickle_params_t data_params = { .Imax = MPL_MS_TO_TICKS(data_message_imax), .Imin = MPL_MS_TO_TICKS(data_message_imin), .k = data_message_k, .TimerExpirations = data_message_timer_expirations }, control_params = { .Imax = MPL_MS_TO_TICKS(control_message_imax), .Imin = MPL_MS_TO_TICKS(control_message_imin), .k = control_message_k, .TimerExpirations = control_message_timer_expirations }; return mpl_domain_create(interface, address, seed_id, seed_id_mode, proactive_forwarding, seed_set_entry_lifetime, &data_params, &control_params) ? 0 : -1; } int_fast8_t multicast_mpl_set_default_parameters(int8_t interface_id, bool proactive_forwarding, uint16_t seed_set_entry_lifetime, uint32_t data_message_imin, uint32_t data_message_imax, uint8_t data_message_k, uint8_t data_message_timer_expirations, uint32_t control_message_imin, uint32_t control_message_imax, uint8_t control_message_k, uint8_t control_message_timer_expirations) { protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); if (!interface) { return -1; } interface->mpl_proactive_forwarding = proactive_forwarding; interface->mpl_seed_set_entry_lifetime = seed_set_entry_lifetime; interface->mpl_data_trickle_params.Imin = MPL_MS_TO_TICKS(data_message_imin); interface->mpl_data_trickle_params.Imax = MPL_MS_TO_TICKS(data_message_imax); interface->mpl_data_trickle_params.k = data_message_k; interface->mpl_data_trickle_params.TimerExpirations = data_message_timer_expirations; interface->mpl_control_trickle_params.Imin = MPL_MS_TO_TICKS(control_message_imin); interface->mpl_control_trickle_params.Imax = MPL_MS_TO_TICKS(control_message_imax); interface->mpl_control_trickle_params.k = control_message_k; interface->mpl_control_trickle_params.TimerExpirations = control_message_timer_expirations; return 0; } int_fast8_t multicast_mpl_set_default_seed_id(int8_t interface_id, multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id) { protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); if (!interface) { return -1; } switch (seed_id_mode) { case MULTICAST_MPL_SEED_ID_128_BIT: case MULTICAST_MPL_SEED_ID_64_BIT: case MULTICAST_MPL_SEED_ID_16_BIT: memcpy(interface->mpl_seed_id, seed_id, seed_id_mode); break; case MULTICAST_MPL_SEED_ID_IPV6_SRC_FOR_DOMAIN: case MULTICAST_MPL_SEED_ID_IID_EUI64: case MULTICAST_MPL_SEED_ID_IID_SLAAC: case MULTICAST_MPL_SEED_ID_MAC: case MULTICAST_MPL_SEED_ID_MAC_SHORT: break; default: return -2; } interface->mpl_seed_id_mode = seed_id_mode; return 0; } int8_t multicast_mpl_domain_unsubscribe(int8_t interface_id, const uint8_t address[16]) { protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); if (!interface) { return -1; } return mpl_domain_delete(interface, address) ? 0 : -1; } void multicast_set_parameters(uint8_t i_min, uint8_t i_doublings, uint8_t k, uint8_t timer_expirations, uint16_t window_expiration) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get(IF_6LoWPAN); if (!cur) { return; } trickle_time_t i_max = i_min; for (; i_doublings; i_doublings--) { if (i_max <= TRICKLE_TIME_MAX / 2) { i_max *= 2; } else { i_max = TRICKLE_TIME_MAX; break; } } /* Set the interface's default parameters, and also update the All-Forwarders domain */ cur->mpl_data_trickle_params.Imin = i_min; cur->mpl_data_trickle_params.Imax = i_max; cur->mpl_data_trickle_params.k = k; cur->mpl_data_trickle_params.TimerExpirations = timer_expirations; /* MPL core uses a 4:1 ratio for seed and message lifetimes - we somewhat * arbitrarily treat window expiration parameter as a request for message lifetime. */ uint32_t message_lifetime_ticks = (uint32_t) i_max * timer_expirations + window_expiration; uint32_t seed_lifetime_ticks = 4 * message_lifetime_ticks; cur->mpl_seed_set_entry_lifetime = (seed_lifetime_ticks + 20) / 20; // convert to seconds mpl_domain_t *domain = mpl_domain_lookup(cur, ADDR_ALL_MPL_FORWARDERS); if (!domain) { return; } mpl_domain_change_timing(domain, &cur->mpl_data_trickle_params, cur->mpl_seed_set_entry_lifetime); } #else // HAVE_MPL int8_t multicast_mpl_domain_subscribe(int8_t interface_id, const uint8_t address[16], multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id) { (void) interface_id; (void) address; (void) seed_id_mode; (void) seed_id; return -1; } int8_t multicast_mpl_domain_subscribe_with_parameters (int8_t interface_id, const uint8_t address[16], multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id, bool proactive_forwarding, uint16_t seed_set_entry_lifetime, uint32_t data_message_imin, uint32_t data_message_imax, uint8_t data_message_k, uint8_t data_message_timer_expirations, uint32_t control_message_imin, uint32_t control_message_imax, uint8_t control_message_k, uint8_t control_message_timer_expirations) { (void) interface_id; (void) address; (void) seed_id_mode; (void) seed_id; (void) proactive_forwarding; (void) seed_set_entry_lifetime; (void) data_message_imin; (void) data_message_imax; (void) data_message_k; (void) data_message_timer_expirations; (void) control_message_imin; (void) control_message_imax; (void) control_message_k; (void) control_message_timer_expirations; return -1; } int_fast8_t multicast_mpl_set_default_parameters(int8_t interface_id, bool proactive_forwarding, uint16_t seed_set_entry_lifetime, uint32_t data_message_imin, uint32_t data_message_imax, uint8_t data_message_k, uint8_t data_message_timer_expirations, uint32_t control_message_imin, uint32_t control_message_imax, uint8_t control_message_k, uint8_t control_message_timer_expirations) { (void) interface_id; (void) proactive_forwarding; (void) seed_set_entry_lifetime; (void) data_message_imin; (void) data_message_imax; (void) data_message_k; (void) data_message_timer_expirations; (void) control_message_imin; (void) control_message_imax; (void) control_message_k; (void) control_message_timer_expirations; return -1; } int_fast8_t multicast_mpl_set_default_seed_id(int8_t interface_id, multicast_mpl_seed_id_mode_e seed_id_mode, const void *seed_id) { (void) interface_id; (void) seed_id_mode; (void) seed_id; return -1; } int8_t multicast_mpl_domain_unsubscribe(int8_t interface_id, const uint8_t address[16]) { (void) interface_id; (void) address; return -1; } void multicast_set_parameters(uint8_t i_min, uint8_t i_doublings, uint8_t k, uint8_t timer_expirations, uint16_t window_expiration) { (void) i_min; (void) i_doublings; (void) k; (void) timer_expirations; (void) window_expiration; } #endif // HAVE_MPL uint8_t multicast_add_address(const uint8_t *address_ptr, uint8_t use_trickle MAYBE_UNUSED) { uint8_t ret_val = 1; if (!addr_is_ipv6_multicast(address_ptr)) { return 0; } uint8_t scope = addr_ipv6_multicast_scope(address_ptr); if (scope == 0) { // reserved return 0; } else if (scope <= IPV6_SCOPE_LINK_LOCAL) { use_trickle = false; } // Hacky hack. // Consider 6LoWPAN and Ethernet interfaces - if in the same scope zone, attach to one, // 6LoWPAN by preference. If different, attach to both. // If use_trickle is set, then // 1) Make sure MPL is enabled on 6LoWPAN by creating the ff03::fc domain // 2) Subscribe to that MPL domain if Realm Local and not acting as single domain // (If larger scope, then we don't create a domain, we tunnel in ff03::fc) protocol_interface_info_entry_t *lowpan = protocol_stack_interface_info_get(IF_6LoWPAN); protocol_interface_info_entry_t *ethernet = protocol_stack_interface_info_get(IF_IPV6); if (lowpan && ethernet && lowpan->zone_index[scope] == ethernet->zone_index[scope]) { ethernet = NULL; // Both interfaces in same zone, join only on 6LoWPAN } if (lowpan) { #ifdef HAVE_MPL if (use_trickle && !lowpan->mpl_seed) { mpl_domain_create(lowpan, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL); } if (use_trickle && scope == IPV6_SCOPE_REALM_LOCAL && !lowpan->mpl_treat_realm_domains_as_one) { ret_val = multicast_mpl_domain_subscribe(lowpan->id, address_ptr, MULTICAST_MPL_SEED_ID_DEFAULT, NULL); } else #endif { addr_add_group(lowpan, address_ptr); } } if (ethernet) { addr_add_group(ethernet, address_ptr); } return ret_val; } uint8_t multicast_free_address(const uint8_t *address_ptr) { // Hacky hack protocol_interface_info_entry_t *lowpan = protocol_stack_interface_info_get(IF_6LoWPAN); if (lowpan) { #ifdef HAVE_MPL /* First try to delete from MPL - if that fails, delete as plain group */ if (multicast_mpl_domain_unsubscribe(lowpan->id, address_ptr) < 0) #endif { addr_remove_group(lowpan, address_ptr); } } protocol_interface_info_entry_t *ethernet = protocol_stack_interface_info_get(IF_IPV6); if (ethernet) { addr_remove_group(ethernet, address_ptr); } return 0; }