diff --git a/README.md b/README.md index 91ad3bb2c9..f614033fa7 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,39 @@ -ARM IPV6/6LoWPAN stack. +ARM Mesh networking stack ======================= -This repository contains the ARM IPv6/6LoWPAN/Thread Stack for mbed OS. +This repository contains the ARM mesh networking stack that provides support for the following mesh protocols: + + * 6LoWPAN with Neighbor Discovery (ND) and Mesh Link Establishment (MLE) + * Thread + * Wi-SUN + + All networking stacks are using IEEE 802.15.4 based radios. + +The full documentation is hosted in [Mbed OS documentation](https://os.mbed.com/docs/mbed-os/latest/reference/mesh-tech.html). + +On mbed OS, mesh networking stacks can be used through [Mbed Mesh API](https://os.mbed.com/docs/mbed-os/latest/apis/mesh-api.html) and [Network Socket API](https://os.mbed.com/docs/mbed-os/v5.11/apis/network-socket.html). + +To see, how the mesh networking stack works, check the example application [mbed-os-example-mesh-minimal](https://github.com/ARMmbed/mbed-os-example-mesh-minimal). + + +##6LoWPAN with ND and MLE + +This networking stack is using standard 6LoWPAN and uses: + +* Neighbor Discovery Protocol ([RFC4861](https://tools.ietf.org/html/rfc4861)) to locate other devices in the mesh network. +* Mesh-Link-Establishment ([draft-kelsey-intarea-mesh-link-establishment-06](https://tools.ietf.org/html/draft-kelsey-intarea-mesh-link-establishment-06)) is used for establishing and configuring secure radio links. + +##Thread +Thread is standardized by [Thread group](https://www.threadgroup.org/). ![](docs/img/thread_certified.png) mbed OS is now a Thread Certified Component. Using IPv6 with 6LoWPAN as the foundation, Thread technology provides a low-power, self-healing mesh network designed for the home. -The documentation is hosted in [here](https://os.mbed.com/docs/v5.6/tutorials/6lowpan-mesh.html). +##Wi-SUN +Wi-SUN (Smart Utility Networks) specification is standardized by [Wi-SUN Alliance](https://www.wi-sun.org/). -On mbed OS, usage is through [mbed Mesh API](https://os.mbed.com/docs/v5.6/reference/mesh.html) and [Socket API](https://os.mbed.com/docs/v5.6/reference/network-socket.html). - -To see, how the 6LoWPAN Stack works, check the example application [mbed-os-example-mesh-minimal](https://github.com/ARMmbed/mbed-os-example-mesh-minimal). +Mbed OS release 5.12 contains the initial Mbed Wi-SUN FAN implementation. Functionality of the Mbed Wi-SUN network stack will be updated when the Wi-SUN protocol is specified further. ## License diff --git a/nanostack/dhcp_service_api.h b/nanostack/dhcp_service_api.h index 5bdce7b35d..27315a5540 100644 --- a/nanostack/dhcp_service_api.h +++ b/nanostack/dhcp_service_api.h @@ -133,6 +133,16 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty */ void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address); +/** +* \brief Get DHCPv6 Relay Agent address pointer. +* +* \param instance The instance ID of the registered server. +* +* \return NULL when address is not available +* { +*/ +uint8_t *dhcp_service_relay_global_addres_get(uint16_t instance); + /** * \brief Deletes a server instance. @@ -188,6 +198,15 @@ uint32_t dhcp_service_send_req(uint16_t instance_id, uint8_t options, void *ptr, */ void dhcp_service_set_retry_timers(uint32_t msg_tr_id, uint16_t timeout_init, uint16_t timeout_max, uint8_t retrans_max); +/** + * \brief Update DHCP service server address to active tx process. + * + * \param msg_tr_id The message transaction ID. + * \param server_address New destination address to server / relay Agent. + * + */ +void dhcp_service_update_server_address(uint32_t msg_tr_id, uint8_t *server_address); + /** * \brief Stops transactions for a message (retransmissions). * diff --git a/nanostack/fhss_api.h b/nanostack/fhss_api.h index 44af3da84e..84c0c17c32 100644 --- a/nanostack/fhss_api.h +++ b/nanostack/fhss_api.h @@ -143,13 +143,13 @@ typedef int16_t fhss_synch_state_set(const fhss_api_t *api, fhss_states fhss_sta typedef uint32_t fhss_read_timestamp(const fhss_api_t *api); /** - * @brief Get retransmission period. FHSS uses different retry periods for different destinations. + * @brief Get additional retransmission period. FHSS uses different retry periods depending on destination or channel availability. * @param api FHSS instance. * @param destination_address Destination MAC address. * @param phy_mtu PHY MTU size. - * @return Retransmission period. + * @return Retransmission period in microsecond which should be added to normal backoff period. */ -typedef uint16_t fhss_get_retry_period(const fhss_api_t *api, uint8_t *destination_address, uint16_t phy_mtu); +typedef uint32_t fhss_get_retry_period(const fhss_api_t *api, uint8_t *destination_address, uint16_t phy_mtu); /** * @brief Write synchronization info to given pointer. diff --git a/nanostack/mlme.h b/nanostack/mlme.h index 5db31f7c3d..8f0a354514 100644 --- a/nanostack/mlme.h +++ b/nanostack/mlme.h @@ -264,6 +264,7 @@ typedef enum { macAutoRequestKeyIndex = 0x7b, /* 3) { + return -1; + } + + mac_helper_keytable_descriptor_set(interface->mac_api, key, id, interface->mac_parameters->mac_default_key_attribute_id); + return 0; +} + +int8_t mac_helper_security_auto_request_key_index_set(protocol_interface_info_entry_t *interface, uint8_t id) +{ + if (id == 0) { + return -1; + } + + mac_helper_pib_8bit_set(interface, macAutoRequestKeyIndex, id); + return 0; +} + int8_t mac_helper_security_pairwisekey_set(protocol_interface_info_entry_t *interface, const uint8_t *key, const uint8_t *mac_64, uint8_t key_attribute) { @@ -374,6 +394,33 @@ int8_t mac_helper_security_prev_key_set(protocol_interface_info_entry_t *interfa } +int8_t mac_helper_security_key_to_descriptor_set(protocol_interface_info_entry_t *interface, const uint8_t *key, uint8_t id, uint8_t descriptor) +{ + if (id == 0) { + return -1; + } + + mac_helper_keytable_descriptor_set(interface->mac_api, key, id, descriptor); + return 0; +} + +int8_t mac_helper_security_key_descriptor_clear(protocol_interface_info_entry_t *interface, uint8_t descriptor) +{ + if (!interface->mac_api) { + return -1; + } + + mlme_set_t set_req; + mlme_key_descriptor_entry_t key_description; + memset(&key_description, 0, sizeof(mlme_key_descriptor_entry_t)); + + set_req.attr = macKeyTable; + set_req.value_pointer = &key_description; + set_req.value_size = sizeof(mlme_key_descriptor_entry_t); + set_req.attr_index = descriptor; + interface->mac_api->mlme_req(interface->mac_api, MLME_SET, &set_req); + return 0; +} void mac_helper_security_key_swap_next_to_default(protocol_interface_info_entry_t *interface) { diff --git a/source/6LoWPAN/MAC/mac_helper.h b/source/6LoWPAN/MAC/mac_helper.h index 8bc04f7e04..8435328a53 100644 --- a/source/6LoWPAN/MAC/mac_helper.h +++ b/source/6LoWPAN/MAC/mac_helper.h @@ -67,10 +67,18 @@ uint8_t mac_helper_default_key_index_get(struct protocol_interface_info_entry *i int8_t mac_helper_security_default_key_set(struct protocol_interface_info_entry *interface, const uint8_t *key, uint8_t id, uint8_t keyid_mode); +int8_t mac_helper_security_default_recv_key_set(struct protocol_interface_info_entry *interface, const uint8_t *key, uint8_t id, uint8_t keyid_mode); + +int8_t mac_helper_security_auto_request_key_index_set(struct protocol_interface_info_entry *interface, uint8_t id); + int8_t mac_helper_security_next_key_set(struct protocol_interface_info_entry *interface, uint8_t *key, uint8_t id, uint8_t keyid_mode); int8_t mac_helper_security_prev_key_set(struct protocol_interface_info_entry *interface, uint8_t *key, uint8_t id, uint8_t keyid_mode); +int8_t mac_helper_security_key_to_descriptor_set(struct protocol_interface_info_entry *interface, const uint8_t *key, uint8_t id, uint8_t descriptor); + +int8_t mac_helper_security_key_descriptor_clear(struct protocol_interface_info_entry *interface, uint8_t descriptor); + void mac_helper_security_key_swap_next_to_default(struct protocol_interface_info_entry *interface); int8_t mac_helper_security_pairwisekey_set(struct protocol_interface_info_entry *interface, const uint8_t *key, const uint8_t *mac_64, uint8_t key_attribute); diff --git a/source/6LoWPAN/ND/nd_router_object.c b/source/6LoWPAN/ND/nd_router_object.c index e53119bb89..9c8f50351f 100644 --- a/source/6LoWPAN/ND/nd_router_object.c +++ b/source/6LoWPAN/ND/nd_router_object.c @@ -926,7 +926,7 @@ bool nd_ns_aro_handler(protocol_interface_info_entry_t *cur_interface, const uin /* TODO - check hard upper limit on registrations? */ if (ws_info(cur_interface) && - !ws_common_allow_child_registration(cur_interface)) { + !ws_common_allow_child_registration(cur_interface, aro_out->eui64)) { aro_out->present = true; aro_out->status = ARO_FULL; return true; diff --git a/source/6LoWPAN/Thread/thread_address_registration_client.h b/source/6LoWPAN/Thread/thread_address_registration_client.h index d43f964d94..6aa8c19dba 100644 --- a/source/6LoWPAN/Thread/thread_address_registration_client.h +++ b/source/6LoWPAN/Thread/thread_address_registration_client.h @@ -34,6 +34,7 @@ #ifdef HAVE_THREAD_V2 void thread_address_registration_init(void); +bool thread_address_registration_running(void); void thread_address_registration_deinit(void); void thread_address_registration_timer_set(protocol_interface_info_entry_t *interface, uint16_t dua_delay_seconds, uint16_t mlr_refresh_seconds); @@ -41,6 +42,7 @@ void thread_address_registration_timer(protocol_interface_info_entry_t *interfac #else #define thread_address_registration_init(void) +#define thread_address_registration_running(void) #define thread_address_registration_deinit(void) #define thread_address_registration_timer_set(interface, dua_delay_seconds, mlr_refresh_seconds); diff --git a/source/6LoWPAN/Thread/thread_bbr_api.c b/source/6LoWPAN/Thread/thread_bbr_api.c index da53a45684..4401a150ec 100644 --- a/source/6LoWPAN/Thread/thread_bbr_api.c +++ b/source/6LoWPAN/Thread/thread_bbr_api.c @@ -517,6 +517,34 @@ static int thread_border_relay_to_leader_cb(int8_t service_id, uint8_t source_ad } #ifdef HAVE_THREAD_BORDER_ROUTER +static bool thread_bbr_default_route_exists(struct protocol_interface_info_entry *cur, uint8_t prefix_ptr[8]) +{ + uint16_t rloc16 = mac_helper_mac16_address_get(cur); + ns_list_foreach(thread_network_data_prefix_cache_entry_t, prefix, &cur->thread_info->networkDataStorage.localPrefixList) { + + if (prefix_ptr && + (prefix->servicesPrefixLen != 64 || + memcmp(prefix_ptr, prefix->servicesPrefix, 8) != 0)) { + // Only matching prefixes are counted + continue; + } + + ns_list_foreach(thread_network_server_data_entry_t, br, &prefix->borderRouterList) { + if (br->routerID == 0xfffe) { + continue; + } + if (!br->P_default_route) { + continue; + } + if (rloc16 != br->routerID) { + // different default route exists + return true; + } + } + } + return false; +} + static bool thread_bbr_i_host_prefix(struct protocol_interface_info_entry *cur, uint8_t prefix_ptr[8], uint8_t *br_count, bool *i_am_lowest) { bool i_host_this_prefix = false; @@ -600,14 +628,15 @@ static void thread_bbr_network_data_send(thread_bbr_t *this, uint8_t prefix[8], this->br_info_published = true; } -static void thread_bbr_routing_enable(thread_bbr_t *this) +static void thread_bbr_routing_enable(thread_bbr_t *this, bool multicast_routing_enabled) { if (this->routing_enabled) { return; } tr_info("br: enable routing"); // Start multicast proxying - multicast_fwd_set_forwarding(this->interface_id, true); + // We do not enable multicast forwarding as there is other default router present in network + multicast_fwd_set_forwarding(this->interface_id, multicast_routing_enabled); this->routing_enabled = true; } @@ -663,7 +692,13 @@ static void thread_bbr_status_check(thread_bbr_t *this, uint32_t seconds) // Check from network data are we currently BR or not and change routing state if (this->br_hosted) { - thread_bbr_routing_enable(this); + + //If there is a default router present in any prefix other than us we do not forward multicast + //This prevents multicasts to different interfaces where Thread Mesh is forwarder + bool forward_multicast = !thread_bbr_default_route_exists(cur, NULL); + thread_extension_bbr_mcast_fwd_check(cur->id, &forward_multicast); + + thread_bbr_routing_enable(this, forward_multicast); } else { thread_bbr_routing_disable(this); } @@ -896,6 +931,15 @@ int8_t thread_bbr_init(int8_t interface_id, uint16_t external_commisssioner_port return 0; } +int8_t thread_bbr_get_commissioner_service(int8_t interface_id) +{ + thread_bbr_t *this = thread_bbr_find_by_interface(interface_id); + if (!this) { + return 0; + } + + return this->br_service_id; +} void thread_bbr_delete(int8_t interface_id) { thread_bbr_t *this = thread_bbr_find_by_interface(interface_id); @@ -1100,8 +1144,11 @@ int thread_bbr_start(int8_t interface_id, int8_t backbone_interface_id) // By default multicast forwarding is not enabled as it causes multicast loops multicast_fwd_set_forwarding(this->interface_id, false); - // Adjust BBR neighbor and destination cache size - arm_nwk_ipv6_max_cache_entries(THREAD_BBR_IPV6_DESTINATION_CACHE_SIZE); + // Configure BBR neighbour cache parameters + arm_nwk_ipv6_neighbour_cache_configure(THREAD_BBR_IPV6_NEIGHBOUR_CACHE_SIZE, + THREAD_BBR_IPV6_NEIGHBOUR_CACHE_SHORT_TERM, + THREAD_BBR_IPV6_NEIGHBOUR_CACHE_LONG_TERM, + THREAD_BBR_IPV6_NEIGHBOUR_CACHE_LIFETIME); thread_extension_bbr_init(interface_id, backbone_interface_id); @@ -1125,6 +1172,7 @@ int thread_bbr_timeout_set(int8_t interface_id, uint32_t timeout_a, uint32_t tim #endif // HAVE_THREAD_BORDER_ROUTER } + int thread_bbr_prefix_set(int8_t interface_id, uint8_t *prefix) { (void) interface_id; @@ -1159,8 +1207,6 @@ int thread_bbr_validation_interface_address_set(int8_t interface_id, const uint8 #endif // HAVE_THREAD_BORDER_ROUTER } - - void thread_bbr_stop(int8_t interface_id) { (void) interface_id; diff --git a/source/6LoWPAN/Thread/thread_bbr_api_internal.h b/source/6LoWPAN/Thread/thread_bbr_api_internal.h index 9c26ccc41f..e9074ebd67 100644 --- a/source/6LoWPAN/Thread/thread_bbr_api_internal.h +++ b/source/6LoWPAN/Thread/thread_bbr_api_internal.h @@ -66,6 +66,13 @@ void thread_bbr_seconds_timer(int8_t interface_id, uint32_t tics); * \param interface_id current interface id */ int thread_bbr_commissioner_proxy_service_update(int8_t interface_id); +/** + * \brief get commissioner service id to add new services + * + * \param interface_id current interface id + * \return service id or 0 if invalid + */ +int8_t thread_bbr_get_commissioner_service(int8_t interface_id); #else #define thread_bbr_init(interface_id, external_commisssioner_port) diff --git a/source/6LoWPAN/Thread/thread_bootstrap.c b/source/6LoWPAN/Thread/thread_bootstrap.c index 38e900c4c3..965f6b213f 100644 --- a/source/6LoWPAN/Thread/thread_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_bootstrap.c @@ -888,6 +888,7 @@ void thread_interface_init(protocol_interface_info_entry_t *cur) thread_discovery_reset(cur->id); thread_routing_set_mesh_callbacks(cur); dhcp_client_init(cur->id); + dhcp_client_configure(cur->id, false, false, false); thread_management_client_init(cur->id); thread_address_registration_init(); cur->mpl_seed_id_mode = MULTICAST_MPL_SEED_ID_MAC_SHORT; @@ -945,6 +946,31 @@ static void thread_interface_bootsrap_mode_init(protocol_interface_info_entry_t tr_debug("Set End node Mode"); cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_END_DEVICE; } + + if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_ROUTER) { + // set router neighbour cache + ipv6_neighbour_cache_configure(THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SIZE, + THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SHORT_TERM, + THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LONG_TERM, + THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LIFETIME); + // set router destination cache + ipv6_destination_cache_configure(THREAD_ROUTER_IPV6_DESTINATION_CACHE_SIZE, + THREAD_ROUTER_IPV6_DESTINATION_CACHE_SHORT_TERM, + THREAD_ROUTER_IPV6_DESTINATION_CACHE_LONG_TERM, + THREAD_ROUTER_IPV6_DESTINATION_CACHE_LIFETIME); + } else { + // device is some sort of end device + ipv6_neighbour_cache_configure(THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SIZE, + THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SHORT_TERM, + THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LONG_TERM, + THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LIFETIME); + + ipv6_destination_cache_configure(THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SIZE, + THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SHORT_TERM, + THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LONG_TERM, + THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LIFETIME); + } + cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER; } @@ -2540,7 +2566,7 @@ int thread_bootstrap_network_data_process(protocol_interface_info_entry_t *cur, } else { tr_debug("SLAAC address set as NOT preferred."); } - addr_set_preferred_lifetime(cur, e, genericService.P_preferred ? 0xfffffffff : 0); + addr_set_preferred_lifetime(cur, e, genericService.P_preferred ? 0xffffffff : 0); } } } diff --git a/source/6LoWPAN/Thread/thread_common.c b/source/6LoWPAN/Thread/thread_common.c index c1babfdd04..953f039091 100644 --- a/source/6LoWPAN/Thread/thread_common.c +++ b/source/6LoWPAN/Thread/thread_common.c @@ -988,11 +988,7 @@ void thread_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t ticks) thread_resolution_client_timer(cur->id, ticks); thread_key_switch_timer(cur, ticks); thread_child_update_req_timer(cur, ticks); - - if (!thread_bootstrap_should_register_address(cur)) { - /* Only FTD refreshes the address registration timer */ - thread_address_registration_timer(cur, ticks); - } + thread_address_registration_timer(cur, ticks); if (thread_attach_ready(cur) != 0) { return; diff --git a/source/6LoWPAN/Thread/thread_common.h b/source/6LoWPAN/Thread/thread_common.h index 62053a5597..780455957f 100644 --- a/source/6LoWPAN/Thread/thread_common.h +++ b/source/6LoWPAN/Thread/thread_common.h @@ -192,6 +192,7 @@ typedef struct thread_connectivity_s { typedef struct thread_parent_info_s { uint8_t mac64[8]; uint16_t shortAddress; + uint16_t version; uint8_t router_id; uint8_t pathCostToLeader; bool childUpdatePending: 1; diff --git a/source/6LoWPAN/Thread/thread_config.h b/source/6LoWPAN/Thread/thread_config.h index a4542f01cf..4b8d41db18 100644 --- a/source/6LoWPAN/Thread/thread_config.h +++ b/source/6LoWPAN/Thread/thread_config.h @@ -326,11 +326,34 @@ */ #define THREAD_BBR_ROUTER_ID_REQUEST_STATUS THREAD_COAP_STATUS_TLV_HAVE_CHILD_ID_REQUEST -/* - * Number of destination and neighbor cache entries assuming 250 thread devices (worst case) connecting to cloud service. +/* Border Router IPv6 neighbour and destination cache configuration + * Number of neighbor cache entries assuming 250 thread devices (worst case) connecting to cloud service. * Six entries reserved for backbone devices. */ -#define THREAD_BBR_IPV6_DESTINATION_CACHE_SIZE 256 +#define THREAD_BBR_IPV6_NEIGHBOUR_CACHE_SIZE 256 +#define THREAD_BBR_IPV6_NEIGHBOUR_CACHE_SHORT_TERM 128 +#define THREAD_BBR_IPV6_NEIGHBOUR_CACHE_LONG_TERM 32 +#define THREAD_BBR_IPV6_NEIGHBOUR_CACHE_LIFETIME 600 + +/* Router IPv6 neighbour and destination cache configuration */ +#define THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SIZE 128 +#define THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_SHORT_TERM 64 +#define THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LONG_TERM 8 +#define THREAD_ROUTER_IPV6_NEIGHBOUR_CACHE_LIFETIME 600 +#define THREAD_ROUTER_IPV6_DESTINATION_CACHE_SIZE 32 +#define THREAD_ROUTER_IPV6_DESTINATION_CACHE_SHORT_TERM 16 +#define THREAD_ROUTER_IPV6_DESTINATION_CACHE_LONG_TERM 4 +#define THREAD_ROUTER_IPV6_DESTINATION_CACHE_LIFETIME 600 + +/* End device IPv6 neighbour and destination cache configuration */ +#define THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SIZE 32 +#define THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_SHORT_TERM 16 +#define THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LONG_TERM 4 +#define THREAD_END_DEVICE_IPV6_NEIGHBOUR_CACHE_LIFETIME 600 +#define THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SIZE 16 +#define THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_SHORT_TERM 8 +#define THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LONG_TERM 4 +#define THREAD_END_DEVICE_IPV6_DESTINATION_CACHE_LIFETIME 600 /* * Timeout to solicit address from DHCP if previous request fails. diff --git a/source/6LoWPAN/Thread/thread_extension.h b/source/6LoWPAN/Thread/thread_extension.h index 2e9fd3d41f..efc2860a52 100644 --- a/source/6LoWPAN/Thread/thread_extension.h +++ b/source/6LoWPAN/Thread/thread_extension.h @@ -49,6 +49,7 @@ void thread_extension_mtd_service_register(protocol_interface_info_entry_t *cur) void thread_extension_network_data_process(struct protocol_interface_info_entry *cur); int thread_extension_primary_bbr_get(struct protocol_interface_info_entry *cur, uint8_t *addr_ptr, uint8_t *seq_ptr, uint32_t *timer1_ptr, uint32_t *timer2_ptr); void thread_extension_address_registration(struct protocol_interface_info_entry *interface, const uint8_t *addr, const uint8_t *child_mac64, bool refresh_child_entry, bool duplicate_child_detected); +void thread_extension_child_address_registration_response_process(struct protocol_interface_info_entry *interface); void thread_extension_dua_address_generate(protocol_interface_info_entry_t *cur, const uint8_t *domain_prefix, uint8_t domain_prefix_len); void thread_extension_aloc_generate(struct protocol_interface_info_entry *cur); bool thread_extension_aloc_map(protocol_interface_info_entry_t *cur, uint16_t *addr16); @@ -62,11 +63,13 @@ int thread_extension_service_init(protocol_interface_info_entry_t *cur); void thread_extension_addr_ntf_send(struct protocol_interface_info_entry *cur, uint8_t *destination_address, const uint8_t *addr_data_ptr, uint8_t bbr_status); #ifdef HAVE_THREAD_ROUTER bool thread_extension_joining_enabled(int8_t interface_id); +bool thread_extension_is_reed_upgrade_allowed(protocol_interface_info_entry_t *cur); uint8_t thread_extension_discover_response_len(protocol_interface_info_entry_t *cur); uint8_t *thread_extension_discover_response_write(protocol_interface_info_entry_t *cur, uint8_t *ptr); #else #define thread_extension_joining_enabled(interface_id) (false) +#define thread_extension_is_reed_upgrade_allowed(cur) (true) #define thread_extension_discover_response_len(cur) (0) #define thread_extension_discover_response_write(cur, ptr) (ptr) #endif //HAVE_THREAD_ROUTER @@ -78,10 +81,12 @@ uint8_t *thread_extension_discover_response_write(protocol_interface_info_entry_ #define thread_extension_network_data_process(cur) ((void) 0) #define thread_extension_primary_bbr_get(cur,addr_ptr,seq_ptr,timer1_ptr, timer2_ptr) (-1) #define thread_extension_address_registration(interface,addr,child_mac64,refresh_child_entry,duplicate_child_detected) ((void) 0) +#define thread_extension_child_address_registration_response_process(interface) ((void) 0) #define thread_extension_aloc_generate(cur) ((void) 0) #define thread_extension_aloc_map(cur, addr16) (false) #define thread_extension_mcast_subscrition_change(interface) ((void) 0) #define thread_extension_enabled(cur) (false) +#define thread_extension_is_reed_upgrade_allowed(cur) (true) #define thread_extension_version_check(version) (false) #define thread_extension_discover_response_read(nwk_info, discover_response_tlv, data_ptr, data_len) ((void) 0) #define thread_extension_discover_response_tlv_write(data, version, securityPolicy) ((void) 0) diff --git a/source/6LoWPAN/Thread/thread_extension_bbr.h b/source/6LoWPAN/Thread/thread_extension_bbr.h index 153a82bac2..8289c44562 100644 --- a/source/6LoWPAN/Thread/thread_extension_bbr.h +++ b/source/6LoWPAN/Thread/thread_extension_bbr.h @@ -47,7 +47,6 @@ typedef struct thread_pbbr_dua_info { } thread_pbbr_dua_info_t; #if defined(HAVE_THREAD_V2) && defined(HAVE_THREAD_BORDER_ROUTER) - int8_t thread_extension_bbr_init(int8_t interface_id, int8_t backbone_interface_id); void thread_extension_bbr_delete(int8_t interface_id); bool thread_extension_bbr_nd_query_process(protocol_interface_info_entry_t *cur, const uint8_t *target_addr, uint16_t rloc); @@ -58,6 +57,10 @@ int thread_extension_bbr_address_set(int8_t interface_id, const uint8_t *addr_pt void thread_extension_bbr_route_update(protocol_interface_info_entry_t *cur); int thread_extension_bbr_prefix_set(int8_t interface_id, uint8_t *prefix); void thread_extension_bbr_old_partition_data_clean(int8_t interface_id); +void thread_extension_bbr_status_override_get(uint8_t *dua_status, uint8_t *dua_count, uint8_t *ba_failure_count); +void thread_extension_bbr_status_override_set(uint8_t dua_status, uint8_t dua_count, uint8_t ba_failure_count); +void thread_extension_status_override_count_set(uint8_t value); +void thread_extension_bbr_mcast_fwd_check(int8_t interface_id, bool *multicast_fwd); #else @@ -72,6 +75,10 @@ void thread_extension_bbr_old_partition_data_clean(int8_t interface_id); #define thread_extension_bbr_sequence_number_set(interface_id, seq_number) (-1) #define thread_extension_bbr_prefix_set(interface_id, prefix) 0 #define thread_extension_bbr_old_partition_data_clean(interface_id) +#define thread_extension_bbr_status_override_get(dua_status, dua_count, ba_failure_count); +#define thread_extension_bbr_status_override_set(dua_status, dua_count, ba_failure_count); +#define thread_extension_status_override_count_set(value) +#define thread_extension_bbr_mcast_fwd_check(interface_id, multicast_fwd) #endif #ifdef __cplusplus diff --git a/source/6LoWPAN/Thread/thread_extension_bootstrap.h b/source/6LoWPAN/Thread/thread_extension_bootstrap.h index a03eef1848..7eb2d9b5c5 100644 --- a/source/6LoWPAN/Thread/thread_extension_bootstrap.h +++ b/source/6LoWPAN/Thread/thread_extension_bootstrap.h @@ -1,3 +1,4 @@ + /* * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause @@ -38,6 +39,7 @@ extern "C" { #define thread_extension_bootstrap_free(cur); #define thread_extension_bootstrap_device_certificate_set(cur, device_certificate_ptr, device_certificate_len, priv_key_ptr, priv_key_len) (-1) #define thread_extension_bootstrap_network_certificate_set(cur, domain_certificate_ptr, domain_certificate_len) (-1) +#define thread_extension_bootstrap_network_certificate_available(cur) (false) #define thread_extension_bootstrap_network_private_key_set(cur, priv_key_ptr, priv_key_len) (-1) #define thread_extension_bootstrap_thread_name_set(cur, thread_name) (-1) #define thread_extension_bootstrap_commission_start(cur, parent_address, port, done_cb) (-1) diff --git a/source/6LoWPAN/Thread/thread_host_bootstrap.c b/source/6LoWPAN/Thread/thread_host_bootstrap.c index 7685f6454c..50b230c31d 100644 --- a/source/6LoWPAN/Thread/thread_host_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_host_bootstrap.c @@ -897,6 +897,7 @@ static void thread_mle_child_request_receive_cb(int8_t interface_id, mle_message parent->shortAddress = scan_result->shortAddress; parent->router_id = (scan_result->shortAddress >> 10); memcpy(parent->mac64, scan_result->mac64, 8); + parent->version = scan_result->version; //Check Network Data TLV if (networkDataTlv.tlvType == MLE_TYPE_NETWORK_DATA) { thread_bootstrap_network_data_save(cur, &leaderData, networkDataTlv.dataPtr, networkDataTlv.tlvLen); diff --git a/source/6LoWPAN/Thread/thread_joiner_application.c b/source/6LoWPAN/Thread/thread_joiner_application.c index 93e25f91a1..b3bb5cf915 100644 --- a/source/6LoWPAN/Thread/thread_joiner_application.c +++ b/source/6LoWPAN/Thread/thread_joiner_application.c @@ -58,6 +58,7 @@ #include "thread_network_synch.h" #include "thread_network_data_lib.h" #include "thread_joiner_application.h" +#include "thread_extension.h" #include "6LoWPAN/Thread/thread_extension_bootstrap.h" #include "mac_api.h" #include "6LoWPAN/MAC/mac_helper.h" @@ -318,6 +319,7 @@ static link_configuration_s *link_configuration_create(void) } memset(this, 0, sizeof(link_configuration_s)); this->securityPolicy = SECURITY_POLICY_ALL_SECURITY; // Set all default values ('1') for security policy flags + this->securityPolicyExt = SECURITY_POLICY_ALL_SECURITY; // Set all default values return this; } @@ -345,6 +347,7 @@ static void link_configuration_copy(link_configuration_s *this, link_configurati this->panId = configuration_ptr->panId; this->rfChannel = configuration_ptr->rfChannel; this->securityPolicy = configuration_ptr->securityPolicy; + this->securityPolicyExt = configuration_ptr->securityPolicyExt; this->timestamp = configuration_ptr->timestamp; return; } @@ -396,10 +399,14 @@ static int link_configuration_update(link_configuration_s *link_configuration, u if (thread_meshcop_tlv_find(msg_ptr, msg_len, MESHCOP_TLV_PSKC, &ptr) >= 16) { memcpy(link_configuration->PSKc, ptr, 16); } - - if (thread_meshcop_tlv_find(msg_ptr, msg_len, MESHCOP_TLV_SECURITY_POLICY, &ptr) >= 3) { + uint16_t tlv_len = thread_meshcop_tlv_find(msg_ptr, msg_len, MESHCOP_TLV_SECURITY_POLICY, &ptr); + if (tlv_len >= 3) { + link_configuration->securityPolicyExt = SECURITY_POLICY_ALL_SECURITY; link_configuration->securityPolicy = ptr[2]; link_configuration->key_rotation = common_read_16_bit(ptr); + if (tlv_len > 3) { + link_configuration->securityPolicyExt = ptr[3]; + } } return 0; @@ -512,8 +519,8 @@ static void device_configuration_validate(device_configuration_s *this) this->vendor_stack_version[1] = (uint8_t)(THREAD_ARM_OUI >> 8); this->vendor_stack_version[2] = (uint8_t)(THREAD_ARM_OUI); this->vendor_stack_version[3] = (uint8_t)(THREAD_BUILD_NUMBER >> 4); - this->vendor_stack_version[4] = (uint8_t)(((THREAD_BUILD_NUMBER & 0x0f) << 4) || THREAD_REVISION_NUMBER); - this->vendor_stack_version[5] = (uint8_t)((THREAD_VERSION_MIN << 4) || THREAD_VERSION_MAJ); + this->vendor_stack_version[4] = (uint8_t)(((THREAD_BUILD_NUMBER & 0x0f) << 4) | THREAD_REVISION_NUMBER); + this->vendor_stack_version[5] = (uint8_t)((THREAD_VERSION_MIN << 4) | THREAD_VERSION_MAJ); } } @@ -710,7 +717,7 @@ static void configuration_set_copy_mandatory(configuration_set_t *destination_pt tr_debug("mandatory TLVs needed: %s", trace_array(tlv_list, tlv_list_len)); configuration_set_add_fields(destination_ptr, source_ptr->data, source_ptr->length, tlv_list, tlv_list_len); } -static void configuration_set_generate(configuration_set_t *destination_ptr, link_configuration_s *configuration_ptr) +static void configuration_set_generate(int8_t interface_id, configuration_set_t *destination_ptr, link_configuration_s *configuration_ptr) { uint8_t *response_ptr; @@ -733,11 +740,18 @@ static void configuration_set_generate(configuration_set_t *destination_ptr, lin response_ptr = thread_tmfcop_tlv_data_write(response_ptr, MESHCOP_TLV_PSKC, 16, configuration_ptr->PSKc); response_ptr = thread_tmfcop_tlv_data_write(response_ptr, MESHCOP_TLV_NETWORK_NAME, stringlen((char *)&configuration_ptr->name, 16), configuration_ptr->name); *response_ptr++ = MESHCOP_TLV_SECURITY_POLICY; // type - *response_ptr++ = 3; // length - response_ptr = common_write_16_bit(configuration_ptr->key_rotation, response_ptr); - *response_ptr++ = configuration_ptr->securityPolicy; + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); + if (thread_extension_version_check(thread_info(cur)->version)) { + *response_ptr++ = 4; // length + response_ptr = common_write_16_bit(configuration_ptr->key_rotation, response_ptr); + *response_ptr++ = configuration_ptr->securityPolicy; + *response_ptr++ = configuration_ptr->securityPolicyExt; + } else { + *response_ptr++ = 3; // length + response_ptr = common_write_16_bit(configuration_ptr->key_rotation, response_ptr); + *response_ptr++ = configuration_ptr->securityPolicy; + } response_ptr = thread_tmfcop_tlv_data_write_uint64(response_ptr, MESHCOP_TLV_ACTIVE_TIME_STAMP, configuration_ptr->timestamp); - destination_ptr->length = response_ptr - destination_ptr->data; } @@ -859,7 +873,7 @@ int thread_joiner_application_init(int8_t interface_id, device_configuration_s * //If no master key or PSKc set we assume not valid configuration for thread network others may be possible to be 0 //This allows some configurations to be set statically for testing purposes this->configuration_valid = true; - configuration_set_generate(this->active_configuration_ptr, this->configuration_ptr); + configuration_set_generate(this->interface_id, this->active_configuration_ptr, this->configuration_ptr); } } // Always load link configuration from bootstrap state machine. NVM overrides Static configuration @@ -1533,7 +1547,7 @@ int thread_joiner_application_link_configuration_store(int8_t interface_id, link } thread_joiner_application_validate_settings(this);// Generate all random information - configuration_set_generate(this->active_configuration_ptr, link_config); + configuration_set_generate(this->interface_id, this->active_configuration_ptr, link_config); link_configuration_update(this->configuration_ptr, this->active_configuration_ptr->data, this->active_configuration_ptr->length); this->configuration_ptr->key_sequence = link_config->key_sequence; this->configuration_valid = true; diff --git a/source/6LoWPAN/Thread/thread_mle_message_handler.c b/source/6LoWPAN/Thread/thread_mle_message_handler.c index c88e5acb93..85644ecbd7 100644 --- a/source/6LoWPAN/Thread/thread_mle_message_handler.c +++ b/source/6LoWPAN/Thread/thread_mle_message_handler.c @@ -744,6 +744,7 @@ static bool thread_address_registration_tlv_check(protocol_interface_info_entry_ } } } + thread_extension_child_address_registration_response_process(cur); return ret_val; } diff --git a/source/6LoWPAN/Thread/thread_router_bootstrap.c b/source/6LoWPAN/Thread/thread_router_bootstrap.c index f1399a7298..70c19064e0 100644 --- a/source/6LoWPAN/Thread/thread_router_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_router_bootstrap.c @@ -67,6 +67,7 @@ #include "6LoWPAN/Thread/thread_tmfcop_lib.h" #include "6LoWPAN/Thread/thread_nvm_store.h" #include "6LoWPAN/Thread/thread_neighbor_class.h" +#include "6LoWPAN/Thread/thread_extension_bootstrap.h" #include "thread_management_if.h" #include "Common_Protocols/ipv6.h" #include "Common_Protocols/icmpv6.h" @@ -1484,6 +1485,12 @@ void thread_router_bootstrap_mle_receive_cb(int8_t interface_id, mle_message_t * return; } + // check if security policy prevents sending of parent response + if (!thread_extension_is_reed_upgrade_allowed(cur)) { + tr_debug("Security policy prevents parent response; drop packet"); + return; + } + if (thread_am_reed(cur)) { // If we are in REED mode and receive PARENT_REQ from our parent, don't send response. if (thread_router_parent_address_check(cur, mle_msg->packet_src_address)) { @@ -1597,8 +1604,14 @@ void thread_router_bootstrap_mle_receive_cb(int8_t interface_id, mle_message_t * return; } - // If we are in REED mode and receive child ID request from our parent, call connection error. + // check if security policy prevents sending of child id response + if (!thread_extension_is_reed_upgrade_allowed(cur)) { + tr_debug("Security policy prevents child id response; drop packet"); + return; + } + if (thread_am_reed(cur)) { + // If we are in REED mode and receive child ID request from our parent, call connection error. if (thread_router_parent_address_check(cur, mle_msg->packet_src_address)) { tr_debug("Child ID req from own parent -> connection error"); entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); @@ -2226,6 +2239,10 @@ bool thread_router_bootstrap_reed_upgrade(protocol_interface_info_entry_t *cur) return false; } + if (!thread_extension_is_reed_upgrade_allowed(cur)) { + return false; + } + if (thread_am_router(cur)) { return false; } diff --git a/source/6LoWPAN/Thread/thread_test_api.c b/source/6LoWPAN/Thread/thread_test_api.c index c1f0668e40..0ef8eb4a6e 100644 --- a/source/6LoWPAN/Thread/thread_test_api.c +++ b/source/6LoWPAN/Thread/thread_test_api.c @@ -49,6 +49,7 @@ #include "6LoWPAN/Thread/thread_discovery.h" #include "6LoWPAN/Thread/thread_nvm_store.h" #include "6LoWPAN/Thread/thread_extension_bootstrap.h" +#include "6LoWPAN/Thread/thread_extension_bbr.h" #include "6LoWPAN/Thread/thread_neighbor_class.h" #include "MLE/mle.h" #include "thread_meshcop_lib.h" @@ -639,6 +640,23 @@ int thread_test_version_set(int8_t interface_id, uint8_t version) return -1; #endif } + +int thread_test_pbbr_response_override_set(int8_t interface_id, uint8_t dua_status, uint8_t dua_count, uint8_t ba_failure_count) +{ +#ifdef HAVE_THREAD + (void)interface_id; + thread_extension_bbr_status_override_set(dua_status, dua_count, ba_failure_count); + return 0; + +#else + (void)interface_id; + (void)dua_status; + (void)dua_count; + (void)ba_failure_count; + return -1; +#endif +} + int thread_test_router_selection_jitter_set(int8_t interface_id, uint32_t jitter) { #ifdef HAVE_THREAD diff --git a/source/6LoWPAN/ws/ws_bbr_api.c b/source/6LoWPAN/ws/ws_bbr_api.c index 5d242ae687..2cabf92da2 100644 --- a/source/6LoWPAN/ws/ws_bbr_api.c +++ b/source/6LoWPAN/ws/ws_bbr_api.c @@ -35,8 +35,11 @@ #include "net_rpl.h" #include "Service_Libs/nd_proxy/nd_proxy.h" #include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "6LoWPAN/ws/ws_pae_controller.h" #include "DHCPv6_Server/DHCPv6_server_service.h" +#include "ws_bbr_api.h" + #define TRACE_GROUP "wsbs" #define RPL_INSTANCE_ID 1 @@ -49,6 +52,7 @@ * */ static int8_t backbone_interface_id = -1; // BBR backbone information +static uint16_t configuration = BBR_ULA_C | BBR_GUA_C | BBR_GUA_ROUTE; static uint8_t static_dodag_prefix[8] = {0xfd, 0x00, 0x61, 0x72, 0x6d}; static uint8_t static_ula_address[16] = {0}; @@ -109,15 +113,10 @@ static void ws_bbr_rpl_root_start(uint8_t *dodag_id) tr_err("RPL dodag init failed"); return; } - memcpy(static_dodag_id, dodag_id, 16); - // RPL memory limits set larger for Border router rpl_control_set_memory_limits(64 * 1024, 0); - - uint8_t t_flags = PIO_A; - - rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, t_flags, 7200, 7200, false); - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, 0x18, 7200, false); + // Save configured static id to check for changes later + memcpy(static_dodag_id, dodag_id, 16); } static void ws_bbr_rpl_root_stop(void) @@ -130,6 +129,17 @@ static void ws_bbr_rpl_root_stop(void) memset(global_dodag_id, 0, 16); } +static void ws_bbr_ula_prefix_enable(uint8_t *dodag_id) +{ + tr_info("RPL ula prefix start"); + + uint8_t t_flags = PIO_A; + + rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, t_flags, 7200, 7200, false); + rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, dodag_id, 64, 0x18, 7200, false); +} + + static int ws_border_router_proxy_validate(int8_t interface_id, uint8_t *address) { @@ -170,12 +180,13 @@ static int ws_bbr_static_ula_create(protocol_interface_info_entry_t *cur) tr_info("BBR generate ula prefix"); // This address is only used if no other address available. - if_address_entry_t *add_entry = icmpv6_slaac_address_add(cur, static_dodag_prefix, 64, 0xffffffff, 0, true, SLAAC_IID_FIXED); + if_address_entry_t *add_entry = icmpv6_slaac_address_add(cur, static_dodag_prefix, 64, 0xffffffff, 0xffffffff, true, SLAAC_IID_FIXED); if (!add_entry) { return -1; } memcpy(static_ula_address, add_entry->address, 16); tr_info("BBR generate ula prefix addr %s", trace_ipv6(static_ula_address)); + addr_policy_table_add_entry(static_dodag_prefix, 64, 2, WS_NON_PREFFRED_LABEL); return 0; } @@ -266,6 +277,32 @@ static bool wisun_dhcp_address_add_cb(int8_t interfaceId, dhcp_address_cache_upd return true; } +static void ws_bbr_dhcp_server_start(protocol_interface_info_entry_t *cur, uint8_t *global_id) +{ + uint8_t ll[16]; + memcpy(ll, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(&ll[8], cur->mac, 8); + ll[8] ^= 2; + + tr_debug("DHCP server activate %s", trace_ipv6(global_id)); + + if (DHCPv6_server_service_init(cur->id, global_id, cur->mac, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) != 0) { + tr_error("DHCPv6 Server create fail"); + return; + } + DHCPv6_server_service_callback_set(cur->id, global_id, NULL, wisun_dhcp_address_add_cb); + + DHCPv6_server_service_set_address_autonous_flag(cur->id, global_id, true); + DHCPv6_server_service_set_address_validlifetime(cur->id, global_id, 7200); + + ws_dhcp_client_address_request(cur, global_id, ll); +} +static void ws_bbr_dhcp_server_stop(protocol_interface_info_entry_t *cur, uint8_t *global_id) +{ + tr_debug("DHCP server deactivate %s", trace_ipv6(global_id)); + DHCPv6_server_service_delete(cur->id, global_id, false); + +} static void ws_bbr_rpl_status_check(protocol_interface_info_entry_t *cur) { @@ -277,56 +314,73 @@ static void ws_bbr_rpl_status_check(protocol_interface_info_entry_t *cur) ws_bbr_dodag_get(cur, static_id, global_id); + // Check if we need to wait for Global ID + if (configuration & BBR_GUA_WAIT) { + if (memcmp(global_dodag_id, ADDR_UNSPECIFIED, 16) == 0 && + memcmp(global_id, ADDR_UNSPECIFIED, 16) == 0) { + // We need to wait for Global connectivity to start anything + return; + } + } + if (memcmp(static_dodag_id, static_id, 16) != 0) { // Static id updated or first setup ws_bbr_rpl_root_start(static_id); + if (configuration & BBR_ULA_C) { + // Start static ULA prefix and routing always + ws_bbr_ula_prefix_enable(static_id); + } } + if (memcmp(global_dodag_id, global_id, 16) != 0) { // Global prefix changed if (memcmp(global_dodag_id, ADDR_UNSPECIFIED, 16) != 0) { // TODO remove old global prefix tr_info("RPL GUA deactivate %s", trace_ipv6(global_dodag_id)); - rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, PIO_A, 7200, 7200, false); - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, 0x18, 7200, false); - // Old backbone information is deleted after 120 seconds - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, NULL, 0, 0, 120, true); - rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 120, 0, true); - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 120, true); + rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, NULL, 0, 0, 0, true); + rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 0, 0, true); + rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_dodag_id, 64, 0, 0, true); ipv6_route_add_with_info(global_dodag_id, 64, backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 120, 0); - DHCPv6_server_service_delete(cur->id, global_dodag_id, false); - // Set old addresses to deferred and timeout - ws_dhcp_client_address_delete(cur, global_dodag_id); + ws_bbr_dhcp_server_stop(cur, global_dodag_id); } // TODO add global prefix if (memcmp(global_id, ADDR_UNSPECIFIED, 16) != 0) { - //DHCPv6 Server set here - //Interface LL64 address - uint8_t ll[16]; - memcpy(ll, ADDR_LINK_LOCAL_PREFIX, 8); - memcpy(&ll[8], cur->mac, 8); - ll[8] ^= 2; - - if (DHCPv6_server_service_init(cur->id, global_id, cur->mac, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) != 0) { - tr_error("DHCPv6 Server create fail"); - return; - } - DHCPv6_server_service_callback_set(cur->id, global_id, NULL, wisun_dhcp_address_add_cb); - - DHCPv6_server_service_set_address_autonous_flag(cur->id, global_id, true); - DHCPv6_server_service_set_address_validlifetime(cur->id, global_id, 7200); - - tr_info("RPL GUA activate %s", trace_ipv6(global_id)); - ws_dhcp_client_address_request(cur, global_id, ll); + //DHCPv6 Server flags set 0 by default + uint8_t t_flags = 0; + // Add default route to RPL rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, NULL, 0, 0, 7200, false); - rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, PIO_A, 7200, 0, false); - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, static_dodag_id, 64, 0x18, 7200, false); - rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_id, 64, 0, 7200, 7200, false); - rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_id, 64, 0, 7200, false); + // Enable default routing to backbone ipv6_route_add_with_info(global_id, 64, backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 0xffffffff, 0); + + if (configuration & BBR_GUA_SLAAC) { + // GUA prefix is using SLAAC so no DHCP started and set correct flags for prefix + t_flags = PIO_A; + icmpv6_slaac_address_add(cur, global_id, 64, 0xffffffff, 0xffffffff, true, SLAAC_IID_FIXED); + } else { + ws_bbr_dhcp_server_start(cur, global_id); + } + + if (configuration & BBR_GUA_C) { + // Add also global prefix and route to RPL + uint32_t valid_lifetime; + if (t_flags & PIO_A) { + valid_lifetime = 7200; + } else { + valid_lifetime = 0; + } + + rpl_control_update_dodag_prefix(protocol_6lowpan_rpl_root_dodag, global_id, 64, t_flags, valid_lifetime, valid_lifetime, false); + + } + if (configuration & BBR_GUA_ROUTE) { + // Add also global prefix and route to RPL + rpl_control_update_dodag_route(protocol_6lowpan_rpl_root_dodag, global_id, 64, 0, 7200, false); + } + } memcpy(global_dodag_id, global_id, 16); rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); @@ -420,12 +474,44 @@ uint16_t ws_bbr_pan_size(protocol_interface_info_entry_t *cur) if (test_pan_size_override != 0xffff) { return test_pan_size_override; } + // + const uint8_t *prefix_ptr; + if ((configuration & (BBR_ULA_C | BBR_GUA_C)) == BBR_GUA_C) { + //Use just GUA Prefix + prefix_ptr = global_dodag_id; - rpl_control_get_instance_dao_target_count(cur->rpl_domain, RPL_INSTANCE_ID, NULL, &result); + } else { + //Use ULA for indentifier + prefix_ptr = static_dodag_id; + } + + rpl_control_get_instance_dao_target_count(cur->rpl_domain, RPL_INSTANCE_ID, NULL, prefix_ptr, &result); return result; } +bool ws_bbr_ready_to_start(protocol_interface_info_entry_t *cur) +{ + (void)cur; + uint8_t global_address[16]; + + if (backbone_interface_id < 0) { + // No need to wait for backbone + return true; + } + + if ((configuration & BBR_BB_WAIT) != BBR_BB_WAIT) { + // No need to wait for backbone + return true; + } + + if (arm_net_address_get(backbone_interface_id, ADDR_IPV6_GP, global_address) != 0) { + // No global prefix available + return false; + } + + return true; +} #endif //HAVE_WS_BORDER_ROUTER /* Public APIs @@ -471,18 +557,44 @@ void ws_bbr_stop(int8_t interface_id) (void)interface_id; #endif } +int ws_bbr_configure(int8_t interface_id, uint16_t options) +{ +#ifdef HAVE_WS_BORDER_ROUTER + + (void)interface_id; + if (protocol_6lowpan_rpl_root_dodag && + options != configuration) { + //Configuration changed delete previus setup + ws_bbr_rpl_root_stop(); + } + configuration = options; + return 0; +#else + (void)interface_id; + (void)options; + return -1; +#endif +} int ws_bbr_node_keys_remove(int8_t interface_id, uint8_t *eui64) { (void) interface_id; (void) eui64; +#ifdef HAVE_WS_BORDER_ROUTER + return ws_pae_controller_node_keys_remove(interface_id, eui64); +#else return -1; +#endif } int ws_bbr_node_access_revoke_start(int8_t interface_id) { (void) interface_id; +#ifdef HAVE_WS_BORDER_ROUTER + return ws_pae_controller_node_access_revoke_start(interface_id); +#else return -1; +#endif } diff --git a/source/6LoWPAN/ws/ws_bbr_api_internal.h b/source/6LoWPAN/ws/ws_bbr_api_internal.h index a3e7b6e44b..6dfef0b321 100644 --- a/source/6LoWPAN/ws/ws_bbr_api_internal.h +++ b/source/6LoWPAN/ws/ws_bbr_api_internal.h @@ -29,13 +29,15 @@ uint16_t ws_bbr_pan_size(protocol_interface_info_entry_t *cur); void ws_bbr_rpl_config(uint8_t imin, uint8_t doubling, uint8_t redundancy); +bool ws_bbr_ready_to_start(protocol_interface_info_entry_t *cur); #else #define ws_bbr_seconds_timer( cur, seconds) #define ws_bbr_pan_size(cur) 0 -#define ws_bbr_rpl_config( imin, doubling, redundancy); +#define ws_bbr_rpl_config( imin, doubling, redundancy) +#define ws_bbr_ready_to_start(cur) true #endif //HAVE_WS_BORDER_ROUTER diff --git a/source/6LoWPAN/ws/ws_bootstrap.c b/source/6LoWPAN/ws/ws_bootstrap.c index 56caffe04a..2193e6eae8 100644 --- a/source/6LoWPAN/ws/ws_bootstrap.c +++ b/source/6LoWPAN/ws/ws_bootstrap.c @@ -85,8 +85,21 @@ static uint16_t ws_bootstrap_routing_cost_calculate(protocol_interface_info_entr static uint16_t ws_bootstrap_rank_get(protocol_interface_info_entry_t *cur); static uint16_t ws_bootstrap_min_rank_inc_get(protocol_interface_info_entry_t *cur); -static void ws_bootstrap_key_insert(protocol_interface_info_entry_t *cur, uint8_t gtk_index, uint8_t *gtk); +static void ws_bootstrap_mac_security_enable(protocol_interface_info_entry_t *cur); +static void ws_bootstrap_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t operation, uint8_t index, uint8_t *key); +static void ws_bootstrap_nw_key_clear(protocol_interface_info_entry_t *cur, uint8_t slot); +static void ws_bootstrap_nw_key_index_set(protocol_interface_info_entry_t *cur, uint8_t index); +static void ws_bootstrap_nw_frame_counter_set(protocol_interface_info_entry_t *cur, uint32_t counter); static void ws_bootstrap_authentication_completed(protocol_interface_info_entry_t *cur, bool success); +static void ws_bootstrap_pan_version_increment(protocol_interface_info_entry_t *cur); +static ws_nud_table_entry_t *ws_nud_entry_discover(protocol_interface_info_entry_t *cur, void *neighbor); +static void ws_nud_entry_remove(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry_ptr); + +typedef enum { + WS_PARENT_SOFT_SYNCH = 0, /**< let FHSS make decision if synchronization is needed*/ + WS_PARENT_HARD_SYNCH, /**< Synch FHSS with latest synch information*/ + WS_EAPOL_PARENT_SYNCH, /**< Broadcast synch with EAPOL parent*/ +} ws_parent_synch_e; mac_neighbor_table_entry_t *ws_bootstrap_mac_neighbor_add(struct protocol_interface_info_entry *interface, const uint8_t *src64) @@ -142,6 +155,9 @@ static void ws_bootstrap_address_notification_cb(struct protocol_interface_info_ } else if (reason == ADDR_CALLBACK_DELETED) { // What to do? // Go through address list and check if there is global address still available + //Discover prefix policy + addr_policy_remove_by_label(WS_NON_PREFFRED_LABEL); + interface->global_address_available = false; ns_list_foreach(if_address_entry_t, addr_str, &interface->ip_addresses) { if (addr_ipv6_scope(addr_str->address, interface) > IPV6_SCOPE_LINK_LOCAL) { @@ -150,8 +166,14 @@ static void ws_bootstrap_address_notification_cb(struct protocol_interface_info_ break; } } - } else { - tr_debug("Address notification addr: %s reason: %d", trace_ipv6(addr->address), reason); + } else if (reason == ADDR_CALLBACK_TIMER) { + tr_debug("Address Re registration %s", trace_ipv6(addr->address)); + + if (!interface->ws_info->address_registration_event_active) { + interface->ws_info->address_registration_event_active = true; + tr_info("Register ARO"); + ws_bootsrap_event_trig(WS_ADDRESS_ADDED, interface->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); + } } } @@ -170,6 +192,21 @@ static int ws_bootstrap_tasklet_init(protocol_interface_info_entry_t *cur) return 0; } +static void ws_nwk_event_post(protocol_interface_info_entry_t *cur, arm_nwk_interface_status_type_e posted_event) +{ + arm_event_s event = { + .receiver = cur->net_start_tasklet, + .sender = protocol_read_tasklet_id(), /**< Event sender Tasklet ID */ + .event_type = ARM_LIB_NWK_INTERFACE_EVENT, + .event_data = posted_event, + .event_id = (int8_t) cur->id, + .data_ptr = NULL, + .priority = ARM_LIB_LOW_PRIORITY_EVENT, + }; + if (eventOS_event_send(&event) != 0) { + tr_error("nwk_net_event_post(): event send failed"); + } +} static int8_t ws_bootsrap_event_trig(ws_bootsrap_event_type_e event_type, int8_t interface_id, arm_library_event_priority_e priority, void *event_data) { @@ -218,21 +255,32 @@ static ws_nud_table_entry_t *ws_nud_entry_get_free(protocol_interface_info_entry void ws_nud_entry_remove_active(protocol_interface_info_entry_t *cur, void *neighbor) { - ns_list_foreach(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) { - if (entry->neighbor_info == neighbor) { - mac_neighbor_table_entry_t *mac_neighbor = neighbor; - ns_list_remove(&cur->ws_info->active_nud_process, entry); - ns_list_add_to_end(&cur->ws_info->free_nud_entries, entry); - if (mac_neighbor->nud_active) { - mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), mac_neighbor, mac_neighbor->link_lifetime); - } + ws_nud_table_entry_t *entry = ws_nud_entry_discover(cur, neighbor); - mac_neighbor_table_neighbor_connected(mac_neighbor_info(cur), mac_neighbor); - return; + if (entry) { + mac_neighbor_table_entry_t *mac_neighbor = neighbor; + ns_list_remove(&cur->ws_info->active_nud_process, entry); + ns_list_add_to_end(&cur->ws_info->free_nud_entries, entry); + if (mac_neighbor->nud_active) { + mac_neighbor_table_neighbor_refresh(mac_neighbor_info(cur), mac_neighbor, mac_neighbor->link_lifetime); } + + mac_neighbor_table_neighbor_connected(mac_neighbor_info(cur), mac_neighbor); } } + +static ws_nud_table_entry_t *ws_nud_entry_discover(protocol_interface_info_entry_t *cur, void *neighbor) +{ + ns_list_foreach(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) { + if (entry->neighbor_info == neighbor) { + return entry; + } + } + return NULL; +} + + static void ws_nud_state_clean(protocol_interface_info_entry_t *cur, ws_nud_table_entry_t *entry) { mac_neighbor_table_entry_t *neighbor = entry->neighbor_info; @@ -244,6 +292,14 @@ static void ws_nud_state_clean(protocol_interface_info_entry_t *cur, ws_nud_tabl } } +static void ws_nud_entry_remove(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *entry_ptr) +{ + ws_nud_table_entry_t *nud_entry = ws_nud_entry_discover(cur, entry_ptr); + if (nud_entry) { + ws_nud_state_clean(cur, nud_entry); + } +} + static bool ws_nud_message_build(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *neighbor) { //Send NS @@ -444,8 +500,6 @@ static int8_t ws_fhss_discovery_configure(protocol_interface_info_entry_t *cur) fhss_configuration.fhss_broadcast_interval = 0; uint8_t tmp_uc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->fhss_uc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels); uint8_t tmp_bc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->fhss_bc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels); - memset(fhss_configuration.channel_mask, 0, sizeof(uint32_t) * 8); - channel_list_set_channel(fhss_configuration.channel_mask, tmp_uc_fixed_channel, true); fhss_configuration.unicast_fixed_channel = tmp_uc_fixed_channel; fhss_configuration.broadcast_fixed_channel = tmp_bc_fixed_channel; ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration); @@ -477,7 +531,7 @@ static int8_t ws_fhss_enable(protocol_interface_info_entry_t *cur) /* Sets the parent and broadcast schedule we are following * */ -static void ws_bootstrap_primary_parent_set(struct protocol_interface_info_entry *cur, llc_neighbour_req_t *neighbor_info, bool force_synch) +static void ws_bootstrap_primary_parent_set(struct protocol_interface_info_entry *cur, llc_neighbour_req_t *neighbor_info, ws_parent_synch_e synch_req) { fhss_ws_configuration_t fhss_configuration; @@ -490,7 +544,9 @@ static void ws_bootstrap_primary_parent_set(struct protocol_interface_info_entry // Learning broadcast network configuration if (neighbor_info->ws_neighbor->broadcast_shedule_info_stored) { - ws_fhss_set_defaults(cur, &fhss_configuration); + if (synch_req != WS_EAPOL_PARENT_SYNCH) { + ws_fhss_set_defaults(cur, &fhss_configuration); + } fhss_configuration.ws_bc_channel_function = (fhss_ws_channel_functions)neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_channel_function; if (fhss_configuration.ws_bc_channel_function == WS_FIXED_CHANNEL) { cur->ws_info->hopping_schdule.bc_fixed_channel = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.fixed_channel; @@ -500,18 +556,36 @@ static void ws_bootstrap_primary_parent_set(struct protocol_interface_info_entry fhss_configuration.fhss_bc_dwell_interval = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_dwell_interval; fhss_configuration.fhss_broadcast_interval = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_interval; fhss_configuration.broadcast_fixed_channel = cur->ws_info->fhss_bc_fixed_channel; - + neighbor_info->ws_neighbor->synch_done = true; } ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration); // We have broadcast schedule set up set the broadcast parent schedule - ns_fhss_ws_set_parent(cur->ws_info->fhss_api, neighbor_info->neighbor->mac64, &neighbor_info->ws_neighbor->fhss_data.bc_timing_info, force_synch); + ns_fhss_ws_set_parent(cur->ws_info->fhss_api, neighbor_info->neighbor->mac64, &neighbor_info->ws_neighbor->fhss_data.bc_timing_info, synch_req != WS_PARENT_SOFT_SYNCH); // Update LLC to follow updated fhss settings ws_bootstrap_llc_hopping_update(cur, &fhss_configuration); } +void ws_bootstrap_eapol_parent_synch(struct protocol_interface_info_entry *cur, llc_neighbour_req_t *neighbor_info) +{ + if (cur->ws_info->configuration_learned || !neighbor_info->ws_neighbor->broadcast_shedule_info_stored || !neighbor_info->ws_neighbor->broadcast_timing_info_stored) { + return; + } + + if (memcmp(neighbor_info->neighbor->mac64, cur->ws_info->parent_info.addr, 8)) { + return; + } + + //Store Brodacst Shedule + if (!neighbor_info->ws_neighbor->synch_done) { + ws_bootstrap_primary_parent_set(cur, neighbor_info, WS_EAPOL_PARENT_SYNCH); + } else { + ns_fhss_ws_set_parent(cur->ws_info->fhss_api, neighbor_info->neighbor->mac64, &neighbor_info->ws_neighbor->fhss_data.bc_timing_info, false); + } +} + static void ws_bootstrap_ll_address_validate(struct protocol_interface_info_entry *cur) { // Configure EUI64 for MAC if missing @@ -572,10 +646,13 @@ uint16_t ws_etx_read(protocol_interface_info_entry_t *interface, addrtype_t addr } } else { - if (!ws_neighbour || !etx_entry || etx_entry->etx_samples < 1 || - !ws_neighbour->candidate_parent) { + if (!ws_neighbour || !etx_entry || etx_entry->etx_samples < 1 /*|| + !ws_neighbour->candidate_parent*/) { // if RSL value is not good enough candidate parent flag is removed and device not accepted as parent //tr_debug("ws_etx_read not valid parent"); + if (etx_entry && etx_entry->etx_samples) { + tr_debug("ws_etx_read not valid %u RSL IN(%u), %u RSL out(%u)", ws_neighbor_class_rsl_in_get(ws_neighbour), ws_neighbour->rsl_in, ws_neighbor_class_rsl_out_get(ws_neighbour), ws_neighbour->rsl_out); + } return 0xffff; } @@ -589,6 +666,7 @@ uint16_t ws_etx_read(protocol_interface_info_entry_t *interface, addrtype_t addr } else { if (!ws_neighbour->broadcast_timing_info_stored) { //Global shedule is stored already + tr_debug("ws_etx_read not valid NO BTI"); return 0xffff; } } @@ -598,10 +676,6 @@ uint16_t ws_etx_read(protocol_interface_info_entry_t *interface, addrtype_t addr if (etx == 0) { return 0xffff; } - if (etx > 0x800) { - // Wi-SUN section 6.2.3.1.6.1 says ETX can only be maximum of 1024 (8*128) in RPL units, ie 8.0. - etx = 0x800; - } //tr_debug("ws_etx_read etx:%d", etx); return etx; @@ -624,7 +698,8 @@ static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur) return -3; } - + //Enable Power bootup timer setup + cur->ws_info->power_up_setup = true; // Save FHSS api cur->ws_info->fhss_api = ns_sw_mac_get_fhss_api(cur->mac_api); @@ -642,14 +717,16 @@ static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur) /* Disable SLLAO send/mandatory receive with the ARO */ cur->ipv6_neighbour_cache.use_eui64_as_slla_in_aro = true; /* Omit sending of NA if ARO SUCCESS */ - cur->ipv6_neighbour_cache.omit_aro_success = true; + cur->ipv6_neighbour_cache.omit_na_aro_success = true; + /* Omit sending of NA and consider ACK to be success */ + cur->ipv6_neighbour_cache.omit_na = true; // do not process AROs from NA. This is overriden by Wi-SUN specific failure handling cur->ipv6_neighbour_cache.recv_na_aro = false; /* Disable NUD Probes */ cur->ipv6_neighbour_cache.send_nud_probes = false; cur->ipv6_neighbour_cache.probe_avoided_routers = true; dhcp_client_init(cur->id); - dhcp_client_configure(cur->id, true); //RENEW uses SOLICIT + dhcp_client_configure(cur->id, true, true, true); //RENEW uses SOLICIT, Interface will use 1 instance for address get, IAID address hint is not used. dhcp_client_solicit_timeout_set(cur->id, WS_DHCP_SOLICIT_TIMEOUT, WS_DHCP_SOLICIT_MAX_RT, WS_DHCP_SOLICIT_MAX_RC); @@ -819,6 +896,7 @@ static void ws_bootstrap_pan_advertisement_analyse(struct protocol_interface_inf // Save route cost for all neighbours llc_neighbour_req_t neighbor_info; + neighbor_info.neighbor = NULL; if (ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false)) { neighbor_info.ws_neighbor->routing_cost = pan_information.routing_cost; } @@ -848,14 +926,14 @@ static void ws_bootstrap_pan_advertisement_analyse(struct protocol_interface_inf uint16_t pan_cost = (pan_information.routing_cost / PRC_WEIGHT_FACTOR) + (pan_information.pan_size / PS_WEIGHT_FACTOR); uint16_t current_pan_cost = (cur->ws_info->parent_info.pan_information.routing_cost / PRC_WEIGHT_FACTOR) + (cur->ws_info->parent_info.pan_information.pan_size / PS_WEIGHT_FACTOR); if (current_pan_cost < pan_cost) { - tr_info("EAPOL target dropped Higher Pan cost"); + tr_info("EAPOL target dropped Higher Pan cost %u > %u current", pan_cost, current_pan_cost); return; } // If pan cost is the same then we select the one we hear highest if (current_pan_cost == pan_cost && cur->ws_info->parent_info.signal_dbm > data->signal_dbm) { - tr_info("EAPOL target dropped Lower link quality"); + tr_info("EAPOL target dropped Lower link quality %u < %u current", data->signal_dbm, cur->ws_info->parent_info.signal_dbm); return; } @@ -895,12 +973,16 @@ parent_selected: ws_bootstrap_pan_advertisement_analyse_active(cur, &pan_information); // Learn latest network information - if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { - cur->ws_info->pan_information.pan_size = pan_information.pan_size; - cur->ws_info->pan_information.routing_cost = pan_information.routing_cost; - cur->ws_info->pan_information.rpl_routing_method = pan_information.rpl_routing_method; - cur->ws_info->pan_information.use_parent_bs = pan_information.use_parent_bs; - cur->ws_info->pan_information.version = pan_information.version; + if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER && neighbor_info.neighbor) { + + if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) { + cur->ws_info->pan_information.pan_size = pan_information.pan_size; + cur->ws_info->pan_information.routing_cost = pan_information.routing_cost; + cur->ws_info->pan_information.rpl_routing_method = pan_information.rpl_routing_method; + cur->ws_info->pan_information.use_parent_bs = pan_information.use_parent_bs; + cur->ws_info->pan_information.version = pan_information.version; + } + } } @@ -930,7 +1012,6 @@ static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry ws_bs_ie_t ws_bs_ie; uint8_t *gtkhash_ptr; - if (data->SrcPANId != cur->ws_info->network_pan_id) { tr_debug("Wrong PAN id r:%u own:%u", data->SrcPANId, cur->ws_info->network_pan_id); return; @@ -986,19 +1067,26 @@ static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie); if (cur->ws_info->configuration_learned) { - // received version is lower se we need to reset the trickle + tr_info("PAN Config analyse own:%d, heard:%d", cur->ws_info->pan_information.pan_version, pan_version); if (cur->ws_info->pan_information.pan_version == pan_version) { + // Same version heard so it is consistent trickle_consistent_heard(&cur->ws_info->trickle_pan_config); + if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) { + ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_SOFT_SYNCH); + } + // no need to process more + return; } else { - tr_info("different pan version heard"); + // received version is different so we need to reset the trickle trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); + if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) { + ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_HARD_SYNCH); + } if (common_serial_number_greater_16(cur->ws_info->pan_information.pan_version, pan_version)) { // older version heard ignoring the message return; } } - - } if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { @@ -1006,28 +1094,18 @@ static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry return; } - if (cur->ws_info->configuration_learned) { - bool old_version = cur->ws_info->pan_information.pan_version == pan_version; - if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) { - // RPL priority parent configuration we must update FHSS data - //Update synch to primary parent allways to update broadcast shedule and timing - ws_bootstrap_primary_parent_set(cur, &neighbor_info, !old_version); - } - - if (old_version) { - // No new information - return; - } - } - /* - * Learn new information from border router + * Learn new information from neighbor */ - tr_info("Updated PAN configuration heard"); + tr_info("Updated PAN configuration own:%d, heard:%d", cur->ws_info->pan_information.pan_version, pan_version); + // restart PAN version timer cur->ws_info->pan_version_timeout_timer = PAN_VERSION_TIMEOUT; cur->ws_info->pan_information.pan_version = pan_version; - memcpy(cur->ws_info->gtkhash, gtkhash_ptr, 32); + + ws_pae_controller_gtk_hash_update(cur, gtkhash_ptr); + + ws_pae_controller_nw_key_index_update(cur, data->Key.KeyIndex - 1); if (!cur->ws_info->configuration_learned) { // Generate own hopping schedules Follow first parent broadcast and plans and also use same unicast dwell @@ -1036,7 +1114,7 @@ static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry // return to state machine after 1-2 s cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(10, 20); // enable frequency hopping for unicast channel and start listening first neighbour - ws_bootstrap_primary_parent_set(cur, &neighbor_info, true); + ws_bootstrap_primary_parent_set(cur, &neighbor_info, WS_PARENT_HARD_SYNCH); // set neighbor as priority parent clear if there is others protocol_6lowpan_neighbor_priority_clear_all(cur->id, PRIORITY_1ST); neighbor_info.neighbor->link_role = PRIORITY_PARENT_NEIGHBOUR; @@ -1060,13 +1138,13 @@ static void ws_bootstrap_pan_config_solicit_analyse(struct protocol_interface_in */ llc_neighbour_req_t neighbor_info; - if (!ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, true)) { - return; + if (ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false)) { + etx_lqi_dbm_update(cur->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index); + ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, ws_utt, data->timestamp); + ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, ws_us); } - etx_lqi_dbm_update(cur->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index); - ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, ws_utt, data->timestamp); - ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, ws_us); + /* * A consistent transmission is defined as a PAN Configuration Solicit with @@ -1175,19 +1253,19 @@ static void ws_bootstrap_asynch_ind(struct protocol_interface_info_entry *cur, c switch (message_type) { case WS_FT_PAN_ADVERT: // Analyse Advertisement - tr_debug("received ADVERT"); + tr_info("received ADVERT Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm); ws_bootstrap_pan_advertisement_analyse(cur, data, ie_ext, &ws_utt, &ws_us); break; case WS_FT_PAN_ADVERT_SOL: - tr_debug("received ADVERT SOL"); + tr_info("received ADVERT SOL Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm); ws_bootstrap_pan_advertisement_solicit_analyse(cur, data, &ws_utt, &ws_us); break; case WS_FT_PAN_CONF: - tr_debug("received CONFIG"); + tr_info("received CONFIG Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm); ws_bootstrap_pan_config_analyse(cur, data, ie_ext, &ws_utt, &ws_us); break; default: - tr_debug("received CONFIG SOL"); + tr_info("received CONFIG SOL Src:%s rssi:%d", trace_array(data->SrcAddr, 8), data->signal_dbm); ws_bootstrap_pan_config_solicit_analyse(cur, data, &ws_utt, &ws_us); break; } @@ -1221,19 +1299,32 @@ static void ws_bootstrap_neighbor_table_clean(struct protocol_interface_info_ent continue; } - if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, cur->mac64)) { - // We have registered entry so we have been selected as parent - continue; + if (cur->trusted_device) { + + if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, cur->mac64)) { + // We have registered entry so we have been selected as parent + continue; + } + + memcpy(ll_target + 8, cur->mac64, 8); + ll_target[8] ^= 2; + + if (rpl_control_is_dodag_parent(interface, ll_target)) { + // Possible parent is limited to 3 by default? + continue; + } } - memcpy(ll_target + 8, cur->mac64, 8); - ll_target[8] ^= 2; - if (rpl_control_is_dodag_parent(interface, ll_target)) { - // Possible parent is limited to 3 by default? - continue; + + if (cur->trusted_device) { + neighbor_entry_ptr = cur; + } else { + if (cur->link_lifetime - cur->lifetime > WS_NEIGHBOR_NOT_TRUSTED_LINK_TIMEOUT) { + //Accept only Enough Old not trusted Device + neighbor_entry_ptr = cur; + } } - neighbor_entry_ptr = cur; } if (neighbor_entry_ptr) { tr_info("dropped oldest neighbour %s", trace_array(neighbor_entry_ptr->mac64, 8)); @@ -1299,6 +1390,10 @@ static void ws_neighbor_entry_remove_notify(mac_neighbor_table_entry_t *entry_pt protocol_6lowpan_release_short_link_address_from_neighcache(cur, entry_ptr->mac16); protocol_6lowpan_release_long_link_address_from_neighcache(cur, entry_ptr->mac64); } + + //NUD Process Clear Here + ws_nud_entry_remove(cur, entry_ptr); + ws_bootstrap_neighbor_delete(cur, entry_ptr->index); } @@ -1387,6 +1482,13 @@ int ws_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode) if (!etx_storage_list_allocate(cur->id, buffer.device_decription_table_size)) { return -1; } + if (!etx_cached_etx_parameter_set(WS_ETX_MIN_WAIT_TIME, WS_ETX_MIN_SAMPLE_COUNT)) { + etx_storage_list_allocate(cur->id, 0); + return -1; + } + + etx_max_update_set(WS_ETX_MAX_UPDATE); + if (blacklist_init() != 0) { tr_err("MLE blacklist init failed."); return -1; @@ -1452,7 +1554,7 @@ int ws_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode) ret_val = -4; goto init_fail; } - if (ws_pae_controller_cb_register(cur, &ws_bootstrap_authentication_completed, &ws_bootstrap_key_insert) < 0) { + if (ws_pae_controller_cb_register(cur, &ws_bootstrap_authentication_completed, &ws_bootstrap_nw_key_set, &ws_bootstrap_nw_key_clear, &ws_bootstrap_nw_key_index_set, &ws_bootstrap_nw_frame_counter_set, &ws_bootstrap_pan_version_increment) < 0) { ret_val = -4; goto init_fail; } @@ -1542,26 +1644,32 @@ int ws_bootstrap_set_rf_config(protocol_interface_info_entry_t *cur, phy_rf_chan set_request.value_pointer = &ack_wait_symbols; set_request.value_size = sizeof(ack_wait_symbols); cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request); + // Set multi CSMA-CA configuration + mlme_multi_csma_ca_param_t multi_csma_params = {WS_NUMBER_OF_CSMA_PERIODS, WS_CSMA_MULTI_CCA_INTERVAL}; + set_request.attr = macMultiCSMAParameters; + set_request.value_pointer = &multi_csma_params; + set_request.value_size = sizeof(mlme_multi_csma_ca_param_t); + cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request); return 0; } int ws_bootstrap_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) { - tr_warn("ARO registration Failure %s", trace_ipv6(ll_address)); - - blacklist_update(ll_address, false); - rpl_control_neighbor_delete(cur, ll_address); - mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), ll_address, false, NULL); if (mac_neighbor) { - ws_bootstrap_neighbor_delete(cur, mac_neighbor->index); - // TODO Add to blacklist + mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), mac_neighbor); } return 0; } - +int ws_bootstrap_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) +{ + blacklist_update(ll_address, false); + rpl_control_neighbor_delete(cur, ll_address); + ws_bootstrap_neighbor_remove(cur, ll_address); + return 0; +} static void ws_bootstrap_mac_activate(protocol_interface_info_entry_t *cur, uint16_t channel, uint16_t panid, bool coordinator) { @@ -1592,6 +1700,7 @@ static void ws_bootstrap_fhss_activate(protocol_interface_info_entry_t *cur) tr_debug("MAC init"); mac_helper_pib_boolean_set(cur, macRxOnWhenIdle, true); cur->lowpan_info &= ~INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE; + ws_bootstrap_mac_security_enable(cur); ws_bootstrap_mac_activate(cur, cur->ws_info->fhss_uc_fixed_channel, cur->ws_info->network_pan_id, true); return; } @@ -1619,7 +1728,8 @@ static void ws_bootstrap_network_configuration_learn(protocol_interface_info_ent // Timing information can be modified here ws_llc_set_pan_information_pointer(cur, &cur->ws_info->pan_information); - ws_llc_set_gtkhash(cur, cur->ws_info->gtkhash); + uint8_t *gtkhash = ws_pae_controller_gtk_hash_ptr_get(cur); + ws_llc_set_gtkhash(cur, gtkhash); // TODO update own fhss schedules we are starting to follow first parent return; @@ -1728,31 +1838,11 @@ static void ws_dhcp_client_global_adress_cb(int8_t interface, uint8_t dhcp_addr[ tr_debug("DHCPv6 %s status %u", trace_ipv6(dhcp_addr), register_status); } -static bool ws_address_entry_available(uint8_t *prefixPtr, if_address_list_t *list) -{ - bool addressReady = false; - ns_list_foreach(if_address_entry_t, entry, list) { - if (prefixPtr) { - if (memcmp(entry->address, prefixPtr, 8) == 0) { - addressReady = true; - break; - } - } else { - if (entry->source == ADDR_SOURCE_DHCP) { - addressReady = true; - break; - } - } - } - return addressReady; -} void ws_dhcp_client_address_request(protocol_interface_info_entry_t *cur, uint8_t *prefix, uint8_t *parent_link_local) { - if (!ws_address_entry_available(prefix, &cur->ip_addresses)) { - if (dhcp_client_get_global_address(cur->id, parent_link_local, prefix, cur->mac, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE, ws_dhcp_client_global_adress_cb) != 0) { - tr_error("DHCPp client request fail"); - } + if (dhcp_client_get_global_address(cur->id, parent_link_local, prefix, cur->mac, DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE, ws_dhcp_client_global_adress_cb) != 0) { + tr_error("DHCPp client request fail"); } } @@ -1778,24 +1868,30 @@ static void ws_rpl_prefix_callback(prefix_entry_t *prefix, void *handle, uint8_t * from a prefix advertised by a parent. */ if (prefix->options & PIO_A) { - if (icmpv6_slaac_prefix_update(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime) != 0) { - ipv6_interface_slaac_handler(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime); + + if (parent_link_local) { + if (icmpv6_slaac_prefix_update(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime) != 0) { + ipv6_interface_slaac_handler(cur, prefix->prefix, prefix->prefix_len, prefix->lifetime, prefix->preftime); + /* + * Give SLAAC addresses a different label and low precedence to indicate that + * they probably shouldn't be used for external traffic. SLAAC use in Wi-SUN is non-standard, + * and we use it for mesh-local traffic we should prefer any DHCP-assigned addresses + * for talking to the outside world + * + */ + addr_policy_table_add_entry(prefix->prefix, prefix->prefix_len, 2, WS_NON_PREFFRED_LABEL); + } + } else { + icmpv6_slaac_prefix_update(cur, prefix->prefix, prefix->prefix_len, 0, 0); } } else if (prefix->prefix_len) { - if (prefix->preftime == 0) { - // Delete all pending transactions from DHCP - // TODO this also deletes the address even when lifetime would allow it to be present - dhcp_client_global_address_delete(cur->id, NULL, prefix->prefix); - } else { - // Create new address using DHCP + // Create new address using DHCP + if (parent_link_local) { ws_dhcp_client_address_request(cur, prefix->prefix, parent_link_local); - } - // If we have addresses generated we update the lifetimes always - ns_list_foreach(if_address_entry_t, entry, &cur->ip_addresses) { - if (entry->prefix_len == prefix->prefix_len && bitsequal(entry->address, prefix->prefix, prefix->prefix_len)) { - entry->preferred_lifetime = prefix->preftime; - entry->valid_lifetime = prefix->lifetime; - } + } else { + /* Deprecate address and remove client */ + tr_debug("Prefix invalidation %s", trace_ipv6(prefix->prefix)); + dhcp_client_global_address_delete(cur->id, NULL, prefix->prefix); } } } @@ -1839,10 +1935,13 @@ static void ws_bootstrap_advertise_start(protocol_interface_info_entry_t *cur) { cur->ws_info->trickle_pa_running = true; trickle_start(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery); - trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery); cur->ws_info->trickle_pc_running = true; trickle_start(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); - trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); +} + +static void ws_bootstrap_pan_version_increment(protocol_interface_info_entry_t *cur) +{ + cur->ws_info->pan_version_timer = 1; } // Start network scan @@ -1870,7 +1969,19 @@ static void ws_bootstrap_start_discovery(protocol_interface_info_entry_t *cur) // Reset advertisement solicit trickle to start discovering network cur->ws_info->trickle_pas_running = true; trickle_start(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery); - trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery); + if (cur->ws_info->power_up_setup) { + cur->ws_info->power_up_setup = false; + tr_debug("PAS init I %u and t %u", cur->ws_info->trickle_pan_advertisement_solicit.I, cur->ws_info->trickle_pan_advertisement_solicit.t); + } else { + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery); + } + + if ((cur->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) != INTERFACE_NWK_BOOTSRAP_ACTIVE) { + // we have sent bootstrap ready event and now + // restarted discovery so bootstrap down event is sent + cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE; + ws_nwk_event_post(cur, ARM_NWK_NWK_CONNECTION_DOWN); + } // Discovery statemachine is checkked after two trickle interval cur->bootsrap_state_machine_cnt = 2 * cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_8bit() % 50; @@ -1879,25 +1990,38 @@ static void ws_bootstrap_start_discovery(protocol_interface_info_entry_t *cur) // Start authentication static void ws_bootstrap_start_authentication(protocol_interface_info_entry_t *cur) { - tr_debug("authentication start"); + // Set PAN ID and network name to controller + ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->network_name); + ws_pae_controller_authenticate(cur); } - -static void ws_bootstrap_key_insert(protocol_interface_info_entry_t *cur, uint8_t gtk_index, uint8_t *gtk) +static void ws_bootstrap_mac_security_enable(protocol_interface_info_entry_t *cur) { - // Convert GTK to Group AES Key (GAK) + mac_helper_default_security_level_set(cur, AES_SECURITY_LEVEL_ENC_MIC64); + mac_helper_default_security_key_id_mode_set(cur, MAC_KEY_ID_MODE_IDX); +} - // Verify HASH etc. +static void ws_bootstrap_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t slot, uint8_t index, uint8_t *key) +{ + mac_helper_security_key_to_descriptor_set(cur, key, index + 1, slot); +} - // Check index, for now only reacts to keys of index 0 - if (gtk_index == 0) { - mac_helper_security_key_clean(cur); - mac_helper_default_security_level_set(cur, AES_SECURITY_LEVEL_ENC_MIC64); - mac_helper_default_security_key_id_mode_set(cur, MAC_KEY_ID_MODE_IDX); - //Set Keys - mac_helper_security_default_key_set(cur, gtk, gtk_index + 1, MAC_KEY_ID_MODE_IDX); - } +static void ws_bootstrap_nw_key_clear(protocol_interface_info_entry_t *cur, uint8_t slot) +{ + mac_helper_security_key_descriptor_clear(cur, slot); +} + +static void ws_bootstrap_nw_key_index_set(protocol_interface_info_entry_t *cur, uint8_t index) +{ + // Set send key + mac_helper_security_auto_request_key_index_set(cur, index + 1); +} + +static void ws_bootstrap_nw_frame_counter_set(protocol_interface_info_entry_t *cur, uint32_t counter) +{ + // Set frame counter + mac_helper_link_frame_counter_set(cur->id, counter); } static void ws_bootstrap_authentication_completed(protocol_interface_info_entry_t *cur, bool success) @@ -1955,7 +2079,7 @@ void ws_bootstrap_event_configuration_start(protocol_interface_info_entry_t *cur } void ws_bootstrap_event_authentication_start(protocol_interface_info_entry_t *cur) { - ws_bootsrap_event_trig(WS_AUTHENTICATION_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); + ws_bootstrap_state_change(cur, ER_PANA_AUTH); } void ws_bootstrap_event_operation_start(protocol_interface_info_entry_t *cur) { @@ -2155,6 +2279,13 @@ static void ws_bootstrap_event_handler(arm_event_s *event) if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { tr_debug("Border router start network"); + + if (!ws_bbr_ready_to_start(cur)) { + // Wi-SUN not started yet we wait for Border router permission + ws_bootstrap_state_change(cur, ER_WAIT_RESTART); + cur->nwk_nd_re_scan_count = randLIB_get_random_in_range(40, 100); + return; + } ws_pae_controller_auth_init(cur); // Randomize fixed channels. Only used if channel plan is fixed. @@ -2167,7 +2298,9 @@ static void ws_bootstrap_event_handler(arm_event_s *event) cur->ws_info->pan_information.rpl_routing_method = true; cur->ws_info->pan_information.use_parent_bs = true; cur->ws_info->pan_information.version = WS_FAN_VERSION_1_0; - ws_llc_set_gtkhash(cur, cur->ws_info->gtkhash); + + uint8_t *gtkhash = ws_pae_controller_gtk_hash_ptr_get(cur); + ws_llc_set_gtkhash(cur, gtkhash); cur->ws_info->pan_version_timer = PAN_VERSION_LIFETIME; // Set default parameters for FHSS when starting a discovery @@ -2187,6 +2320,9 @@ static void ws_bootstrap_event_handler(arm_event_s *event) // Set authenticator relay to port 10253 and PAE to 10254 (and to own ll address) ws_eapol_auth_relay_start(cur, EAPOL_RELAY_SOCKET_PORT, ll_addr, PAE_AUTH_SOCKET_PORT); + // Set PAN ID and network name to controller + ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->network_name); + // Set PAE port to 10254 and authenticator relay to 10253 (and to own ll address) ws_pae_controller_authenticator_start(cur, PAE_AUTH_SOCKET_PORT, ll_addr, EAPOL_RELAY_SOCKET_PORT); break; @@ -2199,21 +2335,6 @@ static void ws_bootstrap_event_handler(arm_event_s *event) // Start network scan ws_bootstrap_start_discovery(cur); break; - case WS_AUTHENTICATION_START: - tr_info("authentication start"); - // only advert sol stopped as we might be doing re authentication - cur->ws_info->trickle_pas_running = false; - //Add Test ecurity key and security level's - - - // Advertisements stopped during the EAPOL - cur->ws_info->trickle_pa_running = false; - cur->ws_info->trickle_pc_running = false; - cur->ws_info->trickle_pas_running = false; - cur->ws_info->trickle_pcs_running = false; - - ws_bootstrap_start_authentication(cur); - break; case WS_CONFIGURATION_START: tr_info("Configuration start"); @@ -2375,10 +2496,15 @@ static void ws_bootstrap_state_change(protocol_interface_info_entry_t *cur, icmp cur->bootsrap_state_machine_cnt = 1; cur->nwk_bootstrap_state = nwk_bootstrap_state; } + void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur) { switch (cur->nwk_bootstrap_state) { + case ER_WAIT_RESTART: + tr_debug("WS SM:Wait for startup"); + ws_bootstrap_event_discovery_start(cur); + break; case ER_ACTIVE_SCAN: tr_debug("WS SM:Active Scan"); ws_bootstrap_network_scan_process(cur); @@ -2387,6 +2513,19 @@ void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur) tr_debug("WS SM:configuration Scan"); ws_bootstrap_configure_process(cur); break; + case ER_PANA_AUTH: + tr_info("authentication start"); + // only advert sol stopped as we might be doing re authentication + cur->ws_info->trickle_pas_running = false; + //Add Test ecurity key and security level's + // Advertisements stopped during the EAPOL + cur->ws_info->trickle_pa_running = false; + cur->ws_info->trickle_pc_running = false; + cur->ws_info->trickle_pas_running = false; + cur->ws_info->trickle_pcs_running = false; + + ws_bootstrap_start_authentication(cur); + break; case ER_RPL_SCAN: tr_debug("WS SM:Wait RPL to contact DODAG root"); ws_bootstrap_rpl_wait_process(cur); @@ -2396,10 +2535,8 @@ void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur) // Bootstrap_done event to application nwk_bootsrap_state_update(ARM_NWK_BOOTSTRAP_READY, cur); break; - default: tr_warn("WS SM:Invalid state %d", cur->nwk_bootstrap_state); - } } @@ -2416,7 +2553,10 @@ void ws_bootstrap_trickle_timer(protocol_interface_info_entry_t *cur, uint16_t t // send PAN Configuration solicit if (cur->ws_info->pas_requests > PCS_MAX) { // if MAX PCS sent restart discovery - tr_debug("Restart???"); + + // Remove network keys from MAC + ws_pae_controller_nw_keys_remove(cur); + ws_bootstrap_event_discovery_start(cur); return; } @@ -2459,7 +2599,12 @@ void ws_primary_parent_update(protocol_interface_info_entry_t *interface, mac_ne llc_neighbour_req_t neighbor_info; neighbor_info.neighbor = neighbor; neighbor_info.ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor->index); - ws_bootstrap_primary_parent_set(interface, &neighbor_info, true); + ws_bootstrap_primary_parent_set(interface, &neighbor_info, WS_PARENT_HARD_SYNCH); + uint8_t link_local_address[16]; + memcpy(link_local_address, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(link_local_address + 8, neighbor->mac64, 8); + link_local_address[8] ^= 2; + dhcp_client_server_address_update(interface->id, NULL, link_local_address); ws_secondary_parent_update(interface); } diff --git a/source/6LoWPAN/ws/ws_bootstrap.h b/source/6LoWPAN/ws/ws_bootstrap.h index d678cd7e2c..789279e0fc 100644 --- a/source/6LoWPAN/ws/ws_bootstrap.h +++ b/source/6LoWPAN/ws/ws_bootstrap.h @@ -23,7 +23,6 @@ typedef enum { WS_INIT_EVENT = 0, /**< tasklet initializion event*/ WS_DISCOVERY_START, /**< discovery start*/ WS_CONFIGURATION_START, /**< configuration learn start*/ - WS_AUTHENTICATION_START, /**< authentication start*/ WS_OPERATION_START, /**< active operation start*/ WS_ROUTING_READY, /**< RPL routing connected to BR*/ WS_ADDRESS_ADDED /**< Address added to IF*/ @@ -31,6 +30,8 @@ typedef enum { #ifdef HAVE_WS +struct llc_neighbour_req; + int ws_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode); void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur); @@ -41,6 +42,8 @@ int ws_bootstrap_set_rf_config(protocol_interface_info_entry_t *cur, phy_rf_chan int ws_bootstrap_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address); +int ws_bootstrap_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address); + /*State machine transactions*/ void ws_bootstrap_event_discovery_start(protocol_interface_info_entry_t *cur); @@ -72,12 +75,15 @@ void ws_dhcp_client_address_delete(protocol_interface_info_entry_t *cur, uint8_t bool ws_eapol_relay_state_active(protocol_interface_info_entry_t *cur); +void ws_bootstrap_eapol_parent_synch(struct protocol_interface_info_entry *cur, struct llc_neighbour_req *neighbor_info); + #else #define ws_bootstrap_init(interface_id, bootstrap_mode) (-1) #define ws_bootstrap_state_machine(cur) #define ws_bootstrap_restart(cur) #define ws_bootstrap_neighbor_remove(cur, ll_address) +#define ws_bootstrap_aro_failure(cur, ll_address) #define ws_primary_parent_update(interface, neighbor) #define ws_secondary_parent_update(interface) diff --git a/source/6LoWPAN/ws/ws_common.c b/source/6LoWPAN/ws/ws_common.c index 5d46b71172..5478366ec5 100644 --- a/source/6LoWPAN/ws/ws_common.c +++ b/source/6LoWPAN/ws/ws_common.c @@ -274,6 +274,7 @@ int8_t ws_common_allocate_and_init(protocol_interface_info_entry_t *cur) cur->ws_info->hopping_schdule.operating_mode = OPERATING_MODE_3; cur->ws_info->hopping_schdule.operating_class = 2; ws_common_regulatory_domain_config(cur); + cur->ws_info->network_size_config = NETWORK_SIZE_AUTOMATIC; ws_common_network_size_configure(cur, 10); // defaults to small network size // Set defaults for the device. user can modify these. @@ -302,7 +303,12 @@ void ws_common_network_size_configure(protocol_interface_info_entry_t *cur, uint // imin: 14 (16s) // doublings:3 (128s) // redundancy; 0 Disabled - ws_bbr_rpl_config(0, 0, 0);// set the default values + if (cur->ws_info->network_size_config == NETWORK_SIZE_AUTOMATIC) { + ws_bbr_rpl_config(14, 3, 0); + } else { + ws_bbr_rpl_config(0, 0, 0); + } + } else if (network_size < 300) { // Configure the Wi-SUN discovery trickle parameters cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_medium; @@ -348,11 +354,19 @@ void ws_common_neighbor_update(protocol_interface_info_entry_t *cur, const uint8 void ws_common_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) { - //Neighbor connectected update + tr_warn("ARO registration Failure %s", trace_ipv6(ll_address)); + ws_bootstrap_aro_failure(cur, ll_address); +} + +void ws_common_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) +{ + tr_debug("neighbor remove %s", trace_ipv6(ll_address)); ws_bootstrap_neighbor_remove(cur, ll_address); } -bool ws_common_allow_child_registration(protocol_interface_info_entry_t *interface) + + +bool ws_common_allow_child_registration(protocol_interface_info_entry_t *interface, const uint8_t *eui64) { uint8_t child_count = 0; uint8_t max_child_count = mac_neighbor_info(interface)->list_total_size - WS_NON_CHILD_NEIGHBOUR_COUNT; @@ -362,6 +376,12 @@ bool ws_common_allow_child_registration(protocol_interface_info_entry_t *interfa max_child_count = test_max_child_count_override; } + //Validate Is EUI64 already allocated for any address + if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, eui64)) { + tr_info("Child registration from old child"); + return true; + } + ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &mac_neighbor_info(interface)->neighbour_list) { if (ipv6_neighbour_has_registered_by_eui64(&interface->ipv6_neighbour_cache, cur->mac64)) { diff --git a/source/6LoWPAN/ws/ws_common.h b/source/6LoWPAN/ws/ws_common.h index 08cb4a5cc4..3c1d220cc7 100644 --- a/source/6LoWPAN/ws/ws_common.h +++ b/source/6LoWPAN/ws/ws_common.h @@ -79,6 +79,7 @@ typedef struct ws_info_s { bool trickle_pa_running: 1; bool trickle_pcs_running: 1; bool trickle_pc_running: 1; + bool power_up_setup: 1; // default fhss parameters for this device uint8_t fhss_uc_dwell_interval; uint8_t fhss_bc_dwell_interval; @@ -118,7 +119,9 @@ void ws_common_neighbor_update(protocol_interface_info_entry_t *cur, const uint8 void ws_common_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address); -bool ws_common_allow_child_registration(protocol_interface_info_entry_t *cur); +void ws_common_neighbor_remove(protocol_interface_info_entry_t *cur, const uint8_t *ll_address); + +bool ws_common_allow_child_registration(protocol_interface_info_entry_t *cur, const uint8_t *eui64); #define ws_info(cur) ((cur)->ws_info) #else @@ -126,8 +129,9 @@ bool ws_common_allow_child_registration(protocol_interface_info_entry_t *cur); #define ws_common_seconds_timer(cur, seconds) #define ws_common_neighbor_update(cur, ll_address) ((void) 0) #define ws_common_aro_failure(cur, ll_address) +#define ws_common_neighbor_remove(cur, ll_address) #define ws_common_fast_timer(cur, ticks) ((void) 0) -#define ws_common_allow_child_registration(cur) (false) +#define ws_common_allow_child_registration(cur, eui64) (false) #endif //HAVE_WS diff --git a/source/6LoWPAN/ws/ws_common_defines.h b/source/6LoWPAN/ws/ws_common_defines.h index bf799edfe8..4df0dd641f 100644 --- a/source/6LoWPAN/ws/ws_common_defines.h +++ b/source/6LoWPAN/ws/ws_common_defines.h @@ -186,6 +186,7 @@ typedef struct ws_bs_ie { #define WS_FAN_VERSION_1_0 1 #define WS_NEIGHBOR_LINK_TIMEOUT 2200 +#define WS_NEIGHBOR_NOT_TRUSTED_LINK_TIMEOUT 60 #define WS_NEIGHBOR_NUD_TIMEOUT WS_NEIGHBOR_LINK_TIMEOUT / 2 #define WS_NEIGBOR_ETX_SAMPLE_MAX 3 @@ -198,6 +199,18 @@ typedef struct ws_bs_ie { #define WS_NUD_RANDOM_COMPARE (WS_NUD_RAND_PROBABILITY*WS_NUD_RANDOM_SAMPLE_LENGTH) / 100 +#define WS_ETX_MIN_SAMPLE_COUNT 4 + +#define WS_ETX_MAX_UPDATE 1024 + +#define WS_ETX_MIN_WAIT_TIME 60 + +/** + * Wi-sun spesific non-preferred prefix policy label + */ + +#define WS_NON_PREFFRED_LABEL 36 + /* * Threshold (referenced to DEVICE_MIN_SENS) above which a neighbor node may be considered for inclusion into candidate parent set */ @@ -226,6 +239,11 @@ typedef struct ws_bs_ie { */ #define WS_TACK_MAX_MS 5 +// With FHSS we need to check CCA twice on TX channel +#define WS_NUMBER_OF_CSMA_PERIODS 2 +// Interval between two CCA checks +#define WS_CSMA_MULTI_CCA_INTERVAL 1000 + /* Default FHSS timing information * */ diff --git a/source/6LoWPAN/ws/ws_config.h b/source/6LoWPAN/ws/ws_config.h index 7720af7872..f7eecaa201 100644 --- a/source/6LoWPAN/ws/ws_config.h +++ b/source/6LoWPAN/ws/ws_config.h @@ -27,8 +27,8 @@ * */ -#define WS_RPL_DIO_IMIN 14 -#define WS_RPL_DIO_DOUBLING 3 +#define WS_RPL_DIO_IMIN 15 +#define WS_RPL_DIO_DOUBLING 2 #define WS_RPL_DIO_REDUNDANCY 0 diff --git a/source/6LoWPAN/ws/ws_eapol_pdu.c b/source/6LoWPAN/ws/ws_eapol_pdu.c index eb13735085..64cac4dd57 100644 --- a/source/6LoWPAN/ws/ws_eapol_pdu.c +++ b/source/6LoWPAN/ws/ws_eapol_pdu.c @@ -197,12 +197,15 @@ int8_t ws_eapol_pdu_send_to_mpx(protocol_interface_info_entry_t *interface_ptr, } msdu_entry->data_ptr = data; msdu_entry->buffer = buffer; - msdu_entry->handle = eapol_pdu_data->msdu_handle++; + msdu_entry->handle = eapol_pdu_data->msdu_handle; ns_list_add_to_start(&eapol_pdu_data->msdu_list, msdu_entry); memcpy(data_request.DstAddr, eui_64, 8); data_request.msdu = data; data_request.msduLength = size; + data_request.msduHandle = eapol_pdu_data->msdu_handle; + + eapol_pdu_data->msdu_handle++; eapol_pdu_data->mpx_api->mpx_data_request(eapol_pdu_data->mpx_api, &data_request, eapol_pdu_data->mpx_user_id); return 0; diff --git a/source/6LoWPAN/ws/ws_empty_functions.c b/source/6LoWPAN/ws/ws_empty_functions.c index 982e27bd79..0881574e92 100644 --- a/source/6LoWPAN/ws/ws_empty_functions.c +++ b/source/6LoWPAN/ws/ws_empty_functions.c @@ -181,5 +181,12 @@ int ws_test_gtk_time_settings_set(int8_t interface_id, uint8_t revocat_lifetime_ return -1; } -#endif // no HAVE_WS +int ws_test_next_gtk_set(int8_t interface_id, uint8_t *gtk[4]) +{ + (void) interface_id; + (void) gtk; + return -1; +} + +#endif // no HAVE_WS diff --git a/source/6LoWPAN/ws/ws_llc_data_service.c b/source/6LoWPAN/ws/ws_llc_data_service.c index e9e3e2eb9b..9c9192bb1b 100644 --- a/source/6LoWPAN/ws/ws_llc_data_service.c +++ b/source/6LoWPAN/ws/ws_llc_data_service.c @@ -36,6 +36,8 @@ #include "6LoWPAN/ws/ws_llc.h" #include "6LoWPAN/ws/ws_mpx_header.h" #include "6LoWPAN/ws/ws_pae_controller.h" +#include "Security/PANA/pana_eap_header.h" +#include "Security/eapol/eapol_helper.h" #include "Service_Libs/etx/etx.h" #include "fhss_ws_extension.h" @@ -511,9 +513,12 @@ static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t mac_payload_IE_t ws_wp_nested; ws_us_ie_t us_ie; bool us_ie_inline = false; + bool bs_ie_inline = false; ws_wp_nested.id = WS_WP_NESTED_IE; + ws_bs_ie_t ws_bs_ie; if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) > 2) { us_ie_inline = ws_wp_nested_us_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &us_ie); + bs_ie_inline = ws_wp_nested_bs_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &ws_bs_ie); } llc_neighbour_req_t neighbor_info; @@ -534,8 +539,12 @@ static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t if (ws_wh_ea_read(ie_ext->headerIeList, ie_ext->headerIeListLength, auth_eui64)) { ws_pae_controller_border_router_addr_write(base->interface_ptr, auth_eui64); } + if (bs_ie_inline) { + ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie); + } } + //Update BT if it is part of message ws_bt_ie_t ws_bt; if (ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt)) { @@ -543,6 +552,8 @@ static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) { // We have broadcast schedule set up set the broadcast parent schedule ns_fhss_ws_set_parent(interface->ws_info->fhss_api, neighbor_info.neighbor->mac64, &neighbor_info.ws_neighbor->fhss_data.bc_timing_info, false); + } else if (ws_utt.message_type == WS_FT_EAPOL) { + ws_bootstrap_eapol_parent_synch(interface, &neighbor_info); } } @@ -617,6 +628,37 @@ static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id) return header_size; } +static bool ws_eapol_handshake_first_msg(uint8_t *pdu, uint16_t length, protocol_interface_info_entry_t *cur) +{ + if (!ws_eapol_relay_state_active(cur)) { + return false; + } + + eapol_pdu_t eapol_pdu; + uint8_t kmp_type = *pdu++; + length--; + if (!eapol_parse_pdu_header(pdu, length, &eapol_pdu)) { + return false; + } + if (eapol_pdu.packet_type == EAPOL_EAP_TYPE) { + if (eapol_pdu.msg.eap.eap_code == EAP_REQ && eapol_pdu.msg.eap.type == EAP_IDENTITY) { + return true; + } + } else { + + uint8_t key_mask = eapol_pdu_key_mask_get(&eapol_pdu); + if (kmp_type == 6 && key_mask == KEY_INFO_KEY_ACK) { + //FWK first message validation + return true; + } else if (kmp_type == 7 && key_mask == (KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME)) { + //GWK first message validation + return true; + } + } + + return false; +} + static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id) { llc_data_base_t *base = ws_llc_discover_by_mpx(api); @@ -646,11 +688,9 @@ static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data nested_wp_id.vp_ie = true; } } else if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) { - - if (*data->msdu == 1) { //Only when KMP_ID is 1 - ie_header_mask.ea_ie = ws_eapol_relay_state_active(base->interface_ptr); - ie_header_mask.bt_ie = ie_header_mask.ea_ie; - } + ie_header_mask.bt_ie = ws_eapol_relay_state_active(base->interface_ptr); + ie_header_mask.ea_ie = ws_eapol_handshake_first_msg(data->msdu, data->msduLength, base->interface_ptr); + nested_wp_id.bs_ie = ie_header_mask.ea_ie; } @@ -735,6 +775,11 @@ static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data ptr = ws_wp_base_write(ptr, nested_ie_length); //Write unicast schedule ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true); + + if (nested_wp_id.bs_ie) { + //Write Broadcastcast schedule + ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false); + } } diff --git a/source/6LoWPAN/ws/ws_neighbor_class.c b/source/6LoWPAN/ws/ws_neighbor_class.c index 230e5e9986..79036b8375 100644 --- a/source/6LoWPAN/ws/ws_neighbor_class.c +++ b/source/6LoWPAN/ws/ws_neighbor_class.c @@ -138,7 +138,6 @@ uint8_t ws_neighbor_class_rssi_from_dbm_calculate(int8_t dbm_heard) { if (DEVICE_MIN_SENS > dbm_heard) { // We are hearing packet with lower than min_sens dynamically learn the sensitivity - tr_info("heard packet below min sensitivity"); DEVICE_MIN_SENS = dbm_heard; } return dbm_heard - DEVICE_MIN_SENS; diff --git a/source/6LoWPAN/ws/ws_neighbor_class.h b/source/6LoWPAN/ws/ws_neighbor_class.h index 3858465430..bc86ab70f8 100644 --- a/source/6LoWPAN/ws/ws_neighbor_class.h +++ b/source/6LoWPAN/ws/ws_neighbor_class.h @@ -31,6 +31,7 @@ typedef struct ws_neighbor_class_entry { bool candidate_parent: 1; bool broadcast_timing_info_stored: 1; bool broadcast_shedule_info_stored: 1; + bool synch_done : 1; } ws_neighbor_class_entry_t; /** @@ -147,7 +148,7 @@ uint8_t ws_neighbor_class_rssi_from_dbm_calculate(int8_t dbm_heard); * */ #define ws_neighbor_class_rsl_in_get(ws_neighbour) (ws_neighbour->rsl_in >> WS_RSL_SCALING) -#define ws_neighbor_class_rsl_out_get(ws_neighbour) (ws_neighbour->rsl_in >> WS_RSL_SCALING) +#define ws_neighbor_class_rsl_out_get(ws_neighbour) (ws_neighbour->rsl_out >> WS_RSL_SCALING) /** * ws_neighbor_class_neighbor_broadcast_schedule_set a function for update neighbor broadcast shedule information diff --git a/source/6LoWPAN/ws/ws_pae_auth.c b/source/6LoWPAN/ws/ws_pae_auth.c index ff89417ae1..78f559696f 100644 --- a/source/6LoWPAN/ws/ws_pae_auth.c +++ b/source/6LoWPAN/ws/ws_pae_auth.c @@ -17,6 +17,7 @@ #include "nsconfig.h" #include +#include #include "ns_types.h" #include "ns_list.h" #include "ns_trace.h" @@ -39,6 +40,7 @@ #include "Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h" #include "Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.h" #include "6LoWPAN/ws/ws_pae_controller.h" +#include "6LoWPAN/ws/ws_pae_timers.h" #include "6LoWPAN/ws/ws_pae_auth.h" #include "6LoWPAN/ws/ws_pae_lib.h" @@ -54,23 +56,43 @@ // Wait for for supplicant to indicate activity (e.g. to send a message) #define WAIT_FOR_AUTHENTICATION_TICKS 5 * 60 * 10 // 5 minutes +// Maximum number of simultaneous EAP-TLS negotiations +#define MAX_SIMULTANEOUS_EAP_TLS_NEGOTIATIONS 3 + +/* If EAP-TLS is delayed due to simultaneous negotiations limit, defines how + long to wait for previous negotiation to complete */ +#define EAP_TLS_NEGOTIATION_TRIGGER_TIMEOUT 60 * 10 // 60 seconds + typedef struct { ns_list_link_t link; /**< Link */ kmp_service_t *kmp_service; /**< KMP service */ protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + ws_pae_auth_gtk_hash_set *hash_set; /**< GTK hash set callback */ + ws_pae_auth_nw_key_insert *nw_key_insert; /**< Key insert callback */ + ws_pae_auth_nw_key_index_set *nw_key_index_set; /**< Key index set callback */ supp_list_t active_supp_list; /**< List of active supplicants */ supp_list_t inactive_supp_list; /**< List of inactive supplicants */ arm_event_storage_t *timer; /**< Timer */ sec_prot_gtk_keys_t *gtks; /**< GTKs */ + sec_prot_gtk_keys_t *next_gtks; /**< Next GTKs */ const sec_prot_certs_t *certs; /**< Certificates */ - bool timer_running; /**< Timer is running */ + timer_settings_t *timer_settings; /**< Timer settings */ + uint16_t slow_timer_seconds; /**< Slow timer seconds */ + bool timer_running : 1; /**< Timer is running */ + bool gtk_new_inst_req_exp : 1; /**< GTK new install required timer expired */ + bool gtk_new_act_time_exp: 1; /**< GTK new activation time expired */ } pae_auth_t; +static int8_t ws_pae_auth_network_keys_from_gtks_set(pae_auth_t *pae_auth); +static int8_t ws_pae_auth_active_gtk_set(pae_auth_t *pae_auth, uint8_t index); +static int8_t ws_pae_auth_network_key_index_set(pae_auth_t *pae_auth, uint8_t index); static void ws_pae_auth_free(pae_auth_t *pae_auth); static pae_auth_t *ws_pae_auth_get(protocol_interface_info_entry_t *interface_ptr); static pae_auth_t *ws_pae_auth_by_kmp_service_get(kmp_service_t *service); static int8_t ws_pae_auth_event_send(kmp_service_t *service, void *data); static void ws_pae_auth_tasklet_handler(arm_event_s *event); +static void ws_pae_auth_gtk_key_insert(pae_auth_t *pae_auth); +static int8_t ws_pae_auth_new_gtk_activate(pae_auth_t *pae_auth); static int8_t ws_pae_auth_timer_if_start(kmp_service_t *service, kmp_api_t *kmp); static int8_t ws_pae_auth_timer_if_stop(kmp_service_t *service, kmp_api_t *kmp); static int8_t ws_pae_auth_timer_start(pae_auth_t *pae_auth); @@ -82,13 +104,15 @@ static kmp_api_t *ws_pae_auth_kmp_incoming_ind(kmp_service_t *service, kmp_type_ static void ws_pae_auth_kmp_api_create_confirm(kmp_api_t *kmp, kmp_result_e result); static void ws_pae_auth_kmp_api_create_indication(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr); static void ws_pae_auth_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e result, kmp_sec_keys_t *sec_keys); +static void ws_pae_auth_next_kmp_trigger(pae_auth_t *pae_auth, supp_entry_t *supp_entry); +static kmp_type_e ws_pae_auth_next_protocol_get(supp_entry_t *supp_entry); static kmp_api_t *ws_pae_auth_kmp_create_and_start(kmp_service_t *service, kmp_type_e type, supp_entry_t *supp_entry); static void ws_pae_auth_kmp_api_finished(kmp_api_t *kmp); static int8_t tasklet_id = -1; static NS_LIST_DEFINE(pae_auth_list, pae_auth_t, link); -int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs) +int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks, sec_prot_gtk_keys_t *next_gtks, const sec_prot_certs_t *certs, timer_settings_t *timer_settings) { if (!interface_ptr || !gtks || !certs) { return -1; @@ -108,8 +132,17 @@ int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot ws_pae_lib_supp_list_init(&pae_auth->inactive_supp_list); pae_auth->timer = NULL; + pae_auth->hash_set = NULL; + pae_auth->nw_key_insert = NULL; + pae_auth->nw_key_index_set = NULL; + pae_auth->gtks = gtks; + pae_auth->next_gtks = next_gtks; pae_auth->certs = certs; + pae_auth->timer_settings = timer_settings; + pae_auth->slow_timer_seconds = 0; + pae_auth->gtk_new_inst_req_exp = false; + pae_auth->gtk_new_act_time_exp = false; pae_auth->kmp_service = kmp_service_create(); if (!pae_auth->kmp_service) { @@ -205,6 +238,212 @@ int8_t ws_pae_auth_delete(protocol_interface_info_entry_t *interface_ptr) return 0; } +void ws_pae_auth_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_auth_gtk_hash_set *hash_set, ws_pae_auth_nw_key_insert *nw_key_insert, ws_pae_auth_nw_key_index_set *nw_key_index_set) +{ + if (!interface_ptr) { + return; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return; + } + + pae_auth->hash_set = hash_set; + pae_auth->nw_key_insert = nw_key_insert; + pae_auth->nw_key_index_set = nw_key_index_set; +} + +void ws_pae_auth_start(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return; + } + + // Checks if there is predefined active key + int8_t index = sec_prot_keys_gtk_status_active_get(pae_auth->gtks); + if (index < 0) { + // If there is no key, inserts a new one + ws_pae_auth_gtk_key_insert(pae_auth); + index = sec_prot_keys_gtk_install_order_first_index_get(pae_auth->gtks); + ws_pae_auth_active_gtk_set(pae_auth, index); + } else { + ws_pae_auth_active_gtk_set(pae_auth, index); + } + + // Inserts keys and updates GTK hash on stack + ws_pae_auth_network_keys_from_gtks_set(pae_auth); + + // Sets active key index + ws_pae_auth_network_key_index_set(pae_auth, index); +} + +void ws_pae_auth_gtks_updated(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return; + } + + ws_pae_auth_network_keys_from_gtks_set(pae_auth); +} + +int8_t ws_pae_auth_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index) +{ + if (!interface_ptr) { + return -1; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return -1; + } + + ws_pae_auth_active_gtk_set(pae_auth, index); + ws_pae_auth_network_key_index_set(pae_auth, index); + return 0; +} + +int8_t ws_pae_auth_node_keys_remove(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64) +{ + if (!interface_ptr) { + return -1; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return -1; + } + + // Checks if supplicant is active + supp_entry_t *supp = ws_pae_lib_supp_list_entry_eui_64_get(&pae_auth->active_supp_list, eui_64); + + if (supp) { + // Deletes keys and marks as revoked + sec_prot_keys_pmk_delete(&supp->sec_keys); + sec_prot_keys_ptk_delete(&supp->sec_keys); + supp->access_revoked = true; + tr_info("Access revoked; keys removed, eui-64: %s", trace_array(kmp_address_eui_64_get(supp->addr), 8)); + return 0; + } + + // Checks if supplicant is inactive + supp = ws_pae_lib_supp_list_entry_eui_64_get(&pae_auth->inactive_supp_list, eui_64); + if (supp) { + // Deletes supplicant + tr_info("Access revoked; deleted, eui-64: %s", trace_array(kmp_address_eui_64_get(supp->addr), 8)); + ws_pae_lib_supp_list_remove(&pae_auth->inactive_supp_list, supp); + return 0; + } + + return -1; +} + +int8_t ws_pae_auth_node_access_revoke_start(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return -1; + } + + // Gets active GTK + int8_t active_index = sec_prot_keys_gtk_status_active_get(pae_auth->gtks); + + if (active_index >= 0) { + // As default removes other keys than active + int8_t not_removed_index = active_index; + + uint32_t revocation_lifetime = ws_pae_timers_gtk_revocation_lifetime_get(pae_auth->timer_settings); + + uint32_t active_lifetime = sec_prot_keys_gtk_lifetime_get(pae_auth->gtks, active_index); + + // If active GTK lifetime is larger than revocation lifetime decrements active GTK lifetime + if (active_lifetime > revocation_lifetime) { + sec_prot_keys_gtk_lifetime_decrement(pae_auth->gtks, active_index, active_lifetime - revocation_lifetime); + tr_info("Access revocation start, GTK active index: %i, revoked lifetime: %"PRIu32"", active_index, revocation_lifetime); + } else { + // Otherwise decrements lifetime of the GTK to be installed after the active one + int8_t second_index = sec_prot_keys_gtk_install_order_second_index_get(pae_auth->gtks); + if (second_index >= 0) { + // Second GTK revocation lifetime is the active GTK lifetime added with revocation time + uint32_t second_revocation_lifetime = active_lifetime + revocation_lifetime; + + uint32_t second_lifetime = sec_prot_keys_gtk_lifetime_get(pae_auth->gtks, second_index); + if (second_lifetime > second_revocation_lifetime) { + sec_prot_keys_gtk_lifetime_decrement(pae_auth->gtks, second_index, second_lifetime - second_revocation_lifetime); + tr_info("Access revocation start, GTK second active index: %i, revoked lifetime: %"PRIu32"", second_index, second_revocation_lifetime); + } + // Removes other keys than active and GTK to be installed next + not_removed_index = second_index; + } + } + + // Deletes other GTKs + int8_t last_index = sec_prot_keys_gtk_install_order_last_index_get(pae_auth->gtks); + while (last_index >= 0 && last_index != not_removed_index) { + tr_info("Access revocation GTK clear index: %i", last_index); + sec_prot_keys_gtk_clear(pae_auth->gtks, last_index); + last_index = sec_prot_keys_gtk_install_order_last_index_get(pae_auth->gtks); + } + } + + // Adds new GTK + ws_pae_auth_gtk_key_insert(pae_auth); + ws_pae_auth_network_keys_from_gtks_set(pae_auth); + + return 0; +} + +static int8_t ws_pae_auth_network_keys_from_gtks_set(pae_auth_t *pae_auth) +{ + // Authenticator keys are always fresh + sec_prot_keys_gtk_status_all_fresh_set(pae_auth->gtks); + + if (pae_auth->hash_set) { + uint8_t gtk_hash[32]; + sec_prot_keys_gtks_hash_generate(pae_auth->gtks, gtk_hash); + pae_auth->hash_set(pae_auth->interface_ptr, gtk_hash); + } + + if (pae_auth->nw_key_insert) { + pae_auth->nw_key_insert(pae_auth->interface_ptr, pae_auth->gtks); + } + + return 0; +} + +static int8_t ws_pae_auth_active_gtk_set(pae_auth_t *pae_auth, uint8_t index) +{ + return sec_prot_keys_gtk_status_active_set(pae_auth->gtks, index); +} + +static int8_t ws_pae_auth_gtk_clear(pae_auth_t *pae_auth, uint8_t index) +{ + return sec_prot_keys_gtk_clear(pae_auth->gtks, index); +} + +static int8_t ws_pae_auth_network_key_index_set(pae_auth_t *pae_auth, uint8_t index) +{ + if (pae_auth->nw_key_index_set) { + pae_auth->nw_key_index_set(pae_auth->interface_ptr, index); + } + + return 0; +} + static void ws_pae_auth_free(pae_auth_t *pae_auth) { if (!pae_auth) { @@ -287,7 +526,7 @@ static void ws_pae_auth_tasklet_handler(arm_event_s *event) } } -void ws_pae_auth_timer(uint16_t ticks) +void ws_pae_auth_fast_timer(uint16_t ticks) { ns_list_foreach(pae_auth_t, pae_auth, &pae_auth_list) { if (!ws_pae_auth_timer_running(pae_auth)) { @@ -302,6 +541,111 @@ void ws_pae_auth_timer(uint16_t ticks) } } +void ws_pae_auth_slow_timer(uint16_t seconds) +{ + ns_list_foreach(pae_auth_t, pae_auth, &pae_auth_list) { + + // Gets index of currently active GTK + int8_t active_index = sec_prot_keys_gtk_status_active_get(pae_auth->gtks); + + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (!sec_prot_keys_gtk_is_set(pae_auth->gtks, i)) { + continue; + } + uint32_t timer_seconds = sec_prot_keys_gtk_lifetime_decrement(pae_auth->gtks, i, seconds); + if (active_index == i) { + if (!pae_auth->gtk_new_inst_req_exp) { + pae_auth->gtk_new_inst_req_exp = ws_pae_timers_gtk_new_install_required(pae_auth->timer_settings, timer_seconds); + if (pae_auth->gtk_new_inst_req_exp) { + int8_t second_index = sec_prot_keys_gtk_install_order_second_index_get(pae_auth->gtks); + if (second_index < 0) { + tr_info("GTK new install required active index: %i, time: %"PRIu32", system time: %"PRIu32"", active_index, timer_seconds, protocol_core_monotonic_time / 10); + ws_pae_auth_gtk_key_insert(pae_auth); + ws_pae_auth_network_keys_from_gtks_set(pae_auth); + } else { + tr_info("GTK new install already done; second index: %i, time: %"PRIu32", system time: %"PRIu32"", second_index, timer_seconds, protocol_core_monotonic_time / 10); + } + } + } + + if (!pae_auth->gtk_new_act_time_exp) { + pae_auth->gtk_new_act_time_exp = ws_pae_timers_gtk_new_activation_time(pae_auth->timer_settings, timer_seconds); + if (pae_auth->gtk_new_act_time_exp) { + int8_t new_active_index = ws_pae_auth_new_gtk_activate(pae_auth); + tr_info("GTK new activation time active index: %i, time: %"PRIu32", new index: %i, system time: %"PRIu32"", active_index, timer_seconds, new_active_index, protocol_core_monotonic_time / 10); + if (new_active_index >= 0) { + ws_pae_auth_network_key_index_set(pae_auth, new_active_index); + } + pae_auth->gtk_new_inst_req_exp = false; + pae_auth->gtk_new_act_time_exp = false; + } + } + } + + if (timer_seconds == 0) { + tr_info("GTK expired index: %i, system time: %"PRIu32"", i, protocol_core_monotonic_time / 10); + ws_pae_auth_gtk_clear(pae_auth, i); + ws_pae_auth_network_keys_from_gtks_set(pae_auth); + } + } + + pae_auth->slow_timer_seconds += seconds; + if (pae_auth->slow_timer_seconds > 60) { + ws_pae_lib_supp_list_slow_timer_update(&pae_auth->active_supp_list, pae_auth->timer_settings, pae_auth->slow_timer_seconds); + ws_pae_lib_supp_list_slow_timer_update(&pae_auth->inactive_supp_list, pae_auth->timer_settings, pae_auth->slow_timer_seconds); + pae_auth->slow_timer_seconds = 0; + } + } +} + +static void ws_pae_auth_gtk_key_insert(pae_auth_t *pae_auth) +{ + // Gets index to install the key + uint8_t install_index = sec_prot_keys_gtk_install_index_get(pae_auth->gtks); + + // Key to install + uint8_t gtk_value[GTK_LEN]; + + // Checks if next GTK values are set and gets first GTK to install + int8_t next_gtk_index = sec_prot_keys_gtk_install_order_first_index_get(pae_auth->next_gtks); + if (next_gtk_index >= 0) { + // Gets GTK value + uint8_t *gtk = sec_prot_keys_gtk_get(pae_auth->next_gtks, next_gtk_index); + memcpy(gtk_value, gtk, GTK_LEN); + // Sets same key back to next GTKs but as the last key to be installed + sec_prot_keys_gtk_clear(pae_auth->next_gtks, next_gtk_index); + sec_prot_keys_gtk_set(pae_auth->next_gtks, next_gtk_index, gtk_value, 0); + } else { + randLIB_get_n_bytes_random(gtk_value, GTK_LEN); + } + + // Gets latest installed key lifetime and adds GTK expire offset to it + uint32_t lifetime = pae_auth->timer_settings->gtk_expire_offset; + int8_t last_index = sec_prot_keys_gtk_install_order_last_index_get(pae_auth->gtks); + if (last_index >= 0) { + lifetime += sec_prot_keys_gtk_lifetime_get(pae_auth->gtks, last_index); + } + + // Installs the new key + sec_prot_keys_gtk_clear(pae_auth->gtks, install_index); + sec_prot_keys_gtk_set(pae_auth->gtks, install_index, gtk_value, lifetime); + + // Authenticator keys are always fresh + sec_prot_keys_gtk_status_all_fresh_set(pae_auth->gtks); + + tr_info("GTK install new index: %i, lifetime: %"PRIu32" system time: %"PRIu32"", install_index, lifetime, protocol_core_monotonic_time / 10); +} + +static int8_t ws_pae_auth_new_gtk_activate(pae_auth_t *pae_auth) +{ + int8_t new_active_index = sec_prot_keys_gtk_install_order_second_index_get(pae_auth->gtks); + if (new_active_index >= 0) { + ws_pae_auth_active_gtk_set(pae_auth, new_active_index); + } + + return new_active_index; +} + static int8_t ws_pae_auth_timer_if_start(kmp_service_t *service, kmp_api_t *kmp) { pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); @@ -363,19 +707,16 @@ static bool ws_pae_auth_timer_running(pae_auth_t *pae_auth) static void ws_pae_auth_kmp_service_addr_get(kmp_service_t *service, kmp_api_t *kmp, kmp_addr_t *local_addr, kmp_addr_t *remote_addr) { - (void) service; + pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); + if (!pae_auth) { + return; + } -#if 0 // Get own EUI-64 link_layer_address_s mac_params; if (arm_nwk_mac_address_read(pae_auth->interface_ptr->id, &mac_params) >= 0) { kmp_address_eui_64_set(local_addr, mac_params.mac_long); } -#endif - - // For now fixed since not yet support for EA-IE in supplicants - uint8_t addr[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - kmp_address_eui_64_set(local_addr, addr); // Get supplicant address supp_entry_t *entry = kmp_api_data_get(kmp); @@ -422,6 +763,8 @@ static kmp_api_t *ws_pae_auth_kmp_incoming_ind(kmp_service_t *service, kmp_type_ return 0; } sec_prot_keys_init(&supp_entry->sec_keys, pae_auth->gtks, pae_auth->certs); + // Fixes the address of the supplicant to keys + sec_prot_keys_ptk_eui_64_write(&supp_entry->sec_keys, kmp_address_eui_64_get(addr)); } else { // Updates relay address kmp_address_copy(supp_entry->addr, addr); @@ -498,64 +841,116 @@ static void ws_pae_auth_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e // Should not be possible return; } + kmp_service_t *service = kmp_api_service_get(kmp); + pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); + if (!pae_auth) { + // Should not be possible + return; + } - // Gets type - kmp_type_e type = kmp_api_type_get(kmp); + ws_pae_auth_next_kmp_trigger(pae_auth, supp_entry); +} - if (type > IEEE_802_1X_INITIAL_KEY) { - // For EAPOL-key, start EAP-TLS towards supplicant - type = IEEE_802_1X_MKA; - tr_debug("PAE start EAP-TLS, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); - } else if (type == IEEE_802_1X_MKA) { - // After EAP-TLS start 4WH towards supplicant - type = IEEE_802_11_4WH; - // Insert GTK0 - sec_prot_keys_gtk_insert_index_set(supp_entry->sec_keys.gtks, 0); - tr_debug("PAE start 4WH, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); - } else if (type == IEEE_802_11_4WH) { - // After 4WH start GKH towards supplicant - type = IEEE_802_11_GKH; - // Insert GTK1 - sec_prot_keys_gtk_insert_index_set(supp_entry->sec_keys.gtks, 1); - tr_debug("PAE start GKH, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); - } else if (type == IEEE_802_11_GKH) { - tr_debug("PAE authenticated, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); - // After GKH end +static void ws_pae_auth_next_kmp_trigger(pae_auth_t *pae_auth, supp_entry_t *supp_entry) +{ + // Disables KMP retry timer + supp_entry->retry_ticks = 0; + + // Get next protocol based on what keys supplicant has + kmp_type_e next_type = ws_pae_auth_next_protocol_get(supp_entry); + + if (next_type == KMP_TYPE_NONE) { + // All done return; } // Increases waiting time for supplicant authentication ws_pae_lib_supp_timer_ticks_set(supp_entry, WAIT_FOR_AUTHENTICATION_TICKS); - kmp_service_t *service = kmp_api_service_get(kmp); - pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); - if (!pae_auth) { - return; + if (next_type == IEEE_802_1X_MKA) { + /* For EAP-TLS, limits the number of ongoing negotiations. If limit + is reached, authenticator does not initiate EAP-TLS right away. + If previous EAP-TLS negotiation completes before negotiation + trigger timeout, authenticator initiates EAP-TLS towards + supplicant. Otherwise supplicant must re-send initial EAPOL-Key + to try again using its trickle schedule */ + uint16_t ongoing_eap_tls_cnt = ws_pae_lib_supp_list_kmp_count(&pae_auth->active_supp_list, IEEE_802_1X_MKA); + if (ongoing_eap_tls_cnt >= MAX_SIMULTANEOUS_EAP_TLS_NEGOTIATIONS) { + supp_entry->retry_ticks = EAP_TLS_NEGOTIATION_TRIGGER_TIMEOUT; + tr_info("EAP-TLS max ongoing reached, count %i, delayed: eui-64: %s", ongoing_eap_tls_cnt, trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + return; + } } // Create new instance - kmp_api_t *new_kmp = ws_pae_auth_kmp_create_and_start(pae_auth->kmp_service, type, supp_entry); + kmp_api_t *new_kmp = ws_pae_auth_kmp_create_and_start(pae_auth->kmp_service, next_type, supp_entry); if (!new_kmp) { return; } // For EAP-TLS create also TLS in addition to EAP-TLS - if (type == IEEE_802_1X_MKA) { + if (next_type == IEEE_802_1X_MKA) { if (ws_pae_lib_kmp_list_type_get(&supp_entry->kmp_list, TLS_PROT) != NULL) { // TLS already exists, wait for it to be deleted ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, new_kmp); return; } // Create TLS instance */ - if (ws_pae_auth_kmp_create_and_start(service, TLS_PROT, supp_entry) == NULL) { + if (ws_pae_auth_kmp_create_and_start(pae_auth->kmp_service, TLS_PROT, supp_entry) == NULL) { ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, new_kmp); return; } } - kmp_api_create_request(new_kmp, type, supp_entry->addr, &supp_entry->sec_keys); + kmp_api_create_request(new_kmp, next_type, supp_entry->addr, &supp_entry->sec_keys); } +static kmp_type_e ws_pae_auth_next_protocol_get(supp_entry_t *supp_entry) +{ + kmp_type_e next_type = KMP_TYPE_NONE; + sec_prot_keys_t *sec_keys = &supp_entry->sec_keys; + + // Supplicant has indicated that PMK is not valid + if (sec_keys->pmk_mismatch) { + sec_keys->ptk_mismatch = true; + // start EAP-TLS towards supplicant + next_type = IEEE_802_1X_MKA; + tr_info("PAE start EAP-TLS, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + } else if (sec_keys->ptk_mismatch) { + // start 4WH towards supplicant + next_type = IEEE_802_11_4WH; + tr_info("PAE start 4WH, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + } + + int8_t gtk_index = -1; + if (next_type != IEEE_802_1X_MKA) { + // Checks if GTK needs to be inserted + gtk_index = sec_prot_keys_gtk_insert_index_from_gtkl_get(sec_keys); + + // For 4WH insert always a key, in case no other then active + if (next_type == IEEE_802_11_4WH && gtk_index < 0) { + gtk_index = sec_prot_keys_gtk_status_active_get(sec_keys->gtks); + } + } + + if (gtk_index >= 0) { + if (next_type == KMP_TYPE_NONE && gtk_index >= 0) { + // Update just GTK + next_type = IEEE_802_11_GKH; + tr_info("PAE start GKH, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + } + + tr_info("PAE update GTK index: %i, eui-64: %s", gtk_index, trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + } + + if (next_type == KMP_TYPE_NONE) { + tr_info("PAE authenticated, eui-64: %s", trace_array(kmp_address_eui_64_get(supp_entry->addr), 8)); + } + + return next_type; +} + + static kmp_api_t *ws_pae_auth_kmp_create_and_start(kmp_service_t *service, kmp_type_e type, supp_entry_t *supp_entry) { // Create KMP instance for new authentication @@ -594,8 +989,25 @@ static void ws_pae_auth_kmp_api_finished(kmp_api_t *kmp) return; } + pae_auth_t *pae_auth = NULL; + supp_entry_t *retry_supp = NULL; + // When EAP-TLS completes check if there are other supplicants that have requested it lately + if (kmp_api_type_get(kmp) == IEEE_802_1X_MKA) { + kmp_service_t *service = kmp_api_service_get(kmp); + pae_auth = ws_pae_auth_by_kmp_service_get(service); + if (pae_auth) { + retry_supp = ws_pae_lib_supp_list_entry_retry_timer_get(&pae_auth->active_supp_list); + } + } + // Delete KMP ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, kmp); + + if (retry_supp) { + tr_info("PAE next KMP trigger, eui-64: %s", trace_array(kmp_address_eui_64_get(retry_supp->addr), 8)); + ws_pae_auth_next_kmp_trigger(pae_auth, retry_supp); + } + } #endif /* HAVE_PAE_AUTH */ diff --git a/source/6LoWPAN/ws/ws_pae_auth.h b/source/6LoWPAN/ws/ws_pae_auth.h index b33589c661..dec5eb69ec 100644 --- a/source/6LoWPAN/ws/ws_pae_auth.h +++ b/source/6LoWPAN/ws/ws_pae_auth.h @@ -44,13 +44,15 @@ * \param remote_addr remote address * \param remote_port remote port * \param gtks group keys + * \param next_gtks next group keys to be used * \param cert_chain certificate chain + * \param timer_settings timer settings * * \return < 0 failure * \return >= 0 success * */ -int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs); +int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks, sec_prot_gtk_keys_t *next_gtks, const sec_prot_certs_t *certs, timer_settings_t *timer_settings); /** * ws_pae_auth_addresses_set set relay addresses @@ -78,19 +80,126 @@ int8_t ws_pae_auth_addresses_set(protocol_interface_info_entry_t *interface_ptr, int8_t ws_pae_auth_delete(protocol_interface_info_entry_t *interface_ptr); /** - * ws_pae_auth_timer PAE authenticator timer call + * ws_pae_auth_fast_timer PAE authenticator fast timer call * * \param ticks elapsed ticks * */ -void ws_pae_auth_timer(uint16_t ticks); +void ws_pae_auth_fast_timer(uint16_t ticks); + +/** + * ws_pae_auth_slow_timer PAE authenticator slow call + * + * \param seconds elapsed seconds + * + */ +void ws_pae_auth_slow_timer(uint16_t seconds); + +/** + * ws_pae_auth_start start PAE authenticator + * + * \param interface_ptr interface + * + */ +void ws_pae_auth_start(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_auth_gtks_updated indicates that GTKs has been updated + * + * \param interface_ptr interface + * + */ +void ws_pae_auth_gtks_updated(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_auth_gtks_updated indicates that key index has been updated + * + * \param interface_ptr interface + * \param index key index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_auth_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + +/** + * ws_pae_auth_node_keys_remove removes nodes keys + * + * \param interface_ptr interface + * \param eui64 node's EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_auth_node_keys_remove(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui64); + +/** + * ws_pae_auth_node_access_revoke_start start node's access revoke + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_auth_node_access_revoke_start(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_auth_gtk_hash_set GTK hash set callback + * + * \param interface_ptr interface + * \param gtkhash GTK hash, 32 bytes + * + */ +typedef void ws_pae_auth_gtk_hash_set(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash); + +/** + * ws_pae_auth_nw_key_insert network key insert callback + * + * \param interface_ptr interface + * \param gtks group keys + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t ws_pae_auth_nw_key_insert(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks); + +/** + * ws_pae_auth_nw_key_index_set network send key index set callback + * + * \param interface_ptr interface + * \param index network send key index + * + */ +typedef void ws_pae_auth_nw_key_index_set(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + +/** + * ws_pae_auth_cb_register register PAE authenticator callbacks + * + * \param interface_ptr interface + * \param hash_set GTK hash set callback + * \param nw_key_insert network key index callback + * \param nw_key_index_set network send key index callback + * + */ +void ws_pae_auth_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_auth_gtk_hash_set *hash_set, ws_pae_auth_nw_key_insert *nw_key_insert, ws_pae_auth_nw_key_index_set *nw_key_index_set); #else -#define ws_pae_auth_init(interface_ptr, gtks, certs) 1 +#define ws_pae_auth_init(interface_ptr, gtks, next_gtks, certs, timer_settings) 1 #define ws_pae_auth_addresses_set(interface_ptr, local_port, remote_addr, remote_port) 1 #define ws_pae_auth_delete NULL -#define ws_pae_auth_timer NULL +#define ws_pae_auth_cb_register(interface_ptr, hash_set, nw_key_insert, nw_key_index_set) {(void) hash_set;} +#define ws_pae_auth_start(interface_ptr) +#define ws_pae_auth_gtks_updated NULL +#define ws_pae_auth_nw_key_index_update NULL +#define ws_pae_auth_node_keys_remove(interface_ptr, eui64) -1 +#define ws_pae_auth_node_access_revoke_start(interface_ptr) +#define ws_pae_auth_fast_timer NULL +#define ws_pae_auth_slow_timer NULL #endif diff --git a/source/6LoWPAN/ws/ws_pae_controller.c b/source/6LoWPAN/ws/ws_pae_controller.c index edb051ebb4..a00dbd3b71 100644 --- a/source/6LoWPAN/ws/ws_pae_controller.c +++ b/source/6LoWPAN/ws/ws_pae_controller.c @@ -28,8 +28,10 @@ #include "6LoWPAN/ws/ws_pae_controller.h" #include "Security/protocols/sec_prot_certs.h" #include "Security/protocols/sec_prot_keys.h" +#include "6LoWPAN/ws/ws_pae_timers.h" #include "6LoWPAN/ws/ws_pae_supp.h" #include "6LoWPAN/ws/ws_pae_auth.h" +#include "mbedtls/sha256.h" #ifdef HAVE_WS @@ -39,76 +41,110 @@ typedef int8_t ws_pae_delete(protocol_interface_info_entry_t *interface_ptr); typedef void ws_pae_timer(uint16_t ticks); typedef int8_t ws_pae_br_addr_write(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); typedef int8_t ws_pae_br_addr_read(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64); - +typedef void ws_pae_gtks_updated(protocol_interface_info_entry_t *interface_ptr); +typedef int8_t ws_pae_gtk_hash_update(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash); +typedef int8_t ws_pae_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index); typedef struct { - ns_list_link_t link; /**< Link */ - uint8_t target_eui_64[8]; /**< EAPOL target */ - uint16_t target_pan_id; /**< EAPOL target PAN ID */ - uint8_t br_eui_64[8]; /**< Border router EUI-64 */ - sec_prot_gtk_keys_t gtks; /**< GTKs */ - sec_prot_certs_t certs; /**< Certificates */ - protocol_interface_info_entry_t *interface_ptr; /**< List link entry */ - ws_pae_controller_auth_completed *auth_completed; /**< Authentication completed callback, continue bootstrap */ - ws_pae_controller_key_insert *key_insert; /**< Key insert callback */ - ws_pae_delete *pae_delete; /**< PAE delete callback */ - ws_pae_timer *pae_timer; /**< PAE timer callback */ - ws_pae_br_addr_write *pae_br_addr_write; /**< PAE Border router EUI-64 write callback */ - ws_pae_br_addr_read *pae_br_addr_read; /**< PAE Border router EUI-64 read callback */ + uint8_t hash[8]; /**< GTK hash for the key */ + bool installed : 1; /**< Key has been installed on MAC */ + bool fresh : 1; /**< Key is fresh i.e. not used on sending */ +} nw_key_t; + +typedef struct { + ns_list_link_t link; /**< Link */ + uint8_t target_eui_64[8]; /**< EAPOL target */ + uint16_t target_pan_id; /**< EAPOL target PAN ID */ + uint8_t br_eui_64[8]; /**< Border router EUI-64 */ + sec_prot_gtk_keys_t gtks; /**< GTKs */ + sec_prot_gtk_keys_t next_gtks; /**< Next GTKs */ + int8_t gtk_index; /**< GTK index */ + uint8_t gtkhash[32]; /**< GTK hashes */ + sec_prot_certs_t certs; /**< Certificates */ + nw_key_t nw_key[4]; /**< Currently active network keys (on MAC) */ + char *network_name; /**< Network name for GAK generation */ + timer_settings_t timer_settings; /**< Timer settings */ + protocol_interface_info_entry_t *interface_ptr; /**< List link entry */ + ws_pae_controller_auth_completed *auth_completed; /**< Authentication completed callback, continue bootstrap */ + ws_pae_controller_nw_key_set *nw_key_set; /**< Key set callback */ + ws_pae_controller_nw_key_clear *nw_key_clear; /**< Key clear callback */ + ws_pae_controller_nw_send_key_index_set *nw_send_key_index_set; /**< Send key index set callback */ + ws_pae_controller_nw_frame_counter_set *nw_frame_counter_set; /**< Frame counter set callback */ + ws_pae_controller_pan_ver_increment *pan_ver_increment; /**< PAN version increment callback */ + ws_pae_delete *pae_delete; /**< PAE delete callback */ + ws_pae_timer *pae_fast_timer; /**< PAE fast timer callback */ + ws_pae_timer *pae_slow_timer; /**< PAE slow timer callback */ + ws_pae_br_addr_write *pae_br_addr_write; /**< PAE Border router EUI-64 write callback */ + ws_pae_br_addr_read *pae_br_addr_read; /**< PAE Border router EUI-64 read callback */ + ws_pae_gtks_updated *pae_gtks_updated; /**< PAE GTKs updated */ + ws_pae_gtk_hash_update *pae_gtk_hash_update; /**< PAE GTK HASH update */ + ws_pae_nw_key_index_update *pae_nw_key_index_update; /**< PAE NW key index update */ + bool gtks_set : 1; /**< GTKs are set */ + bool gtkhash_set : 1; /**< GTK hashes are set */ + bool key_index_set : 1; /**< NW key index is set */ } pae_controller_t; -static void ws_pae_controller_test_keys_set(sec_prot_gtk_keys_t *gtks); static pae_controller_t *ws_pae_controller_get(protocol_interface_info_entry_t *interface_ptr); +static pae_controller_t *ws_pae_controller_get_or_create(int8_t interface_id); +static void ws_pae_controller_gtk_hash_set(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash); +static int8_t ws_pae_controller_nw_key_check_and_insert(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks); +static void ws_pae_controller_active_nw_key_clear(nw_key_t *nw_key); +static void ws_pae_controller_active_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t index); +static int8_t ws_pae_controller_gak_from_gtk(uint8_t *gak, uint8_t *gtk, char *network_name); +static void ws_pae_controller_nw_key_index_check_and_set(protocol_interface_info_entry_t *interface_ptr, uint8_t index); +static void ws_pae_controller_data_init(pae_controller_t *controller); static NS_LIST_DEFINE(pae_controller_list, pae_controller_t, link); +#if !defined(HAVE_PAE_SUPP) && !defined(HAVE_PAE_AUTH) + static void ws_pae_controller_test_keys_set(sec_prot_gtk_keys_t *gtks) { - uint8_t gtk[2][GTK_LEN]; + uint8_t gtk[GTK_LEN]; // Test data for (int i = 0; i < GTK_LEN; i++) { - gtk[0][i] = 0xcf - i; - gtk[1][i] = 0xef - i; + gtk[i] = 0xcf - i; } - sec_prot_keys_gtk_set(gtks, 0, gtk[0]); - sec_prot_keys_gtk_set(gtks, 1, gtk[1]); - - sec_prot_keys_gtkl_set(gtks, 0xFF); - - sec_prot_keys_gtk_insert_index_set(gtks, 0); + sec_prot_keys_gtk_set(gtks, 0, gtk, GTK_DEFAULT_LIFETIME); } +#else + +#define ws_pae_controller_test_keys_set(gtks); + +#endif + int8_t ws_pae_controller_authenticate(protocol_interface_info_entry_t *interface_ptr) { - if (!interface_ptr) { - return -1; - } - pae_controller_t *controller = ws_pae_controller_get(interface_ptr); if (!controller) { return -1; } - if (ws_pae_supp_authenticate(controller->interface_ptr, controller->target_pan_id, controller->target_eui_64) == PAE_SUPP_NOT_ENABLED) { - // Already authenticated - ws_pae_controller_test_keys_set(&controller->gtks); - - uint8_t index; - uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(&controller->gtks, &index); - - controller->key_insert(controller->interface_ptr, index, gtk); +#ifdef HAVE_PAE_SUPP + // In case test keys are set uses those and does not initiate authentication + if (controller->gtks_set) { + if (sec_prot_keys_gtks_are_updated(&controller->gtks)) { + ws_pae_controller_nw_key_check_and_insert(controller->interface_ptr, &controller->gtks); + sec_prot_keys_gtks_updated_reset(&controller->gtks); + } controller->auth_completed(interface_ptr, true); + return 0; } - /////////// - // For now fixed since not yet support for EA-IE - const uint8_t addr[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - if (controller->pae_br_addr_write) { - controller->pae_br_addr_write(interface_ptr, addr); + if (ws_pae_supp_authenticate(controller->interface_ptr, controller->target_pan_id, controller->target_eui_64) < 0) { + controller->auth_completed(interface_ptr, false); } - //////////////// + +#else + ws_pae_controller_test_keys_set(&controller->gtks); + ws_pae_controller_nw_key_check_and_insert(interface_ptr, &controller->gtks); + ws_pae_controller_nw_key_index_check_and_set(interface_ptr, 0); + + controller->auth_completed(interface_ptr, true); +#endif return 0; } @@ -127,21 +163,32 @@ int8_t ws_pae_controller_authenticator_start(protocol_interface_info_entry_t *in return -1; } +#ifdef HAVE_PAE_AUTH + if (sec_prot_keys_gtks_are_updated(&controller->gtks)) { + ws_pae_auth_gtks_updated(interface_ptr); + if (controller->gtk_index >= 0) { + controller->pae_nw_key_index_update(interface_ptr, controller->gtk_index); + } + sec_prot_keys_gtks_updated_reset(&controller->gtks); + } +#else ws_pae_controller_test_keys_set(&controller->gtks); - - uint8_t index; - uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(&controller->gtks, &index); - - controller->key_insert(controller->interface_ptr, index, gtk); + ws_pae_controller_nw_key_check_and_insert(interface_ptr, &controller->gtks); + ws_pae_controller_nw_key_index_check_and_set(interface_ptr, 0); +#endif if (ws_pae_auth_addresses_set(interface_ptr, local_port, remote_addr, remote_port) < 0) { return -1; } + ws_pae_auth_cb_register(interface_ptr, ws_pae_controller_gtk_hash_set, ws_pae_controller_nw_key_check_and_insert, ws_pae_controller_nw_key_index_check_and_set); + + ws_pae_auth_start(interface_ptr); + return 0; } -int8_t ws_pae_controller_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_controller_auth_completed *completed, ws_pae_controller_key_insert *key_insert) +int8_t ws_pae_controller_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_controller_auth_completed *completed, ws_pae_controller_nw_key_set *nw_key_set, ws_pae_controller_nw_key_clear *nw_key_clear, ws_pae_controller_nw_send_key_index_set *nw_send_key_index_set, ws_pae_controller_nw_frame_counter_set *nw_frame_counter_set, ws_pae_controller_pan_ver_increment *pan_ver_increment) { if (!interface_ptr) { return -1; @@ -153,7 +200,11 @@ int8_t ws_pae_controller_cb_register(protocol_interface_info_entry_t *interface_ } controller->auth_completed = completed; - controller->key_insert = key_insert; + controller->nw_key_set = nw_key_set; + controller->nw_key_clear = nw_key_clear; + controller->nw_send_key_index_set = nw_send_key_index_set; + controller->nw_frame_counter_set = nw_frame_counter_set; + controller->pan_ver_increment = pan_ver_increment; return 0; } @@ -189,6 +240,8 @@ int8_t ws_pae_controller_nw_info_set(protocol_interface_info_entry_t *interface_ return -1; } + controller->network_name = network_name; + return ws_pae_supp_nw_info_set(interface_ptr, pan_id, network_name); } @@ -206,6 +259,202 @@ int8_t ws_pae_controller_nw_key_valid(protocol_interface_info_entry_t *interface return ws_pae_supp_nw_key_valid(interface_ptr); } +static int8_t ws_pae_controller_nw_key_check_and_insert(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks) +{ + int8_t ret = -1; + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return ret; + } + + uint8_t gtkhash[GTK_ALL_HASHES_LEN]; + sec_prot_keys_gtks_hash_generate(gtks, gtkhash); + + nw_key_t *nw_key = controller->nw_key; + + // Delete old keys + uint8_t *gtk_hash_ptr = gtkhash; + for (uint8_t i = 0; i < GTK_NUM; i++) { + // If hash is not set for a key + if (sec_prot_keys_gtk_hash_empty(gtk_hash_ptr)) { + // Deletes the key if it is set + if (!sec_prot_keys_gtk_hash_empty(nw_key[i].hash)) { + tr_info("NW key remove: %i", i); + controller->nw_key_clear(interface_ptr, i); + nw_key[i].installed = false; + } + } + gtk_hash_ptr += GTK_HASH_LEN; + } + + // Insert new keys + gtk_hash_ptr = gtkhash; + for (uint8_t i = 0; i < GTK_NUM; i++) { + // If hash is set for a key + if (!sec_prot_keys_gtk_hash_empty(gtk_hash_ptr)) { + int hash_matches = memcmp(gtk_hash_ptr, nw_key[i].hash, GTK_HASH_LEN); + // If the hash does not match (not set or modified) or not installed + if (hash_matches != 0 || !nw_key[i].installed) { + + memcpy(nw_key[i].hash, gtk_hash_ptr, GTK_HASH_LEN); + + nw_key[i].installed = true; + if (hash_matches != 0) { + nw_key[i].fresh = true; + } + + uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i); + tr_info("NW key set: %i, hash: %s", i, trace_array(nw_key[i].hash, 8)); + uint8_t gak[GTK_LEN]; + if (ws_pae_controller_gak_from_gtk(gak, gtk, controller->network_name) >= 0) { + controller->nw_key_set(interface_ptr, i, i, gak); + ret = 0; + } else { + tr_error("GAK generation failed network name: %s", controller->network_name); + ret = -1; + } + } + } + + gtk_hash_ptr += GTK_HASH_LEN; + } + + return ret; +} + +static void ws_pae_controller_active_nw_key_clear(nw_key_t *nw_key) +{ + memset(nw_key, 0, sizeof(nw_key_t)); + nw_key->installed = false; + nw_key->fresh = false; +} + +static int8_t ws_pae_controller_gak_from_gtk(uint8_t *gak, uint8_t *gtk, char *network_name) +{ +#if defined(HAVE_PAE_SUPP) || defined(HAVE_PAE_AUTH) + uint8_t network_name_len = strlen(network_name); + if (network_name_len == 0) { + return -1; + } + + uint8_t input[network_name_len + GTK_LEN]; + memcpy(input, network_name, network_name_len); + memcpy(input + network_name_len, gtk, GTK_LEN); + + int8_t ret_val = 0; + + mbedtls_sha256_context ctx; + + mbedtls_sha256_init(&ctx); + + if (mbedtls_sha256_starts_ret(&ctx, 0) != 0) { + ret_val = -1; + goto error; + } + + if (mbedtls_sha256_update_ret(&ctx, input, network_name_len + GTK_LEN) != 0) { + ret_val = -1; + goto error; + } + + uint8_t output[32]; + + if (mbedtls_sha256_finish_ret(&ctx, output) != 0) { + ret_val = -1; + goto error; + } + + memcpy(gak, &output[0], 16); + +error: + mbedtls_sha256_free(&ctx); + + return ret_val; +#else + (void) network_name; + memcpy(gak, gtk, 16); + return 0; +#endif +} + +int8_t ws_pae_controller_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + if (controller->pae_nw_key_index_update) { + controller->pae_nw_key_index_update(interface_ptr, index); + } + + return 0; +} + +void ws_pae_controller_nw_keys_remove(protocol_interface_info_entry_t *interface_ptr) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return; + } + + tr_info("NW keys remove"); + + nw_key_t *nw_key = controller->nw_key; + for (uint8_t i = 0; i < GTK_NUM; i++) { + // Deletes the key if it is set + if (!sec_prot_keys_gtk_hash_empty(nw_key[i].hash)) { + tr_info("NW key remove: %i", i); + controller->nw_key_clear(interface_ptr, i); + nw_key[i].installed = false; + } + } +} + +static void ws_pae_controller_nw_key_index_check_and_set(protocol_interface_info_entry_t *interface_ptr, uint8_t index) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return; + } + + if (controller->nw_send_key_index_set) { + tr_info("NW send key index set: %i", index + 1); + controller->nw_send_key_index_set(interface_ptr, index); + controller->nw_frame_counter_set(interface_ptr, 0); + } + + // Do not update PAN version for initial key index set + if (controller->key_index_set) { + if (controller->pan_ver_increment) { + controller->pan_ver_increment(interface_ptr); + } + } else { + controller->key_index_set = true; + } +} + +static void ws_pae_controller_active_nw_key_set(protocol_interface_info_entry_t *cur, uint8_t index) +{ + pae_controller_t *controller = ws_pae_controller_get(cur); + if (!controller) { + return; + } + + if (controller->nw_send_key_index_set) { + controller->nw_send_key_index_set(controller->interface_ptr, index); + + // If index has changed and the key for the index is fresh reset frame counter + if (controller->gtk_index != index && controller->nw_key[index].fresh) { + controller->nw_frame_counter_set(cur, 0); + } + + controller->gtk_index = index; + controller->nw_key[index].fresh = false; + } +} + int8_t ws_pae_controller_init(protocol_interface_info_entry_t *interface_ptr) { if (!interface_ptr) { @@ -221,75 +470,97 @@ int8_t ws_pae_controller_init(protocol_interface_info_entry_t *interface_ptr) return -1; } - memset(controller->target_eui_64, 0, 8); - memset(controller->br_eui_64, 0, 8); controller->interface_ptr = interface_ptr; controller->auth_completed = NULL; - controller->key_insert = NULL; - controller->pae_delete = NULL; - controller->pae_timer = NULL; - controller->pae_br_addr_write = NULL; - controller->pae_br_addr_read = NULL; + controller->nw_key_set = NULL; + controller->nw_key_clear = NULL; + controller->nw_send_key_index_set = NULL; + controller->nw_frame_counter_set = NULL; + controller->pan_ver_increment = NULL; - sec_prot_keys_gtks_init(&controller->gtks); - sec_prot_certs_init(&controller->certs); + ws_pae_controller_data_init(controller); ns_list_add_to_end(&pae_controller_list, controller); return 0; } +static void ws_pae_controller_data_init(pae_controller_t *controller) +{ + memset(controller->target_eui_64, 0, 8); + memset(controller->br_eui_64, 0, 8); + memset(controller->gtkhash, 0, 32); + + ws_pae_controller_active_nw_key_clear(&controller->nw_key[0]); + ws_pae_controller_active_nw_key_clear(&controller->nw_key[1]); + ws_pae_controller_active_nw_key_clear(&controller->nw_key[2]); + ws_pae_controller_active_nw_key_clear(&controller->nw_key[3]); + + controller->target_pan_id = 0xffff; + controller->pae_delete = NULL; + controller->pae_fast_timer = NULL; + controller->pae_slow_timer = NULL; + controller->pae_br_addr_write = NULL; + controller->pae_br_addr_read = NULL; + controller->pae_gtks_updated = NULL; + controller->pae_gtk_hash_update = NULL; + controller->pae_nw_key_index_update = NULL; + controller->gtks_set = false; + controller->gtkhash_set = false; + controller->key_index_set = false; + controller->gtk_index = -1; + controller->network_name = NULL; + sec_prot_keys_gtks_init(&controller->gtks); + sec_prot_keys_gtks_init(&controller->next_gtks); + sec_prot_certs_init(&controller->certs); + ws_pae_timers_settings_init(&controller->timer_settings); +} + int8_t ws_pae_controller_supp_init(protocol_interface_info_entry_t *interface_ptr) { - if (!interface_ptr) { - return -1; - } - pae_controller_t *controller = ws_pae_controller_get(interface_ptr); if (!controller) { return -1; } - if (ws_pae_supp_init(controller->interface_ptr, &controller->certs) < 0) { + if (ws_pae_supp_init(controller->interface_ptr, &controller->certs, &controller->timer_settings) < 0) { return -1; } controller->pae_delete = ws_pae_supp_delete; - controller->pae_timer = ws_pae_supp_timer; + controller->pae_fast_timer = ws_pae_supp_fast_timer; + controller->pae_slow_timer = ws_pae_supp_slow_timer; controller->pae_br_addr_write = ws_pae_supp_border_router_addr_write; controller->pae_br_addr_read = ws_pae_supp_border_router_addr_read; + controller->pae_gtk_hash_update = ws_pae_supp_gtk_hash_update; + controller->pae_nw_key_index_update = ws_pae_supp_nw_key_index_update; - ws_pae_supp_cb_register(controller->interface_ptr, controller->auth_completed, controller->key_insert); + ws_pae_supp_cb_register(controller->interface_ptr, controller->auth_completed, ws_pae_controller_nw_key_check_and_insert, ws_pae_controller_active_nw_key_set); return 0; } int8_t ws_pae_controller_auth_init(protocol_interface_info_entry_t *interface_ptr) { - if (!interface_ptr) { - return -1; - } - pae_controller_t *controller = ws_pae_controller_get(interface_ptr); if (!controller) { return -1; } - if (ws_pae_auth_init(controller->interface_ptr, &controller->gtks, &controller->certs) < 0) { + if (ws_pae_auth_init(controller->interface_ptr, &controller->gtks, &controller->next_gtks, &controller->certs, &controller->timer_settings) < 0) { return -1; } controller->pae_delete = ws_pae_auth_delete; - controller->pae_timer = ws_pae_auth_timer; + controller->pae_fast_timer = ws_pae_auth_fast_timer; + controller->pae_slow_timer = ws_pae_auth_slow_timer; + controller->pae_gtks_updated = ws_pae_auth_gtks_updated; + controller->pae_nw_key_index_update = ws_pae_auth_nw_key_index_update; return 0; } int8_t ws_pae_controller_stop(protocol_interface_info_entry_t *interface_ptr) { - if (!interface_ptr) { - return -1; - } - pae_controller_t *controller = ws_pae_controller_get(interface_ptr); if (!controller) { return -1; @@ -300,6 +571,12 @@ int8_t ws_pae_controller_stop(protocol_interface_info_entry_t *interface_ptr) controller->pae_delete(interface_ptr); } + // Free data + sec_prot_certs_delete(&controller->certs); + + // Init controller data + ws_pae_controller_data_init(controller); + return 0; } @@ -317,9 +594,6 @@ int8_t ws_pae_controller_delete(protocol_interface_info_entry_t *interface_ptr) } ns_list_remove(&pae_controller_list, controller); - - sec_prot_certs_delete(&controller->certs); - ns_dyn_mem_free(controller); return 0; @@ -327,29 +601,135 @@ int8_t ws_pae_controller_delete(protocol_interface_info_entry_t *interface_ptr) int8_t ws_pae_controller_certificate_chain_set(const arm_certificate_chain_entry_s *new_chain) { + if (!new_chain) { + return -1; + } + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { // Delete previous information sec_prot_certs_delete(&entry->certs); + // Adds a trusted certificate from index 0 if (new_chain->cert_chain[0]) { cert_chain_entry_t *root_ca_chain = sec_prot_certs_chain_entry_create(); sec_prot_certs_cert_set(root_ca_chain, 0, (uint8_t *) new_chain->cert_chain[0], new_chain->cert_len[0]); sec_prot_certs_chain_list_add(&entry->certs.trusted_cert_chain_list, root_ca_chain); } - if (new_chain->cert_chain[1] && new_chain->key_chain[1]) { - sec_prot_certs_cert_set(&entry->certs.own_cert_chain, 0, (uint8_t *) new_chain->cert_chain[1], new_chain->cert_len[1]); - uint8_t key_len = strlen((char *) new_chain->key_chain[1]) + 1; - sec_prot_certs_priv_key_set(&entry->certs.own_cert_chain, (uint8_t *) new_chain->key_chain[1], key_len); + // Adds own certificate chain from indexes 1 to 3 + for (uint8_t i = 1; i < SEC_PROT_CERT_CHAIN_DEPTH; i++) { + if (new_chain->cert_chain[i]) { + sec_prot_certs_cert_set(&entry->certs.own_cert_chain, i - 1, (uint8_t *) new_chain->cert_chain[i], new_chain->cert_len[i]); + if (new_chain->key_chain[i]) { + // Will be the key from top certificate in chain after all certificates are added + uint8_t key_len = strlen((char *) new_chain->key_chain[i]) + 1; + sec_prot_certs_priv_key_set(&entry->certs.own_cert_chain, (uint8_t *) new_chain->key_chain[i], key_len); + } + } } } return 0; } +int8_t ws_pae_controller_trusted_certificate_add(const arm_certificate_entry_s *cert) +{ + if (!cert) { + return -1; + } + + int8_t ret = -1; + + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + cert_chain_entry_t *trusted_cert = sec_prot_certs_chain_entry_create(); + sec_prot_certs_cert_set(trusted_cert, 0, (uint8_t *) cert->cert, cert->cert_len); + + if (sec_prot_certs_chain_list_entry_find(&entry->certs.trusted_cert_chain_list, trusted_cert)) { + sec_prot_certs_chain_entry_delete(trusted_cert); + continue; + } + sec_prot_certs_chain_list_add(&entry->certs.trusted_cert_chain_list, trusted_cert); + ret = 0; + } + + return ret; +} + +int8_t ws_pae_controller_trusted_certificate_remove(const arm_certificate_entry_s *cert) +{ + if (!cert) { + return -1; + } + + int8_t ret = -1; + + cert_chain_entry_t *trusted_cert = sec_prot_certs_chain_entry_create(); + sec_prot_certs_cert_set(trusted_cert, 0, (uint8_t *) cert->cert, cert->cert_len); + + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + cert_chain_entry_t *removed_cert = sec_prot_certs_chain_list_entry_find(&entry->certs.trusted_cert_chain_list, trusted_cert); + if (removed_cert) { + sec_prot_certs_chain_list_entry_delete(&entry->certs.trusted_cert_chain_list, removed_cert); + ret = 0; + } + } + + sec_prot_certs_chain_entry_delete(trusted_cert); + + return ret; +} + +int8_t ws_pae_controller_certificate_revocation_list_add(const arm_cert_revocation_list_entry_s *crl) +{ + if (!crl) { + return -1; + } + + int8_t ret = -1; + + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + cert_revocat_list_entry_t *cert_revoc_list = sec_prot_certs_revocat_list_entry_create(); + sec_prot_certs_revocat_list_set(cert_revoc_list, crl->crl, crl->crl_len); + + if (sec_prot_certs_revocat_lists_entry_find(&entry->certs.cert_revocat_lists, cert_revoc_list)) { + sec_prot_certs_revocat_list_entry_delete(cert_revoc_list); + continue; + } + + sec_prot_certs_revocat_lists_add(&entry->certs.cert_revocat_lists, cert_revoc_list); + ret = 0; + } + + return ret; +} + +int8_t ws_pae_controller_certificate_revocation_list_remove(const arm_cert_revocation_list_entry_s *crl) +{ + if (!crl) { + return -1; + } + + int8_t ret = -1; + + cert_revocat_list_entry_t *cert_revoc_list = sec_prot_certs_revocat_list_entry_create(); + sec_prot_certs_revocat_list_set(cert_revoc_list, crl->crl, crl->crl_len); + + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + cert_revocat_list_entry_t *removed_cert_revoc_list = sec_prot_certs_revocat_lists_entry_find(&entry->certs.cert_revocat_lists, cert_revoc_list); + if (removed_cert_revoc_list) { + sec_prot_certs_revocat_lists_entry_delete(&entry->certs.cert_revocat_lists, removed_cert_revoc_list); + ret = 0; + } + } + + sec_prot_certs_revocat_list_entry_delete(cert_revoc_list); + + return ret; +} + int8_t ws_pae_controller_border_router_addr_write(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64) { - if (!interface_ptr || !eui_64) { + if (!eui_64) { return -1; } @@ -365,12 +745,11 @@ int8_t ws_pae_controller_border_router_addr_write(protocol_interface_info_entry_ } return 0; - } int8_t ws_pae_controller_border_router_addr_read(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64) { - if (!interface_ptr || !eui_64) { + if (!eui_64) { return -1; } @@ -388,11 +767,197 @@ int8_t ws_pae_controller_border_router_addr_read(protocol_interface_info_entry_t return 0; } -void ws_pae_controller_timer(uint16_t ticks) +int8_t ws_pae_controller_gtk_update(int8_t interface_id, uint8_t *gtk[4]) +{ + if (!gtk) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + // Removes keys set as not used + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (!gtk[i]) { + sec_prot_keys_gtk_clear(&controller->gtks, i); + } + } + + // Inserts new keys + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (gtk[i]) { + uint32_t lifetime = sec_prot_keys_gtk_install_order_last_lifetime_get(&controller->gtks); + lifetime += controller->timer_settings.gtk_expire_offset; + if (sec_prot_keys_gtk_set(&controller->gtks, i, gtk[i], lifetime) >= 0) { + tr_info("GTK set index: %i, lifetime %"PRIu32", system time: %"PRIu32"", i, lifetime, protocol_core_monotonic_time / 10); + } + } + } + + // Notifies PAE authenticator that GTKs have been updated */ + if (controller->pae_gtks_updated) { + controller->pae_gtks_updated(controller->interface_ptr); + } + + return 0; +} + +int8_t ws_pae_controller_next_gtk_update(int8_t interface_id, uint8_t *gtk[4]) +{ + if (!gtk) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + // Inserts new keys and removed keys set as not used + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (gtk[i]) { + sec_prot_keys_gtk_set(&controller->next_gtks, i, gtk[i], 0); + } else { + sec_prot_keys_gtk_clear(&controller->next_gtks, i); + } + } + + return 0; +} + +int8_t ws_pae_controller_active_key_update(int8_t interface_id, uint8_t index) +{ + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + controller->gtk_index = index; + + if (controller->pae_nw_key_index_update) { + controller->pae_nw_key_index_update(controller->interface_ptr, index); + } + + return 0; +} + +int8_t ws_pae_controller_key_lifetime_update(int8_t interface_id, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime) +{ + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + ws_pae_timers_lifetime_set(&controller->timer_settings, gtk_lifetime, pmk_lifetime, ptk_lifetime); + + return 0; +} + +int8_t ws_pae_controller_gtk_time_settings_update(int8_t interface_id, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint8_t new_install_req, uint32_t max_mismatch) +{ + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + ws_pae_timers_gtk_time_settings_set(&controller->timer_settings, revocat_lifetime_reduct, new_activation_time, new_install_req, max_mismatch); + + return 0; +} + +int8_t ws_pae_controller_node_keys_remove(int8_t interface_id, uint8_t *eui_64) +{ +#ifndef HAVE_PAE_AUTH + (void) eui_64; +#endif + + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + return ws_pae_auth_node_keys_remove(controller->interface_ptr, eui_64); +} + +int8_t ws_pae_controller_node_access_revoke_start(int8_t interface_id) +{ + pae_controller_t *controller = ws_pae_controller_get_or_create(interface_id); + if (!controller) { + return -1; + } + + ws_pae_auth_node_access_revoke_start(controller->interface_ptr); + + return -1; +} + +static void ws_pae_controller_gtk_hash_set(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return; + } + + memcpy(controller->gtkhash, gtkhash, 32); + + tr_info("GTK hash set %s %s %s %s", + trace_array(>khash[0], 8), + trace_array(>khash[8], 8), + trace_array(>khash[16], 8), + trace_array(>khash[24], 8)); + + // Do not update PAN version for initial hash set + if (controller->gtkhash_set) { + if (controller->pan_ver_increment) { + controller->pan_ver_increment(interface_ptr); + } + } else { + controller->gtkhash_set = true; + } +} + +uint8_t *ws_pae_controller_gtk_hash_ptr_get(protocol_interface_info_entry_t *interface_ptr) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return NULL; + } + + return controller->gtkhash; +} + +int8_t ws_pae_controller_gtk_hash_update(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash) +{ + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + memcpy(controller->gtkhash, gtkhash, 32); + + if (controller->pae_gtk_hash_update) { + return controller->pae_gtk_hash_update(interface_ptr, gtkhash); + } + + return 0; +} + +void ws_pae_controller_fast_timer(uint16_t ticks) { ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { - if (entry->pae_timer) { - entry->pae_timer(ticks); + if (entry->pae_fast_timer) { + entry->pae_fast_timer(ticks); + } + } +} + +void ws_pae_controller_slow_timer(uint16_t seconds) +{ + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + if (entry->pae_slow_timer) { + entry->pae_slow_timer(seconds); } } } @@ -408,5 +973,24 @@ static pae_controller_t *ws_pae_controller_get(protocol_interface_info_entry_t * return NULL; } +static pae_controller_t *ws_pae_controller_get_or_create(int8_t interface_id) +{ + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur) { + return NULL; + } + + pae_controller_t *controller = ws_pae_controller_get(cur); + + if (!controller) { + if (ws_pae_controller_init(cur) < 0) { + return NULL; + } + controller = ws_pae_controller_get(cur); + } + + return controller; +} + #endif /* HAVE_WS */ diff --git a/source/6LoWPAN/ws/ws_pae_controller.h b/source/6LoWPAN/ws/ws_pae_controller.h index db1115534f..c7d75d3610 100644 --- a/source/6LoWPAN/ws/ws_pae_controller.h +++ b/source/6LoWPAN/ws/ws_pae_controller.h @@ -124,6 +124,50 @@ int8_t ws_pae_controller_delete(protocol_interface_info_entry_t *interface_ptr); */ int8_t ws_pae_controller_certificate_chain_set(const arm_certificate_chain_entry_s *chain); +/** + * ws_pae_controller_trusted_certificate_add add trusted certificate + * + * \param cert trusted certificate + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_trusted_certificate_add(const arm_certificate_entry_s *cert); + +/** + * ws_pae_controller_trusted_certificate_remove remove trusted certificate + * + * \param cert trusted certificate + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_trusted_certificate_remove(const arm_certificate_entry_s *cert); + +/** + * ws_pae_controller_certificate_revocation_list_add add certification revocation list + * + * \param crl certification revocation list + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_certificate_revocation_list_add(const arm_cert_revocation_list_entry_s *crl); + +/** + * ws_pae_controller_certificate_revocation_list_remove remove certification revocation list + * + * \param crl certification revocation list + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_certificate_revocation_list_remove(const arm_cert_revocation_list_entry_s *crl); + /** * ws_pae_controller_nw_info_set set network information * @@ -173,14 +217,172 @@ int8_t ws_pae_controller_border_router_addr_write(protocol_interface_info_entry_ int8_t ws_pae_controller_border_router_addr_read(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64); /** - * ws_pae_controller_key_insert new GTK key available callback + * ws_pae_controller_gtk_update update GTKs (test interface) * - * \param interface_ptr interface - * \param gtk_index index of the new GTK key - * \param gtk new GTK key + * \param interface_id interface identifier + * \param gtk GTK array, if GTK is not set, pointer for the index shall be NULL. + * + * \return < 0 failure + * \return >= 0 success * */ -typedef void ws_pae_controller_key_insert(protocol_interface_info_entry_t *interface_ptr, uint8_t gtk_index, uint8_t *gtk); +int8_t ws_pae_controller_gtk_update(int8_t interface_id, uint8_t *gtk[4]); + +/** + * ws_pae_controller_next_gtk_update update next GTKs used during GTK lifecycle (test interface) + * + * \param interface_id interface identifier + * \param gtk GTK array, if GTK is not set, pointer for the index shall be NULL. + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_next_gtk_update(int8_t interface_id, uint8_t *gtk[4]); + +/** + * ws_pae_controller_key_lifetime_update update key lifetime + * + * \param interface_id interface identifier + * \param gtk_lifetime GTK lifetime + * \param pmk_lifetime PMK lifetime + * \param ptk_lifetime PTK lifetime + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_key_lifetime_update(int8_t interface_id, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime); + +/** + * ws_pae_controller_gtk_time_settings_update update GTK time settings + * + * \param interface_id interface identifier + * \param revocat_lifetime_reduct revocation lifetime reduction + * \param new_activation_time new activation time + * \param new_install_req new install required + * \param max_mismatch max mismatch time + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_gtk_time_settings_update(int8_t interface_id, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint8_t new_install_req, uint32_t max_mismatch); + +/** + * ws_pae_controller_node_keys_remove remove node's keys + * + * \param interface_id interface identifier + * \param eui-64 EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_node_keys_remove(int8_t interface_id, uint8_t *eui_64); + +/** + * ws_pae_controller_node_access_revoke_start start node's access revoke + * + * \param interface_id interface identifier + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_node_access_revoke_start(int8_t interface_id); + +/** + * ws_pae_controller_active_key_update update active key (test interface) + * + * \param interface_id interface identifier + * \param index GTK index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_active_key_update(int8_t interface_id, uint8_t index); + +/** + * ws_pae_controller_gtk_hash_ptr_get get pointer to GTK hash storage + * + * \param interface_ptr interface + * + * \return pointer to GTK has storage or NULL + * + */ +uint8_t *ws_pae_controller_gtk_hash_ptr_get(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_gtk_hash_update GTK hash has been updated (on PAN configuration) + * + * \param interface_ptr interface + * \param gtkhash new GTK hash + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_gtk_hash_update(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash); + +/** + * ws_pae_controller_nw_key_index_update key index been updated (on PAN configuration) + * + * \param interface_ptr interface + * \param index key index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + +/** + * ws_pae_controller_nw_keys_remove remove network keys + * + * \param interface_ptr interface + * + */ +void ws_pae_controller_nw_keys_remove(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_nw_key_insert network key insert callback + * + * \param interface_ptr interface + * \param slot key slot (MAC key descriptor), from 0 to 4 + * \param index index of the new network key + * \param key new key + * + */ +typedef void ws_pae_controller_nw_key_set(protocol_interface_info_entry_t *interface_ptr, uint8_t slot, uint8_t index, uint8_t *key); + +/** + * ws_pae_controller_nw_key_clear network key clear callback + * + * \param interface_ptr interface + * \param slot key slot (MAC key descriptor), from 0 to 4 + * + */ +typedef void ws_pae_controller_nw_key_clear(protocol_interface_info_entry_t *interface_ptr, uint8_t slot); + +/** + * ws_pae_controller_nw_send_key_index_set network send key index set callback + * + * \param interface_ptr interface + * \param index index of the key to be used on sending + * + */ +typedef void ws_pae_controller_nw_send_key_index_set(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + +/** + * ws_pae_controller_nw_frame_counter_set network frame counter set callback + * + * \param interface_ptr interface + * \param counter frame counter + * + */ +typedef void ws_pae_controller_nw_frame_counter_set(protocol_interface_info_entry_t *interface_ptr, uint32_t counter); /** * ws_pae_controller_auth_completed authentication completed callback @@ -191,26 +393,47 @@ typedef void ws_pae_controller_key_insert(protocol_interface_info_entry_t *inter */ typedef void ws_pae_controller_auth_completed(protocol_interface_info_entry_t *interface_ptr, bool success); +/** + * ws_pae_controller_pan_ver_increment PAN version increment callback + * + * \param interface_ptr interface + * + */ +typedef void ws_pae_controller_pan_ver_increment(protocol_interface_info_entry_t *interface_ptr); + /** * ws_pae_controller_cb_register register PEA controller callbacks * * \param interface_ptr interface * \param completed authentication completed callback - * \param key_insert GTK key insert callback + * \param nw_key_set network key set callback + * \param nw_key_clear network key clear callback + * \param nw_send_key_index_set network send key index set callback + * \param nw_frame_counter_set network frame counter set callback + * \param pan_ver_increment PAN version increment callback * * \return < 0 failure * \return >= 0 success * */ -int8_t ws_pae_controller_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_controller_auth_completed *completed, ws_pae_controller_key_insert *key_insert); +int8_t ws_pae_controller_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_controller_auth_completed *completed, ws_pae_controller_nw_key_set *nw_key_set, ws_pae_controller_nw_key_clear *nw_key_clear, ws_pae_controller_nw_send_key_index_set *nw_send_key_index_set, ws_pae_controller_nw_frame_counter_set *nw_frame_counter_set, ws_pae_controller_pan_ver_increment *pan_ver_increment); /** - * ws_pae_controller_timer PAE controller timer call + * ws_pae_controller_fast_timer PAE controller fast timer call * * \param ticks elapsed ticks * */ -void ws_pae_controller_timer(uint16_t ticks); +void ws_pae_controller_fast_timer(uint16_t ticks); + + +/** + * ws_pae_controller_slow_timer PAE controller slow timer call + * + * \param seconds elapsed seconds + * + */ +void ws_pae_controller_slow_timer(uint16_t seconds); #else @@ -222,14 +445,16 @@ void ws_pae_controller_timer(uint16_t ticks); #define ws_pae_controller_border_router_addr_write(interface_ptr, eui_64) -1 #define ws_pae_controller_border_router_addr_read(interface_ptr, eui_64) -1 +#define ws_pae_controller_gtk_set(interface_id, gtk) -1 +#define ws_pae_controller_next_gtks_update(interface_id, gtk) -1 + #define ws_pae_controller_init(interface_ptr) 1 #define ws_pae_controller_supp_init(interface_ptr) 1 #define ws_pae_controller_auth_init(interface_ptr) 1 #define ws_pae_controller_stop(interface_ptr) #define ws_pae_controller_delete(interface_ptr) -#define ws_pae_controller_cb_register(interface_ptr, completed, key_insert) 1 -#define ws_pae_controller_timer(ticks) +#define ws_pae_controller_cb_register(interface_ptr, completed, nw_key_set, nw_key_clear, nw_send_key_index_set, pan_ver_increment) 1 #endif diff --git a/source/6LoWPAN/ws/ws_pae_lib.c b/source/6LoWPAN/ws/ws_pae_lib.c index 3d613e10f9..13d311f8f4 100644 --- a/source/6LoWPAN/ws/ws_pae_lib.c +++ b/source/6LoWPAN/ws/ws_pae_lib.c @@ -28,6 +28,7 @@ #include "Security/kmp/kmp_api.h" #include "Security/protocols/sec_prot_certs.h" #include "Security/protocols/sec_prot_keys.h" +#include "6LoWPAN/ws/ws_pae_timers.h" #include "6LoWPAN/ws/ws_pae_lib.h" #ifdef HAVE_WS @@ -208,13 +209,28 @@ bool ws_pae_lib_supp_list_timer_update(supp_list_t *active_supp_list, supp_list_ return timer_running; } +void ws_pae_lib_supp_list_slow_timer_update(supp_list_t *supp_list, timer_settings_t *timer_settings, uint16_t seconds) +{ + ns_list_foreach(supp_entry_t, entry, supp_list) { + if (sec_prot_keys_pmk_lifetime_decrement(&entry->sec_keys, timer_settings->pmk_lifetime, seconds)) { + tr_info("PMK and PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(kmp_address_eui_64_get(entry->addr), 8), protocol_core_monotonic_time / 10); + } + if (sec_prot_keys_ptk_lifetime_decrement(&entry->sec_keys, timer_settings->ptk_lifetime, seconds)) { + tr_info("PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(kmp_address_eui_64_get(entry->addr), 8), protocol_core_monotonic_time / 10); + } + } + +} + void ws_pae_lib_supp_init(supp_entry_t *entry) { ws_pae_lib_kmp_list_init(&entry->kmp_list); entry->addr = 0; memset(&entry->sec_keys, 0, sizeof(sec_prot_keys_t)); entry->ticks = 0; + entry->retry_ticks = 0; entry->active = true; + entry->access_revoked = false; } void ws_pae_lib_supp_delete(supp_entry_t *entry) @@ -235,9 +251,20 @@ bool ws_pae_lib_supp_timer_update(supp_entry_t *entry, uint16_t ticks, ws_pae_li entry->ticks -= ticks; } else { entry->ticks = 0; + entry->retry_ticks = 0; } } + // Updates retry timer + if (entry->retry_ticks > ticks) { + entry->retry_ticks -= ticks; + } else { + if (entry->retry_ticks > 0) { + tr_info("EAP-TLS max ongoing delay timeout eui-64: %s", trace_array(kmp_address_eui_64_get(entry->addr), 8)); + } + entry->retry_ticks = 0; + } + return keep_timer_running; } @@ -274,6 +301,12 @@ void ws_pae_lib_supp_list_to_inactive(supp_list_t *active_supp_list, supp_list_t tr_debug("PAE: to inactive, eui-64: %s", trace_array(kmp_address_eui_64_get(entry->addr), 8)); + if (entry->access_revoked) { + tr_info("Access revoked; deleted, eui-64: %s", trace_array(kmp_address_eui_64_get(entry->addr), 8)); + ws_pae_lib_supp_list_remove(active_supp_list, entry); + return; + } + ns_list_remove(active_supp_list, entry); ns_list_add_to_start(inactive_supp_list, entry); @@ -286,4 +319,35 @@ void ws_pae_lib_supp_list_to_inactive(supp_list_t *active_supp_list, supp_list_t entry->addr = addr; } +uint16_t ws_pae_lib_supp_list_kmp_count(supp_list_t *supp_list, kmp_type_e type) +{ + uint16_t kmp_count = 0; + + ns_list_foreach(supp_entry_t, entry, supp_list) { + ns_list_foreach(kmp_entry_t, kmp_entry, &entry->kmp_list) { + if (kmp_api_type_get(kmp_entry->kmp) == type) { + kmp_count++; + } + } + } + + return kmp_count; +} + +supp_entry_t *ws_pae_lib_supp_list_entry_retry_timer_get(supp_list_t *supp_list) +{ + supp_entry_t *retry_supp = NULL; + + ns_list_foreach(supp_entry_t, entry, supp_list) { + // Finds entry with shortest timeout i.e. oldest one + if (entry->retry_ticks > 0) { + if (!retry_supp || retry_supp->retry_ticks > entry->retry_ticks) { + retry_supp = entry; + } + } + } + + return retry_supp; +} + #endif /* HAVE_WS */ diff --git a/source/6LoWPAN/ws/ws_pae_lib.h b/source/6LoWPAN/ws/ws_pae_lib.h index 6ccc888447..a9d239b950 100644 --- a/source/6LoWPAN/ws/ws_pae_lib.h +++ b/source/6LoWPAN/ws/ws_pae_lib.h @@ -18,6 +18,11 @@ #ifndef WS_PAE_LIB_H_ #define WS_PAE_LIB_H_ +/* + * Port access entity library functions. + * + */ + typedef struct { kmp_api_t *kmp; /**< KMP API */ bool timer_running; /**< Timer running inside KMP */ @@ -31,7 +36,9 @@ typedef struct { kmp_addr_t *addr; /**< EUI-64 (Relay IP address, Relay port) */ sec_prot_keys_t sec_keys; /**< Security keys */ uint32_t ticks; /**< Ticks */ - bool active; /**< Is active */ + uint16_t retry_ticks; /**< Retry ticks */ + bool active : 1; /**< Is active */ + bool access_revoked : 1; /**< Nodes access is revoked */ ns_list_link_t link; /**< Link */ } supp_entry_t; @@ -144,11 +151,6 @@ typedef void ws_pae_lib_kmp_timer_timeout(kmp_api_t *kmp, uint16_t ticks); */ bool ws_pae_lib_kmp_timer_update(kmp_list_t *kmp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout); - - - - - /** * ws_pae_lib_supp_list_init initiates supplicant list * @@ -211,6 +213,16 @@ void ws_pae_lib_supp_list_delete(supp_list_t *supp_list); */ bool ws_pae_lib_supp_list_timer_update(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout); +/** + * ws_pae_lib_supp_list_slow_timer_update updates slow timer on supplicant list + * + * \param supp_list list of supplicants + * \param timer_settings timer settings + * \param seconds seconds + * + */ +void ws_pae_lib_supp_list_slow_timer_update(supp_list_t *supp_list, timer_settings_t *timer_settings, uint16_t seconds); + /** * ws_pae_lib_supp_list_timer_update updates supplicant timers * @@ -268,4 +280,25 @@ void ws_pae_lib_supp_list_to_active(supp_list_t *active_supp_list, supp_list_t * */ void ws_pae_lib_supp_list_to_inactive(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry); +/** + * ws_pae_lib_supp_list_kmp_count counts the number of KMPs of a certain type in a list of supplicants + * + * \param supp_list list of supplicants + * \param type KMP type + * + * \return number of KMPs in the supplicant list + * + */ +uint16_t ws_pae_lib_supp_list_kmp_count(supp_list_t *supp_list, kmp_type_e type); + +/** + * ws_pae_lib_supp_list_entry_retry_timer_get checks if some supplicant has retry timer running + * + * \param supp_list list of supplicants + * + * \return supplicant with retry timer running or NULL if no supplicants with timer running + * + */ +supp_entry_t *ws_pae_lib_supp_list_entry_retry_timer_get(supp_list_t *supp_list); + #endif /* WS_PAE_AUTH_H_ */ diff --git a/source/6LoWPAN/ws/ws_pae_nvm_data.c b/source/6LoWPAN/ws/ws_pae_nvm_data.c index b528ef267a..bf07505771 100644 --- a/source/6LoWPAN/ws/ws_pae_nvm_data.c +++ b/source/6LoWPAN/ws/ws_pae_nvm_data.c @@ -104,8 +104,7 @@ int8_t ws_pae_nvm_store_nw_info_tlv_read(nvm_tlv_entry_t *tlv_entry, uint16_t *p if (*tlv++ == 1) { /* GTK is set */ uint32_t lifetime = common_read_32_bit(tlv); tlv += 4; - sec_prot_keys_gtk_set(gtks, i, tlv); - sec_prot_keys_gtk_lifetime_set(gtks, i, lifetime); + sec_prot_keys_gtk_set(gtks, i, tlv, lifetime); tlv += GTK_LEN; } else { tlv += 4 + GTK_LEN; diff --git a/source/6LoWPAN/ws/ws_pae_nvm_data.h b/source/6LoWPAN/ws/ws_pae_nvm_data.h index 24d4af8a48..109017a700 100644 --- a/source/6LoWPAN/ws/ws_pae_nvm_data.h +++ b/source/6LoWPAN/ws/ws_pae_nvm_data.h @@ -18,6 +18,12 @@ #ifndef WS_PAE_NVM_DATA_H_ #define WS_PAE_NVM_DATA_H_ +/* + * Port access entity non-volatile memory (NVM) data module. Module is used + * to create and parse PAE NVM data TLVs. + * + */ + /** * ws_pae_nvm_store_nw_info_tlv_create create NVM network info TLV * diff --git a/source/6LoWPAN/ws/ws_pae_nvm_store.h b/source/6LoWPAN/ws/ws_pae_nvm_store.h index 4fd0bf60b1..130ae73f03 100644 --- a/source/6LoWPAN/ws/ws_pae_nvm_store.h +++ b/source/6LoWPAN/ws/ws_pae_nvm_store.h @@ -18,6 +18,12 @@ #ifndef WS_PAE_NVM_STORE_H_ #define WS_PAE_NVM_STORE_H_ +/* + * Port access entity non-volatile memory (NVM) storage module. Module is used + * to write and read PAE NVM data TLVs to/from filesystem. + * + */ + // tag + length #define NVM_TLV_FIXED_LEN 4 diff --git a/source/6LoWPAN/ws/ws_pae_supp.c b/source/6LoWPAN/ws/ws_pae_supp.c index 73ba16943b..fea7d9ce2b 100644 --- a/source/6LoWPAN/ws/ws_pae_supp.c +++ b/source/6LoWPAN/ws/ws_pae_supp.c @@ -17,6 +17,7 @@ #include "nsconfig.h" #include +#include #include "ns_types.h" #include "ns_list.h" #include "ns_trace.h" @@ -41,6 +42,7 @@ #include "Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h" #include "Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.h" #include "6LoWPAN/ws/ws_pae_controller.h" +#include "6LoWPAN/ws/ws_pae_timers.h" #include "6LoWPAN/ws/ws_pae_supp.h" #include "6LoWPAN/ws/ws_pae_lib.h" #include "6LoWPAN/ws/ws_pae_nvm_store.h" @@ -59,10 +61,17 @@ #define PAE_TASKLET_TIMER 3 // Wait for for authenticator to continue with authentication (e.g. after EAP-TLS to initiate 4WH) -#define WAIT_FOR_AUTHENTICATION_TICKS 30 * 10 // 30 seconds +#define WAIT_FOR_AUTHENTICATION_TICKS 30 * 10 // 30 seconds + +// Wait for re-authentication after GTK update +#define WAIT_FOR_REAUTHENTICATION_TICKS 120 * 10 // 120 seconds // How many times in maximum stored keys are used for authentication -#define STORED_KEYS_MAXIMUM_USE_COUNT 2 +#define STORED_KEYS_MAXIMUM_USE_COUNT 2 + +// Delay for sending the initial EAPOL-Key +#define INITIAL_KEY_TIMER_MIN 3 +#define INITIAL_KEY_TIMER_MAX 30 const char *NW_INFO_FILE = "pae_nw_info"; const char *KEYS_FILE = "pae_keys"; @@ -70,7 +79,8 @@ const char *KEYS_FILE = "pae_keys"; typedef struct { char network_name[33]; /**< Network name for keys */ sec_prot_gtk_keys_t *gtks; /**< Link to GTKs */ - uint16_t pan_id; /**< PAN ID for keys */ + uint16_t new_pan_id; /**< new PAN ID indicated by bootstrap */ + uint16_t key_pan_id; /**< PAN ID for keys */ bool updated : 1; /**< Network info has been updated */ } sec_prot_keys_nw_info_t; @@ -79,27 +89,33 @@ typedef struct { kmp_service_t *kmp_service; /**< KMP service */ protocol_interface_info_entry_t *interface_ptr; /**< Interface */ ws_pae_supp_auth_completed *auth_completed; /**< Authentication completed callback, continue bootstrap */ - ws_pae_supp_key_insert *key_insert; /**< Key insert callback */ + ws_pae_supp_nw_key_insert *nw_key_insert; /**< Key insert callback */ + ws_pae_supp_nw_key_index_set *nw_key_index_set; /**< Key index set callback */ supp_entry_t entry; /**< Supplicant data */ kmp_addr_t target_addr; /**< EAPOL target (parent) address */ - trickle_t auth_trickle_timer; /**< Trickle timer for re-sending initial EAPOL-key */ + uint16_t initial_key_timer; /**< Timer to trigger initial EAPOL-Key */ + trickle_t auth_trickle_timer; /**< Trickle timer for re-sending initial EAPOL-key or for GTK mismatch */ + trickle_params_t auth_trickle_params; /**< Trickle parameters for initial EAPOL-key or for GTK mismatch */ sec_prot_gtk_keys_t gtks; /**< GTKs */ uint8_t new_br_eui_64[8]; /**< Border router EUI-64 indicated by bootstrap */ - uint8_t ptk_eui_64[8]; /**< Border router EUI-64 used on PTK generation */ sec_prot_keys_nw_info_t sec_keys_nw_info; /**< Security keys network information */ + timer_settings_t *timer_settings; /**< Timer settings */ uint8_t nw_keys_used_cnt; /**< How many times bootstrap has been tried with current keys */ - bool auth_trickle_running : 1; /**< Trickle timer running */ + bool auth_trickle_running : 1; /**< Initial EAPOL-Key Trickle timer running */ bool auth_requested : 1; /**< Authentication has been requested */ bool timer_running : 1; /**< Timer is running */ bool new_br_eui_64_set : 1; /**< Border router address has been set */ + bool new_br_eui_64_fresh : 1; /**< Border router address is fresh (set during this authentication attempt) */ } pae_supp_t; -// Wi-SUN specification states initial retransmission to be around 5 minutes and maximum 60 minutes -static const trickle_params_t auth_trickle_params = { - .Imin = 30 * 10, /* 30s; ticks are 100ms */ - .Imax = 60 * 10, /* 60s */ - .k = 0, /* infinity - no consistency checking */ - .TimerExpirations = 3 + +#define TRICKLE_IMIN_180_SECS 180 + +static trickle_params_t initial_eapol_key_trickle_params = { + .Imin = TRICKLE_IMIN_180_SECS, /* 180 second; ticks are 1 second */ + .Imax = TRICKLE_IMIN_180_SECS << 1, /* 360 second */ + .k = 0, /* infinity - no consistency checking */ + .TimerExpirations = 3 }; static void ws_pae_supp_free(pae_supp_t *pae_supp); @@ -122,6 +138,7 @@ static kmp_api_t *ws_pae_supp_kmp_service_api_get(kmp_service_t *service, kmp_ap static kmp_api_t *ws_pae_supp_kmp_incoming_ind(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr); static kmp_api_t *ws_pae_supp_kmp_create_and_start(kmp_service_t *service, kmp_type_e type, pae_supp_t *pae_supp); static int8_t ws_pae_supp_eapol_pdu_address_check(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); +static int8_t ws_pae_supp_parent_eui_64_get(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64); static void ws_pae_supp_kmp_api_create_confirm(kmp_api_t *kmp, kmp_result_e result); static void ws_pae_supp_kmp_api_create_indication(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr); @@ -152,29 +169,33 @@ int8_t ws_pae_supp_authenticate(protocol_interface_info_entry_t *interface_ptr, // Delete GTKs sec_prot_keys_gtks_init(pae_supp->sec_keys_nw_info.gtks); + /* PAN ID has changed, delete key data associated with border router + i.e PMK, PTK, EA-IE data (border router EUI-64) */ + if (pae_supp->sec_keys_nw_info.key_pan_id != 0xFFFF && pae_supp->sec_keys_nw_info.key_pan_id != dest_pan_id) { + sec_prot_keys_pmk_delete(&pae_supp->entry.sec_keys); + sec_prot_keys_ptk_delete(&pae_supp->entry.sec_keys); + sec_prot_keys_ptk_eui_64_delete(&pae_supp->entry.sec_keys); + } + + pae_supp->sec_keys_nw_info.key_pan_id = dest_pan_id; + // Prepare to receive new border router address - pae_supp->new_br_eui_64_set = false; + pae_supp->new_br_eui_64_fresh = false; // Stores target/parent address kmp_address_init(KMP_ADDR_EUI_64, &pae_supp->target_addr, dest_eui_64); // Sets target address in use pae_supp->entry.addr = (kmp_addr_t *) &pae_supp->target_addr; - // Sends initial EAPOL-Key message - if (ws_pae_supp_initial_key_send(pae_supp) < 0) { - pae_supp->auth_completed(interface_ptr, false); - } + pae_supp->auth_requested = true; - // Starts trickle - trickle_start(&pae_supp->auth_trickle_timer, &auth_trickle_params); - pae_supp->auth_trickle_running = true; + // Randomizes the sending of initial EAPOL-Key messsage + pae_supp->initial_key_timer = randLIB_get_random_in_range(INITIAL_KEY_TIMER_MIN, INITIAL_KEY_TIMER_MAX); // Starts supplicant timer ws_pae_supp_timer_start(pae_supp); - pae_supp->auth_requested = true; - - tr_debug("PAE active"); + tr_debug("PAE active, timer %i", pae_supp->initial_key_timer); return 1; } @@ -187,8 +208,8 @@ int8_t ws_pae_supp_nw_info_set(protocol_interface_info_entry_t *interface_ptr, u } // PAN ID has been modified - if (pan_id != 0xffff && pan_id != pae_supp->sec_keys_nw_info.pan_id) { - pae_supp->sec_keys_nw_info.pan_id = pan_id; + if (pan_id != 0xffff && pan_id != pae_supp->sec_keys_nw_info.new_pan_id) { + pae_supp->sec_keys_nw_info.new_pan_id = pan_id; pae_supp->sec_keys_nw_info.updated = true; } @@ -211,6 +232,7 @@ int8_t ws_pae_supp_border_router_addr_write(protocol_interface_info_entry_t *int memcpy(pae_supp->new_br_eui_64, eui_64, 8); pae_supp->new_br_eui_64_set = true; + pae_supp->new_br_eui_64_fresh = true; return 0; } @@ -222,11 +244,12 @@ int8_t ws_pae_supp_border_router_addr_read(protocol_interface_info_entry_t *inte return -1; } - if (!pae_supp->entry.sec_keys.ptk_eui_64 || !pae_supp->entry.sec_keys.ptk_eui_64_set) { + uint8_t *br_eui_64 = sec_prot_keys_ptk_eui_64_get(&pae_supp->entry.sec_keys); + if (!br_eui_64) { return -1; } - memcpy(eui_64, pae_supp->entry.sec_keys.ptk_eui_64, 8); + memcpy(eui_64, br_eui_64, 8); return 0; } @@ -238,6 +261,8 @@ int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr) return -1; } + tr_info("NW key valid"); + // Stored keys are valid pae_supp->nw_keys_used_cnt = 0; @@ -247,6 +272,69 @@ int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr) return 0; } +int8_t ws_pae_supp_gtk_hash_update(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + // Check GTK hashes and initiate EAPOL procedure if mismatch is detected */ + gtk_mismatch_e mismatch = sec_prot_keys_gtks_hash_update(&pae_supp->gtks, gtkhash); + if (mismatch > GTK_NO_MISMATCH) { + tr_info("GTK hash update %s %s %s %s", + trace_array(>khash[0], 8), + trace_array(>khash[8], 8), + trace_array(>khash[16], 8), + trace_array(>khash[24], 8)); + + // Mismatch, initiate EAPOL + if (!pae_supp->auth_trickle_running) { + uint8_t timer_expirations = 3; + // For GTK lifetime mismatch send only once + if (mismatch == GTK_LIFETIME_MISMATCH) { + timer_expirations = 1; + } + + pae_supp->auth_trickle_params.Imin = pae_supp->timer_settings->gtk_request_imin; + pae_supp->auth_trickle_params.Imax = pae_supp->timer_settings->gtk_request_imax; + pae_supp->auth_trickle_params.k = 0; + pae_supp->auth_trickle_params.TimerExpirations = timer_expirations; + + // Starts trickle + trickle_start(&pae_supp->auth_trickle_timer, &pae_supp->auth_trickle_params); + pae_supp->auth_trickle_running = true; + + // Starts supplicant timer + ws_pae_supp_timer_start(pae_supp); + + tr_info("GTK update start imin: %i, imax: %i, max mismatch: %i, tr time: %i", pae_supp->timer_settings->gtk_request_imin, pae_supp->timer_settings->gtk_request_imax, pae_supp->timer_settings->gtk_max_mismatch, pae_supp->auth_trickle_timer.t); + } + } + + // Modify keys + pae_supp->nw_key_insert(pae_supp->interface_ptr, pae_supp->sec_keys_nw_info.gtks); + + return 0; +} + +int8_t ws_pae_supp_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + if (sec_prot_keys_gtk_status_active_set(&pae_supp->gtks, index) >= 0) { + tr_info("NW send key index set: %i", index + 1); + pae_supp->nw_key_index_set(interface_ptr, index); + } else { + tr_info("NW send key index: %i, no changes", index + 1); + } + + return 0; +} + static void ws_pae_supp_nvm_update(pae_supp_t *pae_supp) { // Check if NW info or GTKs have been changed @@ -268,7 +356,7 @@ static int8_t ws_pae_supp_nvm_nw_info_write(pae_supp_t *pae_supp) nvm_tlv_list_t tlv_list; ns_list_init(&tlv_list); - nvm_tlv_entry_t *tlv_entry = ws_pae_nvm_store_nw_info_tlv_create(pae_supp->sec_keys_nw_info.pan_id, + nvm_tlv_entry_t *tlv_entry = ws_pae_nvm_store_nw_info_tlv_create(pae_supp->sec_keys_nw_info.key_pan_id, pae_supp->sec_keys_nw_info.network_name, &pae_supp->gtks); ns_list_add_to_end(&tlv_list, tlv_entry); @@ -288,7 +376,7 @@ static int8_t ws_pae_supp_nvm_nw_info_read(pae_supp_t *pae_supp) ws_pae_nvm_store_tlv_file_read(NW_INFO_FILE, &tlv_list); ns_list_foreach_safe(nvm_tlv_entry_t, entry, &tlv_list) { - ws_pae_nvm_store_nw_info_tlv_read(entry, &pae_supp->sec_keys_nw_info.pan_id, + ws_pae_nvm_store_nw_info_tlv_read(entry, &pae_supp->sec_keys_nw_info.key_pan_id, pae_supp->sec_keys_nw_info.network_name, &pae_supp->gtks); ns_list_remove(&tlv_list, entry); @@ -340,11 +428,29 @@ static void ws_pae_supp_authenticate_response(pae_supp_t *pae_supp, bool success static int8_t ws_pae_supp_initial_key_send(pae_supp_t *pae_supp) { + if (!pae_supp->auth_requested) { + // If not making initial authentication updates target (RPL parent) for each EAPOL-key message + uint8_t parent_eui_64[8]; + if (ws_pae_supp_parent_eui_64_get(pae_supp->interface_ptr, parent_eui_64) < 0) { + return -1; + } + + // Stores target/parent address + kmp_address_init(KMP_ADDR_EUI_64, &pae_supp->target_addr, parent_eui_64); + // Sets parent address in use + pae_supp->entry.addr = (kmp_addr_t *) &pae_supp->target_addr; + + ws_pae_lib_supp_timer_ticks_set(&pae_supp->entry, WAIT_FOR_REAUTHENTICATION_TICKS); + tr_info("PAE wait for auth seconds: %i", WAIT_FOR_REAUTHENTICATION_TICKS / 10); + } + kmp_api_t *kmp = ws_pae_supp_kmp_create_and_start(pae_supp->kmp_service, IEEE_802_1X_MKA_KEY, pae_supp); if (!kmp) { return -1; } + tr_info("EAPOL target: %s", trace_array(kmp_address_eui_64_get(pae_supp->entry.addr), 8)); + kmp_api_create_request(kmp, IEEE_802_1X_MKA_KEY, pae_supp->entry.addr, &pae_supp->entry.sec_keys); return 0; @@ -365,34 +471,21 @@ static int8_t ws_pae_supp_nw_keys_valid_check(pae_supp_t *pae_supp, uint16_t pan return -1; } - // First attempt to authenticate, checks if keys exists - if (pae_supp->nw_keys_used_cnt == 0 && pan_id == pae_supp->sec_keys_nw_info.pan_id) { - sec_prot_gtk_keys_t *gtks = pae_supp->sec_keys_nw_info.gtks; - - bool key_inserted = false; - - for (uint8_t i = 0; i < GTK_NUM; i++) { - uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i); - if (gtk) { - // Insert also non-live keys since GTK hash information not yet received - pae_supp->key_insert(pae_supp->interface_ptr, i, gtk); - key_inserted = true; - } - } - - if (key_inserted) { - tr_debug("Keys inserted"); - pae_supp->nw_keys_used_cnt++; - return 0; - } - } - - if (pae_supp->nw_keys_used_cnt == 0) { - return -1; - } else { + /* Checks if keys match to PAN ID and that needed keys exists (PMK, PTK and a GTK), + and calls inserts function that will update the network keys as needed */ + if ((pan_id == pae_supp->sec_keys_nw_info.key_pan_id) && + (sec_prot_keys_gtk_count(pae_supp->sec_keys_nw_info.gtks) > 0) && + (sec_prot_keys_pmk_get(&pae_supp->entry.sec_keys) != NULL) && + (sec_prot_keys_ptk_get(&pae_supp->entry.sec_keys) != NULL)) { tr_debug("Existing keys used, counter %i", pae_supp->nw_keys_used_cnt); + if (pae_supp->nw_key_insert(pae_supp->interface_ptr, pae_supp->sec_keys_nw_info.gtks) >= 0) { + tr_debug("Keys inserted"); + } pae_supp->nw_keys_used_cnt++; return 0; + } else { + pae_supp->nw_keys_used_cnt = 0; + return -1; } } @@ -405,10 +498,12 @@ static void ws_pae_supp_keys_nw_info_init(sec_prot_keys_nw_info_t *sec_keys_nw_i memset(sec_keys_nw_info, 0, sizeof(sec_prot_keys_nw_info_t)); sec_keys_nw_info->gtks = gtks; + sec_keys_nw_info->new_pan_id = 0xFFFF; + sec_keys_nw_info->key_pan_id = 0xFFFF; sec_keys_nw_info->updated = false; } -void ws_pae_supp_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_supp_auth_completed *completed, ws_pae_supp_key_insert *key_insert) +void ws_pae_supp_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_supp_auth_completed *completed, ws_pae_supp_nw_key_insert *nw_key_insert, ws_pae_supp_nw_key_index_set *nw_key_index_set) { pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); if (!pae_supp) { @@ -416,10 +511,11 @@ void ws_pae_supp_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_ } pae_supp->auth_completed = completed; - pae_supp->key_insert = key_insert; + pae_supp->nw_key_insert = nw_key_insert; + pae_supp->nw_key_index_set = nw_key_index_set; } -int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const sec_prot_certs_t *certs) +int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const sec_prot_certs_t *certs, timer_settings_t *timer_settings) { if (!interface_ptr) { return -1; @@ -435,10 +531,17 @@ int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const se } pae_supp->interface_ptr = interface_ptr; - pae_supp->auth_completed = 0; - pae_supp->key_insert = 0; - pae_supp->auth_trickle_running = false; + pae_supp->auth_completed = NULL; + pae_supp->nw_key_insert = NULL; + pae_supp->nw_key_index_set = NULL; + pae_supp->initial_key_timer = 0; pae_supp->nw_keys_used_cnt = 0; + pae_supp->timer_settings = timer_settings; + pae_supp->auth_trickle_running = false; + pae_supp->auth_requested = false; + pae_supp->timer_running = false; + pae_supp->new_br_eui_64_set = false; + pae_supp->new_br_eui_64_fresh = false; ws_pae_lib_supp_init(&pae_supp->entry); @@ -449,8 +552,6 @@ int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const se sec_prot_keys_gtks_init(&pae_supp->gtks); sec_prot_keys_init(&pae_supp->entry.sec_keys, &pae_supp->gtks, certs); memset(pae_supp->new_br_eui_64, 0, 8); - memset(pae_supp->ptk_eui_64, 0, 8); - sec_prot_keys_ptk_eui_64_set(&pae_supp->entry.sec_keys, pae_supp->ptk_eui_64); pae_supp->kmp_service = kmp_service_create(); if (!pae_supp->kmp_service) { @@ -619,29 +720,18 @@ static void ws_pae_supp_tasklet_handler(arm_event_s *event) } } -void ws_pae_supp_timer(uint16_t ticks) +void ws_pae_supp_fast_timer(uint16_t ticks) { ns_list_foreach(pae_supp_t, pae_supp, &pae_supp_list) { if (!ws_pae_supp_timer_running(pae_supp)) { continue; } - // Checks whether initial EAPOL-Key message needs to be re-send - if (pae_supp->auth_trickle_running) { - if (trickle_timer(&pae_supp->auth_trickle_timer, &auth_trickle_params, ticks)) { - ws_pae_supp_initial_key_send(pae_supp); - } - // Maximum number of trickle expires, authentication fails - if (!trickle_running(&pae_supp->auth_trickle_timer, &auth_trickle_params)) { - ws_pae_supp_authenticate_response(pae_supp, false); - } - } - // Updates KMP timers and supplicant authentication ongoing timer bool running = ws_pae_lib_supp_timer_update(&pae_supp->entry, ticks, kmp_service_timer_if_timeout); // Checks whether timer needs to be active - if (!pae_supp->auth_trickle_running && !running) { + if (!pae_supp->initial_key_timer && !pae_supp->auth_trickle_running && !running) { tr_debug("PAE idle"); // Sets target/parent address to null @@ -655,6 +745,48 @@ void ws_pae_supp_timer(uint16_t ticks) } } +void ws_pae_supp_slow_timer(uint16_t seconds) +{ + ns_list_foreach(pae_supp_t, pae_supp, &pae_supp_list) { + + // Checks whether initial EAPOL-Key message needs to be re-send or new GTK request to be sent + if (pae_supp->auth_trickle_running) { + if (trickle_timer(&pae_supp->auth_trickle_timer, &pae_supp->auth_trickle_params, seconds)) { + ws_pae_supp_initial_key_send(pae_supp); + } + // Maximum number of trickle expires, authentication fails + if (!trickle_running(&pae_supp->auth_trickle_timer, &pae_supp->auth_trickle_params)) { + ws_pae_supp_authenticate_response(pae_supp, false); + } + } + + // Decrements GTK lifetimes + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (!sec_prot_keys_gtk_is_set(&pae_supp->gtks, i)) { + continue; + } + sec_prot_keys_gtk_lifetime_decrement(&pae_supp->gtks, i, seconds); + } + + if (pae_supp->initial_key_timer > 0) { + if (pae_supp->initial_key_timer > seconds) { + pae_supp->initial_key_timer -= seconds; + } else { + pae_supp->initial_key_timer = 0; + + // Sends initial EAPOL-Key message + ws_pae_supp_initial_key_send(pae_supp); + + // Starts trickle + pae_supp->auth_trickle_params = initial_eapol_key_trickle_params; + trickle_start(&pae_supp->auth_trickle_timer, &pae_supp->auth_trickle_params); + pae_supp->auth_trickle_running = true; + } + + } + } +} + static int8_t ws_pae_supp_timer_if_start(kmp_service_t *service, kmp_api_t *kmp) { pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); @@ -720,21 +852,38 @@ static int8_t ws_pae_supp_eapol_pdu_address_check(protocol_interface_info_entry_ } } + // Get parent + uint8_t parent_eui_64[8]; + if (ws_pae_supp_parent_eui_64_get(interface_ptr, parent_eui_64) < 0) { + return -1; + } + + // Message from RPL parent, route to self + if (memcmp(parent_eui_64, eui_64, 8) == 0) { + return 0; + } + + return -1; +} + +static int8_t ws_pae_supp_parent_eui_64_get(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64) +{ rpl_dodag_info_t dodag_info; + if (!interface_ptr->rpl_domain) { + return -1; + } struct rpl_instance *instance = rpl_control_enumerate_instances(interface_ptr->rpl_domain, NULL); if (instance && rpl_control_read_dodag_info(instance, &dodag_info)) { // Get parent const uint8_t *parent_ll_addr = rpl_control_preferred_parent_addr(instance, false); - - // Message from RPL parent, route to self - if (parent_ll_addr && memcmp(&parent_ll_addr[8], eui_64, 8) == 0) { + if (parent_ll_addr) { + memcpy(eui_64, &parent_ll_addr[8], 8); + eui_64[0] |= 0x02; return 0; } - - return -1; } - return 0; + return -1; } static void ws_pae_supp_kmp_service_addr_get(kmp_service_t *service, kmp_api_t *kmp, kmp_addr_t *local_addr, kmp_addr_t *remote_addr) @@ -752,13 +901,23 @@ static void ws_pae_supp_kmp_service_addr_get(kmp_service_t *service, kmp_api_t * kmp_address_eui_64_set(local_addr, mac_params.mac_long); } - if (pae_supp->new_br_eui_64_set) { + // BR address has been received during authentication attempt + if (pae_supp->new_br_eui_64_fresh) { kmp_address_eui_64_set(remote_addr, pae_supp->new_br_eui_64); - } else if (pae_supp->entry.sec_keys.ptk_eui_64_set) { - kmp_address_eui_64_set(remote_addr, pae_supp->entry.sec_keys.ptk_eui_64); } else { - memset(remote_addr, 0, 8); - tr_error("No border router EUI-64"); + uint8_t *eui_64 = sec_prot_keys_ptk_eui_64_get(&pae_supp->entry.sec_keys); + // BR address is set on security keys (confirmed using 4WH) + if (eui_64) { + kmp_address_eui_64_set(remote_addr, eui_64); + } else { + // For initial EAPOL key, if BR address has been received during previous attempt, generate PMKID using it + if (pae_supp->new_br_eui_64_set && kmp_api_type_get(kmp) >= IEEE_802_1X_INITIAL_KEY) { + kmp_address_eui_64_set(remote_addr, pae_supp->new_br_eui_64); + } else { + memset(remote_addr, 0, sizeof(kmp_addr_t)); + tr_error("No border router EUI-64"); + } + } } } @@ -897,18 +1056,12 @@ static void ws_pae_supp_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e ws_pae_lib_supp_timer_ticks_set(&pae_supp->entry, WAIT_FOR_AUTHENTICATION_TICKS); } - sec_prot_keys_t *keys = sec_keys; - // Key is to be inserted - if (keys) { - uint8_t gtk_index; - uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(keys->gtks, >k_index); - if (gtk) { - pae_supp->key_insert(pae_supp->interface_ptr, gtk_index, gtk); - sec_prot_keys_gtk_insert_index_clear(keys->gtks); - } - } - if ((type == IEEE_802_11_4WH || type == IEEE_802_11_GKH) && result == KMP_RESULT_OK) { + if (sec_keys) { + sec_prot_keys_t *keys = sec_keys; + pae_supp->nw_key_insert(pae_supp->interface_ptr, keys->gtks); + } + ws_pae_supp_authenticate_response(pae_supp, true); } } diff --git a/source/6LoWPAN/ws/ws_pae_supp.h b/source/6LoWPAN/ws/ws_pae_supp.h index 2a525f94d9..4d22d73f91 100644 --- a/source/6LoWPAN/ws/ws_pae_supp.h +++ b/source/6LoWPAN/ws/ws_pae_supp.h @@ -38,12 +38,13 @@ * * \param interface_ptr interface * \param cert_chain certificate chain + * \param timer_settings timer settings * * \return < 0 failure * \return >= 0 success * */ -int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const sec_prot_certs_t *certs); +int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const sec_prot_certs_t *certs, timer_settings_t *timer_settings); /** * ws_pae_supp_delete deletes PAE supplicant @@ -57,12 +58,20 @@ int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const se int8_t ws_pae_supp_delete(protocol_interface_info_entry_t *interface_ptr); /** - * ws_pae_supp_timer PAE supplicant timer call + * ws_pae_supp_fast_timer PAE supplicant fast timer call * * \param ticks elapsed ticks * */ -void ws_pae_supp_timer(uint16_t ticks); +void ws_pae_supp_fast_timer(uint16_t ticks); + +/** + * ws_pae_supp_slow_timer PAE supplicant slow timer call + * + * \param ticks elapsed seconds + * + */ +void ws_pae_supp_slow_timer(uint16_t seconds); /** * ws_pae_supp_authenticate start EAPOL authentication @@ -126,6 +135,39 @@ int8_t ws_pae_supp_border_router_addr_read(protocol_interface_info_entry_t *inte */ int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr); +/** + * ws_pae_supp_gtk_hash_update GTK hash has been updated (on PAN configuration) + * + * \param interface_ptr interface + * \param gtkhash GTK hash, 32 bytes + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_gtk_hash_update(protocol_interface_info_entry_t *interface_ptr, uint8_t *gtkhash); + +/** + * ws_pae_supp_nw_key_index_update key index been updated (on PAN configuration) + * + * \param interface_ptr interface + * \param index key index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_nw_key_index_update(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + +/** + * ws_pae_supp_nw_key_index_set network send key index set callback + * + * \param interface_ptr interface + * \param index network send key index + * + */ +typedef void ws_pae_supp_nw_key_index_set(protocol_interface_info_entry_t *interface_ptr, uint8_t index); + /** * ws_pae_supp_auth_completed authentication completed callback * @@ -136,36 +178,42 @@ int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr); typedef void ws_pae_supp_auth_completed(protocol_interface_info_entry_t *interface_ptr, bool success); /** - * ws_pae_supp_key_insert new GTK key available + * ws_pae_supp_nw_key_insert network key insert callback * * \param interface_ptr interface - * \param gtk_index index of the new GTK key - * \param gtk new GTK key + * \param gtks group keys + * + * \return < 0 failure + * \return >= 0 success * */ -typedef void ws_pae_supp_key_insert(protocol_interface_info_entry_t *interface_ptr, uint8_t gtk_index, uint8_t *gtk); +typedef int8_t ws_pae_supp_nw_key_insert(protocol_interface_info_entry_t *interface_ptr, sec_prot_gtk_keys_t *gtks); /** * ws_pae_supp_cb_register register PEA supplicant callbacks * * \param interface_ptr interface * \param completed authentication completed callback - * \param key_insert GTK key insert callback + * \param nw_key_insert network key index callback + * \param nw_key_index_set network send key index callback * */ -void ws_pae_supp_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_supp_auth_completed *completed, ws_pae_supp_key_insert *key_insert); +void ws_pae_supp_cb_register(protocol_interface_info_entry_t *interface_ptr, ws_pae_supp_auth_completed *completed, ws_pae_supp_nw_key_insert *nw_key_insert, ws_pae_supp_nw_key_index_set *nw_key_index_set); #else -#define ws_pae_supp_init(interface_ptr, certs) 1 +#define ws_pae_supp_init(interface_ptr, certs, timer_settings) 1 #define ws_pae_supp_delete NULL -#define ws_pae_supp_cb_register(interface_ptr, completed, key_insert) +#define ws_pae_supp_cb_register(interface_ptr, completed, nw_key_insert, nw_key_index_set) #define ws_pae_supp_nw_info_set(interface_ptr, pan_id, network_name) -1 #define ws_pae_supp_nw_key_valid(interface_ptr) -1 -#define ws_pae_supp_timer NULL +#define ws_pae_supp_fast_timer NULL +#define ws_pae_supp_slow_timer NULL #define ws_pae_supp_authenticate(interface_ptr, dest_pan_id, dest_eui_64) PAE_SUPP_NOT_ENABLED #define ws_pae_supp_border_router_addr_write NULL #define ws_pae_supp_border_router_addr_read NULL +#define ws_pae_supp_gtk_hash_update NULL +#define ws_pae_supp_nw_key_index_update NULL #endif diff --git a/source/6LoWPAN/ws/ws_pae_timers.c b/source/6LoWPAN/ws/ws_pae_timers.c new file mode 100644 index 0000000000..d73dc70e21 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_timers.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016-2018, Arm Limited 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 +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "fhss_config.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_pae_timers.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wspt" + +#define SECONDS_IN_DAY 24 * 60 * 60 +#define SECONDS_IN_MONTH 30 * SECONDS_IN_DAY +#define SECONDS_IN_MINUTE 60 + +#define DEFAULT_GTK_EXPIRE_OFFSET 43200 // 30 days +#define DEFAULT_PMK_LIFETIME 4 // 4 months +#define DEFAULT_PTK_LIFETIME 2 // 2 months +#define DEFAULT_GTK_NEW_ACTIVATION_TIME 720 // default 1/720 * 30 days --> 60 minutes +#define DEFAULT_REVOCATION_LIFETIME_REDUCTION 30 // default 1/30 * 30 days --> 1 day +#define DEFAULT_GTK_REQUEST_IMIN 4 // 4 minutes +#define DEFAULT_GTK_REQUEST_IMAX 64 // 64 minutes +#define DEFAULT_GTK_MAX_MISMATCH 64 // 64 minutes +#define DEFAULT_GTK_NEW_INSTALL_REQUIRED 80 // 80 percent of GTK lifetime --> 24 days + +static void ws_pae_timers_calculate(timer_settings_t *timer_settings); + +void ws_pae_timers_settings_init(timer_settings_t *timer_settings) +{ + timer_settings->gtk_expire_offset = DEFAULT_GTK_EXPIRE_OFFSET * SECONDS_IN_MINUTE; + timer_settings->pmk_lifetime = DEFAULT_PMK_LIFETIME * SECONDS_IN_MONTH; + timer_settings->ptk_lifetime = DEFAULT_PTK_LIFETIME * SECONDS_IN_MONTH; + timer_settings->gtk_new_act_time = DEFAULT_GTK_NEW_ACTIVATION_TIME; + timer_settings->revocat_lifetime_reduct = DEFAULT_REVOCATION_LIFETIME_REDUCTION; + timer_settings->gtk_request_imin = DEFAULT_GTK_REQUEST_IMIN * SECONDS_IN_MINUTE; + timer_settings->gtk_request_imax = DEFAULT_GTK_REQUEST_IMAX * SECONDS_IN_MINUTE; + timer_settings->gtk_max_mismatch = DEFAULT_GTK_MAX_MISMATCH * SECONDS_IN_MINUTE; + timer_settings->gtk_new_install_req = DEFAULT_GTK_NEW_INSTALL_REQUIRED; +} + +void ws_pae_timers_lifetime_set(timer_settings_t *timer_settings, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime) +{ + if (gtk_lifetime) { + timer_settings->gtk_expire_offset = gtk_lifetime * 60; + } + if (pmk_lifetime) { + timer_settings->pmk_lifetime = pmk_lifetime * 60; + } + if (ptk_lifetime) { + timer_settings->ptk_lifetime = ptk_lifetime * 60; + } + ws_pae_timers_calculate(timer_settings); +} + +void ws_pae_timers_gtk_time_settings_set(timer_settings_t *timer_settings, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint8_t new_install_req, uint32_t max_mismatch) +{ + if (revocat_lifetime_reduct) { + timer_settings->revocat_lifetime_reduct = revocat_lifetime_reduct; + } + if (new_activation_time) { + timer_settings->gtk_new_act_time = new_activation_time; + } + if (new_install_req) { + timer_settings->gtk_new_install_req = new_install_req; + } + if (max_mismatch) { + timer_settings->gtk_max_mismatch = max_mismatch * 60; + } + ws_pae_timers_calculate(timer_settings); +} + +static void ws_pae_timers_calculate(timer_settings_t *timer_settings) +{ + // Calculate GTK_NEW_INSTALL_REQUIRED < 100 * (1 - 1 / REVOCATION_LIFETIME_REDUCTION) + uint8_t calc_gtk_new_install_req = 100 - (100 / timer_settings->revocat_lifetime_reduct); + + if (timer_settings->gtk_expire_offset < 3600) { + // For very short GTKs give some more time to distribute the new GTK key to network, tune this if needed + calc_gtk_new_install_req = calc_gtk_new_install_req * 60 / 100; + } + + if (timer_settings->gtk_new_install_req > calc_gtk_new_install_req) { + tr_info("GTK new install required adjusted %i", calc_gtk_new_install_req); + timer_settings->gtk_new_install_req = calc_gtk_new_install_req; + } + + // Verify that GTK request Imin and Imax are sensible when compared to revocation lifetime + timer_settings->gtk_request_imin = DEFAULT_GTK_REQUEST_IMIN * SECONDS_IN_MINUTE; + timer_settings->gtk_request_imax = DEFAULT_GTK_REQUEST_IMAX * SECONDS_IN_MINUTE; + + uint32_t gtk_revocation_lifetime = timer_settings->gtk_expire_offset / timer_settings->revocat_lifetime_reduct; + uint32_t new_activation_time = timer_settings->gtk_expire_offset / timer_settings->gtk_new_act_time; + + uint32_t time_to_update = gtk_revocation_lifetime; + if (gtk_revocation_lifetime > new_activation_time) { + time_to_update = gtk_revocation_lifetime - new_activation_time; + } + + tr_info("Key timers revocation lifetime: %"PRIu32", new activation time: %"PRIu32", max mismatch %i, time to update: %"PRIu32"", gtk_revocation_lifetime, new_activation_time, timer_settings->gtk_max_mismatch, time_to_update); + + // If time to update results smaller GTK request Imax use it for calculation otherwise use GTK max mismatch + if (time_to_update < timer_settings->gtk_max_mismatch) { + // If time to update is smaller than GTK request Imax update GTK request values + if (timer_settings->gtk_request_imax > time_to_update) { + timer_settings->gtk_request_imin = time_to_update / 4; + timer_settings->gtk_request_imax = time_to_update / 2; + tr_info("GTK request timers adjusted Imin: %i, Imax: %i", timer_settings->gtk_request_imin, timer_settings->gtk_request_imax); + } + } else if (timer_settings->gtk_request_imax > timer_settings->gtk_max_mismatch) { + // If GTK request Imax is larger than GTK max mismatch update GTK request values + + // For small GTK max mismatch times, scale the Imin to be larger than default 4 / 64; + uint16_t scaler; + if (timer_settings->gtk_max_mismatch < 50) { + scaler = 10; + } else if (timer_settings->gtk_max_mismatch > 600) { + scaler = 1; + } else { + // About 1 minute mismatch, results 37 seconds Imin and 60 seconds Imax + scaler = (600 - timer_settings->gtk_max_mismatch) / 54; + } + + timer_settings->gtk_request_imin = timer_settings->gtk_max_mismatch * scaler * DEFAULT_GTK_REQUEST_IMIN / DEFAULT_GTK_REQUEST_IMAX; + timer_settings->gtk_request_imax = timer_settings->gtk_max_mismatch; + tr_info("GTK request timers adjusted Imin: %i, Imax: %i", timer_settings->gtk_request_imin, timer_settings->gtk_request_imax); + } +} + +bool ws_pae_timers_gtk_new_install_required(timer_settings_t *timer_settings, uint32_t seconds) +{ + uint32_t gtk_new_install_req_seconds = timer_settings->gtk_expire_offset - timer_settings->gtk_new_install_req * timer_settings->gtk_expire_offset / 100; + + if (seconds < gtk_new_install_req_seconds) { + return true; + } else { + return false; + } +} + +bool ws_pae_timers_gtk_new_activation_time(timer_settings_t *timer_settings, uint32_t seconds) +{ + uint32_t gtk_gtk_new_activation_time_seconds = timer_settings->gtk_expire_offset / timer_settings->gtk_new_act_time; + + if (seconds < gtk_gtk_new_activation_time_seconds) { + return true; + } else { + return false; + } +} + +uint32_t ws_pae_timers_gtk_revocation_lifetime_get(timer_settings_t *timer_settings) +{ + return timer_settings->gtk_expire_offset / timer_settings->revocat_lifetime_reduct; +} + +#endif /* HAVE_WS */ diff --git a/source/6LoWPAN/ws/ws_pae_timers.h b/source/6LoWPAN/ws/ws_pae_timers.h new file mode 100644 index 0000000000..55b80a4d52 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_timers.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019, Arm Limited 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. + */ + +#ifndef WS_PAE_TIMERS_H_ +#define WS_PAE_TIMERS_H_ + +typedef struct { + uint32_t gtk_expire_offset; // GTK lifetime; GTK_EXPIRE_OFFSET (seconds) + uint32_t pmk_lifetime; // PMK lifetime (seconds) + uint32_t ptk_lifetime; // PTK lifetime (seconds) + uint16_t gtk_new_act_time; // GTK_NEW_ACTIVATION_TIME (1/X of expire offset) + uint16_t revocat_lifetime_reduct; // REVOCATION_LIFETIME_REDUCTION (reduction of lifetime) + uint16_t gtk_request_imin; // GTK_REQUEST_IMIN (seconds) + uint16_t gtk_request_imax; // GTK_REQUEST_IMAX (seconds) + uint16_t gtk_max_mismatch; // GTK_MAX_MISMATCH (seconds) + uint8_t gtk_new_install_req; // GTK_NEW_INSTALL_REQUIRED (percent of GTK lifetime) +} timer_settings_t; + +/** + * ws_pae_timers_settings_init initializes timer settings structure + * + * \param timer_settings timer settings + * + */ +void ws_pae_timers_settings_init(timer_settings_t *timer_settings); + +/** + * ws_pae_timers_lifetime_set sets GTK, PTK and PTK lifetimes + * + * \param timer_settings timer settings + * \param gtk_lifetime GTK lifetime + * \param pmk_lifetime PMK lifetime + * \param ptk_lifetime PTK lifetime + * + */ +void ws_pae_timers_lifetime_set(timer_settings_t *timer_settings, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime); + +/** + * ws_pae_timers_gtk_time_settings_set sets GTK, PTK and PTK lifetimes + * + * \param timer_settings timer settings + * \param revocat_lifetime_reduct revocation lifetime reduction + * \param new_activation_time new activation time + * \param new_install_req new install required + * \param max_mismatch max mismatch + * + */ +void ws_pae_timers_gtk_time_settings_set(timer_settings_t *timer_settings, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint8_t new_install_req, uint32_t max_mismatch); + +/** + * ws_pae_timers_gtk_new_install_required GTK new install required check + * + * \param timer_settings timer settings + * \param seconds elapsed seconds + * + * \return true new GTK install required expired + * \return false GTK install not required + * + */ +bool ws_pae_timers_gtk_new_install_required(timer_settings_t *timer_settings, uint32_t seconds); + +/** + * ws_pae_timers_gtk_new_activation_time GTK new activation time + * + * \param timer_settings timer settings + * \param seconds elapsed seconds + * + * \return true GTK new activation time expired + * \return false GTK new activation time not expired + * + */ +bool ws_pae_timers_gtk_new_activation_time(timer_settings_t *timer_settings, uint32_t seconds); + +/** + * ws_pae_timers_gtk_revocation_lifetime_get GTK revocation lifetime get + * + * \param timer_settings timer settings + * + * \return GTK revocation lifetime + * + */ +uint32_t ws_pae_timers_gtk_revocation_lifetime_get(timer_settings_t *timer_settings); + +#endif /* WS_PAE_TIMERS_H_ */ diff --git a/source/6LoWPAN/ws/ws_test_api.c b/source/6LoWPAN/ws/ws_test_api.c index c320f5b4a8..09a749b1bb 100644 --- a/source/6LoWPAN/ws/ws_test_api.c +++ b/source/6LoWPAN/ws/ws_test_api.c @@ -25,6 +25,7 @@ #include "6LoWPAN/ws/ws_config.h" #include "6LoWPAN/ws/ws_common.h" #include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "6LoWPAN/ws/ws_pae_controller.h" #include "randLIB.h" #include "ns_trace.h" @@ -60,7 +61,7 @@ int ws_test_gtk_set(int8_t interface_id, uint8_t *gtk[4]) (void) interface_id; (void) gtk; - return 0; + return ws_pae_controller_gtk_update(interface_id, gtk); } int ws_test_active_key_set(int8_t interface_id, uint8_t index) @@ -68,7 +69,7 @@ int ws_test_active_key_set(int8_t interface_id, uint8_t index) (void) interface_id; (void) index; - return 0; + return ws_pae_controller_active_key_update(interface_id, index); } int ws_test_key_lifetime_set(int8_t interface_id, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime) @@ -78,17 +79,27 @@ int ws_test_key_lifetime_set(int8_t interface_id, uint32_t gtk_lifetime, uint32_ (void) pmk_lifetime; (void) ptk_lifetime; - return 0; + return ws_pae_controller_key_lifetime_update(interface_id, gtk_lifetime, pmk_lifetime, ptk_lifetime); } -int ws_test_gtk_time_settings_set(int8_t interface_id, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint32_t max_mismatch) +int ws_test_gtk_time_settings_set(int8_t interface_id, uint8_t revocat_lifetime_reduct, uint8_t new_activation_time, uint8_t new_install_req, uint32_t max_mismatch) { (void) interface_id; (void) revocat_lifetime_reduct; (void) new_activation_time; + (void) new_install_req; (void) max_mismatch; - return 0; + + return ws_pae_controller_gtk_time_settings_update(interface_id, revocat_lifetime_reduct, new_activation_time, new_install_req, max_mismatch); +} + +int ws_test_next_gtk_set(int8_t interface_id, uint8_t *gtk[4]) +{ + (void) interface_id; + (void) gtk; + + return ws_pae_controller_next_gtk_update(interface_id, gtk); } #endif // HAVE_WS diff --git a/source/Common_Protocols/icmpv6.c b/source/Common_Protocols/icmpv6.c index 547ed947d5..3dfac3c936 100644 --- a/source/Common_Protocols/icmpv6.c +++ b/source/Common_Protocols/icmpv6.c @@ -325,12 +325,12 @@ static buffer_t *icmpv6_echo_request_handler(buffer_t *buf) if (addr_is_ipv6_multicast(buf->dst_sa.address)) { const uint8_t *ipv6_ptr; + memcpy(buf->dst_sa.address, buf->src_sa.address, 16); ipv6_ptr = addr_select_source(cur, buf->dst_sa.address, 0); if (!ipv6_ptr) { tr_debug("No address"); return buffer_free(buf); } - memcpy(buf->dst_sa.address, buf->src_sa.address, 16); memcpy(buf->src_sa.address, ipv6_ptr, 16); } else { memswap(buf->dst_sa.address, buf->src_sa.address, 16); @@ -570,16 +570,8 @@ int icmpv6_slaac_prefix_update(struct protocol_interface_info_entry *cur, const //Validate first current list If prefix is already defined adress ns_list_foreach_safe(if_address_entry_t, e, &cur->ip_addresses) { if (e->source == ADDR_SOURCE_SLAAC && (e->prefix_len == prefix_len) && bitsequal(e->address, prefix_ptr, prefix_len)) { - //Update Current lifetimes (see RFC 4862 for rules detail) - if (valid_lifetime > (2 * 60 * 60) || valid_lifetime > e->valid_lifetime) { - addr_set_valid_lifetime(cur, e, valid_lifetime); - } else if (e->valid_lifetime <= (2 * 60 * 60)) { - //NOT Update Valid Lifetime - } else { - addr_set_valid_lifetime(cur, e, 2 * 60 * 60); - } - addr_set_preferred_lifetime(cur, e, preferred_lifetime); + addr_lifetime_update(cur, e, valid_lifetime, preferred_lifetime, 2 * 60 * 60); ret_val = 0; } } @@ -1320,6 +1312,63 @@ uint8_t *icmpv6_write_mtu_option(uint32_t mtu, uint8_t *dptr) return dptr; } +void ack_receive_cb(struct buffer *buffer_ptr, uint8_t status) +{ + /*icmpv6_na_handler functionality based on ACK*/ + ipv6_neighbour_t *neighbour_entry; + uint8_t ll_target[16]; + + if (status != SOCKET_TX_DONE) { + /*NS failed*/ + return; + } + + if (buffer_ptr->dst_sa.addr_type == ADDR_IPV6) { + /*Full IPv6 address*/ + memcpy(ll_target, buffer_ptr->dst_sa.address, 16); + } else if (buffer_ptr->dst_sa.addr_type == ADDR_802_15_4_LONG) { + // Build link local address from long MAC address + memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(ll_target + 8, &buffer_ptr->dst_sa.address[2], 8); + ll_target[8] ^= 2; + } else { + tr_warn("wrong address %d %s", buffer_ptr->dst_sa.addr_type, trace_array(buffer_ptr->dst_sa.address, 16)); + return; + } + + neighbour_entry = ipv6_neighbour_lookup(&buffer_ptr->interface->ipv6_neighbour_cache, ll_target); + if (neighbour_entry) { + ipv6_neighbour_update_from_na(&buffer_ptr->interface->ipv6_neighbour_cache, neighbour_entry, NA_S, buffer_ptr->dst_sa.addr_type, buffer_ptr->dst_sa.address); + } + + if (ws_info(buffer_ptr->interface)) { + ws_common_neighbor_update(buffer_ptr->interface, ll_target); + } +} +void ack_remove_neighbour_cb(struct buffer *buffer_ptr, uint8_t status) +{ + /*icmpv6_na_handler functionality based on ACK*/ + uint8_t ll_target[16]; + (void)status; + + if (buffer_ptr->dst_sa.addr_type == ADDR_IPV6) { + /*Full IPv6 address*/ + memcpy(ll_target, buffer_ptr->dst_sa.address, 16); + } else if (buffer_ptr->dst_sa.addr_type == ADDR_802_15_4_LONG) { + // Build link local address from long MAC address + memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(ll_target + 8, &buffer_ptr->dst_sa.address[2], 8); + ll_target[8] ^= 2; + } else { + tr_warn("wrong address %d %s", buffer_ptr->dst_sa.addr_type, trace_array(buffer_ptr->dst_sa.address, 16)); + return; + } + if (ws_info(buffer_ptr->interface)) { + ws_common_neighbor_remove(buffer_ptr->interface, ll_target); + } + +} + buffer_t *icmpv6_build_ns(protocol_interface_info_entry_t *cur, const uint8_t target_addr[16], const uint8_t *prompting_src_addr, bool unicast, bool unspecified_source, const aro_t *aro) { if (!cur || addr_is_ipv6_multicast(target_addr)) { @@ -1394,10 +1443,15 @@ buffer_t *icmpv6_build_ns(protocol_interface_info_entry_t *cur, const uint8_t ta } /* If ARO Success sending is omitted, MAC ACK is used instead */ /* Setting callback for receiving ACK from adaptation layer */ - if (aro && cur->ipv6_neighbour_cache.omit_aro_success) { + if (aro && cur->ipv6_neighbour_cache.omit_na_aro_success) { buf->ack_receive_cb = rpl_control_address_register_done; } } + if (unicast && (!aro && cur->ipv6_neighbour_cache.omit_na)) { + /*MAC ACK is processed as success response*/ + buf->ack_receive_cb = ack_receive_cb; + } + buf->src_sa.addr_type = ADDR_IPV6; /* NS packets are implicitly on-link. If we ever find ourselves sending an @@ -1530,11 +1584,16 @@ buffer_t *icmpv6_build_na(protocol_interface_info_entry_t *cur, bool solicited, tr_debug("Build NA"); - /* Check if ARO status == success, then sending can be omitted with flag */ - if (aro && cur->ipv6_neighbour_cache.omit_aro_success && aro->status == ARO_SUCCESS) { - tr_debug("Omit success reply"); + /* Check if ARO response and status == success, then sending can be omitted with flag */ + if (aro && cur->ipv6_neighbour_cache.omit_na_aro_success && aro->status == ARO_SUCCESS) { + tr_debug("Omit NA ARO success"); return NULL; } + /* All other than ARO NA messages are omitted and MAC ACK is considered as success */ + if (!tllao_required && (!aro && cur->ipv6_neighbour_cache.omit_na)) { + return NULL; + } + buffer_t *buf = buffer_get(8 + 16 + 16 + 16); /* fixed, target addr, target ll addr, aro */ if (!buf) { @@ -1616,6 +1675,11 @@ buffer_t *icmpv6_build_na(protocol_interface_info_entry_t *cur, bool solicited, memcpy(ptr, aro->eui64, 8); ptr += 8; } + if (ws_info(cur) && aro && aro->status != ARO_SUCCESS) { + /*If Aro failed we will kill the neigbour after we have succeeded in sending message*/ + buf->ack_receive_cb = ack_remove_neighbour_cb; + } + //Force Next Hop is destination ipv6_buffer_route_to(buf, buf->dst_sa.address, cur); diff --git a/source/Common_Protocols/icmpv6_prefix.c b/source/Common_Protocols/icmpv6_prefix.c index d93a1c1bf7..2c63ddd7b2 100644 --- a/source/Common_Protocols/icmpv6_prefix.c +++ b/source/Common_Protocols/icmpv6_prefix.c @@ -35,9 +35,11 @@ prefix_entry_t *icmpv6_prefix_add(prefix_list_t *list, const uint8_t *prefixPtr, entry = icmpv6_prefix_compare(list, prefixPtr, prefix_len); if (entry) { - entry->options = flags; - entry->lifetime = lifeTime; - entry->preftime = prefTime; + if (flags != 0xff) { + entry->options = flags; + entry->lifetime = lifeTime; + entry->preftime = prefTime; + } return entry; } diff --git a/source/Core/include/ns_address_internal.h b/source/Core/include/ns_address_internal.h index 8295226283..651d15cf2a 100644 --- a/source/Core/include/ns_address_internal.h +++ b/source/Core/include/ns_address_internal.h @@ -156,6 +156,7 @@ void addr_fast_timer(struct protocol_interface_info_entry *cur, uint_fast16_t ti void addr_slow_timer(struct protocol_interface_info_entry *cur, uint_fast16_t seconds); struct if_address_entry *addr_add(struct protocol_interface_info_entry *cur, const uint8_t address[__static 16], uint_fast8_t prefix_len, if_address_source_t source, uint32_t valid_lifetime, uint32_t preferred_lifetime, bool skip_dad); int_fast8_t addr_delete(struct protocol_interface_info_entry *cur, const uint8_t address[__static 16]); +int_fast8_t addr_deprecate(struct protocol_interface_info_entry *cur, const uint8_t address[__static 16]); void addr_delete_matching(struct protocol_interface_info_entry *cur, const uint8_t *prefix, uint8_t prefix_len, if_address_source_t source); void addr_delete_entry(struct protocol_interface_info_entry *cur, if_address_entry_t *addr); void addr_set_non_preferred(struct protocol_interface_info_entry *cur, if_address_source_t source); @@ -166,6 +167,8 @@ void addr_cb(struct protocol_interface_info_entry *interface, if_address_entry_t void addr_set_valid_lifetime(struct protocol_interface_info_entry *interface, if_address_entry_t *entry, uint32_t valid_lifetime); void addr_set_preferred_lifetime(struct protocol_interface_info_entry *interface, if_address_entry_t *entry, uint32_t preferred_lifetime); +void addr_lifetime_update(struct protocol_interface_info_entry *interface, if_address_entry_t *address, uint32_t valid_lifetime, uint32_t preferred_lifetime, uint32_t threshold); + int_fast8_t addr_policy_table_add_entry(const uint8_t *prefix, uint8_t len, uint8_t precedence, uint8_t label); int_fast8_t addr_policy_table_delete_entry(const uint8_t *prefix, uint8_t len); uint8_t addr_len_from_type(addrtype_t addr_type); @@ -178,6 +181,8 @@ struct if_address_entry *addr_get_entry(const struct protocol_interface_info_ent bool addr_is_assigned_to_interface(const struct protocol_interface_info_entry *interface, const uint8_t addr[__static 16]); bool addr_is_tentative_for_interface(const struct protocol_interface_info_entry *interface, const uint8_t addr[__static 16]); +void addr_policy_remove_by_label(uint8_t label); + void addr_duplicate_detected(struct protocol_interface_info_entry *interface, const uint8_t addr[__static 16]); struct if_group_entry *addr_add_group(struct protocol_interface_info_entry *interface, const uint8_t group[__static 16]); diff --git a/source/Core/ns_address_internal.c b/source/Core/ns_address_internal.c index 386d00c01d..ec884a3907 100644 --- a/source/Core/ns_address_internal.c +++ b/source/Core/ns_address_internal.c @@ -690,6 +690,13 @@ const uint8_t *addr_select_source(protocol_interface_info_entry_t *interface, co } } + /* Rule 9 select most precated one */ + if (policy_SA->precedence > policy_SB->precedence) { + PREFER_SA; + } else if (policy_SB->precedence > policy_SA->precedence) { + PREFER_SB; + } + /* Tie */ PREFER_SA; } @@ -858,6 +865,7 @@ void addr_fast_timer(protocol_interface_info_entry_t *cur, uint_fast16_t ticks) } #endif } else { + addr->addr_reg_done = 0; addr_cb(cur, addr, ADDR_CALLBACK_TIMER); } @@ -1002,6 +1010,19 @@ int_fast8_t addr_delete(protocol_interface_info_entry_t *cur, const uint8_t addr return -1; } +int_fast8_t addr_deprecate(protocol_interface_info_entry_t *cur, const uint8_t address[static 16]) +{ + ns_list_foreach(if_address_entry_t, e, &cur->ip_addresses) { + if (memcmp(e->address, address, 16) == 0) { + tr_debug("Deprecate address %s", trace_ipv6(e->address)); + addr_lifetime_update(cur, e, 0, 0, 30 * 60); //Accept max 30 min lifetime + return 0; + } + } + + return -1; +} + void addr_delete_matching(protocol_interface_info_entry_t *cur, const uint8_t *prefix, uint8_t prefix_len, if_address_source_t source) { ns_list_foreach_safe(if_address_entry_t, e, &cur->ip_addresses) { @@ -1091,6 +1112,20 @@ void addr_set_preferred_lifetime(protocol_interface_info_entry_t *interface, if_ } } +void addr_lifetime_update(protocol_interface_info_entry_t *interface, if_address_entry_t *address, uint32_t valid_lifetime, uint32_t preferred_lifetime, uint32_t threshold) +{ + //Update Current lifetimes (see RFC 4862 for rules detail) + if (valid_lifetime > threshold || valid_lifetime > address->valid_lifetime) { + addr_set_valid_lifetime(interface, address, valid_lifetime); + } else if (address->valid_lifetime <= threshold) { + //NOT Update Valid Lifetime + } else { + addr_set_valid_lifetime(interface, address, threshold); + } + + addr_set_preferred_lifetime(interface, address, preferred_lifetime); +} + void memswap(uint8_t *restrict a, uint8_t *restrict b, uint_fast8_t len) { while (len--) { @@ -1398,6 +1433,21 @@ int8_t addr_interface_select_source(protocol_interface_info_entry_t *cur, uint8_ return ret_val; } +void addr_policy_remove_by_label(uint8_t label) +{ + ns_list_foreach_safe(addr_policy_table_entry_t, entry, &addr_policy_table) { + if (entry->label == label) { + /* + * Remove label policy if no local address matches" + */ + if (!protocol_interface_any_address_match(entry->prefix, entry->prefix_len)) { + ns_list_remove(&addr_policy_table, entry); + ns_dyn_mem_free(entry); + } + } + } +} + // This last function must always be compiled with tracing enabled #ifndef FEA_TRACE_SUPPORT #define FEA_TRACE_SUPPORT 1 diff --git a/source/DHCPv6_Server/DHCPv6_Server_service.c b/source/DHCPv6_Server/DHCPv6_Server_service.c index 4776d6ddea..be3c6c927f 100644 --- a/source/DHCPv6_Server/DHCPv6_Server_service.c +++ b/source/DHCPv6_Server/DHCPv6_Server_service.c @@ -272,7 +272,7 @@ void DHCPv6_server_service_timeout_cb(uint32_t timeUpdateInSeconds) * /param guaPrefix Prefix which will be removed * /param delete_gua_addresses Whether or not assigned addresses with the prefix should be removed from the interface. */ -void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 16], bool delete_gua_addresses) +void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 8], bool delete_gua_addresses) { dhcpv6_gua_server_entry_s *serverInfo = libdhcpv6_server_data_get_by_prefix_and_interfaceid(interface, guaPrefix); if (serverInfo) { @@ -390,7 +390,7 @@ int DHCPv6_server_service_init(int8_t interface, uint8_t guaPrefix[static 16], u return -1; } -void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 16], bool delete_gua_addresses) +void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 8], bool delete_gua_addresses) { (void) interface; (void) guaPrefix; diff --git a/source/DHCPv6_Server/DHCPv6_server_service.h b/source/DHCPv6_Server/DHCPv6_server_service.h index 7373bedaa0..d8c45e7327 100644 --- a/source/DHCPv6_Server/DHCPv6_server_service.h +++ b/source/DHCPv6_Server/DHCPv6_server_service.h @@ -54,7 +54,7 @@ void DHCPv6_server_service_callback_set(int8_t interface, uint8_t guaPrefix[stat * /param guaPrefix Prefix which will be removed * /param delete_gua_addresses Whether or not assigned addresses with the prefix should be removed from the interface. */ -void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 16], bool delete_gua_addresses); +void DHCPv6_server_service_delete(int8_t interface, uint8_t guaPrefix[static 8], bool delete_gua_addresses); void DHCPv6_server_service_timeout_cb(uint32_t timeUpdateInSeconds); diff --git a/source/DHCPv6_client/dhcpv6_client_api.h b/source/DHCPv6_client/dhcpv6_client_api.h index b1b6609e88..472b9ff8f2 100644 --- a/source/DHCPv6_client/dhcpv6_client_api.h +++ b/source/DHCPv6_client/dhcpv6_client_api.h @@ -38,9 +38,12 @@ void dhcp_client_init(int8_t interface); /* Set configurations for DHCP client * + * /param interface Client Inteface ID * /param renew_uses_solicit Instead of renew message SOLICIT is used. + * /param one_client_for_this_interface True Interface use oneinstance for allocate address + * /param no_address_hint IAID use address at Solicit */ -void dhcp_client_configure(int8_t interface, bool renew_uses_solicit); +void dhcp_client_configure(int8_t interface, bool renew_uses_solicit, bool one_client_for_this_interface, bool no_address_hint); /* Set Timeout parameters for SOLICIT transactions * @@ -92,11 +95,13 @@ void dhcp_client_global_address_renew(int8_t interface); * /param prefix dhcp server ML16 address where address is registered. * */ -void dhcp_client_global_address_delete(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16]); +void dhcp_client_global_address_delete(int8_t interface, uint8_t *dhcp_addr, uint8_t prefix[static 16]); void dhcp_relay_agent_enable(int8_t interface, uint8_t border_router_address[static 16]); +int dhcp_client_server_address_update(int8_t interface, uint8_t prefix[static 16], uint8_t server_address[static 16]); + #endif /* DHCPV6_CLIENT_API_H_ */ diff --git a/source/DHCPv6_client/dhcpv6_client_service.c b/source/DHCPv6_client/dhcpv6_client_service.c index 0e1c97cd81..5ed5209893 100644 --- a/source/DHCPv6_client/dhcpv6_client_service.c +++ b/source/DHCPv6_client/dhcpv6_client_service.c @@ -41,11 +41,14 @@ typedef struct { uint8_t libDhcp_instance; int8_t interface; bool renew_uses_solicit: 1; + bool one_instance_interface: 1; + bool no_address_hint: 1; } dhcp_client_class_t; static dhcp_client_class_t dhcp_client; -void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr); +static bool dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr); +void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason); void dhcp_client_init(int8_t interface) @@ -57,14 +60,19 @@ void dhcp_client_init(int8_t interface) dhcp_client.sol_timeout = 0; dhcp_client.sol_max_rt = 0; dhcp_client.sol_max_rc = 0; + dhcp_client.renew_uses_solicit = false; + dhcp_client.one_instance_interface = false; + dhcp_client.no_address_hint = false; return; } -void dhcp_client_configure(int8_t interface, bool renew_uses_solicit) +void dhcp_client_configure(int8_t interface, bool renew_uses_solicit, bool one_client_for_this_interface, bool no_address_hint) { // Set true if RENEW is not used and SOLICIT sent instead. (void)interface; dhcp_client.renew_uses_solicit = renew_uses_solicit; + dhcp_client.one_instance_interface = one_client_for_this_interface; + dhcp_client.no_address_hint = no_address_hint; } void dhcp_client_solicit_timeout_set(int8_t interface, uint16_t timeout, uint16_t max_rt, uint8_t max_rc) @@ -170,6 +178,14 @@ int dhcp_solicit_resp_cb(uint16_t instance_id, void *ptr, uint8_t msg_name, uin goto error_exit; } + if (dhcp_client.one_instance_interface && memcmp(srv_data_ptr->iaNontemporalAddress.addressPrefix, dhcp_ia_non_temporal_params.nonTemporalAddress, 16)) { + + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(dhcp_client.interface); + if (cur) { + addr_deprecate(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + } + } + memcpy(srv_data_ptr->iaNontemporalAddress.addressPrefix, dhcp_ia_non_temporal_params.nonTemporalAddress, 16); srv_data_ptr->iaNontemporalAddress.preferredTime = dhcp_ia_non_temporal_params.preferredValidLifeTime; srv_data_ptr->iaNontemporalAddress.validLifetime = dhcp_ia_non_temporal_params.validLifeTime; @@ -177,13 +193,13 @@ int dhcp_solicit_resp_cb(uint16_t instance_id, void *ptr, uint8_t msg_name, uin srv_data_ptr->serverLinkType = serverId.linkType; srv_data_ptr->T0 = dhcp_ia_non_temporal_params.T0; srv_data_ptr->T1 = dhcp_ia_non_temporal_params.T1; - srv_data_ptr->iaNonTemporalStructValid = true; - dhcpv6_client_set_address(dhcp_client.interface, srv_data_ptr); + + bool status = dhcpv6_client_set_address(dhcp_client.interface, srv_data_ptr); if (dhcp_client.global_address_cb) { - dhcp_client.global_address_cb(dhcp_client.interface, srv_data_ptr->server_address, srv_data_ptr->iaNontemporalAddress.addressPrefix, true); + dhcp_client.global_address_cb(dhcp_client.interface, srv_data_ptr->server_address, srv_data_ptr->iaNontemporalAddress.addressPrefix, status); } return RET_MSG_ACCEPTED; error_exit: @@ -198,20 +214,36 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 uint8_t *payload_ptr; uint32_t payload_len; dhcpv6_client_server_data_t *srv_data_ptr; + bool add_prefix; if (mac64 == NULL || dhcp_addr == NULL) { tr_error("Invalid parameters"); return -1; } - if (!prefix) { + if (!prefix || dhcp_client.one_instance_interface) { //NULL Definition will only check That Interface is not generated - if (libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance)) { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance); + if (srv_data_ptr) { //Already Created to same interface + if (dhcp_client.one_instance_interface && prefix) { + if (srv_data_ptr->iaNonTemporalStructValid) { + if (memcmp(srv_data_ptr->iaNontemporalAddress.addressPrefix, prefix, 8)) { + //Request new address direct from Server if prefix is new + srv_data_ptr->iaNonTemporalStructValid = false; + dhcpv6_renew(protocol_stack_interface_info_get_by_id(interface), NULL, ADDR_CALLBACK_TIMER); + } + return 0; + } + } return -1; } + } else if (dhcp_client_server_address_update(interface, prefix, dhcp_addr) == 0) { + //No need for allocate new + return 0; } + tr_debug("GEN new Dhcpv6 client %u", dhcp_client.libDhcp_instance); srv_data_ptr = libdhcvp6_nontemporalAddress_server_data_allocate(interface, dhcp_client.libDhcp_instance, mac64, link_type, prefix, dhcp_addr); if (!srv_data_ptr) { @@ -219,7 +251,15 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 return -1; } - payload_len = libdhcpv6_solication_message_length(link_type, prefix != NULL, 0); + + if (!prefix || dhcp_client.no_address_hint) { + add_prefix = false; + } else { + add_prefix = prefix != NULL; + } + + payload_len = libdhcpv6_solication_message_length(link_type, add_prefix, 0); + payload_ptr = ns_dyn_mem_temporary_alloc(payload_len); if (!payload_ptr) { @@ -238,7 +278,7 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 solPacket.transActionId = libdhcpv6_txid_get(); /*Non Temporal Address */ - if (prefix) { + if (prefix && !dhcp_client.no_address_hint) { dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; nonTemporalAddress.requestedAddress = prefix; libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &solPacket, &nonTemporalAddress, NULL); @@ -253,6 +293,7 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); return -1; } + srv_data_ptr->iaNonTemporalStructValid = false; if (dhcp_client.sol_timeout != 0) { // Default retry values are modified from specification update to message dhcp_service_set_retry_timers(srv_data_ptr->transActionId, dhcp_client.sol_timeout, dhcp_client.sol_max_rt, dhcp_client.sol_max_rc); @@ -261,13 +302,44 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 return 0; } +int dhcp_client_server_address_update(int8_t interface, uint8_t prefix[static 16], uint8_t server_address[static 16]) +{ + dhcpv6_client_server_data_t *srv_data_ptr = NULL; + + if (dhcp_client.interface != interface) { + tr_debug("Interface not match"); + return -1; + } + + if (prefix) { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface, prefix); + } else if (dhcp_client.one_instance_interface) { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance); + } + if (!srv_data_ptr) { + return -1; + } + + if (memcmp(srv_data_ptr->server_address, server_address, 16) == 0) { + return 0; + } + + memcpy(srv_data_ptr->server_address, server_address, 16); + if (!srv_data_ptr->iaNonTemporalStructValid) { + dhcp_service_update_server_address(srv_data_ptr->transActionId, server_address); + } + return 0; +} + + + void dhcp_client_global_address_renew(int8_t interface) { (void)interface; return; } -void dhcp_client_global_address_delete(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16]) +void dhcp_client_global_address_delete(int8_t interface, uint8_t *dhcp_addr, uint8_t prefix[static 16]) { protocol_interface_info_entry_t *cur; dhcpv6_client_server_data_t *srv_data_ptr; @@ -276,33 +348,35 @@ void dhcp_client_global_address_delete(int8_t interface, uint8_t dhcp_addr[stati srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface, prefix); cur = protocol_stack_interface_info_get_by_id(interface); - do { - if (cur == NULL || srv_data_ptr == NULL) { - return; - } - dhcp_service_req_remove(srv_data_ptr->transActionId);// remove all pending retransmissions - tr_debug("Deleting address: %s", trace_ipv6(srv_data_ptr->iaNontemporalAddress.addressPrefix)); + if (cur == NULL || srv_data_ptr == NULL) { + return; + } + dhcp_service_req_remove(srv_data_ptr->transActionId);// remove all pending retransmissions + if (dhcp_client.one_instance_interface) { + addr_deprecate(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + } else { addr_delete(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + } - libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); - srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface, prefix); - } while (srv_data_ptr); - - return; + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); } void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason) { - dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; + dhcp_link_options_params_t serverLink; uint8_t *payload_ptr; uint32_t payload_len; - dhcpv6_client_server_data_t *srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface->id, addr->address); + dhcpv6_client_server_data_t *srv_data_ptr; + if (addr) { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface->id, addr->address); + } else { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance); + } if (srv_data_ptr == NULL) { - tr_warn("Dhcp address lost"); - return ; + return; } if (reason == ADDR_CALLBACK_INVALIDATED) { dhcp_service_req_remove(srv_data_ptr->transActionId);//stop retransmissions of renew @@ -315,10 +389,12 @@ void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t return; } - payload_len = libdhcpv6_address_request_message_len(srv_data_ptr->clientLinkIdType, srv_data_ptr->serverLinkType, 0); + payload_len = libdhcpv6_address_request_message_len(srv_data_ptr->clientLinkIdType, srv_data_ptr->serverLinkType, 0, !dhcp_client.no_address_hint); payload_ptr = ns_dyn_mem_temporary_alloc(payload_len); if (payload_ptr == NULL) { - addr->state_timer = 200; //Retry after 20 seconds + if (addr) { + addr->state_timer = 200; //Retry after 20 seconds + } tr_error("Out of memory"); return ; } @@ -337,18 +413,33 @@ void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t packetReq.messageType = DHCPV6_SOLICATION_TYPE; } - // Set Address information - nonTemporalAddress.requestedAddress = srv_data_ptr->iaNontemporalAddress.addressPrefix; - nonTemporalAddress.preferredLifeTime = srv_data_ptr->iaNontemporalAddress.preferredTime; - nonTemporalAddress.validLifeTime = srv_data_ptr->iaNontemporalAddress.validLifetime; + serverLink.linkID = srv_data_ptr->serverLinkId; serverLink.linkType = srv_data_ptr->serverLinkType; - libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &packetReq, &nonTemporalAddress, &serverLink); + if (dhcp_client.no_address_hint && dhcp_client.renew_uses_solicit) { + packetReq.timerT0 = 0; + packetReq.timerT1 = 0; + libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &packetReq, NULL, &serverLink); + } else { + // Set Address information + dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; + nonTemporalAddress.requestedAddress = srv_data_ptr->iaNontemporalAddress.addressPrefix; + nonTemporalAddress.preferredLifeTime = srv_data_ptr->iaNontemporalAddress.preferredTime; + nonTemporalAddress.validLifeTime = srv_data_ptr->iaNontemporalAddress.validLifetime; + libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &packetReq, &nonTemporalAddress, &serverLink); + } // send solicit - srv_data_ptr->transActionId = dhcp_service_send_req(dhcp_client.service_instance, 0, srv_data_ptr, srv_data_ptr->server_address, payload_ptr, payload_len, dhcp_solicit_resp_cb); + uint8_t *server_address = dhcp_service_relay_global_addres_get(dhcp_client.relay_instance); + if (!server_address) { + server_address = srv_data_ptr->server_address; + } + + srv_data_ptr->transActionId = dhcp_service_send_req(dhcp_client.service_instance, 0, srv_data_ptr, server_address, payload_ptr, payload_len, dhcp_solicit_resp_cb); if (srv_data_ptr->transActionId == 0) { ns_dyn_mem_free(payload_ptr); - addr->state_timer = 200; //Retry after 20 seconds + if (addr) { + addr->state_timer = 200; //Retry after 20 seconds + } tr_error("DHCP renew send failed"); } if (packetReq.messageType == DHCPV6_SOLICATION_TYPE && dhcp_client.sol_timeout != 0) { @@ -357,7 +448,7 @@ void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t } } -void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr) +static bool dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr) { protocol_interface_info_entry_t *cur = NULL; if_address_entry_t *address_entry = NULL; @@ -365,10 +456,11 @@ void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { - return; + return false; } renewTimer = libdhcpv6_renew_time_define(srv_data_ptr); + srv_data_ptr->iaNonTemporalStructValid = true; address_entry = addr_get_entry(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); if (address_entry == NULL) { // create new @@ -380,8 +472,10 @@ void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t if (address_entry == NULL) { tr_error("Address add failed"); - return; + srv_data_ptr->iaNonTemporalStructValid = false; + return false; } + if (renewTimer) { // translate seconds to 100ms ticks if (renewTimer < 0xffffffff / 10) { @@ -392,6 +486,7 @@ void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t } address_entry->state_timer = renewTimer; address_entry->cb = dhcpv6_renew; + return true; } #endif diff --git a/source/MAC/IEEE802_15_4/mac_defines.h b/source/MAC/IEEE802_15_4/mac_defines.h index d5623049ed..232496c1dd 100644 --- a/source/MAC/IEEE802_15_4/mac_defines.h +++ b/source/MAC/IEEE802_15_4/mac_defines.h @@ -192,6 +192,7 @@ typedef struct protocol_interface_rf_mac_setup { unsigned macCurrentBE: 4; uint8_t macMaxCSMABackoffs; uint8_t backoff_period_in_10us; // max 2550us - it's 320us for standard 250kbps + uint8_t mac_frame_filters; /* MAC channel parameters */ channel_list_s mac_channel_list; uint8_t scan_duration; //Needed??? @@ -219,6 +220,8 @@ typedef struct protocol_interface_rf_mac_setup { uint16_t mac_ack_wait_duration; uint8_t mac_mlme_retry_max; uint8_t aUnitBackoffPeriod; + uint8_t number_of_csma_ca_periods; /**< Number of CSMA-CA periods */ + uint16_t multi_cca_interval; /**< Length of the additional CSMA-CA period(s) in microseconds */ /* Indirect queue parameters */ struct mac_pre_build_frame *indirect_pd_data_request_queue; arm_event_t mac_mcps_timer_event; diff --git a/source/MAC/IEEE802_15_4/mac_header_helper_functions.c b/source/MAC/IEEE802_15_4/mac_header_helper_functions.c index 7865cc7cf7..5f8273bb6b 100644 --- a/source/MAC/IEEE802_15_4/mac_header_helper_functions.c +++ b/source/MAC/IEEE802_15_4/mac_header_helper_functions.c @@ -267,6 +267,9 @@ const uint8_t *mac_header_parse_fcf_dsn(mac_fcf_sequence_t *header, const uint8_ } else { header->DSN = 0; } + //Check PanID presents at header + header->DstPanPresents = mac_dst_panid_present(header); + header->SrcPanPresents = mac_src_panid_present(header); return ptr; } diff --git a/source/MAC/IEEE802_15_4/mac_mcps_sap.c b/source/MAC/IEEE802_15_4/mac_mcps_sap.c index b884b2c0f1..4035ec7ddf 100644 --- a/source/MAC/IEEE802_15_4/mac_mcps_sap.c +++ b/source/MAC/IEEE802_15_4/mac_mcps_sap.c @@ -362,8 +362,6 @@ static int8_t mac_virtual_data_req_handler(protocol_interface_rf_mac_setup_s *rf } mac_header_parse_fcf_dsn(&buffer->fcf_dsn, data_ptr); - buffer->fcf_dsn.DstPanPresents = mac_dst_panid_present(&buffer->fcf_dsn); - buffer->fcf_dsn.SrcPanPresents = mac_src_panid_present(&buffer->fcf_dsn); // Use MAC sequence as handle buffer->msduHandle = buffer->fcf_dsn.DSN; memcpy(buffer->mac_payload, data_ptr, data_length); @@ -982,11 +980,6 @@ static void mac_data_interface_frame_handler(mac_pre_parsed_frame_t *buf) mcps_sap_pre_parsed_frame_buffer_free(buf); return; } - - if (mac_filter_modify_link_quality(rf_mac_setup->mac_interface_id, buf) == 1) { - mcps_sap_pre_parsed_frame_buffer_free(buf); - return; - } /* push data to stack if sniffer mode is enabled */ if (rf_mac_setup->macProminousMode) { mac_nap_tun_data_handler(buf, rf_mac_setup); @@ -1423,13 +1416,20 @@ static void mcps_data_confirm_handle(protocol_interface_rf_mac_setup_s *rf_ptr, // FHSS checks if this failed buffer needs to be pushed back to TX queue and retransmitted if ((rf_ptr->mac_tx_result == MAC_TX_FAIL) || (rf_ptr->mac_tx_result == MAC_CCA_FAIL)) { if (rf_ptr->fhss_api->data_tx_fail(rf_ptr->fhss_api, buffer->msduHandle, mac_convert_frame_type_to_fhss(buffer->fcf_dsn.frametype)) == true) { + + if (rf_ptr->mac_tx_result == MAC_TX_FAIL) { + buffer->fhss_retry_count += 1 + rf_ptr->mac_tx_status.retry; + } else { + buffer->fhss_retry_count += rf_ptr->mac_tx_status.retry; + } + buffer->fhss_cca_retry_count += rf_ptr->mac_tx_status.cca_cnt; mcps_sap_pd_req_queue_write(rf_ptr, buffer); return; } } } - confirm.cca_retries = rf_ptr->mac_tx_status.cca_cnt; - confirm.tx_retries = rf_ptr->mac_tx_status.retry; + confirm.cca_retries = rf_ptr->mac_tx_status.cca_cnt + buffer->fhss_cca_retry_count; + confirm.tx_retries = rf_ptr->mac_tx_status.retry + buffer->fhss_retry_count; mac_common_data_confirmation_handle(rf_ptr, buffer); confirm.msduHandle = buffer->msduHandle; confirm.status = buffer->status; @@ -1600,9 +1600,8 @@ static int8_t mcps_generic_packet_build(protocol_interface_rf_mac_setup_s *rf_pt return 0; } -int8_t mcps_generic_ack_build(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf, const uint8_t *data_ptr, const mcps_ack_data_payload_t *ack_payload, uint32_t rx_time) +int8_t mcps_generic_ack_build(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf, const uint8_t *data_ptr, const mcps_ack_data_payload_t *ack_payload) { - (void)rx_time; phy_device_driver_s *dev_driver = rf_ptr->dev_driver->phy_driver; dev_driver_tx_buffer_s *tx_buf = &rf_ptr->dev_driver_tx_buffer; @@ -1740,7 +1739,7 @@ int8_t mcps_generic_ack_build(protocol_interface_rf_mac_setup_s *rf_ptr, const m rf_ptr->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_CSMA_PARAMETERS, (uint8_t *) &csma_params); if (rf_ptr->active_pd_data_request) { timer_mac_stop(rf_ptr); - mac_pd_sap_set_phy_tx_time(rf_ptr, 0, false); + mac_pd_abort_active_tx(rf_ptr); } return mcps_pd_data_cca_trig(rf_ptr, buffer); } @@ -1828,6 +1827,13 @@ static int8_t mcps_pd_data_cca_trig(protocol_interface_rf_mac_setup_s *rf_ptr, m } cca_enabled = true; } + // Use double CCA check with FHSS for data packets only + if (rf_ptr->fhss_api && !rf_ptr->mac_ack_tx_active && !rf_ptr->active_pd_data_request->asynch_request) { + if ((buffer->tx_time - (rf_ptr->multi_cca_interval * (rf_ptr->number_of_csma_ca_periods - 1))) > mac_mcps_sap_get_phy_timestamp(rf_ptr)) { + buffer->csma_periods_left = rf_ptr->number_of_csma_ca_periods - 1; + buffer->tx_time -= (rf_ptr->multi_cca_interval * (rf_ptr->number_of_csma_ca_periods - 1)); + } + } mac_pd_sap_set_phy_tx_time(rf_ptr, buffer->tx_time, cca_enabled); if (mac_plme_cca_req(rf_ptr) != 0) { if (buffer->fcf_dsn.frametype == MAC_FRAME_ACK) { diff --git a/source/MAC/IEEE802_15_4/mac_mcps_sap.h b/source/MAC/IEEE802_15_4/mac_mcps_sap.h index d419b8089d..a4f4baeb73 100644 --- a/source/MAC/IEEE802_15_4/mac_mcps_sap.h +++ b/source/MAC/IEEE802_15_4/mac_mcps_sap.h @@ -57,6 +57,11 @@ typedef enum { #define MAC_SAP_TRIG_TX 7 #define MCPS_SAP_DATA_ACK_CNF_EVENT 8 +// Default number of CSMA-CA periods +#define MAC_DEFAULT_NUMBER_OF_CSMA_PERIODS 1 +// Interval between two CCA checks +#define MAC_DEFAULT_CSMA_MULTI_CCA_INTERVAL 1000 + /** * @brief struct mac_aux_security_header_t MAC auxiliarity security header structure * INTERNAL use only @@ -126,6 +131,9 @@ typedef struct mac_pre_build_frame { uint8_t *mac_payload; uint8_t status; uint8_t asynch_channel; + uint8_t csma_periods_left; + uint8_t fhss_retry_count; + uint8_t fhss_cca_retry_count; uint32_t tx_time; bool upper_layer_request; bool mac_allocated_payload_ptr: 1; @@ -206,6 +214,6 @@ uint8_t mcps_sap_purge_reg_handler(struct protocol_interface_rf_mac_setup *rf_ma int8_t mcps_pd_data_rebuild(struct protocol_interface_rf_mac_setup *rf_ptr, mac_pre_build_frame_t *buffer); -int8_t mcps_generic_ack_build(struct protocol_interface_rf_mac_setup *rf_ptr, const mac_fcf_sequence_t *fcf, const uint8_t *data_ptr, const mcps_ack_data_payload_t *ack_payload, uint32_t rx_time); +int8_t mcps_generic_ack_build(struct protocol_interface_rf_mac_setup *rf_ptr, const mac_fcf_sequence_t *fcf, const uint8_t *data_ptr, const mcps_ack_data_payload_t *ack_payload); #endif /* MAC_IEEE802_15_4_MAC_MCPS_SAP_H_ */ diff --git a/source/MAC/IEEE802_15_4/mac_mlme.c b/source/MAC/IEEE802_15_4/mac_mlme.c index 03eb5fdfae..bea62f2676 100644 --- a/source/MAC/IEEE802_15_4/mac_mlme.c +++ b/source/MAC/IEEE802_15_4/mac_mlme.c @@ -582,7 +582,7 @@ static int8_t mac_mlme_8bit_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, break; case macMinBE: - if (value > rf_mac_setup->macMaxBE) { + if (value < rf_mac_setup->macMaxBE) { rf_mac_setup->macMinBE = value; } break; @@ -724,6 +724,16 @@ static int8_t mac_mlme_handle_set_values(protocol_interface_rf_mac_setup_s *rf_m return -1; } +static int8_t mac_mlme_set_multi_csma_parameters(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req) +{ + mlme_multi_csma_ca_param_t multi_csma_params; + memcpy(&multi_csma_params, set_req->value_pointer, sizeof(mlme_multi_csma_ca_param_t)); + rf_mac_setup->multi_cca_interval = multi_csma_params.multi_cca_interval; + rf_mac_setup->number_of_csma_ca_periods = multi_csma_params.number_of_csma_ca_periods; + tr_debug("Multi CSMA-CA, interval: %u, periods %u", rf_mac_setup->multi_cca_interval, rf_mac_setup->number_of_csma_ca_periods); + return 0; +} + int8_t mac_mlme_set_req(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req) { if (!set_req || !rf_mac_setup || !rf_mac_setup->dev_driver || !rf_mac_setup->dev_driver->phy_driver) { @@ -749,6 +759,8 @@ int8_t mac_mlme_set_req(protocol_interface_rf_mac_setup_s *rf_mac_setup, const m memcpy(rf_mac_setup->coord_long_address, set_req->value_pointer, 8); } return 0; + case macMultiCSMAParameters: + return mac_mlme_set_multi_csma_parameters(rf_mac_setup, set_req); case macRfConfiguration: rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_RF_CONFIGURATION, (uint8_t *) set_req->value_pointer); mac_mlme_set_symbol_rate(rf_mac_setup); @@ -1072,6 +1084,8 @@ protocol_interface_rf_mac_setup_s *mac_mlme_data_base_allocate(uint8_t *mac64, a entry->mac_interface_id = -1; entry->dev_driver = dev_driver; entry->aUnitBackoffPeriod = 20; //This can be different in some Platform 20 comes from 12-symbol turnaround and 8 symbol CCA read + entry->number_of_csma_ca_periods = MAC_DEFAULT_NUMBER_OF_CSMA_PERIODS; + entry->multi_cca_interval = MAC_DEFAULT_CSMA_MULTI_CCA_INTERVAL; if (mac_sec_mib_init(entry, storage_sizes) != 0) { mac_mlme_data_base_deallocate(entry); @@ -1140,6 +1154,10 @@ protocol_interface_rf_mac_setup_s *mac_mlme_data_base_allocate(uint8_t *mac64, a bool rf_support = false; dev_driver->phy_driver->extension(PHY_EXTENSION_DYNAMIC_RF_SUPPORTED, (uint8_t *)&rf_support); entry->rf_csma_extension_supported = rf_support; + dev_driver->phy_driver->extension(PHY_EXTENSION_FILTERING_SUPPORT, (uint8_t *)&entry->mac_frame_filters); + if (entry->mac_frame_filters & (1 << MAC_FRAME_VERSION_2)) { + tr_debug("PHY supports 802.15.4-2015 frame filtering"); + } mac_mlme_set_symbol_rate(entry); //How many 10us ticks backoff period is for waiting 20symbols which is typically 10 bytes time diff --git a/source/MAC/IEEE802_15_4/mac_pd_sap.c b/source/MAC/IEEE802_15_4/mac_pd_sap.c index 0e1a760b7f..9157c44454 100644 --- a/source/MAC/IEEE802_15_4/mac_pd_sap.c +++ b/source/MAC/IEEE802_15_4/mac_pd_sap.c @@ -41,7 +41,7 @@ // Measured 3750us with 1280 byte secured packet from calculating TX time to starting CSMA timer on PHY. // Typically varies from 500us to several milliseconds depending on packet size and the platform. // MAC should learn and make this dynamic by sending first few packets with predefined CSMA period. -#define MIN_FHSS_CSMA_PERIOD_US 4000 +#define MIN_FHSS_CSMA_PERIOD_US 5000 static int8_t mac_data_interface_tx_done_cb(protocol_interface_rf_mac_setup_s *rf_ptr, phy_link_tx_status_e status, uint8_t cca_retry, uint8_t tx_retry); static void mac_sap_cca_fail_cb(protocol_interface_rf_mac_setup_s *rf_ptr); @@ -83,6 +83,10 @@ void mac_csma_backoff_start(protocol_interface_rf_mac_setup_s *rf_mac_setup) uint32_t mac_csma_backoff_get(protocol_interface_rf_mac_setup_s *rf_mac_setup) { + // Use minimum allowed CSMA-CA for asynch frames + if (rf_mac_setup->active_pd_data_request->asynch_request) { + return MIN_FHSS_CSMA_PERIOD_US; + } uint8_t backoff = mac_csma_random_backoff_get(rf_mac_setup); uint32_t backoff_in_us; //Multiple aUnitBackoffPeriod symbol time @@ -101,6 +105,13 @@ uint32_t mac_csma_backoff_get(protocol_interface_rf_mac_setup_s *rf_mac_setup) if (backoff_in_us < MIN_FHSS_CSMA_PERIOD_US) { backoff_in_us += MIN_FHSS_CSMA_PERIOD_US; } + // Backoff must be long enough to make multiple CCA checks + if (backoff_in_us < (uint32_t)(rf_mac_setup->multi_cca_interval * (rf_mac_setup->number_of_csma_ca_periods - 1))) { + backoff_in_us += ((rf_mac_setup->multi_cca_interval * (rf_mac_setup->number_of_csma_ca_periods - 1)) - backoff_in_us); + } + if (rf_mac_setup->mac_tx_retry) { + backoff_in_us += rf_mac_setup->fhss_api->get_retry_period(rf_mac_setup->fhss_api, rf_mac_setup->active_pd_data_request->DstAddr, rf_mac_setup->dev_driver->phy_driver->phy_MTU); + } } return backoff_in_us; } @@ -199,6 +210,20 @@ int8_t mac_pd_sap_req(protocol_interface_rf_mac_setup_s *rf_mac_setup) } +/** + * Abort active PHY transmission. + * + * \param rf_mac_setup pointer to MAC. + * + */ +void mac_pd_abort_active_tx(protocol_interface_rf_mac_setup_s *rf_mac_setup) +{ + phy_csma_params_t csma_params; + // Set TX time to 0 to abort current transmission + csma_params.backoff_time = 0; + rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_SET_CSMA_PARAMETERS, (uint8_t *) &csma_params); +} + /** * Set PHY TX time. * @@ -227,6 +252,9 @@ void mac_pd_sap_set_phy_tx_time(protocol_interface_rf_mac_setup_s *rf_mac_setup, */ static uint32_t mac_pd_sap_get_phy_rx_time(protocol_interface_rf_mac_setup_s *rf_mac_setup) { + if (!rf_mac_setup->rf_csma_extension_supported) { + return 0; + } uint8_t rx_time_buffer[4]; rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_READ_RX_TIME, rx_time_buffer); return common_read_32_bit(rx_time_buffer); @@ -341,6 +369,10 @@ static void mac_sap_no_ack_cb(protocol_interface_rf_mac_setup_s *rf_ptr) rf_ptr->mac_cca_retry = 0; rf_ptr->mac_tx_retry++; //Update retry counter mac_csma_param_init(rf_ptr); + // Increase current backoff exponent when retry count grows + for (int retry_index = rf_ptr->mac_tx_retry; retry_index > 0; retry_index--) { + mac_csma_BE_update(rf_ptr); + } rf_ptr->mac_tx_status.retry++; /*Send retry using random interval*/ if (mcps_pd_data_rebuild(rf_ptr, rf_ptr->active_pd_data_request)) { @@ -387,17 +419,17 @@ static int8_t mac_data_interface_tx_done_cb(protocol_interface_rf_mac_setup_s *r if (status == PHY_LINK_CCA_PREPARE) { if (rf_ptr->mac_ack_tx_active) { - return 0; + return PHY_TX_ALLOWED; } if (mac_data_asynch_channel_switch(rf_ptr, rf_ptr->active_pd_data_request)) { - return 0; + return PHY_TX_ALLOWED; } if (rf_ptr->fhss_api) { mac_pre_build_frame_t *active_buf = rf_ptr->active_pd_data_request; if (!active_buf) { - return -1; + return PHY_TX_NOT_ALLOWED; } // Change to destination channel and write synchronization info to Beacon frames here @@ -408,16 +440,22 @@ static int8_t mac_data_interface_tx_done_cb(protocol_interface_rf_mac_setup_s *r // When FHSS TX handle returns -1, transmission of the packet is currently not allowed -> restart CCA timer if (tx_handle_retval == -1) { mac_sap_cca_fail_cb(rf_ptr); - return -2; + return PHY_TX_NOT_ALLOWED; } // When FHSS TX handle returns -3, we are trying to transmit broadcast packet on unicast channel -> push back // to queue by using CCA fail event if (tx_handle_retval == -3) { mac_tx_done_state_set(rf_ptr, MAC_CCA_FAIL); - return -3; + return PHY_TX_NOT_ALLOWED; } else if (tx_handle_retval == -2) { mac_tx_done_state_set(rf_ptr, MAC_UNKNOWN_DESTINATION); - return -2; + return PHY_TX_NOT_ALLOWED; + } + if (active_buf->csma_periods_left > 0) { + active_buf->csma_periods_left--; + active_buf->tx_time += rf_ptr->multi_cca_interval; + mac_pd_sap_set_phy_tx_time(rf_ptr, active_buf->tx_time, true); + return PHY_RESTART_CSMA; } } @@ -479,6 +517,9 @@ static int8_t mac_data_interface_tx_done_cb(protocol_interface_rf_mac_setup_s *r mac_sap_cca_fail_cb(rf_ptr); break; + case PHY_LINK_CCA_OK: + break; + case PHY_LINK_TX_FAIL: mac_sap_no_ack_cb(rf_ptr); break; @@ -531,7 +572,7 @@ static int8_t mac_data_interface_tx_done_by_ack_cb(protocol_interface_rf_mac_set return 0; } -static bool mac_pd_sap_ack_validation(protocol_interface_rf_mac_setup_s *rf_ptr, mac_fcf_sequence_t *fcf_dsn, const uint8_t *data_ptr) +static bool mac_pd_sap_ack_validation(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf_dsn, const uint8_t *data_ptr) { if (!rf_ptr->active_pd_data_request || !rf_ptr->active_pd_data_request->fcf_dsn.ackRequested) { return false; //No active Data request anymore or no ACK request for current TX @@ -587,10 +628,227 @@ static bool mac_pd_sap_ack_validation(protocol_interface_rf_mac_setup_s *rf_ptr, return true; } +static int8_t mac_pd_sap_validate_fcf(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf_read, arm_pd_sap_generic_ind_t *pd_data_ind) +{ + switch (fcf_read->frametype) { + case FC_DATA_FRAME: + if (fcf_read->SrcAddrMode == MAC_ADDR_MODE_NONE) { + return -1; + } else if (fcf_read->DstAddrMode == MAC_ADDR_MODE_NONE && fcf_read->frameVersion != MAC_FRAME_VERSION_2015) { + return -1; + } + break; + case FC_BEACON_FRAME: + if (fcf_read->SrcAddrMode == MAC_ADDR_MODE_NONE || fcf_read->DstAddrMode != MAC_ADDR_MODE_NONE) { + return -1; + } + break; + case FC_ACK_FRAME: + // Only accept version 2015 Acks + if (fcf_read->frameVersion != MAC_FRAME_VERSION_2015) { + return -1; + } + //Validate Ack doesn't request Ack + if (fcf_read->ackRequested) { + return -1; + } + //Validate ACK + if (!mac_pd_sap_ack_validation(rf_ptr, fcf_read, pd_data_ind->data_ptr)) { + return -1; + } + break; + case FC_CMD_FRAME: + break; + default: + return -1; + } + return 0; +} + +static bool mac_pd_sap_panid_filter_common(const uint8_t *mac_header, const mac_fcf_sequence_t *fcf_read, uint16_t own_pan_id) +{ + // Beacon frames shouldn't be dropped as they might be used by load balancing + if (fcf_read->frametype == MAC_FRAME_BEACON) { + return true; + } + if (own_pan_id == 0xffff) { + return true; + } + uint16_t dst_pan_id = mac_header_get_dst_panid(fcf_read, mac_header, 0xffff); + if (dst_pan_id == 0xffff) { + return true; + } + if (own_pan_id == dst_pan_id) { + return true; + } + return false; +} + +static bool mac_pd_sap_panid_v2_filter(const uint8_t *ptr, const mac_fcf_sequence_t *fcf_read, uint16_t pan_id) +{ + if ((fcf_read->DstAddrMode == MAC_ADDR_MODE_NONE) && (fcf_read->frametype == FC_DATA_FRAME || fcf_read->frametype == FC_CMD_FRAME)) { + return true; + } + if ((fcf_read->DstAddrMode == MAC_ADDR_MODE_64_BIT) && (fcf_read->SrcAddrMode == MAC_ADDR_MODE_64_BIT) && fcf_read->intraPan) { + return true; + } + return mac_pd_sap_panid_filter_common(ptr, fcf_read, pan_id); +} + +static bool mac_pd_sap_addr_filter_common(const uint8_t *mac_header, const mac_fcf_sequence_t *fcf_read, uint8_t *mac_64bit_addr, uint16_t mac_16bit_addr) +{ + uint8_t cmp_table[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + uint8_t dst_addr[8]; + mac_header_get_dst_address(fcf_read, mac_header, dst_addr); + + switch (fcf_read->DstAddrMode) { + case MAC_ADDR_MODE_16_BIT: + if (!memcmp(dst_addr, cmp_table, 2)) { + return true; + } + uint8_t temp[2]; + common_write_16_bit(mac_16bit_addr, temp); + if (!memcmp(temp, dst_addr, 2)) { + return true; + } + break; + case MAC_ADDR_MODE_64_BIT: + if (!memcmp(dst_addr, cmp_table, 8)) { + return true; + } + if (!memcmp(mac_64bit_addr, dst_addr, 8)) { + return true; + } + break; + case MAC_ADDR_MODE_NONE: + return true; + break; + default: + break; + } + return false; +} + +static bool mac_pd_sap_addr_v2_filter(const uint8_t *mac_header, const mac_fcf_sequence_t *fcf_read, uint8_t *mac_64bit_addr, uint16_t mac_16bit_addr) +{ + return mac_pd_sap_addr_filter_common(mac_header, fcf_read, mac_64bit_addr, mac_16bit_addr); +} + +static bool mac_pd_sap_rx_filter(const uint8_t *mac_header, const mac_fcf_sequence_t *fcf_read, uint8_t phy_filter_mask, uint8_t *mac_64bit_addr, uint16_t mac_16bit_addr, uint16_t pan_id) +{ + uint8_t version = fcf_read->frameVersion; + + if (version == MAC_FRAME_VERSION_2015 && !(phy_filter_mask & (1 << MAC_FRAME_VERSION_2))) { + if (!mac_pd_sap_panid_v2_filter(mac_header, fcf_read, pan_id)) { + return false; + } + if (!mac_pd_sap_addr_v2_filter(mac_header, fcf_read, mac_64bit_addr, mac_16bit_addr)) { + return false; + } + } + return true; +} + +static int8_t mac_pd_sap_generate_ack(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf_read, arm_pd_sap_generic_ind_t *pd_data_ind) +{ + //Generate ACK when Extension is enabled and ACK is requested only for version 2 frames. + if (!rf_ptr->mac_extension_enabled || !fcf_read->ackRequested || (fcf_read->frameVersion != MAC_FRAME_VERSION_2015)) { + return 0; + } + if (rf_ptr->mac_ack_tx_active) { + return -1; + } + + mcps_ack_data_payload_t ack_payload; + mac_api_t *mac_api = get_sw_mac_api(rf_ptr); + mac_api->enhanced_ack_data_req_cb(mac_api, &ack_payload, pd_data_ind->dbm, pd_data_ind->link_quality); + //Calculate Delta time + + return mcps_generic_ack_build(rf_ptr, fcf_read, pd_data_ind->data_ptr, &ack_payload); +} + +static mac_pre_parsed_frame_t *mac_pd_sap_allocate_receive_buffer(protocol_interface_rf_mac_setup_s *rf_ptr, const mac_fcf_sequence_t *fcf_read, arm_pd_sap_generic_ind_t *pd_data_ind) +{ + mac_pre_parsed_frame_t *buffer = mcps_sap_pre_parsed_frame_buffer_get(pd_data_ind->data_ptr, pd_data_ind->data_len); + if (!buffer) { + return NULL; + } + + //Copy Pre Parsed values + buffer->fcf_dsn = *fcf_read; + buffer->timestamp = mac_pd_sap_get_phy_rx_time(rf_ptr); + buffer->ack_pendinfg_status = mac_data_interface_read_last_ack_pending_status(rf_ptr); + /* Set default flags */ + buffer->dbm = pd_data_ind->dbm; + buffer->LQI = pd_data_ind->link_quality; + buffer->mac_class_ptr = rf_ptr; + return buffer; +} + +static int8_t mac_pd_sap_parse_length_fields(mac_pre_parsed_frame_t *buffer, arm_pd_sap_generic_ind_t *pd_data_ind, const uint8_t *parse_ptr) +{ + if (buffer->fcf_dsn.frametype > FC_CMD_FRAME) { + return -1; + } + buffer->mac_header_length = parse_ptr - pd_data_ind->data_ptr; + int16_t length = pd_data_ind->data_len; + buffer->mac_header_length += mac_header_address_length(&buffer->fcf_dsn); + length -= buffer->mac_header_length; + if (length < 0) { + return -1; + } + + buffer->mac_payload_length = (buffer->frameLength - buffer->mac_header_length); + + if (buffer->fcf_dsn.securityEnabled) { + //Read KEYID Mode + uint8_t key_id_mode, security_level, mic_len; + uint8_t *security_ptr = &buffer->buf[buffer->mac_header_length]; + uint8_t auxBaseHeader = *security_ptr; + key_id_mode = (auxBaseHeader >> 3) & 3; + security_level = auxBaseHeader & 7; + + switch (key_id_mode) { + case MAC_KEY_ID_MODE_IMPLICIT: + if (security_level) { + buffer->security_aux_header_length = 5; + } else { + buffer->security_aux_header_length = 1; + } + break; + case MAC_KEY_ID_MODE_IDX: + buffer->security_aux_header_length = 6; + break; + case MAC_KEY_ID_MODE_SRC4_IDX: + buffer->security_aux_header_length = 10; + break; + default: + buffer->security_aux_header_length = 14; + break; + } + + length -= buffer->security_aux_header_length; + mic_len = mac_security_mic_length_get(security_level); + + length -= mic_len; + + //Verify that data length is not negative + if (length < 0) { + return -1; + } + + buffer->mac_payload_length -= (buffer->security_aux_header_length + mic_len); + } + //Do not accept command frame with length 0 + if (buffer->fcf_dsn.frametype == FC_CMD_FRAME && length == 0) { + return -1; + } + return 0; +} + int8_t mac_pd_sap_data_cb(void *identifier, arm_phy_sap_msg_t *message) { protocol_interface_rf_mac_setup_s *rf_ptr = (protocol_interface_rf_mac_setup_s *)identifier; - mac_pre_parsed_frame_t *buffer = NULL; if (!rf_ptr || !message) { return -1; @@ -601,184 +859,55 @@ int8_t mac_pd_sap_data_cb(void *identifier, arm_phy_sap_msg_t *message) } if (message->id == MAC15_4_PD_SAP_DATA_IND) { - const uint8_t *ptr; arm_pd_sap_generic_ind_t *pd_data_ind = &(message->message.generic_data_ind); - if (pd_data_ind->data_len < 3) { return -1; } - ptr = pd_data_ind->data_ptr; - uint32_t time_stamp = 0; - if (rf_ptr->rf_csma_extension_supported) { - time_stamp = mac_pd_sap_get_phy_rx_time(rf_ptr); - } mac_fcf_sequence_t fcf_read; - ptr = mac_header_parse_fcf_dsn(&fcf_read, ptr); - //Check PanID presents at header - fcf_read.DstPanPresents = mac_dst_panid_present(&fcf_read); - fcf_read.SrcPanPresents = mac_src_panid_present(&fcf_read); - int16_t length = pd_data_ind->data_len; - if (!rf_ptr->macProminousMode) { + const uint8_t *ptr = mac_header_parse_fcf_dsn(&fcf_read, pd_data_ind->data_ptr); - //Unsupported Frame - if (fcf_read.frametype > FC_CMD_FRAME || (fcf_read.frametype == FC_ACK_FRAME && fcf_read.frameVersion != MAC_FRAME_VERSION_2015)) { + mac_pre_parsed_frame_t *buffer = mac_pd_sap_allocate_receive_buffer(rf_ptr, &fcf_read, pd_data_ind); + if (buffer && mac_filter_modify_link_quality(rf_ptr->mac_interface_id, buffer) == 1) { + goto ERROR_HANDLER; + } + if (!rf_ptr->macProminousMode) { + if (mac_pd_sap_validate_fcf(rf_ptr, &fcf_read, pd_data_ind)) { goto ERROR_HANDLER; } - - switch (fcf_read.frametype) { - case FC_DATA_FRAME: - if (fcf_read.SrcAddrMode == MAC_ADDR_MODE_NONE) { - return -1; - } else if (fcf_read.DstAddrMode == MAC_ADDR_MODE_NONE && fcf_read.frameVersion != MAC_FRAME_VERSION_2015) { - return -1; - } - break; - case FC_BEACON_FRAME: - if (fcf_read.SrcAddrMode == MAC_ADDR_MODE_NONE || fcf_read.DstAddrMode != MAC_ADDR_MODE_NONE) { - return -1; - } - break; - case FC_ACK_FRAME: - //Validate here that we are waiting ack - if (fcf_read.ackRequested) { - return -1; - } - - //Validate ACK - if (!mac_pd_sap_ack_validation(rf_ptr, &fcf_read, pd_data_ind->data_ptr)) { - return -1; - } - break; - - default: - break; + if (!mac_pd_sap_rx_filter(pd_data_ind->data_ptr, &fcf_read, rf_ptr->mac_frame_filters, rf_ptr->mac64, rf_ptr->mac_short_address, rf_ptr->pan_id)) { + goto ERROR_HANDLER; } - - //Generate ACK when Extension is enabled and ACK is requested - if (rf_ptr->mac_extension_enabled && fcf_read.ackRequested && fcf_read.frameVersion == MAC_FRAME_VERSION_2015) { - //SEND ACK here - if (rf_ptr->mac_ack_tx_active) { - return -1; + if (mac_pd_sap_generate_ack(rf_ptr, &fcf_read, pd_data_ind)) { + goto ERROR_HANDLER; + } + if (buffer) { + if (mac_pd_sap_parse_length_fields(buffer, pd_data_ind, ptr)) { + goto ERROR_HANDLER; } - - mcps_ack_data_payload_t ack_payload; - mac_api_t *mac_api = get_sw_mac_api(rf_ptr); - mac_api->enhanced_ack_data_req_cb(mac_api, &ack_payload, pd_data_ind->dbm, pd_data_ind->link_quality); - //Calculate Delta time - - if (mcps_generic_ack_build(rf_ptr, &fcf_read, pd_data_ind->data_ptr, &ack_payload, time_stamp) != 0) { - return -1; + if (!mac_header_information_elements_parse(buffer)) { + goto ERROR_HANDLER; + } + if (buffer->fcf_dsn.frametype == FC_ACK_FRAME) { + if (mac_data_interface_tx_done_by_ack_cb(rf_ptr, buffer)) { + mcps_sap_pre_parsed_frame_buffer_free(buffer); + } + return 0; } } } - - buffer = mcps_sap_pre_parsed_frame_buffer_get(pd_data_ind->data_ptr, pd_data_ind->data_len); - if (!buffer) { sw_mac_stats_update(rf_ptr, STAT_MAC_RX_DROP, 0); return -3; } - - //Copy Pre Parsed values - buffer->fcf_dsn = fcf_read; - buffer->timestamp = time_stamp; - - buffer->ack_pendinfg_status = mac_data_interface_read_last_ack_pending_status(rf_ptr); - - - // Upward direction functions assume no headroom and are trusting that removed bytes are still valid. - // see mac.c:655 - - /* Set default flags */ - buffer->dbm = pd_data_ind->dbm; - buffer->LQI = pd_data_ind->link_quality; - buffer->mac_class_ptr = rf_ptr; - //Dnamic calculation for FCF + SEQ parse - buffer->mac_header_length = ptr - pd_data_ind->data_ptr; - - if (!rf_ptr->macProminousMode) { - - if (buffer->fcf_dsn.frametype > FC_CMD_FRAME) { - goto ERROR_HANDLER; - } - - buffer->mac_header_length += mac_header_address_length(&buffer->fcf_dsn); - - length -= buffer->mac_header_length; - - if (length < 0) { - goto ERROR_HANDLER; - } - - buffer->mac_payload_length = (buffer->frameLength - buffer->mac_header_length); - - if (buffer->fcf_dsn.securityEnabled) { - //Read KEYID Mode - uint8_t key_id_mode, security_level, mic_len; - uint8_t *security_ptr = &buffer->buf[buffer->mac_header_length]; - uint8_t auxBaseHeader = *security_ptr; - key_id_mode = (auxBaseHeader >> 3) & 3; - security_level = auxBaseHeader & 7; - - switch (key_id_mode) { - case MAC_KEY_ID_MODE_IMPLICIT: - if (security_level) { - buffer->security_aux_header_length = 5; - } else { - buffer->security_aux_header_length = 1; - } - break; - case MAC_KEY_ID_MODE_IDX: - buffer->security_aux_header_length = 6; - break; - case MAC_KEY_ID_MODE_SRC4_IDX: - buffer->security_aux_header_length = 10; - break; - default: - buffer->security_aux_header_length = 14; - break; - } - - length -= buffer->security_aux_header_length; - mic_len = mac_security_mic_length_get(security_level); - - length -= mic_len; - - //Verify that data length is not negative - if (length < 0) { - goto ERROR_HANDLER; - } - - buffer->mac_payload_length -= (buffer->security_aux_header_length + mic_len); - } - - //Do not accept command frame with length 0 - if (fcf_read.frametype == FC_CMD_FRAME && length == 0) { - goto ERROR_HANDLER; - } - - //Parse IE Elements - if (!mac_header_information_elements_parse(buffer)) { - goto ERROR_HANDLER; - } - } - - if (!rf_ptr->macProminousMode && buffer->fcf_dsn.frametype == FC_ACK_FRAME) { - if (mac_data_interface_tx_done_by_ack_cb(rf_ptr, buffer)) { - mcps_sap_pre_parsed_frame_buffer_free(buffer); - } + if (mcps_sap_pd_ind(buffer) == 0) { return 0; - } else { - if (mcps_sap_pd_ind(buffer) == 0) { - return 0; - } } ERROR_HANDLER: mcps_sap_pre_parsed_frame_buffer_free(buffer); + sw_mac_stats_update(rf_ptr, STAT_MAC_RX_DROP, 0); return -1; - } else if (message->id == MAC15_4_PD_SAP_DATA_TX_CONFIRM) { arm_pd_sap_15_4_confirm_with_params_t *pd_data_cnf = &(message->message.mac15_4_pd_sap_confirm); return mac_data_interface_tx_done_cb(rf_ptr, pd_data_cnf->status, pd_data_cnf->cca_retry, pd_data_cnf->tx_retry); diff --git a/source/MAC/IEEE802_15_4/mac_pd_sap.h b/source/MAC/IEEE802_15_4/mac_pd_sap.h index d6364ba527..9df0f4a0e5 100644 --- a/source/MAC/IEEE802_15_4/mac_pd_sap.h +++ b/source/MAC/IEEE802_15_4/mac_pd_sap.h @@ -39,6 +39,8 @@ int8_t mac_pd_sap_req(struct protocol_interface_rf_mac_setup *rf_mac_setup); int8_t mac_plme_cca_req(struct protocol_interface_rf_mac_setup *rf_mac_setup); +void mac_pd_abort_active_tx(struct protocol_interface_rf_mac_setup *rf_mac_setup); + void mac_pd_sap_set_phy_tx_time(struct protocol_interface_rf_mac_setup *rf_mac_setup, uint32_t tx_time, bool cca_enabled); void mac_pd_sap_rf_low_level_function_set(void *mac_ptr, void *driver); diff --git a/source/MPL/mpl.c b/source/MPL/mpl.c index ead9e8b6c5..1474c89187 100644 --- a/source/MPL/mpl.c +++ b/source/MPL/mpl.c @@ -33,6 +33,7 @@ #include "Service_Libs/Trickle/trickle.h" #include "6LoWPAN/MAC/mac_helper.h" #include "6LoWPAN/Thread/thread_common.h" +#include "6LoWPAN/ws/ws_common.h" #include "MPL/mpl.h" #define TRACE_GROUP "mpl" @@ -1105,7 +1106,8 @@ static buffer_t *mpl_exthdr_provider(buffer_t *buf, ipv6_exthdr_stage_t stage, i /* "Compress" seed ID if it's the IPv6 source address */ /* (For Thread, also compress if source is the 16-bit address) */ - if (seed_id_len == 16 && addr_ipv6_equal(seed_id, buf->src_sa.address)) { + /* (For Wi-sun, not support seed id address compression */ + if (!ws_info(buf->interface) && seed_id_len == 16 && addr_ipv6_equal(seed_id, buf->src_sa.address)) { seed_id_len = 0; } else if (seed_id_len == 2 && thread_addr_is_mesh_local_16(buf->src_sa.address, buf->interface) && seed_id[0] == buf->src_sa.address[14] && seed_id[1] == buf->src_sa.address[15]) { diff --git a/source/NWK_INTERFACE/Include/protocol.h b/source/NWK_INTERFACE/Include/protocol.h index b61bcbd3fa..7a0ec03529 100644 --- a/source/NWK_INTERFACE/Include/protocol.h +++ b/source/NWK_INTERFACE/Include/protocol.h @@ -121,6 +121,7 @@ typedef enum icmp_state { ER_BOOTSTRAP_SCAN_FAIL, ER_BOOTSTRAP_LEADER_UP, ER_BOOTSTRAP_NEW_FRAGMENT_START, + ER_WAIT_RESTART, ER_RPL_LOCAL_REPAIR, } icmp_state_t; @@ -514,4 +515,5 @@ extern void protocol_core_dhcpv6_allocated_address_remove(protocol_interface_inf extern void nwk_bootsrap_state_update(arm_nwk_interface_status_type_e posted_event, protocol_interface_info_entry_t *cur); void bootsrap_next_state_kick(icmp_state_t new_state, protocol_interface_info_entry_t *cur); int8_t protocol_interface_address_compare(const uint8_t *addr); +bool protocol_interface_any_address_match(const uint8_t *prefix, uint8_t prefix_len); #endif /* _NS_PROTOCOL_H */ diff --git a/source/NWK_INTERFACE/protocol_core.c b/source/NWK_INTERFACE/protocol_core.c index b4225aa1c9..a05ac5f55e 100644 --- a/source/NWK_INTERFACE/protocol_core.c +++ b/source/NWK_INTERFACE/protocol_core.c @@ -81,6 +81,7 @@ #include "6LoWPAN/Fragmentation/cipv6_fragmenter.h" #include "Service_Libs/load_balance/load_balance_api.h" #include "Service_Libs/pan_blacklist/pan_blacklist_api.h" +#include "Service_Libs/etx/etx.h" #include "mac_api.h" #include "ethernet_mac_api.h" @@ -260,6 +261,7 @@ void core_timer_event_handle(uint16_t ticksUpdate) if (cur->nwk_wpan_nvm_api) { cur->nwk_wpan_nvm_api->nvm_params_update_cb(cur->nwk_wpan_nvm_api, false); } + etx_cache_timer(cur->id, seconds); } } else if (cur->nwk_id == IF_IPV6) { //Slow Pointer Update @@ -300,6 +302,9 @@ void core_timer_event_handle(uint16_t ticksUpdate) ipv6_destination_cache_timer(seconds); ipv6_frag_timer(seconds); cipv6_frag_timer(seconds); +#ifdef HAVE_WS + ws_pae_controller_slow_timer(seconds); +#endif protocol_6lowpan_mle_timer(seconds); /* This limit bad behaviour device's MLE link reject generation */ @@ -339,7 +344,7 @@ void core_timer_event_handle(uint16_t ticksUpdate) icmpv6_radv_timer(ticksUpdate); protocol_core_security_tick_update(ticksUpdate); #ifdef HAVE_WS - ws_pae_controller_timer(ticksUpdate); + ws_pae_controller_fast_timer(ticksUpdate); #endif platform_enter_critical(); protocol_core_timer_info.core_timer_event = false; @@ -1135,3 +1140,27 @@ int8_t protocol_interface_address_compare(const uint8_t *addr) return -1; } +static bool protocol_address_prefix_cmp(protocol_interface_info_entry_t *interface, const uint8_t *prefix, uint8_t prefix_len) +{ + ns_list_foreach(if_address_entry_t, adr, &interface->ip_addresses) { + if (bitsequal(adr->address, prefix, prefix_len)) { + /* Prefix stil used at list so stop checking */ + return true; + } + } + return false; +} + +bool protocol_interface_any_address_match(const uint8_t *prefix, uint8_t prefix_len) +{ + ns_list_foreach(protocol_interface_info_entry_t, cur, &protocol_interface_info_list) { + + if (protocol_address_prefix_cmp(cur, prefix, prefix_len)) { + return true; + } + } + + return false; +} + + diff --git a/source/RPL/rpl_control.c b/source/RPL/rpl_control.c index 91f54154d3..963b2f3c19 100644 --- a/source/RPL/rpl_control.c +++ b/source/RPL/rpl_control.c @@ -151,17 +151,6 @@ static void rpl_control_publish_own_addresses(rpl_domain_t *domain, rpl_instance } } -static void rpl_control_publish_own_address(rpl_domain_t *domain, const if_address_entry_t *addr) -{ - ns_list_foreach(rpl_instance_t, instance, &domain->instances) { - if (!rpl_instance_am_root(instance)) { - uint32_t descriptor = 0; - bool want_descriptor = rpl_policy_target_descriptor_for_own_address(domain, addr->address, addr->source, addr->data, &descriptor); - rpl_instance_publish_dao_target(instance, addr->address, 128, addr->valid_lifetime, true, want_descriptor, descriptor); - } - } -} - void rpl_control_publish_host_address(rpl_domain_t *domain, const uint8_t addr[16], uint32_t lifetime) { ns_list_foreach(rpl_instance_t, instance, &domain->instances) { @@ -260,10 +249,6 @@ static void rpl_control_addr_notifier(struct protocol_interface_info_entry *inte } switch (reason) { - case ADDR_CALLBACK_DAD_COMPLETE: - case ADDR_CALLBACK_REFRESHED: - rpl_control_publish_own_address(interface->rpl_domain, addr); - break; case ADDR_CALLBACK_DELETED: rpl_control_unpublish_address(interface->rpl_domain, addr->address); break; @@ -345,11 +330,7 @@ void rpl_control_set_domain_on_interface(protocol_interface_info_entry_t *cur, r cur->rpl_domain = domain; addr_add_group(cur, ADDR_LINK_LOCAL_ALL_RPL_NODES); } - ns_list_foreach(if_address_entry_t, addr, &cur->ip_addresses) { - if (!addr_is_ipv6_link_local(addr->address)) { - rpl_control_publish_own_address(domain, addr); - } - } + if (downstream) { domain->non_storing_downstream_interface = cur->id; } @@ -690,8 +671,9 @@ static void rpl_control_process_prefix_options(protocol_interface_info_entry_t * bool router_addr_set = false; rpl_neighbour_t *pref_parent = rpl_instance_preferred_parent(instance); - -// const rpl_dodag_conf_t *conf = rpl_dodag_get_config(dodag); + if (neighbour == pref_parent) { + rpl_dodag_update_unpublished_dio_prefix_start(dodag); + } for (;;) { const uint8_t *ptr = rpl_control_find_option(start, end - start, RPL_PREFIX_INFO_OPTION, 30); @@ -735,6 +717,9 @@ static void rpl_control_process_prefix_options(protocol_interface_info_entry_t * start = ptr + 32; } + if (neighbour == pref_parent) { + rpl_dodag_update_unpublished_dio_prefix_finish(dodag); + } } void rpl_control_process_prefix_option(prefix_entry_t *prefix, protocol_interface_info_entry_t *cur) @@ -894,10 +879,6 @@ malformed: if (!instance) { return buffer_free(buf); } - - if ((g_mop_prf & RPL_MODE_MASK) != RPL_MODE_NO_DOWNWARD) { - rpl_control_publish_own_addresses(domain, instance); - } } /* Lookup any existing neighbour entry */ @@ -1156,7 +1137,7 @@ void rpl_control_transmit_dio(rpl_domain_t *domain, protocol_interface_info_entr } else { prefix->options &= ~ PIO_R; - if (rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING) { + if (rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING && prefix->lifetime != 0) { continue; } } @@ -1194,7 +1175,7 @@ void rpl_control_transmit_dio(rpl_domain_t *domain, protocol_interface_info_entr ns_list_foreach_safe(prefix_entry_t, prefix, prefixes) { /* See equivalent checks in length calculation above */ if ((prefix->options & (PIO_L | RPL_PIO_PUBLISHED)) == PIO_L || - (!(prefix->options & PIO_R) && rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING)) { + (!(prefix->options & PIO_R) && rpl_dodag_mop(dodag) == RPL_MODE_NON_STORING && prefix->lifetime != 0)) { continue; } @@ -1207,6 +1188,14 @@ void rpl_control_transmit_dio(rpl_domain_t *domain, protocol_interface_info_entr common_write_32_bit(0, ptr + 12); // reserved memcpy(ptr + 16, prefix->prefix, 16); ptr += 32; + /* Transmitting a multicast DIO decrements the hold count for 0 lifetime prefixes */ + if (dst == NULL && (prefix->options & RPL_PIO_AGE)) { + int hold_count = prefix->options & RPL_PIO_HOLD_MASK; + if (hold_count) { + hold_count--; + prefix->options = (prefix->options & ~RPL_PIO_HOLD_MASK) | hold_count; + } + } } ns_list_foreach_safe(rpl_dio_route_t, route, routes) { @@ -1616,6 +1605,7 @@ void rpl_control_slow_timer(uint16_t seconds) ns_list_foreach(rpl_domain_t, domain, &rpl_domains) { ns_list_foreach_safe(rpl_instance_t, instance, &domain->instances) { + rpl_control_publish_own_addresses(domain, instance); rpl_instance_slow_timer(instance, seconds); rpl_downward_dao_slow_timer(instance, seconds); /* We purge one item from each instance, so as not to favour one domain or instance */ @@ -1654,13 +1644,14 @@ rpl_instance_t *rpl_control_lookup_instance(rpl_domain_t *domain, uint8_t instan return rpl_lookup_instance(domain, instance_id, dodagid); } -bool rpl_control_get_instance_dao_target_count(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, uint16_t *target_count) +bool rpl_control_get_instance_dao_target_count(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, const uint8_t *prefix, uint16_t *target_count) { rpl_instance_t *instance = rpl_lookup_instance(domain, instance_id, dodagid); if (!instance) { return false; } - *target_count = rpl_upward_read_dao_target_list_size(instance); + + *target_count = rpl_upward_read_dao_target_list_size(instance, prefix); return true; } diff --git a/source/RPL/rpl_control.h b/source/RPL/rpl_control.h index 7e9f123e87..20b82351ce 100644 --- a/source/RPL/rpl_control.h +++ b/source/RPL/rpl_control.h @@ -160,7 +160,7 @@ void rpl_control_print(route_print_fn_t *print_fn); struct rpl_instance *rpl_control_enumerate_instances(rpl_domain_t *domain, struct rpl_instance *instance); struct rpl_instance *rpl_control_lookup_instance(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid); -bool rpl_control_get_instance_dao_target_count(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, uint16_t *target_count); +bool rpl_control_get_instance_dao_target_count(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid, const uint8_t *prefix, uint16_t *target_count); bool rpl_control_read_dodag_info(const struct rpl_instance *instance, struct rpl_dodag_info_t *dodag_info); const rpl_dodag_conf_t *rpl_control_get_dodag_config(const struct rpl_instance *instance); const uint8_t *rpl_control_preferred_parent_addr(const struct rpl_instance *instance, bool global); diff --git a/source/RPL/rpl_downward.c b/source/RPL/rpl_downward.c index 5f4c9f316a..a0359ecaea 100644 --- a/source/RPL/rpl_downward.c +++ b/source/RPL/rpl_downward.c @@ -345,8 +345,9 @@ void rpl_instance_publish_dao_target(rpl_instance_t *instance, const uint8_t *pr { rpl_dao_target_t *target = rpl_instance_lookup_published_dao_target(instance, prefix, prefix_len); if (target) { + int diff = target->lifetime > valid_lifetime ? target->lifetime - valid_lifetime : valid_lifetime - target->lifetime; target->lifetime = valid_lifetime; - if (!own) { + if (!own && diff > 60) { /* For non-owned targets, publish triggers a refresh */ rpl_downward_target_refresh(target); rpl_instance_dao_trigger(instance, 0); @@ -590,7 +591,7 @@ void rpl_instance_send_address_registration(protocol_interface_info_entry_t *int aro.status = ARO_SUCCESS; aro.present = true; - aro.lifetime = addr->valid_lifetime; + aro.lifetime = (addr->valid_lifetime / 60) + 1; memcpy(aro.eui64, interface->mac, 8); // go through neighbour list, and send to all assigned parents. diff --git a/source/RPL/rpl_upward.c b/source/RPL/rpl_upward.c index 5990fbdf6a..1f18507dad 100644 --- a/source/RPL/rpl_upward.c +++ b/source/RPL/rpl_upward.c @@ -932,6 +932,33 @@ const prefix_list_t *rpl_dodag_get_prefix_list(const rpl_dodag_t *dodag) { return &dodag->prefixes; } + +/* Called before updating all prefixes in a DIO */ +void rpl_dodag_update_unpublished_dio_prefix_start(rpl_dodag_t *dodag) +{ + /* Clear age flags - will use as a marker for entries being in the DIO */ + ns_list_foreach(prefix_entry_t, entry, &dodag->prefixes) { + if (!(entry->options & RPL_PIO_PUBLISHED)) { + entry->options &= ~RPL_PIO_AGE; + } + } +} + + +/* Called after updating all prefixes in a DIO */ +void rpl_dodag_update_unpublished_dio_prefix_finish(rpl_dodag_t *dodag) +{ + /* Any remaining non-published entries that don't have the age flag + * set are not being sent by parent any more, so we should stop sending + * too, except for the minimimum count requirement on 0 lifetime. + */ + ns_list_foreach_safe(prefix_entry_t, entry, &dodag->prefixes) { + if ((entry->options & (RPL_PIO_PUBLISHED | RPL_PIO_AGE | RPL_PIO_HOLD_MASK)) == 0) { + rpl_dodag_delete_dio_prefix(dodag, entry); + } + } +} + prefix_entry_t *rpl_dodag_update_dio_prefix(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, uint32_t preftime, bool publish, bool age) { /* Don't let them set funny flags - we won't propagate them either. @@ -948,20 +975,30 @@ prefix_entry_t *rpl_dodag_update_dio_prefix(rpl_dodag_t *dodag, const uint8_t *p flags |= RPL_PIO_AGE; } - if (lifetime == 0) { - flags |= RPL_MAX_FINAL_RTR_ADVERTISEMENTS; - } - - prefix_entry_t *entry = icmpv6_prefix_add(&dodag->prefixes, prefix, prefix_len, lifetime, preftime, flags); + prefix_entry_t *entry = icmpv6_prefix_add(&dodag->prefixes, prefix, prefix_len, lifetime, preftime, 0xff); /* icmpv6_prefix_add indicates a new entry by leaving options set to 0xFF */ if (entry) { + /* Newly-seen zero lifetimes should be advertised at least a few times - + * count this down in the RPL_PIO_HOLD_COUNT field + */ + if (lifetime == 0 && (entry->options == 0xFF || entry->lifetime != 0)) { + flags |= RPL_MAX_FINAL_RTR_ADVERTISEMENTS; + } entry->options = flags; + entry->lifetime = lifetime; + entry->preftime = preftime; } return entry; } void rpl_dodag_delete_dio_prefix(rpl_dodag_t *dodag, prefix_entry_t *prefix) { + rpl_instance_t *instance = dodag->instance; + + if (instance && instance->domain->prefix_cb) { + instance->domain->prefix_cb(prefix, instance->domain->cb_handle, NULL); + } + ns_list_remove(&dodag->prefixes, prefix); ns_dyn_mem_free(prefix); } @@ -984,7 +1021,11 @@ static void rpl_dodag_age_prefixes(rpl_dodag_t *dodag, uint16_t seconds) prefix->lifetime -= seconds; } else { prefix->lifetime = 0; - if ((prefix->options & RPL_PIO_HOLD_MASK) == 0) { + /* Only delete on timeout if we're publishing - otherwise we will + * keep advertising until we see our parent stop advertising it - deletion + * is handled in rpl_control_process_prefix_options. + */ + if ((prefix->options & (RPL_PIO_PUBLISHED | RPL_PIO_HOLD_MASK)) == RPL_PIO_PUBLISHED) { rpl_dodag_delete_dio_prefix(dodag, prefix); } } @@ -1742,8 +1783,19 @@ void rpl_upward_print_instance(rpl_instance_t *instance, route_print_fn_t *print } } -uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance) +uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance, const uint8_t *target_prefix) { + + if (target_prefix) { + uint16_t registered_address_count = 0; + ns_list_foreach(rpl_dao_target_t, target, &instance->dao_targets) { + if (bitsequal(target->prefix, target_prefix, 64)) { + registered_address_count++; + } + } + return registered_address_count; + } + return ns_list_count(&instance->dao_targets); } diff --git a/source/RPL/rpl_upward.h b/source/RPL/rpl_upward.h index 5c626fa10a..b34e15c9c5 100644 --- a/source/RPL/rpl_upward.h +++ b/source/RPL/rpl_upward.h @@ -112,6 +112,8 @@ rpl_dio_route_t *rpl_dodag_update_dio_route(rpl_dodag_t *dodag, const uint8_t *p void rpl_dodag_delete_dio_route(rpl_dodag_t *dodag, rpl_dio_route_t *route); const rpl_dio_route_list_t *rpl_dodag_get_route_list(const rpl_dodag_t *dodag); +void rpl_dodag_update_unpublished_dio_prefix_start(rpl_dodag_t *dodag); +void rpl_dodag_update_unpublished_dio_prefix_finish(rpl_dodag_t *dodag); prefix_entry_t *rpl_dodag_update_dio_prefix(rpl_dodag_t *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, uint32_t preftime, bool publish, bool age); void rpl_dodag_delete_dio_prefix(rpl_dodag_t *dodag, prefix_entry_t *prefix); const prefix_list_t *rpl_dodag_get_prefix_list(const rpl_dodag_t *dodag); @@ -143,5 +145,5 @@ void rpl_instance_run_parent_selection(rpl_instance_t *instance); void rpl_upward_print_instance(rpl_instance_t *instance, route_print_fn_t *print_fn); bool rpl_upward_read_dodag_info(const rpl_instance_t *instance, struct rpl_dodag_info_t *dodag_info); -uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance); +uint16_t rpl_upward_read_dao_target_list_size(const rpl_instance_t *instance, const uint8_t *target_prefix); #endif /* RPL_UPWARD_H_ */ diff --git a/source/Security/TLS/tls_lib.c b/source/Security/TLS/tls_lib.c index 99c206df68..07146da65c 100644 --- a/source/Security/TLS/tls_lib.c +++ b/source/Security/TLS/tls_lib.c @@ -1161,7 +1161,7 @@ buffer_t *tls_client_up(buffer_t *buf, sec_suite_t *tls_suite) if (tls_header_ptr) { if (tls_header_ptr->type == TLS_HANDSHAKE && (tls_heap != 0)) { tr_debug("Type:Handshake"); - if ((tls_suite->state == TLS_CHANGE_CHIPHER)) { + if (tls_suite->state == TLS_CHANGE_CHIPHER) { if (tls_header_ptr->length < 32) { tr_debug("Too short Chiher Text"); } else if ((algo_ok & 0x20) && (tls_suite->state == PRF_CALC)) { @@ -1477,7 +1477,7 @@ buffer_t *tls_server_up(buffer_t *buf, sec_suite_t *tls_suite) if (tls_header_ptr) { if (tls_header_ptr->type == TLS_HANDSHAKE && (tls_heap != 0)) { tr_debug("Type:Handshake"); - if ((tls_suite->state == TLS_CHANGE_CHIPHER)) { + if (tls_suite->state == TLS_CHANGE_CHIPHER) { if (tls_header_ptr->length < 32) { tr_debug("Too short Chiher Text"); } else if ((algo_ok & 0x20) && (tls_suite->state == PRF_CALC)) { diff --git a/source/Security/eapol/eapol_helper.c b/source/Security/eapol/eapol_helper.c index 64a45e3935..03d2afcb8a 100644 --- a/source/Security/eapol/eapol_helper.c +++ b/source/Security/eapol/eapol_helper.c @@ -274,5 +274,25 @@ uint16_t eapol_pdu_key_frame_init(eapol_pdu_t *eapol_pdu, uint16_t data_length, } +uint8_t eapol_pdu_key_mask_get(eapol_pdu_t *eapol_pdu) +{ + uint8_t key_mask = 0; + + if (eapol_pdu->msg.key.key_information.install) { + key_mask |= KEY_INFO_INSTALL; + } + if (eapol_pdu->msg.key.key_information.key_ack) { + key_mask |= KEY_INFO_KEY_ACK; + } + if (eapol_pdu->msg.key.key_information.key_mic) { + key_mask |= KEY_INFO_KEY_MIC; + } + if (eapol_pdu->msg.key.key_information.secured_key_frame) { + key_mask |= KEY_INFO_SECURED_KEY_FRAME; + } + + return key_mask; +} + #endif diff --git a/source/Security/eapol/eapol_helper.h b/source/Security/eapol/eapol_helper.h index 47439f1f79..d7fc8d5fe0 100644 --- a/source/Security/eapol/eapol_helper.h +++ b/source/Security/eapol/eapol_helper.h @@ -18,15 +18,16 @@ #ifndef EAPOL_HELPER_H_ #define EAPOL_HELPER_H_ -#define EAPOL_PROTOCOL_VERSION 3 -#define EAPOL_EAP_TYPE 0 -#define EAPOL_KEY_TYPE 3 -#define EAPOL_KEY_NONCE_LEN 32 -#define EAPOL_KEY_MIC_LEN 16 +#define EAPOL_PROTOCOL_VERSION 3 +#define EAPOL_EAP_TYPE 0 +#define EAPOL_KEY_TYPE 3 +#define EAPOL_KEY_NONCE_LEN 32 +#define EAPOL_KEY_MIC_LEN 16 +#define EAPOL_KEY_LEN 16 -#define EAPOL_BASE_LENGTH 4 //Protocol version 1 byte, Packet type 1 byte, packet length 2 byte +#define EAPOL_BASE_LENGTH 4 //Protocol version 1 byte, Packet type 1 byte, packet length 2 byte -#define EAPOL_KEY_FRAME_BASE_SIZE 95 +#define EAPOL_KEY_FRAME_BASE_SIZE 95 struct eap_header_t; @@ -96,4 +97,18 @@ uint16_t eapol_pdu_key_frame_init(eapol_pdu_t *eapol_pdu, uint16_t data_length, void eapol_write_key_packet_mic(uint8_t *eapol_pdu, uint8_t *mic); +#define KEY_INFO_INSTALL 0x01 +#define KEY_INFO_KEY_ACK 0x02 +#define KEY_INFO_KEY_MIC 0x04 +#define KEY_INFO_SECURED_KEY_FRAME 0x08 + +/** + * eapol_pdu_key_mask_get gets masked EAPOL-Key message bits + * + * \param eapol_pdu EAPOL PDU + * + * \return mask + */ +uint8_t eapol_pdu_key_mask_get(eapol_pdu_t *eapol_pdu); + #endif /* EAPOL_HELPER_H_ */ diff --git a/source/Security/kmp/kmp_api.c b/source/Security/kmp/kmp_api.c index 27ed10d4a6..479f0d15bb 100644 --- a/source/Security/kmp/kmp_api.c +++ b/source/Security/kmp/kmp_api.c @@ -37,23 +37,24 @@ #define TRACE_GROUP "kmap" struct kmp_api_s { - void *app_data_ptr; /**< Opaque pointer for application data */ - kmp_api_create_confirm *create_conf; /**< KMP-CREATE.confirm callback */ - kmp_api_create_indication *create_ind; /**< KMP-CREATE.indication callback */ - kmp_api_finished_indication *finished_ind; /**< KMP-FINISHED.indication callback */ - kmp_api_finished *finished; /**< Finished i.e. ready to be deleted callback */ - kmp_type_e type; /**< KMP type */ - kmp_addr_t *addr; /**< Supplicant EUI-64, Relay IP address, Relay port */ - kmp_service_t *service; /**< KMP service */ - bool timer_start_pending; /**< Timer is pending to start */ - sec_prot_t sec_prot; /**< Security protocol interface */ + void *app_data_ptr; /**< Opaque pointer for application data */ + kmp_api_create_confirm *create_conf; /**< KMP-CREATE.confirm callback */ + kmp_api_create_indication *create_ind; /**< KMP-CREATE.indication callback */ + kmp_api_finished_indication *finished_ind; /**< KMP-FINISHED.indication callback */ + kmp_api_finished *finished; /**< Finished i.e. ready to be deleted callback */ + kmp_type_e type; /**< KMP type */ + kmp_addr_t *addr; /**< Supplicant EUI-64, Relay IP address, Relay port */ + kmp_service_t *service; /**< KMP service */ + bool timer_start_pending : 1; /**< Timer is pending to start */ + bool receive_disable : 1; /**< Receiving disabled, do not route messages anymore */ + sec_prot_t sec_prot; /**< Security protocol interface */ }; typedef struct { - kmp_type_e type; /**< Security protocol type callback */ - kmp_sec_prot_size *size; /**< Security protocol data size callback */ - kmp_sec_prot_init *init; /**< Security protocol init */ - ns_list_link_t link; /**< Link */ + kmp_type_e type; /**< Security protocol type callback */ + kmp_sec_prot_size *size; /**< Security protocol data size callback */ + kmp_sec_prot_init *init; /**< Security protocol init */ + ns_list_link_t link; /**< Link */ } kmp_sec_prot_entry_t; typedef NS_LIST_HEAD(kmp_sec_prot_entry_t, link) kmp_sec_prot_list_t; @@ -88,6 +89,7 @@ static void kmp_sec_prot_timer_stop(sec_prot_t *prot); static void kmp_sec_prot_state_machine_call(sec_prot_t *prot); static void kmp_sec_prot_eui64_addr_get(sec_prot_t *prot, uint8_t *local_eui64, uint8_t *remote_eui64); static sec_prot_t *kmp_sec_prot_by_type_get(sec_prot_t *prot, uint8_t type); +static void kmp_sec_prot_receive_disable(sec_prot_t *prot); #define kmp_api_get_from_prot(prot) (kmp_api_t *)(((uint8_t *)prot) - offsetof(kmp_api_t, sec_prot)); @@ -126,6 +128,7 @@ kmp_api_t *kmp_api_create(kmp_service_t *service, kmp_type_e type) kmp->addr = 0; kmp->service = service; kmp->timer_start_pending = false; + kmp->receive_disable = false; memset(&kmp->sec_prot, 0, sec_size); @@ -140,6 +143,7 @@ kmp_api_t *kmp_api_create(kmp_service_t *service, kmp_type_e type) kmp->sec_prot.state_machine_call = kmp_sec_prot_state_machine_call; kmp->sec_prot.addr_get = kmp_sec_prot_eui64_addr_get; kmp->sec_prot.type_get = kmp_sec_prot_by_type_get; + kmp->sec_prot.receive_disable = kmp_sec_prot_receive_disable; if (sec_prot->init(&kmp->sec_prot) < 0) { ns_dyn_mem_free(kmp); @@ -279,6 +283,12 @@ static sec_prot_t *kmp_sec_prot_by_type_get(sec_prot_t *prot, uint8_t type) return &kmp_by_type->sec_prot; } +static void kmp_sec_prot_receive_disable(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->receive_disable = true; +} + void kmp_api_delete(kmp_api_t *kmp) { if (kmp->sec_prot.delete) { @@ -312,9 +322,9 @@ kmp_type_e kmp_api_type_from_id_get(uint8_t kmp_id) case IEEE_802_11_4WH: return IEEE_802_11_4WH; case IEEE_802_11_GKH: - return IEEE_802_1X_MKA; + return IEEE_802_11_GKH; default: - return INVALID_KMP_TYPE; + return KMP_TYPE_NONE; } } @@ -426,6 +436,11 @@ int8_t kmp_service_msg_if_receive(kmp_service_t *service, kmp_type_e type, const return -1; } + // Security protocol has disables message receiving + if (kmp->receive_disable) { + return -1; + } + int8_t ret = kmp->sec_prot.receive(&kmp->sec_prot, pdu, size); return ret; } diff --git a/source/Security/kmp/kmp_api.h b/source/Security/kmp/kmp_api.h index a03ad8e249..cb19ec168d 100644 --- a/source/Security/kmp/kmp_api.h +++ b/source/Security/kmp/kmp_api.h @@ -29,7 +29,7 @@ */ typedef enum { - INVALID_KMP_TYPE = 0, + KMP_TYPE_NONE = 0, IEEE_802_1X_MKA = 1, IEEE_802_11_4WH = 6, diff --git a/source/Security/kmp/kmp_eapol_pdu_if.c b/source/Security/kmp/kmp_eapol_pdu_if.c index c56f5b09ee..3424fd3eda 100644 --- a/source/Security/kmp/kmp_eapol_pdu_if.c +++ b/source/Security/kmp/kmp_eapol_pdu_if.c @@ -151,7 +151,7 @@ int8_t kmp_eapol_pdu_if_receive(protocol_interface_info_entry_t *interface_ptr, void *data_pdu = &eapol_kmp_pdu->kmp_data; kmp_type_e type = kmp_api_type_from_id_get(eapol_kmp_pdu->kmp_id); - if (type == INVALID_KMP_TYPE) { + if (type == KMP_TYPE_NONE) { return -1; } diff --git a/source/Security/kmp/kmp_socket_if.c b/source/Security/kmp/kmp_socket_if.c index ba6412678b..d4dc4a32a8 100644 --- a/source/Security/kmp/kmp_socket_if.c +++ b/source/Security/kmp/kmp_socket_if.c @@ -180,7 +180,7 @@ static void kmp_socket_if_socket_cb(void *ptr) data_ptr += 8; kmp_type_e type = kmp_api_type_from_id_get(*data_ptr++); - if (type == INVALID_KMP_TYPE) { + if (type == KMP_TYPE_NONE) { ns_dyn_mem_free(pdu); return; } diff --git a/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c b/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c index 19895c3cb3..d562e90601 100644 --- a/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c +++ b/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c @@ -60,6 +60,7 @@ typedef struct { tls_data_t tls_send; /**< EAP-TLS send buffer */ tls_data_t tls_recv; /**< EAP-TLS receive buffer */ uint8_t eap_id_seq; /**< EAP sequence */ + uint8_t recv_eap_id_seq; /**< Last received EAP sequence */ uint8_t eap_code; /**< Received EAP code */ uint8_t eap_type; /**< Received EAP type */ int8_t tls_result; /**< Result of TLS operation */ @@ -72,7 +73,7 @@ static const trickle_params_t eap_tls_trickle_params = { .Imin = 200, /* 20s; ticks are 100ms */ .Imax = 450, /* 45s */ .k = 0, /* infinity - no consistency checking */ - .TimerExpirations = 4 + .TimerExpirations = 2 }; static uint16_t auth_eap_tls_sec_prot_size(void); @@ -129,6 +130,7 @@ static int8_t auth_eap_tls_sec_prot_init(sec_prot_t *prot) data->tls_prot = NULL; data->eap_id_seq = 0; + data->recv_eap_id_seq = 0; data->eap_code = 0; data->eap_type = 0; eap_tls_sec_prot_lib_message_init(&data->tls_recv); @@ -188,21 +190,32 @@ static int8_t auth_eap_tls_sec_prot_message_handle(sec_prot_t *prot) uint16_t length = data->recv_eapol_pdu.msg.eap.length; bool new_seq_id = false; - // Confirmation that supplicant has received the message, proceed with protocol - if (data->recv_eapol_pdu.msg.eap.id_seq == data->eap_id_seq) { + bool old_seq_id = false; + + // Already received sequence ID is received again, ignore + if (data->recv_eapol_pdu.msg.eap.id_seq < data->eap_id_seq) { + old_seq_id = true; + } else if (data->recv_eapol_pdu.msg.eap.id_seq == data->eap_id_seq) { + // Confirmation that supplicant has received the message, proceed with protocol + data->recv_eap_id_seq = data->recv_eapol_pdu.msg.eap.id_seq; data->eap_id_seq++; new_seq_id = true; } - tr_debug("recv EAP %s type %s id %i flags %x len %i", eap_msg_trace[data->eap_code - 1], - data->eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->recv_eapol_pdu.msg.eap.id_seq, - length >= 6 ? data_ptr[0] : 0, length); + tr_info("EAP-TLS: recv %s type %s id %i flags %x len %i, eui-64 %s", eap_msg_trace[data->eap_code - 1], + data->eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->recv_eapol_pdu.msg.eap.id_seq, + length >= 6 ? data_ptr[0] : 0, length, trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + + if (old_seq_id) { + return EAP_TLS_MSG_DECODE_ERROR; + } if (data->eap_type == EAP_IDENTITY) { return EAP_TLS_MSG_IDENTITY; } if (!data_ptr || length < 6) { + tr_error("EAP-TLS: decode error"); return EAP_TLS_MSG_DECODE_ERROR; } @@ -226,16 +239,23 @@ static int8_t auth_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_c eap_tls_sec_prot_lib_message_allocate(&data->tls_send, TLS_HEAD_LEN, 0); flags = EAP_TLS_START; } - } else if (eap_code != EAP_SUCCESS && eap_code != EAP_FAILURE) { + } else if (eap_code == EAP_SUCCESS || eap_code == EAP_FAILURE) { + // Send Success and Failure with same identifier as received in EAP Response + data->eap_id_seq = data->recv_eap_id_seq; + } else { return -1; } uint16_t eapol_pdu_size; - uint8_t *eapol_decoded_data = eap_tls_sec_prot_lib_message_build(eap_code, eap_type, flags, data->eap_id_seq, prot->header_size, &data->tls_send, &eapol_pdu_size); + uint8_t *eapol_decoded_data = eap_tls_sec_prot_lib_message_build(eap_code, eap_type, &flags, data->eap_id_seq, prot->header_size, &data->tls_send, &eapol_pdu_size); if (!eapol_decoded_data) { return -1; } + tr_info("EAP-TLS: send %s type %s id %i flags %x len %i, eui-64: %s", eap_msg_trace[eap_code - 1], + eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->eap_id_seq, flags, eapol_pdu_size, + trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -267,10 +287,13 @@ static void auth_eap_tls_sec_prot_tls_finished_indication(sec_prot_t *tls_prot, if (result == SEC_RESULT_OK) { data->tls_result = EAP_TLS_RESULT_HANDSHAKE_OVER; + tr_info("EAP-TLS: handshake success"); } else if (result == SEC_RESULT_CONF_ERROR) { data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR; + tr_error("EAP-TLS: handshake fatal error"); } else { data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FAILED; + tr_error("EAP-TLS: handshake failed"); } data->tls_ongoing = false; @@ -328,12 +351,7 @@ static void auth_eap_tls_sec_prot_init_tls(sec_prot_t *prot) static void auth_eap_tls_sec_prot_delete_tls(sec_prot_t *prot) { - eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); - // If initialized, TLS terminates on its own - if (data->tls_prot) { - return; - } - + // Triggers TLS to terminate if it is not already terminating by its own sec_prot_t *tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); if (tls_prot) { tls_prot->finished_send(tls_prot); @@ -353,7 +371,7 @@ static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot) // Wait KMP-CREATE.request case EAP_TLS_STATE_CREATE_REQ: - tr_debug("EAP-TLS start"); + tr_info("EAP-TLS start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); prot->timer_start(prot); @@ -387,9 +405,6 @@ static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot) return; } - // Increment sequence ID - //auth_eap_tls_sec_prot_seq_id_update(prot); - // Sends EAP request, TLS EAP start auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_TLS, EAP_TLS_EXCHANGE_START); @@ -477,6 +492,8 @@ static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot) } else { // TLS done, indicate success to peer if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_OVER) { + // Supplicant PMK is now valid + sec_prot_keys_pmk_mismatch_reset(prot->sec_keys); // Sends EAP success auth_eap_tls_sec_prot_message_send(prot, EAP_SUCCESS, 0, EAP_TLS_EXCHANGE_NONE); } else { @@ -491,13 +508,12 @@ static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot) break; case EAP_TLS_STATE_FINISH: - tr_debug("EAP-TLS finish"); + tr_info("EAP-TLS finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); // KMP-FINISHED.indication, prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISHED); - data->common.ticks = 10 * 10; break; case EAP_TLS_STATE_FINISHED: diff --git a/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c index c67f304597..b3d4e87008 100644 --- a/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c +++ b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c @@ -88,6 +88,7 @@ int8_t eap_tls_sec_prot_lib_message_handle(uint8_t *data, uint16_t length, bool // Handles the length field if (data[0] & EAP_TLS_FRAGMENT_LENGTH) { if (length < 5) { + tr_error("EAP-TLS: decode error"); return EAP_TLS_MSG_DECODE_ERROR; } @@ -138,19 +139,16 @@ int8_t eap_tls_sec_prot_lib_message_handle(uint8_t *data, uint16_t length, bool return result; } -uint8_t *eap_tls_sec_prot_lib_message_build(uint8_t eap_code, uint8_t eap_type, uint8_t flags, uint8_t eap_id_seq, uint8_t header_size, tls_data_t *tls_send, uint16_t *length) +uint8_t *eap_tls_sec_prot_lib_message_build(uint8_t eap_code, uint8_t eap_type, uint8_t *flags, uint8_t eap_id_seq, uint8_t header_size, tls_data_t *tls_send, uint16_t *length) { uint16_t eap_len = 4; uint8_t *data_ptr = NULL; // Write EAP-TLS data (from EAP-TLS flags field onward) if (tls_send->data) { - data_ptr = eap_tls_sec_prot_lib_fragment_write(tls_send->data + TLS_HEAD_LEN, tls_send->total_len, tls_send->handled_len, &eap_len, &flags); + data_ptr = eap_tls_sec_prot_lib_fragment_write(tls_send->data + TLS_HEAD_LEN, tls_send->total_len, tls_send->handled_len, &eap_len, flags); } - tr_debug("send EAP %s type %s id %i flags %x len %i", eap_msg_trace[eap_code - 1], - eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", eap_id_seq, flags, eap_len); - eapol_pdu_t eapol_pdu; *length = eapol_pdu_eap_frame_init(&eapol_pdu, eap_code, eap_id_seq, eap_type, eap_len, data_ptr); diff --git a/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h index 37ff0f2741..0de960bb05 100644 --- a/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h +++ b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h @@ -117,6 +117,6 @@ int8_t eap_tls_sec_prot_lib_message_handle(uint8_t *data, uint16_t length, bool * \return pointer to message to be sent or NULL in case of failure * */ -uint8_t *eap_tls_sec_prot_lib_message_build(uint8_t eap_code, uint8_t eap_type, uint8_t flags, uint8_t eap_id_seq, uint8_t header_size, tls_data_t *tls_send, uint16_t *length); +uint8_t *eap_tls_sec_prot_lib_message_build(uint8_t eap_code, uint8_t eap_type, uint8_t *flags, uint8_t eap_id_seq, uint8_t header_size, tls_data_t *tls_send, uint16_t *length); #endif /* EAP_TLS_SEC_PROT_H_ */ diff --git a/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c b/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c index 5a61d865aa..eb4228f44a 100644 --- a/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c +++ b/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c @@ -53,12 +53,16 @@ typedef enum { EAP_TLS_STATE_FINISHED = SEC_STATE_FINISHED } eap_tls_sec_prot_state_e; +// Filters EAP re-transmission bursts that arrive with same EAP sequence number +#define BURST_FILTER_TIMER_TIMEOUT 5 * 10 + typedef struct { sec_prot_common_t common; /**< Common data */ sec_prot_t *tls_prot; /**< TLS security protocol */ eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */ tls_data_t tls_send; /**< EAP-TLS send buffer */ tls_data_t tls_recv; /**< EAP-TLS receive buffer */ + uint16_t burst_filt_timer; /**< Burst filter timer */ uint8_t eap_id_seq; /**< EAP sequence */ uint8_t eap_code; /**< Received EAP code */ uint8_t eap_type; /**< Received EAP type */ @@ -72,7 +76,7 @@ static const trickle_params_t eap_tls_trickle_params = { .Imin = 200, /* 20s; ticks are 100ms */ .Imax = 450, /* 45s */ .k = 0, /* infinity - no consistency checking */ - .TimerExpirations = 4 + .TimerExpirations = 2 }; static uint16_t supp_eap_tls_sec_prot_size(void); @@ -127,6 +131,7 @@ static int8_t supp_eap_tls_sec_prot_init(sec_prot_t *prot) sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_INIT); data->tls_prot = NULL; + data->burst_filt_timer = 0; data->eap_id_seq = 0; data->eap_code = 0; data->eap_type = 0; @@ -186,14 +191,29 @@ static int8_t supp_eap_tls_sec_prot_message_handle(sec_prot_t *prot) uint8_t *data_ptr = data->recv_eapol_pdu.msg.eap.data_ptr; uint16_t length = data->recv_eapol_pdu.msg.eap.length; - uint8_t new_seq_id = false; - if (data->recv_eapol_pdu.msg.eap.id_seq > data->eap_id_seq) { - new_seq_id = true; - } + tr_info("EAP-TLS recv %s type %s id %i flags %x len %i", eap_msg_trace[data->eap_code - 1], + data->eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->recv_eapol_pdu.msg.eap.id_seq, + length >= 6 ? data_ptr[0] : 0, length); - tr_debug("recv EAP %s type %s id %i flags %x len %i", eap_msg_trace[data->eap_code - 1], - data->eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->recv_eapol_pdu.msg.eap.id_seq, - length >= 6 ? data_ptr[0] : 0, length); + uint8_t new_seq_id = false; + // New sequence identifier received + if (data->recv_eapol_pdu.msg.eap.id_seq > data->eap_id_seq) { + data->burst_filt_timer = BURST_FILTER_TIMER_TIMEOUT; + new_seq_id = true; + } else if (data->recv_eapol_pdu.msg.eap.id_seq == data->eap_id_seq) { + if (data->burst_filt_timer > 0) { + /* If retransmission arrives when burst filter timer is running, ignores it + and starts timer again */ + data->burst_filt_timer = BURST_FILTER_TIMER_TIMEOUT; + return EAP_TLS_MSG_DECODE_ERROR; + } else { + // If retransmission arrives after timeout, starts timer again + data->burst_filt_timer = BURST_FILTER_TIMER_TIMEOUT; + } + } else if (data->recv_eapol_pdu.msg.eap.id_seq < data->eap_id_seq) { + // Already received sequence ID is received again, ignore + return EAP_TLS_MSG_DECODE_ERROR; + } if (data->eap_type == EAP_IDENTITY) { return EAP_TLS_MSG_IDENTITY; @@ -233,11 +253,14 @@ static int8_t supp_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_c } uint16_t eapol_pdu_size; - uint8_t *eapol_decoded_data = eap_tls_sec_prot_lib_message_build(eap_code, eap_type, flags, data->eap_id_seq, prot->header_size, &data->tls_send, &eapol_pdu_size); + uint8_t *eapol_decoded_data = eap_tls_sec_prot_lib_message_build(eap_code, eap_type, &flags, data->eap_id_seq, prot->header_size, &data->tls_send, &eapol_pdu_size); if (!eapol_decoded_data) { return -1; } + tr_info("EAP-TLS: send %s type %s id %i flags %x len %i", eap_msg_trace[eap_code - 1], + eap_type == EAP_IDENTITY ? "IDENTITY" : "TLS", data->eap_id_seq, flags, eapol_pdu_size); + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -248,6 +271,13 @@ static int8_t supp_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_c static void supp_eap_tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) { eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + if (data->burst_filt_timer > ticks) { + data->burst_filt_timer -= ticks; + } else { + data->burst_filt_timer = 0; + } + sec_prot_timer_timeout_handle(prot, &data->common, &eap_tls_trickle_params, ticks); } @@ -278,11 +308,14 @@ static void supp_eap_tls_sec_prot_tls_finished_indication(sec_prot_t *tls_prot, if (result == SEC_RESULT_OK) { data->tls_result = EAP_TLS_RESULT_HANDSHAKE_OVER; + tr_info("EAP-TLS: handshake success"); } else if (result == SEC_RESULT_CONF_ERROR) { data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR; + tr_error("EAP-TLS: handshake fatal error"); } else { // On failure has sent ALERT data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FAILED; + tr_error("EAP-TLS: handshake failed"); } data->tls_ongoing = false; @@ -340,12 +373,7 @@ static void supp_eap_tls_sec_prot_init_tls(sec_prot_t *prot) static void supp_eap_tls_sec_prot_delete_tls(sec_prot_t *prot) { - eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); - // If initialized, TLS terminates on its own - if (data->tls_prot) { - return; - } - + // Triggers TLS to terminate if it is not already terminating by its own sec_prot_t *tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); if (tls_prot) { tls_prot->finished_send(tls_prot); @@ -374,7 +402,7 @@ static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot) // Store sequence ID supp_eap_tls_sec_prot_seq_id_update(prot); - tr_debug("EAP-TLS start"); + tr_info("EAP-TLS start"); prot->timer_start(prot); @@ -402,8 +430,8 @@ static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot) case EAP_TLS_STATE_REQUEST_TLS_EAP: // On timeout if (sec_prot_result_timeout_check(&data->common)) { - // Re-send EAP response, Identity - supp_eap_tls_sec_prot_message_send(prot, EAP_RESPONSE, EAP_IDENTITY, EAP_TLS_EXCHANGE_NONE); + /* Waits for next trickle expire. If trickle expirations reach the limit, + terminates EAP-TLS */ return; } @@ -435,8 +463,8 @@ static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot) case EAP_TLS_STATE_REQUEST: // On timeout if (sec_prot_result_timeout_check(&data->common)) { - // Re-send EAP response - supp_eap_tls_sec_prot_message_send(prot, EAP_RESPONSE, EAP_TLS, EAP_TLS_EXCHANGE_ONGOING); + /* Waits for next trickle expire. If trickle expirations reach the limit, + terminates EAP-TLS */ return; } @@ -488,7 +516,7 @@ static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot) break; case EAP_TLS_STATE_FINISH: - tr_debug("EAP-TLS finish"); + tr_info("EAP-TLS finish"); // KMP-FINISHED.indication, prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); diff --git a/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c b/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c index b46db4676c..99cbcd5749 100644 --- a/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c +++ b/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c @@ -155,12 +155,18 @@ static int8_t auth_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t si // Get message data->recv_msg = auth_fwh_sec_prot_message_get(&data->recv_eapol_pdu, prot->sec_keys); if (data->recv_msg != FWH_MESSAGE_UNKNOWN) { + tr_info("4WH: recv %s, eui-64: %s", data->recv_msg == FWH_MESSAGE_2 ? "Message 2" : "Message 4", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + // Call state machine data->recv_pdu = pdu; data->recv_size = size; prot->state_machine(prot); + } else { + tr_error("4WH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); } ret_val = 0; + } else { + tr_error("4WH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); } memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); @@ -180,7 +186,7 @@ static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, return FWH_MESSAGE_UNKNOWN; } - uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu); switch (key_mask) { case KEY_INFO_KEY_MIC: @@ -191,7 +197,7 @@ static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, break; case KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME: // Only accept message from supplicant with expected replay counter - if (eapol_pdu->msg.key.replay_counter == sec_prot_keys_pmk_replay_cnt_get(sec_keys)) { + if (eapol_pdu->msg.key.replay_counter == sec_prot_keys_pmk_replay_cnt_get(sec_keys)) { msg = FWH_MESSAGE_4; } break; @@ -241,14 +247,14 @@ static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ break; case FWH_MESSAGE_3: { uint8_t gtk_index; - uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(prot->sec_keys->gtks, >k_index); + uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(prot->sec_keys, >k_index); if (gtk) { kde_end = kde_gtk_write(kde_end, gtk_index, gtk); uint32_t gtk_lifetime = sec_prot_keys_gtk_lifetime_get(prot->sec_keys->gtks, gtk_index); kde_end = kde_lifetime_write(kde_end, gtk_lifetime); } - uint8_t gtkl = sec_prot_keys_gtkl_get(prot->sec_keys->gtks); + uint8_t gtkl = sec_prot_keys_fresh_gtkl_get(prot->sec_keys->gtks); kde_end = kde_gtkl_write(kde_end, gtkl); kde_padding_write(kde_end, kde_start + kde_len); } @@ -267,7 +273,7 @@ static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ sec_prot_keys_pmk_replay_cnt_increment(prot->sec_keys); eapol_pdu.msg.key.replay_counter = sec_prot_keys_pmk_replay_cnt_get(prot->sec_keys); eapol_pdu.msg.key.key_information.key_ack = true; - eapol_pdu.msg.key.key_length = 32; + eapol_pdu.msg.key.key_length = EAPOL_KEY_LEN; eapol_pdu.msg.key.key_nonce = data->nonce; break; case FWH_MESSAGE_3: @@ -279,7 +285,7 @@ static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ eapol_pdu.msg.key.key_information.secured_key_frame = true; eapol_pdu.msg.key.key_information.encrypted_key_data = true; eapol_pdu.msg.key.key_nonce = data->nonce; - eapol_pdu.msg.key.key_length = 32; + eapol_pdu.msg.key.key_length = EAPOL_KEY_LEN; break; default: break; @@ -293,6 +299,8 @@ static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ return -1; } + tr_info("4WH: send %s, eui-64: %s", msg == FWH_MESSAGE_1 ? "Message 1" : "Message 3", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -319,7 +327,7 @@ static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot) // Wait KMP-CREATE.request case FWH_STATE_CREATE_REQ: - tr_debug("4WH start"); + tr_info("4WH: start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); uint8_t *pmk = sec_prot_keys_pmk_get(prot->sec_keys); if (!pmk) { // If PMK is not set fails @@ -383,13 +391,18 @@ static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot) return; } + // If GTK was inserted set it valid + sec_prot_keys_gtkl_from_gtk_insert_index_set(prot->sec_keys); + // Reset PTK mismatch + sec_prot_keys_ptk_mismatch_reset(prot->sec_keys); + // Update PTK sec_prot_keys_ptk_write(prot->sec_keys, data->new_ptk); sec_prot_state_set(prot, &data->common, FWH_STATE_FINISH); } break; case FWH_STATE_FINISH: - tr_debug("4WH finish"); + tr_info("4WH: finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); // KMP-FINISHED.indication, prot->finished_ind(prot, sec_prot_result_get(&data->common), 0); @@ -417,6 +430,7 @@ static int8_t auth_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t * uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce; if (!remote_nonce) { + tr_error("SNonce invalid"); return 1; } diff --git a/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c b/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c index 97fc30958f..775e4160de 100644 --- a/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c +++ b/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c @@ -47,6 +47,7 @@ typedef enum { FWH_STATE_CREATE_IND = SEC_STATE_CREATE_IND, FWH_STATE_MESSAGE_1 = SEC_STATE_FIRST, FWH_STATE_MESSAGE_3, + FWH_STATE_MESSAGE_3_RETRY_WAIT, FWH_STATE_CREATE_RESP_SUPP_RETRY, FWH_STATE_FINISH = SEC_STATE_FINISH, FWH_STATE_FINISHED = SEC_STATE_FINISHED @@ -76,22 +77,18 @@ typedef struct { void *recv_pdu; /**< received pdu */ uint16_t recv_size; /**< received pdu size */ uint64_t recv_replay_cnt; /**< received replay counter */ + bool msg3_received : 1; /**< Valid Message 3 has been received */ + bool msg3_retry_wait : 1; /**< Waiting for Message 3 retry */ + bool recv_replay_cnt_set : 1; /**< received replay counter set */ } fwh_sec_prot_int_t; -static const trickle_params_t fwh_trickle_params = { - .Imin = 50, /* 5000ms; ticks are 100ms */ - .Imax = 150, /* 15000ms */ - .k = 0, /* infinity - no consistency checking */ - .TimerExpirations = 4 -}; - static uint16_t supp_fwh_sec_prot_size(void); static int8_t supp_fwh_sec_prot_init(sec_prot_t *prot); static void supp_fwh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); static void supp_fwh_sec_prot_delete(sec_prot_t *prot); static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); -static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys); +static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(sec_prot_t *prot, eapol_pdu_t *eapol_pdu); static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot); static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg); @@ -101,6 +98,7 @@ static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t * static int8_t supp_fwh_sec_prot_mic_validate(sec_prot_t *prot); static void supp_fwh_sec_prot_recv_replay_counter_store(sec_prot_t *prot); +static bool supp_fwh_sec_prot_recv_replay_cnt_compare(uint64_t received_counter, sec_prot_t *prot); static void supp_fwh_sec_prot_anonce_store(sec_prot_t *prot); static int8_t supp_fwh_sec_prot_anonce_validate(sec_prot_t *prot); static void supp_fwh_sec_prot_security_replay_counter_update(sec_prot_t *prot); @@ -141,6 +139,10 @@ static int8_t supp_fwh_sec_prot_init(sec_prot_t *prot) sec_prot_state_set(prot, &data->common, FWH_STATE_INIT); data->common.ticks = 30 * 10; // 30 seconds + data->msg3_received = false; + data->msg3_retry_wait = false; + data->recv_replay_cnt = 0; + data->recv_replay_cnt_set = false; uint8_t eui64[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; sec_prot_lib_nonce_init(data->snonce, eui64, 1000); @@ -171,14 +173,20 @@ static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t si // Decoding is successful if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { // Get message - data->recv_msg = supp_fwh_sec_prot_message_get(&data->recv_eapol_pdu, prot->sec_keys); + data->recv_msg = supp_fwh_sec_prot_message_get(prot, &data->recv_eapol_pdu); if (data->recv_msg != FWH_MESSAGE_UNKNOWN) { + tr_info("4WH: recv %s", data->recv_msg == FWH_MESSAGE_1 ? "Message 1" : "Message 3"); + // Call state machine data->recv_pdu = pdu; data->recv_size = size; prot->state_machine(prot); + } else { + tr_error("4WH: recv error"); } ret_val = 0; + } else { + tr_error("4WH: recv error"); } memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); @@ -189,7 +197,7 @@ static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t si return ret_val; } -static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys) +static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(sec_prot_t *prot, eapol_pdu_t *eapol_pdu) { fwh_sec_prot_msg_e msg = FWH_MESSAGE_UNKNOWN; @@ -198,23 +206,33 @@ static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, return FWH_MESSAGE_UNKNOWN; } - uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu); switch (key_mask) { + // Message 1 case KEY_INFO_KEY_ACK: - // Must have valid replay counter - if (eapol_pdu->msg.key.replay_counter > sec_prot_keys_pmk_replay_cnt_get(sec_keys)) { + /* Must have valid replay counter, both larger for PMK and larger that is used on + * the four way handshake session (note: PMK replay counter is not updated for Message 1 + * but session specific counter is) + */ + if (sec_prot_keys_pmk_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot->sec_keys) && + supp_fwh_sec_prot_recv_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot)) { msg = FWH_MESSAGE_1; + } else { + tr_error("4WH: invalid replay counter %"PRId64, eapol_pdu->msg.key.replay_counter); } break; + // Message 3 case KEY_INFO_INSTALL | KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME: // Must have valid replay counter - if (eapol_pdu->msg.key.replay_counter > sec_prot_keys_pmk_replay_cnt_get(sec_keys)) { + if (sec_prot_keys_pmk_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot->sec_keys)) { if (eapol_pdu->msg.key.key_information.encrypted_key_data) { // This should include the GTK KDE, Lifetime KDE and GTKL KDE. // At least some of them should be present msg = FWH_MESSAGE_3; } + } else { + tr_error("4WH: invalid replay counter %"PRId64, eapol_pdu->msg.key.replay_counter); } break; default: @@ -256,6 +274,8 @@ static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ return -1; } + tr_info("4WH: send %s", msg == FWH_MESSAGE_2 ? "Message 2" : "Message 4"); + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -266,7 +286,7 @@ static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_ static void supp_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) { fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); - sec_prot_timer_timeout_handle(prot, &data->common, &fwh_trickle_params, ticks); + sec_prot_timer_timeout_handle(prot, &data->common, NULL, ticks); } static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) @@ -291,7 +311,7 @@ static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) return; } - tr_debug("4WH start"); + tr_info("4WH: start"); // Store authenticator nonce for check when 4WH Message 3 is received supp_fwh_sec_prot_anonce_store(prot); @@ -335,9 +355,13 @@ static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) if (supp_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) { return; } + + supp_fwh_sec_prot_recv_replay_counter_store(prot); + // Send 4WH message 2 supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_2); data->common.ticks = 30 * 10; // 30 seconds + return; } else if (data->recv_msg != FWH_MESSAGE_3) { return; } @@ -359,6 +383,7 @@ static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) supp_fwh_sec_prot_recv_replay_counter_store(prot); supp_fwh_sec_prot_security_replay_counter_update(prot); + data->msg3_received = true; // Sends 4WH Message 4 supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_4); @@ -367,19 +392,37 @@ static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) break; case FWH_STATE_FINISH: - tr_debug("4WH finish"); + if (data->msg3_retry_wait) { + tr_info("4WH: Message 3 retry timeout"); + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + return; + } - // KMP-FINISHED.indication - sec_prot_keys_ptk_write(prot->sec_keys, data->new_ptk); - sec_prot_keys_ptk_eui_64_write(prot->sec_keys, data->remote_eui64); - prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); - sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + // If Message 3 has been received updates key data and waits for Message 3 retry + if (data->msg3_received) { + data->msg3_retry_wait = true; + + tr_info("4WH: finish, wait Message 3 retry"); + + sec_prot_keys_ptk_write(prot->sec_keys, data->new_ptk); + sec_prot_keys_ptk_eui_64_write(prot->sec_keys, data->remote_eui64); + + data->common.ticks = 60 * 10; // 60 seconds + // KMP-FINISHED.indication + prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); + sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_3_RETRY_WAIT); + } else { + tr_info("4WH: finish"); + // KMP-FINISHED.indication + prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + } break; - case FWH_STATE_FINISHED: + case FWH_STATE_MESSAGE_3_RETRY_WAIT: if (sec_prot_result_timeout_check(&data->common)) { - prot->timer_stop(prot); - prot->finished(prot); + tr_info("4WH: Message 3 retry timeout"); + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); } else { if (data->recv_msg != FWH_MESSAGE_3) { return; @@ -403,25 +446,15 @@ static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) supp_fwh_sec_prot_recv_replay_counter_store(prot); supp_fwh_sec_prot_security_replay_counter_update(prot); - tr_debug("4WH start again"); + tr_info("4WH: send Message 4 again"); - // Send KMP-CREATE.indication - prot->create_ind(prot); - sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_RESP_SUPP_RETRY); + supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_4); } break; - // Special case for second receiving of 4WH message 3 - case FWH_STATE_CREATE_RESP_SUPP_RETRY: - if (sec_prot_result_ok_check(&data->common)) { - // Send 4WH message 4 - supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_4); - data->common.ticks = 30 * 10; // 30 seconds - sec_prot_state_set(prot, &data->common, FWH_STATE_FINISH); - } else { - // Ready to be deleted - sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); - } + case FWH_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); break; default: @@ -438,7 +471,8 @@ static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t * uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce; if (!remote_nonce) { - return 1; + tr_error("No ANonce"); + return -1; } uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); @@ -457,6 +491,21 @@ static void supp_fwh_sec_prot_recv_replay_counter_store(sec_prot_t *prot) { fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); data->recv_replay_cnt = data->recv_eapol_pdu.msg.key.replay_counter; + data->recv_replay_cnt_set = true; +} + +static bool supp_fwh_sec_prot_recv_replay_cnt_compare(uint64_t received_counter, sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + // If previous value is set must be greater + if (data->recv_replay_cnt_set && received_counter > data->recv_replay_cnt) { + return true; + } else if (!data->recv_replay_cnt_set && received_counter >= data->recv_replay_cnt) { + // Otherwise allows also same value e.g. zero + return true; + } + + return false; } static void supp_fwh_sec_prot_anonce_store(sec_prot_t *prot) @@ -469,6 +518,7 @@ static int8_t supp_fwh_sec_prot_anonce_validate(sec_prot_t *prot) { fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); if (memcmp(data->anonce, data->recv_eapol_pdu.msg.key.key_nonce, EAPOL_KEY_NONCE_LEN) != 0) { + tr_error("ANonce invalid"); return -1; } return 0; @@ -508,7 +558,7 @@ static int8_t supp_fwh_kde_handle(sec_prot_t *prot) case FWH_MESSAGE_3: // If a valid new GTK value present, insert it - if (sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys->gtks) < 0) { + if (sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys) < 0) { goto error; } break; @@ -521,6 +571,7 @@ static int8_t supp_fwh_kde_handle(sec_prot_t *prot) return 0; error: + tr_error("Invalid KDEs"); ns_dyn_mem_free(kde); return -1; } diff --git a/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c b/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c index ce204f082c..2c8ddc304c 100644 --- a/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c +++ b/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c @@ -139,12 +139,18 @@ static int8_t auth_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t si if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { // Get message if (auth_gkh_sec_prot_message_get(&data->recv_eapol_pdu, prot->sec_keys) != GKH_MESSAGE_UNKNOWN) { + tr_info("GKH: recv Message 2, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + // Call state machine data->recv_pdu = pdu; data->recv_size = size; prot->state_machine(prot); + } else { + tr_error("GKH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); } ret_val = 0; + } else { + tr_error("GKH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); } memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); @@ -163,7 +169,7 @@ static gkh_sec_prot_msg_e auth_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, return GKH_MESSAGE_UNKNOWN; } - uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu); switch (key_mask) { case KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME: @@ -204,14 +210,14 @@ static int8_t auth_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_ switch (msg) { case GKH_MESSAGE_1: { uint8_t gtk_index; - uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(prot->sec_keys->gtks, >k_index); + uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(prot->sec_keys, >k_index); if (gtk) { kde_end = kde_gtk_write(kde_end, gtk_index, gtk); uint32_t gtk_lifetime = sec_prot_keys_gtk_lifetime_get(prot->sec_keys->gtks, gtk_index); kde_end = kde_lifetime_write(kde_end, gtk_lifetime); } - uint8_t gtkl = sec_prot_keys_gtkl_get(prot->sec_keys->gtks); + uint8_t gtkl = sec_prot_keys_fresh_gtkl_get(prot->sec_keys->gtks); kde_end = kde_gtkl_write(kde_end, gtkl); kde_padding_write(kde_end, kde_start + kde_len); } @@ -245,6 +251,8 @@ static int8_t auth_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_ return -1; } + tr_info("GKH: send Message 1, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -270,7 +278,7 @@ static void auth_gkh_sec_prot_state_machine(sec_prot_t *prot) // Wait KMP-CREATE.request case GKH_STATE_CREATE_REQ: - tr_debug("GKH start"); + tr_info("GKH start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); prot->timer_start(prot); @@ -296,12 +304,15 @@ static void auth_gkh_sec_prot_state_machine(sec_prot_t *prot) if (auth_gkh_sec_prot_mic_validate(prot) < 0) { return; } + // Set inserted GTK valid + sec_prot_keys_gtkl_from_gtk_insert_index_set(prot->sec_keys); + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISH); } break; case GKH_STATE_FINISH: - tr_debug("GKH finish"); + tr_info("GKH finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); // KMP-FINISHED.indication, prot->finished_ind(prot, sec_prot_result_get(&data->common), 0); diff --git a/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c b/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c index 5fd7a98d17..0b54aa67ef 100644 --- a/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c +++ b/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c @@ -142,12 +142,18 @@ static int8_t supp_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t si if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { // Get message if (supp_gkh_sec_prot_message_get(&data->recv_eapol_pdu, prot->sec_keys) != GKH_MESSAGE_UNKNOWN) { + tr_info("GKH: recv Message 1"); + // Call state machine data->recv_pdu = pdu; data->recv_size = size; prot->state_machine(prot); + } else { + tr_error("GKH: recv error"); } ret_val = 0; + } else { + tr_error("GKH: recv error"); } memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); @@ -166,16 +172,18 @@ static gkh_sec_prot_msg_e supp_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, return GKH_MESSAGE_UNKNOWN; } - uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu); switch (key_mask) { case KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME: // Must have valid replay counter - if (eapol_pdu->msg.key.replay_counter > sec_prot_keys_pmk_replay_cnt_get(sec_keys)) { + if (sec_prot_keys_pmk_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, sec_keys)) { if (eapol_pdu->msg.key.key_information.encrypted_key_data) { // This should include the GTK KDE, Lifetime KDE and GTKL KDE. msg = GKH_MESSAGE_1; } + } else { + tr_error("GKH: invalid replay counter %"PRId64, eapol_pdu->msg.key.replay_counter); } break; default: @@ -207,6 +215,8 @@ static int8_t supp_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_ return -1; } + tr_info("GKH: send Message 2"); + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { return -1; } @@ -304,15 +314,11 @@ static int8_t supp_gkh_kde_handle(sec_prot_t *prot) } // If a valid new GTK value present, insert it - int8_t ret = sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys->gtks); + int8_t ret = sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys); ns_dyn_mem_free(kde); - if (ret < 0 || sec_prot_keys_gtk_insert_index_get(prot->sec_keys->gtks) < 0) { - return -1; - } else { - return 0; - } + return ret; } #endif /* HAVE_WS */ diff --git a/source/Security/protocols/key_sec_prot/key_sec_prot.c b/source/Security/protocols/key_sec_prot/key_sec_prot.c index 4320d44e9c..901c54feda 100644 --- a/source/Security/protocols/key_sec_prot/key_sec_prot.c +++ b/source/Security/protocols/key_sec_prot/key_sec_prot.c @@ -113,13 +113,23 @@ static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_k uint16_t kde_len = KDE_GTKL_LEN; uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); + uint8_t pmkid[PMKID_LEN]; if (pmk) { - kde_len += KDE_PMKID_LEN; + if (sec_prot_lib_pmkid_generate(prot, pmkid, false) >= 0) { + kde_len += KDE_PMKID_LEN; + } else { + pmk = NULL; + } } uint8_t *ptk = sec_prot_keys_ptk_get(sec_keys); + uint8_t ptkid[PTKID_LEN]; if (ptk) { - kde_len += KDE_PTKID_LEN; + if (sec_prot_lib_ptkid_generate(prot, ptkid, false) >= 0) { + kde_len += KDE_PTKID_LEN; + } else { + ptk = NULL; + } } uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len); @@ -130,20 +140,14 @@ static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_k uint8_t *kde_end = kde_start; if (pmk) { - uint8_t pmkid[PMKID_LEN]; - if (sec_prot_lib_pmkid_generate(prot, pmkid, true) >= 0) { - kde_end = kde_pmkid_write(kde_end, pmkid); - } + kde_end = kde_pmkid_write(kde_end, pmkid); } if (ptk) { - uint8_t ptkid[PTKID_LEN]; - if (sec_prot_lib_ptkid_generate(prot, ptkid, true) >= 0) { - kde_end = kde_ptkid_write(kde_end, ptkid); - } + kde_end = kde_ptkid_write(kde_end, ptkid); } - uint8_t gtkl = sec_prot_keys_gtkl_get(sec_keys->gtks); + uint8_t gtkl = sec_prot_keys_fresh_gtkl_get(sec_keys->gtks); kde_end = kde_gtkl_write(kde_end, gtkl); kde_len = kde_end - kde_start; @@ -157,14 +161,15 @@ static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_k if (!eapol_decoded_data) { data->result = SEC_RESULT_ERR_NO_MEM; } else { - //Test Data eapol_pdu.msg.key.key_information.install = false; eapol_pdu.msg.key.key_information.pairwise_key = false; eapol_pdu.msg.key.key_information.request = true; - eapol_pdu.msg.key.replay_counter = 10; - eapol_pdu.msg.key.key_length = 32; + eapol_pdu.msg.key.replay_counter = 0; + eapol_pdu.msg.key.key_length = 0; eapol_write_pdu_frame(eapol_decoded_data + prot->header_size, &eapol_pdu); + tr_info("Initial EAPOL-Key send, PMKID %s PTKID %s GTKL %x", pmk ? "set" : "not set", ptk ? "set" : "not set", gtkl); + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { data->result = SEC_RESULT_ERR_NO_MEM; } @@ -187,11 +192,67 @@ static void key_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e res static int8_t key_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) { eapol_pdu_t eapol_pdu; + + tr_info("Initial EAPOL-Key recv, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + // Decoding is successful if (eapol_parse_pdu_header(pdu, size, &eapol_pdu)) { + if (eapol_pdu.packet_type != EAPOL_KEY_TYPE) { + tr_info("not EAPOL-Key packet"); + prot->finished(prot); + return -1; + } + + uint16_t kde_len; + uint8_t *kde = sec_prot_lib_message_handle(prot->sec_keys->ptk, &kde_len, &eapol_pdu); + if (!kde) { + tr_error("no KDEs"); + prot->finished(prot); + return -1; + } + + // Default assumption is that PMK and PTK are not valid + prot->sec_keys->pmk_mismatch = true; + prot->sec_keys->ptk_mismatch = true; + + // Checks if supplicant indicates that it has valid PMK + uint8_t remote_keyid[KEYID_LEN]; + if (kde_pmkid_read(kde, kde_len, remote_keyid) >= 0) { + uint8_t pmkid[PMKID_LEN]; + if (sec_prot_lib_pmkid_generate(prot, pmkid, true) >= 0) { + if (memcmp(remote_keyid, pmkid, PMKID_LEN) == 0) { + prot->sec_keys->pmk_mismatch = false; + } + } + } + + // Checks if supplicant indicates that it has valid PTK + if (kde_ptkid_read(kde, kde_len, remote_keyid) >= 0) { + uint8_t ptkid[PTKID_LEN]; + if (sec_prot_lib_ptkid_generate(prot, ptkid, true) >= 0) { + if (memcmp(remote_keyid, ptkid, PTKID_LEN) == 0) { + prot->sec_keys->ptk_mismatch = false; + } + } + } + + // Get the GTKL that supplicant indicates + uint8_t gtkl; + if (kde_gtkl_read(kde, kde_len, >kl) >= 0) { + prot->sec_keys->gtkl = gtkl; + } else { + tr_error("no GTKL"); + return -1; + } + + tr_info("PMK %s PTK %s GTKL %x", prot->sec_keys->pmk_mismatch ? "not live" : "live", prot->sec_keys->ptk_mismatch ? "not live" : "live", gtkl); + + ns_dyn_mem_free(kde); + prot->create_ind(prot); return 0; } else { + tr_error("Invalid"); // No error handling yet, indicate just that ready to be deleted prot->finished(prot); return -1; @@ -208,8 +269,6 @@ static void key_sec_prot_state_machine(sec_prot_t *prot) // empty break; case KEY_CREATE_REQ: - tr_debug("initial EAPOL-Key send"); - // KMP-CREATE.confirm prot->create_conf(prot, data->result); @@ -221,8 +280,6 @@ static void key_sec_prot_state_machine(sec_prot_t *prot) prot->finished(prot); break; case KEY_CREATE_RESP: - tr_debug("initial EAPOL-Key receive"); - if (data->result == SEC_RESULT_OK) { // KMP-FINISHED.indication, no meaning for eapol-key, just completes transfer prot->finished_ind(prot, SEC_RESULT_OK, 0); diff --git a/source/Security/protocols/sec_prot.h b/source/Security/protocols/sec_prot.h index f1dc886200..59929a7217 100644 --- a/source/Security/protocols/sec_prot.h +++ b/source/Security/protocols/sec_prot.h @@ -207,6 +207,18 @@ typedef void sec_prot_eui64_addr_get(sec_prot_t *prot, uint8_t *local_eui64, uin */ typedef sec_prot_t *sec_prot_by_type_get(sec_prot_t *prot, uint8_t type); +/** + * sec_prot_receive_disable disables receiving of messages + * + * \param prot protocol + * + * \return security protocol or NULL + * + */ +typedef void sec_prot_receive_disable(sec_prot_t *prot); + +typedef struct sec_prot_int_data_s sec_prot_int_data_t; + // Security protocol data struct sec_prot_s { sec_prot_create_request *create_req; /**< Create request */ @@ -232,10 +244,11 @@ struct sec_prot_s { sec_prot_eui64_addr_get *addr_get; /**< Gets EUI-64 addresses */ sec_prot_by_type_get *type_get; /**< Gets security protocol by type */ + sec_prot_receive_disable *receive_disable; /**< Disable receiving of messages */ sec_prot_keys_t *sec_keys; /**< Security keys storage pointer */ uint8_t header_size; /**< Header size */ - uint8_t data; /**< Protocol internal data */ + sec_prot_int_data_t *data; /**< Protocol internal data */ }; #endif /* SEC_PROT_H_ */ diff --git a/source/Security/protocols/sec_prot_certs.c b/source/Security/protocols/sec_prot_certs.c index 3414fb272c..cc779a1149 100644 --- a/source/Security/protocols/sec_prot_certs.c +++ b/source/Security/protocols/sec_prot_certs.c @@ -71,6 +71,11 @@ void sec_prot_certs_chain_entry_init(cert_chain_entry_t *entry) memset(entry, 0, sizeof(cert_chain_entry_t)); } +void sec_prot_certs_chain_entry_delete(cert_chain_entry_t *entry) +{ + ns_dyn_mem_free(entry); +} + int8_t sec_prot_certs_cert_set(cert_chain_entry_t *entry, uint8_t index, uint8_t *cert, uint16_t cert_len) { if (!entry || index >= SEC_PROT_CERT_CHAIN_DEPTH) { @@ -127,6 +132,33 @@ void sec_prot_certs_chain_list_delete(cert_chain_list_t *chain_list) } } +void sec_prot_certs_chain_list_entry_delete(cert_chain_list_t *chain_list, cert_chain_entry_t *entry) +{ + ns_list_remove(chain_list, entry); + sec_prot_certs_chain_entry_delete(entry); +} + +cert_chain_entry_t *sec_prot_certs_chain_list_entry_find(cert_chain_list_t *chain_list, cert_chain_entry_t *entry) +{ + ns_list_foreach(cert_chain_entry_t, list_entry, chain_list) { + bool match = true; + for (uint8_t i = 0; i < SEC_PROT_CERT_CHAIN_DEPTH; i++) { + if (list_entry->cert_len[i] != entry->cert_len[i]) { + match = false; + break; + } + if (memcmp(list_entry->cert[i], entry->cert[i], list_entry->cert_len[i]) != 0) { + match = false; + break; + } + } + if (match) { + return list_entry; + } + } + return NULL; +} + cert_revocat_list_entry_t *sec_prot_certs_revocat_list_entry_create(void) { cert_revocat_list_entry_t *entry = ns_dyn_mem_alloc(sizeof(cert_revocat_list_entry_t)); @@ -142,7 +174,12 @@ void sec_prot_certs_revocat_list_entry_init(cert_revocat_list_entry_t *entry) memset(entry, 0, sizeof(cert_revocat_list_entry_t)); } -int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, uint8_t *crl, uint16_t crl_len) +void sec_prot_certs_revocat_list_entry_delete(cert_revocat_list_entry_t *entry) +{ + ns_dyn_mem_free(entry); +} + +int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, const uint8_t *crl, uint16_t crl_len) { if (!entry) { return -1; @@ -154,7 +191,7 @@ int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, uint8_t return 0; } -uint8_t *sec_prot_certs_revocat_list_get(const cert_revocat_list_entry_t *entry, uint16_t *crl_len) +const uint8_t *sec_prot_certs_revocat_list_get(const cert_revocat_list_entry_t *entry, uint16_t *crl_len) { if (!entry) { return NULL; @@ -168,6 +205,25 @@ void sec_prot_certs_revocat_lists_add(cert_revocat_lists_t *cert_revocat_lists, ns_list_add_to_end(cert_revocat_lists, entry); } +void sec_prot_certs_revocat_lists_entry_delete(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry) +{ + ns_list_remove(cert_revocat_lists, entry); + sec_prot_certs_revocat_list_entry_delete(entry); +} + +cert_revocat_list_entry_t *sec_prot_certs_revocat_lists_entry_find(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry) +{ + ns_list_foreach_safe(cert_revocat_list_entry_t, list_entry, cert_revocat_lists) { + if (list_entry->crl_len == entry->crl_len) { + if (memcmp(list_entry->crl, entry->crl, list_entry->crl_len) == 0) { + return list_entry; + } + } + } + + return NULL; +} + void sec_prot_certs_revocat_lists_delete(cert_revocat_lists_t *cert_revocat_lists) { ns_list_foreach_safe(cert_revocat_list_entry_t, entry, cert_revocat_lists) { diff --git a/source/Security/protocols/sec_prot_certs.h b/source/Security/protocols/sec_prot_certs.h index eb53ec5248..3c2ce3d1a9 100644 --- a/source/Security/protocols/sec_prot_certs.h +++ b/source/Security/protocols/sec_prot_certs.h @@ -46,7 +46,7 @@ typedef struct { } cert_chain_entry_t; typedef struct { - uint8_t *crl; /**< Certificate Revocation List */ + const uint8_t *crl; /**< Certificate Revocation List */ uint16_t crl_len; /**< Certificate Revocation List length */ ns_list_link_t link; /**< Link */ } cert_revocat_list_entry_t; @@ -92,6 +92,13 @@ cert_chain_entry_t *sec_prot_certs_chain_entry_create(void); */ void sec_prot_certs_chain_entry_init(cert_chain_entry_t *entry); +/** + * sec_prot_certs_chain_entry_delete deletes certificate chain entry + * + * \param entry certificate chain entry + */ +void sec_prot_certs_chain_entry_delete(cert_chain_entry_t *entry); + /** * sec_prot_certs_cert_set set certificate to chain entry * @@ -153,6 +160,26 @@ void sec_prot_certs_chain_list_add(cert_chain_list_t *cert_chain_list, cert_chai */ void sec_prot_certs_chain_list_delete(cert_chain_list_t *chain_list); +/** + * sec_prot_certs_chain_list_entry_delete deletes entry from certificate chain list + * + * \param cert_chain_list certificate chain entry list + * \param entry deleted certificate chain entry + * + */ +void sec_prot_certs_chain_list_entry_delete(cert_chain_list_t *chain_list, cert_chain_entry_t *entry); + +/** + * sec_prot_certs_chain_list_entry_find finds entry from certificate chain list + * + * \param cert_chain_list certificate chain entry list + * \param entry searched certificate chain entry + * + * \return certificate chain entry or NULL + * + */ +cert_chain_entry_t *sec_prot_certs_chain_list_entry_find(cert_chain_list_t *chain_list, cert_chain_entry_t *entry); + /** * sec_prot_certs_revocat_list_entry_create allocate memory for certificate revocation list entry * @@ -167,6 +194,13 @@ cert_revocat_list_entry_t *sec_prot_certs_revocat_list_entry_create(void); */ void sec_prot_certs_revocat_list_entry_init(cert_revocat_list_entry_t *entry); +/** + * sec_prot_certs_revocat_list_entry_delete deletes certificate revocation list entry + * + * \param entry certificate revocation list entry + */ +void sec_prot_certs_revocat_list_entry_delete(cert_revocat_list_entry_t *entry); + /** * sec_prot_certs_revocat_list_set set certificate revocation list to list entry * @@ -177,7 +211,7 @@ void sec_prot_certs_revocat_list_entry_init(cert_revocat_list_entry_t *entry); * \return < 0 failure * \return >= 0 success */ -int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, uint8_t *crl, uint16_t crl_len); +int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, const uint8_t *crl, uint16_t crl_len); /** * sec_prot_certs_revocat_list_set set certificate revocation list from list entry @@ -187,16 +221,35 @@ int8_t sec_prot_certs_revocat_list_set(cert_revocat_list_entry_t *entry, uint8_t * * \return pointer to crl or NULL */ -uint8_t *sec_prot_certs_revocat_list_get(const cert_revocat_list_entry_t *entry, uint16_t *crl_len); +const uint8_t *sec_prot_certs_revocat_list_get(const cert_revocat_list_entry_t *entry, uint16_t *crl_len); /** - * sec_prot_certs_revocat_lists_add add certificate chain entry to certificate chain list + * sec_prot_certs_revocat_lists_add add certificate revocation list entry to certificate revocation lists * * \param cert_revocat_lists certificate revocation lists * \param entry certificate revocation list entry */ void sec_prot_certs_revocat_lists_add(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry); +/** + * sec_prot_certs_revocat_lists_entry_delete delete certificate revocation list entry from certificate revocation lists + * + * \param cert_revocat_lists certificate revocation lists + * \param entry certificate revocation list entry + * + */ +void sec_prot_certs_revocat_lists_entry_delete(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry); + +/** + * sec_prot_certs_revocat_lists_entry_find find certificate revocation list entry from certificate revocation lists + * + * \param cert_revocat_lists certificate revocation lists + * \param entry certificate revocation list entry + * + * \return certificate revocation list entry or NULL + */ +cert_revocat_list_entry_t *sec_prot_certs_revocat_lists_entry_find(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry); + /** * sec_prot_certs_chain_list_delete delete certificate chain list * diff --git a/source/Security/protocols/sec_prot_keys.c b/source/Security/protocols/sec_prot_keys.c index ce1b0de5ff..ccb287a127 100644 --- a/source/Security/protocols/sec_prot_keys.c +++ b/source/Security/protocols/sec_prot_keys.c @@ -25,8 +25,14 @@ #include "Common_Protocols/ipv6_constants.h" #include "socket_api.h" #include "6LoWPAN/ws/ws_config.h" +#include "Security/kmp/kmp_addr.h" +#include "Security/kmp/kmp_api.h" +#include "Security/PANA/pana_eap_header.h" +#include "Security/eapol/eapol_helper.h" #include "Security/protocols/sec_prot_certs.h" #include "Security/protocols/sec_prot_keys.h" +#include "Security/protocols/sec_prot.h" +#include "Security/protocols/sec_prot_lib.h" #ifdef HAVE_WS @@ -47,13 +53,20 @@ sec_prot_keys_t *sec_prot_keys_create(sec_prot_gtk_keys_t *gtks, const sec_prot_ void sec_prot_keys_init(sec_prot_keys_t *sec_keys, sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs) { memset(sec_keys, 0, sizeof(sec_prot_keys_t)); + sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL; + sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL; sec_keys->pmk_key_replay_cnt = 0; sec_keys->gtks = gtks; sec_keys->certs = certs; + sec_keys->gtkl = 0; + sec_keys->gtk_set_index = -1; sec_keys->pmk_set = false; sec_keys->ptk_set = false; + sec_keys->pmk_key_replay_cnt_set = false; sec_keys->updated = false; sec_keys->ptk_eui_64_set = false; + sec_keys->pmk_mismatch = false; + sec_keys->ptk_mismatch = false; } void sec_prot_keys_delete(sec_prot_keys_t *sec_keys) @@ -76,7 +89,6 @@ sec_prot_gtk_keys_t *sec_prot_keys_gtks_create(void) void sec_prot_keys_gtks_init(sec_prot_gtk_keys_t *gtks) { memset(gtks, 0, sizeof(sec_prot_gtk_keys_t)); - gtks->gtk_set_index = -1; gtks->updated = false; } @@ -89,10 +101,22 @@ void sec_prot_keys_pmk_write(sec_prot_keys_t *sec_keys, uint8_t *pmk) { memcpy(sec_keys->pmk, pmk, PMK_LEN); sec_keys->pmk_key_replay_cnt = 0; + sec_keys->pmk_key_replay_cnt_set = false; + sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL; sec_keys->pmk_set = true; sec_keys->updated = true; } +void sec_prot_keys_pmk_delete(sec_prot_keys_t *sec_keys) +{ + memset(sec_keys->pmk, 0, PMK_LEN); + sec_keys->pmk_key_replay_cnt = 0; + sec_keys->pmk_key_replay_cnt_set = false; + sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL; + sec_keys->pmk_set = false; + sec_keys->updated = true; +} + uint8_t *sec_prot_keys_pmk_get(sec_prot_keys_t *sec_keys) { if (!sec_keys->pmk_set) { @@ -109,21 +133,87 @@ uint64_t sec_prot_keys_pmk_replay_cnt_get(sec_prot_keys_t *sec_keys) void sec_prot_keys_pmk_replay_cnt_set(sec_prot_keys_t *sec_keys, uint64_t counter) { + sec_keys->pmk_key_replay_cnt_set = true; sec_keys->pmk_key_replay_cnt = counter; } void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys) { + // Start from zero i.e. does not increment on first call + if (!sec_keys->pmk_key_replay_cnt_set) { + sec_keys->pmk_key_replay_cnt_set = true; + return; + } sec_keys->pmk_key_replay_cnt++; } +bool sec_prot_keys_pmk_replay_cnt_compare(uint64_t received_counter, sec_prot_keys_t *sec_keys) +{ + // If previous value is set must be greater + if (sec_keys->pmk_key_replay_cnt_set && received_counter > sec_keys->pmk_key_replay_cnt) { + return true; + } else if (!sec_keys->pmk_key_replay_cnt_set && received_counter >= sec_keys->pmk_key_replay_cnt) { + // Otherwise allows also same value e.g. zero + return true; + } + + return false; +} + +void sec_prot_keys_pmk_mismatch_set(sec_prot_keys_t *sec_keys) +{ + sec_keys->pmk_mismatch = true; +} + +void sec_prot_keys_pmk_mismatch_reset(sec_prot_keys_t *sec_keys) +{ + sec_keys->pmk_mismatch = false; +} + +bool sec_prot_keys_pmk_mismatch_is_set(sec_prot_keys_t *sec_keys) +{ + return sec_keys->pmk_mismatch; +} + +bool sec_prot_keys_pmk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds) +{ + if (!sec_keys->pmk_set) { + return false; + } + + if (sec_keys->pmk_lifetime == PMK_LIFETIME_INSTALL) { + sec_keys->pmk_lifetime = default_lifetime; + } + + if (sec_keys->pmk_lifetime > seconds) { + sec_keys->pmk_lifetime -= seconds; + } else { + if (sec_keys->pmk_lifetime > 0) { + sec_keys->pmk_lifetime = 0; + sec_prot_keys_ptk_delete(sec_keys); + sec_prot_keys_pmk_delete(sec_keys); + return true; + } + } + return false; +} + void sec_prot_keys_ptk_write(sec_prot_keys_t *sec_keys, uint8_t *ptk) { memcpy(sec_keys->ptk, ptk, PTK_LEN); + sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL; sec_keys->ptk_set = true; sec_keys->updated = true; } +void sec_prot_keys_ptk_delete(sec_prot_keys_t *sec_keys) +{ + memset(sec_keys->ptk, 0, PTK_LEN); + sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL; + sec_keys->ptk_set = false; + sec_keys->updated = true; +} + uint8_t *sec_prot_keys_ptk_get(sec_prot_keys_t *sec_keys) { if (!sec_keys->ptk_set) { @@ -133,29 +223,66 @@ uint8_t *sec_prot_keys_ptk_get(sec_prot_keys_t *sec_keys) return sec_keys->ptk; } -void sec_prot_keys_ptk_eui_64_set(sec_prot_keys_t *sec_keys, uint8_t *eui_64) +void sec_prot_keys_ptk_mismatch_set(sec_prot_keys_t *sec_keys) { - sec_keys->ptk_eui_64 = eui_64; + sec_keys->ptk_mismatch = true; } -void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, uint8_t *eui_64) +void sec_prot_keys_ptk_mismatch_reset(sec_prot_keys_t *sec_keys) { - if (sec_keys->ptk_eui_64) { - memcpy(sec_keys->ptk_eui_64, eui_64, 8); - sec_keys->ptk_eui_64_set = true; - sec_keys->updated = true; - } + sec_keys->ptk_mismatch = false; +} + +bool sec_prot_keys_ptk_mismatch_is_set(sec_prot_keys_t *sec_keys) +{ + return sec_keys->ptk_mismatch; +} + +void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, const uint8_t *eui_64) +{ + memcpy(sec_keys->ptk_eui_64, eui_64, 8); + sec_keys->ptk_eui_64_set = true; + sec_keys->updated = true; } uint8_t *sec_prot_keys_ptk_eui_64_get(sec_prot_keys_t *sec_keys) { - if (!sec_keys->ptk_eui_64 || !sec_keys->ptk_eui_64_set) { + if (!sec_keys->ptk_eui_64_set) { return NULL; } return sec_keys->ptk_eui_64; } +void sec_prot_keys_ptk_eui_64_delete(sec_prot_keys_t *sec_keys) +{ + memset(sec_keys->ptk_eui_64, 0, 8); + sec_keys->ptk_eui_64_set = false; + sec_keys->updated = true; +} + +bool sec_prot_keys_ptk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds) +{ + if (!sec_keys->ptk_set) { + return false; + } + + if (sec_keys->ptk_lifetime == PTK_LIFETIME_INSTALL) { + sec_keys->ptk_lifetime = default_lifetime; + } + + if (sec_keys->ptk_lifetime > seconds) { + sec_keys->ptk_lifetime -= seconds; + } else { + if (sec_keys->ptk_lifetime > 0) { + sec_prot_keys_ptk_delete(sec_keys); + sec_keys->ptk_lifetime = 0; + return true; + } + } + return false; +} + bool sec_prot_keys_are_updated(sec_prot_keys_t *sec_keys) { return sec_keys->updated; @@ -166,82 +293,129 @@ void sec_prot_keys_updated_reset(sec_prot_keys_t *sec_keys) sec_keys->updated = false; } -uint8_t sec_prot_keys_gtkl_get(sec_prot_gtk_keys_t *gtks) +uint8_t sec_prot_keys_fresh_gtkl_get(sec_prot_gtk_keys_t *gtks) { - uint8_t gtkl = (uint8_t) gtks->gtk[0].live | - (((uint8_t) gtks->gtk[1].live) << 1) | - (((uint8_t) gtks->gtk[2].live) << 2) | - (((uint8_t) gtks->gtk[3].live) << 3); + uint8_t gtkl = 0; + + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_status_is_live(gtks, i)) { + gtkl |= 1 << i; + } + } return gtkl; } -void sec_prot_keys_gtkl_set(sec_prot_gtk_keys_t *gtks, uint8_t gtkl) +void sec_prot_keys_gtkl_set(sec_prot_keys_t *sec_keys, uint8_t gtkl) { - for (uint8_t i = 0; i < GTK_NUM; i++) { - if (gtks->gtk[i].set) { - if ((gtkl >> i) & 0x01) { - gtks->gtk[i].live = true; // Live on authenticator - } else { - gtks->gtk[i].live = false; - } - } - } + sec_keys->gtkl = gtkl; } -bool sec_prot_keys_gtk_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index) +bool sec_prot_keys_gtkl_gtk_is_live(sec_prot_keys_t *sec_keys, uint8_t index) { - if (index >= GTK_NUM || !gtks->gtk[index].live) { + if (index >= GTK_NUM) { return false; } - return true; + if (sec_keys->gtkl & (1 << index)) { + return true; + } + + return false; } -int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_gtk_keys_t *gtks, uint8_t index) +int8_t sec_prot_keys_gtkl_gtk_live_set(sec_prot_keys_t *sec_keys, uint8_t index) { - if (index >= GTK_NUM || !gtks->gtk[index].set) { + if (index >= GTK_NUM) { return -1; } - gtks->gtk_set_index = index; + sec_keys->gtkl |= (1 << index); + return 0; } -int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_gtk_keys_t *gtks) +int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_keys_t *sec_keys, uint8_t index) { - return gtks->gtk_set_index; + if (index >= GTK_NUM || !sec_keys->gtks->gtk[index].set) { + return -1; + } + + sec_keys->gtk_set_index = index; + return 0; } -void sec_prot_keys_gtk_insert_index_clear(sec_prot_gtk_keys_t *gtks) +int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_keys_t *sec_keys) { - gtks->gtk_set_index = -1; + return sec_keys->gtk_set_index; } -uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_gtk_keys_t *gtks, uint8_t *index) +void sec_prot_keys_gtk_insert_index_clear(sec_prot_keys_t *sec_keys) { - if (gtks->gtk_set_index >= 0 && gtks->gtk[gtks->gtk_set_index].set && gtks->gtk[gtks->gtk_set_index].live) { - *index = gtks->gtk_set_index; - return gtks->gtk[gtks->gtk_set_index].key; + sec_keys->gtk_set_index = -1; +} + +void sec_prot_keys_gtkl_from_gtk_insert_index_set(sec_prot_keys_t *sec_keys) +{ + if (sec_keys->gtk_set_index >= 0) { + sec_prot_keys_gtkl_gtk_live_set(sec_keys, sec_keys->gtk_set_index); + sec_prot_keys_gtk_insert_index_clear(sec_keys); + } +} + +int8_t sec_prot_keys_gtk_insert_index_from_gtkl_get(sec_prot_keys_t *sec_keys) +{ + // Get currently active key index + int8_t active_index = sec_prot_keys_gtk_status_active_get(sec_keys->gtks); + + if (active_index >= 0 && !sec_prot_keys_gtkl_gtk_is_live(sec_keys, active_index)) { + // If currently active key is not live on remote, inserts it + sec_prot_keys_gtk_insert_index_set(sec_keys, active_index); + return active_index; + } + + // Checks all keys + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_status_is_live(sec_keys->gtks, i)) { + // If key is live, but not indicated on GTKL inserts it + if (!sec_prot_keys_gtkl_gtk_is_live(sec_keys, i)) { + sec_prot_keys_gtk_insert_index_set(sec_keys, i); + return i; + } + } + } + + return -1; +} + +uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_keys_t *sec_keys, uint8_t *index) +{ + if (sec_keys->gtk_set_index >= 0 && sec_keys->gtks->gtk[sec_keys->gtk_set_index].set) { + *index = sec_keys->gtk_set_index; + return sec_keys->gtks->gtk[sec_keys->gtk_set_index].key; } else { return NULL; } } -int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk) +int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk, uint32_t lifetime) { if (!gtk || index >= GTK_NUM) { return -1; } - if (gtks->gtk[index].set && memcmp(gtks->gtk[index].key, gtk, GTK_LEN) != 0) { + // If same GTK is given again, do not update + if (gtks->gtk[index].set && memcmp(gtks->gtk[index].key, gtk, GTK_LEN) == 0) { return -1; } + sec_prot_keys_gtk_clear(gtks, index); + uint8_t install_order = sec_prot_keys_gtk_install_order_last_get(gtks); + gtks->gtk[index].set = true; - gtks->gtk[index].live = false; // Set from GTKL - gtks->gtk[index].hash = false; // Not verified yet - gtks->gtk[index].lifetime = 0; // Should be provided by authenticator + gtks->gtk[index].lifetime = lifetime; + gtks->gtk[index].status = GTK_STATUS_NEW; + gtks->gtk[index].install_order = install_order; memcpy(gtks->gtk[index].key, gtk, GTK_LEN); gtks->updated = true; @@ -249,6 +423,22 @@ int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t * return 0; } +int8_t sec_prot_keys_gtk_clear(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (!gtks || index >= GTK_NUM) { + return -1; + } + + gtks->gtk[index].set = false; + gtks->gtk[index].lifetime = 0; // Should be provided by authenticator + gtks->gtk[index].status = GTK_STATUS_NEW; + memset(gtks->gtk[index].key, 0, GTK_LEN); + + sec_prot_keys_gtk_install_order_update(gtks); + + return 0; +} + bool sec_prot_keys_gtk_is_set(sec_prot_gtk_keys_t *gtks, uint8_t index) { if (index >= GTK_NUM || !gtks->gtk[index].set) { @@ -276,15 +466,15 @@ uint32_t sec_prot_keys_gtk_lifetime_get(sec_prot_gtk_keys_t *gtks, uint8_t index return gtks->gtk[index].lifetime; } -void sec_prot_keys_gtk_lifetime_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint32_t lifetime) +uint32_t sec_prot_keys_gtk_lifetime_decrement(sec_prot_gtk_keys_t *gtks, uint8_t index, uint16_t seconds) { - if (index >= GTK_NUM || !gtks->gtk[index].set) { - return; - + if (gtks->gtk[index].lifetime > seconds) { + gtks->gtk[index].lifetime -= seconds; + } else { + gtks->gtk[index].lifetime = 0; } - gtks->gtk[index].lifetime = lifetime; - gtks->updated = true; + return gtks->gtk[index].lifetime; } bool sec_prot_keys_gtks_are_updated(sec_prot_gtk_keys_t *gtks) @@ -302,4 +492,290 @@ void sec_prot_keys_gtks_updated_reset(sec_prot_gtk_keys_t *gtks) gtks->updated = false; } +void sec_prot_keys_gtk_status_fresh_set(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return; + } + + // Active key remains as active, old keys are never reused + if (gtks->gtk[index].status < GTK_STATUS_FRESH) { + gtks->gtk[index].status = GTK_STATUS_FRESH; + } +} + +void sec_prot_keys_gtk_status_all_fresh_set(sec_prot_gtk_keys_t *gtks) +{ + for (uint8_t i = 0; i < GTK_NUM; i++) { + sec_prot_keys_gtk_status_fresh_set(gtks, i); + } +} + +int8_t sec_prot_keys_gtk_status_active_set(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return -1; + } + + // If key is valid to be taken into use sets it active + if (gtks->gtk[index].status == GTK_STATUS_FRESH) { + // Sets previously active key old + for (uint8_t i = 0; i < GTK_NUM; i++) { + // Sets previously active key old + if (gtks->gtk[i].status == GTK_STATUS_ACTIVE) { + gtks->gtk[i].status = GTK_STATUS_OLD; + } + } + gtks->gtk[index].status = GTK_STATUS_ACTIVE; + return 0; + } + + return -1; +} + +int8_t sec_prot_keys_gtk_status_active_get(sec_prot_gtk_keys_t *gtks) +{ + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (gtks->gtk[i].status == GTK_STATUS_ACTIVE) { + return i; + } + } + + return -1; +} + +bool sec_prot_keys_gtk_status_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return false; + } + + if (gtks->gtk[index].status == GTK_STATUS_FRESH || gtks->gtk[index].status == GTK_STATUS_ACTIVE) { + return true; + } + + return false; +} + +void sec_prot_keys_gtk_status_new_set(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return; + } + + gtks->gtk[index].status = GTK_STATUS_NEW; +} + +void sec_prot_keys_gtks_hash_generate(sec_prot_gtk_keys_t *gtks, uint8_t *gtkhash) +{ + memset(gtkhash, 0, GTK_ALL_HASHES_LEN); + + uint8_t *gtk_hash_ptr = gtkhash; + + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i); + sec_prot_lib_gtkhash_generate(gtk, gtk_hash_ptr); + } + gtk_hash_ptr += GTK_HASH_LEN; + } +} + +gtk_mismatch_e sec_prot_keys_gtks_hash_update(sec_prot_gtk_keys_t *gtks, uint8_t *gtkhash) +{ + uint8_t *gtk_hash_ptr = gtkhash; + + gtk_mismatch_e mismatch = GTK_NO_MISMATCH; + + for (uint8_t i = 0; i < GTK_NUM; i++, gtk_hash_ptr += 8) { + // If hash is not set, stop using the key + if (sec_prot_keys_gtk_hash_empty(gtk_hash_ptr)) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + uint32_t lifetime = sec_prot_keys_gtk_lifetime_get(gtks, i); + if (lifetime > GTK_EXPIRE_MISMATCH_TIME) { + tr_info("GTK mismatch %i expired time, lifetime: %"PRIu32"", i, lifetime); + if (mismatch < GTK_LIFETIME_MISMATCH) { + mismatch = GTK_LIFETIME_MISMATCH; + } + } + sec_prot_keys_gtk_clear(gtks, i); + } + } else { + // Check is hash matches to existing key + uint8_t gtk_hash[8]; + uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i); + if (!gtk) { + // Hash set but GTK is not known, set mismatch + tr_info("GTK mismatch: %i", i); + if (mismatch < GTK_HASH_MISMATCH) { + mismatch = GTK_HASH_MISMATCH; + } + continue; + } + + sec_prot_lib_gtkhash_generate(gtk, gtk_hash); + + if (memcmp(gtk_hash, gtk_hash_ptr, 8) == 0) { + // Key is fresh (or active, if old do not change state) + sec_prot_keys_gtk_status_fresh_set(gtks, i); + } else { + // Hash does not match, set mismatch and delete key + tr_info("GTK mismatch: %i", i); + if (mismatch < GTK_HASH_MISMATCH) { + mismatch = GTK_HASH_MISMATCH; + } + sec_prot_keys_gtk_clear(gtks, i); + } + } + } + + return mismatch; +} + +bool sec_prot_keys_gtk_hash_empty(uint8_t *gtkhash) +{ + const uint8_t empty_hash[GTK_HASH_LEN] = {0}; + if (memcmp(gtkhash, empty_hash, GTK_HASH_LEN) == 0) { + return true; + } else { + return false; + } +} + +int8_t sec_prot_keys_gtk_install_order_last_get(sec_prot_gtk_keys_t *gtks) +{ + int8_t install_order = -1; + + // Gets the last key index + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + if (gtks->gtk[i].install_order > install_order) { + install_order = gtks->gtk[i].install_order; + } + } + } + + return install_order + 1; +} + +int8_t sec_prot_keys_gtk_install_order_last_index_get(sec_prot_gtk_keys_t *gtks) +{ + int8_t install_order = -1; + int8_t index = -1; + + // Gets the last key index + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + if (gtks->gtk[i].install_order > install_order) { + install_order = gtks->gtk[i].install_order; + index = i; + } + } + } + + return index; +} + +uint32_t sec_prot_keys_gtk_install_order_last_lifetime_get(sec_prot_gtk_keys_t *gtks) +{ + uint32_t lifetime = 0; + int8_t install_order = -1; + + // Gets the last key index + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + if (gtks->gtk[i].install_order > install_order) { + install_order = gtks->gtk[i].install_order; + lifetime = gtks->gtk[i].lifetime; + } + } + } + + return lifetime; +} + +int8_t sec_prot_keys_gtk_install_order_first_index_get(sec_prot_gtk_keys_t *gtks) +{ + // Gets the first key index + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + if (gtks->gtk[i].install_order == GTK_INSTALL_ORDER_FIRST) { + return i; + } + } + } + + return -1; +} + +int8_t sec_prot_keys_gtk_install_order_second_index_get(sec_prot_gtk_keys_t *gtks) +{ + // Gets the first key index + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + if (gtks->gtk[i].install_order == GTK_INSTALL_ORDER_SECOND) { + return i; + } + } + } + + return -1; +} + +void sec_prot_keys_gtk_install_order_update(sec_prot_gtk_keys_t *gtks) +{ + int8_t ordered_indexes[4] = {-1, -1, -1, -1}; + + // Creates table of ordered indexes + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + ordered_indexes[gtks->gtk[i].install_order] = i; + } + } + + // Updates indexes of the GTKs + uint8_t new_install_order = 0; + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (ordered_indexes[i] >= 0) { + gtks->gtk[ordered_indexes[i]].install_order = new_install_order++; + } + } +} + +int8_t sec_prot_keys_gtk_install_index_get(sec_prot_gtk_keys_t *gtks) +{ + // Gets the index of the last key to be installed + int8_t install_index = sec_prot_keys_gtk_install_order_last_index_get(gtks); + if (install_index < 0) { + install_index = 0; + } + + // Checks if there is free index, and available uses that for new GTK + for (uint8_t ctr = 0, i = install_index; ctr < GTK_NUM; ctr++) { + if (!sec_prot_keys_gtk_is_set(gtks, i)) { + install_index = i; + break; + } + i++; + if (i >= GTK_NUM) { + i = 0; + } + } + + return install_index; +} + +uint8_t sec_prot_keys_gtk_count(sec_prot_gtk_keys_t *gtks) +{ + uint8_t count = 0; + + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + count++; + } + } + + return count; +} + #endif /* HAVE_WS */ diff --git a/source/Security/protocols/sec_prot_keys.h b/source/Security/protocols/sec_prot_keys.h index 0f748ad14a..1653810b5d 100644 --- a/source/Security/protocols/sec_prot_keys.h +++ b/source/Security/protocols/sec_prot_keys.h @@ -26,34 +26,49 @@ * */ -#define PMK_LEN 32 -#define PTK_LEN 48 -#define GTK_LEN 16 -#define GTK_NUM 4 +#define PMK_LEN 32 +#define PTK_LEN 48 +#define GTK_LEN 16 +#define GTK_NUM 4 -#define KCK_LEN 16 -#define KEK_LEN 16 +#define KCK_LEN 16 +#define KEK_LEN 16 -#define KCK_INDEX 0 -#define KEK_INDEX 16 +#define KCK_INDEX 0 +#define KEK_INDEX 16 -#define PMKID_LEN 16 -#define PTKID_LEN 16 +#define PMKID_LEN 16 +#define PTKID_LEN 16 +#define KEYID_LEN 16 -#define GTK_DEFAULT_LIFETIME 60 * 60 * 24 * 30 // 30 days +#define GTK_DEFAULT_LIFETIME 60 * 60 * 24 * 30 // 30 days +#define GTK_EXPIRE_MISMATCH_TIME 60 // Supplicant GTK expire time mismatch occurs if GTK expires before this time + +#define GTK_STATUS_NEW 0 // New GTK, can transition to fresh +#define GTK_STATUS_FRESH 1 // Fresh GTK, live based on hash, can transition to active +#define GTK_STATUS_ACTIVE 2 // Active GTK, live based on hash, can transition to old +#define GTK_STATUS_OLD 3 // Old GTK, not to be used for sending + +#define GTK_INSTALL_ORDER_FIRST 0 // Install order runs from 0 to 3, where 0 is the GTK to be installed first +#define GTK_INSTALL_ORDER_SECOND 1 // Install order runs from 0 to 3, where 1 is the GTK to be installed second + +#define GTK_HASH_LEN 8 +#define GTK_ALL_HASHES_LEN GTK_HASH_LEN * GTK_NUM + +#define PMK_LIFETIME_INSTALL 0xFFFFF +#define PTK_LIFETIME_INSTALL 0xFFFFF typedef struct { - uint8_t key[GTK_LEN]; /**< Group Transient Key (128 bits) */ - uint32_t lifetime; /**< Lifetime is seconds */ - bool set: 1; /**< Group Transient Key set (valid value) */ - bool live: 1; /**< Group Transient Key live (as indicated by authenticator) */ - bool hash: 1; /**< Group Transient Key matches to hash */ + uint8_t key[GTK_LEN]; /**< Group Transient Key (128 bits) */ + uint32_t lifetime; /**< GTK lifetime in seconds */ + unsigned status : 2; /**< Group Transient Key status */ + unsigned install_order : 2; /**< Order in which GTK keys are added */ + bool set: 1; /**< Group Transient Key set (valid value) */ } gtk_key_t; typedef struct { gtk_key_t gtk[GTK_NUM]; /**< 4 Group Transient Keys */ - int8_t gtk_set_index; /**< Group Transient Key to insert */ - bool updated; /**< Group Transient Keys has been updated */ + bool updated: 1; /**< Group Transient Keys has been updated */ } sec_prot_gtk_keys_t; // Security key data @@ -61,15 +76,32 @@ typedef struct { uint64_t pmk_key_replay_cnt; /**< Pairwise Master Key replay counter */ uint8_t pmk[PMK_LEN]; /**< Pairwise Master Key (256 bits) */ uint8_t ptk[PTK_LEN]; /**< Pairwise Transient Key (384 bits) */ - uint8_t *ptk_eui_64; /**< Pointer to remote EUI-64 used to derive PTK or NULL */ + uint8_t ptk_eui_64[8]; /**< Remote EUI-64 used to derive PTK or NULL */ sec_prot_gtk_keys_t *gtks; /**< Group Transient Keys */ const sec_prot_certs_t *certs; /**< Certificates */ + uint32_t pmk_lifetime; /**< PMK lifetime in seconds */ + uint32_t ptk_lifetime; /**< PTK lifetime in seconds */ + uint8_t gtkl; /**< Remote GTKL information */ + int8_t gtk_set_index; /**< Index of GTK to set */ bool pmk_set: 1; /**< Pairwise Master Key set */ bool ptk_set: 1; /**< Pairwise Transient Key set */ + bool pmk_key_replay_cnt_set: 1; /**< Pairwise Master Key replay counter set */ bool updated: 1; /**< Keys has been updated */ bool ptk_eui_64_set: 1; /**< Remote EUI-64 used to derive PTK is set */ + bool pmk_mismatch: 1; /**< Remote PMK mismatch reported */ + bool ptk_mismatch: 1; /**< Remote PTK mismatch reported */ } sec_prot_keys_t; +/* + * GTK mismatch types, list is ordered according to priority of mismatch i.e. if there + * are both hash and lifetime mismatch, hash has greater priority + */ +typedef enum { + GTK_NO_MISMATCH = 0, + GTK_LIFETIME_MISMATCH, + GTK_HASH_MISMATCH, +} gtk_mismatch_e; + /** * sec_prot_keys_create allocates memory for security keys * @@ -131,6 +163,14 @@ void sec_prot_keys_gtks_delete(sec_prot_gtk_keys_t *gtks); */ void sec_prot_keys_pmk_write(sec_prot_keys_t *sec_keys, uint8_t *pmk); +/** + * sec_prot_keys_pmk_delete deletes PMK + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_pmk_delete(sec_prot_keys_t *sec_keys); + /** * sec_prot_keys_pmk_get gets Pairwise Master Key * @@ -168,6 +208,57 @@ void sec_prot_keys_pmk_replay_cnt_set(sec_prot_keys_t *sec_keys, uint64_t counte */ void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys); +/** + * sec_prot_keys_pmk_replay_cnt_compare compares received replay counter value to PMK replay counter + * + * \param received_counter received replay counter + * \param sec_keys security keys + * + * \return TRUE received replay counter is valid + * \return FALSE received replay counter is not valid + * + */ +bool sec_prot_keys_pmk_replay_cnt_compare(uint64_t received_counter, sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_mismatch_set set PMK mismatch + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_pmk_mismatch_set(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_mismatch_reset reset PMK mismatch + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_pmk_mismatch_reset(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_mismatch_is_set check if PMK mismatch is set + * + * \param sec_keys security keys + * + * \return TRUE or FALSE + * + */ +bool sec_prot_keys_pmk_mismatch_is_set(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_lifetime_decrement decrements PMK lifetime + * + * \param sec_keys security keys + * \param default_lifetime default lifetime for PMK + * \param seconds elapsed seconds + * + * \return true PMK expired and deleted both PMK and PTK + * \return false PMK not expired + * + */ +bool sec_prot_keys_pmk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds); + /** * sec_prot_keys_ptk_write writes Pairwise Transient Key * @@ -177,6 +268,14 @@ void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys); */ void sec_prot_keys_ptk_write(sec_prot_keys_t *sec_keys, uint8_t *ptk); +/** + * sec_prot_keys_ptk_delete deletes PTK + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_ptk_delete(sec_prot_keys_t *sec_keys); + /** * sec_prot_keys_ptk_get gets Pairwise Transient Key * @@ -188,13 +287,30 @@ void sec_prot_keys_ptk_write(sec_prot_keys_t *sec_keys, uint8_t *ptk); uint8_t *sec_prot_keys_ptk_get(sec_prot_keys_t *sec_keys); /** - * sec_prot_keys_ptk_eui_64_set sets PTK EUI-64 storage + * sec_prot_keys_ptk_mismatch_set set PTK mismatch * * \param sec_keys security keys - * \param eui64 EUI-64 storage * */ -void sec_prot_keys_ptk_eui_64_set(sec_prot_keys_t *sec_keys, uint8_t *eui_64); +void sec_prot_keys_ptk_mismatch_set(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_ptk_mismatch_reset reset PTK mismatch + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_ptk_mismatch_reset(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_ptk_mismatch_is_set check if PTK mismatch is set + * + * \param sec_keys security keys + * + * \return TRUE or FALSE + * + */ +bool sec_prot_keys_ptk_mismatch_is_set(sec_prot_keys_t *sec_keys); /** * sec_prot_keys_ptk_eui_64_write writes PTK EUI-64 @@ -203,7 +319,7 @@ void sec_prot_keys_ptk_eui_64_set(sec_prot_keys_t *sec_keys, uint8_t *eui_64); * \param eui64 EUI-64 * */ -void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, uint8_t *eui_64); +void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, const uint8_t *eui_64); /** * sec_prot_keys_ptk_eui_64_get gets PTK EUI-64 @@ -215,6 +331,27 @@ void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, uint8_t *eui_64); */ uint8_t *sec_prot_keys_ptk_eui_64_get(sec_prot_keys_t *sec_keys); +/** + * sec_prot_keys_ptk_eui_64_delete deletes PTK EUI-64 + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_ptk_eui_64_delete(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_ptk_lifetime_decrement decrements PTK lifetime + * + * \param sec_keys security keys + * \param default_lifetime default lifetime for PTK + * \param seconds elapsed seconds + * + * \return true PTK expired and deleted + * \return false PTK not expired + * + */ +bool sec_prot_keys_ptk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds); + /** * sec_prot_keys_are_updated returns security keys have been updated flag * @@ -230,81 +367,111 @@ bool sec_prot_keys_are_updated(sec_prot_keys_t *sec_keys); * * \param sec_keys security keys * - * */ void sec_prot_keys_updated_reset(sec_prot_keys_t *sec_keys); /** - * sec_prot_keys_gtkl_get gets Group Transient Key liveness + * sec_prot_keys_fresh_gtkl_get gets GTK liveness based on GTK status fields * * \param gtks GTK keys * * \return bit field indicating GTK liveness * */ -uint8_t sec_prot_keys_gtkl_get(sec_prot_gtk_keys_t *gtks); +uint8_t sec_prot_keys_fresh_gtkl_get(sec_prot_gtk_keys_t *gtks); /** - * sec_prot_keys_gtkl_set sets Group Transient Key liveness + * sec_prot_keys_gtkl_set sets GTK liveness storage * - * \param gtks GTK keys + * \param sec_keys security keys * \param gtkl bit field indicating GTK liveness * */ -void sec_prot_keys_gtkl_set(sec_prot_gtk_keys_t *gtks, uint8_t gtkl); +void sec_prot_keys_gtkl_set(sec_prot_keys_t *sec_keys, uint8_t gtkl); /** - * sec_prot_keys_gtk_is_live checks if Group Transient Key is live + * sec_prot_keys_gtkl_set checks whether GTK is live on GTK liveness storage * - * \param gtks GTK keys - * \param index GTK index + * \param sec_keys security keys + * \param index index of the GTK which liveness is returned * * \return TRUE GTK is live, FALSE GTK is not live * */ -bool sec_prot_keys_gtk_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index); +bool sec_prot_keys_gtkl_gtk_is_live(sec_prot_keys_t *sec_keys, uint8_t index); + +/** + * sec_prot_keys_gtkl_gtk_live_set sets that GTK is live to GTK liveness storage + * + * \param sec_keys security keys + * \param index index of the GTK which is set live + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t sec_prot_keys_gtkl_gtk_live_set(sec_prot_keys_t *sec_keys, uint8_t index); /** * sec_prot_keys_gtk_insert_index_set sets index of GTK to be inserted * - * \param gtks GTK keys + * \param sec_keys security keys * \param index GTK index * * \return < 0 failure * \return >= 0 success * */ -int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_gtk_keys_t *gtks, uint8_t index); +int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_keys_t *sec_keys, uint8_t index); /** * sec_prot_keys_gtk_insert_index_get gets index of GTK to be inserted * - * \param gtks GTK keys + * \param sec_keys security keys * * \return >= 0 GTK index * \return < 0 no GTK to be inserted * */ -int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_gtk_keys_t *gtks); +int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_keys_t *sec_keys); /** * sec_prot_keys_gtk_insert_index_clear clears the index of GTK to be inserted * - * \param gtks GTK keys + * \param sec_keys security keys * */ -void sec_prot_keys_gtk_insert_index_clear(sec_prot_gtk_keys_t *gtks); +void sec_prot_keys_gtk_insert_index_clear(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_gtkl_from_gtk_insert_index_set sets inserted GTK as live to GTK liveness storage + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_gtkl_from_gtk_insert_index_set(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_gtk_insert_index_from_gtkl_get gets inserted GTK based on GTK liveness storage + * + * \param sec_keys security keys + * + * \return >= 0 GTK index + * \return < 0 no GTK to be inserted + * + */ +int8_t sec_prot_keys_gtk_insert_index_from_gtkl_get(sec_prot_keys_t *sec_keys); /** * sec_prot_keys_get_gtk_to_insert gets GTK that is marked to be inserted * - * \param gtks GTK keys + * \param sec_keys security keys * \param index index of the returned GTK * * \return GTK or NULL * */ -uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_gtk_keys_t *gtks, uint8_t *index); +uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_keys_t *sec_keys, uint8_t *index); /** * sec_prot_keys_gtk_set sets Group Transient Key @@ -312,12 +479,25 @@ uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_gtk_keys_t *gtks, uint8_t *ind * \param gtks GTK keys * \param index index * \param gtk gtk value + * \param lifetime GTK lifetime * * \return < 0 failure * \return >= 0 success * */ -int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk); +int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk, uint32_t lifetime); + +/** + * sec_prot_keys_gtk_clear clears Group Transient Key + * + * \param gtks GTK keys + * \param index index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t sec_prot_keys_gtk_clear(sec_prot_gtk_keys_t *gtks, uint8_t index); /** * sec_prot_keys_gtk_is_set checks if Group Transient Key is set @@ -353,14 +533,16 @@ uint8_t *sec_prot_keys_gtk_get(sec_prot_gtk_keys_t *gtks, uint8_t index); uint32_t sec_prot_keys_gtk_lifetime_get(sec_prot_gtk_keys_t *gtks, uint8_t index); /** - * sec_prot_keys_gtk_lifetime_set sets GTK lifetime + * sec_prot_keys_gtk_lifetime_decrement decrements GTK lifetime * * \param gtks GTK keys - * \param index index - * \param lifetime GTK lifetime + * \param index index for GTK + * \param seconds elapsed seconds + * + * \return new GTK lifetime * */ -void sec_prot_keys_gtk_lifetime_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint32_t lifetime); +uint32_t sec_prot_keys_gtk_lifetime_decrement(sec_prot_gtk_keys_t *gtks, uint8_t index, uint16_t seconds); /** * sec_prot_keys_gtks_are_updated returns GTKs have been updated flag @@ -390,4 +572,163 @@ void sec_prot_keys_gtks_updated_set(sec_prot_gtk_keys_t *gtks); */ void sec_prot_keys_gtks_updated_reset(sec_prot_gtk_keys_t *gtks); +/** + * sec_prot_keys_gtk_status_fresh_set sets GTK as status fresh (if GTK status new) + * + * \param gtks GTK keys + * \param index index + * + */ +void sec_prot_keys_gtk_status_fresh_set(sec_prot_gtk_keys_t *gtks, uint8_t index); + +/** + * sec_prot_keys_gtk_status_fresh_set sets all GTK statuses fresh (if GTK status new) + * + * \param gtks GTK keys + * + */ +void sec_prot_keys_gtk_status_all_fresh_set(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_status_active_set sets fresh GTK to active, and currently active as old + * + * \param gtks GTK keys + * \param index index + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t sec_prot_keys_gtk_status_active_set(sec_prot_gtk_keys_t *gtks, uint8_t index); + +/** + * sec_prot_keys_gtk_status_active_get gets active GTK + * + * \param gtks GTK keys + * + * \return >= 0 GTK index + * \return < 0 no active GTK + * + */ +int8_t sec_prot_keys_gtk_status_active_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_status_is_live checks whether GTK is active + * + * \param gtks GTK keys + * \param index index + * + * \return TRUE active, FALSE non active + * + */ +bool sec_prot_keys_gtk_status_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index); + +/** + * sec_prot_keys_gtks_hash_generate generate GTK hash based on all GTKs + * + * \param gtks GTK keys + * \param gtk_hash GTK hash + * + */ +void sec_prot_keys_gtks_hash_generate(sec_prot_gtk_keys_t *gtks, uint8_t *gtk_hash); + +/** + * sec_prot_keys_gtks_hash_update update GTKs based on GTK hash + * + * \param gtks GTK keys + * \param gtk_hash GTK hash + * + * \return GTK mismatch type or no mismatch + * + */ +gtk_mismatch_e sec_prot_keys_gtks_hash_update(sec_prot_gtk_keys_t *gtks, uint8_t *gtkhash); + +/** + * sec_prot_keys_gtk_hash_empty checks if GTK hash field is empty + * + * \param gtk_hash GTK hash + * + * \return TRUE GTK hash is empty, FALSE GTK hash is set + * + */ +bool sec_prot_keys_gtk_hash_empty(uint8_t *gtkhash); + +/** + * sec_prot_keys_gtk_install_order_last_get gets install order of the last GTK to be installd + * + * \param gtks GTK keys + * + * \return install order + * + */ +int8_t sec_prot_keys_gtk_install_order_last_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_order_last_get gets GTK that is to be installed last + * + * \param gtks GTK keys + * + * \return GTK index + * + */ +int8_t sec_prot_keys_gtk_install_order_last_index_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_order_last_lifetime_get gets lifetime of the GTK that is to be installed last + * + * \param gtks GTK keys + * + * \return lifetime + * + */ +uint32_t sec_prot_keys_gtk_install_order_last_lifetime_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_order_first_index_get gets index of the GTK that is to be installed first + * + * \param gtks GTK keys + * + * \return GTK index + * + */ +int8_t sec_prot_keys_gtk_install_order_first_index_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_order_second_index_get gets index of the GTK that is to be installed second first + * + * \param gtks GTK keys + * + * \return GTK index + * + */ +int8_t sec_prot_keys_gtk_install_order_second_index_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_order_update updates install order (if GTKs are removed set the order back) + * + * \param gtks GTK keys + * + */ +void sec_prot_keys_gtk_install_order_update(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_install_index_get gets a free index for GTK to be installed + * + * \param gtks GTK keys + * + * \return GTK index + * + */ +int8_t sec_prot_keys_gtk_install_index_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtk_count counts GTK keys + * + * \param gtks GTK keys + * + * \return count of keys, 0 for no keys + * + */ +uint8_t sec_prot_keys_gtk_count(sec_prot_gtk_keys_t *gtks); + #endif /* SEC_PROT_KEYS_H_ */ diff --git a/source/Security/protocols/sec_prot_lib.c b/source/Security/protocols/sec_prot_lib.c index f5c42d65b7..ff7d126a25 100644 --- a/source/Security/protocols/sec_prot_lib.c +++ b/source/Security/protocols/sec_prot_lib.c @@ -38,6 +38,7 @@ #include "Service_Libs/ieee_802_11/ieee_802_11.h" #include "Service_Libs/hmac/hmac_sha1.h" #include "Service_Libs/nist_aes_kw/nist_aes_kw.h" +#include "mbedtls/sha256.h" #ifdef HAVE_WS @@ -53,7 +54,7 @@ void sec_prot_init(sec_prot_common_t *data) void sec_prot_timer_timeout_handle(sec_prot_t *prot, sec_prot_common_t *data, const trickle_params_t *trickle_params, uint16_t ticks) { - if (data->trickle_running) { + if (data->trickle_running && trickle_params) { bool running = trickle_running(&data->trickle_timer, trickle_params); // Checks for trickle timer expiration */ @@ -108,9 +109,16 @@ void sec_prot_state_set(sec_prot_t *prot, sec_prot_common_t *data, uint8_t state return; case SEC_STATE_FINISHED: - // Wait for timeout + // If not already on finished state + if (data->state != SEC_STATE_FINISHED) { + // Wait for timeout + data->ticks = SEC_FINISHED_TIMEOUT; + } data->trickle_running = false; - data->ticks = SEC_FINISHED_TIMEOUT; + + // Disables receiving of messages when state machine sets SEC_STATE_FINISHED + prot->receive_disable(prot); + // Clear result sec_prot_result_set(data, SEC_RESULT_OK); break; @@ -159,22 +167,10 @@ bool sec_prot_result_ok_check(sec_prot_common_t *data) return false; } -/* - * IEEE 802.11 advises using sequential nonces, but should this be - * randlib? - */ void sec_prot_lib_nonce_generate(uint8_t *nonce) { - // For now, use randlib + // Use randlib randLIB_get_n_bytes_random(nonce, EAPOL_KEY_NONCE_LEN); - -#if 0 - for (int i = 31; i >= 0; i--) { - if (++nonce[i] != 0) { - break; - } - } -#endif } /* @@ -285,10 +281,11 @@ int8_t sec_prot_lib_pmkid_calc(const uint8_t *pmk, const uint8_t *auth_eui64, co ptr += EUI64_LEN; memcpy(ptr, supp_eui64, EUI64_LEN); - if (hmac_sha1_calc(pmk, PMK_LEN, data, data_len, pmkid) < 0) { + if (hmac_sha1_calc(pmk, PMK_LEN, data, data_len, pmkid, PMKID_LEN) < 0) { return -1; } + tr_info("PMKID %s EUI-64 %s %s", trace_array(pmkid, PMKID_LEN), trace_array(auth_eui64, 8), trace_array(supp_eui64, 8)); return 0; } @@ -306,10 +303,11 @@ int8_t sec_prot_lib_ptkid_calc(const uint8_t *ptk, const uint8_t *auth_eui64, co ptr += EUI64_LEN; memcpy(ptr, supp_eui64, EUI64_LEN); - if (hmac_sha1_calc(ptk, PTK_LEN, data, data_len, ptkid) < 0) { + if (hmac_sha1_calc(ptk, PTK_LEN, data, data_len, ptkid, PTKID_LEN) < 0) { return -1; } + tr_info("PTKID %s EUI-64 %s %s", trace_array(ptkid, PTKID_LEN), trace_array(auth_eui64, 8), trace_array(supp_eui64, 8)); return 0; } @@ -337,7 +335,7 @@ uint8_t *sec_prot_lib_message_build(uint8_t *ptk, uint8_t *kde, uint16_t kde_len if (eapol_pdu->msg.key.key_information.key_mic) { uint8_t mic[EAPOL_KEY_MIC_LEN]; - if (hmac_sha1_calc(ptk, KCK_LEN, eapol_pdu_frame + header_size, eapol_pdu_size, mic) < 0) { + if (hmac_sha1_calc(ptk, KCK_LEN, eapol_pdu_frame + header_size, eapol_pdu_size, mic, EAPOL_KEY_MIC_LEN) < 0) { ns_dyn_mem_free(eapol_pdu_frame); return NULL; } @@ -362,6 +360,7 @@ uint8_t *sec_prot_lib_message_handle(uint8_t *ptk, uint16_t *kde_len, eapol_pdu_ if (eapol_pdu->msg.key.key_information.encrypted_key_data) { size_t output_len = eapol_pdu->msg.key.key_data_length; if (nist_aes_key_wrap(0, &ptk[KEK_INDEX], 128, key_data, key_data_len, kde, &output_len) < 0 || output_len != (size_t) key_data_len - 8) { + tr_error("Decrypt failed"); ns_dyn_mem_free(kde); return NULL; } @@ -373,42 +372,40 @@ uint8_t *sec_prot_lib_message_handle(uint8_t *ptk, uint16_t *kde_len, eapol_pdu_ return kde; } -int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_gtk_keys_t *gtks) +int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_keys_t *sec_keys) { - // If a valid new GTK value present, insert it - int8_t gtk_set_index = -1; + int8_t gtk_index = -1; uint8_t key_id; uint8_t gtk[GTK_LEN]; if (kde_gtk_read(kde, kde_len, &key_id, gtk) >= 0) { - // A new GTK value - if (sec_prot_keys_gtk_set(gtks, key_id, gtk) >= 0) { - gtk_set_index = (int8_t) key_id; // Insert + uint32_t lifetime = 0; + if (kde_lifetime_read(kde, kde_len, &lifetime) >= 0) { + } - uint32_t lifetime; - if (kde_lifetime_read(kde, kde_len, &lifetime) >= 0) { - sec_prot_keys_gtk_lifetime_set(gtks, key_id, lifetime); + // A new GTK value + if (sec_prot_keys_gtk_set(sec_keys->gtks, key_id, gtk, lifetime) >= 0) { + gtk_index = (int8_t) key_id; // Insert } } uint8_t gtkl; if (kde_gtkl_read(kde, kde_len, >kl) >= 0) { - sec_prot_keys_gtkl_set(gtks, gtkl); + sec_prot_keys_gtkl_set(sec_keys, gtkl); } else { + tr_error("No GTKL"); return -1; } // Sanity checks - if (gtk_set_index >= 0) { - if (!sec_prot_keys_gtk_is_live(gtks, gtk_set_index)) { - gtk_set_index = -1; + if (gtk_index >= 0) { + if (!sec_prot_keys_gtkl_gtk_is_live(sec_keys, gtk_index)) { + tr_error("mismatch between GTK and GTKL"); } } - if (gtk_set_index >= 0) { - sec_prot_keys_gtk_insert_index_set(gtks, gtk_set_index); - } + tr_info("GTK recv index %i lifetime %"PRIu32"", gtk_index, sec_prot_keys_gtk_lifetime_get(sec_keys->gtks, gtk_index)); return 0; } @@ -421,45 +418,36 @@ int8_t sec_prot_lib_mic_validate(uint8_t *ptk, uint8_t *mic, uint8_t *pdu, uint8 eapol_write_key_packet_mic(pdu, 0); uint8_t calc_mic[EAPOL_KEY_MIC_LEN]; - if (hmac_sha1_calc(ptk, EAPOL_KEY_MIC_LEN, pdu, pdu_size, calc_mic) < 0) { + if (hmac_sha1_calc(ptk, EAPOL_KEY_MIC_LEN, pdu, pdu_size, calc_mic, EAPOL_KEY_MIC_LEN) < 0) { + tr_error("MIC invalid"); return -1; } if (memcmp(recv_mic, calc_mic, EAPOL_KEY_MIC_LEN) != 0) { + tr_error("MIC invalid"); return -1; } return 0; } -uint8_t sec_prot_lib_key_mask_get(eapol_pdu_t *eapol_pdu) -{ - uint8_t key_mask = 0; - - if (eapol_pdu->msg.key.key_information.install) { - key_mask |= KEY_INFO_INSTALL; - } - if (eapol_pdu->msg.key.key_information.key_ack) { - key_mask |= KEY_INFO_KEY_ACK; - } - if (eapol_pdu->msg.key.key_information.key_mic) { - key_mask |= KEY_INFO_KEY_MIC; - } - if (eapol_pdu->msg.key.key_information.secured_key_frame) { - key_mask |= KEY_INFO_SECURED_KEY_FRAME; - } - - return key_mask; -} - int8_t sec_prot_lib_pmkid_generate(sec_prot_t *prot, uint8_t *pmkid, bool is_auth) { - uint8_t local_eui64[8]; - uint8_t remote_eui64[8]; - prot->addr_get(prot, local_eui64, remote_eui64); uint8_t *pmk = sec_prot_keys_pmk_get(prot->sec_keys); if (!pmk) { return -1; } + uint8_t local_eui64[8]; + uint8_t remote_eui64[8]; + // Tries to get the EUI-64 that is validated by PTK procedure or bound to supplicant entry + uint8_t *remote_eui64_ptr = sec_prot_keys_ptk_eui_64_get(prot->sec_keys); + if (remote_eui64_ptr) { + memcpy(remote_eui64, remote_eui64_ptr, 8); + prot->addr_get(prot, local_eui64, NULL); + } else { + // If validated EUI-64 is not present, use the remote EUI-64 + prot->addr_get(prot, local_eui64, remote_eui64); + } + if (is_auth) { return sec_prot_lib_pmkid_calc(pmk, local_eui64, remote_eui64, pmkid); } else { @@ -470,12 +458,16 @@ int8_t sec_prot_lib_pmkid_generate(sec_prot_t *prot, uint8_t *pmkid, bool is_aut int8_t sec_prot_lib_ptkid_generate(sec_prot_t *prot, uint8_t *ptkid, bool is_auth) { uint8_t local_eui64[8]; - uint8_t remote_eui64[8]; - prot->addr_get(prot, local_eui64, remote_eui64); + prot->addr_get(prot, local_eui64, NULL); uint8_t *ptk = sec_prot_keys_pmk_get(prot->sec_keys); if (!ptk) { return -1; } + // Uses always the EUI-64 that is validated by PTK procedure or bound to supplicant entry + uint8_t *remote_eui64 = sec_prot_keys_ptk_eui_64_get(prot->sec_keys); + if (!remote_eui64) { + return -1; + } if (is_auth) { return sec_prot_lib_ptkid_calc(ptk, local_eui64, remote_eui64, ptkid); @@ -484,5 +476,46 @@ int8_t sec_prot_lib_ptkid_generate(sec_prot_t *prot, uint8_t *ptkid, bool is_aut } } +int8_t sec_prot_lib_gtkhash_generate(uint8_t *gtk, uint8_t *gtk_hash) +{ + int8_t ret_val = 0; + + mbedtls_sha256_context ctx; + + mbedtls_sha256_init(&ctx); + + if (mbedtls_sha256_starts_ret(&ctx, 0) != 0) { + ret_val = -1; + goto error; + } + + if (mbedtls_sha256_update_ret(&ctx, gtk, 16) != 0) { + ret_val = -1; + goto error; + } + + uint8_t output[32]; + + if (mbedtls_sha256_finish_ret(&ctx, output) != 0) { + ret_val = -1; + goto error; + } + + memcpy(gtk_hash, &output[24], 8); + +error: + mbedtls_sha256_free(&ctx); + + return ret_val; +} + +uint8_t *sec_prot_remote_eui_64_addr_get(sec_prot_t *prot) +{ + if (prot->sec_keys && prot->sec_keys->ptk_eui_64_set) { + return prot->sec_keys->ptk_eui_64; + } else { + return NULL; + } +} #endif /* HAVE_WS */ diff --git a/source/Security/protocols/sec_prot_lib.h b/source/Security/protocols/sec_prot_lib.h index ebf32bf809..4786e49cd4 100644 --- a/source/Security/protocols/sec_prot_lib.h +++ b/source/Security/protocols/sec_prot_lib.h @@ -34,10 +34,6 @@ #define SEC_TOTAL_TIMEOUT 30 * 60 * 10 // 30 minutes #define SEC_FINISHED_TIMEOUT 5 * 10 // 5 seconds -#define KEY_INFO_INSTALL 0x01 -#define KEY_INFO_KEY_ACK 0x02 -#define KEY_INFO_KEY_MIC 0x04 -#define KEY_INFO_SECURED_KEY_FRAME 0x08 // Common data shared between security protocols needing general timers and state machines typedef struct { @@ -139,12 +135,12 @@ uint8_t *sec_prot_lib_message_handle(uint8_t *ptk, uint16_t *kde_len, eapol_pdu_ * * \param kde KDEs * \param kde_len length of the KDEs - * \param gtks GTKs + * \param sec_keys security keys * * \return < 0 failure * \return >= 0 success */ -int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_gtk_keys_t *gtks); +int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_keys_t *sec_keys); /** * sec_prot_lib_mic_validate validates MIC @@ -159,15 +155,6 @@ int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_gtk_keys_t */ int8_t sec_prot_lib_mic_validate(uint8_t *ptk, uint8_t *mic, uint8_t *pdu, uint8_t pdu_size); -/** - * sec_prot_lib_key_mask_get gets masked EAPOL-Key message bits - * - * \param eapol_pdu EAPOL PDU - * - * \return mask - */ -uint8_t sec_prot_lib_key_mask_get(eapol_pdu_t *eapol_pdu); - /** * sec_prot_lib_pmkid_generate generate PMK ID from PMK * @@ -192,6 +179,26 @@ int8_t sec_prot_lib_pmkid_generate(sec_prot_t *prot, uint8_t *pmkid, bool is_aut */ int8_t sec_prot_lib_ptkid_generate(sec_prot_t *prot, uint8_t *ptkid, bool is_auth); +/** + * sec_prot_lib_gtkhash_generate generate GTK hash from GTK + * + * \param gtk GTK + * \param gtk_hash GTK hash + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_gtkhash_generate(uint8_t *gtk, uint8_t *gtk_hash); + +/** + * sec_prot_remote_eui_64_addr_get get remote EUI-64 (trusted EUI-64 from security keys) + * + * \param prot security protocol + * + * \return pointer to EUI-64 or NULL + */ +uint8_t *sec_prot_remote_eui_64_addr_get(sec_prot_t *prot); + /** * sec_prot_init initiates common data * diff --git a/source/Security/protocols/tls_sec_prot/tls_sec_prot.c b/source/Security/protocols/tls_sec_prot/tls_sec_prot.c index b4cac8675c..ce4ed39ca6 100644 --- a/source/Security/protocols/tls_sec_prot/tls_sec_prot.c +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot.c @@ -54,6 +54,8 @@ typedef enum { TLS_STATE_FINISHED = SEC_STATE_FINISHED } eap_tls_sec_prot_state_e; +typedef struct tls_sec_prot_lib_int_s tls_sec_prot_lib_int_t; + typedef struct { sec_prot_common_t common; /**< Common data */ uint8_t new_pmk[PMK_LEN]; /**< New Pair Wise Master Key */ @@ -61,12 +63,20 @@ typedef struct { tls_data_t tls_recv; /**< TLS receive buffer */ uint32_t int_timer; /**< TLS intermediate timer timeout */ uint32_t fin_timer; /**< TLS final timer timeout */ - bool timer_running; /**< TLS timer running */ - bool finished; /**< TLS finished */ - bool calculating; /**< TLS is calculating */ - uint8_t tls_sec_inst; /**< TLS security library storage, SHALL BE THE LAST FIELD */ + bool fin_timer_timeout; /**< TLS final timer has timeouted */ + bool timer_running : 1; /**< TLS timer running */ + bool finished : 1; /**< TLS finished */ + bool calculating : 1; /**< TLS is calculating */ + bool queued : 1; /**< TLS is queued */ + bool library_init : 1; /**< TLS library has been initialized */ + tls_sec_prot_lib_int_t *tls_sec_inst; /**< TLS security library storage, SHALL BE THE LAST FIELD */ } tls_sec_prot_int_t; +typedef struct { + ns_list_link_t link; /**< Link */ + sec_prot_t *prot; /**< Protocol instance */ +} tls_sec_prot_queue_t; + static uint16_t tls_sec_prot_size(void); static int8_t client_tls_sec_prot_init(sec_prot_t *prot); static int8_t server_tls_sec_prot_init(sec_prot_t *prot); @@ -90,8 +100,14 @@ static int8_t tls_sec_prot_tls_get_timer(void *handle); static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_server); +static bool tls_sec_prot_queue_check(sec_prot_t *prot); +static bool tls_sec_prot_queue_process(sec_prot_t *prot); +static void tls_sec_prot_queue_remove(sec_prot_t *prot); + #define tls_sec_prot_get(prot) (tls_sec_prot_int_t *) &prot->data +static NS_LIST_DEFINE(tls_sec_prot_queue, tls_sec_prot_queue_t, link); + int8_t client_tls_sec_prot_register(kmp_service_t *service) { if (!service) { @@ -145,8 +161,11 @@ static int8_t client_tls_sec_prot_init(sec_prot_t *prot) eap_tls_sec_prot_lib_message_init(&data->tls_send); data->int_timer = 0; data->fin_timer = 0; + data->fin_timer_timeout = false; data->timer_running = false; data->calculating = false; + data->queued = false; + data->library_init = false; return 0; } @@ -172,8 +191,11 @@ static int8_t server_tls_sec_prot_init(sec_prot_t *prot) eap_tls_sec_prot_lib_message_init(&data->tls_send); data->int_timer = 0; data->fin_timer = 0; + data->fin_timer_timeout = false; data->timer_running = false; data->calculating = false; + data->queued = false; + data->library_init = false; return 0; } @@ -182,6 +204,11 @@ static void tls_sec_prot_delete(sec_prot_t *prot) tls_sec_prot_int_t *data = tls_sec_prot_get(prot); eap_tls_sec_prot_lib_message_free(&data->tls_send); eap_tls_sec_prot_lib_message_free(&data->tls_recv); + if (data->library_init) { + tr_info("TLS: free library"); + tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst); + } + tls_sec_prot_queue_remove(prot); } static void tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) @@ -237,13 +264,22 @@ static void tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) if (data->fin_timer > ticks) { data->fin_timer -= ticks; } else { - data->fin_timer = 0; - prot->state_machine_call(prot); + if (data->fin_timer > 0) { + data->fin_timer_timeout = true; + data->fin_timer = 0; + } } } - if (data->calculating) { - prot->state_machine(prot); + /* Checks if TLS sessions queue is enabled, and if queue is enabled whether the + session is first in the queue i.e. allowed to process */ + if (tls_sec_prot_queue_process(prot)) { + if (data->fin_timer_timeout) { + data->fin_timer_timeout = false; + prot->state_machine(prot); + } else if (data->calculating || data->queued) { + prot->state_machine(prot); + } } sec_prot_timer_timeout_handle(prot, &data->common, NULL, ticks); @@ -261,7 +297,7 @@ static void client_tls_sec_prot_state_machine(sec_prot_t *prot) // Wait KMP-CREATE.request case TLS_STATE_CREATE_REQ: - tr_debug("TLS start"); + tr_debug("TLS: start"); prot->timer_start(prot); @@ -300,6 +336,7 @@ static void client_tls_sec_prot_state_machine(sec_prot_t *prot) if (result != TLS_SEC_PROT_LIB_CONTINUE) { if (result == TLS_SEC_PROT_LIB_ERROR) { + tr_error("TLS: error"); sec_prot_result_set(&data->common, SEC_RESULT_ERROR); } sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); @@ -307,7 +344,7 @@ static void client_tls_sec_prot_state_machine(sec_prot_t *prot) break; case TLS_STATE_FINISH: - tr_debug("TLS finish"); + tr_debug("TLS: finish"); data->calculating = false; @@ -320,9 +357,15 @@ static void client_tls_sec_prot_state_machine(sec_prot_t *prot) sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED); tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst); + data->library_init = false; break; case TLS_STATE_FINISHED: + tr_debug("TLS: finished, free %s", data->library_init ? "T" : "F"); + if (data->library_init) { + tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst); + data->library_init = false; + } prot->timer_stop(prot); prot->finished(prot); break; @@ -336,6 +379,7 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) { tls_sec_prot_int_t *data = tls_sec_prot_get(prot); int8_t result; + bool client_hello = false; switch (sec_prot_state_get(&data->common)) { case TLS_STATE_INIT: @@ -345,9 +389,10 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) // Wait EAP request, Identity (starts handshake on supplicant) case TLS_STATE_CLIENT_HELLO: - tr_debug("TLS start"); + tr_debug("TLS: start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); prot->timer_start(prot); + client_hello = true; sec_prot_state_set(prot, &data->common, TLS_STATE_CREATE_RESP); @@ -377,6 +422,14 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) break; case TLS_STATE_PROCESS: + // If not client hello, reserves slot on TLS queue + if (!client_hello && !tls_sec_prot_queue_check(prot)) { + data->queued = true; + return; + } else { + data->queued = false; + } + result = tls_sec_prot_lib_process((tls_security_t *) &data->tls_sec_inst); if (result == TLS_SEC_PROT_LIB_CALCULATING) { @@ -394,6 +447,7 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) if (result != TLS_SEC_PROT_LIB_CONTINUE) { if (result == TLS_SEC_PROT_LIB_ERROR) { + tr_error("TLS: error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); sec_prot_result_set(&data->common, SEC_RESULT_ERROR); } sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); @@ -401,7 +455,7 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) break; case TLS_STATE_FINISH: - tr_debug("TLS finish"); + tr_debug("TLS: finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); data->calculating = false; @@ -413,14 +467,22 @@ static void server_tls_sec_prot_state_machine(sec_prot_t *prot) prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED); + tls_sec_prot_queue_remove(prot); tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst); + data->library_init = false; break; - case TLS_STATE_FINISHED: + case TLS_STATE_FINISHED: { + uint8_t *remote_eui_64 = sec_prot_remote_eui_64_addr_get(prot); + tr_debug("TLS: finished, eui-64: %s free %s", remote_eui_64 ? trace_array(sec_prot_remote_eui_64_addr_get(prot), 8) : "not set", data->library_init ? "T" : "F"); + if (data->library_init) { + tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst); + data->library_init = false; + } prot->timer_stop(prot); prot->finished(prot); break; - + } default: break; } @@ -533,7 +595,10 @@ static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_s { tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + // Must be free if library initialize is done + data->library_init = true; if (tls_sec_prot_lib_init((tls_security_t *)&data->tls_sec_inst) < 0) { + tr_error("TLS: library init fail"); return -1; } @@ -542,10 +607,79 @@ static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_s tls_sec_prot_tls_set_timer, tls_sec_prot_tls_get_timer); if (tls_sec_prot_lib_connect((tls_security_t *)&data->tls_sec_inst, is_server, prot->sec_keys->certs) < 0) { + tr_error("TLS: library connect fail"); return -1; } return 0; } +static bool tls_sec_prot_queue_check(sec_prot_t *prot) +{ + bool queue_add = true; + bool queue_continue = false; + uint8_t entry_index = 0; + + // Checks if TLS queue is empty or this instance is the first entry + if (ns_list_is_empty(&tls_sec_prot_queue)) { + queue_continue = true; + } else { + ns_list_foreach(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) { + if (entry->prot == prot) { + queue_add = false; + if (entry_index < 3) { + queue_continue = true; + break; + } else { + queue_continue = false; + } + } + entry_index++; + } + } + + // Adds entry to queue if not there already + if (queue_add) { + tr_debug("TLS QUEUE add index: %i, eui-64: %s", entry_index, trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + tls_sec_prot_queue_t *entry = ns_dyn_mem_temporary_alloc(sizeof(tls_sec_prot_queue_t)); + if (entry) { + entry->prot = prot; + ns_list_add_to_end(&tls_sec_prot_queue, entry); + } + } + + return queue_continue; +} + +static bool tls_sec_prot_queue_process(sec_prot_t *prot) +{ + if (ns_list_is_empty(&tls_sec_prot_queue)) { + return true; + } + + uint8_t entry_index = 0; + ns_list_foreach(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) { + if (entry->prot == prot) { + return true; + } + if (entry_index > 2) { + return false; + } + entry_index++; + } + + return false; +} + +static void tls_sec_prot_queue_remove(sec_prot_t *prot) +{ + ns_list_foreach_safe(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) { + if (entry->prot == prot) { + ns_list_remove(&tls_sec_prot_queue, entry); + ns_dyn_mem_free(entry); + tr_debug("TLS QUEUE remove%s, eui-64: %s", ns_list_is_empty(&tls_sec_prot_queue) ? " last" : "", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8)); + } + } +} + #endif /* HAVE_WS */ diff --git a/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c b/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c index 817862390e..56f02ab3fe 100644 --- a/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c @@ -67,13 +67,15 @@ struct tls_security_s { mbedtls_entropy_context entropy; /**< mbed TLS entropy context */ mbedtls_x509_crt cacert; /**< CA certificate(s) */ - mbedtls_x509_crl crl; /**< Certificate Revocation List */ + mbedtls_x509_crl *crl; /**< Certificate Revocation List */ mbedtls_x509_crt owncert; /**< Own certificate(s) */ mbedtls_pk_context pkey; /**< Private key for own certificate */ uint8_t client_random[32]; /**< Client random (from Client Hello) */ uint8_t server_random[32]; /**< Server random (from Server Hello) */ + uint8_t step; /**< Random extract step */ + void *handle; /**< Handle provided in callbacks (defined by library user) */ tls_sec_prot_lib_send *send; /**< Send callback */ tls_sec_prot_lib_receive *receive; /**< Receive callback */ @@ -94,27 +96,43 @@ static void tls_sec_prot_lib_random_extract(tls_security_t *sec, const uint8_t * static void tls_sec_prot_lib_debug(void *ctx, int level, const char *file, int line, const char *string); #endif +#ifdef MBEDTLS_PLATFORM_MEMORY +static void *tls_sec_prot_lib_mem_calloc(size_t count, size_t size); +static void tls_sec_prot_lib_mem_free(void *ptr); +#endif + int8_t tls_sec_prot_lib_init(tls_security_t *sec) { const char *pers = "ws_tls"; +#ifdef MBEDTLS_PLATFORM_MEMORY + // Disable for now + //mbedtls_platform_set_calloc_free(tls_sec_prot_lib_mem_calloc, tls_sec_prot_lib_mem_free); +#endif + + mbedtls_ssl_init(&sec->ssl); mbedtls_ssl_config_init(&sec->conf); mbedtls_ctr_drbg_init(&sec->ctr_drbg); mbedtls_entropy_init(&sec->entropy); mbedtls_x509_crt_init(&sec->cacert); - mbedtls_x509_crl_init(&sec->crl); + mbedtls_x509_crt_init(&sec->owncert); mbedtls_pk_init(&sec->pkey); + sec->crl = NULL; + sec->step = 0; + if (mbedtls_entropy_add_source(&sec->entropy, tls_sec_lib_entropy_poll, NULL, 128, MBEDTLS_ENTROPY_SOURCE_WEAK) < 0) { + tr_error("Entropy add fail"); return -1; } if ((mbedtls_ctr_drbg_seed(&sec->ctr_drbg, mbedtls_entropy_func, &sec->entropy, (const unsigned char *) pers, strlen(pers))) != 0) { + tr_error("drbg seed fail"); return -1; } @@ -146,7 +164,10 @@ void tls_sec_prot_lib_set_cb_register(tls_security_t *sec, void *handle, void tls_sec_prot_lib_free(tls_security_t *sec) { mbedtls_x509_crt_free(&sec->cacert); - mbedtls_x509_crl_free(&sec->crl); + if (sec->crl) { + mbedtls_x509_crl_free(sec->crl); + ns_dyn_mem_free(sec->crl); + } mbedtls_x509_crt_free(&sec->owncert); mbedtls_pk_free(&sec->pkey); mbedtls_entropy_free(&sec->entropy); @@ -158,6 +179,7 @@ void tls_sec_prot_lib_free(tls_security_t *sec) static int tls_sec_prot_lib_configure_certificates(tls_security_t *sec, const sec_prot_certs_t *certs) { if (!certs->own_cert_chain.cert[0]) { + tr_error("no own cert"); return -1; } @@ -168,11 +190,13 @@ static int tls_sec_prot_lib_configure_certificates(tls_security_t *sec, const se uint8_t *cert = sec_prot_certs_cert_get(&certs->own_cert_chain, index, &cert_len); if (!cert) { if (index == 0) { + tr_error("No own cert"); return -1; } break; } if (mbedtls_x509_crt_parse(&sec->owncert, cert, cert_len) < 0) { + tr_error("Own cert parse eror"); return -1; } index++; @@ -182,15 +206,18 @@ static int tls_sec_prot_lib_configure_certificates(tls_security_t *sec, const se uint8_t key_len; uint8_t *key = sec_prot_certs_priv_key_get(&certs->own_cert_chain, &key_len); if (!key) { + tr_error("No private key"); return -1; } if (mbedtls_pk_parse_key(&sec->pkey, key, key_len, NULL, 0) < 0) { + tr_error("Private key parse error"); return -1; } // Configure own certificate chain and private key if (mbedtls_ssl_conf_own_cert(&sec->conf, &sec->owncert, &sec->pkey) != 0) { + tr_error("Own cert and private key conf error"); return -1; } @@ -202,11 +229,13 @@ static int tls_sec_prot_lib_configure_certificates(tls_security_t *sec, const se uint8_t *cert = sec_prot_certs_cert_get(entry, index, &cert_len); if (!cert) { if (index == 0) { + tr_error("No trusted cert"); return -1; } break; } if (mbedtls_x509_crt_parse(&sec->cacert, cert, cert_len) < 0) { + tr_error("Trusted cert parse error"); return -1; } index++; @@ -216,17 +245,27 @@ static int tls_sec_prot_lib_configure_certificates(tls_security_t *sec, const se // Parse certificate revocation lists ns_list_foreach(cert_revocat_list_entry_t, entry, &certs->cert_revocat_lists) { uint16_t crl_len; - uint8_t *crl = sec_prot_certs_revocat_list_get(entry, &crl_len); + const uint8_t *crl = sec_prot_certs_revocat_list_get(entry, &crl_len); if (!crl) { break; } - if (mbedtls_x509_crl_parse(&sec->crl, crl, crl_len) < 0) { + if (!sec->crl) { + sec->crl = ns_dyn_mem_temporary_alloc(sizeof(mbedtls_x509_crl)); + if (!sec->crl) { + tr_error("No memory for CRL"); + return -1; + } + mbedtls_x509_crl_init(sec->crl); + } + + if (mbedtls_x509_crl_parse(sec->crl, crl, crl_len) < 0) { + tr_error("CRL parse error"); return -1; } } // Configure trusted certificates and certificate revocation lists - mbedtls_ssl_conf_ca_chain(&sec->conf, &sec->cacert, &sec->crl); + mbedtls_ssl_conf_ca_chain(&sec->conf, &sec->cacert, sec->crl); // Certificate verify required on both client and server mbedtls_ssl_conf_authmode(&sec->conf, MBEDTLS_SSL_VERIFY_REQUIRED); @@ -246,6 +285,7 @@ int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_p } if ((mbedtls_ssl_config_defaults(&sec->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, 0)) != 0) { + tr_error("config defaults fail"); return -1; } @@ -258,6 +298,7 @@ int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_p #endif if ((mbedtls_ssl_setup(&sec->ssl, &sec->conf)) != 0) { + tr_error("ssl setup fail"); return -1; } @@ -267,11 +308,10 @@ int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_p // Configure certificates, keys and certificate revocation list if (tls_sec_prot_lib_configure_certificates(sec, certs) != 0) { - tr_debug("security credential configure failed"); + tr_error("cert conf fail"); return -1; } - // Configure ciphersuites static const int sec_suites[] = { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, @@ -292,6 +332,13 @@ int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_p mbedtls_ssl_conf_min_version(&sec->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3); mbedtls_ssl_conf_max_version(&sec->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3); +#ifdef MBEDTLS_ECP_RESTARTABLE + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + // Temporary to enable non blocking ECC */ + sec->ssl.handshake->ecrs_enabled = 1; + } +#endif + return 0; } @@ -310,7 +357,7 @@ int8_t tls_sec_prot_lib_process(tls_security_t *sec) while (ret != MBEDTLS_ERR_SSL_WANT_READ) { ret = mbedtls_ssl_handshake_step(&sec->ssl); -#ifdef MBEDTLS_ECP_RESTARTABLE +#if defined(MBEDTLS_ECP_RESTARTABLE) && defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) if (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS /* || ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS */) { return TLS_SEC_PROT_LIB_CALCULATING; } @@ -365,10 +412,7 @@ static int tls_sec_prot_lib_ssl_recv(void *ctx, unsigned char *buf, size_t len) static void tls_sec_prot_lib_random_extract(tls_security_t *sec, const uint8_t *buf, uint16_t len) { - static uint8_t *random_ptr = NULL; - static uint8_t step = 0; - - if (step == 0) { + if (sec->step == 0) { if (*buf++ != 22 && len < 5) { return; } @@ -379,14 +423,15 @@ static void tls_sec_prot_lib_random_extract(tls_security_t *sec, const uint8_t * buf++; // length buf++; - step++; + sec->step++; if (len < 6) { return; } } - if (step == 1) { + if (sec->step == 1) { + uint8_t *random_ptr; if (*buf == 0x01) { // Client hello random_ptr = sec->client_random; } else if (*buf == 0x02) { // Server hello @@ -405,7 +450,7 @@ static void tls_sec_prot_lib_random_extract(tls_security_t *sec, const uint8_t * memcpy(random_ptr, buf, 32); - step = 0; + sec->step = 0; } } @@ -438,6 +483,7 @@ static int tls_sec_lib_entropy_poll(void *ctx, unsigned char *output, size_t len char *c = (char *)ns_dyn_mem_temporary_alloc(len); if (!c) { + tr_error("entropy alloca fail"); return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; } memset(c, 0, len); @@ -451,6 +497,24 @@ static int tls_sec_lib_entropy_poll(void *ctx, unsigned char *output, size_t len return (0); } +#ifdef MBEDTLS_PLATFORM_MEMORY +static void *tls_sec_prot_lib_mem_calloc(size_t count, size_t size) +{ + void *mem_ptr = ns_dyn_mem_temporary_alloc(count * size); + + if (mem_ptr) { + // Calloc should initialize with zero + memset(mem_ptr, 0, count * size); + } + return mem_ptr; +} + +static void tls_sec_prot_lib_mem_free(void *ptr) +{ + ns_dyn_mem_free(ptr); +} +#endif + #else /* WS_MBEDTLS_SECURITY_ENABLED */ int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_prot_certs_t *certs) diff --git a/source/Service_Libs/Trickle/trickle.c b/source/Service_Libs/Trickle/trickle.c index 4640b49371..5eaa8a1fa6 100644 --- a/source/Service_Libs/Trickle/trickle.c +++ b/source/Service_Libs/Trickle/trickle.c @@ -119,7 +119,7 @@ bool trickle_timer(trickle_t *t, const trickle_params_t *params, uint16_t ticks) t->I = params->Imax; } - if (t->e < UINT8_MAX) { + if (t->e < TRICKLE_EXPIRATIONS_INFINITE - 1) { t->e++; } trickle_begin_interval(t); diff --git a/source/Service_Libs/etx/etx.c b/source/Service_Libs/etx/etx.c index c7b3b63a32..802b85dccd 100644 --- a/source/Service_Libs/etx/etx.c +++ b/source/Service_Libs/etx/etx.c @@ -25,6 +25,7 @@ #include "Core/include/ns_address_internal.h" #include "MLE/mle.h" +#include "NWK_INTERFACE/Include/protocol_abstract.h" #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_stats.h" #include "Service_Libs/etx/etx.h" @@ -40,14 +41,33 @@ static uint16_t etx_current_calc(uint16_t etx, uint8_t accumulated_failures); static uint16_t etx_dbm_lqi_calc(uint8_t lqi, int8_t dbm); static void etx_value_change_callback_needed_check(uint16_t etx, uint16_t *stored_diff_etx, uint8_t accumulated_failures, uint8_t attribute_index); static void etx_accum_failures_callback_needed_check(etx_storage_t *entry, uint8_t attribute_index); +static void etx_cache_entry_init(uint8_t attribute_index); + +#if ETX_ACCELERATED_SAMPLE_COUNT == 0 || ETX_ACCELERATED_SAMPLE_COUNT > 6 +#error "ETX_ACCELERATED_SAMPLE_COUNT accepted values 1-6" +#endif + +#if ETX_ACCELERATED_INTERVAL == 0 +#error "ETX_ACCELERATED_INTERVAL can't be zero" +#endif + +#if ETX_ACCELERATED_INTERVAL >= ETX_ACCELERATED_SAMPLE_COUNT +#error "ETX_ACCELERATED_INTERVAL must be < ETX_ACCELERATED_SAMPLE_COUNT" +#endif + typedef struct { - uint16_t hysteresis; // 12 bit fraction - uint8_t accum_threshold; etx_value_change_handler_t *callback_ptr; etx_accum_failures_handler_t *accum_cb_ptr; etx_storage_t *etx_storage_list; + etx_sample_storage_t *etx_cache_storage_list; + uint32_t max_etx_update; + uint16_t hysteresis; // 12 bit fraction + uint8_t accum_threshold; + uint8_t etx_min_sampling_time; uint8_t ext_storage_list_size; + uint8_t min_sample_count; + bool cache_sample_requested; int8_t interface_id; } ext_info_t; @@ -57,10 +77,107 @@ static ext_info_t etx_info = { .callback_ptr = NULL, .accum_cb_ptr = NULL, .etx_storage_list = NULL, + .etx_cache_storage_list = NULL, .ext_storage_list_size = 0, + .min_sample_count = 0, + .max_etx_update = 0, + .cache_sample_requested = false, + .etx_min_sampling_time = 0, .interface_id = -1 }; +static void etx_calculation(etx_storage_t *entry, uint16_t attempts, uint8_t acks_rx, uint8_t attribute_index) +{ + if (etx_info.hysteresis && !entry->stored_diff_etx) { + entry->stored_diff_etx = entry->etx; + } + + + uint32_t etx = attempts << (12 - ETX_MOVING_AVERAGE_FRACTION); + + if (acks_rx > 1) { + etx /= acks_rx; + } + + if ((etx_info.max_etx_update) && etx > etx_info.max_etx_update) { + etx = etx_info.max_etx_update; + } + + //Add old etx 7/8 to new one + etx += entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); + + if (etx > 0xffff) { + etx = 0xffff; + } + + // If real ETX value has been received do not update based on LQI or dBm + entry->tmp_etx = false; + entry->etx = etx; + + etx_cache_entry_init(attribute_index); + + // Checks if ETX value change callback is needed + etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index); +} + +static void etx_cache_entry_init(uint8_t attribute_index) +{ + if (!etx_info.cache_sample_requested) { + return; + } + + etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + attribute_index; + storage->attempts_count = 0; + storage->etx_timer = etx_info.etx_min_sampling_time; + storage->received_acks = 0; + storage->sample_count = 0; +} + +static bool etx_update_possible(etx_sample_storage_t *storage, etx_storage_t *entry, uint16_t time_update) +{ + if (storage->etx_timer && time_update) { + if (time_update >= storage->etx_timer) { + storage->etx_timer = 0; + } else { + storage->etx_timer -= time_update; + } + } + + if (entry->etx_samples > ETX_ACCELERATED_SAMPLE_COUNT) { + //Slower ETX update phase + if (storage->sample_count < etx_info.min_sample_count || storage->etx_timer) { + if (storage->sample_count < 0xff) { + return false; + } + } + } else { + //Accelerated ETX at for new neighbor + if (storage->sample_count < ETX_ACCELERATED_INTERVAL) { + return false; + } + } + + tr_debug("ETX update possible %u attempts, %u rx ack", storage->attempts_count, storage->received_acks); + + return true; + +} + + +static etx_sample_storage_t *etx_cache_sample_update(uint8_t attribute_index, uint8_t attempts, bool ack_rx) +{ + etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + attribute_index; + storage->attempts_count += attempts; + if (ack_rx) { + storage->received_acks++; + } + storage->sample_count++; + return storage; + +} + + + /** * \brief A function to update ETX value based on transmission attempts * @@ -74,7 +191,6 @@ static ext_info_t etx_info = { */ void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool success, uint8_t attribute_index) { - uint32_t etx; uint8_t accumulated_failures; // Gets table entry etx_storage_t *entry = etx_storage_entry_get(interface_id, attribute_index); @@ -85,6 +201,18 @@ void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool succ entry->etx_samples++; } + if (etx_info.cache_sample_requested && !entry->tmp_etx) { + + etx_sample_storage_t *storage = etx_cache_sample_update(attribute_index, attempts, success); + entry->accumulated_failures = 0; + if (!etx_update_possible(storage, entry, 0)) { + return; + } + + etx_calculation(entry, storage->attempts_count, storage->received_acks, attribute_index); + return; + } + accumulated_failures = entry->accumulated_failures; if (!success) { @@ -104,28 +232,10 @@ void etx_transm_attempts_update(int8_t interface_id, uint8_t attempts, bool succ } if (entry->etx) { - // If hysteresis is set stores ETX value for comparison - if (etx_info.hysteresis && !entry->stored_diff_etx) { - entry->stored_diff_etx = entry->etx; - } if (success) { - // ETX = 7/8 * current ETX + 1/8 * ((attempts + failed attempts) << 12) - etx = entry->etx - (entry->etx >> ETX_MOVING_AVERAGE_FRACTION); - etx += (attempts + accumulated_failures) << (12 - ETX_MOVING_AVERAGE_FRACTION); - - if (etx > 0xffff) { - entry->etx = 0xffff; - } else { - entry->etx = etx; - } + etx_calculation(entry, attempts + accumulated_failures, 1, attribute_index); } - - // If real ETX value has been received do not update based on LQI or dBm - entry->tmp_etx = false; - - // Checks if ETX value change callback is needed - etx_value_change_callback_needed_check(entry->etx, &(entry->stored_diff_etx), entry->accumulated_failures, attribute_index); } } @@ -437,6 +547,9 @@ bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size) { if (!etx_storage_size) { ns_dyn_mem_free(etx_info.etx_storage_list); + ns_dyn_mem_free(etx_info.etx_cache_storage_list); + etx_info.etx_cache_storage_list = NULL; + etx_info.cache_sample_requested = false; etx_info.etx_storage_list = NULL; etx_info.ext_storage_list_size = 0; return true; @@ -447,12 +560,18 @@ bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size) } ns_dyn_mem_free(etx_info.etx_storage_list); + etx_info.cache_sample_requested = false; etx_info.ext_storage_list_size = 0; etx_info.etx_storage_list = ns_dyn_mem_alloc(sizeof(etx_storage_t) * etx_storage_size); + if (!etx_info.etx_storage_list) { + ns_dyn_mem_free(etx_info.etx_storage_list); + etx_info.etx_storage_list = NULL; + etx_info.ext_storage_list_size = 0; return false; } + etx_info.ext_storage_list_size = etx_storage_size; etx_info.interface_id = interface_id; etx_storage_t *list_ptr = etx_info.etx_storage_list; @@ -465,6 +584,51 @@ bool etx_storage_list_allocate(int8_t interface_id, uint8_t etx_storage_size) } +bool etx_cached_etx_parameter_set(uint8_t min_wait_time, uint8_t etx_min_sample_count) +{ + //No ini ETX allocation done yet + if (etx_info.ext_storage_list_size == 0) { + return false; + } + + if (min_wait_time || etx_min_sample_count) { + if (!etx_info.etx_cache_storage_list) { + //allocate + etx_info.etx_cache_storage_list = ns_dyn_mem_alloc(sizeof(etx_sample_storage_t) * etx_info.ext_storage_list_size); + + if (!etx_info.etx_cache_storage_list) { + return false; + } + etx_info.cache_sample_requested = true; + etx_sample_storage_t *sample_list = etx_info.etx_cache_storage_list; + for (uint8_t i = 0; i < etx_info.ext_storage_list_size; i++) { + memset(sample_list, 0, sizeof(etx_sample_storage_t)); + sample_list++; + } + } + + } else { + //Free Cache table we not need that anymore + etx_info.cache_sample_requested = false; + ns_dyn_mem_free(etx_info.etx_cache_storage_list); + etx_info.etx_cache_storage_list = NULL; + } + + etx_info.min_sample_count = etx_min_sample_count; + etx_info.etx_min_sampling_time = min_wait_time; + + return true; +} + +void etx_max_update_set(uint16_t etx_max_update) +{ + if (etx_max_update) { + etx_info.max_etx_update = (etx_max_update / 128) << (12 - ETX_MOVING_AVERAGE_FRACTION); + } else { + etx_info.max_etx_update = 0; + } +} + etx_storage_t *etx_storage_entry_get(int8_t interface_id, uint8_t attribute_index) { if (etx_info.interface_id != interface_id || !etx_info.etx_storage_list || attribute_index >= etx_info.ext_storage_list_size) { @@ -593,6 +757,12 @@ void etx_neighbor_remove(int8_t interface_id, uint8_t attribute_index) } etx_info.callback_ptr(etx_info.interface_id, stored_diff_etx, 0xffff, attribute_index); } + + if (etx_info.cache_sample_requested) { + //Clear cached values + etx_sample_storage_t *cache_entry = etx_info.etx_cache_storage_list + attribute_index; + memset(cache_entry, 0, sizeof(etx_sample_storage_t)); + } //Clear all data base back to zero for new user memset(entry, 0, sizeof(etx_storage_t)); } @@ -626,3 +796,35 @@ void etx_neighbor_add(int8_t interface_id, uint8_t attribute_index) } } } + +void etx_cache_timer(int8_t interface_id, uint16_t seconds_update) +{ + if (!etx_info.cache_sample_requested) { + return; + } + + protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id); + if (!interface) { + return; + } + + + if (!mac_neighbor_info(interface)) { + return; + } + + ns_list_foreach(mac_neighbor_table_entry_t, neighbour, &mac_neighbor_info(interface)->neighbour_list) { + + etx_storage_t *etx_entry = etx_storage_entry_get(interface_id, neighbour->index); + + if (!etx_entry || etx_entry->tmp_etx) { + continue; + } + etx_sample_storage_t *storage = etx_info.etx_cache_storage_list + neighbour->index; + + if (etx_update_possible(storage, etx_entry, seconds_update)) { + etx_calculation(etx_entry, storage->attempts_count, storage->received_acks, neighbour->index); + } + } + +} diff --git a/source/Service_Libs/etx/etx.h b/source/Service_Libs/etx/etx.h index 26a7024c6f..55c125456d 100644 --- a/source/Service_Libs/etx/etx.h +++ b/source/Service_Libs/etx/etx.h @@ -31,6 +31,25 @@ Range for value can be from 1 to 11 */ #define ETX_MOVING_AVERAGE_FRACTION 3 // n >> 3, 1/8 +/* ETX cache configuration definitions */ + +/* Amount of accelerated ETX samples + * After this value is received the ETX calculation starts to follow the + * slower ETX cache configuration values. + * + * Maximum value is 6 + */ +#define ETX_ACCELERATED_SAMPLE_COUNT 3 + +/* Amount of samples before updating the ETX value when in accelerated mode. + * + * if set to 1 every sample updates an ETX value. if set to 2 every second + * ETX sample updates the ETX value. + * + * This value should be smaller than ETX_ACCELERATED_SAMPLE_COUNT + */ +#define ETX_ACCELERATED_INTERVAL 2 + typedef struct etx_storage_s { uint16_t etx; /*!< 12 bits fraction */ uint16_t stored_diff_etx; /*!< 12 bits fraction */ @@ -41,6 +60,13 @@ typedef struct etx_storage_s { unsigned etx_samples: 3; } etx_storage_t; +typedef struct etx_sample_storage_s { + uint16_t attempts_count; /*!< TX attempt count */ + uint8_t etx_timer; /*!< Count down from configured value 0 means that ETX Update is possible done again*/ + uint8_t received_acks; /*!< Received ACK's */ + uint8_t sample_count; /*!< Finished TX count */ +} etx_sample_storage_t; + /** * \brief A function to update ETX value based on transmission attempts * @@ -222,4 +248,41 @@ void etx_neighbor_remove(int8_t interface_id, uint8_t attribute_index); */ void etx_neighbor_add(int8_t interface_id, uint8_t attribute_index); +/** + * \brief A function for update cached ETX calculation + * + * Shuold be call second intevall + * + * \param interface_id Interface ID + * \param seconds_update Seconds Update + * + */ +void etx_cache_timer(int8_t interface_id, uint16_t seconds_update); + +/** + * \brief A function for enable cached ETX mode and parametrs + * + * Default values for enabled Cached mode is wait time 60 seconds, etx_max_update is 0 (disabled) and etx_min_sample_count is 4. + * ETX update will happen when min wait time is reached and also reached min etx sample count. + * + * \param min_wait_time how many seconds must wait before do new ETX + * \param etx_min_sample_count define how many completed TX process must be done for new ETX. Min accepted value is 4. + * + * \return true Enable is OK + * \return false Memory allocation fail + * + */ +bool etx_cached_etx_parameter_set(uint8_t min_wait_time, uint8_t etx_min_sample_count); + + +/** + * \brief A function for set Maxium ETX update + * + * ETX RFC define that that Max value for update is 0xffff but this API cuold make that Poor link start go down slowly. + * + * \param etx_max_update 0 No limit for Update higher value means. This pameter will change normal ETX which could be 0xffff. + * + */ +void etx_max_update_set(uint16_t etx_max_update); + #endif /* ETX_H_ */ diff --git a/source/Service_Libs/fhss/channel_functions.c b/source/Service_Libs/fhss/channel_functions.c index 1d2d0c372c..594686b9bb 100644 --- a/source/Service_Libs/fhss/channel_functions.c +++ b/source/Service_Libs/fhss/channel_functions.c @@ -15,6 +15,7 @@ * limitations under the License. */ #include "nsconfig.h" +#include "common_functions.h" #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) @@ -182,9 +183,9 @@ int32_t dh1cf_get_uc_channel_index(uint16_t slot_number, uint8_t *mac, int16_t n { int32_t channel_number; uint32_t key[3]; - key[0] = (uint32_t)slot_number; - key[1] = mac[4] << 24 | mac[5] << 16 | mac[6] << 8 | mac[7]; - key[2] = mac[0] << 24 | mac[1] << 16 | mac[2] << 8 | mac[3]; + key[0] = (uint32_t) slot_number; + key[1] = common_read_32_bit(&mac[4]); + key[2] = common_read_32_bit(&mac[0]); channel_number = dh1cf_hashword(key, 3, 0) % number_of_channels; return channel_number; } @@ -193,8 +194,8 @@ int32_t dh1cf_get_bc_channel_index(uint16_t slot_number, uint16_t bsi, int16_t n { int32_t channel_number; uint32_t key[3]; - key[0] = (uint32_t)slot_number; - key[1] = bsi << 16; + key[0] = (uint32_t) slot_number; + key[1] = (uint32_t) bsi << 16; key[2] = 0; channel_number = dh1cf_hashword(key, 3, 0) % number_of_channels; return channel_number; diff --git a/source/Service_Libs/fhss/fhss.c b/source/Service_Libs/fhss/fhss.c index 8ee00ed7f6..350c9e0e07 100644 --- a/source/Service_Libs/fhss/fhss.c +++ b/source/Service_Libs/fhss/fhss.c @@ -1191,7 +1191,7 @@ static void fhss_receive_frame_callback(const fhss_api_t *api, uint16_t pan_id, } } -static uint16_t fhss_get_retry_period_callback(const fhss_api_t *api, uint8_t *destination_address, uint16_t phy_mtu) +static uint32_t fhss_get_retry_period_callback(const fhss_api_t *api, uint8_t *destination_address, uint16_t phy_mtu) { uint16_t retry_period = 0; uint16_t random_number = randLIB_get_16bit(); diff --git a/source/Service_Libs/fhss/fhss_ws.c b/source/Service_Libs/fhss/fhss_ws.c index 04f7bfa050..7cf0cd9473 100644 --- a/source/Service_Libs/fhss/fhss_ws.c +++ b/source/Service_Libs/fhss/fhss_ws.c @@ -82,7 +82,7 @@ static void fhss_event_timer_cb(int8_t timer_id, uint16_t slots); static void fhss_ws_update_uc_channel_callback(fhss_structure_t *fhss_structure); static void fhss_unicast_handler(const fhss_api_t *fhss_api, uint16_t delay); static bool fhss_ws_check_tx_allowed(fhss_structure_t *fhss_structure); -static uint8_t fhss_set_txrx_slot_length(fhss_structure_t *fhss_structure); +static uint32_t fhss_set_txrx_slot_length(fhss_structure_t *fhss_structure); // This function supports rounding up static int32_t divide_integer(int32_t dividend, int32_t divisor) @@ -169,9 +169,9 @@ static int fhss_ws_manage_channel_table_allocation(fhss_structure_t *fhss_struct return 0; } -static uint8_t fhss_set_txrx_slot_length(fhss_structure_t *fhss_structure) +static uint32_t fhss_set_txrx_slot_length(fhss_structure_t *fhss_structure) { - uint8_t number_of_tx_slots = ((fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) / WS_MAX_TXRX_SLOT_LEN_MS) / 2; + uint32_t number_of_tx_slots = ((fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) / WS_MAX_TXRX_SLOT_LEN_MS) / 2; if (!number_of_tx_slots) { return 0; } @@ -197,11 +197,23 @@ static int32_t fhss_ws_calc_bc_channel(fhss_structure_t *fhss_structure) } } #ifdef FHSS_CHANNEL_DEBUG - tr_info("%"PRIu32" BC %u %u", fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api), next_channel, fhss_structure->ws->bc_slot); + tr_info("%"PRIu32" BC %u %u %u", fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api), next_channel, fhss_structure->ws->bc_slot, fhss_structure->ws->fhss_configuration.bsi); #endif /*FHSS_CHANNEL_DEBUG*/ return next_channel; } +static uint8_t calc_own_tx_trig_slot(uint8_t own_hop) +{ + if (own_hop == 0xff) { + return 0; + } + if (own_hop & 1) { + own_hop--; + } + own_hop /= 2; + return (own_hop & 1); +} + static void fhss_broadcast_handler(const fhss_api_t *fhss_api, uint16_t delay) { int32_t next_channel; @@ -213,6 +225,7 @@ static void fhss_broadcast_handler(const fhss_api_t *fhss_api, uint16_t delay) if (fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval == 0 || fhss_structure->ws->fhss_configuration.fhss_broadcast_interval == 0) { // stop broadcast schedule fhss_structure->ws->is_on_bc_channel = false; + fhss_structure->ws->synchronization_time = 0; return; } if (fhss_structure->ws->is_on_bc_channel == false) { @@ -230,19 +243,27 @@ static void fhss_broadcast_handler(const fhss_api_t *fhss_api, uint16_t delay) uint16_t bc_max_random = (bc_dwell_us / 10) / 50; eventOS_callback_timer_start(fhss_structure->fhss_event_timer, randLIB_get_random_in_range(bc_min_random, bc_max_random)); } else { + fhss_structure->ws->unicast_start_time_us = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); uint32_t timeout = MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval); fhss_ws_start_timer(fhss_structure, timeout - (delay * fhss_structure->platform_functions.fhss_resolution_divider), fhss_broadcast_handler); fhss_structure->ws->is_on_bc_channel = false; // Should return to own (unicast) listening channel after broadcast channel next_channel = fhss_structure->rx_channel; /* Start timer with random timeout to trigger unicast TX queue poll event. - * Min random is 1/30 of the TX slot length. - * Max random is 1/10 of the TX slot length. + * For hops 0,1,4,5,8,9,... + * Min random is 1/100 of the TX slot length. + * Max random is 1/5 of the TX slot length. + * + * For hops 2,3,6,7,10,11,... + * Min random is 1/100 of the TX slot length plus 0.5*TX slot length. + * Max random is 1/10 of the TX slot length plus 0.5*TX slot length. * Event timer resolution is 50us. */ + // returns 1 if polling of TX queue is done on latter half of the TX slot + uint8_t own_tx_trig_slot = calc_own_tx_trig_slot(fhss_structure->own_hop); uint32_t txrx_slot_length_us = MS_TO_US(fhss_structure->ws->txrx_slot_length_ms); - uint16_t uc_min_random = (txrx_slot_length_us / 30) / 50; - uint16_t uc_max_random = (txrx_slot_length_us / 10) / 50; + uint16_t uc_min_random = (((txrx_slot_length_us / 2) * own_tx_trig_slot) / 50) + ((txrx_slot_length_us / 100) / 50); + uint16_t uc_max_random = (((txrx_slot_length_us / 2) * own_tx_trig_slot) / 50) + ((txrx_slot_length_us / 5) / 50); bool tx_allowed = fhss_ws_check_tx_allowed(fhss_structure); if (!tx_allowed) { uc_min_random += (txrx_slot_length_us) / 50; @@ -288,6 +309,10 @@ static void fhss_event_timer_cb(int8_t timer_id, uint16_t slots) } else { // On unicast, start timer to trigger polling event on next TX slot uint32_t delay_between_tx_slots_us = MS_TO_US(fhss_structure->ws->txrx_slot_length_ms) * 2; + // Timer could drift to RX slot when broadcast interval is high. Return timer to TX slot. + if (fhss_ws_check_tx_allowed(fhss_structure) == false) { + delay_between_tx_slots_us -= MS_TO_US(fhss_structure->ws->txrx_slot_length_ms - (calc_own_tx_trig_slot(fhss_structure->own_hop) * (fhss_structure->ws->txrx_slot_length_ms / 2))); + } if (delay_between_tx_slots_us < get_remaining_slots_us(fhss_structure, fhss_broadcast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval))) { eventOS_callback_timer_start(fhss_structure->fhss_event_timer, delay_between_tx_slots_us / 50); } @@ -376,6 +401,8 @@ static int16_t fhss_ws_synch_state_set_callback(const fhss_api_t *api, fhss_stat fhss_ws_start_timer(fhss_structure, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_uc_dwell_interval), fhss_unicast_handler); fhss_structure->ws->unicast_timer_running = true; } + } else if (fhss_state == FHSS_UNSYNCHRONIZED) { + fhss_structure->ws->synchronization_time = 0; } fhss_structure->fhss_state = fhss_state; @@ -445,7 +472,6 @@ static int fhss_ws_tx_handle_callback(const fhss_api_t *api, bool is_broadcast_a if (fhss_structure->fhss_state == FHSS_SYNCHRONIZED) { fhss_ws_neighbor_timing_info_t *neighbor_timing_info = fhss_structure->ws->get_neighbor_info(api, destination_address); if (!neighbor_timing_info) { - tr_err("FHSS: No neighbor info: %s", trace_array(destination_address, 8)); return -2; } // TODO: WS bootstrap has to store neighbors number of channels @@ -467,7 +493,7 @@ static int fhss_ws_tx_handle_callback(const fhss_api_t *api, bool is_broadcast_a } } #ifdef FHSS_CHANNEL_DEBUG - tr_debug("TX channel: %u %u", tx_channel, destination_slot + 1); + tr_debug("TX channel: %u %u %s", tx_channel, destination_slot + 1, trace_array(destination_address, 8)); #endif /*FHSS_CHANNEL_DEBUG*/ fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, tx_channel); } @@ -500,23 +526,19 @@ static bool fhss_ws_check_tx_allowed(fhss_structure_t *fhss_structure) if (fhss_structure->ws->is_on_bc_channel == true) { return true; } - uint8_t number_of_tx_slots = fhss_set_txrx_slot_length(fhss_structure); + uint32_t number_of_tx_slots = fhss_set_txrx_slot_length(fhss_structure); // Allow transmission when broadcast interval is very short comparing to MAX slot length if (!number_of_tx_slots) { return true; } uint32_t remaining_time_ms = get_remaining_slots_us(fhss_structure, fhss_broadcast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval)) / 1000; - uint32_t tx_slot_begin = (fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) - (fhss_structure->ws->txrx_slot_length_ms * (fhss_structure->own_hop & 1)); - uint32_t rx_slot_begin = tx_slot_begin - fhss_structure->ws->txrx_slot_length_ms; - uint8_t n_o_tx_slots = number_of_tx_slots; - - while (n_o_tx_slots--) { - if ((remaining_time_ms <= tx_slot_begin) && (remaining_time_ms > rx_slot_begin)) { - return true; - } - tx_slot_begin -= (2 * fhss_structure->ws->txrx_slot_length_ms); - rx_slot_begin = tx_slot_begin - fhss_structure->ws->txrx_slot_length_ms; + uint32_t tx_slot_begin_ms = (fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) - (fhss_structure->ws->txrx_slot_length_ms * (fhss_structure->own_hop & 1)); + tx_slot_begin_ms = tx_slot_begin_ms - (((tx_slot_begin_ms - remaining_time_ms) / (2 * fhss_structure->ws->txrx_slot_length_ms)) * (2 * fhss_structure->ws->txrx_slot_length_ms)); + uint32_t rx_slot_begin_ms = tx_slot_begin_ms - fhss_structure->ws->txrx_slot_length_ms; + // Check if we are currently on TX slot. + if ((remaining_time_ms <= tx_slot_begin_ms) && (remaining_time_ms > rx_slot_begin_ms)) { + return true; } return false; @@ -698,6 +720,45 @@ static bool fhss_ws_use_broadcast_queue_cb(const fhss_api_t *api, bool is_broadc return is_broadcast_addr; } +static uint32_t fhss_ws_get_retry_period_callback(const fhss_api_t *api, uint8_t *destination_address, uint16_t phy_mtu) +{ + (void) destination_address; + (void) phy_mtu; + uint32_t return_value = 0; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return return_value; + } + if (fhss_structure->own_hop == 0xff) { + return return_value; + } + if (fhss_structure->ws->is_on_bc_channel == true) { + return return_value; + } + + uint32_t txrx_slot_length_us = MS_TO_US(fhss_structure->ws->txrx_slot_length_ms); + uint32_t unicast_start_us = fhss_structure->ws->unicast_start_time_us; + uint32_t cur_time_us = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); + uint32_t tx_trig_offset_us = (txrx_slot_length_us / 2) * calc_own_tx_trig_slot(fhss_structure->own_hop); + + uint32_t next_tx_trig_slot_start_us = unicast_start_us + (txrx_slot_length_us * !fhss_ws_check_tx_allowed(fhss_structure)) + tx_trig_offset_us; + uint32_t next_tx_trig_slot_end_us = next_tx_trig_slot_start_us + (txrx_slot_length_us / 2); + while ((next_tx_trig_slot_start_us < cur_time_us) || ((next_tx_trig_slot_start_us - cur_time_us) > (uint32_t) MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval))) { + if (cur_time_us < next_tx_trig_slot_end_us) { + return 0; + } + next_tx_trig_slot_start_us += (txrx_slot_length_us * 2); + next_tx_trig_slot_end_us = next_tx_trig_slot_start_us + (txrx_slot_length_us / 2); + } + return_value = next_tx_trig_slot_start_us - cur_time_us; + uint32_t time_to_bc_channel = get_remaining_slots_us(fhss_structure, fhss_broadcast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval)); + if (time_to_bc_channel <= return_value) { + return_value += MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval); + } + + return return_value; +} + static void fhss_unicast_handler(const fhss_api_t *fhss_api, uint16_t delay) { uint32_t timeout = 0; @@ -734,7 +795,7 @@ int fhss_ws_set_callbacks(fhss_structure_t *fhss_structure) fhss_structure->fhss_api->data_tx_fail = &fhss_ws_data_tx_fail_callback; fhss_structure->fhss_api->synch_state_set = &fhss_ws_synch_state_set_callback; fhss_structure->fhss_api->read_timestamp = NULL; - fhss_structure->fhss_api->get_retry_period = NULL; + fhss_structure->fhss_api->get_retry_period = &fhss_ws_get_retry_period_callback; fhss_structure->fhss_api->write_synch_info = &fhss_ws_write_synch_info_callback; fhss_structure->fhss_api->init_callbacks = &fhss_init_callbacks_cb; return 0; @@ -753,6 +814,7 @@ int fhss_ws_set_parent(fhss_structure_t *fhss_structure, const uint8_t eui64[8], return 0; } platform_enter_critical(); + uint16_t own_bc_slot = fhss_structure->ws->bc_slot; uint32_t prev_synchronization_time = fhss_structure->ws->synchronization_time; fhss_structure->ws->synchronization_time = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); uint32_t time_since_last_synch_us = fhss_structure->ws->synchronization_time - prev_synchronization_time; @@ -774,19 +836,30 @@ int fhss_ws_set_parent(fhss_structure_t *fhss_structure, const uint8_t eui64[8], fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval = bc_timing_info->broadcast_dwell_interval; fhss_structure->ws->fhss_configuration.fhss_broadcast_interval = bc_timing_info->broadcast_interval; fhss_set_txrx_slot_length(fhss_structure); - fhss_structure->ws->bc_slot = bc_timing_info->broadcast_slot + slots_since_reception; + if (fhss_structure->ws->fhss_configuration.ws_bc_channel_function == WS_FIXED_CHANNEL) { + fhss_structure->ws->bc_slot = 0; + } else { + fhss_structure->ws->bc_slot = bc_timing_info->broadcast_slot + slots_since_reception; + } if (fhss_structure->ws->fhss_configuration.ws_bc_channel_function == WS_TR51CF) { fhss_structure->ws->bc_slot %= fhss_structure->number_of_channels; } platform_exit_critical(); //TODO: support multiple parents fhss_structure->ws->parent_bc_info = bc_timing_info; - if (prev_synchronization_time) { + if (prev_synchronization_time && fhss_structure->ws->fhss_configuration.ws_bc_channel_function != WS_FIXED_CHANNEL) { + //TODO: Compensation for fixed channel configuration if (SYNCH_COMPENSATION_MIN_INTERVAL <= US_TO_S(time_since_last_synch_us)) { // Update clock drift - fhss_structure->ws->drift_per_millisecond_ns += divide_integer(MS_TO_NS(true_bc_interval_offset - own_bc_interval_offset), US_TO_MS(time_since_last_synch_us)); + int32_t drift_per_ms_tmp = divide_integer(MS_TO_NS((true_bc_interval_offset - own_bc_interval_offset) + ((int32_t)(fhss_structure->ws->bc_slot - own_bc_slot) * bc_timing_info->broadcast_interval)), US_TO_MS(time_since_last_synch_us)); + if (drift_per_ms_tmp > MAX_DRIFT_COMPENSATION_STEP) { + drift_per_ms_tmp = MAX_DRIFT_COMPENSATION_STEP; + } else if (drift_per_ms_tmp < -MAX_DRIFT_COMPENSATION_STEP) { + drift_per_ms_tmp = -MAX_DRIFT_COMPENSATION_STEP; + } + fhss_structure->ws->drift_per_millisecond_ns += drift_per_ms_tmp; } - tr_debug("synch to parent: %s, drift: %"PRIi32"ms in %"PRIu32" seconds, compensation: %"PRIi32"ns per ms", trace_array(eui64, 8), true_bc_interval_offset - own_bc_interval_offset, US_TO_S(time_since_last_synch_us), fhss_structure->ws->drift_per_millisecond_ns); + tr_debug("synch to parent: %s, drift: %"PRIi32"ms in %"PRIu32" seconds, compensation: %"PRIi32"ns per ms", trace_array(eui64, 8), true_bc_interval_offset - own_bc_interval_offset + ((int32_t)(fhss_structure->ws->bc_slot - own_bc_slot) * bc_timing_info->broadcast_interval), US_TO_S(time_since_last_synch_us), fhss_structure->ws->drift_per_millisecond_ns); } return 0; } @@ -812,6 +885,10 @@ int fhss_ws_configuration_set(fhss_structure_t *fhss_structure, const fhss_ws_co fhss_stop_timer(fhss_structure, fhss_unicast_handler); fhss_structure->ws->unicast_timer_running = false; } + if (fhss_configuration->ws_bc_channel_function == WS_FIXED_CHANNEL || (fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval == 0 || fhss_structure->ws->fhss_configuration.fhss_broadcast_interval == 0)) { + fhss_structure->ws->synchronization_time = 0; + } + if ((fhss_structure->ws->unicast_timer_running == false) && (fhss_configuration->ws_uc_channel_function != WS_FIXED_CHANNEL) && fhss_configuration->fhss_uc_dwell_interval) { fhss_ws_start_timer(fhss_structure, MS_TO_US(fhss_configuration->fhss_uc_dwell_interval), fhss_unicast_handler); fhss_structure->ws->unicast_timer_running = true; @@ -840,5 +917,4 @@ int fhss_ws_set_hop_count(fhss_structure_t *fhss_structure, const uint8_t hop_co fhss_structure->own_hop = hop_count; return 0; } - #endif // HAVE_WS diff --git a/source/Service_Libs/fhss/fhss_ws.h b/source/Service_Libs/fhss/fhss_ws.h index c86898c2b1..917b599878 100644 --- a/source/Service_Libs/fhss/fhss_ws.h +++ b/source/Service_Libs/fhss/fhss_ws.h @@ -27,6 +27,8 @@ #define DEFAULT_MIN_SYNCH_INTERVAL 60 // Drift compensation allowed if at least SYNCH_COMPENSATION_MIN_INTERVAL (seconds) since last synchronization #define SYNCH_COMPENSATION_MIN_INTERVAL 60 +// MAX compensation per received synchronization info in ns +#define MAX_DRIFT_COMPENSATION_STEP 10 typedef struct fhss_ws fhss_ws_t; struct fhss_ws { @@ -36,6 +38,7 @@ struct fhss_ws { uint16_t min_synch_interval; uint32_t txrx_slot_length_ms; uint32_t synchronization_time; + uint32_t unicast_start_time_us; int32_t drift_per_millisecond_ns; int16_t *tr51_channel_table; uint8_t *tr51_output_table; diff --git a/source/Service_Libs/hmac/hmac_sha1.c b/source/Service_Libs/hmac/hmac_sha1.c index f4c79d2d21..7e7e47dcb6 100644 --- a/source/Service_Libs/hmac/hmac_sha1.c +++ b/source/Service_Libs/hmac/hmac_sha1.c @@ -25,7 +25,7 @@ #define TRACE_GROUP "hmac" -int8_t hmac_sha1_calc(const uint8_t *key, uint16_t key_len, const uint8_t *data, uint16_t data_len, uint8_t *result) +int8_t hmac_sha1_calc(const uint8_t *key, uint16_t key_len, const uint8_t *data, uint16_t data_len, uint8_t *result, uint8_t result_len) { #ifdef EXTRA_DEBUG_INFO // Extensive debug for now, to be disabled later @@ -62,13 +62,21 @@ int8_t hmac_sha1_calc(const uint8_t *key, uint16_t key_len, const uint8_t *data, if (mbedtls_md_hmac_update(&ctx, (const unsigned char *) data, data_len) != 0) { goto error; } - if (mbedtls_md_hmac_finish(&ctx, result) != 0) { + + uint8_t result_value[20]; + if (mbedtls_md_hmac_finish(&ctx, result_value) != 0) { goto error; } mbedtls_md_free(&ctx); + if (result_len > 20) { + result_len = 20; + } + + memcpy(result, result_value, result_len); + #ifdef EXTRA_DEBUG_INFO - tr_debug("hmac_sha_1 result %s\n", trace_array(result, 20)); + tr_debug("hmac_sha_1 result %s\n", trace_array(result_value, 20)); #endif return 0; diff --git a/source/Service_Libs/hmac/hmac_sha1.h b/source/Service_Libs/hmac/hmac_sha1.h index e387e46781..77ef76bfc0 100644 --- a/source/Service_Libs/hmac/hmac_sha1.h +++ b/source/Service_Libs/hmac/hmac_sha1.h @@ -27,12 +27,13 @@ * \param key_len key length * \param data pointer to data * \param data_len data length - * \param result pointer to result, must be at least 160 bytes + * \param result pointer to result + * \param result_len in bytes, maximum is 20 bytes * * \return < 0 failure * \return >= 0 success * */ -int8_t hmac_sha1_calc(const uint8_t *key, uint16_t key_len, const uint8_t *data, uint16_t data_len, uint8_t *result); +int8_t hmac_sha1_calc(const uint8_t *key, uint16_t key_len, const uint8_t *data, uint16_t data_len, uint8_t *result, uint8_t result_len); #endif /* HMAC_SHA1_ */ diff --git a/source/Service_Libs/ieee_802_11/ieee_802_11.c b/source/Service_Libs/ieee_802_11/ieee_802_11.c index 544586f890..fb36a03c67 100644 --- a/source/Service_Libs/ieee_802_11/ieee_802_11.c +++ b/source/Service_Libs/ieee_802_11/ieee_802_11.c @@ -67,7 +67,7 @@ int8_t ieee_802_11_prf_finish(ieee_802_11_prf_t *prf, uint8_t *result) for (uint8_t i = 0; i < (prf->bits + 159) / 160; i++) { prf->string[prf->a_len + 1 + prf->b_len] = i; /* X (index) */ - if (hmac_sha1_calc(prf->key, prf->key_len, prf->string, string_len, result) < 0) { + if (hmac_sha1_calc(prf->key, prf->key_len, prf->string, string_len, result, 20) < 0) { return -1; } result += 160 / 8; diff --git a/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp.h b/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp.h index dba2c85226..04881c1f84 100644 --- a/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp.h +++ b/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp.h @@ -1,5 +1,6 @@ /************************************************************************** * +* Copyright (c) 2019 Arm Limited and affiliates. * Copyright 2011-2016 by Andrey Butok. FNET Community. * Copyright 2008-2010 by Andrey Butok. Freescale Semiconductor, Inc. * @@ -71,6 +72,11 @@ #define FNET_COMP_PACKED_VAR __packed #endif +#if FNET_CFG_COMP_CLANG + #define FNET_COMP_PACKED_BEGIN _Pragma("pack(1)") + #define FNET_COMP_PACKED_END _Pragma("pack()") +#endif + #if FNET_CFG_COMP_GNUC #define FNET_COMP_PACKED_BEGIN _Pragma("pack(1)") #define FNET_COMP_PACKED_END _Pragma("pack(8)") diff --git a/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp_config.h b/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp_config.h index 4371090d0d..cf878c5aa1 100644 --- a/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp_config.h +++ b/source/Service_Libs/mdns/fnet/fnet_stack/port/compiler/fnet_comp_config.h @@ -1,5 +1,6 @@ /************************************************************************** * +* Copyright (c) 2019 Arm Limited and affiliates. * Copyright 2011-2016 by Andrey Butok. FNET Community. * Copyright 2008-2010 by Andrey Butok. Freescale Semiconductor, Inc. * @@ -40,7 +41,8 @@ * Current version of the FNET supports the following compiler definitions: * - @c FNET_CFG_COMP_IAR = Used compiler is IAR. * - @c FNET_CFG_COMP_GNUC = Used compiler is GCC. - * - @c FNET_CFG_COMP_UV = Used compiler is Keil uVision. + * - @c FNET_CFG_COMP_CLANG = Used compiler is Keil uVision with Clang. + * - @c FNET_CFG_COMP_UV = Used compiler is Keil uVision with armcc. * - @c FNET_CFG_COMP_GHS = Used compiler is Green Hills. * - @c FNET_CFG_COMP_DCC = Used compiler is Diab. * - @c FNET_CFG_COMP_CW = Used compiler is CodeWarrior. @@ -61,7 +63,10 @@ /* GHS compiler. */ #elif (defined(FNET_CFG_COMP_GHS) && FNET_CFG_COMP_GHS) #define FNET_COMP_STR "GHS" - /* Keil uVision compiler. */ + /* Keil uVision compiler with armclang. */ +#elif (defined(FNET_CFG_COMP_CLANG) && FNET_CFG_COMP_CLANG) + #define FNET_COMP_STR "CLANG" + /* Keil uVision compiler with armcc. */ #elif (defined(FNET_CFG_COMP_UV) && FNET_CFG_COMP_UV) #define FNET_COMP_STR "UV" /* GNU GCC */ @@ -85,7 +90,11 @@ #elif defined(__ghs__) #define FNET_CFG_COMP_GHS (1) #define FNET_COMP_STR "GHS" - /* Keil uVision compiler. */ + /* Keil uVision compiler using Clang. */ + #elif defined(__clang__) + #define FNET_CFG_COMP_CLANG (1) + #define FNET_COMP_STR "CLANG" + /* Keil uVision compiler using armcc. */ #elif defined(__CC_ARM) #define FNET_CFG_COMP_UV (1) #define FNET_COMP_STR "UV" @@ -109,6 +118,9 @@ #ifndef FNET_CFG_COMP_CW #define FNET_CFG_COMP_CW (0) #endif +#ifndef FNET_CFG_COMP_CLANG + #define FNET_CFG_COMP_CLANG (0) +#endif #ifndef FNET_CFG_COMP_UV #define FNET_CFG_COMP_UV (0) #endif diff --git a/source/Service_Libs/mdns/fnet/fnet_stack/services/mdns/fnet_mdns.c b/source/Service_Libs/mdns/fnet/fnet_stack/services/mdns/fnet_mdns.c index 7254708622..4ae61bef83 100644 --- a/source/Service_Libs/mdns/fnet/fnet_stack/services/mdns/fnet_mdns.c +++ b/source/Service_Libs/mdns/fnet/fnet_stack/services/mdns/fnet_mdns.c @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (c) 2017, Arm Limited and affiliates. +* Copyright (c) 2017, 2019 Arm Limited and affiliates. * Copyright 2016 by Andrey Butok. FNET Community. * *************************************************************************** @@ -1614,7 +1614,7 @@ static const fnet_uint8_t *fnet_mdns_process_response(fnet_mdns_if_t *mdns_if, c mdns_if->host_name_conflict_timestamp = fnet_timer_get_ms(); /* If in Probing state, change name and try again */ - if((mdns_if->state == FNET_MDNS_STATE_PROBING)) + if(mdns_if->state == FNET_MDNS_STATE_PROBING) { fnet_mdns_update_name_counter(mdns_if); /* Regenerate name.*/ } diff --git a/source/Service_Libs/mdns/fnet_user_config.h b/source/Service_Libs/mdns/fnet_user_config.h index 864932e899..3c74283898 100644 --- a/source/Service_Libs/mdns/fnet_user_config.h +++ b/source/Service_Libs/mdns/fnet_user_config.h @@ -42,7 +42,6 @@ /* Environment */ #define FNET_CFG_CPU_LITTLE_ENDIAN (1) // Simulator is big endian, Add HW endianess also, follow target -#define FNET_CFG_COMP_GNUC (1) // select compiler to keep compiler selection happy #define FNET_CFG_MDNS_MAX (1) // allow only 1 mDNS server, as socket needs to be bound to port 5353 #define FNET_CFG_MDNS_SERVICE_MAX (2) // Allow 2 services per server diff --git a/source/Service_Libs/nist_aes_kw/nist_aes_kw.c b/source/Service_Libs/nist_aes_kw/nist_aes_kw.c index 6164731010..5345c0deda 100644 --- a/source/Service_Libs/nist_aes_kw/nist_aes_kw.c +++ b/source/Service_Libs/nist_aes_kw/nist_aes_kw.c @@ -20,9 +20,17 @@ #include "ns_types.h" #include "ns_list.h" #include "ns_trace.h" -#if defined(HAVE_WS) && (defined(HAVE_PAE_SUPP) || defined(HAVE_PAE_AUTH)) + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_NIST_KW_C) && defined(HAVE_WS) && (defined(HAVE_PAE_SUPP) || defined(HAVE_PAE_AUTH)) #include "mbedtls/nist_kw.h" #endif + #include "Service_Libs/nist_aes_kw/nist_aes_kw.h" #ifdef HAVE_WS @@ -31,7 +39,7 @@ int8_t nist_aes_key_wrap(uint8_t is_wrap, const uint8_t *key, int16_t key_bits, const uint8_t *input, size_t input_len, uint8_t *output, size_t *output_len) { -#if defined(HAVE_PAE_SUPP) || defined(HAVE_PAE_AUTH) +#if defined(MBEDTLS_NIST_KW_C) && (defined(HAVE_PAE_SUPP) || defined(HAVE_PAE_AUTH)) int8_t ret_val = 0; mbedtls_nist_kw_context ctx; diff --git a/source/configs/base/cfg_ws_border_router.h b/source/configs/base/cfg_ws_border_router.h index cfcb5f7bc2..d2fec89889 100644 --- a/source/configs/base/cfg_ws_border_router.h +++ b/source/configs/base/cfg_ws_border_router.h @@ -24,3 +24,5 @@ #define HAVE_WS_BORDER_ROUTER #define HAVE_ND_PROXY #define MULTICAST_FORWARDING +#define HAVE_PAE_AUTH +#define HAVE_EAPOL_RELAY diff --git a/source/configs/base/cfg_ws_router.h b/source/configs/base/cfg_ws_router.h index 4a0e906133..4822bbc37e 100644 --- a/source/configs/base/cfg_ws_router.h +++ b/source/configs/base/cfg_ws_router.h @@ -22,3 +22,5 @@ #define HAVE_6LOWPAN_ND #define HAVE_MPL #define HAVE_WS +#define HAVE_PAE_SUPP +#define HAVE_EAPOL_RELAY diff --git a/source/configs/cfg_generic.h b/source/configs/cfg_generic.h index 7e645ef43e..9ed2d221e5 100644 --- a/source/configs/cfg_generic.h +++ b/source/configs/cfg_generic.h @@ -33,6 +33,3 @@ #define THREAD_THCI_SUPPORT #define HAVE_WS #define MLE_TEST -//#define HAVE_PAE_SUPP -//#define HAVE_PAE_AUTH -//#define HAVE_EAPOL_RELAY diff --git a/source/configs/cfg_ws_border_router.h b/source/configs/cfg_ws_border_router.h index 638fd8f3b9..102890cc60 100644 --- a/source/configs/cfg_ws_border_router.h +++ b/source/configs/cfg_ws_border_router.h @@ -23,6 +23,3 @@ #define HAVE_DHCPV6_SERVER #define EXTRA_CONSISTENCY_CHECKS -//#define HAVE_PAE_SUPP -//#define HAVE_PAE_AUTH -//#define HAVE_EAPOL_RELAY diff --git a/source/ipv6_stack/ipv6_routing_table.c b/source/ipv6_stack/ipv6_routing_table.c index 804ec63049..d51eb029d3 100644 --- a/source/ipv6_stack/ipv6_routing_table.c +++ b/source/ipv6_stack/ipv6_routing_table.c @@ -52,14 +52,40 @@ #define NCACHE_GC_PERIOD 20 /* seconds */ #define DCACHE_GC_PERIOD 20 /* seconds */ -static uint16_t current_max_cache = 64; +/* Neighbour Cache garbage collection parameters (per interface) */ +/* Parameters only for garbage-collectible entries; registered entries counted separately */ +#define NCACHE_MAX_LONG_TERM 8 /* Target for basic GC - expire old entries if more than this */ +#define NCACHE_MAX_SHORT_TERM 32 /* Expire stale entries if more than this */ +#define NCACHE_MAX_ABSOLUTE 64 /* Never have more than this */ +#define NCACHE_GC_AGE 600 /* 10 minutes (1s units - decremented every slow timer call) */ + +/* Destination Cache garbage collection parameters (system-wide) */ +#define DCACHE_MAX_LONG_TERM 16 +#define DCACHE_MAX_SHORT_TERM 40 +#define DCACHE_MAX_ABSOLUTE 64 /* Never have more than this */ +#define DCACHE_GC_AGE (30 * DCACHE_GC_PERIOD) /* 10 minutes */ + +typedef struct destination_cache_configuration_s { + uint16_t max_entries; // Never have more than this + uint16_t short_term_entries; // Expire stale entries if more than this + uint16_t long_term_entries; // Target for basic GC - expire old entries if more than this + uint16_t entry_lifetime; // 20s units - decremented once per periodic GC +} destination_cache_config_t; + +typedef struct neighbour_cache_configuration_s { + uint16_t max_entries; // Never have more than this + uint16_t short_term_entries; // Expire stale entries if more than this + uint16_t long_term_entries; // Target for basic GC - expire old entries if more than this + uint16_t entry_lifetime; // 1s units - decremented every slow timer call +} neighbour_cache_config_t; + +static destination_cache_config_t destination_cache_config = {DCACHE_MAX_ABSOLUTE, DCACHE_MAX_SHORT_TERM, DCACHE_MAX_LONG_TERM, DCACHE_GC_AGE}; +static neighbour_cache_config_t neighbour_cache_config = {NCACHE_MAX_ABSOLUTE, NCACHE_MAX_SHORT_TERM, NCACHE_MAX_LONG_TERM, NCACHE_GC_AGE}; /* We track "lifetime" of garbage-collectible entries, resetting * when used. Entries with lifetime 0 are favoured * for garbage-collection. */ -#define NCACHE_GC_AGE 600 /* 10 minutes (1s units - decremented every slow timer call) */ -#define DCACHE_GC_AGE 30 /* 10 minutes (20s units - decremented once per periodic GC) */ -#define DCACHE_GC_AGE_LL 6 /* 2 minutes for link-local destinations */ +#define DCACHE_GC_AGE_LL (120 / DCACHE_GC_PERIOD) /* 2 minutes for link-local destinations, in DCACHE_GC_PERIOD intervals */ /* For probable routers, consider them unreachable if ETX is greater than this */ #define ETX_REACHABILITY_THRESHOLD 0x200 /* 8.8 fixed-point, so 2 */ @@ -79,27 +105,6 @@ static uint8_t ipv6_route_table_get_max_entries(int8_t interface_id, ipv6_route_ static uint16_t dcache_gc_timer; -static uint16_t cache_long_term(bool is_destination) -{ - uint16_t value = current_max_cache / 8; - if (is_destination) { - value *= 2; - } - if (value < 4) { - value = 4; - } - return value; -} - -static uint16_t cache_short_term(bool is_destination) -{ - uint16_t value = current_max_cache / 2; - if (value < cache_long_term(is_destination)) { - return cache_long_term(is_destination); - } - return value; -} - static uint32_t next_probe_time(ipv6_neighbour_cache_t *cache, uint_fast8_t retrans_num) { uint32_t t = cache->retrans_timer; @@ -120,7 +125,57 @@ int8_t ipv6_neighbour_set_current_max_cache(uint16_t max_cache) if (max_cache < 4) { return -1; } - current_max_cache = max_cache; + + // adjust destination cache + destination_cache_config.max_entries = max_cache; + destination_cache_config.long_term_entries = max_cache / 4; + if (destination_cache_config.long_term_entries < 4) { + destination_cache_config.long_term_entries = 4; + } + destination_cache_config.short_term_entries = max_cache / 2; + if (destination_cache_config.short_term_entries < destination_cache_config.long_term_entries) { + destination_cache_config.short_term_entries = destination_cache_config.long_term_entries; + } + + // adjust neighbour cache + neighbour_cache_config.max_entries = max_cache; + neighbour_cache_config.long_term_entries = max_cache / 8; + if (neighbour_cache_config.long_term_entries < 4) { + neighbour_cache_config.long_term_entries = 4; + } + neighbour_cache_config.short_term_entries = max_cache / 4; + if (neighbour_cache_config.short_term_entries < neighbour_cache_config.long_term_entries) { + neighbour_cache_config.short_term_entries = neighbour_cache_config.long_term_entries; + } + + return 0; +} + +int8_t ipv6_destination_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime) +{ + if ((max_entries < 4) || (short_term_threshold >= max_entries) || (long_term_threshold >= short_term_threshold) || (lifetime < 120)) { + return -1; + } + + destination_cache_config.max_entries = max_entries; + destination_cache_config.short_term_entries = short_term_threshold; + destination_cache_config.long_term_entries = long_term_threshold; + destination_cache_config.entry_lifetime = lifetime; + + return 0; +} + +int8_t ipv6_neighbour_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime) +{ + if ((max_entries < 4) || (short_term_threshold >= max_entries) || (long_term_threshold >= short_term_threshold) || (lifetime < 120)) { + return -1; + } + + neighbour_cache_config.max_entries = max_entries; + neighbour_cache_config.short_term_entries = short_term_threshold; + neighbour_cache_config.long_term_entries = long_term_threshold; + neighbour_cache_config.entry_lifetime = lifetime; + return 0; } @@ -260,7 +315,7 @@ ipv6_neighbour_t *ipv6_neighbour_lookup_or_create(ipv6_neighbour_cache_t *cache, } } - if (count >= current_max_cache) { + if (count >= neighbour_cache_config.max_entries) { entry = ns_list_get_last(&cache->list); ipv6_neighbour_entry_remove(cache, entry); } @@ -311,7 +366,7 @@ ipv6_neighbour_t *ipv6_neighbour_used(ipv6_neighbour_cache_t *cache, ipv6_neighb { /* Reset the GC life, if it's a GC entry */ if (entry->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { - entry->lifetime = NCACHE_GC_AGE; + entry->lifetime = neighbour_cache_config.entry_lifetime; } /* Move it to the front of the list */ @@ -653,7 +708,7 @@ static void ipv6_neighbour_cache_gc_periodic(ipv6_neighbour_cache_t *cache) } } - if (gc_count <= cache_long_term(false)) { + if (gc_count <= neighbour_cache_config.long_term_entries) { return; } @@ -669,9 +724,9 @@ static void ipv6_neighbour_cache_gc_periodic(ipv6_neighbour_cache_t *cache) continue; } - if (entry->lifetime == 0 || gc_count > cache_short_term(false)) { + if (entry->lifetime == 0 || gc_count > neighbour_cache_config.short_term_entries) { ipv6_neighbour_entry_remove(cache, entry); - if (--gc_count <= cache_long_term(false)) { + if (--gc_count <= neighbour_cache_config.long_term_entries) { break; } } @@ -876,7 +931,7 @@ ipv6_destination_t *ipv6_destination_lookup_or_create(const uint8_t *address, in if (!entry) { - if (count > current_max_cache) { + if (count > destination_cache_config.max_entries) { entry = ns_list_get_last(&ipv6_destination_cache); ns_list_remove(&ipv6_destination_cache, entry); ipv6_destination_release(entry); @@ -915,7 +970,7 @@ ipv6_destination_t *ipv6_destination_lookup_or_create(const uint8_t *address, in if (addr_ipv6_scope(address, NULL) <= IPV6_SCOPE_LINK_LOCAL) { entry->lifetime = DCACHE_GC_AGE_LL; } else { - entry->lifetime = DCACHE_GC_AGE; + entry->lifetime = destination_cache_config.entry_lifetime / DCACHE_GC_PERIOD; } return entry; @@ -1048,7 +1103,7 @@ static void ipv6_destination_cache_gc_periodic(void) #endif } - if (gc_count <= cache_long_term(true)) { + if (gc_count <= destination_cache_config.long_term_entries) { return; } @@ -1058,10 +1113,10 @@ static void ipv6_destination_cache_gc_periodic(void) * MAX_LONG_TERM. */ ns_list_foreach_reverse_safe(ipv6_destination_t, entry, &ipv6_destination_cache) { - if (entry->lifetime == 0 || gc_count > cache_short_term(true)) { + if (entry->lifetime == 0 || gc_count > destination_cache_config.short_term_entries) { ns_list_remove(&ipv6_destination_cache, entry); ipv6_destination_release(entry); - if (--gc_count <= cache_long_term(true)) { + if (--gc_count <= destination_cache_config.long_term_entries) { break; } } diff --git a/source/ipv6_stack/ipv6_routing_table.h b/source/ipv6_stack/ipv6_routing_table.h index 8d2eb0250e..291077527b 100644 --- a/source/ipv6_stack/ipv6_routing_table.h +++ b/source/ipv6_stack/ipv6_routing_table.h @@ -123,7 +123,8 @@ typedef struct ipv6_neighbour_cache { bool recv_ns_aro : 1; bool recv_na_aro : 1; bool use_eui64_as_slla_in_aro : 1; - bool omit_aro_success : 1; + bool omit_na_aro_success : 1; + bool omit_na : 1; // except for ARO successes which have a separate flag int8_t interface_id; uint8_t max_ll_len; uint8_t gc_timer; @@ -170,8 +171,9 @@ extern void ipv6_neighbour_cache_fast_timer(ipv6_neighbour_cache_t *cache, uint1 extern void ipv6_neighbour_cache_slow_timer(ipv6_neighbour_cache_t *cache, uint8_t seconds); extern void ipv6_neighbour_cache_print(const ipv6_neighbour_cache_t *cache, route_print_fn_t *print_fn); extern void ipv6_router_gone(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry); - extern int8_t ipv6_neighbour_set_current_max_cache(uint16_t max_cache); +extern int8_t ipv6_destination_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime); +extern int8_t ipv6_neighbour_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime); /* Backwards compatibility with test app */ #define ROUTE_RPL_UP ROUTE_RPL_DIO diff --git a/source/libDHCPv6/dhcp_service_api.c b/source/libDHCPv6/dhcp_service_api.c index 3557a531a8..b784904dd8 100644 --- a/source/libDHCPv6/dhcp_service_api.c +++ b/source/libDHCPv6/dhcp_service_api.c @@ -441,7 +441,7 @@ void recv_dhcp_relay_msg(void *cb_res) } uint8_t gp_address[16]; //Get blobal address from interface - if (arm_net_address_get(sckt_data->interface_id, ADDR_IPV6_GP, gp_address) != 0) { + if (addr_interface_select_source(interface_ptr, gp_address, relay_srv->server_address, 0) != 0) { // No global prefix available tr_error("No GP address"); goto cleanup; @@ -609,13 +609,23 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address) { - relay_instance_t *realay_srv = dhcp_service_relay_find(instance); - if (realay_srv) { - realay_srv->relay_activated = true; - memcpy(realay_srv->server_address, server_address, 16); + relay_instance_t *relay_srv = dhcp_service_relay_find(instance); + if (relay_srv) { + relay_srv->relay_activated = true; + memcpy(relay_srv->server_address, server_address, 16); } } +uint8_t *dhcp_service_relay_global_addres_get(uint16_t instance) +{ + relay_instance_t *relay_srv = dhcp_service_relay_find(instance); + if (!relay_srv || !relay_srv->relay_activated) { + return NULL; + } + + return relay_srv->server_address; +} + void dhcp_service_delete(uint16_t instance) { server_instance_t *srv_ptr; @@ -739,6 +749,16 @@ void dhcp_service_set_retry_timers(uint32_t msg_tr_id, uint16_t timeout_init, ui return; } +void dhcp_service_update_server_address(uint32_t msg_tr_id, uint8_t *server_address) +{ + msg_tr_t *msg_tr_ptr; + msg_tr_ptr = dhcp_tr_find(msg_tr_id); + + if (msg_tr_ptr != NULL) { + memcpy(msg_tr_ptr->addr.address, server_address, 16); + } +} + void dhcp_service_req_remove(uint32_t msg_tr_id) { if (dhcp_service) { diff --git a/source/libDHCPv6/libDHCPv6.c b/source/libDHCPv6/libDHCPv6.c index 3bc2ec7844..53beaaadc3 100644 --- a/source/libDHCPv6/libDHCPv6.c +++ b/source/libDHCPv6/libDHCPv6.c @@ -208,7 +208,7 @@ dhcpv6_client_server_data_t *libdhcpv6_nonTemporal_entry_get_by_transactionId(ui dhcpv6_client_server_data_t *libdhcpv6_nonTemporal_entry_get_by_prefix(int8_t interfaceId, uint8_t *prefix) { ns_list_foreach(dhcpv6_client_server_data_t, cur, &dhcpv6_client_nonTemporal_list) { - if ((cur->interfaceId == interfaceId) && cur->iaNonTemporalStructValid) { + if ((cur->interfaceId == interfaceId)) { if (memcmp(cur->iaNontemporalAddress.addressPrefix, prefix, 8) == 0) { return cur; } @@ -697,7 +697,7 @@ int libdhcpv6_get_IA_address(uint8_t *ptr, uint16_t data_length, dhcp_ia_non_tem return -1; } -uint16_t libdhcpv6_address_request_message_len(uint16_t clientLinkType, uint16_t serverLinkType, uint8_t requstOptionCnt) +uint16_t libdhcpv6_address_request_message_len(uint16_t clientLinkType, uint16_t serverLinkType, uint8_t requstOptionCnt, bool add_address) { uint16_t length = 0; length += libdhcpv6_header_size(); @@ -706,7 +706,7 @@ uint16_t libdhcpv6_address_request_message_len(uint16_t clientLinkType, uint16_t length += libdhcpv6_duid_option_size(serverLinkType); length += libdhcvp6_request_option_size(requstOptionCnt); length += libdhcpv6_rapid_commit_option_size(); - length += libdhcpv6_non_temporal_address_size(true); + length += libdhcpv6_non_temporal_address_size(add_address); return length; } #ifdef HAVE_DHCPV6_SERVER diff --git a/source/libDHCPv6/libDHCPv6.h b/source/libDHCPv6/libDHCPv6.h index a32f46c1c0..7cbf1cdd7e 100644 --- a/source/libDHCPv6/libDHCPv6.h +++ b/source/libDHCPv6/libDHCPv6.h @@ -266,7 +266,7 @@ uint16_t libdhcvp6_request_option_size(uint8_t optionCnt); uint16_t libdhcpv6_non_temporal_address_size(bool addressDefined); uint16_t libdhcpv6_solication_message_length(uint16_t clientLinkType, bool addressDefined, uint8_t requestOptionCount); -uint16_t libdhcpv6_address_request_message_len(uint16_t clientLinkType, uint16_t serverLinkType, uint8_t requstOptionCnt); +uint16_t libdhcpv6_address_request_message_len(uint16_t clientLinkType, uint16_t serverLinkType, uint8_t requstOptionCnt, bool add_address); #ifdef HAVE_DHCPV6_SERVER uint16_t libdhcpv6_address_reply_message_len(uint16_t clientLinkType, uint16_t serverLinkType, uint16_t vendorDataLen, bool rapidCommon, bool status); #else diff --git a/source/libNET/src/net_ipv6.c b/source/libNET/src/net_ipv6.c index 3320a07924..164b387ec7 100644 --- a/source/libNET/src/net_ipv6.c +++ b/source/libNET/src/net_ipv6.c @@ -43,6 +43,16 @@ int8_t arm_nwk_ipv6_max_cache_entries(uint16_t max_entries) return ipv6_neighbour_set_current_max_cache(max_entries); } +int8_t arm_nwk_ipv6_destination_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime) +{ + return ipv6_destination_cache_configure(max_entries, short_term_threshold, long_term_threshold, lifetime); +} + +int8_t arm_nwk_ipv6_neighbour_cache_configure(uint16_t max_entries, uint16_t short_term_threshold, uint16_t long_term_threshold, uint16_t lifetime) +{ + return ipv6_neighbour_cache_configure(max_entries, short_term_threshold, long_term_threshold, lifetime); +} + void arm_nwk_ipv6_auto_flow_label(bool auto_flow_label) { ipv6_flow_auto_label = auto_flow_label; diff --git a/source/libNET/src/net_load_balance.c b/source/libNET/src/net_load_balance.c index 71d072b547..90f4960dff 100644 --- a/source/libNET/src/net_load_balance.c +++ b/source/libNET/src/net_load_balance.c @@ -268,7 +268,7 @@ void net_load_balance_internal_state_activate(protocol_interface_info_entry_t *i static int8_t net_load_balance_api_get_node_count_cb(void *lb_user, uint16_t *node_count) { protocol_interface_info_entry_t *interface_ptr = lb_user; - if (rpl_control_get_instance_dao_target_count(interface_ptr->rpl_domain, 1, NULL, node_count)) { + if (rpl_control_get_instance_dao_target_count(interface_ptr->rpl_domain, 1, NULL, NULL, node_count)) { return 0; } diff --git a/source/libNET/src/ns_net.c b/source/libNET/src/ns_net.c index 8f5c498917..f5296e64f0 100644 --- a/source/libNET/src/ns_net.c +++ b/source/libNET/src/ns_net.c @@ -946,26 +946,42 @@ int8_t arm_network_certificate_chain_set(const arm_certificate_chain_entry_s *ch int8_t arm_network_trusted_certificate_add(const arm_certificate_entry_s *cert) { +#ifdef HAVE_WS + return ws_pae_controller_trusted_certificate_add(cert); +#else (void) cert; return -1; +#endif } int8_t arm_network_trusted_certificate_remove(const arm_certificate_entry_s *cert) { +#ifdef HAVE_WS + return ws_pae_controller_trusted_certificate_remove(cert); +#else (void) cert; return -1; +#endif } int8_t arm_network_certificate_revocation_list_add(const arm_cert_revocation_list_entry_s *crl) { +#ifdef HAVE_WS + return ws_pae_controller_certificate_revocation_list_add(crl); +#else (void) crl; return -1; +#endif } int8_t arm_network_certificate_revocation_list_remove(const arm_cert_revocation_list_entry_s *crl) { +#ifdef HAVE_WS + return ws_pae_controller_certificate_revocation_list_remove(crl); +#else (void) crl; return -1; +#endif } /** diff --git a/sources.mk b/sources.mk index 4d0628b2f5..f71f088bdf 100644 --- a/sources.mk +++ b/sources.mk @@ -34,6 +34,7 @@ SRCS += \ source/6LoWPAN/ws/ws_pae_lib.c \ source/6LoWPAN/ws/ws_pae_nvm_data.c \ source/6LoWPAN/ws/ws_pae_nvm_store.c \ + source/6LoWPAN/ws/ws_pae_timers.c \ source/6LoWPAN/ws/ws_eapol_relay.c \ source/6LoWPAN/ws/ws_eapol_auth_relay.c \ source/6LoWPAN/ws/ws_eapol_relay_lib.c \