diff --git a/.gitignore b/.gitignore index bfdcfcbd4f..0730e7a544 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .settings/ -.yotta.json *.dep *.htm *.crf @@ -66,8 +65,7 @@ cscope.files !debugger.ini *Log.txt *.mpw -yotta_modules -yotta_targets build html site + diff --git a/Makefile b/Makefile index addfc4bff1..67d9d854d1 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ clean-extra: $(CLEANTESTDIRS) # 3. TBD: importing to mbed OS TARGET_DIR:=release -CONFIGURATIONS_TO_BUILD:=lowpan_border_router lowpan_host lowpan_router nanostack_full thread_border_router thread_router thread_end_device ethernet_host +CONFIGURATIONS_TO_BUILD:=lowpan_border_router lowpan_host lowpan_router nanostack_full thread_border_router thread_router thread_end_device ethernet_host ws_border_router ws_router TOOLCHAINS_TO_BUILD:=GCC ARM ARMC6 IAR CORES_TO_BUILD:=Cortex-M0 Cortex-M3 diff --git a/mbed_lib.json b/mbed_lib.json index ba94bfbc7d..6640b5e3ef 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -1,7 +1,25 @@ { "name": "nanostack", + "requires": ["nanostack-eventloop", "coap-service"], "config": { - "configuration": "nanostack_full" + "configuration": { + "help": "Build time configuration. Refer to Handbook for valid values. Default: full stack", + "value": "nanostack_full" + } }, - "macros": ["NS_USE_EXTERNAL_MBED_TLS"] + "macros": ["NS_USE_EXTERNAL_MBED_TLS"], + "target_overrides": { + "KW24D": { + "nanostack.configuration": "lowpan_router" + }, + "NCS36510": { + "nanostack.configuration": "lowpan_router" + }, + "TB_SENSE_12": { + "nanostack.configuration": "lowpan_router" + }, + "KW41Z": { + "nanostack.configuration": "lowpan_router" + } + } } diff --git a/nanostack/dhcp_service_api.h b/nanostack/dhcp_service_api.h index 992977a046..5bdce7b35d 100644 --- a/nanostack/dhcp_service_api.h +++ b/nanostack/dhcp_service_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/nanostack/ethernet_mac_api.h b/nanostack/ethernet_mac_api.h index bc5bd828bb..0e83cadfa1 100644 --- a/nanostack/ethernet_mac_api.h +++ b/nanostack/ethernet_mac_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/nanostack/mlme.h b/nanostack/mlme.h index dbe32537ae..5db31f7c3d 100644 --- a/nanostack/mlme.h +++ b/nanostack/mlme.h @@ -264,6 +264,7 @@ typedef enum { macAutoRequestKeyIndex = 0x7b, /* -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "nsdynmemLIB.h" #include "ns_trace.h" #include "NWK_INTERFACE/Include/protocol.h" diff --git a/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan.c b/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan.c index 683f3849b9..1a1d7a5545 100644 --- a/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan.c +++ b/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "eventOS_scheduler.h" #include "ns_trace.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/udp.h" #include "Common_Protocols/ipv6.h" @@ -373,6 +373,7 @@ void protocol_6lowpan_configure_core(protocol_interface_info_entry_t *cur) cur->ipv6_neighbour_cache.link_mtu = LOWPAN_MTU; #ifdef HAVE_6LOWPAN_ND cur->ipv6_neighbour_cache.send_nud_probes = nd_params.send_nud_probes; + cur->ipv6_neighbour_cache.probe_avoided_routers = nd_params.send_nud_probes; cur->iids_map_to_mac = nd_params.iids_map_to_mac; #endif cur->ip_multicast_as_mac_unicast_to_parent = false; diff --git a/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan_bootstrap.c b/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan_bootstrap.c index 1e0aff1179..5f3f17becc 100644 --- a/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan_bootstrap.c +++ b/source/6LoWPAN/Bootstraps/Generic/protocol_6lowpan_bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -1408,6 +1408,8 @@ static void lowpan_mle_receive_security_bypass_cb(int8_t interface_id, mle_messa pana_reset_client_session(); bootsrap_next_state_kick(ER_PANA_AUTH_ERROR, interface); } +#else + (void)mle_msg; #endif } diff --git a/source/6LoWPAN/Fragmentation/cipv6_fragmenter.c b/source/6LoWPAN/Fragmentation/cipv6_fragmenter.c index 1c9879ce5d..df2b4404e3 100644 --- a/source/6LoWPAN/Fragmentation/cipv6_fragmenter.c +++ b/source/6LoWPAN/Fragmentation/cipv6_fragmenter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +26,7 @@ #include "string.h" #include "ns_trace.h" #include "randLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "6LoWPAN/IPHC_Decode/cipv6.h" #include "6LoWPAN/Fragmentation/cipv6_fragmenter.h" #include "NWK_INTERFACE/Include/protocol.h" diff --git a/source/6LoWPAN/IPHC_Decode/iphc_compress.c b/source/6LoWPAN/IPHC_Decode/iphc_compress.c index 6ebee21c34..735955aca9 100644 --- a/source/6LoWPAN/IPHC_Decode/iphc_compress.c +++ b/source/6LoWPAN/IPHC_Decode/iphc_compress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/IPHC_Decode/iphc_decompress.c b/source/6LoWPAN/IPHC_Decode/iphc_decompress.c index c6c475b830..8b584297e1 100644 --- a/source/6LoWPAN/IPHC_Decode/iphc_decompress.c +++ b/source/6LoWPAN/IPHC_Decode/iphc_decompress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/MAC/beacon_handler.c b/source/6LoWPAN/MAC/beacon_handler.c index 1c595294fa..34c7b3c98b 100644 --- a/source/6LoWPAN/MAC/beacon_handler.c +++ b/source/6LoWPAN/MAC/beacon_handler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/MAC/beacon_handler.h b/source/6LoWPAN/MAC/beacon_handler.h index 664d613d68..c08680f60f 100644 --- a/source/6LoWPAN/MAC/beacon_handler.h +++ b/source/6LoWPAN/MAC/beacon_handler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/MAC/mac_helper.h b/source/6LoWPAN/MAC/mac_helper.h index dece92c5c7..8bc04f7e04 100644 --- a/source/6LoWPAN/MAC/mac_helper.h +++ b/source/6LoWPAN/MAC/mac_helper.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #define MAC_HELPER_H #include "mlme.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" struct channel_list_s; struct nwk_scan_params; diff --git a/source/6LoWPAN/MAC/mac_response_handler.c b/source/6LoWPAN/MAC/mac_response_handler.c index eb5bfcb2a2..1c89a92578 100644 --- a/source/6LoWPAN/MAC/mac_response_handler.c +++ b/source/6LoWPAN/MAC/mac_response_handler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,8 +22,8 @@ #include "NWK_INTERFACE/Include/protocol_abstract.h" #include "NWK_INTERFACE/Include/protocol_timer.h" #include "Service_Libs/mle_service/mle_service_api.h" -#include "Core/include/address.h" -#include "Core/include/socket.h" +#include "Core/include/ns_address_internal.h" +#include "Core/include/ns_socket.h" #include "6LoWPAN/Thread/thread_common.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/MAC/mac_helper.h" diff --git a/source/6LoWPAN/MAC/mac_response_handler.h b/source/6LoWPAN/MAC/mac_response_handler.h index 96e8e7f4b7..dc58042ee7 100644 --- a/source/6LoWPAN/MAC/mac_response_handler.h +++ b/source/6LoWPAN/MAC/mac_response_handler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/Mesh/mesh.c b/source/6LoWPAN/Mesh/mesh.c index 16c74e41ac..4cdbbb138c 100644 --- a/source/6LoWPAN/Mesh/mesh.c +++ b/source/6LoWPAN/Mesh/mesh.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,7 @@ #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_stats.h" #include "6LoWPAN/IPHC_Decode/cipv6.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "6LoWPAN/Mesh/mesh.h" #include "6LoWPAN/MAC/mac_helper.h" diff --git a/source/6LoWPAN/ND/nd_defines.h b/source/6LoWPAN/ND/nd_defines.h index ec23632c9d..6cc37bd17c 100644 --- a/source/6LoWPAN/ND/nd_defines.h +++ b/source/6LoWPAN/ND/nd_defines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ #ifndef ND_DEFINES_H_ #define ND_DEFINES_H_ -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "ns_list.h" #include "6LoWPAN/IPHC_Decode/lowpan_context.h" #include "Common_Protocols/icmpv6_prefix.h" diff --git a/source/6LoWPAN/ND/nd_router_object.c b/source/6LoWPAN/ND/nd_router_object.c index b6399d3a73..e53119bb89 100644 --- a/source/6LoWPAN/ND/nd_router_object.c +++ b/source/6LoWPAN/ND/nd_router_object.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -831,6 +831,8 @@ buffer_t *nd_dar_parse(buffer_t *buf, protocol_interface_info_entry_t *cur_inter } drop: +#else + (void)cur_interface; #endif return buffer_free(buf); @@ -845,7 +847,7 @@ static void nd_update_registration(protocol_interface_info_entry_t *cur_interfac neigh->lifetime = aro->lifetime * UINT32_C(60); ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, neigh, IP_NEIGHBOUR_STALE); /* Register with 2 seconds off the lifetime - don't want the NCE to expire before the route */ - ipv6_route_add(neigh->ip_address, 128, cur_interface->id, NULL, ROUTE_ARO, neigh->lifetime - 2, 0); + ipv6_route_add_metric(neigh->ip_address, 128, cur_interface->id, neigh->ip_address, ROUTE_ARO, NULL, 0, neigh->lifetime - 2, 32); /* We need to know peer is a host before publishing - this needs MLE. Not yet established * what to do without MLE - might need special external/non-external prioritisation at root. @@ -864,19 +866,19 @@ static void nd_update_registration(protocol_interface_info_entry_t *cur_interfac neigh->type = IP_NEIGHBOUR_TENTATIVE; neigh->lifetime = 2; ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, neigh, IP_NEIGHBOUR_STALE); - ipv6_route_add(neigh->ip_address, 128, cur_interface->id, NULL, ROUTE_ARO, 4, 0); + ipv6_route_add_metric(neigh->ip_address, 128, cur_interface->id, neigh->ip_address, ROUTE_ARO, NULL, 0, 4, 32); rpl_control_unpublish_address(protocol_6lowpan_rpl_domain, neigh->ip_address); } } void nd_remove_registration(protocol_interface_info_entry_t *cur_interface, addrtype_t ll_type, const uint8_t *ll_address) { - ns_list_foreach_safe(ipv6_neighbour_t, cur, &cur_interface->ipv6_neighbour_cache.list) { if ((cur->type == IP_NEIGHBOUR_REGISTERED || cur->type == IP_NEIGHBOUR_TENTATIVE) && ipv6_neighbour_ll_addr_match(cur, ll_type, ll_address)) { - ipv6_route_delete(cur->ip_address, 128, cur_interface->id, NULL, + + ipv6_route_delete(cur->ip_address, 128, cur_interface->id, cur->ip_address, ROUTE_ARO); ipv6_neighbour_entry_remove(&cur_interface->ipv6_neighbour_cache, cur); @@ -923,6 +925,12 @@ 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)) { + aro_out->present = true; + aro_out->status = ARO_FULL; + return true; + } /* We need to have entry in the Neighbour Cache */ ipv6_neighbour_t *neigh = ipv6_neighbour_lookup_or_create(&cur_interface->ipv6_neighbour_cache, src_addr); diff --git a/source/6LoWPAN/NVM/nwk_nvm.c b/source/6LoWPAN/NVM/nwk_nvm.c index d4badef577..1da43f28cf 100644 --- a/source/6LoWPAN/NVM/nwk_nvm.c +++ b/source/6LoWPAN/NVM/nwk_nvm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "string.h" #include "randLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/MAC/mac_helper.h" #include "6LoWPAN/NVM/nwk_nvm.h" diff --git a/source/6LoWPAN/Thread/thread_bbr_api.c b/source/6LoWPAN/Thread/thread_bbr_api.c index 1b02085e85..da53a45684 100644 --- a/source/6LoWPAN/Thread/thread_bbr_api.c +++ b/source/6LoWPAN/Thread/thread_bbr_api.c @@ -757,6 +757,7 @@ bool thread_bbr_routing_enabled(protocol_interface_info_entry_t *cur) void thread_bbr_network_data_update_notify(protocol_interface_info_entry_t *cur) { + (void)cur; thread_mdns_network_data_update_notify(); thread_extension_bbr_route_update(cur); } @@ -1000,6 +1001,7 @@ int thread_bbr_dua_entry_add(int8_t interface_id, const uint8_t *addr_data_ptr, // Route info autofreed route->info_autofree = true; } + route->lifetime = lifetime; // update lifetime also from old route map = route->info.info; memcpy(map->mleid_ptr, mleid_ptr, 8); map->last_contact_time = protocol_core_monotonic_time; diff --git a/source/6LoWPAN/Thread/thread_beacon.c b/source/6LoWPAN/Thread/thread_beacon.c index 56a6d2fa9d..115cf02134 100644 --- a/source/6LoWPAN/Thread/thread_beacon.c +++ b/source/6LoWPAN/Thread/thread_beacon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_beacon.h b/source/6LoWPAN/Thread/thread_beacon.h index 5c0ee94421..bd50f1a15a 100644 --- a/source/6LoWPAN/Thread/thread_beacon.h +++ b/source/6LoWPAN/Thread/thread_beacon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_bootstrap.c b/source/6LoWPAN/Thread/thread_bootstrap.c index ddcdca0c19..38e900c4c3 100644 --- a/source/6LoWPAN/Thread/thread_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_bootstrap.c @@ -902,6 +902,7 @@ void thread_interface_init(protocol_interface_info_entry_t *cur) cur->if_snoop = thread_nd_snoop; cur->if_icmp_handler = thread_nd_icmp_handler; cur->ipv6_neighbour_cache.send_nud_probes = false; + cur->ipv6_neighbour_cache.probe_avoided_routers = false; cur->ipv6_neighbour_cache.recv_addr_reg = true; cur->send_mld = false; cur->ip_multicast_as_mac_unicast_to_parent = true; @@ -1514,6 +1515,7 @@ int thread_bootstrap_reset(protocol_interface_info_entry_t *cur) cur->thread_info->thread_attached_state = THREAD_STATE_NETWORK_DISCOVER; } cur->ipv6_neighbour_cache.send_nud_probes = false; //Disable NUD probing + cur->ipv6_neighbour_cache.probe_avoided_routers = false; cur->ip_multicast_as_mac_unicast_to_parent = true; //Define Default Contexts if (cur->thread_info->thread_device_mode == THREAD_DEVICE_MODE_SLEEPY_END_DEVICE) { @@ -2726,7 +2728,7 @@ int thread_bootstrap_network_data_activate(protocol_interface_info_entry_t *cur) thread_border_router_network_data_update_notify(cur); thread_bbr_network_data_update_notify(cur); - thread_maintenance_timer_set(cur, THREAD_MAINTENANCE_TIMER_INTERVAL); + thread_maintenance_timer_set(cur); return 0; } @@ -2846,7 +2848,7 @@ void thread_bootstrap_network_prefixes_process(protocol_interface_info_entry_t * thread_addr_write_mesh_local_16(addr, curBorderRouter->routerID, cur->thread_info); /* Do not allow multiple DHCP solicits from one prefix => delete previous */ dhcp_client_global_address_delete(cur->id, NULL, curPrefix->servicesPrefix); - if (dhcp_client_get_global_address(cur->id, addr, curPrefix->servicesPrefix, cur->mac, thread_dhcp_client_gua_error_cb) == 0) { + if (dhcp_client_get_global_address(cur->id, addr, curPrefix->servicesPrefix, cur->mac, DHCPV6_DUID_HARDWARE_EUI64_TYPE, thread_dhcp_client_gua_error_cb) == 0) { tr_debug("GP Address Requested"); } } @@ -2856,8 +2858,8 @@ void thread_bootstrap_network_prefixes_process(protocol_interface_info_entry_t * if ((cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST || cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST) && cur->thread_info->requestFullNetworkData == false) { + tr_debug("Invalidate router ID: %04x", curBorderRouter->routerID); curBorderRouter->routerID = 0xfffe; - tr_debug("Invalidated router ID: %04x", curBorderRouter->routerID); } } @@ -2868,7 +2870,9 @@ void thread_bootstrap_network_prefixes_process(protocol_interface_info_entry_t * } // generate address based on res1 bit if (curBorderRouter->P_res1) { - thread_extension_dua_address_generate(cur, curPrefix->servicesPrefix, 64); + if (!thread_dhcpv6_address_entry_available(curPrefix->servicesPrefix, &cur->ip_addresses)) { + thread_extension_dua_address_generate(cur, curPrefix->servicesPrefix, 64); + } } } // for each borderRouterList diff --git a/source/6LoWPAN/Thread/thread_border_router_api_internal.h b/source/6LoWPAN/Thread/thread_border_router_api_internal.h index c1227173dd..53db69b22b 100644 --- a/source/6LoWPAN/Thread/thread_border_router_api_internal.h +++ b/source/6LoWPAN/Thread/thread_border_router_api_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_commissioning_api.c b/source/6LoWPAN/Thread/thread_commissioning_api.c index fd339dc755..2b498bb151 100644 --- a/source/6LoWPAN/Thread/thread_commissioning_api.c +++ b/source/6LoWPAN/Thread/thread_commissioning_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_commissioning_if.c b/source/6LoWPAN/Thread/thread_commissioning_if.c index fc11217f7a..af327f6cb3 100644 --- a/source/6LoWPAN/Thread/thread_commissioning_if.c +++ b/source/6LoWPAN/Thread/thread_commissioning_if.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_common.c b/source/6LoWPAN/Thread/thread_common.c index c87e55d839..c1babfdd04 100644 --- a/source/6LoWPAN/Thread/thread_common.c +++ b/source/6LoWPAN/Thread/thread_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 2017-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2015, 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -88,7 +88,7 @@ #include "6LoWPAN/MAC/mac_pairwise_key.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "Service_Libs/etx/etx.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "6LoWPAN/Thread/thread_nvm_store.h" #define TRACE_GROUP "thrd" @@ -484,7 +484,9 @@ int thread_info_allocate_and_init(protocol_interface_info_entry_t *cur) cur->thread_info->version = thread_version; // Default implementation version cur->thread_info->thread_device_mode = THREAD_DEVICE_MODE_END_DEVICE; cur->thread_info->childUpdateReqTimer = -1; + cur->thread_info->parent_priority = CONNECTIVITY_PP_INVALID; // default invalid - calculated using child count + thread_maintenance_timer_set(cur); thread_routing_init(&cur->thread_info->routing); thread_network_local_server_data_base_init(&cur->thread_info->localServerDataBase); memset(&cur->thread_info->registered_commissioner, 0, sizeof(thread_commissioner_t)); @@ -961,7 +963,9 @@ static void thread_maintenance_timer(protocol_interface_info_entry_t *cur, uint3 } } - thread_info(cur)->thread_maintenance_timer = THREAD_MAINTENANCE_TIMER_INTERVAL ; + thread_maintenance_timer_set(cur); + + tr_debug("NWK data maintenance scan"); thread_bootstrap_network_data_activate(cur); } @@ -1245,7 +1249,9 @@ uint8_t *thread_connectivity_tlv_write(uint8_t *ptr, protocol_interface_info_ent *ptr++ = 10; // determine parent priority - if ((mode & MLE_DEV_MASK) == MLE_RFD_DEV && (3 * mle_class_rfd_entry_count_get(cur) > 2 * THREAD_MAX_MTD_CHILDREN)) { + if ((thread->parent_priority & CONNECTIVITY_PP_MASK) != CONNECTIVITY_PP_INVALID) { + *ptr++ = thread->parent_priority & CONNECTIVITY_PP_MASK; + } else if ((mode & MLE_DEV_MASK) == MLE_RFD_DEV && (3 * mle_class_rfd_entry_count_get(cur) > 2 * THREAD_MAX_MTD_CHILDREN)) { *ptr++ = CONNECTIVITY_PP_LOW; } else if (!(mode & MLE_RX_ON_IDLE) && (3 * mle_class_sleepy_entry_count_get(cur) > 2 * THREAD_MAX_SED_CHILDREN)) { *ptr++ = CONNECTIVITY_PP_LOW; @@ -2155,9 +2161,9 @@ void thread_neighbor_communication_update(protocol_interface_info_entry_t *cur, thread_neighbor_last_communication_time_update(&cur->thread_info->neighbor_class, neighbor_attribute_index); } -void thread_maintenance_timer_set(protocol_interface_info_entry_t *cur, uint16_t delay) +void thread_maintenance_timer_set(protocol_interface_info_entry_t *cur) { - thread_info(cur)->thread_maintenance_timer = delay; + thread_info(cur)->thread_maintenance_timer = THREAD_MAINTENANCE_TIMER_INTERVAL + randLIB_get_random_in_range(0, THREAD_MAINTENANCE_TIMER_INTERVAL / 10); } #endif diff --git a/source/6LoWPAN/Thread/thread_common.h b/source/6LoWPAN/Thread/thread_common.h index 3c3a798346..62053a5597 100644 --- a/source/6LoWPAN/Thread/thread_common.h +++ b/source/6LoWPAN/Thread/thread_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -68,6 +68,7 @@ struct mac_neighbor_table_entry; #define THREAD_KEY_INDEX(seq) ((uint8_t) (((seq) & 0x0000007f) + 1)) extern uint8_t thread_version; +extern uint8_t thread_max_mcast_addr; extern uint32_t thread_delay_timer_default; extern uint32_t thread_router_selection_jitter; extern uint16_t thread_joiner_port; @@ -316,6 +317,7 @@ typedef struct thread_info_s { //uint8_t lastValidRouteMask[8]; int8_t interface_id; //Thread Interface ID uint8_t version; + uint8_t parent_priority; uint8_t testMaxActiveRouterIdLimit; //Default for this is 32 uint8_t maxChildCount; //Default for this is 24 uint8_t partition_weighting; @@ -448,7 +450,7 @@ bool thread_partition_match(protocol_interface_info_entry_t *cur, thread_leader_ void thread_partition_info_update(protocol_interface_info_entry_t *cur, thread_leader_data_t *leaderData); void thread_neighbor_communication_update(protocol_interface_info_entry_t *cur, uint8_t neighbor_attribute_index); bool thread_stable_context_check(protocol_interface_info_entry_t *cur, buffer_t *buf); -void thread_maintenance_timer_set(protocol_interface_info_entry_t *cur, uint16_t delay); +void thread_maintenance_timer_set(protocol_interface_info_entry_t *cur); #else // HAVE_THREAD NS_DUMMY_DEFINITIONS_OK diff --git a/source/6LoWPAN/Thread/thread_config.h b/source/6LoWPAN/Thread/thread_config.h index 6952684196..a4542f01cf 100644 --- a/source/6LoWPAN/Thread/thread_config.h +++ b/source/6LoWPAN/Thread/thread_config.h @@ -247,7 +247,7 @@ * value for better performance. */ #define THREAD_INDIRECT_BIG_PACKETS_TOTAL 10 -#define THREAD_INDIRECT_SMALL_PACKETS_PER_CHILD 2 +#define THREAD_INDIRECT_SMALL_PACKETS_PER_CHILD 3 /** * Maximum number of MTD children, default 16 diff --git a/source/6LoWPAN/Thread/thread_constants.h b/source/6LoWPAN/Thread/thread_constants.h index 971a1fa1c9..1fdc2aadfd 100644 --- a/source/6LoWPAN/Thread/thread_constants.h +++ b/source/6LoWPAN/Thread/thread_constants.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -186,4 +186,6 @@ /** Thread prefix minimum lifetime in seconds */ #define THREAD_MIN_PREFIX_LIFETIME 3600 +#define THREAD_MCAST_ADDR_PER_MSG 4 // One multicast registration message fits 4 addresses by default + #endif /* THREAD_CONSTANTS_H_ */ diff --git a/source/6LoWPAN/Thread/thread_dhcpv6_server.c b/source/6LoWPAN/Thread/thread_dhcpv6_server.c index 3795fb6263..c01e385bb0 100644 --- a/source/6LoWPAN/Thread/thread_dhcpv6_server.c +++ b/source/6LoWPAN/Thread/thread_dhcpv6_server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Arm Limited and affiliates. + * Copyright (c) 2015, 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_extension.h b/source/6LoWPAN/Thread/thread_extension.h index b6f64d1e90..2e9fd3d41f 100644 --- a/source/6LoWPAN/Thread/thread_extension.h +++ b/source/6LoWPAN/Thread/thread_extension.h @@ -85,7 +85,7 @@ uint8_t *thread_extension_discover_response_write(protocol_interface_info_entry_ #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) -#define thread_extension_service_init(cur) (0) +#define thread_extension_service_init(cur) ((void) 0) #define thread_extension_joining_enabled(interface_id) (false) #define thread_extension_discover_response_len(cur) (0) #define thread_extension_discover_response_write(cur, ptr) (ptr) diff --git a/source/6LoWPAN/Thread/thread_extension_bootstrap.h b/source/6LoWPAN/Thread/thread_extension_bootstrap.h index e6132ccdd9..a03eef1848 100644 --- a/source/6LoWPAN/Thread/thread_extension_bootstrap.h +++ b/source/6LoWPAN/Thread/thread_extension_bootstrap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_host_bootstrap.c b/source/6LoWPAN/Thread/thread_host_bootstrap.c index 7532bc54f0..7685f6454c 100644 --- a/source/6LoWPAN/Thread/thread_host_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_host_bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -73,7 +73,7 @@ #include "Service_Libs/blacklist/blacklist.h" #include "6LoWPAN/MAC/mac_helper.h" #include "6LoWPAN/MAC/mac_data_poll.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #define TRACE_GROUP "tebs" @@ -470,8 +470,9 @@ static bool thread_host_prefer_parent_response(protocol_interface_info_entry_t * { (void) connectivity; (void) cur; + bool cur_version = thread_extension_version_check(thread_info(cur)->version); - if (!thread_extension_version_check(thread_info(cur)->version)) { + if (!cur_version) { return false; } diff --git a/source/6LoWPAN/Thread/thread_leader_service.c b/source/6LoWPAN/Thread/thread_leader_service.c index 5cfed0eccd..28b6282e70 100644 --- a/source/6LoWPAN/Thread/thread_leader_service.c +++ b/source/6LoWPAN/Thread/thread_leader_service.c @@ -393,11 +393,6 @@ static int thread_leader_service_active_set_cb(int8_t service_id, uint8_t source response_ptr = payload; - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - goto send_error_response; - } - if (3 <= thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_CHANNEL, &ptr) && (linkConfiguration->rfChannel != common_read_16_bit(&ptr[1]) || linkConfiguration->channel_page != *ptr)) { tr_debug("Channel changed"); @@ -559,11 +554,6 @@ static int thread_leader_service_pending_set_cb(int8_t service_id, uint8_t sourc tr_info("thread management Pending set"); - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - goto send_error_response; - } - if (2 <= thread_meshcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_COMMISSIONER_SESSION_ID, &session_id)) { // Session id present must be valid if (cur->thread_info->registered_commissioner.session_id != session_id) { @@ -660,7 +650,6 @@ send_error_response: static int thread_leader_service_commissioner_set_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { thread_leader_service_t *this = thread_leader_service_find_by_service(service_id); - sn_coap_msg_code_e response_code = COAP_MSG_CODE_RESPONSE_CHANGED; uint16_t session_id; uint16_t br_locator; uint8_t payload[5]; // 4 + 1 @@ -682,10 +671,6 @@ static int thread_leader_service_commissioner_set_cb(int8_t service_id, uint8_t tr_info("thread management commissioner set"); - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - goto send_error_response; - } //Check if the CoAp payload is greater than maximum commissioner data size and reject if (request_ptr->payload_len > THREAD_MAX_COMMISSIONER_DATA_SIZE) { tr_error("Payload length greater than maximum commissioner data size"); @@ -725,9 +710,7 @@ static int thread_leader_service_commissioner_set_cb(int8_t service_id, uint8_t send_response: // build response ptr = thread_meshcop_tlv_data_write_uint8(ptr, MESHCOP_TLV_STATE, ret == 0 ? 1 : 0xff); - -send_error_response: - coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, response_code, COAP_CT_OCTET_STREAM, payload, ptr - payload); + coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload, ptr - payload); return 0; } @@ -1050,7 +1033,6 @@ static int thread_leader_service_release_cb(int8_t service_id, uint8_t source_ad static int thread_leader_service_petition_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { thread_leader_service_t *this = thread_leader_service_find_by_service(service_id); - sn_coap_msg_code_e response_code = COAP_MSG_CODE_RESPONSE_CHANGED; uint8_t payload[79];// max length for commissioner id is 64 + 4 byte header + 4 + 1 + 4 + 2 uint8_t *ptr; uint16_t session_id = 0; @@ -1067,11 +1049,6 @@ static int thread_leader_service_petition_cb(int8_t service_id, uint8_t source_a tr_debug("Thread management commissioner petition"); - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - goto send_error_response; - } - // save values from message tlv_length = thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_COMMISSIONER_ID, &tlv_data_ptr); @@ -1098,8 +1075,7 @@ static int thread_leader_service_petition_cb(int8_t service_id, uint8_t source_a tr_debug("Petition req recv id %s, RESP session id: %d ret %d", commissioner_id_ptr ? commissioner_id_ptr : "(none)", session_id, ret); -send_error_response: - coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, response_code, COAP_CT_OCTET_STREAM, payload, ptr - payload); + coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload, ptr - payload); return 0; } @@ -1110,7 +1086,6 @@ send_error_response: static int thread_leader_service_petition_ka_cb(int8_t service_id, uint8_t source_address[16], uint16_t source_port, sn_coap_hdr_s *request_ptr) { thread_leader_service_t *this = thread_leader_service_find_by_service(service_id); - sn_coap_msg_code_e response_code = COAP_MSG_CODE_RESPONSE_CHANGED; uint8_t payload[5]; //status 4 + 1 uint8_t *ptr; uint16_t session_id = 0; @@ -1125,12 +1100,6 @@ static int thread_leader_service_petition_ka_cb(int8_t service_id, uint8_t sourc tr_debug("Thread management commissioner keep alive"); - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - ptr = payload; - goto send_error_response; - } - if (2 <= thread_meshcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, MESHCOP_TLV_COMMISSIONER_SESSION_ID, &ptr)) { session_id = common_read_16_bit(ptr); } @@ -1149,8 +1118,7 @@ static int thread_leader_service_petition_ka_cb(int8_t service_id, uint8_t sourc ptr = payload; ptr = thread_meshcop_tlv_data_write_uint8(ptr, MESHCOP_TLV_STATE, state == true ? 1 : 0xff); -send_error_response: - coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, response_code, COAP_CT_OCTET_STREAM, payload, ptr - payload); + coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload, ptr - payload); return 0; } diff --git a/source/6LoWPAN/Thread/thread_lowpower_private_api.c b/source/6LoWPAN/Thread/thread_lowpower_private_api.c index 48ab2b0b31..cf3e8cf04f 100644 --- a/source/6LoWPAN/Thread/thread_lowpower_private_api.c +++ b/source/6LoWPAN/Thread/thread_lowpower_private_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_management_api.c b/source/6LoWPAN/Thread/thread_management_api.c index 69ba03a5d0..60e02e3aee 100644 --- a/source/6LoWPAN/Thread/thread_management_api.c +++ b/source/6LoWPAN/Thread/thread_management_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_management_if.c b/source/6LoWPAN/Thread/thread_management_if.c index a1497f63f9..6398816844 100644 --- a/source/6LoWPAN/Thread/thread_management_if.c +++ b/source/6LoWPAN/Thread/thread_management_if.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -94,6 +94,7 @@ static const uint8_t thread_discovery_extented_address[8] = {0x35, 0x06, 0xfe, 0 uint32_t thread_delay_timer_default = THREAD_DELAY_TIMER_DEFAULT_SECONDS; uint32_t thread_router_selection_jitter = THREAD_ROUTER_SELECTION_JITTER; uint16_t thread_joiner_port = THREAD_DEFAULT_JOINER_PORT; +uint8_t thread_max_mcast_addr = THREAD_MCAST_ADDR_PER_MSG; /* * Prototypes @@ -1345,6 +1346,11 @@ int8_t thread_management_get_request_full_nwk_data(int8_t interface_id, bool *fu int thread_management_device_certificate_set(int8_t interface_id, const unsigned char *device_certificate_ptr, uint16_t device_certificate_len, const unsigned char *priv_key_ptr, uint16_t priv_key_len) { + (void) device_certificate_ptr; + (void) device_certificate_len; + (void) priv_key_ptr; + (void) priv_key_len; + #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; @@ -1358,17 +1364,19 @@ int thread_management_device_certificate_set(int8_t interface_id, const unsigned #else (void) interface_id; - (void) device_certificate_ptr; - (void) device_certificate_len; - (void) priv_key_ptr; - (void) priv_key_len; return -1; #endif } int thread_management_network_certificate_set(int8_t interface_id, const unsigned char *network_certificate_ptr, uint16_t network_certificate_len, const unsigned char *priv_key_ptr, uint16_t priv_key_len) { + (void) network_certificate_ptr; + (void) network_certificate_len; + (void) priv_key_ptr; + (void) priv_key_len; + #ifdef HAVE_THREAD protocol_interface_info_entry_t *cur; + int ret_val; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { @@ -1376,17 +1384,14 @@ int thread_management_network_certificate_set(int8_t interface_id, const unsigne return -1; } - if (0 > thread_extension_bootstrap_network_certificate_set(cur, network_certificate_ptr, network_certificate_len)) { + ret_val = thread_extension_bootstrap_network_certificate_set(cur, network_certificate_ptr, network_certificate_len); + if (0 > ret_val) { return -1; } return thread_extension_bootstrap_network_private_key_set(cur, priv_key_ptr, priv_key_len); #else (void) interface_id; - (void) network_certificate_ptr; - (void) network_certificate_len; - (void) priv_key_ptr; - (void) priv_key_len; return -1; #endif } diff --git a/source/6LoWPAN/Thread/thread_management_server.c b/source/6LoWPAN/Thread/thread_management_server.c index 25e9d33f10..c14f2475ec 100644 --- a/source/6LoWPAN/Thread/thread_management_server.c +++ b/source/6LoWPAN/Thread/thread_management_server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -66,10 +66,19 @@ #include "thread_management_server.h" #include "mac_api.h" #include "6LoWPAN/MAC/mac_data_poll.h" +#include "Common_Protocols/ipv6_constants.h" +#include "Core/include/ns_address_internal.h" #include "mlme.h" #ifdef HAVE_THREAD +//#define TRACE_DEEP +#ifdef TRACE_DEEP +#define tr_deep tr_debug +#else +#define tr_deep(...) +#endif + typedef struct scan_query { int8_t coap_service_id; uint8_t channel_mask[6]; //interface_id, source_address)) { - // request is coming from illegal address, return error immediately - coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_BAD_REQUEST, COAP_CT_OCTET_STREAM, NULL, 0); - return 0; - } - return thread_management_server_tmf_get_request_handler(this->interface_id, service_id, request_ptr); } @@ -416,7 +420,6 @@ static int thread_management_server_commissioner_get_cb(int8_t service_id, uint8 (void) source_port; protocol_interface_info_entry_t *cur; thread_management_server_t *this = thread_management_find_by_service(service_id); - sn_coap_msg_code_e return_code = COAP_MSG_CODE_RESPONSE_CHANGED; uint8_t response_msg[2 + 2 + 2 + 2 + 2 + 16 + 2 + 2]; uint8_t *request_tlv_ptr = NULL; uint16_t request_tlv_len; @@ -432,11 +435,6 @@ static int thread_management_server_commissioner_get_cb(int8_t service_id, uint8 } payload_ptr = ptr = response_msg; - if (!thread_management_server_source_address_check(this->interface_id, source_address)) { - return_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; - goto send_response; - } - if (!cur->thread_info->registered_commissioner.commissioner_valid) { //Error in message is responded with Thread status or if we have access rights problem tr_warn("No registered commissioner"); @@ -464,7 +462,7 @@ static int thread_management_server_commissioner_get_cb(int8_t service_id, uint8 goto send_response; } send_response: - coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, return_code, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr); + coap_service_response_send(this->coap_service_id, COAP_REQUEST_OPTIONS_NONE, request_ptr, COAP_MSG_CODE_RESPONSE_CHANGED, COAP_CT_OCTET_STREAM, payload_ptr, ptr - payload_ptr); return 0; } @@ -1118,6 +1116,58 @@ error_exit: return 0; } +static int coap_msg_prevalidate_cb(int8_t local_interface_id, uint8_t local_address[static 16], uint16_t local_port, int8_t recv_interface_id, uint8_t source_address[static 16], uint16_t source_port, char *coap_uri) +{ + protocol_interface_info_entry_t *cur_local, *cur_source; + uint_fast8_t addr_scope; + + (void) source_address; + (void) source_port; + (void) coap_uri; + + cur_local = protocol_stack_interface_info_get_by_id(local_interface_id); + + if (!cur_local) { + tr_error("No interface for %d", local_interface_id); + return -1; + } + + if (local_port != THREAD_MANAGEMENT_PORT) { + // Message not sent to THREAD_MANAGEMENT_PORT, let it come through + tr_deep("Message %s port %d is not mgmt port", coap_uri, local_port); + return 0; + } + + // check message source address + if (!thread_management_server_source_address_check(local_interface_id, source_address)) { + tr_deep("Drop CoAP msg %s from %s", coap_uri, trace_ipv6(source_address)); + return 3; + } + + /* check our local address scope */ + addr_scope = addr_ipv6_scope(local_address, cur_local); + if (addr_scope > IPV6_SCOPE_REALM_LOCAL) { + tr_deep("Drop CoAP msg %s to %s due %d", coap_uri, trace_ipv6(local_address), addr_scope); + return 1; + } + + if (local_interface_id != recv_interface_id) { + // message received from different interface + cur_source = protocol_stack_interface_info_get_by_id(recv_interface_id); + if (!cur_source) { + tr_deep("No cur for if %d", recv_interface_id); + return -1; + } + addr_scope = addr_ipv6_scope(source_address, cur_source); + if (addr_scope < IPV6_SCOPE_REALM_LOCAL) { + tr_deep("Drop CoAP msg %s from %s to %s due %d", coap_uri, trace_ipv6(source_address), trace_ipv6(local_address), addr_scope); + return 2; + } + } + + return 0; +} + /** * Public interface functions */ @@ -1162,6 +1212,7 @@ int thread_management_server_init(int8_t interface_id) ns_dyn_mem_free(this); return -3; } + coap_service_msg_prevalidate_callback_set(THREAD_MANAGEMENT_PORT, coap_msg_prevalidate_cb); #ifdef HAVE_THREAD_ROUTER if (thread_leader_service_init(interface_id, this->coap_service_id) != 0) { tr_error("Thread leader service init failed"); @@ -1554,10 +1605,15 @@ int thread_management_server_commisoner_data_get(int8_t interface_id, thread_man bool thread_management_server_source_address_check(int8_t interface_id, uint8_t source_address[16]) { link_configuration_s *linkConfiguration; - linkConfiguration = thread_joiner_application_get_config(interface_id); + if (memcmp(ADDR_LINK_LOCAL_PREFIX, source_address, 8) == 0) { + // Source address is from Link local address + return true; + } + + linkConfiguration = thread_joiner_application_get_config(interface_id); if (!linkConfiguration) { - tr_error("No link configuration."); + tr_error("No link cfg for if %d", interface_id); return false; } @@ -1566,15 +1622,12 @@ bool thread_management_server_source_address_check(int8_t interface_id, uint8_t // Source address is RLOC or ALOC } else if (memcmp(source_address, linkConfiguration->mesh_local_ula_prefix, 8) == 0) { // Source address is ML64 TODO this should check that destination address is ALOC or RLOC CoaP Service does not support - } else if (memcmp(ADDR_LINK_LOCAL_PREFIX, source_address, 8)) { - // Source address is from Link local address } else { - tr_error("Message out of thread network; ML prefix: %s, src addr: %s", - trace_ipv6_prefix(linkConfiguration->mesh_local_ula_prefix, 64), - trace_ipv6(source_address)); + tr_deep("Message out of thread network; ML prefix: %s, src addr: %s", + trace_ipv6_prefix(linkConfiguration->mesh_local_ula_prefix, 64), + trace_ipv6(source_address)); return false; } - // TODO: Add other (security) related checks here return true; } diff --git a/source/6LoWPAN/Thread/thread_management_server.h b/source/6LoWPAN/Thread/thread_management_server.h index b7ca9fb5c1..f083a01221 100644 --- a/source/6LoWPAN/Thread/thread_management_server.h +++ b/source/6LoWPAN/Thread/thread_management_server.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_mdns.c b/source/6LoWPAN/Thread/thread_mdns.c index e90f53bc16..168bff5e81 100644 --- a/source/6LoWPAN/Thread/thread_mdns.c +++ b/source/6LoWPAN/Thread/thread_mdns.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_meshcop_lib.c b/source/6LoWPAN/Thread/thread_meshcop_lib.c index ed9cba0ae7..09a70ce682 100644 --- a/source/6LoWPAN/Thread/thread_meshcop_lib.c +++ b/source/6LoWPAN/Thread/thread_meshcop_lib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without diff --git a/source/6LoWPAN/Thread/thread_mle_message_handler.c b/source/6LoWPAN/Thread/thread_mle_message_handler.c index a6530d4ba1..c88e5acb93 100644 --- a/source/6LoWPAN/Thread/thread_mle_message_handler.c +++ b/source/6LoWPAN/Thread/thread_mle_message_handler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -759,7 +759,7 @@ static void thread_host_child_update_request_process(protocol_interface_info_ent uint64_t pending_timestamp = 0;// means no pending timestamp mac_neighbor_table_entry_t *entry_temp; bool data_request_needed = false; - mle_tlv_info_t tlv_info = {0}; + mle_tlv_info_t tlv_info = {MLE_TYPE_SRC_ADDRESS, 0, 0}; tr_debug("Child update request"); entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); @@ -831,7 +831,7 @@ static void thread_parse_child_update_response(protocol_interface_info_entry_t * thread_leader_data_t leaderData = {0}; uint8_t status; bool leader_data_received; - mle_tlv_info_t tlv_info = {0}; + mle_tlv_info_t tlv_info = {MLE_TYPE_SRC_ADDRESS, 0, 0}; if (cur->thread_info->thread_endnode_parent == NULL) { return; diff --git a/source/6LoWPAN/Thread/thread_nd.c b/source/6LoWPAN/Thread/thread_nd.c index 82b8f97647..f25da1ca09 100644 --- a/source/6LoWPAN/Thread/thread_nd.c +++ b/source/6LoWPAN/Thread/thread_nd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -42,7 +42,7 @@ #include "eventOS_event.h" #include "common_functions.h" #include "socket_api.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ipv6.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" @@ -213,6 +213,7 @@ static mac_neighbor_table_entry_t *thread_nd_child_mleid_get(protocol_interface_ static int thread_nd_address_query_lookup(int8_t interface_id, const uint8_t target_addr[static 16], uint16_t *rloc, uint16_t *addr_out, bool *proxy, uint32_t *last_transaction_time, uint8_t *mleid_ptr) { + (void) rloc; protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; diff --git a/source/6LoWPAN/Thread/thread_network_data_storage.c b/source/6LoWPAN/Thread/thread_network_data_storage.c index 16b9c58fb6..4936f1982b 100755 --- a/source/6LoWPAN/Thread/thread_network_data_storage.c +++ b/source/6LoWPAN/Thread/thread_network_data_storage.c @@ -1565,14 +1565,12 @@ int thread_nd_local_list_add_service(thread_network_data_cache_entry_t *networkD */ int thread_nd_local_list_add_on_mesh_prefix(thread_network_data_cache_entry_t *networkDataList, thread_prefix_tlv_t *prefixTlv, thread_border_router_tlv_entry_t *service) { - bool trigDataPropagate = false; thread_network_data_prefix_cache_entry_t *prefix_entry; thread_network_server_data_entry_t *server_entry; - if (service->P_dhcp) { - tr_debug("Add DHCPv6 prefix:%s server: %04x", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen), service->routerID); - } else { - tr_debug("Add SLAAC prefix:%s server: %04x", trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen), service->routerID); - } + bool trigDataPropagate = false; + + tr_debug("Add %s%s%s prefix:%s server: %04x", service->P_dhcp ? "DHCPv6" : "", service->P_slaac ? "SLAAC " : "", service->P_res1 ? "P_res1 " : "", + trace_ipv6_prefix(prefixTlv->Prefix, prefixTlv->PrefixLen), service->routerID); if (!networkDataList) { return -1; @@ -1634,6 +1632,10 @@ int thread_nd_local_list_add_on_mesh_prefix(thread_network_data_cache_entry_t *n trigDataPropagate = true; } + if (server_entry->P_res1 != service->P_res1) { + server_entry->P_res1 = service->P_res1; + trigDataPropagate = true; + } } if (trigDataPropagate) { @@ -1647,7 +1649,6 @@ int thread_nd_local_list_add_on_mesh_prefix(thread_network_data_cache_entry_t *n return 0; } - /** * Del DHCPv6 Server information to route List * @@ -1671,13 +1672,8 @@ int thread_nd_local_list_del_on_mesh_server(thread_network_data_cache_entry_t *n return -1; } - if (service->P_dhcp) { - tr_debug("Del DHCPv6 server: %s", - trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen))); - } else { - tr_debug("Del SLAAC server: %s", - trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen))); - } + tr_debug("Del %s%s%s prefix: %s", service->P_dhcp ? "DHCPv6" : "", service->P_slaac ? "SLAAC " : "", service->P_res1 ? "P_res1 " : "", + trace_array(prefixTlv->Prefix, prefixBits_to_bytes(prefixTlv->PrefixLen))); main_list = thread_prefix_entry_get(&networkDataList->localPrefixList, prefixTlv); if (!main_list) { diff --git a/source/6LoWPAN/Thread/thread_nvm_store.c b/source/6LoWPAN/Thread/thread_nvm_store.c index ba85a26511..683d2f07cb 100644 --- a/source/6LoWPAN/Thread/thread_nvm_store.c +++ b/source/6LoWPAN/Thread/thread_nvm_store.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -35,7 +35,7 @@ #include #include -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "ns_file_system.h" #include "thread_config.h" #include "thread_common.h" diff --git a/source/6LoWPAN/Thread/thread_resolution_client.c b/source/6LoWPAN/Thread/thread_resolution_client.c index be83ddb2af..79740a0296 100644 --- a/source/6LoWPAN/Thread/thread_resolution_client.c +++ b/source/6LoWPAN/Thread/thread_resolution_client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -33,7 +33,7 @@ #include "ns_list.h" #include "ns_trace.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "thread_tmfcop_lib.h" #include "coap_service_api.h" @@ -293,9 +293,14 @@ void thread_resolution_client_init(int8_t interface_id) this->interface_id = interface_id; this->notification_cb_ptr = NULL; this->error_cb_ptr = NULL; - ns_list_init(&this->queries); //TODO: Check if to use ephemeral port here this->coap_service_id = thread_management_server_service_id_get(interface_id); + if (this->coap_service_id < 0) { + tr_err("Thread resolution client init failed"); + ns_dyn_mem_free(this); + return; + } + ns_list_init(&this->queries); ns_list_add_to_start(&instance_list, this); coap_service_register_uri(this->coap_service_id, THREAD_URI_ADDRESS_NOTIFICATION, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_resolution_client_notification_post_cb); diff --git a/source/6LoWPAN/Thread/thread_resolution_server.c b/source/6LoWPAN/Thread/thread_resolution_server.c index e20541eb2b..bbf2821eff 100644 --- a/source/6LoWPAN/Thread/thread_resolution_server.c +++ b/source/6LoWPAN/Thread/thread_resolution_server.c @@ -173,7 +173,7 @@ int thread_resolution_server_init(int8_t interface_id, thread_resolution_server_ this->interface_id = interface_id; this->coap_service_id = thread_management_server_service_id_get(interface_id); if (this->coap_service_id < 0) { - tr_warn("Thread resolution srv init failed"); + tr_err("Thread resolution srv init failed"); ns_dyn_mem_free(this); return -3; } diff --git a/source/6LoWPAN/Thread/thread_router_bootstrap.c b/source/6LoWPAN/Thread/thread_router_bootstrap.c index 6b31791f1a..f1399a7298 100644 --- a/source/6LoWPAN/Thread/thread_router_bootstrap.c +++ b/source/6LoWPAN/Thread/thread_router_bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -83,7 +83,7 @@ #include "mac_api.h" #include "6LoWPAN/MAC/mac_data_poll.h" #include "thread_border_router_api.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" #ifdef HAVE_THREAD_ROUTER @@ -1407,6 +1407,7 @@ static void thread_address_registration_tlv_parse(uint8_t *ptr, uint16_t data_le //Register GP --> 16 int retVal = thread_nd_address_registration(cur, tempIPv6Address, mac16, cur->mac_parameters->pan_id, mac64, &new_neighbour_created); thread_extension_address_registration(cur, tempIPv6Address, mac64, new_neighbour_created, retVal == -2); + (void) retVal; } else { tr_debug("No Context %u", ctxId); } @@ -1427,6 +1428,7 @@ static void thread_address_registration_tlv_parse(uint8_t *ptr, uint16_t data_le //Register GP --> 16 int retVal = thread_nd_address_registration(cur, ptr, mac16, cur->mac_parameters->pan_id, mac64, &new_neighbour_created); thread_extension_address_registration(cur, ptr, mac64, new_neighbour_created, retVal == -2); + (void) retVal; } ptr += 16; @@ -1809,6 +1811,11 @@ void thread_router_bootstrap_mle_receive_cb(int8_t interface_id, mle_message_t * update_mac_mib = true; entry_temp->mac16 = shortAddress; // short address refreshed + if (thread_is_router_addr(shortAddress)) { + // Set full data as REED/Router needs full data (SED will not make links) + thread_neighbor_class_request_full_data_setup_set(&cur->thread_info->neighbor_class, entry_temp->index, true); + } + if (entry_temp->connected_device) { if (mle_tlv_read_tlv(MLE_TYPE_ADDRESS_REGISTRATION, mle_msg->data_ptr, mle_msg->data_length, &addressRegisteredTlv)) { if (!entry_temp->ffd_device) { @@ -1942,9 +1949,9 @@ void thread_router_bootstrap_mle_receive_cb(int8_t interface_id, mle_message_t * uint32_t timeout = 0; uint64_t active_timestamp = 0; uint64_t pending_timestamp = 0; - mle_tlv_info_t addressRegisterTlv = {0}; - mle_tlv_info_t challengeTlv = {0}; - mle_tlv_info_t tlv_req = {0}; + mle_tlv_info_t addressRegisterTlv = {MLE_TYPE_SRC_ADDRESS, 0, 0}; + mle_tlv_info_t challengeTlv = {MLE_TYPE_SRC_ADDRESS, 0, 0}; + mle_tlv_info_t tlv_req = {MLE_TYPE_SRC_ADDRESS, 0, 0}; entry_temp = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur), mle_msg->packet_src_address, false, NULL); if (mle_tlv_read_8_bit_tlv(MLE_TYPE_STATUS, mle_msg->data_ptr, mle_msg->data_length, &status)) { diff --git a/source/6LoWPAN/Thread/thread_test_api.c b/source/6LoWPAN/Thread/thread_test_api.c index 51719670e3..c1f0668e40 100644 --- a/source/6LoWPAN/Thread/thread_test_api.c +++ b/source/6LoWPAN/Thread/thread_test_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without @@ -634,6 +634,7 @@ int thread_test_version_set(int8_t interface_id, uint8_t version) return 0; #else + (void)version; (void)interface_id; return -1; #endif @@ -1269,6 +1270,23 @@ int8_t thread_test_joiner_router_joiner_port_set(uint16_t port) } +int8_t thread_test_mcast_address_per_message_set(uint8_t value) +{ +#ifdef HAVE_THREAD + if (value == 0 || value > 15) { + tr_err("Value not in range. Valid range 1-15"); + return -1; + } + + thread_max_mcast_addr = value; + + return 0; +#else + (void)value; + return -1; +#endif +} + int thread_test_mle_message_send(int8_t interface_id, uint8_t *dst_address, uint8_t msg_id, bool write_src_addr, bool write_leader_data, bool write_network_data, bool write_timestamp, bool write_operational_set, bool write_challenge, uint8_t *msg_ptr, uint8_t msg_len) { #ifdef HAVE_THREAD @@ -1341,6 +1359,8 @@ int thread_test_mle_message_send(int8_t interface_id, uint8_t *dst_address, uint (void)msg_id; (void)write_src_addr; (void)write_leader_data; + (void)write_network_data; + (void)write_timestamp; (void)write_operational_set; (void)write_challenge; (void)msg_ptr; @@ -1367,3 +1387,27 @@ int thread_test_extension_name_set(int8_t interface_id, char extension_name[16]) return -1; #endif } + +int thread_test_parent_priority_set(int8_t interface_id, uint8_t parent_priority) +{ +#ifdef HAVE_THREAD + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur) { + tr_warn("Invalid interface id"); + return -1; + } + + if (!cur->thread_info) { + tr_warn("Not Thread specific interface"); + return -2; + } + cur->thread_info->parent_priority = parent_priority; + return 0; +#else + (void) interface_id; + (void) parent_priority; + return -1; +#endif +} diff --git a/source/6LoWPAN/adaptation_interface.c b/source/6LoWPAN/adaptation_interface.c index b2ba8466e4..eca2559836 100644 --- a/source/6LoWPAN/adaptation_interface.c +++ b/source/6LoWPAN/adaptation_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,8 +23,8 @@ #include "ns_list.h" #include "randLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" -#include "Core/include/socket.h" +#include "Core/include/ns_address_internal.h" +#include "Core/include/ns_socket.h" #include "mac_api.h" #include "mac_mcps.h" #include "mac_common_defines.h" @@ -40,6 +40,8 @@ #include "6LoWPAN/IPHC_Decode/iphc_decompress.h" #include "lowpan_adaptation_interface.h" #include "MLE/mle.h" +#include "Service_Libs/mle_service/mle_service_api.h" +#include "Common_Protocols/icmpv6.h" #ifdef HAVE_RPL #include "RPL/rpl_data.h" #endif @@ -787,49 +789,112 @@ static fragmenter_tx_entry_t *lowpan_adaptation_indirect_first_cached_request_ge return NULL; } -static void lowpan_adaptation_make_room_for_small_packet(protocol_interface_info_entry_t *cur, fragmenter_interface_t *interface_ptr, mac_neighbor_table_entry_t *neighbour_to_count) +static bool lowpan_adaptation_is_priority_message(buffer_t *buf) +{ + // Mle messages + if (buf->dst_sa.port == MLE_ALLOCATED_PORT || buf->src_sa.port == MLE_ALLOCATED_PORT) { + return true; + } + + // Management messages: address solicit, response, query, notification + if (buf->dst_sa.port == THREAD_MANAGEMENT_PORT || buf->src_sa.port == THREAD_MANAGEMENT_PORT) { + return true; + } + + // dhcp messages + if (buf->dst_sa.port == DHCPV6_SERVER_PORT || buf->src_sa.port == DHCPV6_SERVER_PORT) { + return true; + } + + if (buf->dst_sa.port == DHCPV6_CLIENT_PORT || buf->src_sa.port == DHCPV6_CLIENT_PORT) { + return true; + } + + // ICMPv6 messages + if (buf->options.type == ICMPV6_TYPE_ERROR_DESTINATION_UNREACH || + buf->options.type == ICMPV6_TYPE_ERROR_PACKET_TOO_BIG || + buf->options.type == ICMPV6_TYPE_ERROR_TIME_EXCEEDED || + buf->options.type == ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM) { + return true; + } + return false; +} + +static bool lowpan_adaptation_make_room_for_small_packet(protocol_interface_info_entry_t *cur, fragmenter_interface_t *interface_ptr, mac_neighbor_table_entry_t *neighbour_to_count, fragmenter_tx_entry_t *new_entry) { if (interface_ptr->max_indirect_small_packets_per_child == 0) { - return; + // this means there is always space for small packets - no need to check further + return true; } uint_fast16_t count = 0; + fragmenter_tx_entry_t *low_priority_msg_ptr = NULL; ns_list_foreach_reverse_safe(fragmenter_tx_entry_t, tx_entry, &interface_ptr->indirect_tx_queue) { mac_neighbor_table_entry_t *tx_neighbour = mac_neighbor_table_address_discover(mac_neighbor_info(cur), tx_entry->buf->dst_sa.address + 2, tx_entry->buf->dst_sa.addr_type); if (tx_neighbour == neighbour_to_count && buffer_data_length(tx_entry->buf) <= interface_ptr->indirect_big_packet_threshold) { + if (!lowpan_adaptation_is_priority_message(tx_entry->buf)) { + // if there is sub priorities inside message example age here you could compare + low_priority_msg_ptr = tx_entry; + } if (++count >= interface_ptr->max_indirect_small_packets_per_child) { - tr_debug_extra("Purge seq: %d", tx_entry->buf->seq); - if (lowpan_adaptation_indirect_queue_free_message(cur, interface_ptr, tx_entry) == false) { + if (!low_priority_msg_ptr) { + // take last entry if no low priority entry found + if (lowpan_adaptation_is_priority_message(new_entry->buf)) { + low_priority_msg_ptr = tx_entry; + } else { + return false; + } + } + tr_debug_extra("Purge seq: %d", low_priority_msg_ptr->buf->seq); + if (lowpan_adaptation_indirect_queue_free_message(cur, interface_ptr, low_priority_msg_ptr) == false) { /* entry could not be purged from mac, try next entry */ tr_debug_extra("Purge failed, try next"); count--; } + low_priority_msg_ptr = NULL; } } } + return true; } -static void lowpan_adaptation_make_room_for_big_packet(struct protocol_interface_info_entry *cur, fragmenter_interface_t *interface_ptr) +static bool lowpan_adaptation_make_room_for_big_packet(struct protocol_interface_info_entry *cur, fragmenter_interface_t *interface_ptr, fragmenter_tx_entry_t *new_entry) { if (interface_ptr->max_indirect_big_packets_total == 0) { - return; + // this means there is always space for big packets - no need to check further + return true; } uint_fast16_t count = 0; + fragmenter_tx_entry_t *low_priority_msg_ptr = NULL; ns_list_foreach_reverse_safe(fragmenter_tx_entry_t, tx_entry, &interface_ptr->indirect_tx_queue) { if (buffer_data_length(tx_entry->buf) > interface_ptr->indirect_big_packet_threshold) { + if (!lowpan_adaptation_is_priority_message(tx_entry->buf)) { + // if there is sub priorities inside message example age here you could compare + low_priority_msg_ptr = tx_entry; + } if (++count >= interface_ptr->max_indirect_big_packets_total) { - tr_debug_extra("Purge seq: %d", tx_entry->buf->seq); - if (lowpan_adaptation_indirect_queue_free_message(cur, interface_ptr, tx_entry) == false) { - tr_debug("Purge failed, try next entry"); + if (!low_priority_msg_ptr) { + // take last entry if no low priority entry found + if (lowpan_adaptation_is_priority_message(new_entry->buf)) { + low_priority_msg_ptr = tx_entry; + } else { + return false; + } + } + tr_debug_extra("Purge seq: %d", low_priority_msg_ptr->buf->seq); + if (lowpan_adaptation_indirect_queue_free_message(cur, interface_ptr, low_priority_msg_ptr) == false) { + tr_debug_extra("Purge failed, try next entry"); /* entry could not be purged from mac, try next entry */ count--; } + low_priority_msg_ptr = NULL; } } } + return true; } static void lowpan_data_request_to_mac(protocol_interface_info_entry_t *cur, buffer_t *buf, fragmenter_tx_entry_t *tx_ptr, fragmenter_interface_t *interface_ptr) @@ -868,6 +933,7 @@ static void lowpan_data_request_to_mac(protocol_interface_info_entry_t *cur, buf int8_t lowpan_adaptation_interface_tx(protocol_interface_info_entry_t *cur, buffer_t *buf) { + bool is_room_for_new_message; if (!buf) { return -1; } @@ -940,9 +1006,9 @@ int8_t lowpan_adaptation_interface_tx(protocol_interface_info_entry_t *cur, buff // Make room for new message if needed */ if (buffer_data_length(buf) <= interface_ptr->indirect_big_packet_threshold) { - lowpan_adaptation_make_room_for_small_packet(cur, interface_ptr, neigh_entry_ptr); + is_room_for_new_message = lowpan_adaptation_make_room_for_small_packet(cur, interface_ptr, neigh_entry_ptr, tx_ptr); } else { - lowpan_adaptation_make_room_for_big_packet(cur, interface_ptr); + is_room_for_new_message = lowpan_adaptation_make_room_for_big_packet(cur, interface_ptr, tx_ptr); } if (lowpan_adaptation_indirect_mac_data_request_active(interface_ptr, tx_ptr)) { @@ -951,7 +1017,15 @@ int8_t lowpan_adaptation_interface_tx(protocol_interface_info_entry_t *cur, buff tx_ptr->indirect_data_cached = true; } - ns_list_add_to_end(&interface_ptr->indirect_tx_queue, tx_ptr); + if (is_room_for_new_message) { + ns_list_add_to_end(&interface_ptr->indirect_tx_queue, tx_ptr); + } else { + if (tx_ptr->fragmenter_buf) { + ns_dyn_mem_free(tx_ptr->fragmenter_buf); + } + ns_dyn_mem_free(tx_ptr); + goto tx_error_handler; + } // Check if current message can be delivered to MAC or should some cached message be delivered first tx_ptr_cached = lowpan_adaptation_indirect_first_cached_request_get(interface_ptr, tx_ptr); diff --git a/source/6LoWPAN/lowpan_adaptation_interface.h b/source/6LoWPAN/lowpan_adaptation_interface.h index 4921c3b120..562e482034 100644 --- a/source/6LoWPAN/lowpan_adaptation_interface.h +++ b/source/6LoWPAN/lowpan_adaptation_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ #ifndef LOWPAN_ADAPTATION_INTERFACE_H_ #define LOWPAN_ADAPTATION_INTERFACE_H_ -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" struct protocol_interface_info_entry; struct mcps_data_conf_s; diff --git a/source/6LoWPAN/ws/ws_bbr_api.c b/source/6LoWPAN/ws/ws_bbr_api.c new file mode 100644 index 0000000000..5d242ae687 --- /dev/null +++ b/source/6LoWPAN/ws/ws_bbr_api.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2018-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. + */ + +#include +#include "nsconfig.h" +#include "ns_types.h" +#include "ns_trace.h" +#include "net_interface.h" +#include "eventOS_event.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/Bootstraps/protocol_6lowpan.h" +#include "6LoWPAN/Bootstraps/protocol_6lowpan_interface.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bootstrap.h" +#include "RPL/rpl_control.h" +#include "RPL/rpl_data.h" +#include "Common_Protocols/icmpv6.h" +#include "Common_Protocols/icmpv6_radv.h" +#include "ws_management_api.h" +#include "net_rpl.h" +#include "Service_Libs/nd_proxy/nd_proxy.h" +#include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "DHCPv6_Server/DHCPv6_server_service.h" + +#define TRACE_GROUP "wsbs" + +#define RPL_INSTANCE_ID 1 + +#ifdef HAVE_WS_BORDER_ROUTER + + +/* when creating BBR make ULA dodag ID always and when network becomes available add prefix to DHCP + * + * + */ +static int8_t backbone_interface_id = -1; // BBR backbone information + +static uint8_t static_dodag_prefix[8] = {0xfd, 0x00, 0x61, 0x72, 0x6d}; +static uint8_t static_ula_address[16] = {0}; +static uint8_t static_dodag_id[16] = {0}; +static uint8_t global_dodag_id[16] = {0}; +static uint32_t bbr_delay_timer = 20; // initial delay. + +static rpl_dodag_conf_t rpl_conf = { + // Lifetime values + .default_lifetime = 120, + .lifetime_unit = 60, + .objective_code_point = 1, // MRHOF algorithm used + .authentication = 0, + .path_control_size = 7, + .dag_max_rank_increase = 2048, + .min_hop_rank_increase = 256, + // DIO configuration + .dio_interval_min = WS_RPL_DIO_IMIN, + .dio_interval_doublings = WS_RPL_DIO_DOUBLING, + .dio_redundancy_constant = WS_RPL_DIO_REDUNDANCY +}; + +void ws_bbr_rpl_config(uint8_t imin, uint8_t doubling, uint8_t redundancy) +{ + if (imin == 0 || doubling == 0) { + // use default values + imin = WS_RPL_DIO_IMIN; + doubling = WS_RPL_DIO_DOUBLING; + redundancy = WS_RPL_DIO_REDUNDANCY; + } + if (rpl_conf.dio_interval_min == imin && + rpl_conf.dio_interval_doublings == doubling && + rpl_conf.dio_redundancy_constant == redundancy) { + // Same values no update needed + return; + } + rpl_conf.dio_interval_min = imin; + rpl_conf.dio_interval_doublings = doubling; + rpl_conf.dio_redundancy_constant = redundancy; + if (protocol_6lowpan_rpl_root_dodag) { + rpl_control_update_dodag_config(protocol_6lowpan_rpl_root_dodag, &rpl_conf); + rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); + } +} + +static void ws_bbr_rpl_root_start(uint8_t *dodag_id) +{ + tr_info("RPL root start"); + rpl_data_init_root(); + + if (protocol_6lowpan_rpl_root_dodag) { + rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); + protocol_6lowpan_rpl_root_dodag = NULL; + } + + protocol_6lowpan_rpl_root_dodag = rpl_control_create_dodag_root(protocol_6lowpan_rpl_domain, RPL_INSTANCE_ID, dodag_id, &rpl_conf, rpl_conf.min_hop_rank_increase, RPL_GROUNDED | RPL_MODE_NON_STORING | RPL_DODAG_PREF(0)); + if (!protocol_6lowpan_rpl_root_dodag) { + 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); +} + +static void ws_bbr_rpl_root_stop(void) +{ + tr_info("RPL root stop"); + rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); + protocol_6lowpan_rpl_root_dodag = NULL; + memset(static_ula_address, 0, 16); + memset(static_dodag_id, 0, 16); + memset(global_dodag_id, 0, 16); +} + +static int ws_border_router_proxy_validate(int8_t interface_id, uint8_t *address) +{ + + /* Could also check route type, but I don't think it really matters */ + ipv6_route_t *route; + route = ipv6_route_choose_next_hop(address, interface_id, NULL); + if (!route || route->prefix_len < 128) { + return -1; + } + + return 0; +} + +int ws_border_router_proxy_state_update(int8_t caller_interface_id, int8_t handler_interface_id, bool status) +{ + (void)caller_interface_id; + + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(handler_interface_id); + if (!cur) { + tr_error("No Interface"); + return -1; + } + + if (status) { + tr_debug("Border router Backhaul link ready"); + } else { + tr_debug("Border router Backhaul link down"); + } + return 0; +} + +static int ws_bbr_static_ula_create(protocol_interface_info_entry_t *cur) +{ + if (memcmp(static_ula_address, ADDR_UNSPECIFIED, 16) != 0) { + // address generated + return 0; + } + 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 (!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)); + + return 0; +} + +/* + * 0 static non rooted self generated own address + * 1 static address with backbone connectivity + */ +static int ws_bbr_static_dodag_get(protocol_interface_info_entry_t *cur, uint8_t *dodag_id_ptr) +{ + + protocol_interface_info_entry_t *bb_interface = protocol_stack_interface_info_get_by_id(backbone_interface_id); + + if (bb_interface && bb_interface->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) { + // static configuration for ethernet available + ns_list_foreach(if_address_entry_t, add_entry, &cur->ip_addresses) { + if (memcmp(add_entry->address, bb_interface->ipv6_configure->static_prefix64, 8) == 0) { + //tr_info("BBR static config available"); + if (dodag_id_ptr) { + memcpy(dodag_id_ptr, add_entry->address, 16); + } + return 1; + } + } + } + ws_bbr_static_ula_create(cur); + + // only own generated prefix available + if (dodag_id_ptr) { + memcpy(dodag_id_ptr, static_ula_address, 16); + } + + return 0; +} + + +static int ws_bbr_dodag_get(protocol_interface_info_entry_t *cur, uint8_t *static_dodag_id_ptr, uint8_t *dodag_id_ptr) +{ + uint8_t global_address[16]; + + if (static_dodag_id_ptr) { + memset(static_dodag_id_ptr, 0, 16); + } + + if (dodag_id_ptr) { + memset(dodag_id_ptr, 0, 16); + } + + if (ws_bbr_static_dodag_get(cur, static_dodag_id_ptr) < 0) { + // no static configuration available + return -1; + } + + if (arm_net_address_get(backbone_interface_id, ADDR_IPV6_GP, global_address) != 0) { + // No global prefix available + return 0; + } + if (memcmp(global_address, dodag_id_ptr, 8) == 0) { + // static address is same + return 0; + } + memcpy(dodag_id_ptr, global_address, 16); + return 0; +} +static void wisun_bbr_na_send(int8_t interface_id, const uint8_t target[static 16]) +{ + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur) { + return; + } + + buffer_t *buffer = icmpv6_build_na(cur, false, true, true, target, NULL, ADDR_UNSPECIFIED); + protocol_push(buffer); + return; + +} + +static bool wisun_dhcp_address_add_cb(int8_t interfaceId, dhcp_address_cache_update_t *address_info, void *route_src) +{ + (void)route_src; + protocol_interface_info_entry_t *curPtr = protocol_stack_interface_info_get_by_id(interfaceId); + if (!curPtr) { + return false; + } + + // When address is allocated we send NA to backbone to notify the new address and flush from other BBRs + wisun_bbr_na_send(backbone_interface_id, address_info->allocatedAddress); + return true; +} + + +static void ws_bbr_rpl_status_check(protocol_interface_info_entry_t *cur) +{ + + uint8_t static_id[16] = {0}; + uint8_t global_id[16] = {0}; + + //tr_info("BBR status check"); + + ws_bbr_dodag_get(cur, static_id, global_id); + + if (memcmp(static_dodag_id, static_id, 16) != 0) { + // Static id updated or first setup + ws_bbr_rpl_root_start(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); + 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); + } + // 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); + + 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); + ipv6_route_add_with_info(global_id, 64, backbone_interface_id, NULL, ROUTE_THREAD_BBR, NULL, 0, 0xffffffff, 0); + } + memcpy(global_dodag_id, global_id, 16); + rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); + nd_proxy_downstream_interface_register(cur->id, ws_border_router_proxy_validate, ws_border_router_proxy_state_update); + } + +} + +void ws_bbr_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds) +{ + (void)seconds; + + if (!ws_info(cur)) { + return; + } + if (cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + // Not a border router + return; + } + if (!cur->rpl_domain) { + // RPL not started + return; + } + + if (bbr_delay_timer > seconds) { + bbr_delay_timer -= seconds; + } else { + bbr_delay_timer = 20; // 20 second interval between status checks + + // prequisists + // Wi-SUN network configuration started without RPL + + // RPL configured simple + // 1. Wait for backend connection + // 2. When address becomes available in backend start RPL dodag + // 3. if address removed remove dodag + + // RPL configured Advanced + // 1. Add ULA DODAG and and start ROOT even without backend + // a. If static prefix configured use it. + // b. generate random ULA and publish it to backend + // 2. if GUA prefix becomes available in backend add new prefix to DODAG + // 3. if GUA prefix is removed remove the prefix. + + if (protocol_6lowpan_rpl_root_dodag) { + // Border router is active + if (0 != protocol_interface_address_compare(static_dodag_id)) { + // Dodag has become invalid need to delete + tr_info("RPL static dodag not valid anymore %s", trace_ipv6(static_dodag_id)); + ws_bbr_rpl_root_stop(); + } else { + + } + } + ws_bbr_rpl_status_check(cur); + + } + // Normal BBR operation + if (protocol_6lowpan_rpl_root_dodag) { + if (cur->ws_info->pan_version_timer > seconds) { + cur->ws_info->pan_version_timer -= seconds; + } else { + // Border router has timed out + tr_debug("Border router version number update"); + cur->ws_info->pan_version_timer = PAN_VERSION_LIFETIME; + cur->ws_info->pan_information.pan_version++; + // Inconsistent for border router to make information distribute faster + ws_bootstrap_configuration_trickle_reset(cur); + + if (cur->ws_info->network_size_config == NETWORK_SIZE_AUTOMATIC) { + ws_common_network_size_configure(cur, cur->ws_info->pan_information.pan_size); + } + // We update the RPL version in same time to allow nodes to reselect parent + // As configuration is made so that devices cant move downward in dodag this allows it + // TODO think the correct rate for this + if (cur->ws_info->pan_information.pan_version && cur->ws_info->pan_information.pan_version % RPL_VERSION_LIFETIME / PAN_VERSION_LIFETIME == 0) { + // Third the rate of configuration version change at default 5 hours + rpl_control_increment_dodag_version(protocol_6lowpan_rpl_root_dodag); + } + } + + } + +} + +uint16_t test_pan_size_override = 0xffff; + +uint16_t ws_bbr_pan_size(protocol_interface_info_entry_t *cur) +{ + uint16_t result = 0; + if (test_pan_size_override != 0xffff) { + return test_pan_size_override; + } + + rpl_control_get_instance_dao_target_count(cur->rpl_domain, RPL_INSTANCE_ID, NULL, &result); + return result; +} + + +#endif //HAVE_WS_BORDER_ROUTER + +/* Public APIs + * + */ + +int ws_bbr_start(int8_t interface_id, int8_t bb_interface_id) +{ +#ifdef HAVE_WS_BORDER_ROUTER + + (void)interface_id; + protocol_interface_info_entry_t *bb_interface = protocol_stack_interface_info_get_by_id(bb_interface_id); + + if (!bb_interface) { + return -1; + } + // TODO make bb configurations + + backbone_interface_id = bb_interface_id; + + return 0; +#else + (void)interface_id; + (void)bb_interface_id; + return -1; +#endif +} +void ws_bbr_stop(int8_t interface_id) +{ +#ifdef HAVE_WS_BORDER_ROUTER + + (void)interface_id; + backbone_interface_id = -1; + + if (!protocol_6lowpan_rpl_domain) { + return; + } + + rpl_control_delete_dodag_root(protocol_6lowpan_rpl_domain, protocol_6lowpan_rpl_root_dodag); + protocol_6lowpan_rpl_root_dodag = NULL; + +#else + (void)interface_id; +#endif +} + +int ws_bbr_node_keys_remove(int8_t interface_id, uint8_t *eui64) +{ + (void) interface_id; + (void) eui64; + + return -1; +} + +int ws_bbr_node_access_revoke_start(int8_t interface_id) +{ + (void) interface_id; + + return -1; +} diff --git a/source/6LoWPAN/ws/ws_bbr_api_internal.h b/source/6LoWPAN/ws/ws_bbr_api_internal.h new file mode 100644 index 0000000000..a3e7b6e44b --- /dev/null +++ b/source/6LoWPAN/ws/ws_bbr_api_internal.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-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_BBR_API_PRIVATE_H_ +#define WS_BBR_API_PRIVATE_H_ + + +#ifdef HAVE_WS_BORDER_ROUTER + +extern uint16_t test_pan_size_override; + +void ws_bbr_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds); + +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); + + + +#else + +#define ws_bbr_seconds_timer( cur, seconds) +#define ws_bbr_pan_size(cur) 0 +#define ws_bbr_rpl_config( imin, doubling, redundancy); + +#endif //HAVE_WS_BORDER_ROUTER + +#endif /* WS_BBR_API_PRIVATE_H_ */ diff --git a/source/6LoWPAN/ws/ws_bootstrap.c b/source/6LoWPAN/ws/ws_bootstrap.c new file mode 100644 index 0000000000..56caffe04a --- /dev/null +++ b/source/6LoWPAN/ws/ws_bootstrap.c @@ -0,0 +1,2480 @@ +/* + * Copyright (c) 2018-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. + */ + +#include +#include "nsconfig.h" +#include "ns_types.h" +#include "ns_trace.h" +#include "net_interface.h" +#include "eventOS_event.h" +#include "randLIB.h" +#include "common_functions.h" +#include "mac_common_defines.h" +#include "sw_mac.h" +#include "ccmLIB.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/Bootstraps/protocol_6lowpan.h" +#include "6LoWPAN/Bootstraps/protocol_6lowpan_interface.h" +#include "ipv6_stack/protocol_ipv6.h" +#include "6LoWPAN/MAC/mac_helper.h" +#include "6LoWPAN/MAC/mac_data_poll.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/MAC/mac_ie_lib.h" +#include "MPL/mpl.h" +#include "RPL/rpl_protocol.h" +#include "RPL/rpl_control.h" +#include "RPL/rpl_data.h" +#include "Common_Protocols/icmpv6.h" +#include "Common_Protocols/icmpv6_radv.h" +#include "Common_Protocols/ipv6_constants.h" +#include "Service_Libs/Trickle/trickle.h" +#include "Service_Libs/fhss/channel_list.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bootstrap.h" +#include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_llc.h" +#include "6LoWPAN/ws/ws_neighbor_class.h" +#include "6LoWPAN/ws/ws_ie_lib.h" +#include "6LoWPAN/lowpan_adaptation_interface.h" +#include "Service_Libs/etx/etx.h" +#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" +#include "Service_Libs/nd_proxy/nd_proxy.h" +#include "Service_Libs/blacklist/blacklist.h" +#include "platform/topo_trace.h" +#include "libDHCPv6/libDHCPv6.h" +#include "DHCPv6_client/dhcpv6_client_api.h" +#include "ws_management_api.h" +#include "net_rpl.h" +#include "mac_api.h" +#include "6LoWPAN/ws/ws_pae_controller.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" +#include "6LoWPAN/ws/ws_eapol_auth_relay.h" +#include "6LoWPAN/ws/ws_eapol_relay.h" + +#define TRACE_GROUP "wsbs" + +#ifdef HAVE_WS + +static void ws_bootstrap_event_handler(arm_event_s *event); +static void ws_bootstrap_state_change(protocol_interface_info_entry_t *cur, icmp_state_t nwk_bootstrap_state); +//static bool ws_bootstrap_state_active(struct protocol_interface_info_entry *cur); +//static bool ws_bootstrap_state_wait_rpl(struct protocol_interface_info_entry *cur); +static bool ws_bootstrap_state_discovery(struct protocol_interface_info_entry *cur); +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); + +static bool ws_bootstrap_neighbor_info_request(struct protocol_interface_info_entry *interface, const uint8_t *mac_64, llc_neighbour_req_t *neighbor_buffer, bool request_new); +static uint16_t ws_bootstrap_routing_cost_calculate(protocol_interface_info_entry_t *cur); +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_authentication_completed(protocol_interface_info_entry_t *cur, bool success); + +mac_neighbor_table_entry_t *ws_bootstrap_mac_neighbor_add(struct protocol_interface_info_entry *interface, const uint8_t *src64) + +{ + mac_neighbor_table_entry_t *neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), src64, MAC_ADDR_MODE_64_BIT); + if (neighbor) { + return neighbor; + } + + neighbor = mac_neighbor_table_entry_allocate(mac_neighbor_info(interface), src64); + if (!neighbor) { + return NULL; + } + // TODO only call these for new neighbour + mlme_device_descriptor_t device_desc; + neighbor->lifetime = WS_NEIGHBOR_LINK_TIMEOUT; + neighbor->link_lifetime = WS_NEIGHBOR_LINK_TIMEOUT; + tr_debug("Added new neighbor %s : index:%u", trace_array(src64, 8), neighbor->index); + mac_helper_device_description_write(interface, &device_desc, neighbor->mac64, neighbor->mac16, 0, false); + mac_helper_devicetable_set(&device_desc, interface, neighbor->index, interface->mac_parameters->mac_default_key_index, true); + return neighbor; +} + +static void ws_bootstrap_neighbor_delete(struct protocol_interface_info_entry *interface, uint8_t attribute_index) +{ + mac_helper_devicetable_remove(interface->mac_api, attribute_index); + etx_neighbor_remove(interface->id, attribute_index); + ws_neighbor_class_entry_remove(&interface->ws_info->neighbor_storage, attribute_index); +} + +static void ws_bootstrap_neighbor_list_clean(struct protocol_interface_info_entry *interface) +{ + + mac_neighbor_table_neighbor_list_clean(mac_neighbor_info(interface)); +} + +static void ws_bootstrap_address_notification_cb(struct protocol_interface_info_entry *interface, const struct if_address_entry *addr, if_address_callback_t reason) +{ + /* No need for LL address registration */ + if (addr->source == ADDR_SOURCE_UNKNOWN) { + return; + } + if (reason == ADDR_CALLBACK_DAD_COMPLETE) { + //Trig Address Registartion only when Bootstrap is ready + if (interface->nwk_bootstrap_state == ER_BOOTSRAP_DONE || addr->source == ADDR_SOURCE_DHCP) { + ws_bootsrap_event_trig(WS_ADDRESS_ADDED, interface->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, (void *)addr); + } + if (addr_ipv6_scope(addr->address, interface) > IPV6_SCOPE_LINK_LOCAL) { + // at least ula address available inside mesh. + interface->global_address_available = true; + } + + } else if (reason == ADDR_CALLBACK_DELETED) { + // What to do? + // Go through address list and check if there is global address still available + 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) { + // at least ula address available inside mesh. + interface->global_address_available = true; + break; + } + } + } else { + tr_debug("Address notification addr: %s reason: %d", trace_ipv6(addr->address), reason); + } +} + +static int ws_bootstrap_tasklet_init(protocol_interface_info_entry_t *cur) +{ + if (cur->bootStrapId < 0) { + cur->bootStrapId = eventOS_event_handler_create(&ws_bootstrap_event_handler, WS_INIT_EVENT); + tr_debug("WS tasklet init"); + } + + if (cur->bootStrapId < 0) { + tr_error("tasklet init failed"); + return -1; + } + + + return 0; +} + +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) +{ + arm_event_s event = { + .receiver = interface_id, + .sender = 0, + .event_type = event_type, + .priority = priority, + .data_ptr = event_data, + }; + return eventOS_event_send(&event); +} + +static void ws_nud_table_reset(protocol_interface_info_entry_t *cur) +{ + //Empty active list + ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) { + ns_list_remove(&cur->ws_info->active_nud_process, entry); + } + + //Empty free list + ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->free_nud_entries) { + ns_list_remove(&cur->ws_info->free_nud_entries, entry); + } + //Add to free list to full + for (int i = 0; i < ACTIVE_NUD_PROCESS_MAX; i++) { + ns_list_add_to_end(&cur->ws_info->free_nud_entries, &cur->ws_info->nud_table_entrys[i]); + } +} + +static ws_nud_table_entry_t *ws_nud_entry_get_free(protocol_interface_info_entry_t *cur) +{ + ws_nud_table_entry_t *entry = ns_list_get_first(&cur->ws_info->free_nud_entries); + if (entry) { + entry->wait_response = false; + entry->retry_count = 0; + entry->nud_process = false; + entry->timer = randLIB_get_random_in_range(1, 900); + entry->neighbor_info = NULL; + ns_list_remove(&cur->ws_info->free_nud_entries, entry); + ns_list_add_to_end(&cur->ws_info->active_nud_process, entry); + } + return 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); + } + + mac_neighbor_table_neighbor_connected(mac_neighbor_info(cur), mac_neighbor); + return; + } + } +} + +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; + ns_list_remove(&cur->ws_info->active_nud_process, entry); + ns_list_add_to_end(&cur->ws_info->free_nud_entries, entry); + if (neighbor->nud_active) { + neighbor->nud_active = false; + mac_neighbor_info(cur)->active_nud_process--; + } +} + +static bool ws_nud_message_build(protocol_interface_info_entry_t *cur, mac_neighbor_table_entry_t *neighbor) +{ + //Send NS + uint8_t ll_target[16]; + memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(ll_target + 8, neighbor->mac64, 8); + ll_target[8] ^= 2; + tr_info("NUD generate NS %u", neighbor->index); + buffer_t *buffer = icmpv6_build_ns(cur, ll_target, NULL, true, false, NULL); + if (buffer) { + protocol_push(buffer); + return true; + } + return false; +} + +void ws_nud_active_timer(protocol_interface_info_entry_t *cur, uint16_t ticks) +{ + //Convert TICKS to real milliseconds + if (ticks > 0xffff / 100) { + ticks = 0xffff; + } else { + ticks *= 100; + } + + ns_list_foreach_safe(ws_nud_table_entry_t, entry, &cur->ws_info->active_nud_process) { + if (entry->timer <= ticks) { + //TX Process or timeout + if (entry->wait_response) { + //Timeout for NUD or Probe + if (entry->nud_process) { + tr_debug("NUD NA timeout"); + if (entry->retry_count < 2) { + entry->timer = randLIB_get_random_in_range(1, 900); + entry->wait_response = false; + } else { + //Clear entry from active list + ws_nud_state_clean(cur, entry); + //Remove whole entry + mac_neighbor_table_neighbor_remove(mac_neighbor_info(cur), entry->neighbor_info); + } + } else { + ws_nud_state_clean(cur, entry); + } + + } else { + //Random TX wait period is over + entry->wait_response = ws_nud_message_build(cur, entry->neighbor_info); + if (!entry->wait_response) { + if (entry->nud_process && entry->retry_count < 2) { + entry->timer = randLIB_get_random_in_range(1, 900); + } else { + //Clear entry from active list + //Remove and try again later on + ws_nud_state_clean(cur, entry); + } + } else { + entry->retry_count++; + entry->timer = 5001; + } + } + } else { + entry->timer -= ticks; + } + } +} + +static fhss_ws_neighbor_timing_info_t *ws_get_neighbor_info(const fhss_api_t *api, uint8_t eui64[8]) +{ + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_fhss_api(api); + if (!cur || !cur->mac_parameters || !mac_neighbor_info(cur)) { + return NULL; + } + mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(cur), eui64, MAC_ADDR_MODE_64_BIT); + if (!mac_neighbor) { + return NULL; + } + ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, mac_neighbor->index); + if (!ws_neighbor) { + return NULL; + } + return &ws_neighbor->fhss_data; +} +static void ws_bootstrap_llc_hopping_update(struct protocol_interface_info_entry *cur, const fhss_ws_configuration_t *fhss_configuration) +{ + memcpy(cur->ws_info->hopping_schdule.channel_mask, fhss_configuration->channel_mask, sizeof(uint32_t) * 8); + cur->ws_info->hopping_schdule.uc_fixed_channel = fhss_configuration->unicast_fixed_channel; + cur->ws_info->hopping_schdule.bc_fixed_channel = fhss_configuration->broadcast_fixed_channel; + cur->ws_info->hopping_schdule.uc_channel_function = fhss_configuration->ws_uc_channel_function; + cur->ws_info->hopping_schdule.bc_channel_function = fhss_configuration->ws_bc_channel_function; + cur->ws_info->hopping_schdule.fhss_bc_dwell_interval = fhss_configuration->fhss_bc_dwell_interval; + cur->ws_info->hopping_schdule.fhss_broadcast_interval = fhss_configuration->fhss_broadcast_interval; + cur->ws_info->hopping_schdule.fhss_uc_dwell_interval = fhss_configuration->fhss_uc_dwell_interval; + cur->ws_info->hopping_schdule.fhss_bsi = fhss_configuration->bsi; +} + +static int8_t ws_fhss_initialize(protocol_interface_info_entry_t *cur) +{ + fhss_api_t *fhss_api = ns_sw_mac_get_fhss_api(cur->mac_api); + + if (!fhss_api) { + // When FHSS doesn't exist yet, create one + fhss_ws_configuration_t fhss_configuration; + memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t)); + ws_generate_channel_list(fhss_configuration.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + // using bitwise AND operation for user set channel mask to remove channels not allowed in this device + for (uint8_t n = 0; n < 8; n++) { + fhss_configuration.channel_mask[n] &= cur->ws_info->fhss_channel_mask[n]; + } + + fhss_configuration.fhss_uc_dwell_interval = cur->ws_info->fhss_uc_dwell_interval; + fhss_configuration.ws_uc_channel_function = (fhss_ws_channel_functions)cur->ws_info->fhss_uc_channel_function; + fhss_configuration.ws_bc_channel_function = (fhss_ws_channel_functions)cur->ws_info->fhss_bc_channel_function; + fhss_configuration.fhss_bc_dwell_interval = cur->ws_info->fhss_bc_dwell_interval; + fhss_configuration.fhss_broadcast_interval = cur->ws_info->fhss_bc_interval; + + fhss_api = ns_fhss_ws_create(&fhss_configuration, cur->ws_info->fhss_timer_ptr); + if (!fhss_api) { + tr_error("fhss create failed"); + return -1; + } + ns_sw_mac_fhss_register(cur->mac_api, fhss_api); + } else { + // Read defaults from the configuration to help FHSS testing + const fhss_ws_configuration_t *fhss_configuration = ns_fhss_ws_configuration_get(fhss_api); + if (!fhss_configuration) { + // no configuration set yet + return 0; + } + memcpy(cur->ws_info->fhss_channel_mask, fhss_configuration->channel_mask, sizeof(uint32_t) * 8); + cur->ws_info->fhss_uc_channel_function = fhss_configuration->ws_uc_channel_function; + cur->ws_info->fhss_bc_channel_function = fhss_configuration->ws_bc_channel_function; + cur->ws_info->fhss_bc_dwell_interval = fhss_configuration->fhss_bc_dwell_interval; + cur->ws_info->fhss_bc_interval = fhss_configuration->fhss_broadcast_interval; + cur->ws_info->fhss_uc_dwell_interval = fhss_configuration->fhss_uc_dwell_interval; + } + return 0; +} +static int8_t ws_fhss_set_defaults(protocol_interface_info_entry_t *cur, fhss_ws_configuration_t *fhss_configuration) +{ + fhss_configuration->fhss_uc_dwell_interval = cur->ws_info->fhss_uc_dwell_interval; + fhss_configuration->ws_uc_channel_function = (fhss_ws_channel_functions)cur->ws_info->fhss_uc_channel_function; + fhss_configuration->ws_bc_channel_function = (fhss_ws_channel_functions)cur->ws_info->fhss_bc_channel_function; + fhss_configuration->fhss_bc_dwell_interval = cur->ws_info->fhss_bc_dwell_interval; + fhss_configuration->fhss_broadcast_interval = cur->ws_info->fhss_bc_interval; + if (cur->ws_info->fhss_uc_fixed_channel != 0xffff) { + fhss_configuration->unicast_fixed_channel = cur->ws_info->fhss_uc_fixed_channel; + } + fhss_configuration->broadcast_fixed_channel = cur->ws_info->fhss_bc_fixed_channel; + ws_generate_channel_list(fhss_configuration->channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + // using bitwise AND operation for user set channel mask to remove channels not allowed in this device + for (uint8_t n = 0; n < 8; n++) { + fhss_configuration->channel_mask[n] &= cur->ws_info->fhss_channel_mask[n]; + } + return 0; +} +static int8_t ws_fhss_border_router_configure(protocol_interface_info_entry_t *cur) +{ + // Read configuration of existing FHSS and start using the default values for any network + fhss_ws_configuration_t fhss_configuration; + memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t)); + + if (ns_fhss_ws_configuration_get(cur->ws_info->fhss_api)) { + memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t)); + } + ws_fhss_set_defaults(cur, &fhss_configuration); + ns_fhss_ws_configuration_set(cur->ws_info->fhss_api, &fhss_configuration); + ws_bootstrap_llc_hopping_update(cur, &fhss_configuration); + + return 0; +} + +static uint16_t ws_randomize_fixed_channel(uint16_t configured_fixed_channel, uint8_t number_of_channels) +{ + if (configured_fixed_channel == 0xFFFF) { + return randLIB_get_random_in_range(0, number_of_channels - 1); + } else { + return configured_fixed_channel; + } +} + +static int8_t ws_fhss_discovery_configure(protocol_interface_info_entry_t *cur) +{ + // Read configuration of existing FHSS and start using the default values for any network + fhss_ws_configuration_t fhss_configuration; + memset(&fhss_configuration, 0, sizeof(fhss_ws_configuration_t)); + + if (ns_fhss_ws_configuration_get(cur->ws_info->fhss_api)) { + memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t)); + } + + fhss_configuration.fhss_uc_dwell_interval = 0; + fhss_configuration.ws_uc_channel_function = WS_FIXED_CHANNEL; + fhss_configuration.ws_bc_channel_function = WS_FIXED_CHANNEL; + fhss_configuration.fhss_bc_dwell_interval = 0; + 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); + ws_bootstrap_llc_hopping_update(cur, &fhss_configuration); + + return 0; +} + +static int8_t ws_fhss_enable(protocol_interface_info_entry_t *cur) +{ + const fhss_ws_configuration_t *fhss_configuration = ns_fhss_ws_configuration_get(cur->ws_info->fhss_api); + + if (!cur->ws_info->fhss_api || !fhss_configuration) { + return -1; + } + // Set the LLC information to follow the actual fhss settings + ws_bootstrap_llc_hopping_update(cur, fhss_configuration); + + // Set neighbor info callback + if (ns_fhss_set_neighbor_info_fp(cur->ws_info->fhss_api, &ws_get_neighbor_info)) { + return -1; + } + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + ns_fhss_ws_set_hop_count(cur->ws_info->fhss_api, 0); + } + return 0; +} + +/* 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) +{ + + fhss_ws_configuration_t fhss_configuration; + if (!neighbor_info->ws_neighbor->broadcast_timing_info_stored) { + tr_error("No BC timing info for set new parent"); + return; + } + + memcpy(&fhss_configuration, ns_fhss_ws_configuration_get(cur->ws_info->fhss_api), sizeof(fhss_ws_configuration_t)); + + // Learning broadcast network configuration + if (neighbor_info->ws_neighbor->broadcast_shedule_info_stored) { + 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; + cur->ws_info->fhss_bc_fixed_channel = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.fixed_channel; + } + fhss_configuration.bsi = neighbor_info->ws_neighbor->fhss_data.bc_timing_info.broadcast_schedule_id; + 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; + + } + + 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); + + // Update LLC to follow updated fhss settings + ws_bootstrap_llc_hopping_update(cur, &fhss_configuration); +} + +static void ws_bootstrap_ll_address_validate(struct protocol_interface_info_entry *cur) +{ + // Configure EUI64 for MAC if missing + uint8_t mac64[8]; + if (!cur->mac_api) { + return; + } + + cur->mac_api->mac64_get(cur->mac_api, MAC_EXTENDED_DYNAMIC, mac64); + + if (memcmp(mac64, ADDR_UNSPECIFIED, 8) == 0) { + cur->mac_api->mac64_get(cur->mac_api, MAC_EXTENDED_READ_ONLY, mac64); + } + + if (memcmp(mac64, ADDR_UNSPECIFIED, 8) == 0) { + // Generate random mac because it was not available + randLIB_get_n_bytes_random(mac64, 8); + mac64[0] |= 2; //Set Local Bit + mac64[0] &= ~1; //Clear multicast bit + + tr_info("Generated random MAC %s", trace_array(mac64, 8)); + } + mac_helper_mac64_set(cur, mac64); + + memcpy(cur->iid_eui64, mac64, 8); + /* Invert U/L Bit */ + cur->iid_eui64[0] ^= 2; + memcpy(cur->iid_slaac, cur->iid_eui64, 8); + +} + +/* \return 0x0100 to 0xFFFF ETX value (8 bit fraction) + * \return 0xFFFF address not associated + * \return 0x0000 address unknown or other error + * \return 0x0001 no ETX statistics on this interface + */ +uint16_t ws_etx_read(protocol_interface_info_entry_t *interface, addrtype_t addr_type, const uint8_t *addr_ptr) +{ + + uint16_t etx; + if (!addr_ptr || !interface) { + return 0; + } + + uint8_t attribute_index; + + mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), addr_ptr + PAN_ID_LEN, addr_type); + if (!mac_neighbor) { + return 0xffff; + } + attribute_index = mac_neighbor->index; + ws_neighbor_class_entry_t *ws_neighbour = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, attribute_index); + etx_storage_t *etx_entry = etx_storage_entry_get(interface->id, attribute_index); + + if (interface->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + if (!ws_neighbour || !etx_entry) { + return 0xffff; + } + } else { + + 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"); + return 0xffff; + } + + //If we are not following gbobal Broadcast synch + if (!interface->ws_info->pan_information.use_parent_bs) { + //We must know both information's here + if (!ws_neighbour->broadcast_shedule_info_stored || + !ws_neighbour->broadcast_timing_info_stored) { + return 0xffff; + } + } else { + if (!ws_neighbour->broadcast_timing_info_stored) { + //Global shedule is stored already + return 0xffff; + } + } + } + + etx = etx_local_etx_read(interface->id, attribute_index); + 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; +} + +static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur) +{ + int8_t ret_val = -1; + + if (!cur) { + return -1; + } + + if ((cur->configure_flags & INTERFACE_SETUP_MASK) != INTERFACE_SETUP_READY) { + tr_error("Interface not yet fully configured"); + return -2; + } + if (ws_fhss_initialize(cur) != 0) { + tr_error("fhss initialization failed"); + return -3; + } + + + // Save FHSS api + cur->ws_info->fhss_api = ns_sw_mac_get_fhss_api(cur->mac_api); + + ws_bootstrap_ll_address_validate(cur); + + addr_interface_set_ll64(cur, NULL); + cur->nwk_nd_re_scan_count = 0; + //WS_interface_up(cur); + // Trigger discovery for bootstrap + ret_val = nwk_6lowpan_up(cur); + if (ret_val) { + goto cleanup; + } + + /* 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; + // 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_solicit_timeout_set(cur->id, WS_DHCP_SOLICIT_TIMEOUT, WS_DHCP_SOLICIT_MAX_RT, WS_DHCP_SOLICIT_MAX_RC); + + + ws_nud_table_reset(cur); + + blacklist_params_set( + WS_BLACKLIST_ENTRY_LIFETIME, + WS_BLACKLIST_TIMER_MAX_TIMEOUT, + WS_BLACKLIST_TIMER_TIMEOUT, + WS_BLACKLIST_ENTRY_MAX_NBR, + WS_BLACKLIST_PURGE_NBR, + WS_BLACKLIST_PURGE_TIMER_TIMEOUT); + + ws_bootstrap_event_discovery_start(cur); + + return 0; +cleanup: + return ret_val; +} + +static int8_t ws_bootstrap_down(protocol_interface_info_entry_t *cur) +{ + if (!cur || !(cur->lowpan_info & INTERFACE_NWK_ACTIVE)) { + return -1; + } + + tr_debug("Wi-SUN ifdown"); + + // Reset WS information + // ws_common_reset(cur) + ws_llc_reset(cur); + if (nd_proxy_downstream_interface_unregister(cur->id) != 0) { + tr_warn("nd proxy unregister failed"); + } + ws_nud_table_reset(cur); + dhcp_client_delete(cur->id); + ws_eapol_relay_delete(cur); + ws_eapol_auth_relay_delete(cur); + ws_pae_controller_stop(cur); + blacklist_clear(); + + return nwk_6lowpan_down(cur); +} + +void ws_bootstrap_configuration_reset(protocol_interface_info_entry_t *cur) +{ + // Configure IP stack to operate as Wi-SUN node + + // Do not process beacons + cur->mac_parameters->beacon_ind = NULL; + cur->mac_parameters->mac_security_level = 0; + + // Set default parameters to interface + cur->configure_flags = INTERFACE_BOOTSTRAP_DEFINED; + cur->configure_flags |= INTERFACE_SECURITY_DEFINED; + cur->lowpan_info = 0; + + switch (cur->bootsrap_mode) { + // case NET_6LOWPAN_SLEEPY_HOST: + case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST: + break; + + case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER: + case ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER: + cur->lowpan_info |= INTERFACE_NWK_ROUTER_DEVICE; + break; + + default: + tr_err("Invalid bootstrap_mode"); + } + + cur->nwk_bootstrap_state = ER_ACTIVE_SCAN; + cur->ws_info->network_pan_id = 0xffff; + cur->ws_info->trickle_pas_running = false; + cur->ws_info->trickle_pa_running = false; + cur->ws_info->trickle_pcs_running = false; + cur->ws_info->trickle_pc_running = false; + + //cur->mac_security_key_usage_update_cb = ws_management_mac_security_key_update_cb; + return; +} + +static bool ws_bootstrap_network_name_matches(const struct mcps_data_ie_list *ie_ext, const char *network_name_ptr) +{ + if (!network_name_ptr || !ie_ext) { + return false; + } + + ws_wp_network_name_t network_name; + if (!ws_wp_nested_network_name_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &network_name)) { + tr_warn("No network name IE"); + return false; + } + + if (network_name_ptr == NULL || strncmp(network_name_ptr, (char *)network_name.network_name, network_name.network_name_length) != 0) { + return false; + } + return true; +} + +static void ws_bootstrap_pan_advertisement_analyse_active(struct protocol_interface_info_entry *cur, ws_pan_information_t *pan_information) +{ + /* TODO In Active state + * + * A consistent transmission is defined as a PAN Advertisement received by a node with PAN ID and + * NETNAME-IE / Network Name matching that of the receiving node, and with a PAN-IE / Routing Cost + * the same or better than (less than or equal to) that of the receiving node. + * + * Inconsistent: + * PAN Advertisement solicit + * + * A PAN Advertisement received by a node with PAN ID and NETNAME-IE / Network name matching + * that of the receiving node, and PAN-IE / Routing Cost worse than (greater than) that of the receiving node. + * + */ + + if (pan_information->routing_cost <= cur->ws_info->pan_information.routing_cost) { + trickle_consistent_heard(&cur->ws_info->trickle_pan_advertisement); + } else { + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery); + } + + // automatic network size adjustment + if (cur->ws_info->network_size_config == NETWORK_SIZE_AUTOMATIC && + cur->bootsrap_mode != ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER && + cur->ws_info->pan_information.pan_size != pan_information->pan_size) { + ws_common_network_size_configure(cur, pan_information->pan_size); + } +} + +static void ws_bootstrap_pan_advertisement_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us) +{ + + //Validate Pan Conrfirmation is at packet + ws_pan_information_t pan_information; + if (!ws_wp_nested_pan_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &pan_information)) { + // Corrupted + tr_error("No pan information"); + return; + } + + // if in active scan state + if (!ws_bootstrap_state_discovery(cur)) { + 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; + } + } + + + // Check pan flags so that it is valid + if (!pan_information.rpl_routing_method) { + // NOT RPL routing + tr_warn("Not supported routing"); + return; + } + + /* TODO smart neighbour process + * + * Unsecure packet we cant trust the device? + * + * This message is received from tens of devices and we must select the best parent + * + * We save the best parent and create entry when we have selected the EAPOL target + * + */ + + // Save route cost for all neighbours + llc_neighbour_req_t neighbor_info; + if (ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, false)) { + neighbor_info.ws_neighbor->routing_cost = pan_information.routing_cost; + } + + // Save the best network parent + + if (ws_bootstrap_state_discovery(cur)) { + // Discovery state processing + tr_info("potential parent addr:%s panid:%x signal:%d", trace_array(data->SrcAddr, 8), data->SrcPANId, data->signal_dbm); + + // This parent is selected and used for authentication. + if (memcmp(cur->ws_info->parent_info.addr, ADDR_UNSPECIFIED, 8) != 0) { + + // if we dont have higher than threshold signal only signal level decides parent + if (ws_neighbor_class_rssi_from_dbm_calculate(cur->ws_info->parent_info.signal_dbm) < (CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS) && + ws_neighbor_class_rssi_from_dbm_calculate(data->signal_dbm) > ws_neighbor_class_rssi_from_dbm_calculate(cur->ws_info->parent_info.signal_dbm)) { + // automatically select the best quality link from the below threshold + goto parent_selected; + } + // Drop if signal quality is not good enough + if (ws_neighbor_class_rssi_from_dbm_calculate(data->signal_dbm) < (CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS)) { + tr_info("EAPOL target dropped Link quality too low"); + return; + } + + // Select the lowest PAN cost + 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"); + 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"); + return; + } + + } else { + // First advertise heard + + if (ws_neighbor_class_rssi_from_dbm_calculate(data->signal_dbm) < (CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS)) { + // First neighbor is too low we need to wait one extra trickle + cur->bootsrap_state_machine_cnt += cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_8bit() % 50; + } + } + +parent_selected: + // Parent valid store information + cur->ws_info->parent_info.ws_utt = *ws_utt; + // Saved from unicast IE + cur->ws_info->parent_info.ws_us = *ws_us; + + // Saved from Pan information, do not overwrite pan_version as it is not valid here + cur->ws_info->parent_info.pan_information.pan_size = pan_information.pan_size; + cur->ws_info->parent_info.pan_information.routing_cost = pan_information.routing_cost; + cur->ws_info->parent_info.pan_information.use_parent_bs = pan_information.use_parent_bs; + cur->ws_info->parent_info.pan_information.rpl_routing_method = pan_information.rpl_routing_method; + cur->ws_info->parent_info.pan_information.version = pan_information.version; + + // Saved from message + cur->ws_info->parent_info.timestamp = data->timestamp; + cur->ws_info->parent_info.pan_id = data->SrcPANId; + cur->ws_info->parent_info.link_quality = data->mpduLinkQuality; + cur->ws_info->parent_info.signal_dbm = data->signal_dbm; + memcpy(cur->ws_info->parent_info.addr, data->SrcAddr, 8); + + tr_info("New parent addr:%s panid:%x signal:%d", trace_array(cur->ws_info->parent_info.addr, 8), cur->ws_info->parent_info.pan_id, cur->ws_info->parent_info.signal_dbm); + return; + } + // Active state processing + 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; + } +} + +static void ws_bootstrap_pan_advertisement_solicit_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us) +{ + + (void)data; + (void)ws_utt; + (void)ws_us; + /* + * An inconsistent transmission is defined as: + * A PAN Advertisement Solicit with NETNAME-IE matching that of the receiving node. + */ + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery); + /* + * A consistent transmission is defined as + * a PAN Advertisement Solicit with NETNAME-IE / Network Name matching that configured on the receiving node. + */ + trickle_consistent_heard(&cur->ws_info->trickle_pan_advertisement_solicit); +} + + +static void ws_bootstrap_pan_config_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us) +{ + + uint16_t pan_version; + 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; + } + ws_bt_ie_t ws_bt_ie; + if (!ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt_ie)) { + tr_warn("BT-IE"); + return; + } + + /* + * A consistent transmission is defined as a PAN Configuration with a PAN-ID matching that of the receiving node and + * a PANVER-IE / PAN Version greater than or equal to the receiving node’s current PAN version. + * + * A inconsistent transmission is defined as: + * + * A PAN Configuration with PAN-ID matching that of the receiving node and a + * PANVER-IE / PAN Version that is less than the receiving node’s current PAN version. + */ + + // TODO Add this to neighbor table + // TODO save all information from config message if version number has changed + + if (!ws_wp_nested_pan_version_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &pan_version)) { + // Corrupted + tr_warn("no version"); + return; + } + + gtkhash_ptr = ws_wp_nested_gtkhash_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength); + + if (!gtkhash_ptr) { + // Corrupted + tr_error("No gtk hash"); + return; + } + + if (!ws_wp_nested_bs_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_bs_ie)) { + // Corrupted + tr_error("No broadcast schedule"); + return; + } + llc_neighbour_req_t neighbor_info; + if (!ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, true)) { + return; + } + + etx_lqi_dbm_update(cur->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index); + //Update Neighbor Broadcast and Unicast Parameters + 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); + ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt_ie, data->timestamp); + 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 + if (cur->ws_info->pan_information.pan_version == pan_version) { + trickle_consistent_heard(&cur->ws_info->trickle_pan_config); + } else { + tr_info("different pan version heard"); + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); + 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) { + //Border router does not learn network information + 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 + */ + tr_info("Updated PAN configuration heard"); + + 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); + + if (!cur->ws_info->configuration_learned) { + // Generate own hopping schedules Follow first parent broadcast and plans and also use same unicast dwell + tr_info("learn network configuration"); + cur->ws_info->configuration_learned = true; + // 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); + // 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; + } +} + +static void ws_bootstrap_pan_config_solicit_analyse(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, ws_utt_ie_t *ws_utt, ws_us_ie_t *ws_us) +{ + 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; + } + + /* TODO smart neighbour process + * + * Unsecure packet we cant trust the device? + * + * Question mark in specification also present, now we create neighbour. + * this is moved in future to NS/ND processing triggered by RPL + * + */ + + llc_neighbour_req_t neighbor_info; + if (!ws_bootstrap_neighbor_info_request(cur, data->SrcAddr, &neighbor_info, true)) { + return; + } + + 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 + * a PAN-ID matching that of the receiving node and a NETNAME-IE / Network Name + * matching that configured on the receiving node. + */ + trickle_consistent_heard(&cur->ws_info->trickle_pan_config_solicit); + /* + * inconsistent transmission is defined as either: + * A PAN Configuration Solicit with a PAN-ID matching that of the receiving node and + * a NETNAME-IE / Network Name matching the network name configured on the receiving + */ + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); +} +static bool ws_bootstrap_network_found(protocol_interface_info_entry_t *cur) +{ + tr_debug("analyze network discovery result"); + + // This parent is used for authentication to the network + if (memcmp(cur->ws_info->parent_info.addr, ADDR_UNSPECIFIED, 8) == 0) { + // No parent found yet + return false; + } + return true; +} + +static bool ws_channel_plan_zero_compare(ws_channel_plan_zero_t *rx_plan, ws_hopping_schedule_t *hopping_schdule) +{ + if (rx_plan->operation_class != hopping_schdule->operating_class) { + return false; + } else if (rx_plan->regulator_domain != hopping_schdule->regulatory_domain) { + return false; + } + return true; +} + +static bool ws_channel_plan_one_compare(ws_channel_plan_one_t *rx_plan, ws_hopping_schedule_t *hopping_schdule) +{ + uint16_t num_of_channel = hopping_schdule->number_of_channels; + if (rx_plan->ch0 != hopping_schdule->ch0_freq) { + return false; + } else if (rx_plan->channel_spacing != hopping_schdule->channel_spacing) { + return false; + } else if (rx_plan->number_of_channel != num_of_channel) { + return false; + } + return true; +} + + + + +static void ws_bootstrap_asynch_ind(struct protocol_interface_info_entry *cur, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, uint8_t message_type) +{ + + if (data->SrcAddrMode != MAC_ADDR_MODE_64_BIT) { + // Not from long address + return; + } + //Validate network name + switch (message_type) { + case WS_FT_PAN_ADVERT: + case WS_FT_PAN_ADVERT_SOL: + case WS_FT_PAN_CONF_SOL: + //Check Network Name + if (!ws_bootstrap_network_name_matches(ie_ext, cur->ws_info->network_name)) { + // Not in our network + return; + } + break; + case WS_FT_PAN_CONF: + break; + default: + return; + } + + //UTT-IE and US-IE are mandatory for all Asynch Messages + ws_utt_ie_t ws_utt; + if (!ws_wh_utt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_utt)) { + // Corrupted + return; + } + + ws_us_ie_t ws_us; + if (!ws_wp_nested_us_read(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_us)) { + // Corrupted + return; + } + + //Compare Unicast channel Plan + if (ws_us.channel_plan != cur->ws_info->hopping_schdule.channel_plan) { + return; + } + + if (ws_us.channel_plan == 0) { + if (!ws_channel_plan_zero_compare(&ws_us.plan.zero, &cur->ws_info->hopping_schdule)) { + return; + } + } else if (ws_us.channel_plan == 1) { + if (!ws_channel_plan_one_compare(&ws_us.plan.one, &cur->ws_info->hopping_schdule)) { + return; + } + } + + //Handle Message's + switch (message_type) { + case WS_FT_PAN_ADVERT: + // Analyse Advertisement + tr_debug("received ADVERT"); + 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"); + ws_bootstrap_pan_advertisement_solicit_analyse(cur, data, &ws_utt, &ws_us); + break; + case WS_FT_PAN_CONF: + tr_debug("received CONFIG"); + ws_bootstrap_pan_config_analyse(cur, data, ie_ext, &ws_utt, &ws_us); + break; + default: + tr_debug("received CONFIG SOL"); + ws_bootstrap_pan_config_solicit_analyse(cur, data, &ws_utt, &ws_us); + break; + } +} + +static void ws_bootstrap_asynch_confirm(struct protocol_interface_info_entry *interface, uint8_t asynch_message) +{ + (void)interface; + (void)asynch_message; +} +static void ws_bootstrap_neighbor_table_clean(struct protocol_interface_info_entry *interface) +{ + uint8_t ll_target[16]; + + if (mac_neighbor_info(interface)->neighbour_list_size <= mac_neighbor_info(interface)->list_total_size - WS_NON_CHILD_NEIGHBOUR_COUNT) { + // Enough neighbor entries + return; + } + memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8); + + mac_neighbor_table_entry_t *neighbor_entry_ptr = NULL; + ns_list_foreach_safe(mac_neighbor_table_entry_t, cur, &mac_neighbor_info(interface)->neighbour_list) { + + if (cur->link_role == PRIORITY_PARENT_NEIGHBOUR) { + //This is our primary parent we cannot delete + continue; + } + + if (neighbor_entry_ptr && neighbor_entry_ptr->lifetime < cur->lifetime) { + // We have already shorter link entry found this cannot replace it + 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; + } + + 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; + } + neighbor_entry_ptr = cur; + } + if (neighbor_entry_ptr) { + tr_info("dropped oldest neighbour %s", trace_array(neighbor_entry_ptr->mac64, 8)); + mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_entry_ptr); + } + +} + +static bool ws_bootstrap_neighbor_info_request(struct protocol_interface_info_entry *interface, const uint8_t *mac_64, llc_neighbour_req_t *neighbor_buffer, bool request_new) +{ + neighbor_buffer->neighbor = mac_neighbor_table_address_discover(mac_neighbor_info(interface), mac_64, ADDR_802_15_4_LONG); + if (neighbor_buffer->neighbor) { + neighbor_buffer->ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor_buffer->neighbor->index); + if (!neighbor_buffer->ws_neighbor) { + return false; + } + return true; + } + if (!request_new) { + return false; + } + uint8_t ll_target[16]; + memcpy(ll_target, ADDR_LINK_LOCAL_PREFIX, 8); + memcpy(ll_target + 8, mac_64, 8); + ll_target[8] ^= 2; + + + if (blacklist_reject(ll_target)) { + // Rejected by blacklist + return false; + } + + ws_bootstrap_neighbor_table_clean(interface); + + neighbor_buffer->neighbor = ws_bootstrap_mac_neighbor_add(interface, mac_64); + + if (!neighbor_buffer->neighbor) { + return false; + } + + neighbor_buffer->ws_neighbor = ws_neighbor_class_entry_get(&interface->ws_info->neighbor_storage, neighbor_buffer->neighbor->index); + if (!neighbor_buffer->ws_neighbor) { + mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_buffer->neighbor); + return false; + } + return true; +} + + +static void ws_neighbor_entry_remove_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data) +{ + + protocol_interface_info_entry_t *cur = user_data; + lowpan_adaptation_remove_free_indirect_table(cur, entry_ptr); + // Sleepy host + if (cur->lowpan_info & INTERFACE_NWK_CONF_MAC_RX_OFF_IDLE) { + mac_data_poll_protocol_poll_mode_decrement(cur); + } + + //TODO State machine check here + + if (entry_ptr->ffd_device) { + protocol_6lowpan_release_short_link_address_from_neighcache(cur, entry_ptr->mac16); + protocol_6lowpan_release_long_link_address_from_neighcache(cur, entry_ptr->mac64); + } + ws_bootstrap_neighbor_delete(cur, entry_ptr->index); +} + +static bool ws_neighbor_entry_nud_notify(mac_neighbor_table_entry_t *entry_ptr, void *user_data) +{ + uint32_t time_from_start = entry_ptr->link_lifetime - entry_ptr->lifetime; + bool activate_nud = false; + protocol_interface_info_entry_t *cur = user_data; + + ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, entry_ptr->index); + etx_storage_t *etx_entry = etx_storage_entry_get(cur->id, entry_ptr->index); + + if (!entry_ptr->trusted_device || !ws_neighbor || !etx_entry) { + return false; + } + + if (time_from_start > WS_NEIGHBOR_NUD_TIMEOUT) { + + if (time_from_start > WS_NEIGHBOR_NUD_TIMEOUT * 1.5) { + activate_nud = true; + } else { + uint16_t switch_prob = randLIB_get_random_in_range(0, WS_NUD_RANDOM_SAMPLE_LENGTH - 1); + //Take Random from time WS_NEIGHBOR_NUD_TIMEOUT - WS_NEIGHBOR_NUD_TIMEOUT*1.5 + if (switch_prob < WS_NUD_RANDOM_COMPARE) { + activate_nud = true; + } + } + } else if (etx_entry->etx_samples < WS_NEIGBOR_ETX_SAMPLE_MAX) { + //Take Random number for trig a prope. + //ETX Sample 0: random 1-8 + //ETX Sample 1: random 2-16 + //ETX Sample 2: random 4-32 + uint32_t probe_period = WS_PROBE_INIT_BASE_SECONDS << etx_entry->etx_samples; + uint32_t time_block = 1 << etx_entry->etx_samples; + if (time_from_start >= probe_period) { + tr_debug("Link Probe test %u Sample trig", etx_entry->etx_samples); + activate_nud = true; + } else if (time_from_start > time_block) { + uint16_t switch_prob = randLIB_get_random_in_range(0, probe_period - 1); + //Take Random from time WS_NEIGHBOR_NUD_TIMEOUT - WS_NEIGHBOR_NUD_TIMEOUT*1.5 + if (switch_prob < 2) { + tr_debug("Link Probe test with jitter %"PRIu32", sample %u", time_from_start, etx_entry->etx_samples); + activate_nud = true; + } + } + } + + if (!activate_nud) { + return false; + } + + ws_nud_table_entry_t *entry = ws_nud_entry_get_free(cur); + if (!entry) { + return false; + } + entry->neighbor_info = entry_ptr; + + if (etx_entry->etx_samples >= WS_NEIGBOR_ETX_SAMPLE_MAX) { + entry->nud_process = true; + } + return true; +} + +int ws_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode) +{ + int ret_val = 0; + + ws_neighbor_class_t neigh_info; + neigh_info.neigh_info_list = NULL; + neigh_info.list_size = 0; + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur) { + return -1; + } + + mac_description_storage_size_t buffer; + if (!cur->mac_api || !cur->mac_api->mac_storage_sizes_get || cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) { + return -2; + } + + if (buffer.key_description_table_size < 4) { + tr_err("MAC key_description_table_size too short %d<4", buffer.key_description_table_size); + return -2; + } + + if (!etx_storage_list_allocate(cur->id, buffer.device_decription_table_size)) { + return -1; + } + if (blacklist_init() != 0) { + tr_err("MLE blacklist init failed."); + return -1; + } + + switch (bootstrap_mode) { + // case NET_6LOWPAN_SLEEPY_HOST: + case NET_6LOWPAN_HOST: + cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST; + break; + case NET_6LOWPAN_ROUTER: + cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER; + break; + case NET_6LOWPAN_BORDER_ROUTER: + cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER; + break; + default: + return -3; + } + + if (!ws_neighbor_class_alloc(&neigh_info, buffer.device_decription_table_size)) { + ret_val = -1; + goto init_fail; + } + + //Disable allways by default + lowpan_adaptation_interface_mpx_register(interface_id, NULL, 0); + + mac_neighbor_table_delete(mac_neighbor_info(cur)); + mac_neighbor_info(cur) = mac_neighbor_table_create(buffer.device_decription_table_size, ws_neighbor_entry_remove_notify + , ws_neighbor_entry_nud_notify, cur); + if (!mac_neighbor_info(cur)) { + ret_val = -1; + goto init_fail; + } + + ws_llc_create(cur, &ws_bootstrap_asynch_ind, &ws_bootstrap_asynch_confirm, &ws_bootstrap_neighbor_info_request); + + mpx_api_t *mpx_api = ws_llc_mpx_api_get(cur); + if (!mpx_api) { + ret_val = -4; + goto init_fail; + } + + if (ws_common_allocate_and_init(cur) < 0) { + ret_val = -4; + goto init_fail; + } + + if (ws_bootstrap_tasklet_init(cur) != 0) { + ret_val = -4; + goto init_fail; + } + + //Register MPXUser to adapatation layer + if (lowpan_adaptation_interface_mpx_register(interface_id, mpx_api, MPX_LOWPAN_ENC_USER_ID) != 0) { + ret_val = -4; + goto init_fail; + } + + //Init PAE controller and set callback + if (ws_pae_controller_init(cur) < 0) { + ret_val = -4; + goto init_fail; + } + if (ws_pae_controller_cb_register(cur, &ws_bootstrap_authentication_completed, &ws_bootstrap_key_insert) < 0) { + ret_val = -4; + goto init_fail; + } + + //Init EAPOL PDU handler and register it to MPX + if (ws_eapol_pdu_init(cur) < 0) { + ret_val = -4; + goto init_fail; + } + if (ws_eapol_pdu_mpx_register(cur, mpx_api, MPX_KEY_MANAGEMENT_ENC_USER_ID != 0)) { + ret_val = -4; + // add deallocs + goto init_fail; + } + + cur->if_up = ws_bootstrap_up; + cur->if_down = ws_bootstrap_down; + cur->ws_info->neighbor_storage = neigh_info; + cur->etx_read_override = ws_etx_read; + + ws_bootstrap_configuration_reset(cur); + addr_notification_register(ws_bootstrap_address_notification_cb); + //Enable MAC Security by pass + mlme_set_t set_req; + bool state = true; + set_req.attr = macAcceptByPassUnknowDevice; + set_req.attr_index = 0; + set_req.value_pointer = &state; + set_req.value_size = sizeof(bool); + cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_req); + + // Set the default parameters for MPL + cur->mpl_proactive_forwarding = true; + + cur->mpl_data_trickle_params.Imin = MPL_MS_TO_TICKS(DATA_MESSAGE_IMIN); + cur->mpl_data_trickle_params.Imax = MPL_MS_TO_TICKS(DATA_MESSAGE_IMAX); + cur->mpl_data_trickle_params.TimerExpirations = DATA_MESSAGE_TIMER_EXPIRATIONS; + cur->mpl_data_trickle_params.k = 8; + + // Specification is ruling out the compression mode, but we are now doing it. + cur->mpl_seed = true; + cur->mpl_seed_id_mode = MULTICAST_MPL_SEED_ID_IPV6_SRC_FOR_DOMAIN; + cur->mpl_seed_set_entry_lifetime = MPL_SEED_SET_ENTRY_TIMEOUT; + + cur->mpl_control_trickle_params.TimerExpirations = 0; + + mpl_domain_create(cur, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL); + addr_add_group(cur, ADDR_REALM_LOCAL_ALL_NODES); + addr_add_group(cur, ADDR_REALM_LOCAL_ALL_ROUTERS); + + return 0; + + //Error handling and free memory +init_fail: + lowpan_adaptation_interface_mpx_register(interface_id, NULL, 0); + ws_eapol_pdu_mpx_register(cur, NULL, 0); + mac_neighbor_table_delete(mac_neighbor_info(cur)); + etx_storage_list_allocate(cur->id, 0); + ws_neighbor_class_dealloc(&neigh_info); + ws_llc_delete(cur); + ws_eapol_pdu_delete(cur); + ws_pae_controller_delete(cur); + return ret_val; +} + +int ws_bootstrap_restart(int8_t interface_id) +{ + protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !cur->ws_info) { + return -1; + } + ws_bootstrap_event_discovery_start(cur); + return 0; +} + +int ws_bootstrap_set_rf_config(protocol_interface_info_entry_t *cur, phy_rf_channel_configuration_s rf_configs) +{ + mlme_set_t set_request; + // Set RF configuration + set_request.attr = macRfConfiguration; + set_request.value_pointer = &rf_configs; + set_request.value_size = sizeof(phy_rf_channel_configuration_s); + cur->mac_api->mlme_req(cur->mac_api, MLME_SET, &set_request); + // Set Ack wait duration + uint16_t ack_wait_symbols = WS_ACK_WAIT_SYMBOLS + (WS_TACK_MAX_MS * (rf_configs.datarate / 1000)); + set_request.attr = macAckWaitDuration; + 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); + 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 + } + return 0; +} + + + +static void ws_bootstrap_mac_activate(protocol_interface_info_entry_t *cur, uint16_t channel, uint16_t panid, bool coordinator) +{ + mlme_start_t start_req; + memset(&start_req, 0, sizeof(mlme_start_t)); + + cur->mac_parameters->pan_id = panid; + cur->mac_parameters->mac_channel = channel; + + start_req.PANId = panid; + start_req.LogicalChannel = channel; + start_req.BeaconOrder = 0x0f; + start_req.SuperframeOrder = 0x0f; + start_req.PANCoordinator = coordinator; + + if (cur->mac_api) { + cur->mac_api->mlme_req(cur->mac_api, MLME_START, (void *)&start_req); + } +} + +static void ws_bootstrap_fhss_activate(protocol_interface_info_entry_t *cur) +{ + tr_debug("FHSS activate"); + ws_fhss_enable(cur); + ws_llc_hopping_schedule_config(cur, &cur->ws_info->hopping_schdule); + // Only supporting fixed channel + + 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_activate(cur, cur->ws_info->fhss_uc_fixed_channel, cur->ws_info->network_pan_id, true); + return; +} + +static void ws_bootstrap_network_information_learn(protocol_interface_info_entry_t *cur) +{ + tr_debug("learn network information from parent"); + + // Start following network broadcast timing schedules + + // Regulatory domain saving? cant change? + + // Save network information + cur->ws_info->network_pan_id = cur->ws_info->parent_info.pan_id; + cur->ws_info->pan_information = cur->ws_info->parent_info.pan_information; + cur->ws_info->pan_information.pan_version = 0; // This is learned from actual configuration + + // TODO create parent neighbour table entry for unicast schedule to enable authentication + + return; +} +static void ws_bootstrap_network_configuration_learn(protocol_interface_info_entry_t *cur) +{ + tr_debug("Start using PAN configuration"); + + // 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); + // TODO update own fhss schedules we are starting to follow first parent + + return; +} + +static void ws_bootstrap_ip_stack_addr_clear(protocol_interface_info_entry_t *cur) +{ + tr_debug("ip stack address clear"); + ns_list_foreach_safe(if_address_entry_t, addr, &cur->ip_addresses) { + if (addr->source != ADDR_SOURCE_STATIC && + addr_ipv6_scope(addr->address, cur) > IPV6_SCOPE_LINK_LOCAL) { + // Remove all exept User set address + addr_delete_entry(cur, addr); + } + } +} + +static void ws_bootstrap_ip_stack_reset(protocol_interface_info_entry_t *cur) +{ + tr_debug("ip stack reset"); + // Delete all temporary cached information + ipv6_neighbour_cache_flush(&cur->ipv6_neighbour_cache); + lowpan_context_list_free(&cur->lowpan_contexts); +} + +static void ws_bootstrap_ip_stack_activate(protocol_interface_info_entry_t *cur) +{ + tr_debug("ip stack init"); + clear_power_state(ICMP_ACTIVE); + cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE; + ws_bootstrap_ip_stack_reset(cur); +} + +static void ws_set_fhss_hop(protocol_interface_info_entry_t *cur) +{ + uint16_t own_rank = ws_bootstrap_rank_get(cur); + uint16_t rank_inc = ws_bootstrap_min_rank_inc_get(cur); + if (own_rank == 0xffff || rank_inc == 0xffff) { + return; + } + // Calculate own hop count. This method gets inaccurate when hop count increases. + uint8_t own_hop = (own_rank - rank_inc) / rank_inc; + ns_fhss_ws_set_hop_count(cur->ws_info->fhss_api, own_hop); + tr_debug("own hop: %u, own rank: %u, rank inc: %u", own_hop, own_rank, rank_inc); +} + +static void ws_address_registration_update(protocol_interface_info_entry_t *interface) +{ + if (!interface->ws_info->address_registration_event_active) { + interface->ws_info->address_registration_event_active = true; + tr_info("RPL parent update ... register ARO"); + ws_bootsrap_event_trig(WS_ADDRESS_ADDED, interface->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); + } +} + +static void ws_bootstrap_rpl_callback(rpl_event_t event, void *handle) +{ + + protocol_interface_info_entry_t *cur = handle; + if (!cur->rpl_domain || cur->interface_mode != INTERFACE_UP) { + return; + } + // if waiting for RPL and + if (event == RPL_EVENT_DAO_DONE) { + // Trigger statemachine check + cur->bootsrap_state_machine_cnt = 1; + rpl_dodag_info_t dodag_info; + struct rpl_instance *instance = rpl_control_enumerate_instances(cur->rpl_domain, NULL); + + if (instance && rpl_control_read_dodag_info(instance, &dodag_info)) { + tr_debug("Enable DHCPv6 relay"); + dhcp_relay_agent_enable(cur->id, dodag_info.dodag_id); + + tr_debug("Start EAPOL relay"); + // Set both own port and border router port to 10253 + ws_eapol_relay_start(cur, EAPOL_RELAY_SOCKET_PORT, dodag_info.dodag_id, EAPOL_RELAY_SOCKET_PORT); + // Set network information to PAE + ws_pae_controller_nw_info_set(cur, cur->ws_info->network_pan_id, cur->ws_info->network_name); + // Network key is valid + ws_pae_controller_nw_key_valid(cur); + } + + ws_set_fhss_hop(cur); + + } else if (event == RPL_EVENT_LOCAL_REPAIR_NO_MORE_DIS) { + /* + * RPL goes to passive mode, but does not require any extra changed + * + * We could remove our current addresses learned from RPL + * We could send solicit for configuration and then select new parent when those arrive + * + */ + + } else if (event == RPL_EVENT_DAO_PARENT_SWITCH) { + ws_address_registration_update(cur); + } + cur->ws_info->rpl_state = event; + tr_info("RPL event %d", event); +} + +static void ws_dhcp_client_global_adress_cb(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], bool register_status) +{ + (void)prefix; + (void)interface; + //TODO add handler for negative status + 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"); + } + } +} + +void ws_dhcp_client_address_delete(protocol_interface_info_entry_t *cur, uint8_t *prefix) +{ + dhcp_client_global_address_delete(cur->id, NULL, prefix); +} + +bool ws_eapol_relay_state_active(protocol_interface_info_entry_t *cur) +{ + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER || cur->nwk_bootstrap_state == ER_BOOTSRAP_DONE) { + return true; + } + + return false; +} + +static void ws_rpl_prefix_callback(prefix_entry_t *prefix, void *handle, uint8_t *parent_link_local) +{ + protocol_interface_info_entry_t *cur = (protocol_interface_info_entry_t *) handle; + /* Check if A-Flag. + * A RPL node may use this option for the purpose of Stateless Address Autoconfiguration (SLAAC) + * 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); + } + } 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 + 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; + } + } + } +} + +static void ws_bootstrap_rpl_activate(protocol_interface_info_entry_t *cur) +{ + tr_debug("RPL Activate"); + bool downstream = true; + bool leaf = false; + + addr_add_router_groups(cur); + rpl_control_set_domain_on_interface(cur, protocol_6lowpan_rpl_domain, downstream); + rpl_control_set_callback(protocol_6lowpan_rpl_domain, ws_bootstrap_rpl_callback, ws_rpl_prefix_callback, cur); + // If i am router I Do this + rpl_control_force_leaf(protocol_6lowpan_rpl_domain, leaf); + + cur->ws_info->rpl_state = 0xff; // Set invalid state and learn from event +} + +static void ws_bootstrap_network_start(protocol_interface_info_entry_t *cur) +{ + //Set Network names, Pan information configure, hopping schedule & GTKHash + ws_llc_set_network_name(cur, (uint8_t *)cur->ws_info->network_name, strlen(cur->ws_info->network_name)); + ws_llc_set_pan_information_pointer(cur, &cur->ws_info->pan_information); +} + +static void ws_bootstrap_network_discovery_configure(protocol_interface_info_entry_t *cur) +{ + // Reset information to defaults + cur->ws_info->network_pan_id = 0xffff; + + ws_common_regulatory_domain_config(cur); + ws_fhss_discovery_configure(cur); + + //Set Network names, Pan information configure, hopping schedule & GTKHash + ws_llc_set_network_name(cur, (uint8_t *)cur->ws_info->network_name, strlen(cur->ws_info->network_name)); +} + + +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); +} + +// Start network scan +static void ws_bootstrap_start_discovery(protocol_interface_info_entry_t *cur) +{ + tr_debug("router discovery start"); + ws_bootstrap_state_change(cur, ER_ACTIVE_SCAN); + cur->nwk_nd_re_scan_count = 0; + cur->ws_info->configuration_learned = false; + cur->ws_info->pan_version_timeout_timer = 0; + // Clear parent info + memset(cur->ws_info->parent_info.addr, 0, 8); + + // Clear learned neighbours + ws_bootstrap_neighbor_list_clean(cur); + + // Clear RPL information + rpl_control_remove_domain_from_interface(cur); + + // Clear ip stack from old information + ws_bootstrap_ip_stack_reset(cur); + // New network scan started old addresses not assumed valid anymore + ws_bootstrap_ip_stack_addr_clear(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); + + // 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; +} + +// Start authentication +static void ws_bootstrap_start_authentication(protocol_interface_info_entry_t *cur) +{ + tr_debug("authentication start"); + ws_pae_controller_authenticate(cur); +} + + +static void ws_bootstrap_key_insert(protocol_interface_info_entry_t *cur, uint8_t gtk_index, uint8_t *gtk) +{ + // Convert GTK to Group AES Key (GAK) + + // Verify HASH etc. + + // 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_authentication_completed(protocol_interface_info_entry_t *cur, bool success) +{ + if (success) { + tr_debug("authentication success"); + ws_bootstrap_event_configuration_start(cur); + } else { + tr_debug("authentication failed"); + // What else to do to start over again... + ws_bootstrap_event_discovery_start(cur); + } +} + +// Start configuration learning +static void ws_bootstrap_start_configuration_learn(protocol_interface_info_entry_t *cur) +{ + tr_debug("router configuration learn start"); + ws_bootstrap_state_change(cur, ER_SCAN); + + cur->ws_info->configuration_learned = false; + // Clear parent info + + memset(cur->ws_info->parent_info.addr, 0, 8); + + // Clear all temporary information + ws_bootstrap_ip_stack_reset(cur); + + cur->ws_info->pas_requests = 0; + // Reset advertisement solicit trickle to start discovering network + cur->ws_info->trickle_pcs_running = true; + trickle_start(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery); + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery); +} +static void ws_bootstrap_rpl_scan_start(protocol_interface_info_entry_t *cur) +{ + tr_debug("Start RPL learn"); + // routers wait until RPL root is contacted + ws_bootstrap_state_change(cur, ER_RPL_SCAN); + // Set timeout for check to 30 -60 seconds + cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(WS_RPL_DIS_INITIAL_TIMEOUT / 2, WS_RPL_DIS_INITIAL_TIMEOUT); +} + +/* + * Event transitions + * + * */ +void ws_bootstrap_event_discovery_start(protocol_interface_info_entry_t *cur) +{ + ws_bootsrap_event_trig(WS_DISCOVERY_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); +} +void ws_bootstrap_event_configuration_start(protocol_interface_info_entry_t *cur) +{ + ws_bootsrap_event_trig(WS_CONFIGURATION_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); +} +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); +} +void ws_bootstrap_event_operation_start(protocol_interface_info_entry_t *cur) +{ + ws_bootsrap_event_trig(WS_OPERATION_START, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); +} +void ws_bootstrap_event_routing_ready(protocol_interface_info_entry_t *cur) +{ + ws_bootsrap_event_trig(WS_ROUTING_READY, cur->bootStrapId, ARM_LIB_LOW_PRIORITY_EVENT, NULL); +} +void ws_bootstrap_configuration_trickle_reset(protocol_interface_info_entry_t *cur) +{ + trickle_inconsistent_heard(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery); +} + + +static void ws_bootstrap_pan_advert_solicit(protocol_interface_info_entry_t *cur) +{ + asynch_request_t async_req; + memset(&async_req, 0, sizeof(asynch_request_t)); + async_req.message_type = WS_FT_PAN_ADVERT_SOL; + //Request UTT Header and US and Net name from payload + async_req.wh_requested_ie_list.utt_ie = true; + async_req.wp_requested_nested_ie_list.us_ie = true; + async_req.wp_requested_nested_ie_list.net_name_ie = true; + + ws_generate_channel_list(async_req.channel_list.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + async_req.channel_list.channel_page = CHANNEL_PAGE_10; + async_req.security.SecurityLevel = 0; + + ws_llc_asynch_request(cur, &async_req); +} + +static void ws_bootstrap_pan_config_solicit(protocol_interface_info_entry_t *cur) +{ + asynch_request_t async_req; + memset(&async_req, 0, sizeof(asynch_request_t)); + async_req.message_type = WS_FT_PAN_CONF_SOL; + //Request UTT Header and US and Net name from payload + async_req.wh_requested_ie_list.utt_ie = true; + async_req.wp_requested_nested_ie_list.us_ie = true; + async_req.wp_requested_nested_ie_list.net_name_ie = true; + + ws_generate_channel_list(async_req.channel_list.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + async_req.channel_list.channel_page = CHANNEL_PAGE_10; + async_req.security.SecurityLevel = 0; + + ws_llc_asynch_request(cur, &async_req); +} + +static struct rpl_instance *ws_get_rpl_instance(protocol_interface_info_entry_t *cur) +{ + if (!cur || !cur->rpl_domain) { + return NULL; + } + struct rpl_instance *best_instance = NULL; + ns_list_foreach(struct rpl_instance, instance, &cur->rpl_domain->instances) { + best_instance = instance; + // Select best grounded and lowest rank? But there should be only one really + } + return best_instance; +} + +static uint16_t ws_bootstrap_routing_cost_calculate(protocol_interface_info_entry_t *cur) +{ + mac_neighbor_table_entry_t *mac_neighbor = mac_neighbor_entry_get_priority(mac_neighbor_info(cur)); + if (!mac_neighbor) { + return 0xffff; + } + ws_neighbor_class_entry_t *ws_neighbor = ws_neighbor_class_entry_get(&cur->ws_info->neighbor_storage, mac_neighbor->index); + if (!ws_neighbor) { + return 0xffff; + } + + uint16_t etx = etx_local_etx_read(cur->id, mac_neighbor->index); + if (etx == 0) { + etx = 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; + } + etx = etx >> 1; + + return ws_neighbor->routing_cost + etx; +} + +static uint16_t ws_bootstrap_rank_get(protocol_interface_info_entry_t *cur) +{ + struct rpl_instance *rpl_instance = ws_get_rpl_instance(cur); + if (!rpl_instance) { + return 0xffff; + } + return rpl_control_current_rank(rpl_instance); +} + + +static uint16_t ws_bootstrap_min_rank_inc_get(protocol_interface_info_entry_t *cur) +{ + struct rpl_instance *rpl_instance = ws_get_rpl_instance(cur); + if (!rpl_instance) { + return 0xffff; + } + struct rpl_dodag_info_t dodag_info; + if (!rpl_control_read_dodag_info(rpl_instance, &dodag_info)) { + return 0xffff; + } + return dodag_info.dag_min_hop_rank_inc; +} + +static void ws_bootstrap_pan_advert(protocol_interface_info_entry_t *cur) +{ + asynch_request_t async_req; + memset(&async_req, 0, sizeof(asynch_request_t)); + async_req.message_type = WS_FT_PAN_ADVERT; + //Request UTT Header, Pan information and US and Net name from payload + async_req.wh_requested_ie_list.utt_ie = true; + async_req.wp_requested_nested_ie_list.us_ie = true; + async_req.wp_requested_nested_ie_list.pan_ie = true; + async_req.wp_requested_nested_ie_list.net_name_ie = true; + + ws_generate_channel_list(async_req.channel_list.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + async_req.channel_list.channel_page = CHANNEL_PAGE_10; + async_req.security.SecurityLevel = 0; + + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + // Border routers write the NW size + cur->ws_info->pan_information.pan_size = ws_bbr_pan_size(cur); + cur->ws_info->pan_information.routing_cost = 0; + } else { + // Nodes need to calculate routing cost + // PAN size is saved from latest PAN advertisement + cur->ws_info->pan_information.routing_cost = ws_bootstrap_routing_cost_calculate(cur); + } + + + ws_llc_asynch_request(cur, &async_req); +} + +static void ws_bootstrap_pan_config(protocol_interface_info_entry_t *cur) +{ + asynch_request_t async_req; + memset(&async_req, 0, sizeof(asynch_request_t)); + async_req.message_type = WS_FT_PAN_CONF; + //Request UTT Header, Pan information and US and Net name from payload + async_req.wh_requested_ie_list.utt_ie = true; + async_req.wh_requested_ie_list.bt_ie = true; + async_req.wp_requested_nested_ie_list.us_ie = true; + async_req.wp_requested_nested_ie_list.bs_ie = true; + async_req.wp_requested_nested_ie_list.pan_version_ie = true; + async_req.wp_requested_nested_ie_list.gtkhash_ie = true; + async_req.wp_requested_nested_ie_list.vp_ie = true; + + ws_generate_channel_list(async_req.channel_list.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + + async_req.channel_list.channel_page = CHANNEL_PAGE_10; + async_req.security.SecurityLevel = mac_helper_default_security_level_get(cur); + async_req.security.KeyIdMode = mac_helper_default_security_key_id_mode_get(cur); + async_req.security.KeyIndex = mac_helper_default_key_index_get(cur); + ws_llc_asynch_request(cur, &async_req); +} + +static bool ws_bootstrap_address_registration_ongoing(protocol_interface_info_entry_t *cur) +{ + ns_list_foreach(if_address_entry_t, addr, &cur->ip_addresses) { + if (addr->addr_reg_pend != 0) { + return true; + } + } + + return false; +} + +static void ws_bootstrap_event_handler(arm_event_s *event) +{ + ws_bootsrap_event_type_e event_type; + event_type = (ws_bootsrap_event_type_e)event->event_type; + protocol_interface_info_entry_t *cur; + cur = protocol_stack_interface_info_get_by_bootstrap_id(event->receiver); + if (!cur) { + return; + } + + switch (event_type) { + case WS_INIT_EVENT: + tr_debug("tasklet init"); + break; + case WS_DISCOVERY_START: + tr_info("Discovery start"); + // All trickle timers stopped to allow entry from any state + 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; + + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + tr_debug("Border router start network"); + ws_pae_controller_auth_init(cur); + + // Randomize fixed channels. Only used if channel plan is fixed. + cur->ws_info->fhss_uc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->fhss_uc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels); + cur->ws_info->fhss_bc_fixed_channel = ws_randomize_fixed_channel(cur->ws_info->fhss_bc_fixed_channel, cur->ws_info->hopping_schdule.number_of_channels); + cur->ws_info->network_pan_id = randLIB_get_random_in_range(0, 0xfffd); + cur->ws_info->pan_information.pan_size = 0; + cur->ws_info->pan_information.pan_version = randLIB_get_random_in_range(0, 0xffff); + cur->ws_info->pan_information.routing_cost = 0; + 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); + cur->ws_info->pan_version_timer = PAN_VERSION_LIFETIME; + + // Set default parameters for FHSS when starting a discovery + ws_fhss_border_router_configure(cur); + ws_bootstrap_fhss_activate(cur); + ws_bootstrap_event_operation_start(cur); + + uint8_t ll_addr[16]; + addr_interface_get_ll_address(cur, ll_addr, 1); + + //SET EAPOL authenticator EUI64 + ws_pae_controller_border_router_addr_write(cur, cur->mac); + + // Set EAPOL relay to port 10255 and authenticator relay to 10253 (and to own ll address) + ws_eapol_relay_start(cur, BR_EAPOL_RELAY_SOCKET_PORT, ll_addr, EAPOL_RELAY_SOCKET_PORT); + + // 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 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; + } + ws_pae_controller_supp_init(cur); + + // Configure LLC for network discovery + ws_bootstrap_network_discovery_configure(cur); + ws_bootstrap_fhss_activate(cur); + // 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"); + // Old configuration is considered invalid stopping all + 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; + + // Build list of possible neighbours and learn first broadcast schedule + + ws_bootstrap_start_configuration_learn(cur); + break; + case WS_OPERATION_START: + tr_info("operation start"); + // Advertisements stopped during the RPL scan + 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; + // Activate RPL + // Activate IPv6 stack + ws_bootstrap_ip_stack_activate(cur); + ws_bootstrap_rpl_activate(cur); + ws_bootstrap_network_start(cur); + // Wait for RPL start + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER) { + ws_bootstrap_event_routing_ready(cur); + } else { + ws_bootstrap_rpl_scan_start(cur); + } + break; + case WS_ROUTING_READY: + tr_info("Routing ready"); + // stopped all to make sure we can enter here from any state + 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_advertise_start(cur); + ws_bootstrap_state_change(cur, ER_BOOTSRAP_DONE); + break; + case WS_ADDRESS_ADDED: + cur->ws_info->address_registration_event_active = false; + if (!ws_bootstrap_address_registration_ongoing(cur)) { + rpl_control_register_address(cur, (if_address_entry_t *) event->data_ptr); + } + break; + default: + tr_err("Invalid event received"); + break; + } +} + + +/* + * State machine + * + * */ +void ws_bootstrap_network_scan_process(protocol_interface_info_entry_t *cur) +{ + + if (!ws_bootstrap_network_found(cur)) { + // Next check will be after one trickle + cur->bootsrap_state_machine_cnt += cur->ws_info->trickle_params_pan_discovery.Imin + randLIB_get_8bit() % 50; + return; + } + tr_info("select network"); + + // Add EAPOL neighbour + llc_neighbour_req_t neighbor_info; + if (!ws_bootstrap_neighbor_info_request(cur, cur->ws_info->parent_info.addr, &neighbor_info, true)) { + return; + } + + ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &cur->ws_info->parent_info.ws_utt, cur->ws_info->parent_info.timestamp); + ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &cur->ws_info->parent_info.ws_us); + + + ws_bootstrap_network_information_learn(cur); + ws_bootstrap_fhss_activate(cur); + + ws_pae_controller_set_target(cur, cur->ws_info->parent_info.pan_id, cur->ws_info->parent_info.addr); // temporary!!! store since auth + ws_bootstrap_event_authentication_start(cur); + return; +} + +void ws_bootstrap_configure_process(protocol_interface_info_entry_t *cur) +{ + + if (cur->ws_info->configuration_learned) { + ws_bootstrap_network_configuration_learn(cur); + + + ws_bootstrap_event_operation_start(cur); + + + return; + } + return; +} +void ws_bootstrap_rpl_wait_process(protocol_interface_info_entry_t *cur) +{ + + if (cur->ws_info->rpl_state == RPL_EVENT_DAO_DONE) { + // RPL routing is ready + ws_bootstrap_event_routing_ready(cur); + } else if (!rpl_control_have_dodag(cur->rpl_domain)) { + // RPL not ready send DIS message if possible + if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER) { + // TODO Multicast DIS should be sent only if no DIO heard for some time + rpl_control_transmit_dis(cur->rpl_domain, cur, 0, 0, NULL, 0, ADDR_LINK_LOCAL_ALL_RPL_NODES); + } + // set timer for next DIS + cur->bootsrap_state_machine_cnt = randLIB_get_random_in_range(WS_RPL_DIS_TIMEOUT / 2, WS_RPL_DIS_TIMEOUT); + } + return; +} + +/* + + static bool ws_bootstrap_state_active(struct protocol_interface_info_entry *cur) +{ + if(cur->nwk_bootstrap_state == ER_BOOTSRAP_DONE) { + return true; + } + return false; +} + +static bool ws_bootstrap_state_configure(struct protocol_interface_info_entry *cur) +{ + // Think about the state value + if(cur->nwk_bootstrap_state == ER_SCAN) { + return true; + } + return false; +} + +static bool ws_bootstrap_state_wait_rpl(struct protocol_interface_info_entry *cur) +{ + // Think about the state value + if(cur->nwk_bootstrap_state == ER_RPL_SCAN) { + return true; + } + return false; +} +*/ +static bool ws_bootstrap_state_discovery(struct protocol_interface_info_entry *cur) +{ + if (cur->nwk_bootstrap_state == ER_ACTIVE_SCAN) { + return true; + } + return false; +} + +static void ws_bootstrap_state_change(protocol_interface_info_entry_t *cur, icmp_state_t nwk_bootstrap_state) +{ + 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_ACTIVE_SCAN: + tr_debug("WS SM:Active Scan"); + ws_bootstrap_network_scan_process(cur); + break; + case ER_SCAN: + tr_debug("WS SM:configuration Scan"); + ws_bootstrap_configure_process(cur); + break; + case ER_RPL_SCAN: + tr_debug("WS SM:Wait RPL to contact DODAG root"); + ws_bootstrap_rpl_wait_process(cur); + break; + case ER_BOOTSRAP_DONE: + tr_debug("WS SM:Bootstrap Done"); + // 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); + + } +} + +void ws_bootstrap_trickle_timer(protocol_interface_info_entry_t *cur, uint16_t ticks) +{ + if (cur->ws_info->trickle_pas_running && + trickle_timer(&cur->ws_info->trickle_pan_advertisement_solicit, &cur->ws_info->trickle_params_pan_discovery, ticks)) { + // send PAN advertisement solicit + tr_info("Send PAN advertisement Solicit"); + ws_bootstrap_pan_advert_solicit(cur); + } + if (cur->ws_info->trickle_pcs_running && + trickle_timer(&cur->ws_info->trickle_pan_config_solicit, &cur->ws_info->trickle_params_pan_discovery, ticks)) { + // send PAN Configuration solicit + if (cur->ws_info->pas_requests > PCS_MAX) { + // if MAX PCS sent restart discovery + tr_debug("Restart???"); + ws_bootstrap_event_discovery_start(cur); + return; + } + tr_info("Send PAN configuration Solicit"); + cur->ws_info->pas_requests++; + ws_bootstrap_pan_config_solicit(cur); + } + if (cur->ws_info->trickle_pa_running && + trickle_timer(&cur->ws_info->trickle_pan_advertisement, &cur->ws_info->trickle_params_pan_discovery, ticks)) { + // send PAN advertisement + tr_info("Send PAN advertisement"); + ws_bootstrap_pan_advert(cur); + } + if (cur->ws_info->trickle_pc_running && + trickle_timer(&cur->ws_info->trickle_pan_config, &cur->ws_info->trickle_params_pan_discovery, ticks)) { + // send PAN Configuration + tr_info("Send PAN configuration"); + ws_bootstrap_pan_config(cur); + } +} + + +void ws_bootstrap_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds) +{ + if (cur->ws_info->pan_version_timeout_timer) { + // PAN version timer running + if (cur->ws_info->pan_version_timeout_timer > seconds) { + cur->ws_info->pan_version_timeout_timer -= seconds; + } else { + // Border router has timed out + tr_warn("Border router has timed out"); + ws_bootstrap_event_discovery_start(cur); + } + } +} + +void ws_primary_parent_update(protocol_interface_info_entry_t *interface, mac_neighbor_table_entry_t *neighbor) +{ + if (interface->ws_info) { + 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_secondary_parent_update(interface); + } +} + +void ws_secondary_parent_update(protocol_interface_info_entry_t *interface) +{ + if (interface->ws_info) { + ns_list_foreach(if_address_entry_t, address, &interface->ip_addresses) { + if (!addr_is_ipv6_link_local(address->address)) { + address->addr_reg_done = 0; + ws_address_registration_update(interface); + } + } + } +} + +#endif //HAVE_WS diff --git a/source/6LoWPAN/ws/ws_bootstrap.h b/source/6LoWPAN/ws/ws_bootstrap.h index c967b1c86c..d678cd7e2c 100644 --- a/source/6LoWPAN/ws/ws_bootstrap.h +++ b/source/6LoWPAN/ws/ws_bootstrap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,10 @@ void ws_bootstrap_state_machine(protocol_interface_info_entry_t *cur); int ws_bootstrap_restart(int8_t interface_id); +int ws_bootstrap_set_rf_config(protocol_interface_info_entry_t *cur, phy_rf_channel_configuration_s rf_configs); + +int ws_bootstrap_neighbor_remove(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); @@ -66,11 +70,14 @@ void ws_dhcp_client_address_request(protocol_interface_info_entry_t *cur, uint8_ void ws_dhcp_client_address_delete(protocol_interface_info_entry_t *cur, uint8_t *prefix); +bool ws_eapol_relay_state_active(protocol_interface_info_entry_t *cur); + #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_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 new file mode 100644 index 0000000000..5d46b71172 --- /dev/null +++ b/source/6LoWPAN/ws/ws_common.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2018-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. + */ + +#include +#include "nsconfig.h" +#include "ns_types.h" +#include "ns_trace.h" +#include "randLIB.h" +#include +#include +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bootstrap.h" +#include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" +#include "Service_Libs/blacklist/blacklist.h" +#include "ws_management_api.h" +#include "mac_api.h" + +#ifdef HAVE_WS +#define TRACE_GROUP "wscm" + +int8_t DEVICE_MIN_SENS = -93; + +#define TRICKLE_IMIN_60_SECS (60 * 10) +#define TRICKLE_IMIN_30_SECS (30 * 10) +#define TRICKLE_IMIN_15_SECS (15 * 10) + +static const trickle_params_t trickle_params_pan_discovery_large = { + .Imin = TRICKLE_IMIN_60_SECS, /* 60 second; ticks are 1s */ + .Imax = TRICKLE_IMIN_60_SECS << 4, /* 960 seconds 16 min*/ + .k = 1, /* 1 */ + .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE +}; + +static const trickle_params_t trickle_params_pan_discovery_medium = { + .Imin = TRICKLE_IMIN_30_SECS, /* 30 second; ticks are 1s */ + .Imax = TRICKLE_IMIN_30_SECS << 3, /* 240 seconds 4 min*/ + .k = 1, /* 1 */ + .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE +}; + +static const trickle_params_t trickle_params_pan_discovery_small = { + .Imin = TRICKLE_IMIN_15_SECS, /* 15 second; ticks are 1s */ + .Imax = TRICKLE_IMIN_15_SECS << 2, /* 60 seconds 1 min*/ + .k = 1, /* 1 */ + .TimerExpirations = TRICKLE_EXPIRATIONS_INFINITE +}; + +uint16_t test_max_child_count_override = 0xffff; + + +int8_t ws_generate_channel_list(uint32_t *channel_mask, uint16_t number_of_channels, uint8_t regulatory_domain) +{ + (void)regulatory_domain; + for (uint8_t i = 0; i < number_of_channels; i++) { + channel_mask[0 + (i / 32)] |= (1 << (i % 32)); + } + return 0; +} + +static uint32_t ws_decode_channel_spacing(uint8_t channel_spacing) +{ + if (CHANNEL_SPACING_100 == channel_spacing) { + return 100000; + } else if (CHANNEL_SPACING_200 == channel_spacing) { + return 200000; + } else if (CHANNEL_SPACING_250 == channel_spacing) { + return 250000; + } else if (CHANNEL_SPACING_400 == channel_spacing) { + return 400000; + } else if (CHANNEL_SPACING_600 == channel_spacing) { + return 600000; + } + return 0; +} + +static uint32_t ws_get_datarate_using_operating_mode(uint8_t operating_mode) +{ + if ((OPERATING_MODE_1a == operating_mode) || (OPERATING_MODE_1b == operating_mode)) { + return 50000; + } else if ((OPERATING_MODE_2a == operating_mode) || (OPERATING_MODE_2b == operating_mode)) { + return 100000; + } else if (OPERATING_MODE_3 == operating_mode) { + return 150000; + } else if ((OPERATING_MODE_4a == operating_mode) || (OPERATING_MODE_4b == operating_mode)) { + return 200000; + } else if (OPERATING_MODE_5 == operating_mode) { + return 300000; + } + return 0; +} + +static phy_modulation_index_e ws_get_modulation_index_using_operating_mode(uint8_t operating_mode) +{ + if ((OPERATING_MODE_1b == operating_mode) || (OPERATING_MODE_2b == operating_mode) || (OPERATING_MODE_4b == operating_mode)) { + return MODULATION_INDEX_1_0; + } else { + return MODULATION_INDEX_0_5; + } +} + +static int ws_set_domain_rf_config(protocol_interface_info_entry_t *cur) +{ + phy_rf_channel_configuration_s rf_configs; + rf_configs.channel_0_center_frequency = (uint32_t)cur->ws_info->hopping_schdule.ch0_freq * 100000; + rf_configs.channel_spacing = ws_decode_channel_spacing(cur->ws_info->hopping_schdule.channel_spacing); + rf_configs.datarate = ws_get_datarate_using_operating_mode(cur->ws_info->hopping_schdule.operating_mode); + rf_configs.modulation_index = ws_get_modulation_index_using_operating_mode(cur->ws_info->hopping_schdule.operating_mode); + rf_configs.modulation = M_2FSK; + rf_configs.number_of_channels = cur->ws_info->hopping_schdule.number_of_channels; + ws_bootstrap_set_rf_config(cur, rf_configs); + return 0; +} + +int8_t ws_common_regulatory_domain_config(protocol_interface_info_entry_t *cur) +{ + cur->ws_info->hopping_schdule.channel_plan = 0; + + if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_KR) { + if (cur->ws_info->hopping_schdule.operating_class == 1) { + cur->ws_info->hopping_schdule.ch0_freq = 9171; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else if (cur->ws_info->hopping_schdule.operating_class == 2) { + cur->ws_info->hopping_schdule.ch0_freq = 9173; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; + } else { + return -1; + } + } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_EU) { + if (cur->ws_info->hopping_schdule.operating_class == 1) { + cur->ws_info->hopping_schdule.ch0_freq = 8631; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_100; + } else if (cur->ws_info->hopping_schdule.operating_class == 2) { + cur->ws_info->hopping_schdule.ch0_freq = 8631; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else if (cur->ws_info->hopping_schdule.operating_class == 3) { + cur->ws_info->hopping_schdule.ch0_freq = 8701; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_100; + } else if (cur->ws_info->hopping_schdule.operating_class == 4) { + cur->ws_info->hopping_schdule.ch0_freq = 8702; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else { + return -1; + } + } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_NA) { + if (cur->ws_info->hopping_schdule.operating_class == 1) { + cur->ws_info->hopping_schdule.ch0_freq = 9022; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else if (cur->ws_info->hopping_schdule.operating_class == 2) { + cur->ws_info->hopping_schdule.ch0_freq = 9024; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; + } else if (cur->ws_info->hopping_schdule.operating_class == 3) { + cur->ws_info->hopping_schdule.ch0_freq = 9026; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_600; + } else { + return -1; + } + } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_JP) { + if (cur->ws_info->hopping_schdule.operating_class == 1) { + cur->ws_info->hopping_schdule.ch0_freq = 9206; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else if (cur->ws_info->hopping_schdule.operating_class == 2) { + cur->ws_info->hopping_schdule.ch0_freq = 9209; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; + } else if (cur->ws_info->hopping_schdule.operating_class == 3) { + cur->ws_info->hopping_schdule.ch0_freq = 9208; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_600; + } else { + return -1; + } + } else if (cur->ws_info->hopping_schdule.regulatory_domain == REG_DOMAIN_WW) { + if (cur->ws_info->hopping_schdule.operating_class == 1) { + cur->ws_info->hopping_schdule.ch0_freq = 24002; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_200; + } else if (cur->ws_info->hopping_schdule.operating_class == 2) { + cur->ws_info->hopping_schdule.ch0_freq = 24004; + cur->ws_info->hopping_schdule.channel_spacing = CHANNEL_SPACING_400; + } else { + return -1; + } + } else { + return -1; + } + cur->ws_info->hopping_schdule.number_of_channels = (uint8_t)ws_common_channel_number_calc(cur->ws_info->hopping_schdule.regulatory_domain, cur->ws_info->hopping_schdule.operating_class); + if (!cur->ws_info->hopping_schdule.number_of_channels) { + return -1; + } + // Note: doesn't work for Brazil region + ws_generate_channel_list(cur->ws_info->hopping_schdule.channel_mask, cur->ws_info->hopping_schdule.number_of_channels, cur->ws_info->hopping_schdule.regulatory_domain); + ws_set_domain_rf_config(cur); + return 0; +} + +uint16_t ws_common_channel_number_calc(uint8_t regulatory_domain, uint8_t operating_class) +{ + if (regulatory_domain == REG_DOMAIN_KR) { + if (operating_class == 1) { + return 32; + } else if (operating_class == 2) { + return 16; + } + } else if (regulatory_domain == REG_DOMAIN_EU) { + if (operating_class == 1) { + return 69; + } else if (operating_class == 2) { + return 35; + } else if (operating_class == 3) { + return 55; + } else if (operating_class == 4) { + return 27; + } + } else if (regulatory_domain == REG_DOMAIN_NA) { + if (operating_class == 1) { + return 129; + } else if (operating_class == 2) { + return 64; + } else if (operating_class == 3) { + return 42; + } + } else if (regulatory_domain == REG_DOMAIN_JP) { + if (operating_class == 1) { + return 38; + } else if (operating_class == 2) { + return 18; + } else if (operating_class == 3) { + return 12; + } + } else if (regulatory_domain == REG_DOMAIN_WW) { + if (operating_class == 1) { + // TODO we dont support this yet, but it is used as test value + return 416; + } else if (operating_class == 2) { + return 207; + } + } + return 0; +} + +int8_t ws_common_allocate_and_init(protocol_interface_info_entry_t *cur) +{ + + if (!cur->ws_info) { + cur->ws_info = ns_dyn_mem_alloc(sizeof(ws_info_t)); + } + if (!cur->ws_info) { + return -1; + } + + memset(cur->ws_info, 0, sizeof(ws_info_t)); + ns_list_init(&cur->ws_info->active_nud_process); + ns_list_init(&cur->ws_info->free_nud_entries); + + cur->ws_info->pan_information.use_parent_bs = true; + cur->ws_info->pan_information.rpl_routing_method = true; + cur->ws_info->pan_information.version = WS_FAN_VERSION_1_0; + + cur->ws_info->hopping_schdule.regulatory_domain = REG_DOMAIN_EU; + cur->ws_info->hopping_schdule.operating_mode = OPERATING_MODE_3; + cur->ws_info->hopping_schdule.operating_class = 2; + ws_common_regulatory_domain_config(cur); + ws_common_network_size_configure(cur, 10); // defaults to small network size + + // Set defaults for the device. user can modify these. + cur->ws_info->fhss_uc_fixed_channel = 0xffff; + cur->ws_info->fhss_bc_fixed_channel = 0xffff; + cur->ws_info->fhss_uc_dwell_interval = WS_FHSS_UC_DWELL_INTERVAL; + cur->ws_info->fhss_bc_interval = WS_FHSS_BC_INTERVAL; + cur->ws_info->fhss_bc_dwell_interval = WS_FHSS_BC_DWELL_INTERVAL; + cur->ws_info->fhss_uc_channel_function = WS_DH1CF; + cur->ws_info->fhss_bc_channel_function = WS_DH1CF; + for (uint8_t n = 0; n < 8; n++) { + cur->ws_info->fhss_channel_mask[n] = 0xffffffff; + } + + return 0; +} +void ws_common_network_size_configure(protocol_interface_info_entry_t *cur, uint16_t network_size) +{ + // TODO Modify NUD timings based on network size + // TODO Modify EAPOLL timings + + if (network_size < 100) { + // Configure the Wi-SUN discovery trickle parameters + cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_small; + // default values are for Wi-SUN small network parameters + // imin: 14 (16s) + // doublings:3 (128s) + // redundancy; 0 Disabled + ws_bbr_rpl_config(0, 0, 0);// set the default values + } else if (network_size < 300) { + // Configure the Wi-SUN discovery trickle parameters + cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_medium; + // Something in between + // imin: 15 (32s) + // doublings:3 (262s) + // redundancy; 7 + ws_bbr_rpl_config(15, 3, 7); + } else { + // Configure the Wi-SUN discovery trickle parameters + cur->ws_info->trickle_params_pan_discovery = trickle_params_pan_discovery_large; + // Wi-SUN Large network parameters + // imin: 19 (524s, 9 min) + // doublings:1 (1048s, 17 min) + // redundancy; 1 Really heavy redundancy + ws_bbr_rpl_config(19, 1, 1); + } + return; +} + +void ws_common_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds) +{ + ws_bbr_seconds_timer(cur, seconds); + ws_bootstrap_seconds_timer(cur, seconds); + blacklist_ttl_update(seconds); +} + +void ws_common_fast_timer(protocol_interface_info_entry_t *cur, uint16_t ticks) +{ + ws_bootstrap_trickle_timer(cur, ticks); + ws_nud_active_timer(cur, ticks); +} + + +void ws_common_neighbor_update(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) +{ + //Neighbor connectected update + 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_nud_entry_remove_active(cur, mac_neighbor); + } +} + +void ws_common_aro_failure(protocol_interface_info_entry_t *cur, const uint8_t *ll_address) +{ + //Neighbor connectected update + ws_bootstrap_neighbor_remove(cur, ll_address); +} + +bool ws_common_allow_child_registration(protocol_interface_info_entry_t *interface) +{ + uint8_t child_count = 0; + uint8_t max_child_count = mac_neighbor_info(interface)->list_total_size - WS_NON_CHILD_NEIGHBOUR_COUNT; + + // Test API to limit child count + if (test_max_child_count_override != 0xffff) { + max_child_count = test_max_child_count_override; + } + + 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)) { + child_count++; + } + } + if (child_count >= max_child_count) { + tr_warn("Child registration not allowed %d/%d, max:%d", child_count, max_child_count, mac_neighbor_info(interface)->list_total_size); + return false; + } + tr_info("Child registration allowed %d/%d, max:%d", child_count, max_child_count, mac_neighbor_info(interface)->list_total_size); + return true; +} + + +#endif // HAVE_WS + diff --git a/source/6LoWPAN/ws/ws_common.h b/source/6LoWPAN/ws/ws_common.h index f2625f22ab..08cb4a5cc4 100644 --- a/source/6LoWPAN/ws/ws_common.h +++ b/source/6LoWPAN/ws/ws_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,10 +24,14 @@ #include "fhss_api.h" #include "fhss_config.h" #include "net_fhss.h" +#include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/ws/ws_common_defines.h" #include "6LoWPAN/ws/ws_neighbor_class.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" + +extern uint16_t test_max_child_count_override; + struct ws_pan_information_s; struct ws_neighbor_class_s; @@ -61,6 +65,8 @@ typedef struct ws_info_s { trickle_t trickle_pan_config; trickle_t trickle_pan_advertisement_solicit; trickle_t trickle_pan_advertisement; + trickle_params_t trickle_params_pan_discovery; + uint8_t network_size_config; // configuration for network size selection of application. uint8_t rpl_state; // state from rpl_event_t uint8_t pas_requests; // Amount of PAN solicits sent parent_info_t parent_info; @@ -98,20 +104,30 @@ int8_t ws_generate_channel_list(uint32_t *channel_mask, uint16_t number_of_chann int8_t ws_common_regulatory_domain_config(protocol_interface_info_entry_t *cur); +uint16_t ws_common_channel_number_calc(uint8_t regulatory_domain, uint8_t operating_class); + int8_t ws_common_allocate_and_init(protocol_interface_info_entry_t *cur); +void ws_common_network_size_configure(protocol_interface_info_entry_t *cur, uint16_t network_size); + void ws_common_seconds_timer(protocol_interface_info_entry_t *cur, uint32_t seconds); void ws_common_fast_timer(protocol_interface_info_entry_t *cur, uint16_t ticks); void ws_common_neighbor_update(protocol_interface_info_entry_t *cur, const uint8_t *ll_address); +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); + #define ws_info(cur) ((cur)->ws_info) #else #define ws_info(cur) ((ws_info_t *) NULL) #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_fast_timer(cur, ticks) ((void) 0) +#define ws_common_allow_child_registration(cur) (false) #endif //HAVE_WS diff --git a/source/6LoWPAN/ws/ws_common_defines.h b/source/6LoWPAN/ws/ws_common_defines.h index 5a4ceba72e..bf799edfe8 100644 --- a/source/6LoWPAN/ws/ws_common_defines.h +++ b/source/6LoWPAN/ws/ws_common_defines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ #define WH_IE_RSL_TYPE 4 /**< Received Signal Level information */ #define WH_IE_MHDS_TYPE 5 /**< MHDS information for mesh routing */ #define WH_IE_VH_TYPE 6 /**< Vendor header information */ +#define WH_IE_EA_TYPE 9 /**< Eapol Auhtenticator EUI-64 header information */ #define WS_WP_NESTED_IE 4 /**< WS nested Payload IE element'selement could include mltiple sub payload IE */ @@ -215,12 +216,28 @@ typedef struct ws_bs_ie { */ #define WS_RPL_DIS_TIMEOUT 1800 +/* + * MAC Ack wait duration in symbols. 2-FSK modulation used -> 1 bit per symbol. + */ +#define WS_ACK_WAIT_SYMBOLS 800 + +/* + * Tack max time in milliseconds. + */ +#define WS_TACK_MAX_MS 5 + /* Default FHSS timing information * */ -#define WS_FHSS_UC_DWELL_INTERVAL 250; -#define WS_FHSS_BC_INTERVAL 800; -#define WS_FHSS_BC_DWELL_INTERVAL 200; +#define WS_FHSS_UC_DWELL_INTERVAL 255; +#define WS_FHSS_BC_INTERVAL 1020; +#define WS_FHSS_BC_DWELL_INTERVAL 255; +/* + * EAPOL relay and PAE authenticator socket settings + */ +#define EAPOL_RELAY_SOCKET_PORT 10253 +#define BR_EAPOL_RELAY_SOCKET_PORT 10255 +#define PAE_AUTH_SOCKET_PORT 10254 #endif /* WS_COMMON_DEFINES_H_ */ diff --git a/source/6LoWPAN/ws/ws_config.h b/source/6LoWPAN/ws/ws_config.h new file mode 100644 index 0000000000..7720af7872 --- /dev/null +++ b/source/6LoWPAN/ws/ws_config.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018-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_CONFIG_H_ +#define WS_CONFIG_H_ + + +/*RPL parameters for DIO messages + * + * Small scale spec recomendation imin 15, doubling 2, redudancy 0 + * Small scale values imin 14, doubling 3, redudancy 0 + * Large scale network imin 19, doubling 1, redudancy 1 + * + */ + +#define WS_RPL_DIO_IMIN 14 +#define WS_RPL_DIO_DOUBLING 3 +#define WS_RPL_DIO_REDUNDANCY 0 + + +/* Border router version change interval + * + * Minimum interval at which a Border Router shall increment its PAN Version value. + */ + +#define PAN_VERSION_LIFETIME 240 + +#define RPL_VERSION_LIFETIME 5*3600 + +/* Border router connection lost timeout + * + * Interval within which a node expects to detect a change in PAN Version + * (delivered via a PAN Configuration frame / PAN-IE). + * + * the maximum Trickle interval specified for DISC_IMAX (32 minutes). + * + */ + +#define PAN_VERSION_TIMEOUT 1920 + +/* Routing Cost Weighting factor + */ +#define PRC_WEIGHT_FACTOR 256 + +/* Routing Cost Weighting factor + */ +#define PS_WEIGHT_FACTOR 64 + +/* Smoothing factor for RSL calculation 1/8 + */ +#define WS_RSL_SCALING 3 + +/* Device min sensitivity. This value is dynamically configured and depends on radio + * + * Default value for us is -93 + */ +extern int8_t DEVICE_MIN_SENS; + +/* Candidate parent Threshold + */ +#define CAND_PARENT_THRESHOLD 10 + +/* Candidate parent Threshold hysteresis + */ +#define CAND_PARENT_HYSTERISIS 3 + +/* Maximum amount of Pan Configuration Solicits before restarting Discovery. + */ +#define PCS_MAX 5 + + +/* Multicast MPL data message parameters + * IMIN = 10 seconds, IMAX = 3 doublings + */ + +#define DATA_MESSAGE_IMIN (10 * 1000) +#define DATA_MESSAGE_TIMER_EXPIRATIONS 3 +#define DATA_MESSAGE_IMAX (DATA_MESSAGE_IMIN) +#define MPL_SEED_SET_ENTRY_TIMEOUT (DATA_MESSAGE_IMAX * 24 * 4 / 1000) // 10 seconds per hop making this 240 seconds + +/* DHCP client timeout configuration values + * + */ +#define WS_DHCP_SOLICIT_TIMEOUT 60 +#define WS_DHCP_SOLICIT_MAX_RT 3600 +#define WS_DHCP_SOLICIT_MAX_RC 0 + + +/* Neighbour table configuration + * + * Amount of RPL candidate parents + * Amount of ND reply entries left + * rest are used as child count, but is related to neighbour table size + */ +#define WS_RPL_CANDIDATE_PARENT_COUNT 3 // Largest possible value +#define WS_TEMPORARY_NEIGHBOUR_ENTRIES 7 +#define WS_NON_CHILD_NEIGHBOUR_COUNT (WS_RPL_CANDIDATE_PARENT_COUNT + WS_TEMPORARY_NEIGHBOUR_ENTRIES) + +/* + * Neighbour blacklist timers + */ +#define WS_BLACKLIST_ENTRY_LIFETIME 60*30 // initial value for reject +#define WS_BLACKLIST_TIMER_MAX_TIMEOUT 60*60 // Can increase to this +#define WS_BLACKLIST_TIMER_TIMEOUT 60*30 // Blacklist is valid this time after first accept +#define WS_BLACKLIST_ENTRY_MAX_NBR 10 +#define WS_BLACKLIST_PURGE_NBR 3 +#define WS_BLACKLIST_PURGE_TIMER_TIMEOUT 60 + + +#endif /* WS_CONFIG_H_ */ diff --git a/source/6LoWPAN/ws/ws_eapol_auth_relay.c b/source/6LoWPAN/ws/ws_eapol_auth_relay.c new file mode 100644 index 0000000000..cad3a52128 --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_auth_relay.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "mac_api.h" +#include "mac_mcps.h" +#include "Common_Protocols/ipv6_constants.h" +#include "socket_api.h" +#include "6LoWPAN/MAC/mac_helper.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" +#include "6LoWPAN/ws/ws_eapol_relay_lib.h" +#include "6LoWPAN/ws/ws_eapol_auth_relay.h" +#include "common_functions.h" + +#ifdef HAVE_WS +#ifdef HAVE_PAE_AUTH + +#define TRACE_GROUP "wsar" + +typedef struct { + protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + ns_address_t remote_addr; /**< Remote address and port */ + ns_address_t relay_addr; /**< Relay address */ + int8_t socket_id; /**< Socket ID for relay */ + ns_list_link_t link; /**< Link */ +} eapol_auth_relay_t; + +static eapol_auth_relay_t *ws_eapol_auth_relay_get(protocol_interface_info_entry_t *interface_ptr); +static void ws_eapol_auth_relay_socket_cb(void *cb); +static int8_t ws_eapol_auth_relay_send_to_kmp(eapol_auth_relay_t *eapol_auth_relay, const uint8_t *eui_64, const uint8_t *ip_addr, uint16_t port, const void *data, uint16_t data_len); + +static NS_LIST_DEFINE(eapol_auth_relay_list, eapol_auth_relay_t, link); + +int8_t ws_eapol_auth_relay_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port) +{ + if (!interface_ptr || !remote_addr) { + return -1; + } + + if (ws_eapol_auth_relay_get(interface_ptr)) { + return 0; + } + + eapol_auth_relay_t *eapol_auth_relay = ns_dyn_mem_alloc(sizeof(eapol_auth_relay_t)); + if (!eapol_auth_relay) { + return -1; + } + + eapol_auth_relay->interface_ptr = interface_ptr; + + eapol_auth_relay->remote_addr.type = ADDRESS_IPV6; + memcpy(&eapol_auth_relay->relay_addr.address, remote_addr, 16); + eapol_auth_relay->relay_addr.identifier = remote_port; + + eapol_auth_relay->socket_id = socket_open(IPV6_NH_UDP, local_port, &ws_eapol_auth_relay_socket_cb); + if (eapol_auth_relay->socket_id < 0) { + ns_dyn_mem_free(eapol_auth_relay); + return -1; + } + + ns_list_add_to_end(&eapol_auth_relay_list, eapol_auth_relay); + + return 0; +} + +int8_t ws_eapol_auth_relay_delete(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + eapol_auth_relay_t *eapol_auth_relay = ws_eapol_auth_relay_get(interface_ptr); + if (!eapol_auth_relay) { + return -1; + } + + socket_close(eapol_auth_relay->socket_id); + + ns_list_remove(&eapol_auth_relay_list, eapol_auth_relay); + ns_dyn_mem_free(eapol_auth_relay); + + return 0; +} + +static eapol_auth_relay_t *ws_eapol_auth_relay_get(protocol_interface_info_entry_t *interface_ptr) +{ + ns_list_foreach(eapol_auth_relay_t, entry, &eapol_auth_relay_list) { + if (entry->interface_ptr == interface_ptr) { + return entry; + } + } + + return NULL; +} + +static void ws_eapol_auth_relay_socket_cb(void *cb) +{ + socket_callback_t *cb_data = cb; + + if (cb_data->event_type != SOCKET_DATA) { + return; + } + + eapol_auth_relay_t *eapol_auth_relay = NULL; + + ns_list_foreach(eapol_auth_relay_t, entry, &eapol_auth_relay_list) { + if (entry->socket_id == cb_data->socket_id) { + eapol_auth_relay = entry; + break; + } + } + + if (!eapol_auth_relay) { + return; + } + + uint8_t *socket_pdu = ns_dyn_mem_temporary_alloc(cb_data->d_len); + if (!socket_pdu) { + return; + } + + ns_address_t src_addr; + + if (socket_recvfrom(cb_data->socket_id, socket_pdu, cb_data->d_len, 0, &src_addr) != cb_data->d_len) { + ns_dyn_mem_free(socket_pdu); + return; + } + + // Message from source port 10254 (KMP service) -> to IP relay on node or on authenticator + if (src_addr.identifier == eapol_auth_relay->relay_addr.identifier) { + uint8_t *ptr = socket_pdu; + uint8_t *eui_64; + ns_address_t relay_ip_addr; + relay_ip_addr.type = ADDRESS_IPV6; + memcpy(relay_ip_addr.address, ptr, 16); + ptr += 16; + relay_ip_addr.identifier = common_read_16_bit(ptr); + ptr += 2; + eui_64 = ptr; + ptr += 8; + uint16_t data_len = cb_data->d_len - 26; + ws_eapol_relay_lib_send_to_relay(eapol_auth_relay->socket_id, eui_64, &relay_ip_addr, + ptr, data_len); + ns_dyn_mem_free(socket_pdu); + // Other source port (either 10253 or node relay source port) -> to KMP service + } else { + uint8_t *ptr = socket_pdu; + ws_eapol_auth_relay_send_to_kmp(eapol_auth_relay, ptr, src_addr.address, src_addr.identifier, + ptr + 8, cb_data->d_len - 8); + ns_dyn_mem_free(socket_pdu); + } +} + +static int8_t ws_eapol_auth_relay_send_to_kmp(eapol_auth_relay_t *eapol_auth_relay, const uint8_t *eui_64, const uint8_t *ip_addr, uint16_t port, const void *data, uint16_t data_len) +{ + ns_address_t dest_addr = eapol_auth_relay->relay_addr; + + uint8_t temp_array[26]; + ns_iovec_t msg_iov[2]; + ns_msghdr_t msghdr; + //Set messages name buffer + msghdr.msg_name = &dest_addr; + msghdr.msg_namelen = sizeof(dest_addr); + msghdr.msg_iov = &msg_iov[0]; + msghdr.msg_iovlen = 2; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + uint8_t *ptr = temp_array; + memcpy(ptr, ip_addr, 16); + ptr += 16; + ptr = common_write_16_bit(port, ptr); + memcpy(ptr, eui_64, 8); + msg_iov[0].iov_base = temp_array; + msg_iov[0].iov_len = 26; + msg_iov[1].iov_base = (void *)data; + msg_iov[1].iov_len = data_len; + socket_sendmsg(eapol_auth_relay->socket_id, &msghdr, NS_MSG_LEGACY0); + return 0; +} + +#endif /* HAVE_PAE_AUTH */ +#endif /* HAVE_WS */ diff --git a/source/6LoWPAN/ws/ws_eapol_auth_relay.h b/source/6LoWPAN/ws/ws_eapol_auth_relay.h new file mode 100644 index 0000000000..d0a7aa6c09 --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_auth_relay.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-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_EAPOL_AUTH_RELAY_H_ +#define WS_EAPOL_AUTH_RELAY_H_ + +#ifdef HAVE_PAE_AUTH + +/* + * EAPOL authenticator relay acts as a proxy between EAPOL UDP relay and + * authenticator PAE (KMP service). Relay is bound by default to EAPOL UDP + * relay port 10253 (set by local port parameter) and transfers messages + * to/from authenticator PAE. As default PAE is bound to UDP port 10254 + * (set by remote address and port parameters). + * + */ + +/** + * ws_eapol_auth_relay_start start authenticator relay + * + * \param interface_ptr interface + * \param local_port local port + * \param remote_addr remote address + * \param remote_port remote port + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_auth_relay_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port); + +/** + * ws_eapol_auth_relay_delete delete authenticator relay + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_auth_relay_delete(protocol_interface_info_entry_t *interface_ptr); + +#else + +#define ws_eapol_auth_relay_start(interface_ptr, local_port, remote_addr, remote_port) +#define ws_eapol_auth_relay_delete(interface_ptr) + +#endif + +#endif /* WS_EAPOL_AUTH_RELAY_H_ */ diff --git a/source/6LoWPAN/ws/ws_eapol_pdu.c b/source/6LoWPAN/ws/ws_eapol_pdu.c new file mode 100644 index 0000000000..eb13735085 --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_pdu.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "mac_api.h" +#include "mac_mcps.h" +#include "Common_Protocols/ipv6_constants.h" +#include "socket_api.h" +#include "6LoWPAN/MAC/mac_helper.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wsep" + +typedef struct { + uint8_t handle; + void *data_ptr; + void *buffer; + ns_list_link_t link; +} eapol_pdu_msdu_t; + +typedef NS_LIST_HEAD(eapol_pdu_msdu_t, link) eapol_pdu_msdu_list_t; + +typedef struct { + uint8_t priority; + ws_eapol_pdu_address_check *addr_check; + ws_eapol_pdu_receive *receive; + ns_list_link_t link; +} eapol_pdu_recv_cb_t; + +typedef NS_LIST_HEAD(eapol_pdu_recv_cb_t, link) eapol_pdu_recv_cb_list_t; + +typedef struct { + eapol_pdu_recv_cb_list_t recv_cb_list; /**< List of receive callbacks */ + eapol_pdu_msdu_list_t msdu_list; /**< MSDU list */ + ws_eapol_pdu_receive *receive; /**< data receive callback */ + protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + mpx_api_t *mpx_api; /**< MPX api */ + uint16_t mpx_user_id; /**< MPX user identifier */ + uint8_t msdu_handle; /**< MSDU handle */ + ns_list_link_t link; /**< Link */ +} eapol_pdu_data_t; + +static void ws_eapol_pdu_mpx_data_confirm(const mpx_api_t *api, const struct mcps_data_conf_s *data); +static void ws_eapol_pdu_mpx_data_indication(const mpx_api_t *api, const struct mcps_data_ind_s *data); +static void ws_eapol_pdu_data_request_primitiv_set(mcps_data_req_t *dataReq, protocol_interface_info_entry_t *cur); +static eapol_pdu_data_t *ws_eapol_pdu_data_get(protocol_interface_info_entry_t *interface_ptr); + +static NS_LIST_DEFINE(eapol_pdu_data_list, eapol_pdu_data_t, link); + +int8_t ws_eapol_pdu_init(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + if (ws_eapol_pdu_data_get(interface_ptr) != NULL) { + return 0; + } + + eapol_pdu_data_t *eapol_pdu_data = ns_dyn_mem_alloc(sizeof(eapol_pdu_data_t)); + if (!eapol_pdu_data) { + return -1; + } + + eapol_pdu_data->interface_ptr = interface_ptr; + ns_list_init(&eapol_pdu_data->recv_cb_list); + ns_list_init(&eapol_pdu_data->msdu_list); + eapol_pdu_data->msdu_handle = 0; + + ns_list_add_to_end(&eapol_pdu_data_list, eapol_pdu_data); + + return 0; +} + +int8_t ws_eapol_pdu_delete(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + eapol_pdu_data_t *eapol_pdu_data = ws_eapol_pdu_data_get(interface_ptr); + + if (!eapol_pdu_data) { + return -1; + } + + ns_list_foreach_safe(eapol_pdu_recv_cb_t, cb_entry, &eapol_pdu_data->recv_cb_list) { + ns_list_remove(&eapol_pdu_data->recv_cb_list, cb_entry); + ns_dyn_mem_free(cb_entry); + } + + ns_list_foreach_safe(eapol_pdu_msdu_t, msdu_entry, &eapol_pdu_data->msdu_list) { + ns_list_remove(&eapol_pdu_data->msdu_list, msdu_entry); + ns_dyn_mem_free(msdu_entry); + } + + ns_list_remove(&eapol_pdu_data_list, eapol_pdu_data); + ns_dyn_mem_free(eapol_pdu_data); + + return 0; +} + +int8_t ws_eapol_pdu_cb_register(protocol_interface_info_entry_t *interface_ptr, const eapol_pdu_recv_cb_data_t *cb_data) +{ + if (!interface_ptr || !cb_data) { + return -1; + } + + eapol_pdu_data_t *eapol_pdu_data = ws_eapol_pdu_data_get(interface_ptr); + + if (!eapol_pdu_data) { + return -1; + } + + eapol_pdu_recv_cb_t *new_cb = ns_dyn_mem_alloc(sizeof(eapol_pdu_recv_cb_t)); + if (!new_cb) { + return -1; + } + + new_cb->priority = cb_data->priority; + new_cb->addr_check = cb_data->addr_check; + new_cb->receive = cb_data->receive; + + ns_list_foreach(eapol_pdu_recv_cb_t, entry, &eapol_pdu_data->recv_cb_list) { + if (new_cb->priority <= entry->priority) { + ns_list_add_before(&eapol_pdu_data->recv_cb_list, entry, new_cb); + return 0; + } + } + + ns_list_add_to_end(&eapol_pdu_data->recv_cb_list, new_cb); + return 0; +} + +int8_t ws_eapol_pdu_cb_unregister(protocol_interface_info_entry_t *interface_ptr, const eapol_pdu_recv_cb_data_t *cb_data) +{ + if (!interface_ptr || !cb_data) { + return -1; + } + + eapol_pdu_data_t *eapol_pdu_data = ws_eapol_pdu_data_get(interface_ptr); + + if (!eapol_pdu_data) { + return -1; + } + + ns_list_foreach_safe(eapol_pdu_recv_cb_t, entry, &eapol_pdu_data->recv_cb_list) { + if (entry->receive == cb_data->receive) { + ns_list_remove(&eapol_pdu_data->recv_cb_list, entry); + ns_dyn_mem_free(entry); + return 0; + } + } + + return -1; +} + +int8_t ws_eapol_pdu_send_to_mpx(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *data, uint16_t size, void *buffer) +{ + eapol_pdu_data_t *eapol_pdu_data = ws_eapol_pdu_data_get(interface_ptr); + + if (!eapol_pdu_data) { + return -1; + } + + mcps_data_req_t data_request; + ws_eapol_pdu_data_request_primitiv_set(&data_request, eapol_pdu_data->interface_ptr); + + eapol_pdu_msdu_t *msdu_entry = ns_dyn_mem_temporary_alloc(sizeof(eapol_pdu_msdu_t)); + if (!msdu_entry) { + return -1; + } + msdu_entry->data_ptr = data; + msdu_entry->buffer = buffer; + 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; + + eapol_pdu_data->mpx_api->mpx_data_request(eapol_pdu_data->mpx_api, &data_request, eapol_pdu_data->mpx_user_id); + return 0; +} + +static void ws_eapol_pdu_data_request_primitiv_set(mcps_data_req_t *dataReq, protocol_interface_info_entry_t *cur) +{ + memset(dataReq, 0, sizeof(mcps_data_req_t)); + + dataReq->InDirectTx = false; + dataReq->TxAckReq = true; + dataReq->SrcAddrMode = ADDR_802_15_4_LONG; + dataReq->DstAddrMode = ADDR_802_15_4_LONG; + dataReq->DstPANId = mac_helper_panid_get(cur); +} + +int8_t ws_eapol_pdu_mpx_register(protocol_interface_info_entry_t *interface_ptr, struct mpx_api_s *mpx_api, uint16_t mpx_user_id) +{ + if (!interface_ptr) { + return -1; + } + + eapol_pdu_data_t *eapol_pdu_data = ws_eapol_pdu_data_get(interface_ptr); + + if (!eapol_pdu_data) { + return -1; + } + + if (!mpx_api && eapol_pdu_data->mpx_api) { + //Disable Data Callbacks from MPX Class + eapol_pdu_data->mpx_api->mpx_user_registration(eapol_pdu_data->mpx_api, NULL, NULL, eapol_pdu_data->mpx_user_id); + } + + eapol_pdu_data->mpx_api = mpx_api; + eapol_pdu_data->mpx_user_id = mpx_user_id; + + if (eapol_pdu_data->mpx_api) { + eapol_pdu_data->mpx_api->mpx_user_registration(eapol_pdu_data->mpx_api, ws_eapol_pdu_mpx_data_confirm, ws_eapol_pdu_mpx_data_indication, eapol_pdu_data->mpx_user_id); + } + return 0; +} + +static void ws_eapol_pdu_mpx_data_confirm(const mpx_api_t *api, const struct mcps_data_conf_s *data) +{ + eapol_pdu_data_t *eapol_pdu_data = NULL; + + ns_list_foreach(eapol_pdu_data_t, entry, &eapol_pdu_data_list) { + if (entry->mpx_api == api) { + eapol_pdu_data = entry; + break; + } + } + + if (!eapol_pdu_data) { + return; + } + + ns_list_foreach(eapol_pdu_msdu_t, msdu, &eapol_pdu_data->msdu_list) { + if (msdu->handle == data->msduHandle) { + ns_dyn_mem_free(msdu->buffer); + ns_list_remove(&eapol_pdu_data->msdu_list, msdu); + ns_dyn_mem_free(msdu); + return; + } + } +} + +static void ws_eapol_pdu_mpx_data_indication(const mpx_api_t *api, const struct mcps_data_ind_s *data) +{ + if (!data || !data->msduLength || !data->msdu_ptr) { + return; + } + + eapol_pdu_data_t *eapol_pdu_data = NULL; + + ns_list_foreach(eapol_pdu_data_t, entry, &eapol_pdu_data_list) { + if (entry->mpx_api == api) { + eapol_pdu_data = entry; + break; + } + } + + if (!eapol_pdu_data) { + return; + } + + ns_list_foreach(eapol_pdu_recv_cb_t, entry, &eapol_pdu_data->recv_cb_list) { + if (entry->addr_check(eapol_pdu_data->interface_ptr, data->SrcAddr) >= 0) { + entry->receive(eapol_pdu_data->interface_ptr, data->SrcAddr, data->msdu_ptr, data->msduLength); + break; + } + } +} + +static eapol_pdu_data_t *ws_eapol_pdu_data_get(protocol_interface_info_entry_t *interface_ptr) +{ + eapol_pdu_data_t *eapol_pdu_data = NULL; + + ns_list_foreach(eapol_pdu_data_t, entry, &eapol_pdu_data_list) { + if (entry->interface_ptr == interface_ptr) { + eapol_pdu_data = entry; + break; + } + } + + return eapol_pdu_data; +} + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_eapol_pdu.h b/source/6LoWPAN/ws/ws_eapol_pdu.h new file mode 100644 index 0000000000..fc99e08285 --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_pdu.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018-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_EAPOL_PDU_H_ +#define WS_EAPOL_PDU_H_ + +/* + * EAPOL PDU module transfers EAPOL PDUs to/from MPX. Several users + * (e.g. supplicant PAE and EAPOL relay) can register to incoming + * EAPOL PDUs. When registering, users need to define priority that + * defines in which order incoming EAPOL PDUs are offered to them. + * + * Incoming EAPOL PDU user callbacks form a pair on EAPOL PDU module: + * address check callback is called first, and if it returns match + * then incoming EAPOL PDU callback is called. + * + */ + +/** + * ws_eapol_pdu_init initialize EAPOL PDU module + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_init(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_eapol_pdu_mpx_register register EAPOL PDU module to MPX + * + * \param interface_ptr interface + * \param mpx_api MPX API + * \param mpx_user_id MPX user id + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_mpx_register(protocol_interface_info_entry_t *interface_ptr, struct mpx_api_s *mpx_api, uint16_t mpx_user_id); + +/** + * ws_eapol_pdu_delete delete EAPOL PDU module + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_delete(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_eapol_pdu_address_check check incoming EAPOL PDU address + * + * \param interface_ptr interface + * \param eui_64 source EUI-64 + * + * \return < 0 address does not match + * \return >= 0 address matches, call the PDU receive callback + * + */ +typedef int8_t ws_eapol_pdu_address_check(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); + +/** + * ws_eapol_pdu_receive receive EAPOL PDU + * + * \param interface_ptr interface + * \param eui_64 source EUI-64 + * \param data EAPOL PDU + * \param size PDU size + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t ws_eapol_pdu_receive(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *data, uint16_t size); + +typedef enum { + EAPOL_PDU_RECV_HIGH_PRIORITY = 0, + EAPOL_PDU_RECV_MEDIUM_PRIORITY = 100, + EAPOL_PDU_RECV_LOW_PRIORITY = 200 +} eapol_pdu_recv_prior_t; + +typedef struct { + eapol_pdu_recv_prior_t priority; /**< Priority: high, medium or low */ + ws_eapol_pdu_address_check *addr_check; /**< Address check callback */ + ws_eapol_pdu_receive *receive; /**< PDU receive callback */ +} eapol_pdu_recv_cb_data_t; + +/** + * ws_eapol_pdu_cb_register register an incoming EAPOL PDU callback + * + * \param interface_ptr interface + * \param cb_data callback data + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_cb_register(protocol_interface_info_entry_t *interface_ptr, const eapol_pdu_recv_cb_data_t *cb_data); + +/** + * ws_eapol_pdu_cb_unregister unregister an incoming EAPOL PDU callback + * + * \param interface_ptr interface + * \param cb_data callback data + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_cb_unregister(protocol_interface_info_entry_t *interface_ptr, const eapol_pdu_recv_cb_data_t *cb_data); + +/** + * ws_eapol_pdu_send_to_mpx send EAPOL PDU to MPX + * + * \param interface_ptr interface + * \param eui_64 destination EUI-64 + * \param data EAPOL PDU + * \param size PDU size + * \param buffer pointer to allocated buffer + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_pdu_send_to_mpx(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *data, uint16_t size, void *buffer); + +#endif /* WS_EAPOL_PDU_H_ */ diff --git a/source/6LoWPAN/ws/ws_eapol_relay.c b/source/6LoWPAN/ws/ws_eapol_relay.c new file mode 100644 index 0000000000..269ad38d6d --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_relay.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "mac_api.h" +#include "mac_mcps.h" +#include "Common_Protocols/ipv6_constants.h" +#include "socket_api.h" +#include "6LoWPAN/MAC/mac_helper.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" +#include "6LoWPAN/ws/ws_eapol_relay_lib.h" +#include "6LoWPAN/ws/ws_eapol_relay.h" + +#ifdef HAVE_WS +#ifdef HAVE_EAPOL_RELAY + +#define TRACE_GROUP "wser" + +typedef struct { + protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + ns_address_t remote_addr; /**< Remote address (border router address) */ + int8_t socket_id; /**< Socket ID for relay */ + ns_list_link_t link; /**< Link */ +} eapol_relay_t; + +static eapol_relay_t *ws_eapol_relay_get(protocol_interface_info_entry_t *interface_ptr); +static int8_t ws_eapol_relay_eapol_pdu_address_check(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); +static int8_t ws_eapol_relay_eapol_pdu_receive(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *pdu, uint16_t size); +static void ws_eapol_relay_socket_cb(void *cb); + +static const eapol_pdu_recv_cb_data_t eapol_pdu_recv_cb_data = { + .priority = EAPOL_PDU_RECV_LOW_PRIORITY, + .addr_check = ws_eapol_relay_eapol_pdu_address_check, + .receive = ws_eapol_relay_eapol_pdu_receive +}; + +static NS_LIST_DEFINE(eapol_relay_list, eapol_relay_t, link); + +int8_t ws_eapol_relay_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port) +{ + if (!interface_ptr || !remote_addr) { + return -1; + } + + if (ws_eapol_relay_get(interface_ptr)) { + return 0; + } + + eapol_relay_t *eapol_relay = ns_dyn_mem_alloc(sizeof(eapol_relay_t)); + if (!eapol_relay) { + return -1; + } + + eapol_relay->interface_ptr = interface_ptr; + + eapol_relay->remote_addr.type = ADDRESS_IPV6; + memcpy(&eapol_relay->remote_addr.address, remote_addr, 16); + eapol_relay->remote_addr.identifier = remote_port; + + eapol_relay->socket_id = socket_open(IPV6_NH_UDP, local_port, &ws_eapol_relay_socket_cb); + if (eapol_relay->socket_id < 0) { + ns_dyn_mem_free(eapol_relay); + return -1; + } + + if (ws_eapol_pdu_cb_register(interface_ptr, &eapol_pdu_recv_cb_data) < 0) { + ns_dyn_mem_free(eapol_relay); + return -1; + } + + ns_list_add_to_end(&eapol_relay_list, eapol_relay); + + return 0; +} + +int8_t ws_eapol_relay_delete(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + eapol_relay_t *eapol_relay = ws_eapol_relay_get(interface_ptr); + if (!eapol_relay) { + return -1; + } + + socket_close(eapol_relay->socket_id); + + ws_eapol_pdu_cb_unregister(interface_ptr, &eapol_pdu_recv_cb_data); + + ns_list_remove(&eapol_relay_list, eapol_relay); + ns_dyn_mem_free(eapol_relay); + + return 0; +} + +static eapol_relay_t *ws_eapol_relay_get(protocol_interface_info_entry_t *interface_ptr) +{ + ns_list_foreach(eapol_relay_t, entry, &eapol_relay_list) { + if (entry->interface_ptr == interface_ptr) { + return entry; + } + } + + return NULL; +} + +static int8_t ws_eapol_relay_eapol_pdu_address_check(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64) +{ + (void) eui_64; + (void) interface_ptr; + + // Low priority, always route all here if asked + return 0; +} + +static int8_t ws_eapol_relay_eapol_pdu_receive(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *pdu, uint16_t size) +{ + eapol_relay_t *eapol_relay = ws_eapol_relay_get(interface_ptr); + if (!eapol_relay) { + return -1; + } + + ws_eapol_relay_lib_send_to_relay(eapol_relay->socket_id, eui_64, &eapol_relay->remote_addr, pdu, size); + + return 0; +} + +static void ws_eapol_relay_socket_cb(void *cb) +{ + socket_callback_t *cb_data = cb; + + if (cb_data->event_type != SOCKET_DATA) { + return; + } + + eapol_relay_t *eapol_relay = NULL; + + ns_list_foreach(eapol_relay_t, entry, &eapol_relay_list) { + if (entry->socket_id == cb_data->socket_id) { + eapol_relay = entry; + break; + } + } + + if (!eapol_relay) { + return; + } + + uint8_t *socket_pdu = ns_dyn_mem_temporary_alloc(cb_data->d_len); + if (!socket_pdu) { + return; + } + + ns_address_t src_addr; + + if (socket_recvfrom(cb_data->socket_id, socket_pdu, cb_data->d_len, 0, &src_addr) != cb_data->d_len) { + ns_dyn_mem_free(socket_pdu); + return; + } + + //First 8 byte is EUID64 and rsr payload + if (ws_eapol_pdu_send_to_mpx(eapol_relay->interface_ptr, socket_pdu, socket_pdu + 8, cb_data->d_len - 8, socket_pdu) < 0) { + ns_dyn_mem_free(socket_pdu); + } +} + +#endif /* HAVE_EAPOL_RELAY */ +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_eapol_relay.h b/source/6LoWPAN/ws/ws_eapol_relay.h new file mode 100644 index 0000000000..dbbfbc584a --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_relay.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-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_EAPOL_RELAY_H_ +#define WS_EAPOL_RELAY_H_ + +#ifdef HAVE_EAPOL_RELAY + +/* + * EAPOL relay conveys EAPOL PDUs between authenticator EAPOL relay and local + * MPX interface. + * + * On supplicant (i.e. node) relay should be bound to UDP port 10253 (local + * port parameter). + * + * On authenticator (border router) relay will need to use some other port than + * 10253 since authenticator EAPOL relay is bound to port 10253. + * + * Border router address needs to be set on start (remote address and remote port + * parameter). + * + */ + +/** + * ws_eapol_relay_start start EAPOL relay + * + * \param interface_ptr interface + * \param local_port local port + * \param remote_addr remote address (border router relay address) + * \param remote_port remote port (border router relay port) + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_relay_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port); + +/** + * ws_eapol_relay_delete delete EAPOL + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_eapol_relay_delete(protocol_interface_info_entry_t *interface_ptr); + +#else + +#define ws_eapol_relay_start(interface_ptr, local_port, remote_addr, remote_port); +#define ws_eapol_relay_delete(interface_ptr); + +#endif + +#endif /* WS_EAPOL_RELAY_H_ */ diff --git a/source/6LoWPAN/ws/ws_eapol_relay_lib.c b/source/6LoWPAN/ws/ws_eapol_relay_lib.c new file mode 100644 index 0000000000..4352d77149 --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_relay_lib.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "socket_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_eapol_relay_lib.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wsrl" + +int8_t ws_eapol_relay_lib_send_to_relay(const uint8_t socket_id, const uint8_t *eui_64, const ns_address_t *dest_addr, const void *data, uint16_t data_len) +{ + ns_address_t addr = *dest_addr; + + ns_iovec_t msg_iov[2]; + ns_msghdr_t msghdr; + //Set messages name buffer + msghdr.msg_name = &addr; + msghdr.msg_namelen = sizeof(addr); + msghdr.msg_iov = &msg_iov[0]; + msghdr.msg_iovlen = 2; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msg_iov[0].iov_base = (void *)eui_64; + msg_iov[0].iov_len = 8; + msg_iov[1].iov_base = (void *)data; + msg_iov[1].iov_len = data_len; + socket_sendmsg(socket_id, &msghdr, NS_MSG_LEGACY0); + return 0; +} + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_eapol_relay_lib.h b/source/6LoWPAN/ws/ws_eapol_relay_lib.h new file mode 100644 index 0000000000..b1c387951a --- /dev/null +++ b/source/6LoWPAN/ws/ws_eapol_relay_lib.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-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_EAPOL_RELAY_LIB_H_ +#define WS_EAPOL_RELAY_LIB_H_ + +int8_t ws_eapol_relay_lib_send_to_relay(const uint8_t socket_id, const uint8_t *eui_64, const ns_address_t *dest_addr, const void *data, uint16_t data_len); + +#endif /* WS_EAPOL_RELAY_LIB_H_ */ diff --git a/source/6LoWPAN/ws/ws_empty_functions.c b/source/6LoWPAN/ws/ws_empty_functions.c new file mode 100644 index 0000000000..982e27bd79 --- /dev/null +++ b/source/6LoWPAN/ws/ws_empty_functions.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018-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. + */ + +#include +#include "nsconfig.h" +#include "ns_types.h" +#include "ns_trace.h" +#include +#include +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_common.h" + +#include "ws_management_api.h" + +#ifndef HAVE_WS +int ws_management_node_init( + int8_t interface_id, + uint8_t regulatory_domain, + char *network_name_ptr, + fhss_timer_t *fhss_timer_ptr) +{ + (void)interface_id; + (void)regulatory_domain; + (void)network_name_ptr; + (void)fhss_timer_ptr; + return -1; +} + +int ws_management_regulatory_domain_set( + int8_t interface_id, + uint8_t regulatory_domain, + uint8_t operating_class, + uint8_t operating_mode) +{ + (void)interface_id; + (void)regulatory_domain; + (void)operating_class; + (void)operating_mode; + return -1; +} + +int ws_management_network_size_set( + int8_t interface_id, + uint8_t network_size) +{ + (void)interface_id; + (void)network_size; + return -1; +} + +int ws_management_channel_mask_set( + int8_t interface_id, + uint32_t channel_mask[8]) +{ + (void)interface_id; + (void)channel_mask; + return -1; +} + +int ws_management_channel_plan_set( + int8_t interface_id, + uint8_t channel_plan, + uint8_t uc_channel_function, + uint8_t bc_channel_function, + uint32_t ch0_freq, // Stack can not modify this + uint8_t channel_spacing,// Stack can not modify this + uint8_t number_of_channels) +{ + (void)interface_id; + (void)channel_plan; + (void)uc_channel_function; + (void)bc_channel_function; + (void)ch0_freq; + (void)channel_spacing; + (void)number_of_channels; + return -1; +} + +int ws_management_fhss_timing_configure( + int8_t interface_id, + uint8_t fhss_uc_dwell_interval, + uint32_t fhss_broadcast_interval, + uint8_t fhss_bc_dwell_interval) +{ + (void)interface_id; + (void)fhss_uc_dwell_interval; + (void)fhss_broadcast_interval; + (void)fhss_bc_dwell_interval; + return -1; +} + +int ws_management_fhss_unicast_channel_function_configure( + int8_t interface_id, + uint8_t channel_function, + uint16_t fixed_channel, + uint8_t dwell_interval) +{ + (void)interface_id; + (void)channel_function; + (void)fixed_channel; + (void)dwell_interval; + return -1; +} + +int ws_management_fhss_broadcast_channel_function_configure( + int8_t interface_id, + uint8_t channel_function, + uint16_t fixed_channel, + uint8_t dwell_interval, + uint32_t broadcast_interval) +{ + (void)interface_id; + (void)channel_function; + (void)fixed_channel; + (void)dwell_interval; + (void)broadcast_interval; + return -1; +} + +/* ### test api ### */ +int ws_test_pan_size_set(int8_t interface_id, uint16_t pan_size) +{ + (void) interface_id; + (void) pan_size; + return -1; +} +int ws_test_max_child_count_set(int8_t interface_id, uint16_t child_count) +{ + (void) interface_id; + (void) child_count; + return -1; +} + +int ws_test_gtk_set(int8_t interface_id, uint8_t *gtk[4]) +{ + (void) interface_id; + (void) gtk; + + return -1; +} + +int ws_test_active_key_set(int8_t interface_id, uint8_t index) +{ + (void) interface_id; + (void) index; + + return -1; +} + +int ws_test_key_lifetime_set(int8_t interface_id, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime) +{ + (void) interface_id; + (void) gtk_lifetime; + (void) pmk_lifetime; + (void) ptk_lifetime; + + return -1; +} + +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) +{ + (void) interface_id; + (void) revocat_lifetime_reduct; + (void) new_activation_time; + (void) max_mismatch; + + return -1; +} + +#endif // no HAVE_WS + diff --git a/source/6LoWPAN/ws/ws_ie_lib.c b/source/6LoWPAN/ws/ws_ie_lib.c new file mode 100644 index 0000000000..92b95bce68 --- /dev/null +++ b/source/6LoWPAN/ws/ws_ie_lib.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "common_functions.h" +#include "mac_common_defines.h" +#include "6LoWPAN/MAC/mac_ie_lib.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_ie_lib.h" + +static uint8_t *ws_wh_header_base_write(uint8_t *ptr, uint16_t length, uint8_t type) +{ + ptr = mac_ie_header_base_write(ptr, MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID, length + 1); + *ptr++ = type; + return ptr; +} + +static uint16_t ws_channel_plan_length(uint8_t channel_plan) +{ + switch (channel_plan) { + case 0: + //Regulator domain and operationg class inline + return 2; + case 1: + //CHo, Channel spasing and number of channel's inline + return 6; + + default: + return 0; + } +} + +static uint16_t ws_channel_function_length(uint8_t channel_function, uint16_t hop_channel_count) +{ + switch (channel_function) { + case 0: + //Fixed channel inline + return 2; + case 1: + case 2: + return 0; + case 3: + //Hop count + channel hop list + return (1 + hop_channel_count); + default: + return 0; + + } +} + +uint16_t ws_wp_nested_hopping_schedule_length(struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule) +{ + uint16_t length; + uint8_t channel_function; + if (unicast_schedule) { + length = 4; + channel_function = hopping_schedule->uc_channel_function; + } else { + length = 10; + channel_function = hopping_schedule->bc_channel_function; + } + + length += ws_channel_plan_length(hopping_schedule->channel_plan); + + length += ws_channel_function_length(channel_function, 1); + + //Todo Derive some how exluded channel control + return length; +} + +uint8_t *ws_wh_utt_write(uint8_t *ptr, uint8_t message_type) +{ + ptr = ws_wh_header_base_write(ptr, 4, WH_IE_UTT_TYPE); + *ptr++ = message_type; + memset(ptr, 0, 3); + ptr += 3; + return ptr; +} + +uint8_t *ws_wh_bt_write(uint8_t *ptr) +{ + ptr = ws_wh_header_base_write(ptr, 5, WH_IE_BT_TYPE); + memset(ptr, 0, 5); + ptr += 5; + return ptr; +} + + +uint8_t *ws_wh_fc_write(uint8_t *ptr, uint8_t flow_ctrl) +{ + ptr = ws_wh_header_base_write(ptr, 1, WH_IE_FC_TYPE); + *ptr++ = flow_ctrl; + return ptr; +} + +uint8_t *ws_wh_rsl_write(uint8_t *ptr, int8_t rssi) +{ + ptr = ws_wh_header_base_write(ptr, 1, WH_IE_RSL_TYPE); + *ptr++ = rssi; + return ptr; +} + +uint8_t *ws_wh_ea_write(uint8_t *ptr, uint8_t *eui64) +{ + ptr = ws_wh_header_base_write(ptr, 8, WH_IE_EA_TYPE); + memcpy(ptr, eui64, 8); + ptr += 8; + return ptr; +} + +uint8_t *ws_wh_vh_write(uint8_t *ptr, uint8_t *vendor_header, uint8_t vendor_header_length) +{ + ptr = ws_wh_header_base_write(ptr, vendor_header_length, WH_IE_VH_TYPE); + if (vendor_header_length) { + memcpy(ptr, vendor_header, vendor_header_length); + ptr += vendor_header_length; + } + return ptr; +} + +uint8_t *ws_wp_base_write(uint8_t *ptr, uint16_t length) +{ + return mac_ie_payload_base_write(ptr, WS_WP_NESTED_IE, length); +} + +uint8_t *ws_wp_nested_hopping_schedule_write(uint8_t *ptr, struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule) +{ + //Calculate length + uint16_t length = ws_wp_nested_hopping_schedule_length(hopping_schedule, unicast_schedule); + if (!unicast_schedule) { + ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_BS_TYPE, length); + ptr = common_write_32_bit_inverse(hopping_schedule->fhss_broadcast_interval, ptr); + ptr = common_write_16_bit_inverse(hopping_schedule->fhss_bsi, ptr); + *ptr++ = hopping_schedule->fhss_bc_dwell_interval; + } else { + ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_US_TYPE, length); + *ptr++ = hopping_schedule->fhss_uc_dwell_interval; + } + + *ptr++ = hopping_schedule->clock_drift; + *ptr++ = hopping_schedule->timing_accurancy; + uint8_t channel_info_base = 0; + channel_info_base = (hopping_schedule->channel_plan); + if (unicast_schedule) { + channel_info_base |= (hopping_schedule->uc_channel_function << 3); + } else { + channel_info_base |= (hopping_schedule->bc_channel_function << 3); + } + //Todo define excluded channel ctrl + + *ptr++ = channel_info_base; + + switch (hopping_schedule->channel_plan) { + case 0: + //Regulator domain and operationg class inline + *ptr++ = hopping_schedule->regulatory_domain; + *ptr++ = hopping_schedule->operating_class; + break; + case 1: + //CHo, Channel spasing and number of channel's inline + ptr = common_write_24_bit(hopping_schedule->fhss_uc_dwell_interval, ptr); + *ptr++ = ((hopping_schedule->channel_spacing << 4) & 0xf0); + ptr = common_write_16_bit(hopping_schedule->number_of_channels, ptr); + break; + default: + break; + } + uint8_t cf = hopping_schedule->uc_channel_function; + uint16_t fixed_channel = hopping_schedule->uc_fixed_channel; + if (!unicast_schedule) { + cf = hopping_schedule->bc_channel_function; + } + switch (cf) { + case 0: + //Fixed channel inline + if (!unicast_schedule) { + fixed_channel = hopping_schedule->bc_fixed_channel; + } + ptr = common_write_16_bit_inverse(fixed_channel, ptr); + break; + case 1: + case 2: + //No Inline + break; + case 3: + //TODO do this properly + //Hop count + channel hop list + *ptr++ = 1; + *ptr++ = 0; + break; + default: + break; + + } + return ptr; +} + +uint8_t *ws_wp_nested_vp_write(uint8_t *ptr, uint8_t *vendor_payload, uint16_t vendor_payload_length) +{ + if (vendor_payload_length) { + ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_VP_TYPE, vendor_payload_length); + memcpy(ptr, vendor_payload, vendor_payload_length); + ptr += vendor_payload_length; + } + return ptr; +} + +uint8_t *ws_wp_nested_pan_info_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration) +{ + if (!pan_congiguration) { + return mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_TYPE, 0); + } + ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_TYPE, 5); + ptr = common_write_16_bit_inverse(pan_congiguration->pan_size, ptr); + ptr = common_write_16_bit_inverse(pan_congiguration->routing_cost, ptr); + uint8_t temp8 = 0; + temp8 |= (pan_congiguration->use_parent_bs << 0); + temp8 |= (pan_congiguration->rpl_routing_method << 1); + temp8 |= pan_congiguration->version << 5; + *ptr++ = temp8; + return ptr; +} + + +uint8_t *ws_wp_nested_netname_write(uint8_t *ptr, uint8_t *network_name, uint8_t network_name_length) +{ + ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_NETNAME_TYPE, network_name_length); + if (network_name_length) { + memcpy(ptr, network_name, network_name_length); + ptr += network_name_length; + } + return ptr; +} + +uint8_t *ws_wp_nested_pan_ver_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration) +{ + if (!pan_congiguration) { + return ptr; + } + ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_VER_TYPE, 2); + return common_write_16_bit_inverse(pan_congiguration->pan_version, ptr); +} + +uint8_t *ws_wp_nested_gtkhash_write(uint8_t *ptr, uint8_t *gtkhash, uint8_t gtkhash_length) +{ + ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_GTKHASH_TYPE, gtkhash_length); + if (gtkhash_length) { + memcpy(ptr, gtkhash, 32); + ptr += 32; + } + return ptr; +} + +bool ws_wh_utt_read(uint8_t *data, uint16_t length, struct ws_utt_ie *utt_ie) +{ + mac_header_IE_t utt_ie_data; + utt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID; + if (4 != mac_ie_header_sub_id_discover(data, length, &utt_ie_data, WH_IE_UTT_TYPE)) { + // NO UTT header + return false; + } + data = utt_ie_data.content_ptr; + utt_ie->message_type = *data++; + utt_ie->ufsi = common_read_24_bit_inverse(data); + return true; +} + +bool ws_wh_bt_read(uint8_t *data, uint16_t length, struct ws_bt_ie *bt_ie) +{ + mac_header_IE_t btt_ie_data; + btt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID; + if (5 != mac_ie_header_sub_id_discover(data, length, &btt_ie_data, WH_IE_BT_TYPE)) { + return false; + } + data = btt_ie_data.content_ptr; + bt_ie->broadcast_slot_number = common_read_16_bit_inverse(data); + bt_ie->broadcast_interval_offset = common_read_24_bit_inverse(data + 2); + return true; +} + +bool ws_wh_rsl_read(uint8_t *data, uint16_t length, int8_t *rsl) +{ + mac_header_IE_t rsl_ie_data; + rsl_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID; + if (1 != mac_ie_header_sub_id_discover(data, length, &rsl_ie_data, WH_IE_RSL_TYPE)) { + return false; + } + *rsl = *rsl_ie_data.content_ptr; + + return true; +} + +bool ws_wh_ea_read(uint8_t *data, uint16_t length, uint8_t *eui64) +{ + mac_header_IE_t rsl_ie_data; + rsl_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID; + if (8 != mac_ie_header_sub_id_discover(data, length, &rsl_ie_data, WH_IE_EA_TYPE)) { + return false; + } + memcpy(eui64, rsl_ie_data.content_ptr, 8); + + return true; +} + +static uint8_t *ws_channel_plan_zero_read(uint8_t *ptr, ws_channel_plan_zero_t *plan) +{ + plan->regulator_domain = *ptr++; + plan->operation_class = *ptr++; + return ptr; +} + +static uint8_t *ws_channel_plan_one_read(uint8_t *ptr, ws_channel_plan_one_t *plan) +{ + plan->ch0 = common_read_24_bit_inverse(ptr); + ptr += 3; + plan->channel_spacing = (*ptr++ & 0xf0) >> 4; + plan->number_of_channel = common_read_16_bit_inverse(ptr); + ptr += 2; + return ptr; +} + +static uint8_t *ws_channel_function_zero_read(uint8_t *ptr, ws_channel_function_zero_t *plan) +{ + plan->fixed_channel = common_read_16_bit_inverse(ptr); + return ptr + 2; +} + +static uint8_t *ws_channel_function_three_read(uint8_t *ptr, ws_channel_function_three_t *plan) +{ + plan->channel_hop_count = *ptr++; + plan->channel_list = ptr++; + return ptr; +} + +bool ws_wp_nested_us_read(uint8_t *data, uint16_t length, struct ws_us_ie *us_ie) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_US_TYPE; + nested_payload_ie.type_long = true; + if (mac_ie_nested_discover(data, length, &nested_payload_ie) < 4) { + return false; + } + data = nested_payload_ie.content_ptr; + us_ie->dwell_interval = *data++; + us_ie->clock_drift = *data++; + us_ie->timing_accurancy = *data++; + us_ie->channel_plan = (*data & 3); + us_ie->channel_function = (*data & 0x38) >> 3; + us_ie->excluded_channel_ctrl = (*data & 0xc0) >> 6; + data++; + uint16_t info_length = 0; + nested_payload_ie.length -= 4; + info_length = ws_channel_plan_length(us_ie->channel_plan); + if (nested_payload_ie.length < info_length) { + return false; + } + + nested_payload_ie.length -= info_length; + switch (us_ie->channel_plan) { + case 0: + data = ws_channel_plan_zero_read(data, &us_ie->plan.zero); + break; + + case 1: + data = ws_channel_plan_one_read(data, &us_ie->plan.one); + break; + default: + return false; + + } + + info_length = ws_channel_function_length(us_ie->channel_function, 0); + + if (nested_payload_ie.length < info_length) { + return false; + } + nested_payload_ie.length -= info_length; + + + switch (us_ie->channel_function) { + case 0: + data = ws_channel_function_zero_read(data, &us_ie->function.zero); + break; + + case 1: + case 2: + break; + + case 3: + + data = ws_channel_function_three_read(data, &us_ie->function.three); + info_length = us_ie->function.three.channel_hop_count; + if (nested_payload_ie.length < info_length) { + return false; + } + nested_payload_ie.length -= info_length; + data += info_length; + break; + default: + return false; + + } + + return true; +} +bool ws_wp_nested_bs_read(uint8_t *data, uint16_t length, struct ws_bs_ie *bs_ie) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_BS_TYPE; + nested_payload_ie.type_long = true; + if (mac_ie_nested_discover(data, length, &nested_payload_ie) < 10) { + return false; + } + data = nested_payload_ie.content_ptr; + bs_ie->broadcast_interval = common_read_32_bit_inverse(data); + bs_ie->broadcast_schedule_identifier = common_read_16_bit_inverse(data + 4); + data += 6; + bs_ie->dwell_interval = *data++; + bs_ie->clock_drift = *data++; + bs_ie->timing_accurancy = *data++; + + bs_ie->channel_plan = (*data & 3); + bs_ie->channel_function = (*data & 0x38) >> 3; + bs_ie->excluded_channel_ctrl = (*data & 0xc0) >> 6; + data++; + nested_payload_ie.length -= 10; + uint16_t info_length = 0; + + info_length = ws_channel_plan_length(bs_ie->channel_plan); + if (nested_payload_ie.length < info_length) { + return false; + } + nested_payload_ie.length -= info_length; + switch (bs_ie->channel_plan) { + case 0: + data = ws_channel_plan_zero_read(data, &bs_ie->plan.zero); + break; + + case 1: + data = ws_channel_plan_one_read(data, &bs_ie->plan.one); + break; + default: + return false; + + } + + info_length = ws_channel_function_length(bs_ie->channel_function, 0); + if (nested_payload_ie.length < info_length) { + return false; + } + nested_payload_ie.length -= info_length; + + switch (bs_ie->channel_function) { + case 0: + data = ws_channel_function_zero_read(data, &bs_ie->function.zero); + break; + + case 1: + case 2: + break; + + case 3: + data = ws_channel_function_three_read(data, &bs_ie->function.three); + info_length = bs_ie->function.three.channel_hop_count; + if (nested_payload_ie.length < info_length) { + return false; + } + nested_payload_ie.length -= info_length; + data += info_length; + break; + default: + return false; + + } + + return true; +} + +bool ws_wp_nested_pan_read(uint8_t *data, uint16_t length, struct ws_pan_information_s *pan_congiguration) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_PAN_TYPE; + nested_payload_ie.type_long = false; + if (mac_ie_nested_discover(data, length, &nested_payload_ie) != 5) { + return false; + } + + pan_congiguration->pan_size = common_read_16_bit_inverse(nested_payload_ie.content_ptr); + pan_congiguration->routing_cost = common_read_16_bit_inverse(nested_payload_ie.content_ptr + 2); + pan_congiguration->use_parent_bs = (nested_payload_ie.content_ptr[4] & 0x01) == 0x01; + pan_congiguration->rpl_routing_method = (nested_payload_ie.content_ptr[4] & 0x02) == 0x02; + pan_congiguration->version = (nested_payload_ie.content_ptr[4] & 0xe0) >> 5; + + return true; +} + +bool ws_wp_nested_pan_version_read(uint8_t *data, uint16_t length, uint16_t *pan_version) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_PAN_VER_TYPE; + nested_payload_ie.type_long = false; + if (mac_ie_nested_discover(data, length, &nested_payload_ie) != 2) { + return false; + } + *pan_version = common_read_16_bit_inverse(nested_payload_ie.content_ptr); + + return true; +} + +uint8_t *ws_wp_nested_gtkhash_read(uint8_t *data, uint16_t length) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_GTKHASH_TYPE; + nested_payload_ie.type_long = false; + if (mac_ie_nested_discover(data, length, &nested_payload_ie) != 32) { + return NULL; + } + + return nested_payload_ie.content_ptr; +} + + +bool ws_wp_nested_network_name_read(uint8_t *data, uint16_t length, ws_wp_network_name_t *network_name) +{ + mac_nested_payload_IE_t nested_payload_ie; + nested_payload_ie.id = WP_PAYLOAD_IE_NETNAME_TYPE; + nested_payload_ie.type_long = false; + + if (0 == mac_ie_nested_discover(data, length, &nested_payload_ie)) { + return false; + } else if (nested_payload_ie.length > 32) { + //Too long name + return false; + } + network_name->network_name = nested_payload_ie.content_ptr; + network_name->network_name_length = nested_payload_ie.length; + return true; +} + diff --git a/source/6LoWPAN/ws/ws_ie_lib.h b/source/6LoWPAN/ws/ws_ie_lib.h new file mode 100644 index 0000000000..e992b09fd2 --- /dev/null +++ b/source/6LoWPAN/ws/ws_ie_lib.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-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_IE_LIB_H_ +#define WS_IE_LIB_H_ + +struct ws_pan_information_s; +struct ws_utt_ie; +struct ws_bt_ie; +struct ws_us_ie; +struct ws_hopping_schedule_s; + +/** + * @brief ws_wp_network_name_t WS nested payload network name + */ +typedef struct ws_wp_network_name { + uint8_t network_name_length; + uint8_t *network_name; +} ws_wp_network_name_t; + +/* WS_WH HEADER IE */ +uint8_t *ws_wh_utt_write(uint8_t *ptr, uint8_t message_type); +uint8_t *ws_wh_bt_write(uint8_t *ptr); +uint8_t *ws_wh_fc_write(uint8_t *ptr, uint8_t flow_ctrl); +uint8_t *ws_wh_rsl_write(uint8_t *ptr, int8_t rssi); +uint8_t *ws_wh_vh_write(uint8_t *ptr, uint8_t *vendor_header, uint8_t vendor_header_length); +uint8_t *ws_wh_ea_write(uint8_t *ptr, uint8_t *eui64); + +bool ws_wh_utt_read(uint8_t *data, uint16_t length, struct ws_utt_ie *utt_ie); +bool ws_wh_bt_read(uint8_t *data, uint16_t length, struct ws_bt_ie *bt_ie); +bool ws_wh_rsl_read(uint8_t *data, uint16_t length, int8_t *rsl); +bool ws_wh_ea_read(uint8_t *data, uint16_t length, uint8_t *eui64); + +/* WS_WP_NESTED PAYLOD IE */ +uint8_t *ws_wp_base_write(uint8_t *ptr, uint16_t length); +uint8_t *ws_wp_nested_hopping_schedule_write(uint8_t *ptr, struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule); +uint8_t *ws_wp_nested_vp_write(uint8_t *ptr, uint8_t *vendor_payload, uint16_t vendor_payload_length); +uint8_t *ws_wp_nested_pan_info_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration); +uint8_t *ws_wp_nested_netname_write(uint8_t *ptr, uint8_t *network_name, uint8_t network_name_length); +uint8_t *ws_wp_nested_pan_ver_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration); +uint8_t *ws_wp_nested_gtkhash_write(uint8_t *ptr, uint8_t *gtkhash, uint8_t gtkhash_length); +uint16_t ws_wp_nested_hopping_schedule_length(struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule); + +bool ws_wp_nested_us_read(uint8_t *data, uint16_t length, struct ws_us_ie *us_ie); +bool ws_wp_nested_bs_read(uint8_t *data, uint16_t length, struct ws_bs_ie *bs_ie); +bool ws_wp_nested_pan_read(uint8_t *data, uint16_t length, struct ws_pan_information_s *pan_congiguration); +bool ws_wp_nested_pan_version_read(uint8_t *data, uint16_t length, uint16_t *pan_version); +bool ws_wp_nested_network_name_read(uint8_t *data, uint16_t length, ws_wp_network_name_t *network_name); +uint8_t *ws_wp_nested_gtkhash_read(uint8_t *data, uint16_t length); + + +#endif /* WS_IE_LIB_H_ */ diff --git a/source/6LoWPAN/ws/ws_llc.h b/source/6LoWPAN/ws/ws_llc.h new file mode 100644 index 0000000000..a1dab61bff --- /dev/null +++ b/source/6LoWPAN/ws/ws_llc.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018-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_LLC_H_ +#define WS_LLC_H_ + +#include "6LoWPAN/ws/ws_neighbor_class.h" +#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" + +struct protocol_interface_info_entry; +struct mcps_data_ind_s; +struct mcps_data_ie_list; +struct channel_list_s; +struct ws_pan_information_s; +struct mlme_security_s; +struct ws_hopping_schedule_s; +struct ws_neighbor_class_entry; +struct mac_neighbor_table_entry; + + +/** + * @brief wh_ie_sub_list_t ws asynch header IE elemnt request list + */ +typedef struct wh_ie_sub_list_s { + bool utt_ie: 1; /**< Unicast Timing and Frame type information */ + bool bt_ie: 1; /**< Broadcast timing information */ + bool fc_ie: 1; /**< Flow Control for Extended Direct Frame Exchange */ + bool rsl_ie: 1; /**< Received Signal Level information */ + bool vh_ie: 1; /**< Vendor header information */ + bool ea_ie: 1; /**< EAPOL autheticator EUI-64 header information */ +} wh_ie_sub_list_t; + +/** + * @brief wp_nested_ie_sub_list_t ws asynch Nested Payload sub IE element request list + */ +typedef struct wp_nested_ie_sub_list_s { + bool us_ie: 1; /**< Unicast Schedule information */ + bool bs_ie: 1; /**< Broadcast Schedule information */ + bool vp_ie: 1; /**< Vendor Payload information */ + bool pan_ie: 1; /**< PAN Information */ + bool net_name_ie: 1; /**< Network Name information */ + bool pan_version_ie: 1; /**< Pan configuration version */ + bool gtkhash_ie: 1; /**< GTK Hash information */ +} wp_nested_ie_sub_list_t; + +/** + * @brief asynch_request_t Asynch message request parameters + */ +typedef struct asynch_request_s { + unsigned message_type: 3; /**< Asynch message type: WS_FT_PAN_ADVERT, WS_FT_PAN_ADVERT_SOL, WS_FT_PAN_CONF or WS_FT_PAN_CONF_SOL. */ + wh_ie_sub_list_t wh_requested_ie_list; /**< WH-IE header list to message. */ + wp_nested_ie_sub_list_t wp_requested_nested_ie_list; /**< WP-IE Nested IE list to message. */ + struct mlme_security_s security; /**< Request MAC security paramaters */ + struct channel_list_s channel_list; /**< Channel List. */ +} asynch_request_t; + + +/** + * @brief LLC neighbour info request parameters + */ +typedef struct llc_neighbour_req { + struct mac_neighbor_table_entry *neighbor; /**< Generic Link Layer Neighbor information entry. */ + struct ws_neighbor_class_entry *ws_neighbor; /**< Wi-sun Neighbor information entry. */ +} llc_neighbour_req_t; + +/** + * @brief ws_asynch_ind ws asynch data indication + * @param interface Interface pointer + * @param data MCPS-DATA.indication specific values + * @param ie_ext Information element list + */ +typedef void ws_asynch_ind(struct protocol_interface_info_entry *interface, const struct mcps_data_ind_s *data, const struct mcps_data_ie_list *ie_ext, uint8_t message_type); + +/** + * @brief ws_asynch_confirm ws asynch data confirmation to asynch message request + * @param api The API which handled the response + * @param data MCPS-DATA.confirm specific values + * @param user_id MPX user ID + */ +typedef void ws_asynch_confirm(struct protocol_interface_info_entry *interface, uint8_t asynch_message); + +/** + * @brief ws_asynch_confirm ws asynch data confirmation to asynch message request + * @param interface The interface pointer + * @param mac_64 Neighbor 64-bit address + * @param neighbor_buffer Buffer where neighbor infor is buffered + * @param request_new true if is possible to allocate new entry + * + * @return true when neighbor info is available + * @return false when no neighbor info + */ +typedef bool ws_neighbor_info_request(struct protocol_interface_info_entry *interface, const uint8_t *mac_64, llc_neighbour_req_t *neighbor_buffer, bool request_new); + +/** + * @brief ws_llc_create ws LLC module create + * @param interface Interface pointer + * @param asynch_ind_cb Asynch indication + * @param ie_ext Information element list + * + * Function allocate and init LLC class and init it 2 supported 2 API: ws asynch and MPX user are internally registered. + */ +int8_t ws_llc_create(struct protocol_interface_info_entry *interface, ws_asynch_ind *asynch_ind_cb, ws_asynch_confirm *asynch_cnf_cb, ws_neighbor_info_request *ws_neighbor_info_request_cb); + +/** + * @brief ws_llc_reset Reset ws LLC parametrs and clean messages + * @param interface Interface pointer + * + */ +void ws_llc_reset(struct protocol_interface_info_entry *interface); + +/** + * @brief ws_llc_delete Delete LLC interface. ONLY for Test purpose. + * @param interface Interface pointer + * + */ +int8_t ws_llc_delete(struct protocol_interface_info_entry *interface); + +/** + * @brief ws_llc_mpx_api_get Get MPX api for registration purpose. + * @param interface Interface pointer + * + * @return NULL when MPX is not vailabale + * @return Pointer to MPX API + * + */ +mpx_api_t *ws_llc_mpx_api_get(struct protocol_interface_info_entry *interface); + +/** + * @brief ws_llc_asynch_request ws asynch message request to all giving channels + * @param interface Interface pointer + * @param request Asynch message parameters: type, IE and channel list + * + * @return 0 Asynch message pushed to MAC + * @return -1 memory allocate problem + * @return -2 Parameter problem + * + */ +int8_t ws_llc_asynch_request(struct protocol_interface_info_entry *interface, asynch_request_t *request); + + +/** + * @brief ws_llc_set_vendor_header_data Configure WS vendor Header data information (Data of WH_IE_VH_TYPE IE element) + * @param interface Interface pointer + * @param vendor_header pointer to vendor header this pointer must keep alive when it is configured to LLC + * @param vendor_header_length configured vendor header length + * + */ +void ws_llc_set_vendor_header_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_header, uint8_t vendor_header_length); + +/** + * @brief ws_llc_set_vendor_payload_data Configure WS vendor payload data information (Data of WP_PAYLOAD_IE_VP_TYPE IE element) + * @param interface Interface pointer + * @param vendor_payload pointer to vendor payload this pointer must keep alive when it is configured to LLC + * @param vendor_payload_length configured vendor payload length + * + */ +void ws_llc_set_vendor_payload_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_payload, uint8_t vendor_payload_length); + +/** + * @brief ws_llc_set_network_name Configure WS Network name (Data of WP_PAYLOAD_IE_NETNAME_TYPE IE element) + * @param interface Interface pointer + * @param name_length configured network name length + * @param name pointer to network name this pointer must keep alive when it is configured to LLC + * + */ +void ws_llc_set_network_name(struct protocol_interface_info_entry *interface, uint8_t *name, uint8_t name_length); + +/** + * @brief ws_llc_set_gtkhash Configure WS GTK hash information (Data of WP_PAYLOAD_IE_GTKHASH_TYPE IE element) + * @param interface Interface pointer + * @param gtkhash pointer to GTK hash which length is 32 bytes this pointer must keep alive when it is configured to LLC + * + */ +void ws_llc_set_gtkhash(struct protocol_interface_info_entry *interface, uint8_t *gtkhash); + +/** + * @brief ws_llc_set_pan_information_pointer Configure WS PAN information (Data of WP_PAYLOAD_IE_PAN_TYPE IE element) + * @param interface Interface pointer + * @param pan_information_pointer pointer to Pan information this pointer must keep alive when it is configured to LLC + * + */ +void ws_llc_set_pan_information_pointer(struct protocol_interface_info_entry *interface, struct ws_pan_information_s *pan_information_pointer); + +/** + * @brief ws_llc_hopping_schedule_config Configure channel hopping + * @param interface Interface pointer + * @param hopping_schedule pointer to Channel hopping schedule + * + */ +void ws_llc_hopping_schedule_config(struct protocol_interface_info_entry *interface, struct ws_hopping_schedule_s *hopping_schedule); + + + +#endif /* WS_LLC_H_ */ diff --git a/source/6LoWPAN/ws/ws_llc_data_service.c b/source/6LoWPAN/ws/ws_llc_data_service.c new file mode 100644 index 0000000000..e9e3e2eb9b --- /dev/null +++ b/source/6LoWPAN/ws/ws_llc_data_service.c @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "mac_common_defines.h" +#include "mac_api.h" +#include "mac_mcps.h" +#include "common_functions.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/MAC/mac_helper.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/MAC/mac_ie_lib.h" +#include "6LoWPAN/ws/ws_common_defines.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bootstrap.h" +#include "6LoWPAN/ws/ws_ie_lib.h" +#include "6LoWPAN/ws/ws_llc.h" +#include "6LoWPAN/ws/ws_mpx_header.h" +#include "6LoWPAN/ws/ws_pae_controller.h" +#include "Service_Libs/etx/etx.h" +#include "fhss_ws_extension.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wllc" + +#define LLC_MESSAGE_QUEUE_LIST_SIZE_MAX 16 //Do not config over 30 never +#define MPX_USER_SIZE 2 + +typedef struct { + uint16_t user_id; /**< User ID for identify MPX User */ + mpx_data_confirm *data_confirm; /**< User registred MPX Data confirmation call back */ + mpx_data_indication *data_ind; /**< User registred MPX Data indication call back */ +} mpx_user_t; + + +typedef struct { + mpx_api_t mpx_api; /**< API for MPX user like Stack and EAPOL */ + mpx_user_t mpx_user_table[MPX_USER_SIZE]; /**< MPX user list include registered call back pointers and user id's */ + unsigned mpx_id: 4; /**< MPX class sequence number */ +} mpx_class_t; + + +typedef struct { + uint16_t supported_channels; /**< Configured Channel count. This will define Channel infor mask length to some information element */ + uint16_t network_name_length; /**< Network name length */ + uint16_t vendor_payload_length; /**< Vendor spesific payload length */ + uint8_t vendor_header_length; /**< Vendor spesific header length */ + uint8_t gtkhash_length; /**< GTK hash length */ + ws_pan_information_t *pan_congiguration; /**< Pan configururation */ + struct ws_hopping_schedule_s *hopping_schedule;/**< Channel hopping schedule */ + uint8_t *gtkhash; /**< Pointer to GTK HASH user must give pointer which include 4 64-bit HASH array */ + uint8_t *network_name; /**< Network name */ + uint8_t *vendor_header_data; /**< Vendor spesific header data */ + uint8_t *vendor_payload; /**< Vendor spesific payload data */ +} llc_ie_params_t; + +typedef struct { + uint8_t dst_address[8]; /**< Destination address */ + unsigned messsage_type: 3; /**< Frame type to UTT */ + unsigned mpx_id: 5; /**< MPX sequence */ + bool ack_requested: 1; /**< ACK requested */ + unsigned dst_address_type: 2; /**< Destination address type */ + uint8_t msg_handle; /**< LLC genetaed unique MAC handle */ + uint8_t mpx_user_handle; /**< This MPX user defined handle */ + ns_ie_iovec_t ie_vector_list[3]; /**< IE vectors: 1 for Header's, 1 for Payload and for MPX payload */ + mcps_data_req_ie_list_t ie_ext; + ns_list_link_t link; /**< List link entry */ + uint8_t ie_buffer[]; /**< Trailing buffer data */ +} llc_message_t; + +/** get pointer to Mac header start point*/ +#define ws_message_buffer_ptr_get(x) (&(x)->ie_buffer[0]) + +typedef NS_LIST_HEAD(llc_message_t, link) llc_message_list_t; + +typedef struct { + uint8_t mac_handle_base; /**< Mac handle id base this will be updated by 1 after use */ + uint8_t llc_message_list_size; /**< llc_message_list list size */ + mpx_class_t mpx_data_base; /**< MPX data be including USER API Class and user call backs */ + llc_message_list_t llc_message_list; /**< Active Message list */ + llc_ie_params_t ie_params; /**< LLC IE header and Payload data configuration */ + ws_asynch_ind *asynch_ind; /**< LLC Asynch data indication call back configured by user */ + ws_asynch_confirm *asynch_confirm; /**< LLC Asynch data confirmation call back configured by user */ + ws_neighbor_info_request *ws_neighbor_info_request_cb; /**< LLC Neighbour discover API*/ + uint8_t ws_enhanced_ack_elements[WH_IE_ELEMENT_HEADER_LENGTH + 4 + WH_IE_ELEMENT_HEADER_LENGTH + 1]; + ns_ie_iovec_t ws_header_vector; + protocol_interface_info_entry_t *interface_ptr; /**< List link entry */ + ns_list_link_t link; /**< List link entry */ +} llc_data_base_t; + +static NS_LIST_DEFINE(llc_data_base_list, llc_data_base_t, link); + +static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params); +static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params); + +/** LLC message local functions */ +static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list); +static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list); +static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list); +static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base); +static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base, bool mpx_user); + +/** LLC interface sepesific local functions */ +static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface); +static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api); +static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api); + +static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id); +static llc_data_base_t *ws_llc_base_allocate(void); +static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data); +static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext); +static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id); +static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id); +static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id); +static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id); +static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id); +static void ws_llc_mpx_init(mpx_class_t *mpx_class); + +/** Discover Message by message handle id */ +static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list) +{ + ns_list_foreach(llc_message_t, message, list) { + if (message->msg_handle == handle) { + return message; + } + } + return NULL; +} + +static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list) +{ + ns_list_foreach(llc_message_t, message, list) { + if ((message->messsage_type == WS_FT_DATA || message->messsage_type == WS_FT_EAPOL) && message->mpx_id == handle) { + return message; + } + } + return NULL; +} + + +static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list) +{ + uint8_t message_type; + if (user_id == MPX_LOWPAN_ENC_USER_ID) { + message_type = WS_FT_DATA; + } else { + message_type = WS_FT_EAPOL; + } + + ns_list_foreach(llc_message_t, message, list) { + if (message->messsage_type == message_type && message->mpx_user_handle == handle) { + return message; + } + } + return NULL; +} + + + + +//Free message and delete from list +static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base) +{ + ns_list_remove(&llc_base->llc_message_list, message); + ns_dyn_mem_free(message); + llc_base->llc_message_list_size--; +} + +static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base, bool mpx_user) +{ + if (llc_base->llc_message_list_size >= LLC_MESSAGE_QUEUE_LIST_SIZE_MAX) { + return NULL; + } + + llc_message_t *message = ns_dyn_mem_temporary_alloc(sizeof(llc_message_t) + ie_buffer_size); + if (!message) { + return NULL; + } + message->ack_requested = false; + + //Guarantee + while (1) { + if (llc_message_discover_by_mac_handle(llc_base->mac_handle_base, &llc_base->llc_message_list)) { + llc_base->mac_handle_base++; + } else { + break; + } + } + if (mpx_user) { + while (1) { + if (llc_message_discover_by_mpx_id(llc_base->mpx_data_base.mpx_id, &llc_base->llc_message_list)) { + llc_base->mpx_data_base.mpx_id++; + } else { + break; + } + } + } + + //Storage handle and update base + message->msg_handle = llc_base->mac_handle_base++; + if (mpx_user) { + message->mpx_id = llc_base->mpx_data_base.mpx_id++; + } + llc_base->llc_message_list_size++; + ns_list_add_to_end(&llc_base->llc_message_list, message); + return message; +} + +static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface) +{ + ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) { + if (base->interface_ptr == interface) { + return base; + } + } + return NULL; +} + +static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api) +{ + ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) { + if (base->interface_ptr->mac_api == api) { + return base; + } + } + return NULL; +} + +static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api) +{ + ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) { + if (&base->mpx_data_base.mpx_api == api) { + return base; + } + } + return NULL; +} + +static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params) +{ + uint16_t length = 0; + if (requested_list.utt_ie) { + //Static 4 bytes allways UTT + length += WH_IE_ELEMENT_HEADER_LENGTH + 4; + } + + if (requested_list.bt_ie) { + //Static 5 bytes allways + length += WH_IE_ELEMENT_HEADER_LENGTH + 5; + } + + if (requested_list.fc_ie) { + //Static 1 bytes allways + length += WH_IE_ELEMENT_HEADER_LENGTH + 1; + } + + if (requested_list.rsl_ie) { + //Static 1 bytes allways + length += WH_IE_ELEMENT_HEADER_LENGTH + 1; + } + + if (requested_list.vh_ie) { + //Dynamic length + length += WH_IE_ELEMENT_HEADER_LENGTH + params->vendor_header_length; + } + + if (requested_list.ea_ie) { + length += WH_IE_ELEMENT_HEADER_LENGTH + 8; + } + + return length; +} + +static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params) +{ + uint16_t length = 0; + if (requested_list.gtkhash_ie) { + //Static 32 bytes allways + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->gtkhash_length; + } + + if (requested_list.net_name_ie) { + //Dynamic length + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->network_name_length; + } + + if (requested_list.vp_ie && params->vendor_payload_length) { + //Dynamic length + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->vendor_payload_length; + } + + if (requested_list.pan_ie) { + //Static 5 bytes allways + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH; + if (params->pan_congiguration) { + length += 5; + } + } + + if (requested_list.pan_version_ie) { + //Static 2 bytes allways + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH; + if (params->pan_congiguration) { + length += 2; + } + } + + if (requested_list.bs_ie) { + ///Dynamic length + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, false); + } + + if (requested_list.us_ie) { + //Dynamic length + length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, true); + } + + return length; +} + +static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id) +{ + for (int i = 0; i < MPX_USER_SIZE; i++) { + if (mpx_class->mpx_user_table[i].user_id == user_id) { + return &mpx_class->mpx_user_table[i]; + } + } + return NULL; +} + +static llc_data_base_t *ws_llc_base_allocate(void) +{ + llc_data_base_t *base = ns_dyn_mem_temporary_alloc(sizeof(llc_data_base_t)); + if (!base) { + return NULL; + } + memset(base, 0, sizeof(llc_data_base_t)); + + + ns_list_init(&base->llc_message_list); + ns_list_add_to_end(&llc_data_base_list, base); + return base; +} + +/** WS LLC MAC data extension confirmation */ +static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data) +{ + (void) conf_data; + llc_data_base_t *base = ws_llc_discover_by_mac(api); + if (!base) { + return; + } + + protocol_interface_info_entry_t *interface = base->interface_ptr; + + llc_message_t *message = llc_message_discover_by_mac_handle(data->msduHandle, &base->llc_message_list); + if (!message) { + return; + } + + uint8_t messsage_type = message->messsage_type; + uint8_t mpx_user_handle = message->mpx_user_handle; + //ETX update + if (message->ack_requested && messsage_type == WS_FT_DATA) { + llc_neighbour_req_t neighbor_info; + bool success = false; + + switch (data->status) { + case MLME_SUCCESS: + case MLME_TX_NO_ACK: + case MLME_NO_DATA: + if (data->status == MLME_SUCCESS || data->status == MLME_NO_DATA) { + success = true; + } + + if (message->dst_address_type == MAC_ADDR_MODE_64_BIT && base->ws_neighbor_info_request_cb(interface, message->dst_address, &neighbor_info, false)) { + etx_transm_attempts_update(interface->id, 1 + data->tx_retries, success, neighbor_info.neighbor->index); + //TODO discover RSL from Enchanced ACK Header IE elements + ws_utt_ie_t ws_utt; + if (ws_wh_utt_read(conf_data->headerIeList, conf_data->headerIeListLength, &ws_utt)) { + //UTT header + if (success) { + neighbor_info.neighbor->lifetime = neighbor_info.neighbor->link_lifetime; + } + + ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp); + } + + int8_t rsl; + if (ws_wh_rsl_read(conf_data->headerIeList, conf_data->headerIeListLength, &rsl)) { + ws_neighbor_class_rsl_out_calculate(neighbor_info.ws_neighbor, rsl); + } + } + + break; + default: + break; + } + } + //Free message + llc_message_free(message, base); + + if (messsage_type == WS_FT_DATA || messsage_type == WS_FT_EAPOL) { + mpx_user_t *user_cb; + uint16_t mpx_user_id; + if (messsage_type == WS_FT_DATA) { + mpx_user_id = MPX_LOWPAN_ENC_USER_ID; + } else { + mpx_user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID; + } + + user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_user_id); + if (user_cb && user_cb->data_confirm) { + //Call MPX registered call back + mcps_data_conf_t data_conf = *data; + data_conf.msduHandle = mpx_user_handle; + user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf); + } + return; + } + //Async message Confirmation + base->asynch_confirm(base->interface_ptr, messsage_type); + +} + +static void ws_llc_ack_data_req_ext(const mac_api_t *api, mcps_ack_data_payload_t *data, int8_t rssi, uint8_t lqi) +{ + (void) lqi; + llc_data_base_t *base = ws_llc_discover_by_mac(api); + if (!base) { + return; + } + /* Init all by zero */ + memset(data, 0, sizeof(mcps_ack_data_payload_t)); + //Add just 2 header elements to inside 1 block + data->ie_elements.headerIeVectorList = &base->ws_header_vector; + base->ws_header_vector.ieBase = base->ws_enhanced_ack_elements; + base->ws_header_vector.iovLen = sizeof(base->ws_enhanced_ack_elements); + data->ie_elements.headerIovLength = 1; + + //Write Data to block + uint8_t *ptr = base->ws_enhanced_ack_elements; + ptr = ws_wh_utt_write(ptr, WS_FT_ACK); + uint8_t rsl = ws_neighbor_class_rssi_from_dbm_calculate(rssi); + ws_wh_rsl_write(ptr, rsl); +} + +/** WS LLC MAC data extension indication */ +static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext) +{ + llc_data_base_t *base = ws_llc_discover_by_mac(api); + if (!base) { + return; + } + + //Discover Header WH_IE_UTT_TYPE + ws_utt_ie_t ws_utt; + if (!ws_wh_utt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_utt)) { + // NO UTT header + return; + } + + protocol_interface_info_entry_t *interface = base->interface_ptr; + if (!base->ie_params.gtkhash && ws_utt.message_type == WS_FT_DATA) { + return; + } + + //Discover 2 Payload Heder + if (ws_utt.message_type == WS_FT_DATA || ws_utt.message_type == WS_FT_EAPOL) { + + if (data->SrcAddrMode != ADDR_802_15_4_LONG) { + return; + } + + mac_payload_IE_t mpx_ie; + mpx_ie.id = MAC_PAYLOAD_MPX_IE_GROUP_ID; + if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &mpx_ie) < 1) { + // NO MPX + return; + } + //Validate MPX header + mpx_msg_t mpx_frame; + if (!ws_llc_mpx_header_frame_parse(mpx_ie.content_ptr, mpx_ie.length, &mpx_frame)) { + return; + } + + if (mpx_frame.transfer_type != MPX_FT_FULL_FRAME) { + return; //Support only FULL Frame's + } + + mac_payload_IE_t ws_wp_nested; + ws_us_ie_t us_ie; + bool us_ie_inline = false; + ws_wp_nested.id = WS_WP_NESTED_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); + } + + llc_neighbour_req_t neighbor_info; + + if (!base->ws_neighbor_info_request_cb(interface, data->SrcAddr, &neighbor_info, us_ie_inline)) { + tr_debug("Drop message no neighbor"); + return; + } + + ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp); + if (us_ie_inline) { + ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie); + } + + if (ws_utt.message_type == WS_FT_EAPOL) { + uint8_t auth_eui64[8]; + //Discover and write Auhtenticator EUI-64 + 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); + } + } + + //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)) { + ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt, data->timestamp); + 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); + } + } + + //Refresh Neighbor if unicast + if (ws_utt.message_type == WS_FT_DATA && data->DstAddrMode == ADDR_802_15_4_LONG) { + neighbor_info.neighbor->lifetime = neighbor_info.neighbor->link_lifetime; + etx_lqi_dbm_update(interface->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index); + } + if (ws_utt.message_type == WS_FT_DATA) { + // Calculate RSL for all UDATA packages heard + ws_neighbor_class_rsl_in_calculate(neighbor_info.ws_neighbor, data->signal_dbm); + + if (data->Key.SecurityLevel) { + //SET trusted state + mac_neighbor_table_trusted_neighbor(mac_neighbor_info(interface), neighbor_info.neighbor, true); + } + } + + // Discover MPX + mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_frame.multiplex_id); + if (user_cb && user_cb->data_ind) { + mcps_data_ind_t data_ind = *data; + data_ind.msdu_ptr = mpx_frame.frame_ptr; + data_ind.msduLength = mpx_frame.frame_length; + user_cb->data_ind(&base->mpx_data_base.mpx_api, &data_ind); + } + return; + } + + //Asynch Message + if (ws_utt.message_type < WS_FT_DATA && base->asynch_ind) { + mac_payload_IE_t ws_wp_nested; + + ws_wp_nested.id = WS_WP_NESTED_IE; + if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) < 2) { + // NO WS_WP_NESTED_IE Payload + return; + } + + mcps_data_ie_list_t asynch_ie_list; + asynch_ie_list.headerIeList = ie_ext->headerIeList, + asynch_ie_list.headerIeListLength = ie_ext->headerIeListLength; + asynch_ie_list.payloadIeList = ws_wp_nested.content_ptr; + asynch_ie_list.payloadIeListLength = ws_wp_nested.length; + base->asynch_ind(interface, data, &asynch_ie_list, ws_utt.message_type); + } + +} + +static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id) +{ + //TODO add WS_WP_NESTED_IE support + uint16_t header_size = 0; + if (user_id == MPX_LOWPAN_ENC_USER_ID) { + header_size += 7 + 8 + 5 + 2; //UTT+BTT+ MPX + Padding + if (base->ie_params.vendor_header_length) { + header_size += base->ie_params.vendor_header_length + 3; + } + + if (base->ie_params.vendor_payload_length) { + header_size += base->ie_params.vendor_payload_length + 2; + } + + //Dynamic length + header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true); + + } else if (MPX_KEY_MANAGEMENT_ENC_USER_ID) { + header_size += 7 + 5 + 2; + //Dynamic length + header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true); + } + return header_size; +} + +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); + if (!base) { + return; + } + + mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id); + if (!user_cb || !user_cb->data_confirm || !user_cb->data_ind) { + return; + } + + wh_ie_sub_list_t ie_header_mask; + memset(&ie_header_mask, 0, sizeof(wh_ie_sub_list_t)); + + wp_nested_ie_sub_list_t nested_wp_id; + memset(&nested_wp_id, 0, sizeof(wp_nested_ie_sub_list_t)); + ie_header_mask.utt_ie = true; + + if (user_id == MPX_LOWPAN_ENC_USER_ID) { + ie_header_mask.bt_ie = true; + if (base->ie_params.vendor_header_length) { + ie_header_mask.vh_ie = true; + } + + if (base->ie_params.vendor_payload_length) { + 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; + } + + } + + nested_wp_id.us_ie = true; + + uint16_t ie_header_length = ws_wh_headers_length(ie_header_mask, &base->ie_params); + uint16_t nested_ie_length = ws_wp_nested_message_length(nested_wp_id, &base->ie_params); + + uint16_t over_head_size = ie_header_length; + if (nested_ie_length) { + over_head_size += nested_ie_length + 2; + } + //Mpx header size + over_head_size += 5; //MPX FuLL frame 3 bytes + IE header 2 bytes + + //Allocate Message + llc_message_t *message = llc_message_allocate(over_head_size, base, true); + if (!message) { + mcps_data_conf_t data_conf; + memset(&data_conf, 0, sizeof(mcps_data_conf_t)); + data_conf.msduHandle = data->msduHandle; + data_conf.status = MLME_TRANSACTION_OVERFLOW; + user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf); + return; + } + mcps_data_req_t data_req; + message->mpx_user_handle = data->msduHandle; + message->ack_requested = data->TxAckReq; + if (data->TxAckReq) { + message->dst_address_type = data->DstAddrMode; + memcpy(message->dst_address, data->DstAddr, 8); + } + data_req = *data; + data_req.msdu = NULL; + data_req.msduLength = 0; + data_req.msduHandle = message->msg_handle; + + if (!data->TxAckReq) { + data_req.PanIdSuppressed = false; + data_req.DstAddrMode = MAC_ADDR_MODE_NONE; + } else { + data_req.PanIdSuppressed = true; + } + + uint8_t *ptr = ws_message_buffer_ptr_get(message); + if (user_id == MPX_LOWPAN_ENC_USER_ID) { + message->messsage_type = WS_FT_DATA; + } else { + message->messsage_type = WS_FT_EAPOL; + } + + message->ie_vector_list[0].ieBase = ptr; + //Write UTT + + ptr = ws_wh_utt_write(ptr, message->messsage_type); + if (ie_header_mask.bt_ie) { + ptr = ws_wh_bt_write(ptr); + } + + if (user_id == MPX_LOWPAN_ENC_USER_ID) { + if (ie_header_mask.vh_ie) { + ptr = ws_wh_vh_write(ptr, base->ie_params.vendor_header_data, base->ie_params.vendor_header_length); + } + } else if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) { + if (ie_header_mask.ea_ie) { + uint8_t eapol_auth_eui64[8]; + ws_pae_controller_border_router_addr_read(base->interface_ptr, eapol_auth_eui64); + ptr = ws_wh_ea_write(ptr, eapol_auth_eui64); + } + } + + + + message->ie_vector_list[0].iovLen = ie_header_length; + message->ie_ext.headerIeVectorList = &message->ie_vector_list[0]; + message->ie_ext.headerIovLength = 1; + message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1]; + message->ie_ext.payloadIovLength = 2; + message->ie_vector_list[1].ieBase = ptr; + + if (nested_ie_length) { + 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); + } + + + ptr = mac_ie_payload_base_write(ptr, MAC_PAYLOAD_MPX_IE_GROUP_ID, data->msduLength + 3); + mpx_msg_t mpx_header; + mpx_header.transfer_type = MPX_FT_FULL_FRAME; + mpx_header.transaction_id = message->mpx_id; + mpx_header.multiplex_id = user_id; + ptr = ws_llc_mpx_header_write(ptr, &mpx_header); + message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase; + message->ie_vector_list[2].ieBase = data->msdu; + message->ie_vector_list[2].iovLen = data->msduLength; + base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL); + +} + + +static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id) +{ + llc_data_base_t *base = ws_llc_discover_by_mpx(api); + if (!base) { + return -1; + } + + mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id); + if (!user_cb) { + return -1; + } + user_cb->data_confirm = confirm_cb; + user_cb->data_ind = indication_cb; + return 0; +} + +static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id) +{ + llc_data_base_t *base = ws_llc_discover_by_mpx(api); + if (!base) { + return 0; + } + + return ws_mpx_header_size_get(base, user_id); +} + +static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id) +{ + llc_data_base_t *base = ws_llc_discover_by_mpx(api); + if (!base) { + return MLME_INVALID_HANDLE; + } + llc_message_t *message = llc_message_discover_mpx_user_id(purge->msduHandle, user_id, &base->llc_message_list); + if (!message) { + return MLME_INVALID_HANDLE; + } + + mcps_purge_t purge_req; + uint8_t purge_status; + purge_req.msduHandle = message->msg_handle; + purge_status = base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req); + if (purge_status == 0) { + llc_message_free(message, base); + } + + return purge_status; +} + +static void ws_llc_mpx_init(mpx_class_t *mpx_class) +{ + //Init Mbed Class and API + mpx_class->mpx_user_table[0].user_id = MPX_LOWPAN_ENC_USER_ID; + mpx_class->mpx_user_table[1].user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID; + mpx_class->mpx_api.mpx_headroom_size_get = &ws_llc_mpx_header_size_get; + mpx_class->mpx_api.mpx_user_registration = &ws_llc_mpx_data_cb_register; + mpx_class->mpx_api.mpx_data_request = &ws_llc_mpx_data_request; + mpx_class->mpx_api.mpx_data_purge = &ws_llc_mpx_data_purge_request; +} + +static void ws_llc_clean(llc_data_base_t *base) +{ + //Clean Message queue's + mcps_purge_t purge_req; + ns_list_foreach_safe(llc_message_t, message, &base->llc_message_list) { + purge_req.msduHandle = message->msg_handle; + llc_message_free(message, base); + base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req); + + } + memset(&base->ie_params, 0, sizeof(llc_ie_params_t)); +} + + +int8_t ws_llc_create(struct protocol_interface_info_entry *interface, ws_asynch_ind *asynch_ind_cb, ws_asynch_confirm *asynch_cnf_cb, ws_neighbor_info_request *ws_neighbor_info_request_cb) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (base) { + ws_llc_clean(base); + return 0; + } + + //Allocate Data base + base = ws_llc_base_allocate(); + if (!base) { + return -2; + } + + base->interface_ptr = interface; + base->asynch_ind = asynch_ind_cb; + base->asynch_confirm = asynch_cnf_cb; + base->ws_neighbor_info_request_cb = ws_neighbor_info_request_cb; + //Register MAC Extensions + base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, &ws_llc_mac_indication_cb, &ws_llc_mac_confirm_cb, &ws_llc_ack_data_req_ext); + //Init MPX class + ws_llc_mpx_init(&base->mpx_data_base); + return 0; +} + +int8_t ws_llc_delete(struct protocol_interface_info_entry *interface) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return -1; + } + + ws_llc_clean(base); + + ns_list_remove(&llc_data_base_list, base); + //Disable Mac extension + base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, NULL, NULL, NULL); + ns_dyn_mem_free(base); + return 0; +} + + + +void ws_llc_reset(struct protocol_interface_info_entry *interface) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + ws_llc_clean(base); +} + +mpx_api_t *ws_llc_mpx_api_get(struct protocol_interface_info_entry *interface) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return NULL; + } + return &base->mpx_data_base.mpx_api; +} + +int8_t ws_llc_asynch_request(struct protocol_interface_info_entry *interface, asynch_request_t *request) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return -1; + } + + //Calculate IE Buffer size + request->wh_requested_ie_list.fc_ie = false; //Never should not be a part Asynch message + request->wh_requested_ie_list.rsl_ie = false; //Never should not be a part Asynch message + request->wh_requested_ie_list.vh_ie = false; + uint16_t header_buffer_length = ws_wh_headers_length(request->wh_requested_ie_list, &base->ie_params); + uint16_t wp_nested_payload_length = ws_wp_nested_message_length(request->wp_requested_nested_ie_list, &base->ie_params); + + //Allocated + uint16_t total_length = header_buffer_length; + if (wp_nested_payload_length) { + total_length += 2 + wp_nested_payload_length; + } + //Allocate LLC message pointer + + llc_message_t *message = llc_message_allocate(total_length, base, false); + if (!message) { + if (base->asynch_confirm) { + base->asynch_confirm(interface, request->message_type); + } + return 0; + } + message->messsage_type = request->message_type; + + mcps_data_req_t data_req; + memset(&data_req, 0, sizeof(mcps_data_req_t)); + data_req.SeqNumSuppressed = true; + data_req.SrcAddrMode = MAC_ADDR_MODE_64_BIT; + data_req.Key = request->security; + data_req.msduHandle = message->msg_handle; + if (request->message_type == WS_FT_PAN_ADVERT_SOL) { + // PANID not know yet must be supressed + data_req.PanIdSuppressed = true; + } + + uint8_t *ptr = ws_message_buffer_ptr_get(message); + + message->ie_vector_list[0].ieBase = ptr; + message->ie_vector_list[0].iovLen = header_buffer_length; + + message->ie_ext.headerIeVectorList = &message->ie_vector_list[0]; + message->ie_ext.headerIovLength = 1; + + + //Write UTT + if (request->wh_requested_ie_list.utt_ie) { + ptr = ws_wh_utt_write(ptr, message->messsage_type); + } + + if (request->wh_requested_ie_list.bt_ie) { + //Static 5 bytes allways + ptr = ws_wh_bt_write(ptr); + } + + if (wp_nested_payload_length) { + message->ie_vector_list[1].ieBase = ptr; + message->ie_vector_list[1].iovLen = 2 + wp_nested_payload_length; + message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1]; + message->ie_ext.payloadIovLength = 1; + ptr = ws_wp_base_write(ptr, wp_nested_payload_length); + + if (request->wp_requested_nested_ie_list.us_ie) { + //Write unicast schedule + ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true); + } + + if (request->wp_requested_nested_ie_list.bs_ie) { + //Write Broadcastcast schedule + ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false); + } + + if (request->wp_requested_nested_ie_list.pan_ie) { + //Write Pan information + ptr = ws_wp_nested_pan_info_write(ptr, base->ie_params.pan_congiguration); + } + + if (request->wp_requested_nested_ie_list.net_name_ie) { + //Write network name + ptr = ws_wp_nested_netname_write(ptr, base->ie_params.network_name, base->ie_params.network_name_length); + } + + if (request->wp_requested_nested_ie_list.pan_version_ie) { + //Write pan version + ptr = ws_wp_nested_pan_ver_write(ptr, base->ie_params.pan_congiguration); + } + + if (request->wp_requested_nested_ie_list.gtkhash_ie) { + //Write GTKHASH + ptr = ws_wp_nested_gtkhash_write(ptr, base->ie_params.gtkhash, base->ie_params.gtkhash_length); + } + + if (request->wp_requested_nested_ie_list.vp_ie) { + //Write Vendor spesific payload + ptr = ws_wp_nested_vp_write(ptr, base->ie_params.vendor_payload, base->ie_params.vendor_payload_length); + } + } + + base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, &request->channel_list); + + return 0; +} + + +void ws_llc_set_vendor_header_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_header, uint8_t vendor_header_length) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + base->ie_params.vendor_header_data = vendor_header; + base->ie_params.vendor_header_length = vendor_header_length; +} + + +void ws_llc_set_vendor_payload_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_payload, uint8_t vendor_payload_length) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + + base->ie_params.vendor_payload = vendor_payload; + base->ie_params.vendor_payload_length = vendor_payload_length; +} + + +void ws_llc_set_network_name(struct protocol_interface_info_entry *interface, uint8_t *name, uint8_t name_length) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + + base->ie_params.network_name = name; + base->ie_params.network_name_length = name_length; +} + +void ws_llc_set_gtkhash(struct protocol_interface_info_entry *interface, uint8_t *gtkhash) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + + base->ie_params.gtkhash = gtkhash; + if (base->ie_params.gtkhash) { + base->ie_params.gtkhash_length = 32; + } else { + base->ie_params.gtkhash_length = 0; + } +} + + + +void ws_llc_set_pan_information_pointer(struct protocol_interface_info_entry *interface, struct ws_pan_information_s *pan_information_pointer) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + + base->ie_params.pan_congiguration = pan_information_pointer; +} + +void ws_llc_hopping_schedule_config(struct protocol_interface_info_entry *interface, struct ws_hopping_schedule_s *hopping_schedule) +{ + llc_data_base_t *base = ws_llc_discover_by_interface(interface); + if (!base) { + return; + } + base->ie_params.hopping_schedule = hopping_schedule; +} +#endif diff --git a/source/6LoWPAN/ws/ws_management_api.c b/source/6LoWPAN/ws/ws_management_api.c new file mode 100644 index 0000000000..e733080d8d --- /dev/null +++ b/source/6LoWPAN/ws/ws_management_api.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2018-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. + */ + +#include +#include "nsconfig.h" +#include "ns_types.h" +#include "ns_trace.h" +#include +#include +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bootstrap.h" + +#include "ws_management_api.h" + +#define TRACE_GROUP "wsmg" + +#ifdef HAVE_WS + +int ws_management_node_init( + int8_t interface_id, + uint8_t regulatory_domain, + char *network_name_ptr, + fhss_timer_t *fhss_timer_ptr) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + if (!network_name_ptr || !fhss_timer_ptr) { + return -2; + } + cur->ws_info->hopping_schdule.regulatory_domain = regulatory_domain; + if (ws_common_regulatory_domain_config(cur) < 0) { + // Invalid regulatory domain set + return -3; + } + strncpy(cur->ws_info->network_name, network_name_ptr, 32); + cur->ws_info->fhss_timer_ptr = fhss_timer_ptr; + return 0; +} + +int ws_management_regulatory_domain_set( + int8_t interface_id, + uint8_t regulatory_domain, + uint8_t operating_class, + uint8_t operating_mode) +{ + uint8_t regulatory_domain_saved; + uint8_t operating_class_saved; + uint8_t operating_mode_saved; + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + regulatory_domain_saved = cur->ws_info->hopping_schdule.regulatory_domain; + operating_class_saved = cur->ws_info->hopping_schdule.operating_mode; + operating_mode_saved = cur->ws_info->hopping_schdule.operating_class; + if (regulatory_domain != 255) { + cur->ws_info->hopping_schdule.regulatory_domain = regulatory_domain; + } + if (operating_mode != 255) { + cur->ws_info->hopping_schdule.operating_mode = operating_mode; + } + if (operating_class != 255) { + cur->ws_info->hopping_schdule.operating_class = operating_class; + } + if (ws_common_regulatory_domain_config(cur) != 0) { + // Restore old config on failure + //tr_error("unsupported regulatory domain: %d class: %d, mode: %d", regulatory_domain, operating_class, operating_mode); + cur->ws_info->hopping_schdule.regulatory_domain = regulatory_domain_saved; + cur->ws_info->hopping_schdule.operating_mode = operating_mode_saved; + cur->ws_info->hopping_schdule.operating_class = operating_class_saved; + ws_common_regulatory_domain_config(cur); + return -1; + } + // if settings change reset_restart for the settings needed + if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { + // bootstrap active need to restart + ws_bootstrap_restart(interface_id); + } + + return 0; +} + +int ws_management_network_size_set( + int8_t interface_id, + uint8_t network_size) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + if (network_size > NETWORK_SIZE_LARGE) { + return -2; + } + ws_info(cur)->network_size_config = network_size; + + if (network_size == NETWORK_SIZE_LARGE) { + ws_common_network_size_configure(cur, 5000); + } else { + ws_common_network_size_configure(cur, 10); + } + return 0; +} + +int ws_management_channel_mask_set( + int8_t interface_id, + uint32_t channel_mask[8]) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + memcpy(cur->ws_info->fhss_channel_mask, channel_mask, sizeof(uint32_t) * 8); + return 0; +} + +int ws_management_channel_plan_set( + int8_t interface_id, + uint8_t channel_plan, + uint8_t uc_channel_function, + uint8_t bc_channel_function, + uint32_t ch0_freq, // Stack can not modify this + uint8_t channel_spacing,// Stack can not modify this + uint8_t number_of_channels) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + cur->ws_info->hopping_schdule.channel_plan = channel_plan; + cur->ws_info->hopping_schdule.uc_channel_function = uc_channel_function; + cur->ws_info->hopping_schdule.bc_channel_function = bc_channel_function; + cur->ws_info->hopping_schdule.ch0_freq = ch0_freq; + cur->ws_info->hopping_schdule.channel_spacing = channel_spacing; + cur->ws_info->hopping_schdule.number_of_channels = number_of_channels; + + // TODO update fields to llc + return 0; +} + +int ws_management_fhss_timing_configure( + int8_t interface_id, + uint8_t fhss_uc_dwell_interval, + uint32_t fhss_broadcast_interval, + uint8_t fhss_bc_dwell_interval) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + if (fhss_uc_dwell_interval > 0) { + cur->ws_info->fhss_uc_dwell_interval = fhss_uc_dwell_interval; + } + if (fhss_broadcast_interval > 0) { + cur->ws_info->fhss_bc_interval = fhss_broadcast_interval; + + } + if (fhss_bc_dwell_interval > 0) { + cur->ws_info->fhss_bc_dwell_interval = fhss_bc_dwell_interval; + + } + + // if settings change reset_restart for the settings needed + if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { + // bootstrap active need to restart + ws_bootstrap_restart(interface_id); + } + return 0; +} + +int ws_management_fhss_unicast_channel_function_configure( + int8_t interface_id, + uint8_t channel_function, + uint16_t fixed_channel, + uint8_t dwell_interval) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + if (channel_function != WS_FIXED_CHANNEL && + channel_function != WS_VENDOR_DEF_CF && + channel_function != WS_DH1CF && + channel_function != WS_TR51CF) { + return -2; + } + cur->ws_info->fhss_uc_channel_function = channel_function; + cur->ws_info->fhss_uc_fixed_channel = fixed_channel; + cur->ws_info->fhss_uc_dwell_interval = dwell_interval; + + // if settings change reset_restart for the settings needed + if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { + // bootstrap active need to restart + ws_bootstrap_restart(interface_id); + } + return 0; + +} + +int ws_management_fhss_broadcast_channel_function_configure( + int8_t interface_id, + uint8_t channel_function, + uint16_t fixed_channel, + uint8_t dwell_interval, + uint32_t broadcast_interval) +{ + protocol_interface_info_entry_t *cur; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur || !ws_info(cur)) { + return -1; + } + if (channel_function != WS_FIXED_CHANNEL && + channel_function != WS_VENDOR_DEF_CF && + channel_function != WS_DH1CF && + channel_function != WS_TR51CF) { + return -2; + } + cur->ws_info->fhss_bc_channel_function = channel_function; + cur->ws_info->fhss_bc_fixed_channel = fixed_channel; + cur->ws_info->fhss_bc_dwell_interval = dwell_interval; + cur->ws_info->fhss_bc_interval = broadcast_interval; + + // if settings change reset_restart for the settings needed + if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) { + // bootstrap active need to restart + ws_bootstrap_restart(interface_id); + } + return 0; + +} +#endif // HAVE_WS diff --git a/source/6LoWPAN/ws/ws_mpx_header.c b/source/6LoWPAN/ws/ws_mpx_header.c new file mode 100644 index 0000000000..6d0ad5ae56 --- /dev/null +++ b/source/6LoWPAN/ws/ws_mpx_header.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "mac_common_defines.h" +#include "ws_mpx_header.h" + +bool ws_llc_mpx_header_frame_parse(uint8_t *ptr, uint16_t length, mpx_msg_t *msg) +{ + if (!length) { + return false; + } + memset(msg, 0, sizeof(mpx_msg_t)); + bool fragmented_number_present = false; + bool multiplex_id_present = false; + bool fragment_total_size = false; + + msg->transfer_type = *ptr & 7; + msg->transaction_id = ((*ptr++ & 0xf8) >> 3); + length--; + + + switch (msg->transfer_type) { + case MPX_FT_FULL_FRAME: + multiplex_id_present = true; + break; + case MPX_FT_FULL_FRAME_SMALL_MULTILEX_ID: + break; + case MPX_FT_FIRST_OR_SUB_FRAGMENT: + case MPX_FT_LAST_FRAGMENT: + fragmented_number_present = true; + if (length < 2) { + return false; + } + break; + case MPX_FT_ABORT: + if (length == 2) { + fragment_total_size = true; + } else if (length) { + return false; + } + break; + default: + return false; + } + + if (fragmented_number_present) { + + msg->fragment_number = *ptr++; + length--; + if (msg->fragment_number == 0) { //First fragment + fragment_total_size = true; + multiplex_id_present = true; + } + } + + if (fragment_total_size) { + if (length < 2) { + return false; + } + msg->total_upper_layer_size = common_read_16_bit_inverse(ptr); + ptr += 2; + length -= 2; + } + + if (multiplex_id_present) { + if (length < 3) { + return false; + } + msg->multiplex_id = common_read_16_bit_inverse(ptr); + ptr += 2; + length -= 2; + } + + msg->frame_ptr = ptr; + msg->frame_length = length; + return true; +} + + +uint8_t *ws_llc_mpx_header_write(uint8_t *ptr, const mpx_msg_t *msg) +{ + + bool fragmented_number_present = false; + bool multiplex_id_present = false; + bool fragment_total_size = false; + + *ptr = msg->transfer_type; + *ptr++ |= ((msg->transaction_id << 3) & 0xf8); + + switch (msg->transfer_type) { + case MPX_FT_FULL_FRAME: + multiplex_id_present = true; + break; + case MPX_FT_FULL_FRAME_SMALL_MULTILEX_ID: + break; + case MPX_FT_FIRST_OR_SUB_FRAGMENT: + case MPX_FT_LAST_FRAGMENT: + fragmented_number_present = true; + if (msg->fragment_number == 0) { + fragment_total_size = true; + multiplex_id_present = true; + } + break; + case MPX_FT_ABORT: + if (msg->total_upper_layer_size) { + fragment_total_size = true; + } + break; + default: + break; + } + + if (fragmented_number_present) { + *ptr++ = msg->fragment_number; + } + + if (fragment_total_size) { + ptr = common_write_16_bit_inverse(msg->total_upper_layer_size, ptr); + } + + if (multiplex_id_present) { + ptr = common_write_16_bit_inverse(msg->multiplex_id, ptr); + } + return ptr; +} diff --git a/source/6LoWPAN/ws/ws_mpx_header.h b/source/6LoWPAN/ws/ws_mpx_header.h new file mode 100644 index 0000000000..2e64b0571b --- /dev/null +++ b/source/6LoWPAN/ws/ws_mpx_header.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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_MPX_HEADER_H_ +#define WS_MPX_HEADER_H_ + +#define MPX_FT_FULL_FRAME 0 +#define MPX_FT_FULL_FRAME_SMALL_MULTILEX_ID 1 +#define MPX_FT_FIRST_OR_SUB_FRAGMENT 2 +#define MPX_FT_LAST_FRAGMENT 4 +#define MPX_FT_ABORT 6 + +typedef struct { + unsigned transfer_type: 3; + unsigned transaction_id: 5; + uint8_t fragment_number; + uint16_t total_upper_layer_size; + uint16_t multiplex_id; + uint8_t *frame_ptr; + uint16_t frame_length; +} mpx_msg_t; + +bool ws_llc_mpx_header_frame_parse(uint8_t *ptr, uint16_t length, mpx_msg_t *msg); +uint8_t *ws_llc_mpx_header_write(uint8_t *ptr, const mpx_msg_t *msg); + + +#endif /* WS_MPX_HEADER_H_ */ diff --git a/source/6LoWPAN/ws/ws_neighbor_class.c b/source/6LoWPAN/ws/ws_neighbor_class.c new file mode 100644 index 0000000000..230e5e9986 --- /dev/null +++ b/source/6LoWPAN/ws/ws_neighbor_class.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "fhss_config.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_neighbor_class.h" +#include "6LoWPAN/ws/ws_common.h" +#include "ws_management_api.h" + +#ifdef HAVE_WS + + +#define TRACE_GROUP "wsne" + + +bool ws_neighbor_class_alloc(ws_neighbor_class_t *class_data, uint8_t list_size) +{ + + class_data->neigh_info_list = ns_dyn_mem_alloc(sizeof(ws_neighbor_class_entry_t) * list_size); + if (!class_data->neigh_info_list) { + return false; + } + + class_data->list_size = list_size; + ws_neighbor_class_entry_t *list_ptr = class_data->neigh_info_list; + for (uint8_t i = 0; i < list_size; i++) { + memset(list_ptr, 0, sizeof(ws_neighbor_class_entry_t)); + list_ptr->rsl_in = RSL_UNITITIALIZED; + list_ptr->rsl_out = RSL_UNITITIALIZED; + list_ptr++; + } + return true; +} + + +void ws_neighbor_class_dealloc(ws_neighbor_class_t *class_data) +{ + ns_dyn_mem_free(class_data->neigh_info_list); + class_data->neigh_info_list = NULL; + class_data->list_size = 0; +} + +ws_neighbor_class_entry_t *ws_neighbor_class_entry_get(ws_neighbor_class_t *class_data, uint8_t attribute_index) +{ + if (!class_data->neigh_info_list || attribute_index >= class_data->list_size) { + return NULL; + } + + ws_neighbor_class_entry_t *entry = class_data->neigh_info_list + attribute_index; + return entry; +} + +uint8_t ws_neighbor_class_entry_index_get(ws_neighbor_class_t *class_data, ws_neighbor_class_entry_t *entry) +{ + if (!class_data->neigh_info_list) { + return 0xff; + } + return entry - class_data->neigh_info_list; +} + +void ws_neighbor_class_entry_remove(ws_neighbor_class_t *class_data, uint8_t attribute_index) +{ + ws_neighbor_class_entry_t *entry = ws_neighbor_class_entry_get(class_data, attribute_index); + if (entry) { + memset(entry, 0, sizeof(ws_neighbor_class_entry_t)); + entry->rsl_in = RSL_UNITITIALIZED; + entry->rsl_out = RSL_UNITITIALIZED; + } +} + +void ws_neighbor_class_neighbor_unicast_time_info_update(ws_neighbor_class_entry_t *ws_neighbor, ws_utt_ie_t *ws_utt, uint32_t timestamp) +{ + ws_neighbor->fhss_data.uc_timing_info.utt_rx_timestamp = timestamp; + ws_neighbor->fhss_data.uc_timing_info.ufsi = ws_utt->ufsi; +} + +void ws_neighbor_class_neighbor_unicast_schedule_set(ws_neighbor_class_entry_t *ws_neighbor, ws_us_ie_t *ws_us) +{ + ws_neighbor->fhss_data.uc_timing_info.unicast_channel_function = ws_us->channel_function; + if (ws_us->channel_function == WS_FIXED_CHANNEL) { + ws_neighbor->fhss_data.uc_timing_info.fixed_channel = ws_us->function.zero.fixed_channel; + ws_neighbor->fhss_data.uc_timing_info.unicast_number_of_channels = 1; + } else { + if (ws_us->channel_plan == 0) { + ws_neighbor->fhss_data.uc_timing_info.unicast_number_of_channels = ws_common_channel_number_calc(ws_us->plan.zero.regulator_domain, ws_us->plan.zero.operation_class); + } else if (ws_us->channel_plan == 1) { + ws_neighbor->fhss_data.uc_timing_info.unicast_number_of_channels = ws_us->plan.one.number_of_channel; + } else { + ws_neighbor->fhss_data.uc_timing_info.unicast_number_of_channels = 0; + } + } + ws_neighbor->fhss_data.uc_timing_info.unicast_dwell_interval = ws_us->dwell_interval; +} + + +void ws_neighbor_class_neighbor_broadcast_time_info_update(ws_neighbor_class_entry_t *ws_neighbor, ws_bt_ie_t *ws_bt_ie, uint32_t timestamp) +{ + ws_neighbor->broadcast_timing_info_stored = true; + ws_neighbor->fhss_data.bc_timing_info.bt_rx_timestamp = timestamp; + ws_neighbor->fhss_data.bc_timing_info.broadcast_slot = ws_bt_ie->broadcast_slot_number; + ws_neighbor->fhss_data.bc_timing_info.broadcast_interval_offset = ws_bt_ie->broadcast_interval_offset; +} + + +void ws_neighbor_class_neighbor_broadcast_schedule_set(ws_neighbor_class_entry_t *ws_neighbor, ws_bs_ie_t *ws_bs_ie) +{ + ws_neighbor->broadcast_shedule_info_stored = true; + ws_neighbor->fhss_data.bc_timing_info.broadcast_channel_function = ws_bs_ie->channel_function; + if (ws_bs_ie->channel_function == WS_FIXED_CHANNEL) { + ws_neighbor->fhss_data.bc_timing_info.fixed_channel = ws_bs_ie->function.zero.fixed_channel; + } + ws_neighbor->fhss_data.bc_timing_info.broadcast_dwell_interval = ws_bs_ie->dwell_interval; + ws_neighbor->fhss_data.bc_timing_info.broadcast_interval = ws_bs_ie->broadcast_interval; + ws_neighbor->fhss_data.bc_timing_info.broadcast_schedule_id = ws_bs_ie->broadcast_schedule_identifier; +} + +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; +} + +static void ws_neighbor_class_parent_set_analyze(ws_neighbor_class_entry_t *ws_neighbor) +{ + if (ws_neighbor->rsl_in == RSL_UNITITIALIZED || + ws_neighbor->rsl_out == RSL_UNITITIALIZED) { + ws_neighbor->candidate_parent = false; + return; + } + + if (ws_neighbor_class_rsl_in_get(ws_neighbor) < (CAND_PARENT_THRESHOLD - CAND_PARENT_HYSTERISIS) && + ws_neighbor_class_rsl_out_get(ws_neighbor) < (CAND_PARENT_THRESHOLD - CAND_PARENT_HYSTERISIS)) { + ws_neighbor->candidate_parent = false; + } + + if (ws_neighbor_class_rsl_in_get(ws_neighbor) > (CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS) && + ws_neighbor_class_rsl_out_get(ws_neighbor) > (CAND_PARENT_THRESHOLD + CAND_PARENT_HYSTERISIS)) { + ws_neighbor->candidate_parent = true; + } +} + +void ws_neighbor_class_rsl_in_calculate(ws_neighbor_class_entry_t *ws_neighbor, int8_t dbm_heard) +{ + uint8_t rssi = ws_neighbor_class_rssi_from_dbm_calculate(dbm_heard); + if (ws_neighbor->rsl_in == RSL_UNITITIALIZED) { + ws_neighbor->rsl_in = rssi << WS_RSL_SCALING; + } + ws_neighbor->rsl_in = ws_neighbor->rsl_in + rssi - (ws_neighbor->rsl_in >> WS_RSL_SCALING); + ws_neighbor_class_parent_set_analyze(ws_neighbor); + return; +} + +void ws_neighbor_class_rsl_out_calculate(ws_neighbor_class_entry_t *ws_neighbor, uint8_t rsl_reported) +{ + if (ws_neighbor->rsl_out == RSL_UNITITIALIZED) { + ws_neighbor->rsl_out = rsl_reported << WS_RSL_SCALING; + } + ws_neighbor->rsl_out = ws_neighbor->rsl_out + rsl_reported - (ws_neighbor->rsl_out >> WS_RSL_SCALING); + ws_neighbor_class_parent_set_analyze(ws_neighbor); + return; +} + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_neighbor_class.h b/source/6LoWPAN/ws/ws_neighbor_class.h index 738f9340ff..3858465430 100644 --- a/source/6LoWPAN/ws/ws_neighbor_class.h +++ b/source/6LoWPAN/ws/ws_neighbor_class.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/6LoWPAN/ws/ws_pae_auth.c b/source/6LoWPAN/ws/ws_pae_auth.c new file mode 100644 index 0000000000..ff89417ae1 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_auth.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "fhss_config.h" +#include "eventOS_event.h" +#include "eventOS_scheduler.h" +#include "eventOS_event_timer.h" +#include "ns_address.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_config.h" +#include "Security/kmp/kmp_addr.h" +#include "Security/kmp/kmp_api.h" +#include "Security/kmp/kmp_socket_if.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" +#include "Security/protocols/key_sec_prot/key_sec_prot.h" +#include "Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.h" +#include "Security/protocols/tls_sec_prot/tls_sec_prot.h" +#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_auth.h" +#include "6LoWPAN/ws/ws_pae_lib.h" + +#ifdef HAVE_WS +#ifdef HAVE_PAE_AUTH + +#define TRACE_GROUP "wspa" + +#define PAE_TASKLET_INIT 1 +#define PAE_TASKLET_EVENT 2 +#define PAE_TASKLET_TIMER 3 + +// Wait for for supplicant to indicate activity (e.g. to send a message) +#define WAIT_FOR_AUTHENTICATION_TICKS 5 * 60 * 10 // 5 minutes + +typedef struct { + ns_list_link_t link; /**< Link */ + kmp_service_t *kmp_service; /**< KMP service */ + protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + 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 */ + const sec_prot_certs_t *certs; /**< Certificates */ + bool timer_running; /**< Timer is running */ +} pae_auth_t; + +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 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); +static int8_t ws_pae_auth_timer_stop(pae_auth_t *pae_auth); +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); +static kmp_api_t *ws_pae_auth_kmp_service_api_get(kmp_service_t *service, kmp_api_t *kmp, kmp_type_e type); +static kmp_api_t *ws_pae_auth_kmp_incoming_ind(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr); +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 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) +{ + if (!interface_ptr || !gtks || !certs) { + return -1; + } + + if (ws_pae_auth_get(interface_ptr) != NULL) { + return 0; + } + + pae_auth_t *pae_auth = ns_dyn_mem_alloc(sizeof(pae_auth_t)); + if (!pae_auth) { + return -1; + } + + pae_auth->interface_ptr = interface_ptr; + ws_pae_lib_supp_list_init(&pae_auth->active_supp_list); + ws_pae_lib_supp_list_init(&pae_auth->inactive_supp_list); + pae_auth->timer = NULL; + + pae_auth->gtks = gtks; + pae_auth->certs = certs; + + pae_auth->kmp_service = kmp_service_create(); + if (!pae_auth->kmp_service) { + goto error; + } + + if (kmp_service_cb_register(pae_auth->kmp_service, ws_pae_auth_kmp_incoming_ind, ws_pae_auth_kmp_service_addr_get, ws_pae_auth_kmp_service_api_get)) { + goto error; + } + + if (kmp_service_event_if_register(pae_auth->kmp_service, ws_pae_auth_event_send)) { + goto error; + } + + if (kmp_service_timer_if_register(pae_auth->kmp_service, ws_pae_auth_timer_if_start, ws_pae_auth_timer_if_stop)) { + goto error; + } + + if (key_sec_prot_register(pae_auth->kmp_service) < 0) { + goto error; + } + + if (auth_eap_tls_sec_prot_register(pae_auth->kmp_service) < 0) { + goto error; + } + + if (server_tls_sec_prot_register(pae_auth->kmp_service) < 0) { + goto error; + } + + if (auth_fwh_sec_prot_register(pae_auth->kmp_service) < 0) { + goto error; + } + + if (auth_gkh_sec_prot_register(pae_auth->kmp_service) < 0) { + goto error; + } + + if (tasklet_id < 0) { + tasklet_id = eventOS_event_handler_create(ws_pae_auth_tasklet_handler, PAE_TASKLET_INIT); + if (tasklet_id < 0) { + goto error; + } + } + + if (ws_pae_auth_timer_stop(pae_auth) < 0) { + goto error; + } + + ns_list_add_to_end(&pae_auth_list, pae_auth); + + return 0; + +error: + ws_pae_auth_free(pae_auth); + + return -1; +} + +int8_t ws_pae_auth_addresses_set(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port) +{ + if (!interface_ptr || !remote_addr) { + return -1; + } + + pae_auth_t *pae_auth = ws_pae_auth_get(interface_ptr); + if (!pae_auth) { + return -1; + } + if (!pae_auth->kmp_service) { + return -1; + } + + if (kmp_socket_if_register(pae_auth->kmp_service, local_port, remote_addr, remote_port) < 0) { + return -1; + } + + return 0; +} + +int8_t ws_pae_auth_delete(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; + } + + ws_pae_auth_free(pae_auth); + return 0; +} + +static void ws_pae_auth_free(pae_auth_t *pae_auth) +{ + if (!pae_auth) { + return; + } + + ws_pae_lib_supp_list_delete(&pae_auth->active_supp_list); + ws_pae_lib_supp_list_delete(&pae_auth->inactive_supp_list); + + kmp_socket_if_unregister(pae_auth->kmp_service); + + kmp_service_delete(pae_auth->kmp_service); + + ns_list_remove(&pae_auth_list, pae_auth); + ns_dyn_mem_free(pae_auth); +} + +static pae_auth_t *ws_pae_auth_get(protocol_interface_info_entry_t *interface_ptr) +{ + ns_list_foreach(pae_auth_t, entry, &pae_auth_list) { + if (entry->interface_ptr == interface_ptr) { + return entry; + } + } + + return NULL; +} + +static pae_auth_t *ws_pae_auth_by_kmp_service_get(kmp_service_t *service) +{ + ns_list_foreach(pae_auth_t, entry, &pae_auth_list) { + if (entry->kmp_service == service) { + return entry; + } + } + + return NULL; +} + +static int8_t ws_pae_auth_event_send(kmp_service_t *service, void *data) +{ + pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); + if (!pae_auth) { + return -1; + } + + arm_event_s event = { + .receiver = tasklet_id, + .sender = 0, + .event_id = pae_auth->interface_ptr->id, + .data_ptr = data, + .event_type = PAE_TASKLET_EVENT, + .priority = ARM_LIB_LOW_PRIORITY_EVENT, + }; + + if (eventOS_event_send(&event) != 0) { + return -1; + } + + return 0; +} + +static void ws_pae_auth_tasklet_handler(arm_event_s *event) +{ + if (event->event_type == PAE_TASKLET_INIT) { + + } else if (event->event_type == PAE_TASKLET_EVENT) { + pae_auth_t *pae_auth = NULL; + + ns_list_foreach(pae_auth_t, entry, &pae_auth_list) { + if (entry->interface_ptr->id == event->event_id) { + pae_auth = entry; + break; + } + } + + if (pae_auth) { + kmp_service_event_if_event(pae_auth->kmp_service, event->data_ptr); + } + } +} + +void ws_pae_auth_timer(uint16_t ticks) +{ + ns_list_foreach(pae_auth_t, pae_auth, &pae_auth_list) { + if (!ws_pae_auth_timer_running(pae_auth)) { + continue; + } + + // Updates KMP timers + bool running = ws_pae_lib_supp_list_timer_update(&pae_auth->active_supp_list, &pae_auth->inactive_supp_list, ticks, kmp_service_timer_if_timeout); + if (!running) { + ws_pae_auth_timer_stop(pae_auth); + } + } +} + +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); + if (!pae_auth) { + return -1; + } + + if (ws_pae_auth_timer_start(pae_auth) < 0) { + return -1; + } + + supp_entry_t *supp_entry = kmp_api_data_get(kmp); + if (!supp_entry) { + return -1; + } + + kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(&supp_entry->kmp_list, kmp); + if (!entry) { + return -1; + } + + ws_pae_lib_supp_list_to_active(&pae_auth->active_supp_list, &pae_auth->inactive_supp_list, supp_entry); + + ws_pae_lib_kmp_timer_start(&supp_entry->kmp_list, entry); + return 0; +} + +static int8_t ws_pae_auth_timer_if_stop(kmp_service_t *service, kmp_api_t *kmp) +{ + (void) service; + + supp_entry_t *supp_entry = kmp_api_data_get(kmp); + + kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(&supp_entry->kmp_list, kmp); + if (!entry) { + return -1; + } + + ws_pae_lib_kmp_timer_stop(&supp_entry->kmp_list, entry); + return 0; +} + +static int8_t ws_pae_auth_timer_start(pae_auth_t *pae_auth) +{ + pae_auth->timer_running = true; + return 0; +} + +static int8_t ws_pae_auth_timer_stop(pae_auth_t *pae_auth) +{ + pae_auth->timer_running = false; + return 0; +} + +static bool ws_pae_auth_timer_running(pae_auth_t *pae_auth) +{ + return pae_auth->timer_running; +} + +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; + +#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); + if (entry && entry->addr) { + kmp_address_copy(remote_addr, entry->addr); + } +} + +static kmp_api_t *ws_pae_auth_kmp_service_api_get(kmp_service_t *service, kmp_api_t *kmp, kmp_type_e type) +{ + (void) service; + + supp_entry_t *supp_entry = kmp_api_data_get(kmp); + if (!supp_entry) { + return NULL; + } + + return ws_pae_lib_kmp_list_type_get(&supp_entry->kmp_list, type); +} + +static kmp_api_t *ws_pae_auth_kmp_incoming_ind(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr) +{ + pae_auth_t *pae_auth = ws_pae_auth_by_kmp_service_get(service); + if (!pae_auth) { + return NULL; + } + + // Find supplicant from list of active supplicants + supp_entry_t *supp_entry = ws_pae_lib_supp_list_entry_eui_64_get(&pae_auth->active_supp_list, kmp_address_eui_64_get(addr)); + + if (!supp_entry) { + // Find supplicant from list of inactive supplicants + supp_entry = ws_pae_lib_supp_list_entry_eui_64_get(&pae_auth->inactive_supp_list, kmp_address_eui_64_get(addr)); + if (supp_entry) { + // Move supplicant to active list + ws_pae_lib_supp_list_to_active(&pae_auth->active_supp_list, &pae_auth->inactive_supp_list, supp_entry); + } + } + + // If does not exists add it to list + if (!supp_entry) { + supp_entry = ws_pae_lib_supp_list_add(&pae_auth->active_supp_list, addr); + if (!supp_entry) { + return 0; + } + sec_prot_keys_init(&supp_entry->sec_keys, pae_auth->gtks, pae_auth->certs); + } else { + // Updates relay address + kmp_address_copy(supp_entry->addr, addr); + } + + // Increases waiting time for supplicant authentication + ws_pae_lib_supp_timer_ticks_set(supp_entry, WAIT_FOR_AUTHENTICATION_TICKS); + + // Get KMP for supplicant + kmp_api_t *kmp = ws_pae_lib_kmp_list_type_get(&supp_entry->kmp_list, type); + if (kmp) { + return kmp; + } + + // Create a new KMP for initial eapol-key + kmp = kmp_api_create(service, type + IEEE_802_1X_INITIAL_KEY); + + if (!kmp) { + return 0; + } + + kmp_api_data_set(kmp, supp_entry); + // Sets address to KMP + kmp_api_addr_set(kmp, supp_entry->addr); + + // Sets security keys to KMP + kmp_api_sec_keys_set(kmp, &supp_entry->sec_keys); + + if (ws_pae_lib_kmp_list_add(&supp_entry->kmp_list, kmp) == NULL) { + kmp_api_delete(kmp); + return 0; + } + + kmp_api_cb_register(kmp, + ws_pae_auth_kmp_api_create_confirm, + ws_pae_auth_kmp_api_create_indication, + ws_pae_auth_kmp_api_finished_indication, + ws_pae_auth_kmp_api_finished); + + if (kmp_api_start(kmp) < 0) { + ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, kmp); + return 0; + } + + return kmp; +} + +static void ws_pae_auth_kmp_api_create_confirm(kmp_api_t *kmp, kmp_result_e result) +{ + (void) kmp; + (void) result; + // If KMP-CREATE.request has failed, authentication error, just stop for now +} + +static void ws_pae_auth_kmp_api_create_indication(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr) +{ + (void) type; + (void) addr; + // For now, accept every KMP-CREATE.indication + kmp_api_create_response(kmp, KMP_RESULT_OK); +} + +static void ws_pae_auth_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e result, kmp_sec_keys_t *sec_keys) +{ + (void) sec_keys; + + // For now, just ignore if not ok + if (result != KMP_RESULT_OK) { + return; + } + + supp_entry_t *supp_entry = kmp_api_data_get(kmp); + if (!supp_entry) { + // Should not be possible + return; + } + + // Gets type + kmp_type_e type = kmp_api_type_get(kmp); + + 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 + 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; + } + + // Create new instance + kmp_api_t *new_kmp = ws_pae_auth_kmp_create_and_start(pae_auth->kmp_service, 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 (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) { + 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); +} + +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 + kmp_api_t *kmp = kmp_api_create(service, type); + + if (!kmp) { + return NULL; + } + + if (ws_pae_lib_kmp_list_add(&supp_entry->kmp_list, kmp) == NULL) { + kmp_api_delete(kmp); + return NULL; + } + + kmp_api_cb_register(kmp, + ws_pae_auth_kmp_api_create_confirm, + ws_pae_auth_kmp_api_create_indication, + ws_pae_auth_kmp_api_finished_indication, + ws_pae_auth_kmp_api_finished); + + kmp_api_data_set(kmp, supp_entry); + + if (kmp_api_start(kmp) < 0) { + ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, kmp); + return NULL; + } + + return kmp; +} + +static void ws_pae_auth_kmp_api_finished(kmp_api_t *kmp) +{ + supp_entry_t *supp_entry = kmp_api_data_get(kmp); + if (!supp_entry) { + // Should not be possible + return; + } + + // Delete KMP + ws_pae_lib_kmp_list_delete(&supp_entry->kmp_list, kmp); +} + +#endif /* HAVE_PAE_AUTH */ +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_pae_auth.h b/source/6LoWPAN/ws/ws_pae_auth.h new file mode 100644 index 0000000000..b33589c661 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_auth.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018-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_AUTH_H_ +#define WS_PAE_AUTH_H_ + +#ifdef HAVE_PAE_AUTH + +/* + * Authenticator port access entity controls key security protocols using KMP API. + * + * Configures KMP service network access and provides timing and callback services + * for it. Registers needed security protocols to KMP service. + * + * PAE Maintains security keys that are internal to port access entity for + * each supplicant and maintains supplicant security registration state. + * + * Autenticator PAE controls network access keys and provides new network + * access keys to supplicants when they are changed. When supplicant + * network keys are updated, also other keys (master key, pairwise key) + * are updated as needed. + * + */ + +/** + * ws_pae_auth_init initializes PAE authenticator + * + * \param interface_ptr interface + * \param local_port local port + * \param remote_addr remote address + * \param remote_port remote port + * \param gtks group keys + * \param cert_chain certificate chain + * + * \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); + +/** + * ws_pae_auth_addresses_set set relay addresses + * + * \param interface_ptr interface + * \param local_port local port + * \param remote_addr remote address + * \param remote_port remote port + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_auth_addresses_set(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port); + +/** + * ws_pae_auth_delete deletes PAE authenticator + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_auth_delete(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_auth_timer PAE authenticator timer call + * + * \param ticks elapsed ticks + * + */ +void ws_pae_auth_timer(uint16_t ticks); + +#else + +#define ws_pae_auth_init(interface_ptr, gtks, certs) 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 + +#endif + +#endif /* WS_PAE_AUTH_H_ */ diff --git a/source/6LoWPAN/ws/ws_pae_controller.c b/source/6LoWPAN/ws/ws_pae_controller.c new file mode 100644 index 0000000000..edb051ebb4 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_controller.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "fhss_config.h" +#include "ns_address.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_config.h" +#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_supp.h" +#include "6LoWPAN/ws/ws_pae_auth.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wspc" + +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 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 */ +} 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 NS_LIST_DEFINE(pae_controller_list, pae_controller_t, link); + +static void ws_pae_controller_test_keys_set(sec_prot_gtk_keys_t *gtks) +{ + uint8_t gtk[2][GTK_LEN]; + + // Test data + for (int i = 0; i < GTK_LEN; i++) { + gtk[0][i] = 0xcf - i; + gtk[1][i] = 0xef - 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); +} + +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); + controller->auth_completed(interface_ptr, true); + } + + /////////// + // 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); + } + //////////////// + + return 0; +} + +int8_t ws_pae_controller_authenticator_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port) +{ + (void) local_port; + (void) remote_port; + + if (!interface_ptr || !remote_addr) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + 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); + + if (ws_pae_auth_addresses_set(interface_ptr, local_port, remote_addr, remote_port) < 0) { + return -1; + } + + 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) +{ + if (!interface_ptr) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + controller->auth_completed = completed; + controller->key_insert = key_insert; + + return 0; +} + +int8_t ws_pae_controller_set_target(protocol_interface_info_entry_t *interface_ptr, uint16_t target_pan_id, uint8_t *target_eui_64) +{ + if (!interface_ptr) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + controller->target_pan_id = target_pan_id; + memcpy(controller->target_eui_64, target_eui_64, 8); + + return 0; +} + +int8_t ws_pae_controller_nw_info_set(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name) +{ + (void) pan_id; + (void) network_name; + + if (!interface_ptr) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + return ws_pae_supp_nw_info_set(interface_ptr, pan_id, network_name); +} + +int8_t ws_pae_controller_nw_key_valid(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; + } + + return ws_pae_supp_nw_key_valid(interface_ptr); +} + +int8_t ws_pae_controller_init(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + if (ws_pae_controller_get(interface_ptr) != NULL) { + return 0; + } + + pae_controller_t *controller = ns_dyn_mem_alloc(sizeof(pae_controller_t)); + if (!controller) { + 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; + + sec_prot_keys_gtks_init(&controller->gtks); + sec_prot_certs_init(&controller->certs); + + ns_list_add_to_end(&pae_controller_list, controller); + + return 0; +} + +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) { + return -1; + } + + controller->pae_delete = ws_pae_supp_delete; + controller->pae_timer = ws_pae_supp_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; + + ws_pae_supp_cb_register(controller->interface_ptr, controller->auth_completed, controller->key_insert); + + 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) { + return -1; + } + + controller->pae_delete = ws_pae_auth_delete; + controller->pae_timer = ws_pae_auth_timer; + + 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; + } + + // If PAE has been initialized, deletes it + if (controller->pae_delete) { + controller->pae_delete(interface_ptr); + } + + return 0; +} + +int8_t ws_pae_controller_delete(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + ws_pae_controller_stop(interface_ptr); + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + ns_list_remove(&pae_controller_list, controller); + + sec_prot_certs_delete(&controller->certs); + + ns_dyn_mem_free(controller); + + return 0; +} + +int8_t ws_pae_controller_certificate_chain_set(const arm_certificate_chain_entry_s *new_chain) +{ + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + // Delete previous information + sec_prot_certs_delete(&entry->certs); + + 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); + } + } + + return 0; +} + +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) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + if (controller->pae_br_addr_write) { + return controller->pae_br_addr_write(interface_ptr, eui_64); + } else { + memcpy(controller->br_eui_64, eui_64, 8); + } + + 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) { + return -1; + } + + pae_controller_t *controller = ws_pae_controller_get(interface_ptr); + if (!controller) { + return -1; + } + + if (controller->pae_br_addr_read) { + return controller->pae_br_addr_read(interface_ptr, eui_64); + } else { + memcpy(eui_64, controller->br_eui_64, 8); + } + + return 0; +} + +void ws_pae_controller_timer(uint16_t ticks) +{ + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + if (entry->pae_timer) { + entry->pae_timer(ticks); + } + } +} + +static pae_controller_t *ws_pae_controller_get(protocol_interface_info_entry_t *interface_ptr) +{ + ns_list_foreach(pae_controller_t, entry, &pae_controller_list) { + if (entry->interface_ptr == interface_ptr) { + return entry; + } + } + + return NULL; +} + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_pae_controller.h b/source/6LoWPAN/ws/ws_pae_controller.h new file mode 100644 index 0000000000..db1115534f --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_controller.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2018-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_CONTROLLER_H_ +#define WS_PAE_CONTROLLER_H_ + +#ifdef HAVE_WS + +/** + * ws_pae_controller_set_target sets EAPOL target for PAE supplicant + * + * \param interface_ptr interface + * \param target_pan_id EAPOL target PAN ID + * \param target_eui_64 EAPOL target + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_set_target(protocol_interface_info_entry_t *interface_ptr, uint16_t target_pan_id, uint8_t *target_eui_64); + +/** + * ws_pae_controller_authenticate start PAE supplicant authentication + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_authenticate(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_authenticator_start start PAE authenticator + * + * \param interface_ptr interface + * \param local_port local port + * \param remote_addr remote address + * \param remote_port remote port + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_authenticator_start(protocol_interface_info_entry_t *interface_ptr, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port); + +/** + * ws_pae_controller_init initializes PAE controller + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_init(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_init initializes PAE supplicant + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_supp_init(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_init initializes PAE authenticator + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_auth_init(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_stop stop PAE controller (e.g. on interface down) + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_stop(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_delete delete PAE controller (e.g. failure to create interface) + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_delete(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_certificate_chain_set set certificate chain + * + * \param chain certificate chain + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_certificate_chain_set(const arm_certificate_chain_entry_s *chain); + +/** + * ws_pae_controller_nw_info_set set network information + * + * \param interface_ptr interface + * \param pan_id PAD ID + * \param network_name network name + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_nw_info_set(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name); + +/** + * ws_pae_controller_nw_key_valid network key is valid i.e. used successfully on bootstrap + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_nw_key_valid(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_controller_border_router_addr_write write border router address + * + * \param interface_ptr interface + * \param eui_64 pointer to EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_controller_border_router_addr_write(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); + +/** + * ws_pae_controller_border_router_addr_read read border router address + * + * \param interface_ptr interface + * \param eui_64 pointer to EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +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 + * + * \param interface_ptr interface + * \param gtk_index index of the new GTK key + * \param gtk new GTK key + * + */ +typedef void ws_pae_controller_key_insert(protocol_interface_info_entry_t *interface_ptr, uint8_t gtk_index, uint8_t *gtk); + +/** + * ws_pae_controller_auth_completed authentication completed callback + * + * \param interface_ptr interface + * \param success true if authentication was successful + * + */ +typedef void ws_pae_controller_auth_completed(protocol_interface_info_entry_t *interface_ptr, bool success); + +/** + * 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 + * + * \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); + +/** + * ws_pae_controller_timer PAE controller timer call + * + * \param ticks elapsed ticks + * + */ +void ws_pae_controller_timer(uint16_t ticks); + +#else + +#define ws_pae_controller_set_target(interface_ptr, target_pan_id, target_dest_eui_64) +#define ws_pae_controller_authenticate(interface_ptr) + +#define ws_pae_controller_authenticator_start(interface_ptr, local_port, remote_address, remote_port) + +#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_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) + +#endif + +#endif /* WS_PAE_CONTROLLER_H_ */ diff --git a/source/6LoWPAN/ws/ws_pae_lib.c b/source/6LoWPAN/ws/ws_pae_lib.c new file mode 100644 index 0000000000..3d613e10f9 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_lib.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "Security/kmp/kmp_addr.h" +#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_lib.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wspl" + +void ws_pae_lib_kmp_list_init(kmp_list_t *kmp_list) +{ + ns_list_init(kmp_list); +} + +kmp_entry_t *ws_pae_lib_kmp_list_add(kmp_list_t *kmp_list, kmp_api_t *kmp) +{ + // Already in list + kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp); + if (entry) { + return entry; + } + + entry = ns_dyn_mem_alloc(sizeof(kmp_entry_t)); + if (!entry) { + return NULL; + } + entry->kmp = kmp; + entry->timer_running = false; + + ns_list_add_to_end(kmp_list, entry); + + return entry; +} + +int8_t ws_pae_lib_kmp_list_delete(kmp_list_t *kmp_list, kmp_api_t *kmp) +{ + kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp); + + if (entry) { + ns_list_remove(kmp_list, entry); + kmp_api_delete(entry->kmp); + ns_dyn_mem_free(entry); + return 0; + } + + return -1; +} + +kmp_api_t *ws_pae_lib_kmp_list_type_get(kmp_list_t *kmp_list, kmp_type_e type) +{ + ns_list_foreach(kmp_entry_t, cur, kmp_list) { + if (kmp_api_type_get(cur->kmp) == type) { + return cur->kmp; + } + } + + return 0; +} + +void ws_pae_lib_kmp_list_free(kmp_list_t *kmp_list) +{ + ns_list_foreach_safe(kmp_entry_t, cur, kmp_list) { + kmp_api_delete(cur->kmp); + ns_dyn_mem_free(cur); + } +} + +kmp_entry_t *ws_pae_lib_kmp_list_entry_get(kmp_list_t *kmp_list, kmp_api_t *kmp) +{ + ns_list_foreach(kmp_entry_t, cur, kmp_list) { + if (cur->kmp == kmp) { + return cur; + } + } + + return 0; +} + +void ws_pae_lib_kmp_timer_start(kmp_list_t *kmp_list, kmp_entry_t *entry) +{ + if (ns_list_get_first(kmp_list) != entry) { + ns_list_remove(kmp_list, entry); + ns_list_add_to_start(kmp_list, entry); + } + entry->timer_running = true; +} + +void ws_pae_lib_kmp_timer_stop(kmp_list_t *kmp_list, kmp_entry_t *entry) +{ + if (ns_list_get_last(kmp_list) != entry) { + ns_list_remove(kmp_list, entry); + ns_list_add_to_end(kmp_list, entry); + } + entry->timer_running = false; +} + +bool ws_pae_lib_kmp_timer_update(kmp_list_t *kmp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout) +{ + if (ns_list_is_empty(kmp_list)) { + return false; + } + + bool timer_running = false; + + ns_list_foreach_safe(kmp_entry_t, entry, kmp_list) { + if (entry->timer_running) { + timeout(entry->kmp, ticks); + timer_running = true; + } else { + break; + } + } + + return timer_running; +} + +void ws_pae_lib_supp_list_init(supp_list_t *supp_list) +{ + ns_list_init(supp_list); +} + +supp_entry_t *ws_pae_lib_supp_list_add(supp_list_t *supp_list, const kmp_addr_t *addr) +{ + supp_entry_t *entry = ns_dyn_mem_alloc(sizeof(supp_entry_t)); + + if (!entry) { + return NULL; + } + + ws_pae_lib_supp_init(entry); + + entry->addr = kmp_address_create(KMP_ADDR_EUI_64_AND_IP, 0); + kmp_address_copy(entry->addr, addr); + + ns_list_add_to_end(supp_list, entry); + + return entry; +} + +int8_t ws_pae_lib_supp_list_remove(supp_list_t *supp_list, supp_entry_t *supp) +{ + ns_list_remove(supp_list, supp); + + ws_pae_lib_supp_delete(supp); + + ns_dyn_mem_free(supp); + return 0; +} + +supp_entry_t *ws_pae_lib_supp_list_entry_eui_64_get(const supp_list_t *supp_list, const uint8_t *eui_64) +{ + ns_list_foreach(supp_entry_t, cur, supp_list) { + if (memcmp(kmp_address_eui_64_get(cur->addr), eui_64, 8) == 0) { + return cur; + } + } + + return 0; +} + +void ws_pae_lib_supp_list_delete(supp_list_t *supp_list) +{ + ns_list_foreach_safe(supp_entry_t, entry, supp_list) { + ws_pae_lib_supp_list_remove(supp_list, entry); + } +} + +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) +{ + bool timer_running = false; + + ns_list_foreach_safe(supp_entry_t, entry, active_supp_list) { + bool running = ws_pae_lib_supp_timer_update(entry, ticks, timeout); + if (running) { + timer_running = true; + } else { + ws_pae_lib_supp_list_to_inactive(active_supp_list, inactive_supp_list, entry); + } + } + + return timer_running; +} + +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->active = true; +} + +void ws_pae_lib_supp_delete(supp_entry_t *entry) +{ + ws_pae_lib_kmp_list_free(&entry->kmp_list); + kmp_address_delete(entry->addr); +} + +bool ws_pae_lib_supp_timer_update(supp_entry_t *entry, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout) +{ + // Updates KMP timers and calls timeout callback + bool keep_timer_running = ws_pae_lib_kmp_timer_update(&entry->kmp_list, ticks, timeout); + + // If KMPs are not active updates supplicant timer + if (!keep_timer_running) { + if (entry->ticks > ticks) { + keep_timer_running = true; + entry->ticks -= ticks; + } else { + entry->ticks = 0; + } + } + + return keep_timer_running; +} + +void ws_pae_lib_supp_timer_ticks_set(supp_entry_t *entry, uint32_t ticks) +{ + entry->ticks = ticks; +} + +void ws_pae_lib_supp_list_to_active(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry) +{ + if (entry->active) { + return; + } + + tr_debug("PAE: to active, eui-64: %s", trace_array(kmp_address_eui_64_get(entry->addr), 8)); + + ns_list_remove(inactive_supp_list, entry); + ns_list_add_to_start(active_supp_list, entry); + + entry->active = true; + entry->ticks = 0; + + // Adds relay address data + kmp_addr_t *addr = kmp_address_create(KMP_ADDR_EUI_64_AND_IP, kmp_address_eui_64_get(entry->addr)); + kmp_address_delete(entry->addr); + entry->addr = addr; +} + +void ws_pae_lib_supp_list_to_inactive(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry) +{ + if (!entry->active) { + return; + } + + tr_debug("PAE: to inactive, eui-64: %s", trace_array(kmp_address_eui_64_get(entry->addr), 8)); + + ns_list_remove(active_supp_list, entry); + ns_list_add_to_start(inactive_supp_list, entry); + + entry->active = false; + entry->ticks = 0; + + // Removes relay address data + kmp_addr_t *addr = kmp_address_create(KMP_ADDR_EUI_64, kmp_address_eui_64_get(entry->addr)); + kmp_address_delete(entry->addr); + entry->addr = addr; +} + +#endif /* HAVE_WS */ diff --git a/source/6LoWPAN/ws/ws_pae_lib.h b/source/6LoWPAN/ws/ws_pae_lib.h new file mode 100644 index 0000000000..6ccc888447 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_lib.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2018-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_LIB_H_ +#define WS_PAE_LIB_H_ + +typedef struct { + kmp_api_t *kmp; /**< KMP API */ + bool timer_running; /**< Timer running inside KMP */ + ns_list_link_t link; /**< Link */ +} kmp_entry_t; + +typedef NS_LIST_HEAD(kmp_entry_t, link) kmp_list_t; + +typedef struct { + kmp_list_t kmp_list; /**< Ongoing KMP negotiations */ + 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 */ + ns_list_link_t link; /**< Link */ +} supp_entry_t; + +typedef NS_LIST_HEAD(supp_entry_t, link) supp_list_t; + +/** + * ws_pae_lib_kmp_list_init initializes KMP list + * + * \param kmp_list KMP list + * + * \return < 0 failure + * \return >= 0 success + * + */ +void ws_pae_lib_kmp_list_init(kmp_list_t *kmp_list); + +/** + * ws_pae_lib_kmp_list_add adds KMP to KMP list + * + * \param kmp_list KMP list + * \param kmp KMP + * + * \return KMP list entry on success + * \return NULL on failure + * + */ +kmp_entry_t *ws_pae_lib_kmp_list_add(kmp_list_t *kmp_list, kmp_api_t *kmp); + +/** + * ws_pae_lib_kmp_list_delete deletes KMP from KMP list + * + * \param kmp_list KMP list + * \param kmp KMP + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_lib_kmp_list_delete(kmp_list_t *kmp_list, kmp_api_t *kmp); + +/** + * ws_pae_lib_kmp_list_free frees KMP list + * + * \param kmp_list KMP list + * + */ +void ws_pae_lib_kmp_list_free(kmp_list_t *kmp_list); + +/** + * ws_pae_lib_kmp_list_type_get gets KMP entry from KMP list based on KMP type + * + * \param kmp_list KMP list + * \param type type + * + * \return KMP on success + * \return NULL on failure + * + */ +kmp_api_t *ws_pae_lib_kmp_list_type_get(kmp_list_t *kmp_list, kmp_type_e type); + +/** + * ws_pae_lib_kmp_list_entry_get gets KMP entry from KMP list based on KMP + * + * \param kmp_list KMP list + * \param kmp KMP + * + * \return KMP list entry on success + * \return NULL on failure + * + */ +kmp_entry_t *ws_pae_lib_kmp_list_entry_get(kmp_list_t *kmp_list, kmp_api_t *kmp); + +/** + * ws_pae_lib_kmp_timer_start starts KMP timer + * + * \param kmp_list KMP list + * \param entry KMP list entry + * + */ +void ws_pae_lib_kmp_timer_start(kmp_list_t *kmp_list, kmp_entry_t *entry); + +/** + * ws_pae_lib_kmp_timer_stop stops KMP timer + * + * \param kmp_list KMP list + * \param entry KMP list entry + * + */ +void ws_pae_lib_kmp_timer_stop(kmp_list_t *kmp_list, kmp_entry_t *entry); + +/** + * ws_pae_lib_kmp_timer_timeout KMP timer timeout callback + * + * \param kmp KMP + * \param ticks timer ticks + * + */ +typedef void ws_pae_lib_kmp_timer_timeout(kmp_api_t *kmp, uint16_t ticks); + +/** + * ws_pae_lib_kmp_timer_update updates KMP timers on KMP list + * + * \param kmp_list KMP list + * \param ticks timer ticks + * \param timeout callback to call on timeout + * + * \return true KMP list has KMPs + * \return false no KMPs on KMP list + * + */ +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 + * + * \param supp_list supplicant list + * + */ +void ws_pae_lib_supp_list_init(supp_list_t *supp_list); + +/** + * ws_pae_lib_supp_list_add adds entry to supplicant list + * + * \param supp_list supplicant list + * \param addr address + * + * \return supplicant list entry on success + * \return NULL on failure + */ +supp_entry_t *ws_pae_lib_supp_list_add(supp_list_t *supp_list, const kmp_addr_t *addr); + +/** + * ws_pae_lib_supp_list_add removes entry from supplicant list + * + * \param supp_list supplicant list + * \param entry entry + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t ws_pae_lib_supp_list_remove(supp_list_t *supp_list, supp_entry_t *entry); + +/** + * ws_pae_lib_supp_list_entry_eui_64_get gets entry from supplicant list based on EUI-64 + * + * \param supp_list supplicant list + * \param eui_64 EUI-64 + * + * \return supplicant list entry on success + * \return NULL on failure + */ +supp_entry_t *ws_pae_lib_supp_list_entry_eui_64_get(const supp_list_t *supp_list, const uint8_t *eui_64); + +/** + * ws_pae_lib_supp_list_delete deletes supplicant list + * + * \param supp_list supplicant list + * + */ +void ws_pae_lib_supp_list_delete(supp_list_t *supp_list); + +/** + * ws_pae_lib_supp_list_timer_update updates timers on supplicant list + * + * \param active_supp_list list of active supplicants + * \param inactive_supp_list list of inactive supplicants + * \param ticks timer ticks + * \param timeout callback to call on timeout + * + * \return true timer needs still to be running + * \return false timer can be stopped + */ +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_timer_update updates supplicant timers + * + * \param entry supplicant entry + * \param ticks timer ticks + * \param timeout callback to call on timeout + * + * \return true timer needs still to be running + * \return false timer can be stopped + */ +bool ws_pae_lib_supp_timer_update(supp_entry_t *entry, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout); + +/** + * ws_pae_lib_supp_init initiates supplicant entry + * + * \param entry supplicant entry + * + */ +void ws_pae_lib_supp_init(supp_entry_t *entry); + +/** + * ws_pae_lib_supp_delete deletes supplicant entry + * + * \param entry supplicant entry + * + */ +void ws_pae_lib_supp_delete(supp_entry_t *entry); + +/** + * ws_pae_lib_supp_timer_ticks_set sets supplicant timer ticks + * + * \param entry supplicant entry + * \param ticks ticks + * + */ +void ws_pae_lib_supp_timer_ticks_set(supp_entry_t *entry, uint32_t ticks); + +/** + * ws_pae_lib_supp_list_to_active move supplicant to active supplicants list + * + * \param active_supp_list list of active supplicants + * \param inactive_supp_list list of inactive supplicants + * \param entry supplicant entry + * + */ +void ws_pae_lib_supp_list_to_active(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry); + +/** + * ws_pae_lib_supp_list_to_inactive move supplicant to inactive supplicants list + * + * \param active_supp_list list of active supplicants + * \param inactive_supp_list list of inactive supplicants + * \param entry supplicant entry + * + */ +void ws_pae_lib_supp_list_to_inactive(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry); + +#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 new file mode 100644 index 0000000000..b528ef267a --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_nvm_data.c @@ -0,0 +1,214 @@ +/* + * 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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "6LoWPAN/ws/ws_config.h" +#include "ns_file_system.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" +#include "6LoWPAN/ws/ws_pae_nvm_store.h" +#include "6LoWPAN/ws/ws_pae_nvm_data.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wsnv" + +#define PAE_NVM_NW_INFO_TAG 1 +#define PAE_NVM_KEYS_TAG 2 + +// pan_id (2) + network name (33) + (GTK set (1) + GTK lifetime (4) + GTK (16)) * 4 +#define PAE_NVM_NW_INFO_LEN 2 + 33 + (1 + 4 + GTK_LEN) * GTK_NUM + +// PTK EUI-64 set (1) + PTK EUI-64 (8) + PMK set (1) + PMK (32) + PMK replay counter (8) + PTK set (1) + PTK (48) +#define PAE_NVM_KEYS_LEN 1 + 8 + 1 + PMK_LEN + 8 + 1 + PTK_LEN + +nvm_tlv_entry_t *ws_pae_nvm_store_nw_info_tlv_create(uint16_t pan_id, char *nw_name, sec_prot_gtk_keys_t *gtks) +{ + nvm_tlv_entry_t *tlv_entry = ns_dyn_mem_temporary_alloc(sizeof(nvm_tlv_entry_t) + PAE_NVM_NW_INFO_LEN); + if (!tlv_entry) { + return NULL; + } + + tlv_entry->tag = PAE_NVM_NW_INFO_TAG; + tlv_entry->len = PAE_NVM_NW_INFO_LEN; + + uint8_t *tlv = ((uint8_t *) &tlv_entry->tag) + NVM_TLV_FIXED_LEN; + + tlv = common_write_16_bit(pan_id, tlv); + + memset(tlv, 0, 33); + strncpy((char *)tlv, nw_name, 32); + tlv += 33; + + for (uint8_t i = 0; i < GTK_NUM; i++) { + if (sec_prot_keys_gtk_is_set(gtks, i)) { + *tlv++ = 1; // GTK is set + uint32_t lifetime = sec_prot_keys_gtk_lifetime_get(gtks, i); + tlv = common_write_32_bit(lifetime, tlv); + + uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i); + memcpy(tlv, gtk, GTK_LEN); + tlv += GTK_LEN; + } else { + *tlv++ = 0; // GTK is not set + memset(tlv, 0, 4 + GTK_LEN); + tlv += 4 + GTK_LEN; + } + } + + tr_debug("NVM NW_INFO write PAN ID %i name: %s", pan_id, nw_name); + + return tlv_entry; +} + +int8_t ws_pae_nvm_store_nw_info_tlv_read(nvm_tlv_entry_t *tlv_entry, uint16_t *pan_id, char *nw_name, sec_prot_gtk_keys_t *gtks) +{ + if (!tlv_entry || !pan_id || !nw_name || !gtks) { + return -1; + } + + if (tlv_entry->tag != PAE_NVM_NW_INFO_TAG || tlv_entry->len != PAE_NVM_NW_INFO_LEN) { + return -1; + } + + uint8_t *tlv = ((uint8_t *) &tlv_entry->tag) + NVM_TLV_FIXED_LEN; + + *pan_id = common_read_16_bit(tlv); + tlv += 2; + + memset(nw_name, 0, 33); + strncpy(nw_name, (char *) tlv, 32); + tlv += 33; + + for (uint8_t i = 0; i < GTK_NUM; i++) { + 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); + tlv += GTK_LEN; + } else { + tlv += 4 + GTK_LEN; + } + } + sec_prot_keys_gtks_updated_reset(gtks); + + tr_debug("NVM NW_INFO read PAN ID %i name: %s", *pan_id, nw_name); + + return 0; +} + +nvm_tlv_entry_t *ws_pae_nvm_store_keys_tlv_create(sec_prot_keys_t *sec_keys) +{ + nvm_tlv_entry_t *tlv_entry = ns_dyn_mem_temporary_alloc(sizeof(nvm_tlv_entry_t) + PAE_NVM_KEYS_LEN); + if (!tlv_entry) { + return NULL; + } + + tlv_entry->tag = PAE_NVM_KEYS_TAG; + tlv_entry->len = PAE_NVM_KEYS_LEN; + + uint8_t *tlv = ((uint8_t *) &tlv_entry->tag) + NVM_TLV_FIXED_LEN; + + uint8_t *eui_64 = sec_prot_keys_ptk_eui_64_get(sec_keys); + if (eui_64) { + *tlv++ = 1; + memcpy(tlv, eui_64, 8); + } else { + *tlv++ = 0; + memset(tlv, 0, 8); + } + tlv += 8; + + uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); + if (pmk) { + *tlv++ = 1; + memcpy(tlv, pmk, PMK_LEN); + } else { + *tlv++ = 0; + memset(tlv, 0, PMK_LEN); + } + tlv += PMK_LEN; + + uint64_t counter = sec_prot_keys_pmk_replay_cnt_get(sec_keys); + tlv = common_write_64_bit(counter, tlv); + + uint8_t *ptk = sec_prot_keys_ptk_get(sec_keys); + if (ptk) { + *tlv++ = 1; + memcpy(tlv, ptk, PTK_LEN); + } else { + *tlv++ = 0; + memset(tlv, 0, PTK_LEN); + } + tlv += PTK_LEN; + + tr_debug("NVM KEYS write"); + + return tlv_entry; +} + +int8_t ws_pae_nvm_store_keys_tlv_read(nvm_tlv_entry_t *tlv_entry, sec_prot_keys_t *sec_keys) +{ + if (!tlv_entry || !sec_keys) { + return -1; + } + + if (tlv_entry->tag != PAE_NVM_KEYS_TAG || tlv_entry->len != PAE_NVM_KEYS_LEN) { + return -1; + } + + uint8_t *tlv = ((uint8_t *) &tlv_entry->tag) + NVM_TLV_FIXED_LEN; + + // EUI-64 set */ + if (*tlv++ == 1) { + sec_prot_keys_ptk_eui_64_write(sec_keys, tlv); + } + tlv += 8; + + // PMK set + if (*tlv++ == 1) { + sec_prot_keys_pmk_write(sec_keys, tlv); + } + tlv += PMK_LEN; + + uint64_t counter = common_read_64_bit(tlv); + tlv += 8; + sec_prot_keys_pmk_replay_cnt_set(sec_keys, counter); + + // PTK set + if (*tlv++ == 1) { + sec_prot_keys_ptk_write(sec_keys, tlv); + } + + tlv += PTK_LEN; + + sec_prot_keys_updated_reset(sec_keys); + + tr_debug("NVM KEYS read"); + + return 0; +} + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_pae_nvm_data.h b/source/6LoWPAN/ws/ws_pae_nvm_data.h new file mode 100644 index 0000000000..24d4af8a48 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_nvm_data.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-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_NVM_DATA_H_ +#define WS_PAE_NVM_DATA_H_ + +/** + * ws_pae_nvm_store_nw_info_tlv_create create NVM network info TLV + * + * \param pan_id PAN ID + * \param nw_name network name + * \param gtks GTK keys + * + * \return TLV entry or NULL + * + */ +nvm_tlv_entry_t *ws_pae_nvm_store_nw_info_tlv_create(uint16_t pan_id, char *nw_name, sec_prot_gtk_keys_t *gtks); + +/** + * ws_pae_nvm_store_nw_info_tlv_read read from NVM network info TLV + * + * \param tlv_entry TLV entry + * \param pan_id PAN ID + * \param nw_name network name + * \param gtks GTK keys + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_nvm_store_nw_info_tlv_read(nvm_tlv_entry_t *tlv_entry, uint16_t *pan_id, char *nw_name, sec_prot_gtk_keys_t *gtks); + +/** + * ws_pae_nvm_store_keys_tlv_create create NVM keys TLV + * + * \param sec_keys security keys + * + * \return TLV entry or NULL + * + */ +nvm_tlv_entry_t *ws_pae_nvm_store_keys_tlv_create(sec_prot_keys_t *sec_keys); + +/** + * ws_pae_nvm_store_nw_info_tlv_read read from NVM keys TLV + * + * \param tlv_entry TLV entry + * \param sec_keys security keys + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_nvm_store_keys_tlv_read(nvm_tlv_entry_t *tlv_entry, sec_prot_keys_t *sec_keys); + +#endif /* WS_PAE_NVM_DATA_H_ */ diff --git a/source/6LoWPAN/ws/ws_pae_nvm_store.c b/source/6LoWPAN/ws/ws_pae_nvm_store.c new file mode 100644 index 0000000000..568c360206 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_nvm_store.c @@ -0,0 +1,204 @@ +/* + * 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. + */ + +#include "nsconfig.h" +#include +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "6LoWPAN/ws/ws_config.h" +#include "ns_file_system.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" +#include "6LoWPAN/ws/ws_pae_nvm_store.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "wsnv" + +#define MAX_ROOT_PATH_LEN 200 + +static uint16_t ws_pae_nvm_store_path_len_get(const char *file_name); +static const char *ws_pae_nvm_store_get_root_path(void); +static int8_t ws_pae_nvm_store_root_path_valid(void); +static int8_t ws_pae_nvm_store_create_path(char *fast_data_path, const char *file_name); +static int8_t ws_pae_nvm_store_write(const char *file_name, nvm_tlv_list_t *tlv_list); +static int8_t ws_pae_nvm_store_read(const char *file_name, nvm_tlv_list_t *tlv_list); + +int8_t ws_pae_nvm_store_tlv_file_write(const char *file, nvm_tlv_list_t *tlv_list) +{ + if (!ws_pae_nvm_store_root_path_valid()) { + return PAE_NVM_FILE_ROOT_PATH_INVALID; + } + + uint16_t path_len = ws_pae_nvm_store_path_len_get(file); + + char nw_info_path[path_len]; + + ws_pae_nvm_store_create_path(nw_info_path, file); + + return ws_pae_nvm_store_write(nw_info_path, tlv_list); +} + +int8_t ws_pae_nvm_store_tlv_file_read(const char *file, nvm_tlv_list_t *tlv_list) +{ + if (!ws_pae_nvm_store_root_path_valid()) { + return PAE_NVM_FILE_ROOT_PATH_INVALID; + } + + uint16_t path_len = ws_pae_nvm_store_path_len_get(file); + + char nw_info_path[path_len]; + + ws_pae_nvm_store_create_path(nw_info_path, file); + + return ws_pae_nvm_store_read(nw_info_path, tlv_list); +} + +static const char *ws_pae_nvm_store_get_root_path(void) +{ + char *path = ns_file_system_get_root_path(); + + if (NULL == path) { + return ""; + } + return path; +} + +static int8_t ws_pae_nvm_store_root_path_valid(void) +{ + if (NULL == ns_file_system_get_root_path()) { + return 0; + } + int path_len = strlen(ws_pae_nvm_store_get_root_path()); + if (path_len == 0 || path_len > MAX_ROOT_PATH_LEN) { + return 0; + } + return 1; +} + +static uint16_t ws_pae_nvm_store_path_len_get(const char *file_name) +{ + return strlen(file_name) + strlen(ws_pae_nvm_store_get_root_path()) + 1; +} + +static int8_t ws_pae_nvm_store_create_path(char *data_path, const char *file_name) +{ + strcpy(data_path, ws_pae_nvm_store_get_root_path()); + strcat(data_path, file_name); + return 0; +} + +static int8_t ws_pae_nvm_store_write(const char *file_name, nvm_tlv_list_t *tlv_list) +{ + FILE *fp = fopen(file_name, "w"); + if (fp == NULL) { + tr_error("NVM open error: %s", file_name); + return PAE_NVM_FILE_CANNOT_OPEN; + } + + uint16_t list_count = ns_list_count(tlv_list); + size_t n_bytes = fwrite(&list_count, 1, sizeof(uint16_t), fp); + if (n_bytes != sizeof(uint16_t)) { + tr_warning("NVM TLV list count write error"); + fclose(fp); + return PAE_NVM_FILE_WRITE_ERROR; + } + + bool failure = false; + + ns_list_foreach(nvm_tlv_entry_t, entry, tlv_list) { + n_bytes = fwrite(&entry->tag, 1, entry->len + NVM_TLV_FIXED_LEN, fp); + if (n_bytes != (size_t) entry->len + NVM_TLV_FIXED_LEN) { + failure = true; + break; + } + } + + fclose(fp); + if (failure) { + tr_error("NVM write error %s", file_name); + return PAE_NVM_FILE_WRITE_ERROR; + } else { + return PAE_NVM_FILE_SUCCESS; + } +} + +static int8_t ws_pae_nvm_store_read(const char *file_name, nvm_tlv_list_t *tlv_list) +{ + FILE *fp = fopen(file_name, "r"); + if (fp == NULL) { + tr_warning("File not found: %s", file_name); + return PAE_NVM_FILE_CANNOT_OPEN; + } + + uint16_t list_count; + size_t n_bytes = fread(&list_count, 1, sizeof(uint16_t), fp); + if (n_bytes != sizeof(uint16_t)) { + tr_warning("NVM TLV list count read error %s", file_name); + fclose(fp); + return PAE_NVM_FILE_READ_ERROR; + } + + bool failure = false; + + while (list_count-- > 0) { + nvm_tlv_entry_t entry_header; + n_bytes = fread(&entry_header.tag, 1, NVM_TLV_FIXED_LEN, fp); + if (n_bytes != NVM_TLV_FIXED_LEN) { + failure = true; + break; + } + uint16_t len = entry_header.len; + + nvm_tlv_entry_t *entry = ns_dyn_mem_temporary_alloc(sizeof(nvm_tlv_entry_t) + len); + if (!entry) { + failure = true; + break; + } + + memcpy(&entry->tag, &entry_header.tag, NVM_TLV_FIXED_LEN); + + if (len > 0) { + uint8_t *data_ptr = ((uint8_t *)&entry->tag) + NVM_TLV_FIXED_LEN; + n_bytes = fread(data_ptr, 1, len, fp); + if (n_bytes != len) { + ns_dyn_mem_free(entry); + failure = true; + break; + } + } + + ns_list_add_to_end(tlv_list, entry); + } + + fclose(fp); + + if (failure) { + tr_error("NVM read error %s", file_name); + return PAE_NVM_FILE_READ_ERROR; + } else { + return PAE_NVM_FILE_SUCCESS; // return how many bytes was written. + } +} + + +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_pae_nvm_store.h b/source/6LoWPAN/ws/ws_pae_nvm_store.h new file mode 100644 index 0000000000..4fd0bf60b1 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_nvm_store.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-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_NVM_STORE_H_ +#define WS_PAE_NVM_STORE_H_ + +// tag + length +#define NVM_TLV_FIXED_LEN 4 + +typedef struct { + ns_list_link_t link; /**< Link */ + uint16_t tag; /**< Unique tag */ + uint16_t len; /**< Number of the bytes after the length field */ +} nvm_tlv_entry_t; + +typedef NS_LIST_HEAD(nvm_tlv_entry_t, link) nvm_tlv_list_t; + +#define PAE_NVM_FILE_SUCCESS 0 +#define PAE_NVM_FILE_READ_ERROR -1 +#define PAE_NVM_FILE_WRITE_ERROR -2 +#define PAE_NVM_FILE_VERSION_WRONG -3 +#define PAE_NVM_FILE_CANNOT_OPEN -4 +#define PAE_NVM_FILE_ROOT_PATH_INVALID -5 +#define PAE_NVM_FILE_PARAMETER_INVALID -6 +#define PAE_NVM_FILE_REMOVE_ERROR -7 + +/** + * ws_pae_nvm_store_tlv_file_write write a list of TLVs to file + * + * \param file file name + * \param tlv_list TLV list + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_nvm_store_tlv_file_write(const char *file, nvm_tlv_list_t *tlv_list); + +/** + * ws_pae_nvm_store_tlv_file_read read a list of TLVs from file + * + * \param file file name + * \param tlv_list TLV list + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_nvm_store_tlv_file_read(const char *file, nvm_tlv_list_t *tlv_list); + +#endif /* WS_PAE_NVM_STORE_H_ */ diff --git a/source/6LoWPAN/ws/ws_pae_supp.c b/source/6LoWPAN/ws/ws_pae_supp.c new file mode 100644 index 0000000000..73ba16943b --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_supp.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "fhss_config.h" +#include "eventOS_event.h" +#include "eventOS_scheduler.h" +#include "eventOS_event_timer.h" +#include "ns_address.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "RPL/rpl_protocol.h" +#include "RPL/rpl_control.h" +#include "RPL/rpl_data.h" +#include "6LoWPAN/ws/ws_config.h" +#include "Security/kmp/kmp_addr.h" +#include "Security/kmp/kmp_api.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" +#include "Security/protocols/key_sec_prot/key_sec_prot.h" +#include "Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.h" +#include "Security/protocols/tls_sec_prot/tls_sec_prot.h" +#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_supp.h" +#include "6LoWPAN/ws/ws_pae_lib.h" +#include "6LoWPAN/ws/ws_pae_nvm_store.h" +#include "6LoWPAN/ws/ws_pae_nvm_data.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" +#include "Security/kmp/kmp_eapol_pdu_if.h" + +#ifdef HAVE_WS +#ifdef HAVE_PAE_SUPP + +#define TRACE_GROUP "wsps" + +#define PAE_TASKLET_INIT 1 +#define PAE_TASKLET_EVENT 2 +#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 + +// How many times in maximum stored keys are used for authentication +#define STORED_KEYS_MAXIMUM_USE_COUNT 2 + +const char *NW_INFO_FILE = "pae_nw_info"; +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 */ + bool updated : 1; /**< Network info has been updated */ +} sec_prot_keys_nw_info_t; + +typedef struct { + ns_list_link_t link; /**< Link */ + 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 */ + 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 */ + 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 */ + 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_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 */ +} 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 +}; + +static void ws_pae_supp_free(pae_supp_t *pae_supp); +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); +static void ws_pae_supp_nvm_update(pae_supp_t *pae_supp); +static int8_t ws_pae_supp_nw_keys_valid_check(pae_supp_t *pae_supp, uint16_t pan_id); +static int8_t ws_pae_supp_nvm_nw_info_write(pae_supp_t *pae_supp); +static int8_t ws_pae_supp_nvm_keys_write(pae_supp_t *pae_supp); +static pae_supp_t *ws_pae_supp_get(protocol_interface_info_entry_t *interface_ptr); +static int8_t ws_pae_supp_event_send(kmp_service_t *service, void *data); +static void ws_pae_supp_tasklet_handler(arm_event_s *event); +static int8_t ws_pae_supp_timer_if_start(kmp_service_t *service, kmp_api_t *kmp); +static int8_t ws_pae_supp_timer_if_stop(kmp_service_t *service, kmp_api_t *kmp); +static int8_t ws_pae_supp_timer_start(pae_supp_t *pae_supp); +static int8_t ws_pae_supp_timer_stop(pae_supp_t *pae_supp); +static bool ws_pae_supp_timer_running(pae_supp_t *pae_supp); +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); +static kmp_api_t *ws_pae_supp_kmp_service_api_get(kmp_service_t *service, kmp_api_t *kmp, kmp_type_e type); +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 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); +static void ws_pae_supp_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e result, kmp_sec_keys_t *sec_keys); +static void ws_pae_supp_kmp_api_finished(kmp_api_t *kmp); + +static const eapol_pdu_recv_cb_data_t eapol_pdu_recv_cb_data = { + .priority = EAPOL_PDU_RECV_HIGH_PRIORITY, + .addr_check = ws_pae_supp_eapol_pdu_address_check, + .receive = kmp_eapol_pdu_if_receive +}; + +static int8_t tasklet_id = -1; +static NS_LIST_DEFINE(pae_supp_list, pae_supp_t, link); + +int8_t ws_pae_supp_authenticate(protocol_interface_info_entry_t *interface_ptr, uint16_t dest_pan_id, uint8_t *dest_eui_64) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + if (ws_pae_supp_nw_keys_valid_check(pae_supp, dest_pan_id) >= 0) { + pae_supp->auth_completed(interface_ptr, true); + return 0; + } + + // Delete GTKs + sec_prot_keys_gtks_init(pae_supp->sec_keys_nw_info.gtks); + + // Prepare to receive new border router address + pae_supp->new_br_eui_64_set = 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); + } + + // Starts trickle + trickle_start(&pae_supp->auth_trickle_timer, &auth_trickle_params); + pae_supp->auth_trickle_running = true; + + // Starts supplicant timer + ws_pae_supp_timer_start(pae_supp); + + pae_supp->auth_requested = true; + + tr_debug("PAE active"); + + return 1; +} + +int8_t ws_pae_supp_nw_info_set(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + // 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; + pae_supp->sec_keys_nw_info.updated = true; + } + + // Network name has been modified + if (network_name && strncmp(pae_supp->sec_keys_nw_info.network_name, network_name, 33) != 0) { + strncpy(pae_supp->sec_keys_nw_info.network_name, network_name, 32); + pae_supp->sec_keys_nw_info.updated = true; + } + + + return 0; +} + +int8_t ws_pae_supp_border_router_addr_write(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + memcpy(pae_supp->new_br_eui_64, eui_64, 8); + pae_supp->new_br_eui_64_set = true; + + return 0; +} + +int8_t ws_pae_supp_border_router_addr_read(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + if (!pae_supp->entry.sec_keys.ptk_eui_64 || !pae_supp->entry.sec_keys.ptk_eui_64_set) { + return -1; + } + + memcpy(eui_64, pae_supp->entry.sec_keys.ptk_eui_64, 8); + + return 0; +} + +int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + // Stored keys are valid + pae_supp->nw_keys_used_cnt = 0; + + // Update NVM if data has been changed + ws_pae_supp_nvm_update(pae_supp); + + return 0; +} + +static void ws_pae_supp_nvm_update(pae_supp_t *pae_supp) +{ + // Check if NW info or GTKs have been changed + if (pae_supp->sec_keys_nw_info.updated || sec_prot_keys_gtks_are_updated(pae_supp->sec_keys_nw_info.gtks)) { + ws_pae_supp_nvm_nw_info_write(pae_supp); + pae_supp->sec_keys_nw_info.updated = false; + sec_prot_keys_gtks_updated_reset(pae_supp->sec_keys_nw_info.gtks); + } + + // Check if pairwise security keys have been changed + if (sec_prot_keys_are_updated(&pae_supp->entry.sec_keys)) { + ws_pae_supp_nvm_keys_write(pae_supp); + sec_prot_keys_updated_reset(&pae_supp->entry.sec_keys); + } +} + +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, + pae_supp->sec_keys_nw_info.network_name, + &pae_supp->gtks); + ns_list_add_to_end(&tlv_list, tlv_entry); + + ws_pae_nvm_store_tlv_file_write(NW_INFO_FILE, &tlv_list); + ns_list_remove(&tlv_list, tlv_entry); + ns_dyn_mem_free(tlv_entry); + + return 0; +} + +static int8_t ws_pae_supp_nvm_nw_info_read(pae_supp_t *pae_supp) +{ + nvm_tlv_list_t tlv_list; + ns_list_init(&tlv_list); + + 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, + pae_supp->sec_keys_nw_info.network_name, + &pae_supp->gtks); + ns_list_remove(&tlv_list, entry); + ns_dyn_mem_free(entry); + } + + return 0; +} + +static int8_t ws_pae_supp_nvm_keys_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_keys_tlv_create(&pae_supp->entry.sec_keys); + ns_list_add_to_end(&tlv_list, tlv_entry); + + ws_pae_nvm_store_tlv_file_write(KEYS_FILE, &tlv_list); + ns_list_remove(&tlv_list, tlv_entry); + ns_dyn_mem_free(tlv_entry); + + return 0; +} + +static int8_t ws_pae_supp_nvm_keys_read(pae_supp_t *pae_supp) +{ + nvm_tlv_list_t tlv_list; + ns_list_init(&tlv_list); + + ws_pae_nvm_store_tlv_file_read(KEYS_FILE, &tlv_list); + + ns_list_foreach_safe(nvm_tlv_entry_t, entry, &tlv_list) { + ws_pae_nvm_store_keys_tlv_read(entry, &pae_supp->entry.sec_keys); + ns_list_remove(&tlv_list, entry); + ns_dyn_mem_free(entry); + } + + return 0; +} + +static void ws_pae_supp_authenticate_response(pae_supp_t *pae_supp, bool success) +{ + pae_supp->auth_trickle_running = false; + if (pae_supp->auth_requested && pae_supp->auth_completed) { + pae_supp->auth_requested = false; + pae_supp->auth_completed(pae_supp->interface_ptr, success); + } +} + +static int8_t ws_pae_supp_initial_key_send(pae_supp_t *pae_supp) +{ + 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; + } + + kmp_api_create_request(kmp, IEEE_802_1X_MKA_KEY, pae_supp->entry.addr, &pae_supp->entry.sec_keys); + + return 0; +} + +static int8_t ws_pae_supp_nw_keys_valid_check(pae_supp_t *pae_supp, uint16_t pan_id) +{ + // Checks how many times authentication has been tried with current network keys + if (pae_supp->nw_keys_used_cnt >= STORED_KEYS_MAXIMUM_USE_COUNT) { + tr_debug("Keys not valid, delete GTKs"); + + // Delete GTKs + sec_prot_keys_gtks_init(pae_supp->sec_keys_nw_info.gtks); + sec_prot_keys_gtks_updated_set(pae_supp->sec_keys_nw_info.gtks); + ws_pae_supp_nvm_update(pae_supp); + + pae_supp->nw_keys_used_cnt = 0; + 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 { + tr_debug("Existing keys used, counter %i", pae_supp->nw_keys_used_cnt); + pae_supp->nw_keys_used_cnt++; + return 0; + } +} + +static void ws_pae_supp_keys_nw_info_init(sec_prot_keys_nw_info_t *sec_keys_nw_info, sec_prot_gtk_keys_t *gtks) +{ + if (!sec_keys_nw_info) { + return; + } + + memset(sec_keys_nw_info, 0, sizeof(sec_prot_keys_nw_info_t)); + + sec_keys_nw_info->gtks = gtks; + 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) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return; + } + + pae_supp->auth_completed = completed; + pae_supp->key_insert = key_insert; +} + +int8_t ws_pae_supp_init(protocol_interface_info_entry_t *interface_ptr, const sec_prot_certs_t *certs) +{ + if (!interface_ptr) { + return -1; + } + + if (ws_pae_supp_get(interface_ptr) != NULL) { + return 0; + } + + pae_supp_t *pae_supp = ns_dyn_mem_alloc(sizeof(pae_supp_t)); + if (!pae_supp) { + return -1; + } + + pae_supp->interface_ptr = interface_ptr; + pae_supp->auth_completed = 0; + pae_supp->key_insert = 0; + pae_supp->auth_trickle_running = false; + pae_supp->nw_keys_used_cnt = 0; + + ws_pae_lib_supp_init(&pae_supp->entry); + + ws_pae_supp_keys_nw_info_init(&pae_supp->sec_keys_nw_info, &pae_supp->gtks); + + kmp_address_init(KMP_ADDR_EUI_64, &pae_supp->target_addr, 0); + + 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) { + goto error; + } + + if (kmp_service_cb_register(pae_supp->kmp_service, ws_pae_supp_kmp_incoming_ind, ws_pae_supp_kmp_service_addr_get, ws_pae_supp_kmp_service_api_get) < 0) { + goto error; + } + + if (kmp_service_event_if_register(pae_supp->kmp_service, ws_pae_supp_event_send)) { + goto error; + } + + if (kmp_service_timer_if_register(pae_supp->kmp_service, ws_pae_supp_timer_if_start, ws_pae_supp_timer_if_stop)) { + goto error; + } + + if (kmp_eapol_pdu_if_register(pae_supp->kmp_service, interface_ptr) < 0) { + goto error; + } + + if (ws_eapol_pdu_cb_register(interface_ptr, &eapol_pdu_recv_cb_data) < 0) { + goto error; + } + + if (key_sec_prot_register(pae_supp->kmp_service) < 0) { + goto error; + } + + if (supp_eap_tls_sec_prot_register(pae_supp->kmp_service) < 0) { + goto error; + } + + if (client_tls_sec_prot_register(pae_supp->kmp_service) < 0) { + goto error; + } + + if (supp_fwh_sec_prot_register(pae_supp->kmp_service) < 0) { + goto error; + } + + if (supp_gkh_sec_prot_register(pae_supp->kmp_service) < 0) { + goto error; + } + + if (tasklet_id < 0) { + tasklet_id = eventOS_event_handler_create(ws_pae_supp_tasklet_handler, PAE_TASKLET_INIT); + if (tasklet_id < 0) { + goto error; + } + } + + if (ws_pae_supp_timer_stop(pae_supp) < 0) { + goto error; + } + + ws_pae_supp_nvm_nw_info_read(pae_supp); + ws_pae_supp_nvm_keys_read(pae_supp); + + ns_list_add_to_end(&pae_supp_list, pae_supp); + + return 0; + +error: + ws_pae_supp_free(pae_supp); + + return -1; +} + +int8_t ws_pae_supp_delete(protocol_interface_info_entry_t *interface_ptr) +{ + if (!interface_ptr) { + return -1; + } + + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + ws_pae_supp_free(pae_supp); + return 0; +} + +static void ws_pae_supp_free(pae_supp_t *pae_supp) +{ + if (!pae_supp) { + return; + } + + ws_pae_lib_supp_delete(&pae_supp->entry); + + kmp_eapol_pdu_if_unregister(pae_supp->kmp_service); + + ws_eapol_pdu_cb_unregister(pae_supp->interface_ptr, &eapol_pdu_recv_cb_data); + + kmp_service_delete(pae_supp->kmp_service); + + ns_list_remove(&pae_supp_list, pae_supp); + ns_dyn_mem_free(pae_supp); +} + +static pae_supp_t *ws_pae_supp_get(protocol_interface_info_entry_t *interface_ptr) +{ + ns_list_foreach(pae_supp_t, entry, &pae_supp_list) { + if (entry->interface_ptr == interface_ptr) { + return entry; + } + } + + return NULL; +} + +static pae_supp_t *ws_pae_supp_by_kmp_service_get(kmp_service_t *service) +{ + ns_list_foreach(pae_supp_t, entry, &pae_supp_list) { + if (entry->kmp_service == service) { + return entry; + } + } + + return NULL; +} + +static int8_t ws_pae_supp_event_send(kmp_service_t *service, void *data) +{ + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return -1; + } + + arm_event_s event = { + .receiver = tasklet_id, + .sender = 0, + .event_id = pae_supp->interface_ptr->id, + .data_ptr = data, + .event_type = PAE_TASKLET_EVENT, + .priority = ARM_LIB_LOW_PRIORITY_EVENT, + }; + + if (eventOS_event_send(&event) != 0) { + return -1; + } + + return 0; +} + +static void ws_pae_supp_tasklet_handler(arm_event_s *event) +{ + if (event->event_type == PAE_TASKLET_INIT) { + + } else if (event->event_type == PAE_TASKLET_EVENT) { + pae_supp_t *pae_supp = NULL; + + ns_list_foreach(pae_supp_t, entry, &pae_supp_list) { + if (entry->interface_ptr->id == event->event_id) { + pae_supp = entry; + break; + } + } + + if (pae_supp) { + kmp_service_event_if_event(pae_supp->kmp_service, event->data_ptr); + } + } +} + +void ws_pae_supp_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) { + + tr_debug("PAE idle"); + // Sets target/parent address to null + pae_supp->entry.addr = NULL; + + // If not already completed, restart bootstrap + ws_pae_supp_authenticate_response(pae_supp, false); + + ws_pae_supp_timer_stop(pae_supp); + } + } +} + +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); + if (!pae_supp) { + return -1; + } + + if (ws_pae_supp_timer_start(pae_supp) < 0) { + return -1; + } + + kmp_entry_t *entry = kmp_api_data_get(kmp); + if (!entry) { + return -1; + } + ws_pae_lib_kmp_timer_start(&pae_supp->entry.kmp_list, entry); + return 0; +} + +static int8_t ws_pae_supp_timer_if_stop(kmp_service_t *service, kmp_api_t *kmp) +{ + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return -1; + } + + kmp_entry_t *entry = kmp_api_data_get(kmp); + if (!entry) { + return -1; + } + ws_pae_lib_kmp_timer_stop(&pae_supp->entry.kmp_list, entry); + return 0; +} + +static int8_t ws_pae_supp_timer_start(pae_supp_t *pae_supp) +{ + pae_supp->timer_running = true; + return 0; +} + +static int8_t ws_pae_supp_timer_stop(pae_supp_t *pae_supp) +{ + pae_supp->timer_running = false; + return 0; +} + +static bool ws_pae_supp_timer_running(pae_supp_t *pae_supp) +{ + return pae_supp->timer_running; +} + +static int8_t ws_pae_supp_eapol_pdu_address_check(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64) +{ + pae_supp_t *pae_supp = ws_pae_supp_get(interface_ptr); + if (!pae_supp) { + return -1; + } + + // Message from EAPOL target node, route to self + if (pae_supp->entry.addr) { + if (memcmp(eui_64, kmp_address_eui_64_get(pae_supp->entry.addr), 8) == 0) { + return 0; + } + } + + rpl_dodag_info_t dodag_info; + 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) { + return 0; + } + + return -1; + } + + return 0; +} + +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) +{ + (void) kmp; + + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return; + } + + // Get own EUI-64 + link_layer_address_s mac_params; + if (arm_nwk_mac_address_read(pae_supp->interface_ptr->id, &mac_params) >= 0) { + kmp_address_eui_64_set(local_addr, mac_params.mac_long); + } + + if (pae_supp->new_br_eui_64_set) { + 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"); + } +} + +static kmp_api_t *ws_pae_supp_kmp_service_api_get(kmp_service_t *service, kmp_api_t *kmp, kmp_type_e type) +{ + (void) kmp; + + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return NULL; + } + + return ws_pae_lib_kmp_list_type_get(&pae_supp->entry.kmp_list, type); +} + +static kmp_api_t *ws_pae_supp_kmp_incoming_ind(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr) +{ + // Should be MKA, 4WH or GKH and never initial EAPOL-key for supplicant + if (type > IEEE_802_1X_INITIAL_KEY) { + return NULL; + } + + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return NULL; + } + + if (!pae_supp->entry.addr) { + // Does no longer wait for authentication, ignores message + return NULL; + } + + // No longer runs trickle timer for re-sending initial EAPOL-key + pae_supp->auth_trickle_running = false; + + // Updates parent address + kmp_address_copy(pae_supp->entry.addr, addr); + + // Check if ongoing + kmp_api_t *kmp = ws_pae_lib_kmp_list_type_get(&pae_supp->entry.kmp_list, type); + if (kmp) { + return kmp; + } + + // Create new instance + kmp = ws_pae_supp_kmp_create_and_start(service, type, pae_supp); + + // For EAP-TLS create also TLS in addition to EAP-TLS + if (type == IEEE_802_1X_MKA) { + if (ws_pae_lib_kmp_list_type_get(&pae_supp->entry.kmp_list, TLS_PROT) != NULL) { + // TLS already exists, wait for it to be deleted + ws_pae_lib_kmp_list_delete(&pae_supp->entry.kmp_list, kmp); + return NULL; + } + // Create TLS instance */ + if (ws_pae_supp_kmp_create_and_start(service, TLS_PROT, pae_supp) == NULL) { + ws_pae_lib_kmp_list_delete(&pae_supp->entry.kmp_list, kmp); + return NULL; + } + } + + return kmp; +} + +static kmp_api_t *ws_pae_supp_kmp_create_and_start(kmp_service_t *service, kmp_type_e type, pae_supp_t *pae_supp) +{ + // Create new instance + kmp_api_t *kmp = kmp_api_create(service, type); + if (!kmp) { + return NULL; + } + + // Updates parent address + kmp_api_addr_set(kmp, pae_supp->entry.addr); + + // Sets security keys to KMP + kmp_api_sec_keys_set(kmp, &pae_supp->entry.sec_keys); + + kmp_api_cb_register( + kmp, ws_pae_supp_kmp_api_create_confirm, + ws_pae_supp_kmp_api_create_indication, + ws_pae_supp_kmp_api_finished_indication, + ws_pae_supp_kmp_api_finished); + + kmp_entry_t *kmp_entry = ws_pae_lib_kmp_list_add(&pae_supp->entry.kmp_list, kmp); + if (!kmp_entry) { + kmp_api_delete(kmp); + return NULL; + } + + kmp_api_data_set(kmp, kmp_entry); + + if (kmp_api_start(kmp) < 0) { + ws_pae_lib_kmp_list_delete(&pae_supp->entry.kmp_list, kmp); + return NULL; + } + + return kmp; +} + +static void ws_pae_supp_kmp_api_create_confirm(kmp_api_t *kmp, kmp_result_e result) +{ + kmp_service_t *service = kmp_api_service_get(kmp); + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return; + } + + // KMP-CREATE.request has failed, authentication error + if (result != KMP_RESULT_OK) { + ws_pae_supp_authenticate_response(pae_supp, false); + } +} + +static void ws_pae_supp_kmp_api_create_indication(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr) +{ + (void) addr; + (void) type; + + // For now, accept every KMP-CREATE.indication + kmp_api_create_response(kmp, KMP_RESULT_OK); +} + +static void ws_pae_supp_kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e result, kmp_sec_keys_t *sec_keys) +{ + kmp_service_t *service = kmp_api_service_get(kmp); + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return; + } + + kmp_type_e type = kmp_api_type_get(kmp); + + // Whenever EAP-TLS, 4WH or GKH completes increase timer for wait for authentication + if (type < IEEE_802_1X_INITIAL_KEY && result == KMP_RESULT_OK) { + 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) { + ws_pae_supp_authenticate_response(pae_supp, true); + } +} + +static void ws_pae_supp_kmp_api_finished(kmp_api_t *kmp) +{ + kmp_service_t *service = kmp_api_service_get(kmp); + pae_supp_t *pae_supp = ws_pae_supp_by_kmp_service_get(service); + if (!pae_supp) { + return; + } + + // Delete KMP + ws_pae_lib_kmp_list_delete(&pae_supp->entry.kmp_list, kmp); +} + +#endif /* HAVE_PAE_SUPP */ +#endif /* HAVE_WS */ + diff --git a/source/6LoWPAN/ws/ws_pae_supp.h b/source/6LoWPAN/ws/ws_pae_supp.h new file mode 100644 index 0000000000..2a525f94d9 --- /dev/null +++ b/source/6LoWPAN/ws/ws_pae_supp.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-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_SUPP_H_ +#define WS_PAE_SUPP_H_ + +#define PAE_SUPP_NOT_ENABLED 5 + +#ifdef HAVE_PAE_SUPP + +/* + * Supplicant port access entity controls key security protocols using KMP API. + * + * Configures KMP service network access and provides timing and callback services + * for it. Registers needed security protocols to KMP service. + * + * PAE maintains security keys that are internal to port access entity. After + * (re-)authentication provides network access keys to application. + * + */ + +/** + * ws_pae_supp_init initializes PAE supplicant + * + * \param interface_ptr interface + * \param cert_chain certificate chain + * + * \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); + +/** + * ws_pae_supp_delete deletes PAE supplicant + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_delete(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_supp_timer PAE supplicant timer call + * + * \param ticks elapsed ticks + * + */ +void ws_pae_supp_timer(uint16_t ticks); + +/** + * ws_pae_supp_authenticate start EAPOL authentication + * + * \param interface_ptr interface + * \param dest_pan_id EAPOL target PAN ID + * \param dest_eui_64 EAPOL target + * + * \return < 0 failure + * \return 0 authentication done, continue + * \return > 0 authentication started + * + */ +int8_t ws_pae_supp_authenticate(protocol_interface_info_entry_t *interface_ptr, uint16_t dest_pan_id, uint8_t *dest_eui_64); + +/** + * ws_pae_supp_nw_info_set set network information + * + * \param interface_ptr interface + * \param pan_id PAD ID + * \param network_name network name + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_nw_info_set(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name); + +/** + * ws_pae_supp_border_router_addr_write write border router address + * + * \param interface_ptr interface + * \param eui_64 pointer to EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_border_router_addr_write(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64); + +/** + * ws_pae_supp_border_router_addr_read read border router address + * + * \param interface_ptr interface + * \param eui_64 pointer to EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_border_router_addr_read(protocol_interface_info_entry_t *interface_ptr, uint8_t *eui_64); + +/** + * ws_pae_supp_nw_key_valid network key is valid i.e. used successfully on bootstrap + * + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ws_pae_supp_nw_key_valid(protocol_interface_info_entry_t *interface_ptr); + +/** + * ws_pae_supp_auth_completed authentication completed callback + * + * \param interface_ptr interface + * \param success true if authentication was successful + * + */ +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 + * + * \param interface_ptr interface + * \param gtk_index index of the new GTK key + * \param gtk new GTK key + * + */ +typedef void ws_pae_supp_key_insert(protocol_interface_info_entry_t *interface_ptr, uint8_t gtk_index, uint8_t *gtk); + +/** + * 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 + * + */ +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); + +#else + +#define ws_pae_supp_init(interface_ptr, certs) 1 +#define ws_pae_supp_delete NULL +#define ws_pae_supp_cb_register(interface_ptr, completed, key_insert) +#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_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 + +#endif + +#endif /* WS_PAE_SUPP_H_ */ diff --git a/source/6LoWPAN/ws/ws_test_api.c b/source/6LoWPAN/ws/ws_test_api.c new file mode 100644 index 0000000000..c320f5b4a8 --- /dev/null +++ b/source/6LoWPAN/ws/ws_test_api.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" + +#include +#include +#include +#include +#include "NWK_INTERFACE/Include/protocol.h" +#include "6LoWPAN/ws/ws_config.h" +#include "6LoWPAN/ws/ws_common.h" +#include "6LoWPAN/ws/ws_bbr_api_internal.h" +#include "randLIB.h" + +#include "ns_trace.h" +#include "common_functions.h" + +#define TRACE_GROUP "wste" + +#ifdef HAVE_WS + +int ws_test_pan_size_set(int8_t interface_id, uint16_t pan_size) +{ + + (void) interface_id; +#ifdef HAVE_WS_BORDER_ROUTER + test_pan_size_override = pan_size; + return 0; +#else + (void) pan_size; + return -1; +#endif +} + +int ws_test_max_child_count_set(int8_t interface_id, uint16_t child_count) +{ + + (void) interface_id; + test_max_child_count_override = child_count; + return 0; +} + +int ws_test_gtk_set(int8_t interface_id, uint8_t *gtk[4]) +{ + (void) interface_id; + (void) gtk; + + return 0; +} + +int ws_test_active_key_set(int8_t interface_id, uint8_t index) +{ + (void) interface_id; + (void) index; + + return 0; +} + +int ws_test_key_lifetime_set(int8_t interface_id, uint32_t gtk_lifetime, uint32_t pmk_lifetime, uint32_t ptk_lifetime) +{ + (void) interface_id; + (void) gtk_lifetime; + (void) pmk_lifetime; + (void) ptk_lifetime; + + return 0; +} + +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) +{ + (void) interface_id; + (void) revocat_lifetime_reduct; + (void) new_activation_time; + (void) max_mismatch; + + return 0; +} + +#endif // HAVE_WS diff --git a/source/Common_Protocols/icmpv6.c b/source/Common_Protocols/icmpv6.c index 2dd17b9143..547ed947d5 100644 --- a/source/Common_Protocols/icmpv6.c +++ b/source/Common_Protocols/icmpv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,7 +34,7 @@ #include "Common_Protocols/ip.h" #include "Common_Protocols/ipv6.h" #include "Common_Protocols/mld.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "ipv6_stack/protocol_ipv6.h" #include "ipv6_stack/ipv6_routing_table.h" #include "ip_fsc.h" @@ -340,6 +340,26 @@ static buffer_t *icmpv6_echo_request_handler(buffer_t *buf) } #ifdef HAVE_IPV6_ND + +static void icmpv6_na_wisun_aro_handler(protocol_interface_info_entry_t *cur_interface, const uint8_t *dptr, const uint8_t *src_addr) +{ + (void) src_addr; + dptr += 2; + uint16_t life_time; + uint8_t nd_status = *dptr; + dptr += 4; + life_time = common_read_16_bit(dptr); + dptr += 2; + if (memcmp(dptr, cur_interface->mac, 8) != 0) { + return; + } + + (void)life_time; + if (nd_status != ARO_SUCCESS) { + ws_common_aro_failure(cur_interface, src_addr); + } +} + static void icmpv6_na_aro_handler(protocol_interface_info_entry_t *cur_interface, const uint8_t *dptr, const uint8_t *dst_addr) { (void)dst_addr; @@ -985,11 +1005,14 @@ static buffer_t *icmpv6_na_handler(buffer_t *buf) goto drop; } - if (cur->ipv6_neighbour_cache.recv_na_aro) { - const uint8_t *aro = icmpv6_find_option_in_buffer(buf, 20, ICMPV6_OPT_ADDR_REGISTRATION, 2); - if (aro) { + const uint8_t *aro = icmpv6_find_option_in_buffer(buf, 20, ICMPV6_OPT_ADDR_REGISTRATION, 2); + if (aro) { + if (cur->ipv6_neighbour_cache.recv_na_aro) { icmpv6_na_aro_handler(cur, aro, buf->dst_sa.address); } + if (ws_info(cur)) { + icmpv6_na_wisun_aro_handler(cur, aro, buf->src_sa.address); + } } /* No need to create a neighbour cache entry if one doesn't already exist */ @@ -1092,7 +1115,7 @@ buffer_t *icmpv6_up(buffer_t *buf) buf = rpl_control_source_route_error_handler(buf, cur); } #endif - /* no break */ + /* fall through */ default: if (buf) { diff --git a/source/Common_Protocols/ipv6.c b/source/Common_Protocols/ipv6.c index e125cca31e..fe3102591d 100644 --- a/source/Common_Protocols/ipv6.c +++ b/source/Common_Protocols/ipv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "ns_types.h" #include "string.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "ns_trace.h" #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_stats.h" diff --git a/source/Common_Protocols/ipv6_flow.c b/source/Common_Protocols/ipv6_flow.c index 9670f0d395..60e1657881 100644 --- a/source/Common_Protocols/ipv6_flow.c +++ b/source/Common_Protocols/ipv6_flow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Common_Protocols/ipv6_fragmentation.c b/source/Common_Protocols/ipv6_fragmentation.c index 961c92c2f3..1386b398ea 100644 --- a/source/Common_Protocols/ipv6_fragmentation.c +++ b/source/Common_Protocols/ipv6_fragmentation.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,7 +36,7 @@ #include "nsdynmemLIB.h" #include #include "ns_trace.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ip.h" #include "Common_Protocols/ipv6.h" diff --git a/source/Common_Protocols/ipv6_resolution.c b/source/Common_Protocols/ipv6_resolution.c index b374b1da4e..790b226b8a 100644 --- a/source/Common_Protocols/ipv6_resolution.c +++ b/source/Common_Protocols/ipv6_resolution.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,7 @@ #include "ns_list.h" #include "ns_trace.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ipv6.h" #include "Common_Protocols/icmpv6.h" diff --git a/source/Common_Protocols/ipv6_resolution.h b/source/Common_Protocols/ipv6_resolution.h index a54b4d5488..63b6d4b5bc 100644 --- a/source/Common_Protocols/ipv6_resolution.h +++ b/source/Common_Protocols/ipv6_resolution.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #ifndef IPV6_RESOLUTION_H_ #define IPV6_RESOLUTION_H_ -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" struct ipv6_neighbour; struct ipv6_neighbour_cache; diff --git a/source/Common_Protocols/tcp.c b/source/Common_Protocols/tcp.c index d3c9add1a5..311a0e96a7 100644 --- a/source/Common_Protocols/tcp.c +++ b/source/Common_Protocols/tcp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "ns_types.h" #include "ns_trace.h" #include "eventOS_event.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "nsdynmemLIB.h" #include "ip_fsc.h" #include "ns_sha256.h" diff --git a/source/Common_Protocols/udp.c b/source/Common_Protocols/udp.c index df1c1cc9f8..a232e6762c 100644 --- a/source/Common_Protocols/udp.c +++ b/source/Common_Protocols/udp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,7 @@ #include "Common_Protocols/ipv6_constants.h" #include "Common_Protocols/icmpv6.h" #include "Common_Protocols/udp.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "common_functions.h" #define TRACE_GROUP "udp" diff --git a/source/Core/buffer_dyn.c b/source/Core/buffer_dyn.c index 7bb1a12663..1b34e64997 100644 --- a/source/Core/buffer_dyn.c +++ b/source/Core/buffer_dyn.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2018, Arm Limited and affiliates. + * Copyright (c) 2011-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,9 +19,9 @@ #include "string.h" #include "ns_types.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Core/include/ns_buffer.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "ns_trace.h" #include "platform/arm_hal_interrupt.h" #include "NWK_INTERFACE/Include/protocol_stats.h" diff --git a/source/Core/include/address.h b/source/Core/include/ns_address_internal.h similarity index 99% rename from source/Core/include/address.h rename to source/Core/include/ns_address_internal.h index 22f4e380ea..8295226283 100644 --- a/source/Core/include/address.h +++ b/source/Core/include/ns_address_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010-2018, Arm Limited and affiliates. + * Copyright (c) 2008, 2010-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ */ /** * - * \file address.h + * \file ns_address_internal.h * \brief address type definitions. * * nanoStack: supported address types and associated data structures. diff --git a/source/Core/include/ns_buffer.h b/source/Core/include/ns_buffer.h index 71ef81016d..7ebb6e0545 100644 --- a/source/Core/include/ns_buffer.h +++ b/source/Core/include/ns_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2018, Arm Limited and affiliates. + * Copyright (c) 2008-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,7 +32,7 @@ #endif #include "ns_types.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "NWK_INTERFACE/Include/protocol_abstract.h" #include "ns_list.h" #include "ipv6_stack/ipv6_routing_table.h" diff --git a/source/Core/include/socket.h b/source/Core/include/ns_socket.h similarity index 99% rename from source/Core/include/socket.h rename to source/Core/include/ns_socket.h index 6585c64ecc..5e1c566e20 100644 --- a/source/Core/include/socket.h +++ b/source/Core/include/ns_socket.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2017, Arm Limited and affiliates. + * Copyright (c) 2008-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Core/address.c b/source/Core/ns_address_internal.c similarity index 99% rename from source/Core/address.c rename to source/Core/ns_address_internal.c index 7f8b173a4e..386d00c01d 100644 --- a/source/Core/address.c +++ b/source/Core/ns_address_internal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010-2018, Arm Limited and affiliates. + * Copyright (c) 2008, 2010-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,8 @@ * limitations under the License. */ /** - * \file address.c - * \brief Utility functions concernig addresses + * \file ns_address_internal.c + * \brief Utility functions concerning addresses * * This file contains all the utility functions that can be used to * check, manipulate etc. addresses. @@ -176,6 +176,9 @@ static bool addr_is_ipv4_mapped(const uint8_t addr[static 16]) /* Scope(A), as defined in RFC 6724 plus RFC 4007 */ uint_fast8_t addr_ipv6_scope(const uint8_t addr[static 16], const protocol_interface_info_entry_t *interface) { +#ifndef HAVE_THREAD + (void)interface; +#endif if (addr_is_ipv6_multicast(addr)) { return addr_ipv6_multicast_scope(addr); } diff --git a/source/Core/ns_socket.c b/source/Core/ns_socket.c index bb9bf3d5a2..24f7738c6f 100644 --- a/source/Core/ns_socket.c +++ b/source/Core/ns_socket.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2015, 2017-2018, Arm Limited and affiliates. + * Copyright (c) 2008-2015, 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,7 @@ #include "eventOS_event.h" #include "eventOS_scheduler.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "socket_api.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/tcp.h" diff --git a/source/Core/sockbuf.c b/source/Core/sockbuf.c index a61017cbfd..80e1bdd1da 100644 --- a/source/Core/sockbuf.c +++ b/source/Core/sockbuf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/DHCPv6_Server/DHCPv6_Server_service.c b/source/DHCPv6_Server/DHCPv6_Server_service.c index 97a3fabcfd..4776d6ddea 100644 --- a/source/DHCPv6_Server/DHCPv6_Server_service.c +++ b/source/DHCPv6_Server/DHCPv6_Server_service.c @@ -232,7 +232,6 @@ int DHCPv6_server_service_init(int8_t interface, uint8_t guaPrefix[static 16], u uint16_t socketInstance; protocol_interface_info_entry_t *cur; (void)serverDUID; - (void)serverDUIDType; //allocate Socket Service socketInstance = dhcp_service_init(interface, DHCP_INSTANCE_SERVER, DHCPV6_server_service_request_handler); cur = protocol_stack_interface_info_get_by_id(interface); @@ -246,7 +245,7 @@ int DHCPv6_server_service_init(int8_t interface, uint8_t guaPrefix[static 16], u retVal = -2; } else { //allocate server - dhcpv6_gua_server_entry_s *serverInfo = libdhcpv6_gua_server_allocate(guaPrefix, interface, cur->mac, DHCPV6_DUID_HARDWARE_EUI64_TYPE); + dhcpv6_gua_server_entry_s *serverInfo = libdhcpv6_gua_server_allocate(guaPrefix, interface, cur->mac, serverDUIDType); if (serverInfo) { serverInfo->socketInstance_id = socketInstance; socketInstance = 0; diff --git a/source/DHCPv6_Server/DHCPv6_server_service.h b/source/DHCPv6_Server/DHCPv6_server_service.h index d2ba01068f..7373bedaa0 100644 --- a/source/DHCPv6_Server/DHCPv6_server_service.h +++ b/source/DHCPv6_Server/DHCPv6_server_service.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/DHCPv6_client/dhcpv6_client_api.h b/source/DHCPv6_client/dhcpv6_client_api.h index 69ae1ddd20..b1b6609e88 100644 --- a/source/DHCPv6_client/dhcpv6_client_api.h +++ b/source/DHCPv6_client/dhcpv6_client_api.h @@ -34,9 +34,22 @@ * /param interface interface id of this instance. * */ - void dhcp_client_init(int8_t interface); +/* Set configurations for DHCP client + * + * /param renew_uses_solicit Instead of renew message SOLICIT is used. + */ +void dhcp_client_configure(int8_t interface, bool renew_uses_solicit); + +/* Set Timeout parameters for SOLICIT transactions + * + * /param timeout SOLICIT timeout initial value. 0 means use defaults + * /param max_rt SOLICIT timeout max value. + * /param max_rc SOLICIT re-transmission count. 0 means infinite. + */ +void dhcp_client_solicit_timeout_set(int8_t interface, uint16_t timeout, uint16_t max_rt, uint8_t max_rc); + /* Delete dhcp client. * * When this is called all addressed assigned by this module are removed from stack. @@ -57,13 +70,14 @@ void dhcp_client_delete(int8_t interface); * /param dhcp_addr dhcp server ML16 address where address is registered. * /param prefix dhcp server ML16 address where address is registered. * /param mac64 64 bit mac address for identifieng client. + * /param link_type Link hardware type. * /param error_cb error callback that is called if address cannot be created or becomes invalid. * /param register_status true if address registered. * */ typedef void (dhcp_client_global_adress_cb)(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], bool register_status); -int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], uint8_t mac64[static 8], dhcp_client_global_adress_cb *error_cb); +int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], uint8_t mac64[static 8], uint16_t link_type, dhcp_client_global_adress_cb *error_cb); /* Renew all leased adddresses might be used when short address changes * diff --git a/source/DHCPv6_client/dhcpv6_client_service.c b/source/DHCPv6_client/dhcpv6_client_service.c index c5f1e437f9..0e1c97cd81 100644 --- a/source/DHCPv6_client/dhcpv6_client_service.c +++ b/source/DHCPv6_client/dhcpv6_client_service.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,8 +35,12 @@ typedef struct { dhcp_client_global_adress_cb *global_address_cb; uint16_t service_instance; uint16_t relay_instance; + uint16_t sol_timeout; + uint16_t sol_max_rt; + uint8_t sol_max_rc; uint8_t libDhcp_instance; int8_t interface; + bool renew_uses_solicit: 1; } dhcp_client_class_t; static dhcp_client_class_t dhcp_client; @@ -50,6 +54,26 @@ void dhcp_client_init(int8_t interface) dhcp_client.service_instance = dhcp_service_init(interface, DHCP_INSTANCE_CLIENT, NULL); dhcp_client.interface = interface; dhcp_client.libDhcp_instance = libdhcpv6_nonTemporal_entry_get_unique_instance_id(); + dhcp_client.sol_timeout = 0; + dhcp_client.sol_max_rt = 0; + dhcp_client.sol_max_rc = 0; + + return; +} +void dhcp_client_configure(int8_t interface, bool renew_uses_solicit) +{ + // Set true if RENEW is not used and SOLICIT sent instead. + (void)interface; + dhcp_client.renew_uses_solicit = renew_uses_solicit; +} + +void dhcp_client_solicit_timeout_set(int8_t interface, uint16_t timeout, uint16_t max_rt, uint8_t max_rc) +{ + // Set the default retry values for SOLICIT and RENEW messages. + (void)interface; + dhcp_client.sol_timeout = timeout; + dhcp_client.sol_max_rt = max_rt; + dhcp_client.sol_max_rc = max_rc; return; } @@ -167,26 +191,36 @@ error_exit: return RET_MSG_ACCEPTED; } -int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], uint8_t mac64[static 8], dhcp_client_global_adress_cb *error_cb) +int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16], uint8_t mac64[static 8], uint16_t link_type, dhcp_client_global_adress_cb *error_cb) { dhcpv6_solication_base_packet_s solPacket = {0}; - dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; + uint8_t *payload_ptr; uint32_t payload_len; dhcpv6_client_server_data_t *srv_data_ptr; - if (mac64 == NULL || prefix == NULL || dhcp_addr == NULL) { + if (mac64 == NULL || dhcp_addr == NULL) { tr_error("Invalid parameters"); return -1; } - srv_data_ptr = libdhcvp6_nontemporalAddress_server_data_allocate(interface, dhcp_client.libDhcp_instance, mac64, DHCPV6_DUID_HARDWARE_EUI64_TYPE, prefix, dhcp_addr); + if (!prefix) { + //NULL Definition will only check That Interface is not generated + if (libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance)) { + //Already Created to same interface + return -1; + } + } + + srv_data_ptr = libdhcvp6_nontemporalAddress_server_data_allocate(interface, dhcp_client.libDhcp_instance, mac64, link_type, prefix, dhcp_addr); + if (!srv_data_ptr) { tr_error("OOM srv_data_ptr"); return -1; } - payload_len = libdhcpv6_solication_message_length(DHCPV6_DUID_HARDWARE_EUI64_TYPE, true, 0); + payload_len = libdhcpv6_solication_message_length(link_type, prefix != NULL, 0); + payload_ptr = ns_dyn_mem_temporary_alloc(payload_len); if (!payload_ptr) { libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); @@ -198,13 +232,19 @@ int dhcp_client_get_global_address(int8_t interface, uint8_t dhcp_addr[static 16 srv_data_ptr->GlobalAddress = true; // Build solicit solPacket.clientDUID.linkID = mac64; - solPacket.clientDUID.linkType = DHCPV6_DUID_HARDWARE_EUI64_TYPE; + solPacket.clientDUID.linkType = link_type; solPacket.iaID = srv_data_ptr->IAID; solPacket.messageType = DHCPV6_SOLICATION_TYPE; solPacket.transActionId = libdhcpv6_txid_get(); /*Non Temporal Address */ - nonTemporalAddress.requestedAddress = prefix; - libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &solPacket, &nonTemporalAddress, NULL); + + if (prefix) { + dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; + nonTemporalAddress.requestedAddress = prefix; + libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &solPacket, &nonTemporalAddress, NULL); + } else { + libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &solPacket, NULL, NULL); + } // send solicit srv_data_ptr->transActionId = dhcp_service_send_req(dhcp_client.service_instance, 0, srv_data_ptr, dhcp_addr, payload_ptr, payload_len, dhcp_solicit_resp_cb); @@ -213,6 +253,10 @@ 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; } + 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); + } return 0; } @@ -289,6 +333,10 @@ void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t .requestedOptionList = NULL, }; + if (dhcp_client.renew_uses_solicit) { + packetReq.messageType = DHCPV6_SOLICATION_TYPE; + } + // Set Address information nonTemporalAddress.requestedAddress = srv_data_ptr->iaNontemporalAddress.addressPrefix; nonTemporalAddress.preferredLifeTime = srv_data_ptr->iaNontemporalAddress.preferredTime; @@ -303,6 +351,10 @@ void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t 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) { + // 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); + } } void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr) diff --git a/source/MAC/IEEE802_15_4/mac_defines.h b/source/MAC/IEEE802_15_4/mac_defines.h index 6a398396cc..d5623049ed 100644 --- a/source/MAC/IEEE802_15_4/mac_defines.h +++ b/source/MAC/IEEE802_15_4/mac_defines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -216,7 +216,7 @@ typedef struct protocol_interface_rf_mac_setup { uint8_t mac_sequence; uint8_t mac_tx_retry; uint8_t mac_cca_retry; - uint8_t mac_ack_wait_duration; + uint16_t mac_ack_wait_duration; uint8_t mac_mlme_retry_max; uint8_t aUnitBackoffPeriod; /* Indirect queue parameters */ diff --git a/source/MAC/IEEE802_15_4/mac_fhss_callbacks.h b/source/MAC/IEEE802_15_4/mac_fhss_callbacks.h index 364c94339f..744e7c2485 100644 --- a/source/MAC/IEEE802_15_4/mac_fhss_callbacks.h +++ b/source/MAC/IEEE802_15_4/mac_fhss_callbacks.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); 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 c43c1103fc..7865cc7cf7 100644 --- a/source/MAC/IEEE802_15_4/mac_header_helper_functions.c +++ b/source/MAC/IEEE802_15_4/mac_header_helper_functions.c @@ -26,6 +26,7 @@ #include "MAC/IEEE802_15_4/mac_defines.h" #include "MAC/IEEE802_15_4/mac_mcps_sap.h" #include "MAC/IEEE802_15_4/mac_header_helper_functions.h" +#include "MAC/rf_driver_storage.h" static uint8_t *mcps_mac_security_aux_header_start_pointer_get(const mac_pre_parsed_frame_t *buffer); static uint8_t *mac_header_information_elements_write(const mac_pre_build_frame_t *buffer, uint8_t *ptr); @@ -569,7 +570,13 @@ uint8_t *mac_generic_packet_write(struct protocol_interface_rf_mac_setup *rf_ptr ptr += buffer->mac_payload_length; } if (rf_ptr->fhss_api) { - rf_ptr->fhss_api->write_synch_info(rf_ptr->fhss_api, ie_start, buffer->headerIeLength, FHSS_DATA_FRAME, buffer->tx_time); + if (buffer->fcf_dsn.frametype == FC_BEACON_FRAME) { + dev_driver_tx_buffer_s *tx_buf = &rf_ptr->dev_driver_tx_buffer; + uint8_t *synch_info = tx_buf->buf + rf_ptr->dev_driver->phy_driver->phy_header_length + tx_buf->len - FHSS_SYNCH_INFO_LENGTH; + rf_ptr->fhss_api->write_synch_info(rf_ptr->fhss_api, synch_info, FHSS_SYNCH_INFO_LENGTH, FHSS_SYNCH_FRAME, buffer->tx_time); + } else { + rf_ptr->fhss_api->write_synch_info(rf_ptr->fhss_api, ie_start, buffer->headerIeLength, FHSS_DATA_FRAME, buffer->tx_time); + } } 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 2e15a2657a..b884b2c0f1 100644 --- a/source/MAC/IEEE802_15_4/mac_mcps_sap.c +++ b/source/MAC/IEEE802_15_4/mac_mcps_sap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -987,7 +987,11 @@ static void mac_data_interface_frame_handler(mac_pre_parsed_frame_t *buf) mcps_sap_pre_parsed_frame_buffer_free(buf); return; } - //Sniffer Should push here data to stack!!!! + /* push data to stack if sniffer mode is enabled */ + if (rf_mac_setup->macProminousMode) { + mac_nap_tun_data_handler(buf, rf_mac_setup); + return; + } mac_api_t *mac = get_sw_mac_api(rf_mac_setup); if (!mac || (rf_mac_setup->mac_mlme_scan_resp && buf->fcf_dsn.frametype != MAC_FRAME_BEACON)) { mcps_sap_pre_parsed_frame_buffer_free(buf); @@ -1596,9 +1600,9 @@ 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) { + (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; diff --git a/source/MAC/IEEE802_15_4/mac_mlme.c b/source/MAC/IEEE802_15_4/mac_mlme.c index b4efbb2e55..03eb5fdfae 100644 --- a/source/MAC/IEEE802_15_4/mac_mlme.c +++ b/source/MAC/IEEE802_15_4/mac_mlme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -68,6 +68,7 @@ static int8_t mac_mlme_rf_channel_set(struct protocol_interface_rf_mac_setup *rf static void mac_mlme_timer_cb(int8_t timer_id, uint16_t slots); static void mac_mlme_start_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_start_conf_t *conf); static void mac_mlme_scan_confirm_handler(protocol_interface_rf_mac_setup_s *rf_ptr, const mlme_scan_conf_t *conf); +static int mac_mlme_set_symbol_rate(protocol_interface_rf_mac_setup_s *rf_mac_setup); static void mac_mlme_energy_scan_start(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint8_t channel) { @@ -635,6 +636,29 @@ void mac_extended_mac_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const } } +static uint32_t mac_calc_ack_wait_duration(protocol_interface_rf_mac_setup_s *rf_mac_setup, uint16_t symbols) +{ + uint32_t AckWaitDuration = 0; + if (rf_mac_setup->rf_csma_extension_supported) { + AckWaitDuration = symbols * rf_mac_setup->symbol_time_us; + } + return AckWaitDuration; +} + +static int8_t mac_mlme_set_ack_wait_duration(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req) +{ + uint16_t symbols = common_read_16_bit_inverse((uint8_t *)set_req->value_pointer); + uint32_t ack_wait_time_us = mac_calc_ack_wait_duration(rf_mac_setup, symbols); + if (ack_wait_time_us < 50) { + return -1; + } + // MAC timer uses 50us resolution + rf_mac_setup->mac_ack_wait_duration = ack_wait_time_us / 50; + tr_debug("Set macAckWaitDuration: %uus", rf_mac_setup->mac_ack_wait_duration * 50); + + return 0; +} + static int8_t mac_mlme_device_description_set(protocol_interface_rf_mac_setup_s *rf_mac_setup, const mlme_set_t *set_req) { @@ -707,6 +731,8 @@ int8_t mac_mlme_set_req(protocol_interface_rf_mac_setup_s *rf_mac_setup, const m } switch (set_req->attr) { + case macAckWaitDuration: + return mac_mlme_set_ack_wait_duration(rf_mac_setup, set_req); case macDeviceTable: return mac_mlme_device_description_set(rf_mac_setup, set_req); case macKeyTable: @@ -723,6 +749,10 @@ 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 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); + return 0; default: return mac_mlme_handle_set_values(rf_mac_setup, set_req); } @@ -1009,6 +1039,17 @@ static uint8_t mac_backoff_ticks_calc(phy_device_driver_s *phy_driver) return (uint8_t) ticks; } +static int mac_mlme_set_symbol_rate(protocol_interface_rf_mac_setup_s *rf_mac_setup) +{ + if (rf_mac_setup->rf_csma_extension_supported) { + rf_mac_setup->dev_driver->phy_driver->extension(PHY_EXTENSION_GET_SYMBOLS_PER_SECOND, (uint8_t *) &rf_mac_setup->symbol_rate); + rf_mac_setup->symbol_time_us = 1000000 / rf_mac_setup->symbol_rate; + tr_debug("SW-MAC driver support rf extension %"PRIu32" symbol/seconds %"PRIu32" us symbol time length", rf_mac_setup->symbol_rate, rf_mac_setup->symbol_time_us); + return 0; + } + return -1; +} + protocol_interface_rf_mac_setup_s *mac_mlme_data_base_allocate(uint8_t *mac64, arm_device_driver_list_s *dev_driver, mac_description_storage_size_t *storage_sizes) { uint16_t total_length = 0; @@ -1099,11 +1140,7 @@ 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; - if (entry->rf_csma_extension_supported) { - entry->dev_driver->phy_driver->extension(PHY_EXTENSION_GET_SYMBOLS_PER_SECOND, (uint8_t *) &entry->symbol_rate); - entry->symbol_time_us = 1000000 / entry->symbol_rate; - tr_debug("SW-MAC driver support rf extension %"PRIu32" symbol/seconds %"PRIu32" us symbol time length", entry->symbol_rate, entry->symbol_time_us); - } + mac_mlme_set_symbol_rate(entry); //How many 10us ticks backoff period is for waiting 20symbols which is typically 10 bytes time entry->backoff_period_in_10us = mac_backoff_ticks_calc(dev_driver->phy_driver); @@ -1710,8 +1747,6 @@ int8_t mac_mlme_beacon_tx(protocol_interface_rf_mac_setup_s *rf_ptr) }*/ } buf->priority = MAC_PD_DATA_HIGH_PRIOTITY; - - tr_debug("BEA tx"); mcps_sap_pd_req_queue_write(rf_ptr, buf); sw_mac_stats_update(rf_ptr, STAT_MAC_BEA_TX_COUNT, 0); return 0; diff --git a/source/MAC/IEEE802_15_4/mac_pd_sap.c b/source/MAC/IEEE802_15_4/mac_pd_sap.c index 5e9dabc58a..0e1a760b7f 100644 --- a/source/MAC/IEEE802_15_4/mac_pd_sap.c +++ b/source/MAC/IEEE802_15_4/mac_pd_sap.c @@ -400,13 +400,6 @@ static int8_t mac_data_interface_tx_done_cb(protocol_interface_rf_mac_setup_s *r return -1; } - if (active_buf->fcf_dsn.frametype == FC_BEACON_FRAME) { - // FHSS synchronization info is written in the end of transmitted (Beacon) buffer - dev_driver_tx_buffer_s *tx_buf = &rf_ptr->dev_driver_tx_buffer; - uint8_t *synch_info = tx_buf->buf + rf_ptr->dev_driver->phy_driver->phy_header_length + tx_buf->len - FHSS_SYNCH_INFO_LENGTH; - rf_ptr->fhss_api->write_synch_info(rf_ptr->fhss_api, synch_info, 0, FHSS_SYNCH_FRAME, 0); - } - // Change to destination channel and write synchronization info to Beacon frames here int tx_handle_retval = rf_ptr->fhss_api->tx_handle(rf_ptr->fhss_api, !mac_is_ack_request_set(active_buf), active_buf->DstAddr, mac_convert_frame_type_to_fhss(active_buf->fcf_dsn.frametype), @@ -760,16 +753,15 @@ int8_t mac_pd_sap_data_cb(void *identifier, arm_phy_sap_msg_t *message) 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; + } - } - //Do not accept commend 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; + //Parse IE Elements + if (!mac_header_information_elements_parse(buffer)) { + goto ERROR_HANDLER; + } } if (!rf_ptr->macProminousMode && buffer->fcf_dsn.frametype == FC_ACK_FRAME) { @@ -777,9 +769,7 @@ int8_t mac_pd_sap_data_cb(void *identifier, arm_phy_sap_msg_t *message) mcps_sap_pre_parsed_frame_buffer_free(buffer); } return 0; - } else { - if (mcps_sap_pd_ind(buffer) == 0) { return 0; } diff --git a/source/MAC/IEEE802_15_4/mac_security_mib.c b/source/MAC/IEEE802_15_4/mac_security_mib.c index 5dc9efc122..2fe2b8fbeb 100644 --- a/source/MAC/IEEE802_15_4/mac_security_mib.c +++ b/source/MAC/IEEE802_15_4/mac_security_mib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/IEEE802_15_4/mac_security_mib.h b/source/MAC/IEEE802_15_4/mac_security_mib.h index 258fd0cf3d..05be16d572 100644 --- a/source/MAC/IEEE802_15_4/mac_security_mib.h +++ b/source/MAC/IEEE802_15_4/mac_security_mib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/ethernet/ethernet_mac_api.c b/source/MAC/ethernet/ethernet_mac_api.c index 6c0413563c..913b141734 100644 --- a/source/MAC/ethernet/ethernet_mac_api.c +++ b/source/MAC/ethernet/ethernet_mac_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/rf_driver_storage.c b/source/MAC/rf_driver_storage.c index bea47ea4a8..6257406c3e 100644 --- a/source/MAC/rf_driver_storage.c +++ b/source/MAC/rf_driver_storage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/rf_driver_storage.h b/source/MAC/rf_driver_storage.h index 0447f98fac..d9a82d9531 100644 --- a/source/MAC/rf_driver_storage.h +++ b/source/MAC/rf_driver_storage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/serial/serial_mac_api.c b/source/MAC/serial/serial_mac_api.c index bf32f60729..469635208c 100644 --- a/source/MAC/serial/serial_mac_api.c +++ b/source/MAC/serial/serial_mac_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/MAC/virtual_rf/virtual_rf_client.c b/source/MAC/virtual_rf/virtual_rf_client.c index c6e4227a52..81395991f3 100644 --- a/source/MAC/virtual_rf/virtual_rf_client.c +++ b/source/MAC/virtual_rf/virtual_rf_client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,7 @@ static phy_device_driver_s device_driver; static int8_t rf_driver_id = (-1); -static const phy_rf_channel_configuration_s phy_2_4ghz = {2405000000, 5000000, 250000, 16, M_OQPSK}; +static const phy_rf_channel_configuration_s phy_2_4ghz = {.channel_0_center_frequency = 2405000000, .channel_spacing = 5000000, .datarate = 250000, .number_of_channels = 16, .modulation = M_OQPSK}; static const phy_device_channel_page_s phy_channel_pages = { CHANNEL_PAGE_0, &phy_2_4ghz}; static int8_t phy_rf_rx(const uint8_t *data_ptr, uint16_t data_len, uint8_t link_quality, int8_t dbm, int8_t driver_id) diff --git a/source/MAC/virtual_rf/virtual_rf_driver.c b/source/MAC/virtual_rf/virtual_rf_driver.c index ac90cf5246..78e8b9ef2c 100644 --- a/source/MAC/virtual_rf/virtual_rf_driver.c +++ b/source/MAC/virtual_rf/virtual_rf_driver.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,15 +38,15 @@ static int8_t rf_driver_id = (-1); static bool data_request_pending_flag = false; /** XXX: dummy values copied from Atmel RF driver */ -static const phy_rf_channel_configuration_s phy_2_4ghz = {2405000000, 5000000, 250000, 16, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_subghz = {868300000, 2000000, 250000, 11, M_OQPSK}; +static const phy_rf_channel_configuration_s phy_2_4ghz = {.channel_0_center_frequency = 2405000000, .channel_spacing = 5000000, .datarate = 250000, .number_of_channels = 16, .modulation = M_OQPSK}; +static const phy_rf_channel_configuration_s phy_subghz = {.channel_0_center_frequency = 868300000, .channel_spacing = 2000000, .datarate = 250000, .number_of_channels = 11, .modulation = M_OQPSK}; -static const phy_rf_channel_configuration_s phy_subghz_8_ch = {868300000, 2000000, 250000, 8, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_subghz_11_ch = {868300000, 2000000, 250000, 11, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_subghz_16_ch = {868300000, 2000000, 250000, 16, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_2_4ghz_14_ch = {2405000000, 1000000, 250000, 14, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_2_4ghz_5_ch = {2405000000, 1000000, 250000, 5, M_OQPSK}; //For FHSS testing only -static const phy_rf_channel_configuration_s phy_2_4ghz_256_ch = {2405000000, 1000000, 250000, 256, M_OQPSK}; //For FHSS testing only +static const phy_rf_channel_configuration_s phy_subghz_8_ch = {.channel_0_center_frequency = 868300000, .channel_spacing = 2000000, .datarate = 250000, .number_of_channels = 8, .modulation = M_OQPSK}; +static const phy_rf_channel_configuration_s phy_subghz_11_ch = {.channel_0_center_frequency = 868300000, .channel_spacing = 2000000, .datarate = 250000, .number_of_channels = 11, .modulation = M_OQPSK}; +static const phy_rf_channel_configuration_s phy_subghz_16_ch = {.channel_0_center_frequency = 868300000, .channel_spacing = 2000000, .datarate = 250000, .number_of_channels = 16, .modulation = M_OQPSK}; +static const phy_rf_channel_configuration_s phy_2_4ghz_14_ch = {.channel_0_center_frequency = 2405000000, .channel_spacing = 1000000, .datarate = 250000, .number_of_channels = 14, .modulation = M_OQPSK}; +static const phy_rf_channel_configuration_s phy_2_4ghz_5_ch = {.channel_0_center_frequency = 2405000000, .channel_spacing = 1000000, .datarate = 250000, .number_of_channels = 5, .modulation = M_OQPSK}; //For FHSS testing only +static const phy_rf_channel_configuration_s phy_2_4ghz_256_ch = {.channel_0_center_frequency = 2405000000, .channel_spacing = 1000000, .datarate = 250000, .number_of_channels = 256, .modulation = M_OQPSK}; //For FHSS testing only static phy_device_channel_page_s phy_channel_pages[] = { {CHANNEL_PAGE_0, &phy_2_4ghz}, // this will be modified to contain 11 or 16 channels, depending on radio type diff --git a/source/MLE/mle.c b/source/MLE/mle.c index 87644b2ef5..8c24f6b0b1 100644 --- a/source/MLE/mle.c +++ b/source/MLE/mle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "eventOS_event.h" #include "eventOS_event_timer.h" #include "socket_api.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "nsdynmemLIB.h" #include "ns_trace.h" #include "string.h" diff --git a/source/MLE/mle.h b/source/MLE/mle.h index a842556956..7d4389b433 100644 --- a/source/MLE/mle.h +++ b/source/MLE/mle.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #define MLE_H_ #include "nsconfig.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "ns_list.h" struct buffer; diff --git a/source/MLE/mle_tlv.c b/source/MLE/mle_tlv.c index 383ee64514..284fb435ff 100644 --- a/source/MLE/mle_tlv.c +++ b/source/MLE/mle_tlv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/NWK_INTERFACE/Include/protocol.h b/source/NWK_INTERFACE/Include/protocol.h index 18e12b5e99..b61bcbd3fa 100644 --- a/source/NWK_INTERFACE/Include/protocol.h +++ b/source/NWK_INTERFACE/Include/protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +33,7 @@ #include "NWK_INTERFACE/Include/protocol_abstract.h" // Users of protocol.h can assume it includes these headers -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Core/include/ns_buffer.h" // Headers below this are implementation details - users of protocol.h shouldn't rely on them diff --git a/source/NWK_INTERFACE/protocol_core.c b/source/NWK_INTERFACE/protocol_core.c index c7a55e0107..b4225aa1c9 100644 --- a/source/NWK_INTERFACE/protocol_core.c +++ b/source/NWK_INTERFACE/protocol_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "eventOS_callback_timer.h" #include "ns_trace.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_timer.h" #include "platform/arm_hal_interrupt.h" @@ -66,6 +66,9 @@ #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/ws/ws_bootstrap.h" #include "6LoWPAN/ws/ws_common.h" +#ifdef HAVE_WS +#include "6LoWPAN/ws/ws_pae_controller.h" +#endif #include "ipv6_stack/protocol_ipv6.h" #include "Service_Libs/whiteboard/whiteboard.h" @@ -335,6 +338,9 @@ void core_timer_event_handle(uint16_t ticksUpdate) rpl_control_fast_timer(ticksUpdate); icmpv6_radv_timer(ticksUpdate); protocol_core_security_tick_update(ticksUpdate); +#ifdef HAVE_WS + ws_pae_controller_timer(ticksUpdate); +#endif platform_enter_critical(); protocol_core_timer_info.core_timer_event = false; platform_exit_critical(); @@ -778,7 +784,7 @@ protocol_interface_info_entry_t *protocol_stack_interface_info_get_by_fhss_api(c { #ifdef HAVE_WS ns_list_foreach(protocol_interface_info_entry_t, cur, &protocol_interface_info_list) { - if (cur->ws_info->fhss_api == fhss_api) { + if (cur->ws_info && (cur->ws_info->fhss_api == fhss_api)) { return cur; } } diff --git a/source/NWK_INTERFACE/protocol_timer.c b/source/NWK_INTERFACE/protocol_timer.c index e53b199ece..6997849ee7 100644 --- a/source/NWK_INTERFACE/protocol_timer.c +++ b/source/NWK_INTERFACE/protocol_timer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/RPL/rpl_control.c b/source/RPL/rpl_control.c index 6c5650b419..91f54154d3 100644 --- a/source/RPL/rpl_control.c +++ b/source/RPL/rpl_control.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -227,6 +227,25 @@ void rpl_control_address_register_done(struct buffer *buf, uint8_t status) } } +bool rpl_control_is_dodag_parent(protocol_interface_info_entry_t *interface, const uint8_t ll_addr[16]) +{ + // go through instances and parents and check if they match the address. + ns_list_foreach(struct rpl_instance, instance, &interface->rpl_domain->instances) { + if (rpl_instance_address_is_parent(instance, ll_addr)) { + return true; + } + } + return false; +} +void rpl_control_neighbor_delete(protocol_interface_info_entry_t *interface, const uint8_t ll_addr[16]) +{ + // go through instances and delete address. + ns_list_foreach(struct rpl_instance, instance, &interface->rpl_domain->instances) { + rpl_instance_neighbor_delete(instance, ll_addr); + } + return; +} + /* Address changes need to trigger DAO target re-evaluation */ static void rpl_control_addr_notifier(struct protocol_interface_info_entry *interface, const if_address_entry_t *addr, if_address_callback_t reason) { @@ -489,6 +508,14 @@ void rpl_control_increment_dodag_version(rpl_dodag_t *dodag) rpl_dodag_set_version_number_as_root(dodag, new_version); } } +void rpl_control_update_dodag_config(struct rpl_dodag *dodag, const rpl_dodag_conf_t *conf) +{ + + if (rpl_dodag_am_root(dodag)) { + rpl_dodag_update_config(dodag, conf, NULL, NULL); + } +} + void rpl_control_set_dodag_pref(rpl_dodag_t *dodag, uint8_t pref) { diff --git a/source/RPL/rpl_control.h b/source/RPL/rpl_control.h index 100f5d87a2..7e9f123e87 100644 --- a/source/RPL/rpl_control.h +++ b/source/RPL/rpl_control.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -127,6 +127,7 @@ void rpl_control_delete_dodag_root(rpl_domain_t *domain, struct rpl_dodag *dodag void rpl_control_update_dodag_route(struct rpl_dodag *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, bool age); void rpl_control_update_dodag_prefix(struct rpl_dodag *dodag, const uint8_t *prefix, uint8_t prefix_len, uint8_t flags, uint32_t lifetime, uint32_t preftime, bool age); void rpl_control_increment_dodag_version(struct rpl_dodag *dodag); +void rpl_control_update_dodag_config(struct rpl_dodag *dodag, const rpl_dodag_conf_t *conf); void rpl_control_set_dodag_pref(struct rpl_dodag *dodag, uint8_t pref); void rpl_control_increment_dtsn(struct rpl_dodag *dodag); @@ -148,6 +149,8 @@ void rpl_control_publish_host_address(rpl_domain_t *domain, const uint8_t addr[1 void rpl_control_unpublish_address(rpl_domain_t *domain, const uint8_t addr[16]); void rpl_control_register_address(struct protocol_interface_info_entry *interface, if_address_entry_t *addr); void rpl_control_address_register_done(struct buffer *buf, uint8_t status); +bool rpl_control_is_dodag_parent(struct protocol_interface_info_entry *interface, const uint8_t ll_addr[16]); +void rpl_control_neighbor_delete(struct protocol_interface_info_entry *interface, const uint8_t ll_addr[16]); /* Configure and return the routing lookup predicate for a specified RPL instance ID */ ipv6_route_predicate_fn_t *rpl_control_get_route_predicate(rpl_domain_t *domain, uint8_t instance_id, const uint8_t src[16], const uint8_t dst[16]); diff --git a/source/RPL/rpl_downward.c b/source/RPL/rpl_downward.c index fa922e9edc..5f4c9f316a 100644 --- a/source/RPL/rpl_downward.c +++ b/source/RPL/rpl_downward.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -625,6 +625,7 @@ bool rpl_instance_address_registration_done(protocol_interface_info_entry_t *int addr->state_timer = (addr->preferred_lifetime * randLIB_get_random_in_range(75, 85) / 10); } else { tr_error("Address registration failed"); + rpl_delete_neighbour(instance, neighbour); } /* If that was last one to reply, send next one. */ diff --git a/source/RPL/rpl_mrhof.c b/source/RPL/rpl_mrhof.c index 9a34b02086..ea52ee2507 100644 --- a/source/RPL/rpl_mrhof.c +++ b/source/RPL/rpl_mrhof.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/RPL/rpl_objective.h b/source/RPL/rpl_objective.h index cf02a92b36..67c35fd7c8 100644 --- a/source/RPL/rpl_objective.h +++ b/source/RPL/rpl_objective.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Arm Limited and affiliates. + * Copyright (c) 2015, 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/RPL/rpl_of0.c b/source/RPL/rpl_of0.c index 8a93ec4bb2..aa6927c1f1 100644 --- a/source/RPL/rpl_of0.c +++ b/source/RPL/rpl_of0.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/RPL/rpl_policy.c b/source/RPL/rpl_policy.c index f72df852e9..f31a51ec13 100644 --- a/source/RPL/rpl_policy.c +++ b/source/RPL/rpl_policy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ #include "net_interface.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Service_Libs/etx/etx.h" #include "Common_Protocols/ipv6_resolution.h" #include "ipv6_stack/ipv6_routing_table.h" diff --git a/source/RPL/rpl_policy.h b/source/RPL/rpl_policy.h index 61b4034591..469656f474 100644 --- a/source/RPL/rpl_policy.h +++ b/source/RPL/rpl_policy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ #ifndef RPL_POLICY_H_ #define RPL_POLICY_H_ -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "rpl_control.h" bool rpl_policy_join_instance(rpl_domain_t *domain, uint8_t instance_id, const uint8_t *dodagid); diff --git a/source/RPL/rpl_structures.h b/source/RPL/rpl_structures.h index a98d41039e..fac901ccd4 100644 --- a/source/RPL/rpl_structures.h +++ b/source/RPL/rpl_structures.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/RPL/rpl_upward.c b/source/RPL/rpl_upward.c index 96481e5ede..5990fbdf6a 100644 --- a/source/RPL/rpl_upward.c +++ b/source/RPL/rpl_upward.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,7 +38,7 @@ #include "net_interface.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Common_Protocols/icmpv6.h" #include "Common_Protocols/icmpv6_prefix.h" #include "NWK_INTERFACE/Include/protocol_abstract.h" @@ -1564,6 +1564,29 @@ uint16_t rpl_instance_current_rank(const rpl_instance_t *instance) return instance->current_rank; } +bool rpl_instance_address_is_parent(rpl_instance_t *instance, const uint8_t *ipv6_addr) +{ + ns_list_foreach(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { + if (neighbour->dodag_parent && addr_ipv6_equal(neighbour->ll_address, ipv6_addr)) { + return true; + } + if (!neighbour->dodag_parent) { + // list is ordered so first encounter of false means no more parents in list + return false; + } + } + return false; +} +void rpl_instance_neighbor_delete(rpl_instance_t *instance, const uint8_t *ipv6_addr) +{ + ns_list_foreach_safe(rpl_neighbour_t, neighbour, &instance->candidate_neighbours) { + if (addr_ipv6_equal(neighbour->ll_address, ipv6_addr)) { + rpl_delete_neighbour(instance, neighbour); + } + } + return; +} + void rpl_instance_slow_timer(rpl_instance_t *instance, uint16_t seconds) { ns_list_foreach(rpl_dodag_t, dodag, &instance->dodags) { diff --git a/source/RPL/rpl_upward.h b/source/RPL/rpl_upward.h index 1225e28549..5c626fa10a 100644 --- a/source/RPL/rpl_upward.h +++ b/source/RPL/rpl_upward.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -83,6 +83,8 @@ void rpl_instance_dio_trigger(rpl_instance_t *instance, struct protocol_interfac void rpl_instance_set_local_repair(rpl_instance_t *instance, bool repair); bool rpl_instance_local_repair(const rpl_instance_t *instance); uint16_t rpl_instance_current_rank(const rpl_instance_t *instance); +bool rpl_instance_address_is_parent(rpl_instance_t *instance, const uint8_t *ipv6_addr); +void rpl_instance_neighbor_delete(rpl_instance_t *instance, const uint8_t *ipv6_addr); void rpl_instance_slow_timer(rpl_instance_t *instance, uint16_t seconds); rpl_dodag_t *rpl_lookup_dodag(const rpl_instance_t *instance, const uint8_t *dodagid); diff --git a/source/Security/Common/sec_lib.h b/source/Security/Common/sec_lib.h index 30c5d46090..a1601e7fcc 100644 --- a/source/Security/Common/sec_lib.h +++ b/source/Security/Common/sec_lib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/Common/sec_lib_definitions.h b/source/Security/Common/sec_lib_definitions.h index 3be681c36f..d52883e19c 100644 --- a/source/Security/Common/sec_lib_definitions.h +++ b/source/Security/Common/sec_lib_definitions.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/Common/security_lib.c b/source/Security/Common/security_lib.c index 67e163ab7a..ff3d31a0be 100644 --- a/source/Security/Common/security_lib.c +++ b/source/Security/Common/security_lib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "string.h" #include "eventOS_event.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "shalib.h" #include "randLIB.h" diff --git a/source/Security/PANA/eap_protocol.c b/source/Security/PANA/eap_protocol.c index 37fd10b266..8de694d335 100644 --- a/source/Security/PANA/eap_protocol.c +++ b/source/Security/PANA/eap_protocol.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "eventOS_event.h" #include "ns_trace.h" #include "string.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan_bootstrap.h" diff --git a/source/Security/PANA/pana.c b/source/Security/PANA/pana.c index 5ae36b7c34..f09eca992b 100644 --- a/source/Security/PANA/pana.c +++ b/source/Security/PANA/pana.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "string.h" #include "randLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "ccmLIB.h" #include "shalib.h" diff --git a/source/Security/PANA/pana.h b/source/Security/PANA/pana.h index 94b09392b8..a8b3f37256 100644 --- a/source/Security/PANA/pana.h +++ b/source/Security/PANA/pana.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/PANA/pana_avp.c b/source/Security/PANA/pana_avp.c index 6962abafa7..f9f32dbaa8 100644 --- a/source/Security/PANA/pana_avp.c +++ b/source/Security/PANA/pana_avp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/PANA/pana_avp.h b/source/Security/PANA/pana_avp.h index 2aece9bd2f..5ee046c929 100644 --- a/source/Security/PANA/pana_avp.h +++ b/source/Security/PANA/pana_avp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/PANA/pana_client.c b/source/Security/PANA/pana_client.c index 52803fe8d1..d724e28539 100644 --- a/source/Security/PANA/pana_client.c +++ b/source/Security/PANA/pana_client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "string.h" #include "randLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "ccmLIB.h" #include "shalib.h" diff --git a/source/Security/PANA/pana_eap_header.c b/source/Security/PANA/pana_eap_header.c index 99cda0ffa0..8d9ae32728 100644 --- a/source/Security/PANA/pana_eap_header.c +++ b/source/Security/PANA/pana_eap_header.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #include "string.h" #include "common_functions.h" #include "Security/PANA/pana_eap_header.h" -#ifdef PANA +#if defined(PANA) || defined(HAVE_WS) bool eap_header_parse(uint8_t *data_ptr, uint16_t length, eap_header_t *header) { diff --git a/source/Security/PANA/pana_eap_header.h b/source/Security/PANA/pana_eap_header.h index 44dfa6f659..2498134433 100644 --- a/source/Security/PANA/pana_eap_header.h +++ b/source/Security/PANA/pana_eap_header.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -52,7 +52,7 @@ * */ -typedef struct { +typedef struct eap_header { uint16_t length; uint8_t eap_code; uint8_t id_seq; @@ -60,7 +60,7 @@ typedef struct { uint8_t *data_ptr; } eap_header_t; -typedef struct { +typedef struct eap_tls_header { uint8_t *data_ptr; uint8_t eap_tls_flags; uint16_t tls_frame_length; diff --git a/source/Security/PANA/pana_internal_api.h b/source/Security/PANA/pana_internal_api.h index 9ca385bf03..927f7ee18a 100644 --- a/source/Security/PANA/pana_internal_api.h +++ b/source/Security/PANA/pana_internal_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/PANA/pana_server.c b/source/Security/PANA/pana_server.c index 5fbe45c937..e2b863e8f7 100644 --- a/source/Security/PANA/pana_server.c +++ b/source/Security/PANA/pana_server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "string.h" #include "randLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "ccmLIB.h" #include "shalib.h" @@ -1742,6 +1742,8 @@ int8_t pana_server_restore_from_nvm(uint8_t *nvm_data, int8_t interface_id) int8_t pana_server_nvm_callback_set(pana_server_update_cb *update_cb, pana_server_session_get_cb *nvm_get, pana_server_session_get_by_id_cb *nvm_session_get, uint8_t *nvm_static_buffer) { (void)update_cb; + (void)nvm_get; + (void)nvm_session_get; (void)nvm_static_buffer; return -1; } diff --git a/source/Security/TLS/tls_ccm_crypt.h b/source/Security/TLS/tls_ccm_crypt.h index d845eae5e7..b82de08207 100644 --- a/source/Security/TLS/tls_ccm_crypt.h +++ b/source/Security/TLS/tls_ccm_crypt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/TLS/tls_lib.c b/source/Security/TLS/tls_lib.c index e4b438d132..99c206df68 100644 --- a/source/Security/TLS/tls_lib.c +++ b/source/Security/TLS/tls_lib.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,14 +18,14 @@ #include "ns_types.h" #ifdef PANA #include "string.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Core/include/ns_buffer.h" #ifdef ECC #include "libX509_V3.h" #include "ecc.h" #endif #include "randLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" //#include "6LoWPAN/Bootstraps/network_lib.h" #include "shalib.h" #include "Security/TLS/tls_lib.h" diff --git a/source/Security/TLS/tls_lib.h b/source/Security/TLS/tls_lib.h index ce07760455..c369e20635 100644 --- a/source/Security/TLS/tls_lib.h +++ b/source/Security/TLS/tls_lib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Security/eapol/eapol_helper.c b/source/Security/eapol/eapol_helper.c new file mode 100644 index 0000000000..64a45e3935 --- /dev/null +++ b/source/Security/eapol/eapol_helper.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018-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. + */ + + + +#include "nsconfig.h" + +#include "ns_types.h" +#include "eventOS_event.h" +#include "ns_trace.h" +#include "string.h" +#include "common_functions.h" +#include "Security/PANA/pana_eap_header.h" +#include "Security/eapol/eapol_helper.h" + +#ifdef HAVE_WS + +#define KEY_INFO_VERSION_BIT_MASK 0x0007 +#define KEY_INFO_VERSION_BIT_SHIFT 0 +#define KEY_INFO_KEY_TYPE_BIT_MASK 0x0008 +#define KEY_INFO_KEY_TYPE_BIT_SHIFT 3 +#define KEY_INFO_INSTALL_BIT_MASK 0x0040 +#define KEY_INFO_INSTALL_BIT_SHIFT 6 +#define KEY_INFO_ACK_BIT_MASK 0x0080 +#define KEY_INFO_ACK_BIT_SHIFT 7 +#define KEY_INFO_MIC_MASK 0x0100 +#define KEY_INFO_MIC_SHIFT 8 +#define KEY_INFO_SECURE_MASK 0x0200 +#define KEY_INFO_SECURE_SHIFT 9 +#define KEY_INFO_ERROR_MASK 0x0400 +#define KEY_INFO_ERROR_SHIFT 10 +#define KEY_INFO_REQUEST_MASK 0x0800 +#define KEY_INFO_REQUEST_SHIFT 11 +#define KEY_INFO_ENC_KEY_DATA_MASK 0x1000 +#define KEY_INFO_ENC_KEY_DATA_SHIFT 12 +#define KEY_INFO_SMK_MASK 0x2000 +#define KEY_INFO_SMK_SHIFT 13 + +static uint8_t *eapol_key_information_write(eapol_key_information_t *key_information, uint8_t *ptr) +{ + uint16_t key_info = 0; + key_info |= (key_information->description_version << KEY_INFO_VERSION_BIT_SHIFT); + key_info |= (key_information->pairwise_key << KEY_INFO_KEY_TYPE_BIT_SHIFT); + key_info |= (key_information->install << KEY_INFO_INSTALL_BIT_SHIFT); + key_info |= (key_information->key_ack << KEY_INFO_ACK_BIT_SHIFT); + key_info |= (key_information->key_mic << KEY_INFO_MIC_SHIFT); + key_info |= (key_information->secured_key_frame << KEY_INFO_SECURE_SHIFT); + key_info |= (key_information->error << KEY_INFO_ERROR_SHIFT); + key_info |= (key_information->request << KEY_INFO_REQUEST_SHIFT); + key_info |= (key_information->encrypted_key_data << KEY_INFO_ENC_KEY_DATA_SHIFT); + key_info |= (key_information->smk_handshake << KEY_INFO_SMK_SHIFT); + return common_write_16_bit(key_info, ptr); +} + +static uint8_t *eapol_key_information_read(eapol_key_information_t *key_information, uint8_t *ptr) +{ + uint16_t key_info = common_read_16_bit(ptr); + key_information->description_version = ((key_info & KEY_INFO_VERSION_BIT_MASK) >> KEY_INFO_VERSION_BIT_SHIFT); + key_information->pairwise_key = ((key_info & KEY_INFO_KEY_TYPE_BIT_MASK) >> KEY_INFO_KEY_TYPE_BIT_SHIFT); + key_information->install = ((key_info & KEY_INFO_INSTALL_BIT_MASK) >> KEY_INFO_INSTALL_BIT_SHIFT); + key_information->key_ack = ((key_info & KEY_INFO_ACK_BIT_MASK) >> KEY_INFO_ACK_BIT_SHIFT); + key_information->key_mic = ((key_info & KEY_INFO_MIC_MASK) >> KEY_INFO_MIC_SHIFT); + key_information->secured_key_frame = ((key_info & KEY_INFO_SECURE_MASK) >> KEY_INFO_SECURE_SHIFT); + key_information->error = ((key_info & KEY_INFO_ERROR_MASK) >> KEY_INFO_ERROR_SHIFT); + key_information->request = ((key_info & KEY_INFO_REQUEST_MASK) >> KEY_INFO_REQUEST_SHIFT); + key_information->encrypted_key_data = ((key_info & KEY_INFO_ENC_KEY_DATA_MASK) >> KEY_INFO_ENC_KEY_DATA_SHIFT); + key_information->smk_handshake = ((key_info & KEY_INFO_SMK_MASK) >> KEY_INFO_SMK_SHIFT); + return ptr + 2; +} + + +static bool eapol_parse_eap_packet(eapol_pdu_t *eapol_pdu) +{ + return eap_header_parse(eapol_pdu->packet_body, eapol_pdu->packet_length, &eapol_pdu->msg.eap); +} + + +static bool eapol_parse_key_packet(eapol_pdu_t *eapol_pdu) +{ + if (eapol_pdu->packet_length < EAPOL_KEY_FRAME_BASE_SIZE) { + return false; + } + uint8_t *ptr = eapol_pdu->packet_body; + eapol_key_frame_t *key_frame = &eapol_pdu->msg.key; + key_frame->key_description = *ptr++; + if (key_frame->key_description != EAPOL_RSN_KEY_DESCRIPTION) { + return false; + } + ptr = eapol_key_information_read(&key_frame->key_information, ptr); + if (key_frame->key_information.description_version != KEY_DESCRIPTION_HMAC_SHA1_MIC_AES_ENC) { + return false; + } + key_frame->key_length = common_read_16_bit(ptr); + ptr += 2; + + key_frame->replay_counter = common_read_64_bit(ptr); + ptr += 8; + + key_frame->key_nonce = ptr; + ptr += 32; + + key_frame->key_iv = ptr; + ptr += 16; + + key_frame->key_rsc = ptr; + ptr += 16; //Skip 8 byte RSC + RESERVED 8 + + key_frame->key_mic = ptr; + ptr += 16; + + key_frame->key_data_length = common_read_16_bit(ptr); + ptr += 2; + key_frame->key_data = ptr; + if (key_frame->key_data_length > (eapol_pdu->packet_length - EAPOL_KEY_FRAME_BASE_SIZE)) { + return false; + } + + return true; + +} + +void eapol_write_key_packet_mic(uint8_t *eapol_pdu, uint8_t *mic) +{ + if (mic) { + memcpy(&eapol_pdu[81], mic, 16); + } else { + memset(&eapol_pdu[81], 0, 16); + } +} + +bool eapol_parse_pdu_header(uint8_t *ptr, uint16_t data_length, eapol_pdu_t *eapol_pdu) +{ + //Validate MIN length + if (data_length < EAPOL_BASE_LENGTH) { + return false; + } + //Validate Protocol version + uint8_t protocol = *ptr++; + if (protocol != EAPOL_PROTOCOL_VERSION) { + return false; + } + eapol_pdu->packet_type = *ptr++; + eapol_pdu->packet_length = common_read_16_bit(ptr); + ptr += 2; + //Validate Body Length + if (eapol_pdu->packet_length > data_length - EAPOL_BASE_LENGTH) { + return false; + } + eapol_pdu->packet_body = ptr; + + if (eapol_pdu->packet_type == EAPOL_EAP_TYPE) { + return eapol_parse_eap_packet(eapol_pdu); + } else if (eapol_pdu->packet_type == EAPOL_KEY_TYPE) { + return eapol_parse_key_packet(eapol_pdu); + } else { + return false; + } + +} + +uint8_t *eapol_write_pdu_frame(uint8_t *ptr, eapol_pdu_t *eapol_pdu) +{ + *ptr++ = EAPOL_PROTOCOL_VERSION; + *ptr++ = eapol_pdu->packet_type; + ptr = common_write_16_bit(eapol_pdu->packet_length, ptr); + eapol_pdu->packet_body = ptr; + + if (eapol_pdu->packet_type == EAPOL_EAP_TYPE) { + eap_header_t *eap_header = &eapol_pdu->msg.eap; + ptr = eap_header_build(ptr, eap_header->length, eap_header->eap_code, eap_header->id_seq, eap_header->type); + memcpy(ptr, eap_header->data_ptr, eap_header->length - (ptr - eapol_pdu->packet_body)); + ptr += eap_header->length - (ptr - eapol_pdu->packet_body); + + } else if (eapol_pdu->packet_type == EAPOL_KEY_TYPE) { + eapol_key_frame_t *key_frame = &eapol_pdu->msg.key; + *ptr++ = key_frame->key_description; + ptr = eapol_key_information_write(&key_frame->key_information, ptr); + ptr = common_write_16_bit(key_frame->key_length, ptr); + ptr = common_write_64_bit(key_frame->replay_counter, ptr); + + if (key_frame->key_nonce) { + memcpy(ptr, key_frame->key_nonce, 32); + } else { + memset(ptr, 0, 32); + } + ptr += 32; + + if (key_frame->key_iv) { + memcpy(ptr, key_frame->key_iv, 16); + } else { + memset(ptr, 0, 16); + } + ptr += 16; + + if (key_frame->key_rsc) { + memcpy(ptr, key_frame->key_rsc, 8); + } else { + memset(ptr, 0, 8); + } + ptr += 8; + + //Reserved 8bytes + memset(ptr, 0, 8); + ptr += 8; + + if (key_frame->key_mic && key_frame->key_information.key_mic) { + memcpy(ptr, key_frame->key_mic, 16); + } else { + memset(ptr, 0, 16); + } + ptr += 16; + ptr = common_write_16_bit(key_frame->key_data_length, ptr); + if (key_frame->key_data_length && key_frame->key_data) { + memcpy(ptr, key_frame->key_data, key_frame->key_data_length); + ptr += key_frame->key_data_length; + } + } + + return ptr; +} + + + +uint16_t eapol_pdu_eap_frame_init(eapol_pdu_t *eapol_pdu, uint8_t eap_code, uint8_t id_seq, uint8_t type, uint16_t data_length, uint8_t *data_ptr) +{ + memset(eapol_pdu, 0, sizeof(eapol_pdu_t)); + + eapol_pdu->packet_type = EAPOL_EAP_TYPE; + eapol_pdu->packet_length = data_length; + eapol_pdu->msg.eap.eap_code = eap_code; + eapol_pdu->msg.eap.data_ptr = data_ptr; + eapol_pdu->msg.eap.length = data_length; + eapol_pdu->msg.eap.id_seq = id_seq; + eapol_pdu->msg.eap.type = type; + + if (eap_code == EAP_REQ || eap_code == EAP_RESPONSE) { + eapol_pdu->packet_body += 5; + eapol_pdu->msg.eap.length++; // Add space for type + eapol_pdu->packet_length++; + } else { + eapol_pdu->packet_body += 4; + } + + return eapol_pdu_total_length(eapol_pdu); + +} + +uint16_t eapol_pdu_key_frame_init(eapol_pdu_t *eapol_pdu, uint16_t data_length, uint8_t *data_ptr) +{ + memset(eapol_pdu, 0, sizeof(eapol_pdu_t)); + + eapol_pdu->packet_type = EAPOL_KEY_TYPE; + eapol_pdu->packet_length = data_length + EAPOL_KEY_FRAME_BASE_SIZE; + eapol_pdu->msg.key.key_data = data_ptr; + eapol_pdu->msg.key.key_description = EAPOL_RSN_KEY_DESCRIPTION; + eapol_pdu->msg.key.key_data_length = data_length; + eapol_pdu->msg.key.key_information.description_version = KEY_DESCRIPTION_HMAC_SHA1_MIC_AES_ENC; + + return eapol_pdu_total_length(eapol_pdu); + +} + +#endif + diff --git a/source/Security/eapol/eapol_helper.h b/source/Security/eapol/eapol_helper.h new file mode 100644 index 0000000000..47439f1f79 --- /dev/null +++ b/source/Security/eapol/eapol_helper.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-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 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_BASE_LENGTH 4 //Protocol version 1 byte, Packet type 1 byte, packet length 2 byte + +#define EAPOL_KEY_FRAME_BASE_SIZE 95 + +struct eap_header_t; + +typedef struct eapol_key_information { + unsigned description_version: 3; + bool pairwise_key: 1; + bool install: 1; + bool key_ack: 1; + bool key_mic: 1; + bool secured_key_frame: 1; + bool error: 1; + bool request: 1; + bool encrypted_key_data: 1; + bool smk_handshake: 1; +} eapol_key_information_t; + +typedef struct eapol_key_frame { + uint8_t key_description; + eapol_key_information_t key_information; + uint16_t key_length; + uint64_t replay_counter; + uint8_t *key_nonce; /*< Write operation: NULL memset are by 0, otherwise write data */ + uint8_t *key_iv; /*< Write operation: NULL memset are by 0, otherwise write data */ + uint8_t *key_rsc; /*< Write operation: NULL memset are by 0, otherwise write data */ + uint8_t *key_mic; /*< Write operation: NULL memset are by 0, otherwise write data */ + uint16_t key_data_length; + uint8_t *key_data; +} eapol_key_frame_t; + +typedef struct eapol_pdu { + uint8_t packet_type; /*< EAPOL_EAP_TYPE or EAPOL_KEY_TYPE */ + uint16_t packet_length; /*< EAPOL Total length includin full packet body and data */ + uint8_t *packet_body; /*< Data pointer to packet body*/ + union { + eapol_key_frame_t key; + struct eap_header eap; + } msg; +} eapol_pdu_t; + +#define EAPOL_RSN_KEY_DESCRIPTION 2 +#define KEY_DESCRIPTION_HMAC_MD5_MIC_ARC4_ENC 1 +#define KEY_DESCRIPTION_HMAC_SHA1_MIC_AES_ENC 2 +#define KEY_DESCRIPTION_AES_128_CMAC_MIC_AES_ENC 3 + +/** + * Helper macro to get full message length + */ +#define eapol_pdu_total_length(x) (x->packet_length + EAPOL_BASE_LENGTH) + +/** + * Helper macro to message start + */ +#define eapol_pdu_msg_start(x) (x->packet_body - EAPOL_BASE_LENGTH) + +/** + * Parse EAPOL message to EAPOL-pdu frame + * + * \return true when message is valid and supported otherwise return false + */ +bool eapol_parse_pdu_header(uint8_t *ptr, uint16_t data_length, eapol_pdu_t *eapol_pdu); + +uint8_t *eapol_write_pdu_frame(uint8_t *ptr, eapol_pdu_t *eapol_pdu); + +uint16_t eapol_pdu_eap_frame_init(eapol_pdu_t *eapol_pdu, uint8_t eap_code, uint8_t id_seq, uint8_t type, uint16_t data_length, uint8_t *data_ptr); + +uint16_t eapol_pdu_key_frame_init(eapol_pdu_t *eapol_pdu, uint16_t data_length, uint8_t *data_ptr); + +void eapol_write_key_packet_mic(uint8_t *eapol_pdu, uint8_t *mic); + +#endif /* EAPOL_HELPER_H_ */ diff --git a/source/Security/eapol/kde_helper.c b/source/Security/eapol/kde_helper.c new file mode 100644 index 0000000000..042c42a649 --- /dev/null +++ b/source/Security/eapol/kde_helper.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2018-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. + */ + +#include "nsconfig.h" +#include "ns_types.h" +#include "eventOS_event.h" +#include "ns_trace.h" +#include "string.h" +#include "common_functions.h" +#include "Security/eapol/kde_helper.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "kdeh" + +#define IEEE_802_11_OUI ieee_802_11_oui +#define WISUN_OUI wisun_oui + +const uint8_t ieee_802_11_oui[3] = {0x00, 0x0F, 0xAC}; +const uint8_t wisun_oui[3] = {0x0C, 0x5A, 0x9E}; + +#define KDE_TYPE 0xdd + +// IEEE 802.11 +#define KDE_GTK 0x01 +#define KDE_PMKID 0x04 +#define KDE_LIFETIME 0x07 +// Wi-Sun +#define KDE_PTKID 0x01 +#define KDE_GTKL 0x02 + +#define GTK_LEN 16 +#define PMKID_LEN 16 +#define PTKID_LEN 16 + +#define KDE_MIN_LEN 6 +#define KDE_FIXED_LEN 2 + +#define KDE_PADDED_MIN_LEN 16 + +#define KDE_TYPE_INDEX 0 +#define KDE_LENGTH_INDEX 1 +#define KDE_OUI1_INDEX 2 +#define KDE_OUI2_INDEX 3 +#define KDE_OUI3_INDEX 4 +#define KDE_DATA_TYPE_INDEX 5 +#define KDE_DATA_INDEX 6 + +/* + From IEEE 802.11 chapter 11.6.2 EAPOL-Key frames + + If the Encrypted Key Data subfield (of the Key Information field) is 1, + the entire Key Data field shall be encrypted. If the Key Data field + uses the NIST AES key wrap, then the Key Data field shall be padded before + encrypting if the key data length is less than 16 octets or if it is not + a multiple of 8. + The padding consists of appending a single octet 0xdd followed by zero + or more 0x00 octets. When processing a received EAPOL-Key message, the + receiver shall ignore this trailing padding. +*/ +uint16_t kde_padded_length_calc(uint16_t kde_length) +{ + if (kde_length < KDE_PADDED_MIN_LEN) { + return KDE_PADDED_MIN_LEN; + } + + return ((kde_length + 7) / 8) * 8; +} + +void kde_padding_write(uint8_t *start_ptr, uint8_t *end_ptr) +{ + uint8_t padding = 0xdd; + + while (start_ptr < end_ptr) { + *start_ptr++ = padding; + padding = 0x00; + } +} + +static uint8_t *kde_header_write(uint8_t *ptr, const uint8_t *oui, uint8_t data_type, uint8_t data_length) +{ + *ptr++ = KDE_TYPE; + *ptr++ = data_length - KDE_FIXED_LEN; + *ptr++ = oui[0]; + *ptr++ = oui[1]; + *ptr++ = oui[2]; + *ptr++ = data_type; + + return ptr; +} + +static const uint8_t *kde_search(const uint8_t *ptr, uint16_t len, const uint8_t *oui, uint8_t data_type, uint16_t kde_min_len) +{ + while (len >= KDE_MIN_LEN) { + uint16_t kde_len = ptr[1] + KDE_FIXED_LEN; + + // Type shall be 0xdd and length shall be at least 6 */ + if (ptr[KDE_TYPE_INDEX] != 0xdd || kde_len < KDE_MIN_LEN || kde_len > len) { + return NULL; + } + + if (kde_len >= kde_min_len && ptr[KDE_OUI1_INDEX] == oui[0] && ptr[KDE_OUI2_INDEX] == oui[1] + && ptr[KDE_OUI3_INDEX] == oui[2] && ptr[KDE_DATA_TYPE_INDEX] == data_type) { + return &ptr[KDE_DATA_INDEX]; + } + + if (len > kde_len) { + len -= kde_len; + ptr += kde_len; + } else { + return NULL; + } + } + + return NULL; +} + +uint8_t *kde_gtk_write(uint8_t *ptr, uint8_t key_id, const uint8_t *gtk) +{ + ptr = kde_header_write(ptr, IEEE_802_11_OUI, KDE_GTK, KDE_GTK_LEN); + + // bits 0-1: keyid (0,1,2, or 3), bit 2: Tx, other: reserved (0) + *ptr++ = key_id; + *ptr++ = 0x00; // reserved + memcpy(ptr, gtk, GTK_LEN); + ptr += GTK_LEN; + + return ptr; +} + +uint8_t *kde_pmkid_write(uint8_t *ptr, const uint8_t *pmkid) +{ + ptr = kde_header_write(ptr, IEEE_802_11_OUI, KDE_PMKID, KDE_PMKID_LEN); + + memcpy(ptr, pmkid, PMKID_LEN); + ptr += PMKID_LEN; + + return ptr; +} + +uint8_t *kde_ptkid_write(uint8_t *ptr, const uint8_t *ptkid) +{ + ptr = kde_header_write(ptr, WISUN_OUI, KDE_PTKID, KDE_PTKID_LEN); + + memcpy(ptr, ptkid, PTKID_LEN); + ptr += PTKID_LEN; + + return ptr; +} + +uint8_t *kde_lifetime_write(uint8_t *ptr, uint32_t lifetime) +{ + ptr = kde_header_write(ptr, IEEE_802_11_OUI, KDE_LIFETIME, KDE_LIFETIME_LEN); + ptr = common_write_32_bit(lifetime, ptr); + + return ptr; +} + +uint8_t *kde_gtkl_write(uint8_t *ptr, uint8_t gtkl) +{ + ptr = kde_header_write(ptr, WISUN_OUI, KDE_GTKL, KDE_GTKL_LEN); + *ptr++ = gtkl; + + return ptr; +} + +int8_t kde_gtk_read(const uint8_t *ptr, uint16_t len, uint8_t *key_id, uint8_t *gtk) +{ + ptr = kde_search(ptr, len, IEEE_802_11_OUI, KDE_GTK, KDE_GTK_LEN); + if (ptr == NULL) { + return -1; + } + + *key_id = *ptr++ & 0x03; + ptr++; + memcpy(gtk, ptr, GTK_LEN); + + return 0; +} + +int8_t kde_pmkid_read(const uint8_t *ptr, uint16_t len, uint8_t *pmkid) +{ + ptr = kde_search(ptr, len, IEEE_802_11_OUI, KDE_PMKID, KDE_PMKID_LEN); + if (ptr == NULL) { + return -1; + } + + memcpy(pmkid, ptr, PMKID_LEN); + + return 0; +} + +int8_t kde_ptkid_read(const uint8_t *ptr, uint16_t len, uint8_t *ptkid) +{ + ptr = kde_search(ptr, len, WISUN_OUI, KDE_PTKID, KDE_PTKID_LEN); + if (ptr == NULL) { + return -1; + } + + memcpy(ptkid, ptr, PTKID_LEN); + + return 0; +} + +int8_t kde_lifetime_read(const uint8_t *ptr, uint16_t len, uint32_t *lifetime) +{ + ptr = kde_search(ptr, len, IEEE_802_11_OUI, KDE_LIFETIME, KDE_LIFETIME_LEN); + if (ptr == NULL) { + return -1; + } + + *lifetime = common_read_32_bit(ptr); + + return 0; +} + +int8_t kde_gtkl_read(const uint8_t *ptr, uint16_t len, uint8_t *gtkl) +{ + ptr = kde_search(ptr, len, WISUN_OUI, KDE_GTKL, KDE_GTKL_LEN); + if (ptr == NULL) { + return -1; + } + + *gtkl = *ptr; + + return 0; +} + +#endif + diff --git a/source/Security/eapol/kde_helper.h b/source/Security/eapol/kde_helper.h new file mode 100644 index 0000000000..b735828e61 --- /dev/null +++ b/source/Security/eapol/kde_helper.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-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 KDE_HELPER_H_ +#define KDE_HELPER_H_ + +/* + * EAPOL KDE helper functions + * + */ + +#define KDE_GTK_LEN 6 + 2 + 16 +#define KDE_PMKID_LEN 6 + 16 +#define KDE_PTKID_LEN 6 + 16 +#define KDE_LIFETIME_LEN 6 + 4 +#define KDE_GTKL_LEN 6 + 1 + +/** + * kde_padded_length_calc calculates padded length for kde (see IEEE 802.11 chapter 11.6.2 EAPOL-Key frames) + * + * \param kde_length length without padding + * + * \return padded length + */ +uint16_t kde_padded_length_calc(uint16_t kde_length); + +/** + * kde_padding_write writes padded bytes + * + * \param start_ptr first byte of the padding + * \param end_ptr last bytes next byte + * + */ +void kde_padding_write(uint8_t *start_ptr, uint8_t *end_ptr); + +/** + * kde_gtk_write writes GTK + * + * \param ptr pointer where to write + * \param key_id key identifier (index) + * \param gtk GTK + * + * return incremented write pointer + * + */ +uint8_t *kde_gtk_write(uint8_t *ptr, uint8_t key_id, const uint8_t *gtk); + +/** + * kde_pmkid_write writes PMKID + * + * \param ptr pointer where to write + * \param pmkid PMKID + * + * return incremented write pointer + * + */ +uint8_t *kde_pmkid_write(uint8_t *ptr, const uint8_t *pmkid); + +/** + * kde_ptkid_write writes PTKID + * + * \param ptr pointer where to write + * \param pmkid PTKID + * + * return incremented write pointer + * + */ +uint8_t *kde_ptkid_write(uint8_t *ptr, const uint8_t *ptkid); + +/** + * kde_lifetime_write writes GTK lifetime + * + * \param ptr pointer where to write + * \param lifetime GTK lifetime + * + * return incremented write pointer + * + */ +uint8_t *kde_lifetime_write(uint8_t *ptr, uint32_t lifetime); + +/** + * kde_gtkl_write writes GTK liveness information + * + * \param ptr pointer where to write + * \param gtkl GTK liveness bit field + * + * return incremented write pointer + * + */ +uint8_t *kde_gtkl_write(uint8_t *ptr, uint8_t gtkl); + +/** + * kde_gtk_read reads GTK + * + * \param ptr pointer where to read + * \param len length of the remaining data + * \param key_id key identifier (index) + * \param gtk GTK + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kde_gtk_read(const uint8_t *ptr, uint16_t len, uint8_t *key_id, uint8_t *gtk); + +/** + * kde_pmkid_read reads PMKID + * + * \param ptr pointer where to read + * \param len length of the remaining data + * \param pmkid PMKID + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kde_pmkid_read(const uint8_t *ptr, uint16_t len, uint8_t *pmkid); + +/** + * kde_ptkid_read reads PTKID + * + * \param ptr pointer where to read + * \param len length of the remaining data + * \param ptkid PTKID + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kde_ptkid_read(const uint8_t *ptr, uint16_t len, uint8_t *ptkid); + +/** + * kde_lifetime_read reads GTK lifetime + * + * \param ptr pointer where to read + * \param len length of the remaining data + * \param lifetime GTK lifetime + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kde_lifetime_read(const uint8_t *ptr, uint16_t len, uint32_t *lifetime); + +/** + * kde_gtkl_read reads GTK liveness information + * + * \param ptr pointer where to read + * \param len length of the remaining data + * \param gtkl GTK liveness bit field + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kde_gtkl_read(const uint8_t *ptr, uint16_t len, uint8_t *gtkl); + +#endif /* KDE_HELPER_H_ */ diff --git a/source/Security/kmp/kmp_addr.c b/source/Security/kmp/kmp_addr.c new file mode 100644 index 0000000000..bbff7bdad8 --- /dev/null +++ b/source/Security/kmp/kmp_addr.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "eventOS_event.h" +#include "eventOS_scheduler.h" +#include "eventOS_event_timer.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "Common_Protocols/ipv6_constants.h" +#include "6LoWPAN/ws/ws_config.h" +#include "Security/kmp/kmp_addr.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "kmar" + +#define KMP_ADDR_DYN_ALLOC 0x80 +#define KMP_ADDR_TYPE_MASK 0x0F + +typedef struct { + uint8_t type; + uint8_t eui_64[8]; + address_t ip_addr; + uint16_t port; +} kmp_eui_64_ip_addr_t; + +kmp_addr_t *kmp_address_create(kmp_addr_e type, const uint8_t *eui_64) +{ + uint8_t size; + if (type == KMP_ADDR_EUI_64) { + size = sizeof(kmp_addr_t); + } else if (type == KMP_ADDR_EUI_64_AND_IP) { + size = sizeof(kmp_eui_64_ip_addr_t); + } else { + return 0; + } + + kmp_addr_t *addr = ns_dyn_mem_alloc(size); + if (!addr) { + return 0; + } + + kmp_address_init(type, addr, eui_64); + + addr->type |= KMP_ADDR_DYN_ALLOC; + + return addr; +} + +void kmp_address_init(kmp_addr_e type, kmp_addr_t *addr, const uint8_t *eui_64) +{ + uint8_t size; + if (type == KMP_ADDR_EUI_64) { + size = sizeof(kmp_addr_t); + } else if (type == KMP_ADDR_EUI_64_AND_IP) { + size = sizeof(kmp_eui_64_ip_addr_t); + } else { + return; + } + + kmp_addr_t *kmp_addr = addr; + + memset(addr, 0, size); + kmp_addr->type = type; + if (eui_64) { + memcpy(kmp_addr->eui_64, eui_64, 8); + } +} + +void kmp_address_delete(kmp_addr_t *addr) +{ + if (addr && (addr->type & KMP_ADDR_DYN_ALLOC)) { + ns_dyn_mem_free(addr); + } +} + +const uint8_t *kmp_address_eui_64_get(const kmp_addr_t *addr) +{ + if (!addr) { + return NULL; + } + + return addr->eui_64; +} + +const uint8_t *kmp_address_ip_get(const kmp_addr_t *addr) +{ + if (!addr || (addr->type & KMP_ADDR_TYPE_MASK) != KMP_ADDR_EUI_64_AND_IP) { + return NULL; + } + + return ((kmp_eui_64_ip_addr_t *)addr)->ip_addr; +} + +uint16_t kmp_address_port_get(const kmp_addr_t *addr) +{ + if (!addr || (addr->type & KMP_ADDR_TYPE_MASK) != KMP_ADDR_EUI_64_AND_IP) { + return 0; + } + + return ((kmp_eui_64_ip_addr_t *)addr)->port; +} + +int8_t kmp_address_eui_64_set(kmp_addr_t *addr, const uint8_t *eui64) +{ + if (!addr) { + return -1; + } + + memcpy(addr->eui_64, eui64, 8); + return 0; +} + +int8_t kmp_address_ip_set(kmp_addr_t *addr, const uint8_t *ip_addr) +{ + if (!addr || !ip_addr || (addr->type & KMP_ADDR_TYPE_MASK) != KMP_ADDR_EUI_64_AND_IP) { + return -1; + } + + memcpy(((kmp_eui_64_ip_addr_t *)addr)->ip_addr, ip_addr, sizeof(address_t)); + return 0; +} + +int8_t kmp_address_port_set(kmp_addr_t *addr, const uint16_t port) +{ + if (!addr || (addr->type & KMP_ADDR_TYPE_MASK) != KMP_ADDR_EUI_64_AND_IP) { + return -1; + } + + ((kmp_eui_64_ip_addr_t *)addr)->port = port; + return 0; +} + +int8_t kmp_address_copy(kmp_addr_t *to_addr, const kmp_addr_t *from_addr) +{ + if (!to_addr || !from_addr) { + return -1; + } + + memcpy(to_addr->eui_64, from_addr->eui_64, 8); + + kmp_eui_64_ip_addr_t *to_ip_addr = (kmp_eui_64_ip_addr_t *) to_addr; + kmp_eui_64_ip_addr_t *from_ip_addr = (kmp_eui_64_ip_addr_t *) from_addr; + + if ((to_ip_addr->type & KMP_ADDR_TYPE_MASK) == KMP_ADDR_EUI_64_AND_IP + && (from_ip_addr->type & KMP_ADDR_TYPE_MASK) == KMP_ADDR_EUI_64_AND_IP) { + memcpy(to_ip_addr->ip_addr, from_ip_addr->ip_addr, sizeof(address_t)); + to_ip_addr->port = from_ip_addr->port; + } else if ((to_ip_addr->type & KMP_ADDR_TYPE_MASK) == KMP_ADDR_EUI_64_AND_IP) { + memset(to_ip_addr->ip_addr, 0, sizeof(address_t)); + to_ip_addr->port = 0; + } + + return 0; +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/kmp/kmp_addr.h b/source/Security/kmp/kmp_addr.h new file mode 100644 index 0000000000..967839707c --- /dev/null +++ b/source/Security/kmp/kmp_addr.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#ifndef KMP_ADDR_H_ +#define KMP_ADDR_H_ + +typedef enum { + KMP_ADDR_EUI_64 = 0, + KMP_ADDR_EUI_64_AND_IP +} kmp_addr_e; + +typedef struct { + uint8_t type; + uint8_t eui_64[8]; +} kmp_addr_t; + +/** + * kmp_address_create creates address + * + * \param type address type + * \param eui_64 EUI-64 + * + * \return address + * + */ +kmp_addr_t *kmp_address_create(kmp_addr_e type, const uint8_t *eui_64); + +/** + * kmp_address_init initializes address + * + * \param type address type + * \param addr address + * \param eui_64 EUI-64 + * + */ +void kmp_address_init(kmp_addr_e type, kmp_addr_t *addr, const uint8_t *eui_64); + +/** + * kmp_address_delete deletes address + * + * \param addr address + * + */ +void kmp_address_delete(kmp_addr_t *addr); + +/** + * kmp_address_eui_64_get get EUI-64 + * + * \param addr address + * + * \return EUI-64 + * + */ +const uint8_t *kmp_address_eui_64_get(const kmp_addr_t *addr); + +/** + * kmp_address_ip_get get IP address + * + * \param addr address + * + * \return IP address + * + */ +const uint8_t *kmp_address_ip_get(const kmp_addr_t *addr); + +/** + * kmp_address_port_get get port + * + * \param addr address + * + * \return port + * + */ +uint16_t kmp_address_port_get(const kmp_addr_t *addr); + +/** + * kmp_address_eui_64_set set EUI-64 + * + * \param addr address + * \param ip_addr EUI-64 + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_address_eui_64_set(kmp_addr_t *addr, const uint8_t *eui64); + +/** + * kmp_address_ip_set set IP address + * + * \param addr address + * \param ip_addr IP address + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_address_ip_set(kmp_addr_t *addr, const uint8_t *ip_addr); + +/** + * kmp_address_port_set set port address + * + * \param addr address + * \param port port + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_address_port_set(kmp_addr_t *addr, const uint16_t port); + +/** + * kmp_address_copy copies address + * + * \param to_addr address to copy to + * \param from_addr address to copy from + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_address_copy(kmp_addr_t *to_addr, const kmp_addr_t *from_addr); + +#endif /* KMP_ADDR_H_ */ diff --git a/source/Security/kmp/kmp_api.c b/source/Security/kmp/kmp_api.c new file mode 100644 index 0000000000..27ed10d4a6 --- /dev/null +++ b/source/Security/kmp/kmp_api.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "NWK_INTERFACE/Include/protocol.h" +#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/kmp/kmp_socket_if.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" +#include "Security/protocols/sec_prot.h" + +#ifdef HAVE_WS + +#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 */ +}; + +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_sec_prot_entry_t; + +typedef NS_LIST_HEAD(kmp_sec_prot_entry_t, link) kmp_sec_prot_list_t; + +struct kmp_service_s { + kmp_sec_prot_list_t sec_prot_list; /**< Security protocols list */ + kmp_service_incoming_ind *incoming_ind; /**< Callback to application to indicate incoming KMP frame */ + kmp_service_addr_get *addr_get; /**< Callback to get addresses related to KMP */ + kmp_service_api_get *api_get; /**< Callback to get KMP API from a service */ + kmp_service_msg_if_send *send; /**< Callback to send KMP frames */ + kmp_service_timer_if_start *timer_start; /**< Callback to start timer */ + kmp_service_timer_if_stop *timer_stop; /**< Callback to stop timer */ + kmp_service_event_if_event_send *event_send; /**< Callback to send event */ + uint8_t header_size; /**< Header size */ + ns_list_link_t link; /**< Link */ +}; + +typedef struct { + uint8_t kmp_id; /**< Kmp id */ + uint8_t kmp_data; /**< Kmp data e.g. eapol frame */ +} kmp_pdu_t; + +static NS_LIST_DEFINE(kmp_service_list, kmp_service_t, link); + +static void kmp_api_sec_prot_create_confirm(sec_prot_t *prot, sec_prot_result_e result); +static void kmp_api_sec_prot_create_indication(sec_prot_t *prot); +static void kmp_api_sec_prot_finished_indication(sec_prot_t *prot, sec_prot_result_e result, sec_prot_keys_t *sec_keys); +static void kmp_api_sec_prot_finished(sec_prot_t *prot); +static int8_t kmp_sec_prot_send(sec_prot_t *prot, void *pdu, uint16_t size); +static void kmp_sec_prot_timer_start(sec_prot_t *prot); +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); + +#define kmp_api_get_from_prot(prot) (kmp_api_t *)(((uint8_t *)prot) - offsetof(kmp_api_t, sec_prot)); + +kmp_api_t *kmp_api_create(kmp_service_t *service, kmp_type_e type) +{ + if (!service) { + return 0; + } + + kmp_sec_prot_entry_t *sec_prot = 0; + ns_list_foreach(kmp_sec_prot_entry_t, list_entry, &service->sec_prot_list) { + if (list_entry->type == type) { + sec_prot = list_entry; + break; + } + } + + if (!sec_prot) { + // Unknown security protocol + return 0; + } + // Size for security protocol internal data + uint16_t sec_size = sec_prot->size(); + + kmp_api_t *kmp = ns_dyn_mem_temporary_alloc(sizeof(kmp_api_t) + sec_size); + if (!kmp) { + return 0; + } + + kmp->type = type; + kmp->app_data_ptr = 0; + kmp->create_conf = 0; + kmp->create_ind = 0; + kmp->finished_ind = 0; + kmp->finished = 0; + kmp->addr = 0; + kmp->service = service; + kmp->timer_start_pending = false; + + memset(&kmp->sec_prot, 0, sec_size); + + kmp->sec_prot.header_size = service->header_size; + kmp->sec_prot.create_conf = kmp_api_sec_prot_create_confirm; + kmp->sec_prot.create_ind = kmp_api_sec_prot_create_indication; + kmp->sec_prot.finished_ind = kmp_api_sec_prot_finished_indication; + kmp->sec_prot.finished = kmp_api_sec_prot_finished; + kmp->sec_prot.send = kmp_sec_prot_send; + kmp->sec_prot.timer_start = kmp_sec_prot_timer_start; + kmp->sec_prot.timer_stop = kmp_sec_prot_timer_stop; + 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; + + if (sec_prot->init(&kmp->sec_prot) < 0) { + ns_dyn_mem_free(kmp); + return 0; + } + + return (kmp_api_t *) kmp; +} + +int8_t kmp_api_start(kmp_api_t *kmp) +{ + if (kmp->timer_start_pending) { + if (kmp->service->timer_start(kmp->service, kmp) < 0) { + return -1; + } + } + return 0; +} + +void kmp_api_create_request(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr, kmp_sec_keys_t *sec_keys) +{ + kmp->type = type; + kmp->addr = addr; + kmp->sec_prot.create_req(&kmp->sec_prot, sec_keys); +} + +void kmp_api_create_response(kmp_api_t *kmp, kmp_result_e result) +{ + kmp->sec_prot.create_resp(&kmp->sec_prot, (sec_prot_result_e) result); +} + +static void kmp_api_sec_prot_create_confirm(sec_prot_t *prot, sec_prot_result_e result) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->create_conf((kmp_api_t *)kmp, (kmp_result_e) result); +} + +static void kmp_api_sec_prot_create_indication(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->create_ind((kmp_api_t *)kmp, kmp->type, kmp->addr); +} + +static void kmp_api_sec_prot_finished_indication(sec_prot_t *prot, sec_prot_result_e result, sec_prot_keys_t *sec_keys) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->finished_ind((kmp_api_t *)kmp, (kmp_result_e) result, sec_keys); +} + +static void kmp_api_sec_prot_finished(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->finished((kmp_api_t *)kmp); +} + +static int8_t kmp_sec_prot_send(sec_prot_t *prot, void *pdu, uint16_t size) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + + // Convert from internal initial key type to real type if needed + kmp_type_e kmp_id = kmp->type; + if (kmp_id > IEEE_802_1X_INITIAL_KEY) { + kmp_id -= IEEE_802_1X_INITIAL_KEY; + } + + int8_t result = -1; + + if (kmp->service->send) { + result = kmp->service->send(kmp->service, kmp_id, kmp->addr, pdu, size); + } + + if (result < 0) { + ns_dyn_mem_free(pdu); + } + + return result; +} + +static void kmp_sec_prot_timer_start(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->timer_start_pending = false; + if (kmp->service->timer_start(kmp->service, kmp) < 0) { + kmp->timer_start_pending = true; + } +} + +static void kmp_sec_prot_timer_stop(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->service->timer_stop(kmp->service, kmp); + kmp->timer_start_pending = false; +} + +static void kmp_sec_prot_eui64_addr_get(sec_prot_t *prot, uint8_t *local_eui64, uint8_t *remote_eui64) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + + kmp_addr_t local_addr; + kmp_address_init(KMP_ADDR_EUI_64, &local_addr, NULL); + + kmp_addr_t remote_addr; + kmp_address_init(KMP_ADDR_EUI_64, &remote_addr, NULL); + + kmp->service->addr_get(kmp->service, kmp, &local_addr, &remote_addr); + + if (local_eui64) { + memcpy(local_eui64, kmp_address_eui_64_get(&local_addr), 8); + } + if (remote_eui64) { + memcpy(remote_eui64, kmp_address_eui_64_get(&remote_addr), 8); + } +} + +static sec_prot_t *kmp_sec_prot_by_type_get(sec_prot_t *prot, uint8_t type) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + + kmp_type_e kmp_type; + + switch (type) { + case SEC_PROT_TYPE_EAP_TLS: + kmp_type = IEEE_802_1X_MKA; + break; + case SEC_PROT_TYPE_TLS: + kmp_type = TLS_PROT; + break; + default: + return NULL; + } + + kmp_api_t *kmp_by_type = kmp->service->api_get(kmp->service, kmp, kmp_type); + if (!kmp_by_type) { + return NULL; + } + + return &kmp_by_type->sec_prot; +} + +void kmp_api_delete(kmp_api_t *kmp) +{ + if (kmp->sec_prot.delete) { + kmp->sec_prot.delete(&kmp->sec_prot); + } + ns_dyn_mem_free(kmp); +} + +void kmp_api_cb_register(kmp_api_t *kmp, kmp_api_create_confirm *create_conf, kmp_api_create_indication *create_ind, kmp_api_finished_indication *finished_ind, kmp_api_finished *finished) +{ + if (!kmp) { + return; + } + + kmp->create_conf = create_conf; + kmp->create_ind = create_ind; + kmp->finished_ind = finished_ind; + kmp->finished = finished; +} + +kmp_type_e kmp_api_type_get(kmp_api_t *kmp) +{ + return kmp->type; +} + +kmp_type_e kmp_api_type_from_id_get(uint8_t kmp_id) +{ + switch (kmp_id) { + case IEEE_802_1X_MKA: + return IEEE_802_1X_MKA; + case IEEE_802_11_4WH: + return IEEE_802_11_4WH; + case IEEE_802_11_GKH: + return IEEE_802_1X_MKA; + default: + return INVALID_KMP_TYPE; + } +} + +kmp_service_t *kmp_api_service_get(kmp_api_t *kmp) +{ + return kmp->service; +} + +void kmp_api_data_set(kmp_api_t *kmp, void *data) +{ + kmp->app_data_ptr = data; +} + +void *kmp_api_data_get(kmp_api_t *kmp) +{ + return kmp->app_data_ptr; +} + +void kmp_api_addr_set(kmp_api_t *kmp, kmp_addr_t *addr) +{ + kmp->addr = addr; +} + +void kmp_api_sec_keys_set(kmp_api_t *kmp, kmp_sec_keys_t *sec_keys) +{ + kmp->sec_prot.sec_keys = sec_keys; +} + +kmp_service_t *kmp_service_create(void) +{ + kmp_service_t *service = ns_dyn_mem_alloc(sizeof(kmp_service_t)); + if (!service) { + return NULL; + } + + ns_list_init(&service->sec_prot_list); + service->incoming_ind = 0; + service->addr_get = 0; + service->api_get = 0; + service->send = 0; + service->header_size = 0; + + ns_list_add_to_start(&kmp_service_list, service); + + return service; +} + +int8_t kmp_service_delete(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + ns_list_foreach_safe(kmp_service_t, list_entry, &kmp_service_list) { + if (list_entry == service) { + ns_list_foreach_safe(kmp_sec_prot_entry_t, sec_list_entry, &list_entry->sec_prot_list) { + ns_list_remove(&list_entry->sec_prot_list, sec_list_entry); + ns_dyn_mem_free(sec_list_entry); + } + + ns_list_remove(&kmp_service_list, list_entry); + ns_dyn_mem_free(list_entry); + return 0; + } + } + + return -1; +} + +static void kmp_sec_prot_state_machine_call(sec_prot_t *prot) +{ + kmp_api_t *kmp = kmp_api_get_from_prot(prot); + kmp->service->event_send(kmp->service, prot); +} + +int8_t kmp_service_cb_register(kmp_service_t *service, kmp_service_incoming_ind *incoming_ind, kmp_service_addr_get *addr_get, kmp_service_api_get *api_get) +{ + if (!service) { + return -1; + } + + service->incoming_ind = incoming_ind; + service->addr_get = addr_get; + service->api_get = api_get; + + return 0; +} + +int8_t kmp_service_msg_if_register(kmp_service_t *service, kmp_service_msg_if_send *send, uint8_t header_size) +{ + if (!service) { + return -1; + } + + service->send = send; + service->header_size = header_size; + + return 0; +} + +int8_t kmp_service_msg_if_receive(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr, void *pdu, uint16_t size) +{ + if (!service) { + return -1; + } + + kmp_api_t *kmp = (kmp_api_t *) service->incoming_ind(service, type, addr); + if (!kmp) { + return -1; + } + + int8_t ret = kmp->sec_prot.receive(&kmp->sec_prot, pdu, size); + return ret; +} + +int8_t kmp_service_sec_protocol_register(kmp_service_t *service, kmp_type_e type, kmp_sec_prot_size *size, kmp_sec_prot_init *init) +{ + if (!service) { + return -1; + } + + ns_list_foreach(kmp_sec_prot_entry_t, list_entry, &service->sec_prot_list) { + // Already registered + if (list_entry->type == type) { + return -1; + } + } + + kmp_sec_prot_entry_t *sec_prot = ns_dyn_mem_temporary_alloc(sizeof(kmp_sec_prot_entry_t)); + if (!sec_prot) { + return -1; + } + + sec_prot->type = type; + sec_prot->size = size; + sec_prot->init = init; + + ns_list_add_to_start(&service->sec_prot_list, sec_prot); + + return 0; +} + +int8_t kmp_service_sec_protocol_unregister(kmp_service_t *service, kmp_type_e type) +{ + ns_list_foreach(kmp_sec_prot_entry_t, list_entry, &service->sec_prot_list) { + if (list_entry->type == type) { + ns_list_remove(&service->sec_prot_list, list_entry); + ns_dyn_mem_free(list_entry); + return 0; + } + } + return -1; +} + +void kmp_service_timer_if_timeout(kmp_api_t *kmp, uint16_t ticks) +{ + kmp->sec_prot.timer_timeout(&kmp->sec_prot, ticks); +} + +int8_t kmp_service_timer_if_register(kmp_service_t *service, kmp_service_timer_if_start start, kmp_service_timer_if_stop stop) +{ + if (!service) { + return -1; + } + + service->timer_start = start; + service->timer_stop = stop; + return 0; +} + +void kmp_service_event_if_event(kmp_service_t *service, void *data) +{ + (void) service; + + // For now, only state machine events + sec_prot_t *prot = data; + prot->state_machine(prot); +} + +int8_t kmp_service_event_if_register(kmp_service_t *service, kmp_service_event_if_event_send send) +{ + if (!service) { + return -1; + } + + service->event_send = send; + return 0; +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/kmp/kmp_api.h b/source/Security/kmp/kmp_api.h new file mode 100644 index 0000000000..a03ad8e249 --- /dev/null +++ b/source/Security/kmp/kmp_api.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2016-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 KMP_API_H_ +#define KMP_API_H_ + +/* + * KMP API and KMP service module. KMP API offers key management security + * protocols interface towards application. + * + * KMP service provides security protocols access to network, timing + * services and callback services. Application must configure KMP service + * before using KMP API. + * + */ + +typedef enum { + INVALID_KMP_TYPE = 0, + + IEEE_802_1X_MKA = 1, + IEEE_802_11_4WH = 6, + IEEE_802_11_GKH = 7, + TLS_PROT = 8, + + IEEE_802_1X_INITIAL_KEY = 10, + + IEEE_802_1X_MKA_KEY = 11, + IEEE_802_11_4WH_KEY = 16, + IEEE_802_11_GKH_KEY = 17 +} kmp_type_e; + + +typedef enum { + KMP_RESULT_OK = 0, + KMP_RESULT_ERR_NO_MEM = -1, + KMP_RESULT_ERR_UNSPEC = -2 +} kmp_result_e; + +typedef void kmp_sec_keys_t; +typedef struct sec_prot_s sec_prot_t; +typedef struct kmp_api_s kmp_api_t; +typedef struct kmp_service_s kmp_service_t; + +/** + * kmp_api_create_request KMP-CREATE.request + * + * \param kmp instance + * \param type protocol type + * \param addr address + * \param sec_keys security keys + * + */ +void kmp_api_create_request(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr, kmp_sec_keys_t *sec_keys); + +/** + * kmp_api_create_confirm KMP-CREATE.confirm + * + * \param kmp instance + * \param result ok or fail + * + */ +typedef void kmp_api_create_confirm(kmp_api_t *kmp, kmp_result_e result); + +/** + * kmp_api_create_indication KMP-CREATE.indication + * + * \param kmp instance + * \param type protocol type + * \param addr address + * + */ +typedef void kmp_api_create_indication(kmp_api_t *kmp, kmp_type_e type, kmp_addr_t *addr); + +/** + * kmp_api_create_response KMP-CREATE.response + * + * \param kmp instance + * \param result ok or fail + * + */ +void kmp_api_create_response(kmp_api_t *kmp, kmp_result_e result); + +/** + * kmp_api_finished_indication KMP-FINISHED.indication + * + * \param kmp instance + * \param result ok or fail + * \param sec_keys security keys + * + */ +typedef void kmp_api_finished_indication(kmp_api_t *kmp, kmp_result_e result, kmp_sec_keys_t *sec_keys); + +/** + * kmp_api_finished will be called when KMP has finished and is ready for delete + * + * \param kmp instance + * + */ +typedef void kmp_api_finished(kmp_api_t *kmp); + +/** + * kmp_api_create create KMP api + * + * \param service KMP service + * \param type KMP type + * + * \return KMP instance or NULL + * + */ +kmp_api_t *kmp_api_create(kmp_service_t *service, kmp_type_e type); + +/** + * kmp_api_start start KMP api + * + * \param kmp instance + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_api_start(kmp_api_t *kmp); + +/** + * kmp_api_create create KMP api + * + * \param kmp instance + * + */ +void kmp_api_delete(kmp_api_t *kmp); + +/** + * kmp_api_type_get get KMP type + * + * \param kmp instance + * + * \return kmp_type_e KMP type + * + */ +kmp_type_e kmp_api_type_get(kmp_api_t *kmp); + +/** + * kmp_api_type_from_id_get get KMP type from KMP id + * + * \param kmp_id KMP identifier + * + * \return kmp_type_e KMP type + * + */ +kmp_type_e kmp_api_type_from_id_get(uint8_t kmp_id); + +/** + * kmp_api_service_get get KMP service for KMP instance + * + * \param kmp instance + * + * \return service + * + */ +kmp_service_t *kmp_api_service_get(kmp_api_t *kmp); + +/** + * kmp_api_data_set set application data + * + * \param kmp instance + * \param data data + * + */ +void kmp_api_data_set(kmp_api_t *kmp, void *data); + +/** + * kmp_api_data_get read application data + * + * \param kmp instance + * + * \return data + * + */ +void *kmp_api_data_get(kmp_api_t *kmp); + +/** + * kmp_api_addr_set set address + * + * \param kmp instance + * \param addr addr + * + */ +void kmp_api_addr_set(kmp_api_t *kmp, kmp_addr_t *addr); + +/** + * kmp_api_sec_keys_set set security keys + * + * \param kmp instance + * \param sec_keys security keys + * + */ +void kmp_api_sec_keys_set(kmp_api_t *kmp, kmp_sec_keys_t *sec_keys); + +/** + * kmp_api_cb_register registers api callbacks + * + * \param kmp instance + * \param create_conf KMP-CREATE.confirm callback + * \param create_ind KMP-CREATE.indication callback + * \param finished_ind KMP-FINISHED.indication + * \param finish KMP has finished and is ready for delete + * + */ +void kmp_api_cb_register(kmp_api_t *kmp, kmp_api_create_confirm *create_conf, kmp_api_create_indication *create_ind, kmp_api_finished_indication *finished_ind, kmp_api_finished *finished); + +/** + * kmp_service_create creates KMP service + * + * \return service or NULL + * + */ +kmp_service_t *kmp_service_create(void); + +/** + * kmp_service_delete deletes KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_delete(kmp_service_t *service); + +/** + * kmp_service_incoming_ind Notifies application about incoming KMP frame + * + * \param service KMP service + * \param type protocol type + * \param addr address + * + * \return KMP instance or NULL + * + */ +typedef kmp_api_t *kmp_service_incoming_ind(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr); + +/** + * kmp_service_addr_get gets addressing information related to KMP + * + * \param service KMP service + * \param kmp KMP instance + * \param local_addr Local address + * \param remote_addr Remote address + * + */ +typedef void kmp_service_addr_get(kmp_service_t *service, kmp_api_t *kmp, kmp_addr_t *local_addr, kmp_addr_t *remote_addr); + +/** + * kmp_service_api_get gets KMP API from KMP service + * + * \param service KMP service + * \param kmp KMP instance + * \param type protocol type + * + * \return KMP instance or NULL + * + */ +typedef kmp_api_t *kmp_service_api_get(kmp_service_t *service, kmp_api_t *kmp, kmp_type_e type); + +/** + * kmp_service_cb_register registers service callbacks + * + * \param service KMP service + * \param incoming_ind incoming message callback + * \param addr_get gets addressing information callback + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_cb_register(kmp_service_t *service, kmp_service_incoming_ind *incoming_ind, kmp_service_addr_get *addr_get, kmp_service_api_get *api_get); + +/** + * kmp_service_msg_if_receive receive a message + * + * \param service KMP service + * \param type protocol type + * \param addr address + * \param pdu pdu + * \param size pdu size + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_msg_if_receive(kmp_service_t *service, kmp_type_e kmp_id, const kmp_addr_t *addr, void *pdu, uint16_t size); + +/** + * kmp_service_msg_if_send send a message + * + * \param service KMP service + * \param type protocol type + * \param addr address + * \param pdu pdu + * \param size pdu size + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t kmp_service_msg_if_send(kmp_service_t *service, kmp_type_e type, const kmp_addr_t *addr, void *pdu, uint16_t size); + +/** + * kmp_service_msg_if_register registers message interface + * + * \param service KMP service + * \param send KMP PDU send callback + * \param header_size header size + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_msg_if_register(kmp_service_t *service, kmp_service_msg_if_send *send, uint8_t header_size); + +/** + * kmp_sec_prot_size security protocol data size + * + * \return size + * + */ +typedef uint16_t kmp_sec_prot_size(void); + +/** + * kmp_sec_prot_init security protocol init callback + * + * \param prot protocol data + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t kmp_sec_prot_init(sec_prot_t *prot); + +/** + * kmp_service_sec_protocol_register register a security protocol to KMP service + * + * \param service KMP service + * \param type protocol type + * \param size size callback + * \param init init callback + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_sec_protocol_register(kmp_service_t *service, kmp_type_e type, kmp_sec_prot_size *size, kmp_sec_prot_init *init); + +/** + * kmp_service_sec_protocol_unregister unregister a security protocol from KMP service + * + * \param service KMP service + * \param type protocol type + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_sec_protocol_unregister(kmp_service_t *service, kmp_type_e type); + +/** + * kmp_service_timer_if_timeout timer timeout + * + * \param service KMP instance + * + */ +void kmp_service_timer_if_timeout(kmp_api_t *kmp, uint16_t ticks); + +/** + * kmp_service_timer_if_start timer start callback + * + * \param service KMP service + * \param kmp KMP instance + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t kmp_service_timer_if_start(kmp_service_t *service, kmp_api_t *kmp); + +/** + * kmp_service_timer_if_stop timer stop callback + * + * \param service KMP service + * \param kmp KMP instance + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t kmp_service_timer_if_stop(kmp_service_t *service, kmp_api_t *kmp); + +/** + * kmp_service_timer_if_register register a timer interface to KMP service + * + * \param service KMP service + * \param start timer start callback + * \param stop timer stop callback + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_timer_if_register(kmp_service_t *service, kmp_service_timer_if_start start, kmp_service_timer_if_stop stop); + +/** + * kmp_service_event_if_event event callback + * + * \param service KMP service + * \param data opaque callback data + * + */ +void kmp_service_event_if_event(kmp_service_t *service, void *data); + +/** + * kmp_service_event_if_event_send event send callback + * + * \param service KMP service + * \param data opaque callback data + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t kmp_service_event_if_event_send(kmp_service_t *service, void *data); + +/** + * kmp_service_event_if_register register an event interface to KMP service + * + * \param service KMP service + * \param send send event + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_service_event_if_register(kmp_service_t *service, kmp_service_event_if_event_send send); + +#endif /* KMP_API_H_ */ diff --git a/source/Security/kmp/kmp_eapol_pdu_if.c b/source/Security/kmp/kmp_eapol_pdu_if.c new file mode 100644 index 0000000000..c56f5b09ee --- /dev/null +++ b/source/Security/kmp/kmp_eapol_pdu_if.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "6LoWPAN/ws/ws_config.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "Security/kmp/kmp_addr.h" +#include "Security/kmp/kmp_api.h" +#include "Security/kmp/kmp_eapol_pdu_if.h" +#include "6LoWPAN/MAC/mpx_api.h" +#include "6LoWPAN/ws/ws_eapol_pdu.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "kmep" + +#define EAPOL_PDU_IF_HEADER_SIZE 1 + +typedef struct { + uint8_t kmp_id; /**< Kmp id */ + uint8_t kmp_data; /**< Kmp data e.g. eapol frame */ +} eapol_kmp_pdu_t; + +typedef struct { + kmp_service_t *kmp_service; /**< KMP service */ + protocol_interface_info_entry_t *interface_ptr; /**< Interface pointer */ + ns_list_link_t link; /**< Link */ +} kmp_eapol_pdu_if_t; + +static NS_LIST_DEFINE(kmp_eapol_pdu_if_list, kmp_eapol_pdu_if_t, link); + +static int8_t kmp_eapol_pdu_if_send(kmp_service_t *service, kmp_type_e kmp_id, const kmp_addr_t *addr, void *pdu, uint16_t size); + +int8_t kmp_eapol_pdu_if_register(kmp_service_t *service, protocol_interface_info_entry_t *interface_ptr) +{ + if (!service || !interface_ptr) { + return -1; + } + + ns_list_foreach(kmp_eapol_pdu_if_t, entry, &kmp_eapol_pdu_if_list) { + if (entry->kmp_service == service || entry->interface_ptr == interface_ptr) { + return -1; + } + } + + kmp_eapol_pdu_if_t *eapol_pdu_if = ns_dyn_mem_alloc(sizeof(kmp_eapol_pdu_if_t)); + if (!eapol_pdu_if) { + return -1; + } + + eapol_pdu_if->kmp_service = service; + eapol_pdu_if->interface_ptr = interface_ptr; + + if (kmp_service_msg_if_register(service, kmp_eapol_pdu_if_send, EAPOL_PDU_IF_HEADER_SIZE) < 0) { + ns_dyn_mem_free(eapol_pdu_if); + return -1; + } + + ns_list_add_to_end(&kmp_eapol_pdu_if_list, eapol_pdu_if); + + return 0; +} + +int8_t kmp_eapol_pdu_if_unregister(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + ns_list_foreach_safe(kmp_eapol_pdu_if_t, entry, &kmp_eapol_pdu_if_list) { + if (entry->kmp_service == service) { + ns_list_remove(&kmp_eapol_pdu_if_list, entry); + ns_dyn_mem_free(entry); + kmp_service_msg_if_register(service, NULL, 0); + } + } + return 0; +} + +static int8_t kmp_eapol_pdu_if_send(kmp_service_t *service, kmp_type_e kmp_id, const kmp_addr_t *addr, void *pdu, uint16_t size) +{ + if (!service || !addr || !pdu) { + return -1; + } + + protocol_interface_info_entry_t *interface_ptr = NULL; + + ns_list_foreach(kmp_eapol_pdu_if_t, entry, &kmp_eapol_pdu_if_list) { + if (entry->kmp_service == service) { + interface_ptr = entry->interface_ptr; + break; + } + } + + if (!interface_ptr) { + return -1; + } + + const uint8_t *eui_64 = kmp_address_eui_64_get(addr); + if (!eui_64) { + return -1; + } + + uint8_t *ptr = pdu; + *ptr = kmp_id; + + int8_t ret = ws_eapol_pdu_send_to_mpx(interface_ptr, eui_64, pdu, size, pdu); + + return ret; +} + +int8_t kmp_eapol_pdu_if_receive(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *pdu, uint16_t size) +{ + kmp_service_t *service = NULL; + + ns_list_foreach(kmp_eapol_pdu_if_t, entry, &kmp_eapol_pdu_if_list) { + if (entry->interface_ptr == interface_ptr) { + service = entry->kmp_service; + break; + } + } + + if (!service) { + return -1; + } + + kmp_addr_t addr; + kmp_address_init(KMP_ADDR_EUI_64, &addr, eui_64); + + eapol_kmp_pdu_t *eapol_kmp_pdu = pdu; + uint16_t data_pdu_size = size - sizeof(uint8_t); + 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) { + return -1; + } + + int8_t ret = kmp_service_msg_if_receive(service, type, &addr, data_pdu, data_pdu_size); + + return ret; +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/kmp/kmp_eapol_pdu_if.h b/source/Security/kmp/kmp_eapol_pdu_if.h new file mode 100644 index 0000000000..eaad14b075 --- /dev/null +++ b/source/Security/kmp/kmp_eapol_pdu_if.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016-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 KMP_EAPOL_PDU_IF_H_ +#define KMP_EAPOL_PDU_IF_H_ + +/* + * Supplicant KMP interface to/from EAPOL PDU interface (to MPX). + */ + +/** + * kmp_eapol_pdu_if_register register EAPOL PDU interface to KMP service + * + * \param service KMP service to register to + * \param interface_ptr interface + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_eapol_pdu_if_register(kmp_service_t *service, protocol_interface_info_entry_t *interface_ptr); + +/** + * kmp_eapol_pdu_if_unregister unregister EAPOL PDU interface from KMP service + * + * \param service KMP service to unregister from + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_eapol_pdu_if_unregister(kmp_service_t *service); + +/** + * kmp_eapol_pdu_if_receive receive EAPOL PDU to KMP service + * + * \param interface_ptr interface + * \param eui_64 source EUI-64 + * \param pdu EAPOL pdu + * \param size EAPOL pdu size + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_eapol_pdu_if_receive(protocol_interface_info_entry_t *interface_ptr, const uint8_t *eui_64, void *pdu, uint16_t size); + +#endif /* KMP_EAPOL_PDU_IF_H_ */ diff --git a/source/Security/kmp/kmp_socket_if.c b/source/Security/kmp/kmp_socket_if.c new file mode 100644 index 0000000000..ba6412678b --- /dev/null +++ b/source/Security/kmp/kmp_socket_if.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "ns_address.h" +#include "nsdynmemLIB.h" +#include "eventOS_event.h" +#include "eventOS_scheduler.h" +#include "eventOS_event_timer.h" +#include "NWK_INTERFACE/Include/protocol.h" +#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/kmp/kmp_socket_if.h" +#include "common_functions.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "kmsi" + +#define SOCKET_IF_HEADER_SIZE 27 + +typedef struct { + kmp_service_t *kmp_service; /**< KMP service */ + ns_address_t remote_addr; /**< Remote address */ + int8_t socket_id; /**< Socket ID */ + ns_list_link_t link; /**< Link */ +} kmp_socket_if_t; + +static int8_t kmp_socket_if_send(kmp_service_t *service, kmp_type_e kmp_id, const kmp_addr_t *addr, void *pdu, uint16_t size); +static void kmp_socket_if_socket_cb(void *ptr); + +static NS_LIST_DEFINE(kmp_socket_if_list, kmp_socket_if_t, link); + +int8_t kmp_socket_if_register(kmp_service_t *service, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port) +{ + if (!service || !remote_addr) { + return -1; + } + + ns_list_foreach(kmp_socket_if_t, entry, &kmp_socket_if_list) { + if (entry->kmp_service == service) { + return -1; + } + } + + kmp_socket_if_t *socket_if = ns_dyn_mem_alloc(sizeof(kmp_socket_if_t)); + if (!socket_if) { + return -1; + } + + socket_if->kmp_service = service; + + socket_if->remote_addr.type = ADDRESS_IPV6; + memcpy(&socket_if->remote_addr.address, remote_addr, 16); + socket_if->remote_addr.identifier = remote_port; + + socket_if->socket_id = socket_open(IPV6_NH_UDP, local_port, &kmp_socket_if_socket_cb); + if (socket_if->socket_id < 0) { + ns_dyn_mem_free(socket_if); + return -1; + } + + if (kmp_service_msg_if_register(service, kmp_socket_if_send, SOCKET_IF_HEADER_SIZE) < 0) { + ns_dyn_mem_free(socket_if); + return -1; + } + + ns_list_add_to_end(&kmp_socket_if_list, socket_if); + + return 0; +} + +int8_t kmp_socket_if_unregister(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + ns_list_foreach_safe(kmp_socket_if_t, entry, &kmp_socket_if_list) { + if (entry->kmp_service == service) { + ns_list_remove(&kmp_socket_if_list, entry); + socket_close(entry->socket_id); + ns_dyn_mem_free(entry); + kmp_service_msg_if_register(service, NULL, 0); + } + } + return 0; +} + +static int8_t kmp_socket_if_send(kmp_service_t *service, kmp_type_e kmp_id, const kmp_addr_t *addr, void *pdu, uint16_t size) +{ + if (!service || !pdu || !addr) { + return -1; + } + + kmp_socket_if_t *socket_if = NULL; + + ns_list_foreach(kmp_socket_if_t, entry, &kmp_socket_if_list) { + if (entry->kmp_service == service) { + socket_if = entry; + break; + } + } + + if (!socket_if) { + return -1; + } + + //Build UPD Relay + uint8_t *ptr = pdu; + memcpy(ptr, kmp_address_ip_get(addr), 16); + ptr += 16; + ptr = common_write_16_bit(kmp_address_port_get(addr), ptr); + memcpy(ptr, kmp_address_eui_64_get(addr), 8); + ptr += 8; + *ptr = kmp_id; + + socket_sendto(socket_if->socket_id, &socket_if->remote_addr, pdu, size); + ns_dyn_mem_free(pdu); + + return 0; +} + +static void kmp_socket_if_socket_cb(void *ptr) +{ + socket_callback_t *cb_data = ptr; + + if (cb_data->event_type != SOCKET_DATA) { + return; + } + + kmp_socket_if_t *socket_if = NULL; + + ns_list_foreach(kmp_socket_if_t, entry, &kmp_socket_if_list) { + if (entry->socket_id == cb_data->socket_id) { + socket_if = entry; + break; + } + } + + if (!socket_if) { + return; + } + + uint8_t *pdu = ns_dyn_mem_temporary_alloc(cb_data->d_len); + + if (socket_recvfrom(cb_data->socket_id, pdu, cb_data->d_len, 0, 0) != cb_data->d_len) { + ns_dyn_mem_free(pdu); + return; + } + uint8_t *relay_address, *euid64; + uint16_t relay_port; + uint8_t *data_ptr = pdu; + relay_address = data_ptr; + data_ptr += 16; + relay_port = common_read_16_bit(data_ptr); + data_ptr += 2; + euid64 = data_ptr; + data_ptr += 8; + + kmp_type_e type = kmp_api_type_from_id_get(*data_ptr++); + if (type == INVALID_KMP_TYPE) { + ns_dyn_mem_free(pdu); + return; + } + + kmp_addr_t *addr = kmp_address_create(KMP_ADDR_EUI_64_AND_IP, euid64); + if (!addr) { + ns_dyn_mem_free(pdu); + return; + } + + kmp_address_ip_set(addr, relay_address); + kmp_address_port_set(addr, relay_port); + + kmp_service_msg_if_receive(socket_if->kmp_service, type, addr, data_ptr, cb_data->d_len - 27); + kmp_address_delete(addr); + + ns_dyn_mem_free(pdu); +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/kmp/kmp_socket_if.h b/source/Security/kmp/kmp_socket_if.h new file mode 100644 index 0000000000..af1d695081 --- /dev/null +++ b/source/Security/kmp/kmp_socket_if.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016-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 KMP_SOCKET_IF_H_ +#define KMP_SOCKET_IF_H_ + +/* + * Authenticator KMP socket interface to/from EAPOL authenticator relay. EAPOL + * authenticator relay address and port are provided in register call (remote + * address and remote port parameters) + * + * Authenticator KMP socket must be bound to port that EAPOL authenticator + * uses to send messages to Authenticator KMP. Default port is 10254 (local port + * parameter) + * + */ + +/** + * kmp_socket_if_register register socket interface to KMP service + * + * \param service KMP service to register to + * \param local_port local port + * \param remote_addr remote address + * \param remote_port remote port + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_socket_if_register(kmp_service_t *service, uint16_t local_port, const uint8_t *remote_addr, uint16_t remote_port); + +/** + * kmp_socket_if_unregister unregister socket interface from KMP service + * + * \param service KMP service to unregister from + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t kmp_socket_if_unregister(kmp_service_t *service); + +#endif /* KMP_SOCKET_IF_H_ */ 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 new file mode 100644 index 0000000000..19895c3cb3 --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c @@ -0,0 +1,521 @@ +/* + * 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. + */ + +#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 "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" +#include "Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h" +#include "Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "eapa" + +typedef enum { + EAP_TLS_STATE_INIT = SEC_STATE_INIT, + EAP_TLS_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ, + EAP_TLS_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP, + EAP_TLS_STATE_CREATE_IND = SEC_STATE_CREATE_IND, + + EAP_TLS_STATE_RESPONSE_ID = SEC_STATE_FIRST, + EAP_TLS_STATE_RESPONSE_START, + EAP_TLS_STATE_RESPONSE, + + EAP_TLS_STATE_FINISH = SEC_STATE_FINISH, + EAP_TLS_STATE_FINISHED = SEC_STATE_FINISHED +} eap_tls_sec_prot_state_e; + +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 */ + uint8_t eap_id_seq; /**< EAP sequence */ + uint8_t eap_code; /**< Received EAP code */ + uint8_t eap_type; /**< Received EAP type */ + int8_t tls_result; /**< Result of TLS operation */ + bool wait_tls: 1; /**< Wait TLS (ECC calculation) before sending EAP-TLS message */ + bool tls_ongoing: 1; /**< TLS handshake is ongoing */ + bool send_pending: 1; /**< TLS data is not yet send to network */ +} eap_tls_sec_prot_int_t; + +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 +}; + +static uint16_t auth_eap_tls_sec_prot_size(void); +static int8_t auth_eap_tls_sec_prot_init(sec_prot_t *prot); + +static void auth_eap_tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static void auth_eap_tls_sec_prot_delete(sec_prot_t *prot); +static int8_t auth_eap_tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); + +static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot); + +static int8_t auth_eap_tls_sec_prot_message_handle(sec_prot_t *prot); +static int8_t auth_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_code, uint8_t eap_type, uint8_t tls_state); + +static void auth_eap_tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); +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); + +static void auth_eap_tls_sec_prot_seq_id_update(sec_prot_t *prot); + +#define eap_tls_sec_prot_get(prot) (eap_tls_sec_prot_int_t *) &prot->data + +int8_t auth_eap_tls_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_1X_MKA, auth_eap_tls_sec_prot_size, auth_eap_tls_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t auth_eap_tls_sec_prot_size(void) +{ + return sizeof(eap_tls_sec_prot_int_t); +} + +static int8_t auth_eap_tls_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = auth_eap_tls_sec_prot_create_request; + prot->create_resp = 0; + prot->receive = auth_eap_tls_sec_prot_receive; + prot->delete = auth_eap_tls_sec_prot_delete; + prot->state_machine = auth_eap_tls_sec_prot_state_machine; + prot->timer_timeout = auth_eap_tls_sec_prot_timer_timeout; + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_INIT); + + data->tls_prot = NULL; + data->eap_id_seq = 0; + data->eap_code = 0; + data->eap_type = 0; + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + data->tls_result = EAP_TLS_RESULT_ERROR; + data->wait_tls = false; + data->tls_ongoing = false; + data->send_pending = false; + return 0; +} + +static void auth_eap_tls_sec_prot_delete(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_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); +} + +static void auth_eap_tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + prot->sec_keys = sec_keys; + + // Call state machine + prot->state_machine_call(prot); +} + +static int8_t auth_eap_tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + int8_t ret_val = -1; + + // Decoding is successful + if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { + // Handle only EAP messages (ignore initial EAPOL-key retransmissions) + if (data->recv_eapol_pdu.packet_type == EAPOL_EAP_TYPE) { + data->eap_code = data->recv_eapol_pdu.msg.eap.eap_code; + data->eap_type = data->recv_eapol_pdu.msg.eap.type; + + // Call state machine + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->eap_code = 0; + data->eap_type = 0; + + return ret_val; +} + +static int8_t auth_eap_tls_sec_prot_message_handle(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + uint8_t *data_ptr = data->recv_eapol_pdu.msg.eap.data_ptr; + 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) { + 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); + + if (data->eap_type == EAP_IDENTITY) { + return EAP_TLS_MSG_IDENTITY; + } + + if (!data_ptr || length < 6) { + return EAP_TLS_MSG_DECODE_ERROR; + } + + length -= 5; // EAP fields: code, id, length, type + + return eap_tls_sec_prot_lib_message_handle(data_ptr, length, new_seq_id, &data->tls_send, &data->tls_recv); +} + +static int8_t auth_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_code, uint8_t eap_type, uint8_t tls_state) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + uint8_t flags = 0xff; + // EAP-TLS flags field is always present during TLS exchange + if (tls_state == EAP_TLS_EXCHANGE_ONGOING) { + flags = 0x00; + } + + if (eap_code == EAP_REQ) { + if (eap_type == EAP_TLS && tls_state == EAP_TLS_EXCHANGE_START) { + 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) { + 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); + if (!eapol_decoded_data) { + return -1; + } + + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +static void auth_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); + sec_prot_timer_timeout_handle(prot, &data->common, &eap_tls_trickle_params, ticks); +} + +static void auth_eap_tls_sec_prot_tls_create_indication(sec_prot_t *tls_prot) +{ + tls_prot->create_resp(tls_prot, SEC_RESULT_OK); +} + +static void auth_eap_tls_sec_prot_tls_finished_indication(sec_prot_t *tls_prot, sec_prot_result_e result, sec_prot_keys_t *sec_keys) +{ + (void) sec_keys; + + sec_prot_t *prot = tls_prot->type_get(tls_prot, SEC_PROT_TYPE_EAP_TLS); + if (!prot) { + return; + } + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + if (result == SEC_RESULT_OK) { + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_OVER; + } else if (result == SEC_RESULT_CONF_ERROR) { + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR; + } else { + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FAILED; + } + + data->tls_ongoing = false; + + if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR) { + // On fatal error terminate right away + prot->state_machine_call(prot); + } +} + +static int8_t auth_eap_tls_sec_prot_tls_send(sec_prot_t *tls_prot, void *pdu, uint16_t size) +{ + sec_prot_t *prot = tls_prot->type_get(tls_prot, SEC_PROT_TYPE_EAP_TLS); + if (!prot) { + return -1; + } + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + eap_tls_sec_prot_lib_message_free(&data->tls_send); + + data->tls_send.data = pdu; + data->tls_send.total_len = size; + data->tls_send.handled_len = 0; + + data->send_pending = true; + + prot->state_machine_call(prot); + + return 0; +} + +static void auth_eap_tls_sec_prot_init_tls(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + if (data->tls_prot) { + return; + } + + data->tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); + if (!data->tls_prot) { + return; + } + + data->tls_prot->header_size = TLS_HEAD_LEN; + data->tls_prot->sec_keys = prot->sec_keys; + + data->tls_prot->create_conf = NULL; + data->tls_prot->create_ind = auth_eap_tls_sec_prot_tls_create_indication; + data->tls_prot->finished_ind = auth_eap_tls_sec_prot_tls_finished_indication; + data->tls_prot->send = auth_eap_tls_sec_prot_tls_send; + + data->tls_ongoing = true; +} + +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; + } + + sec_prot_t *tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); + if (tls_prot) { + tls_prot->finished_send(tls_prot); + } +} + +static void auth_eap_tls_sec_prot_state_machine(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + int8_t result = EAP_TLS_MSG_CONTINUE; + + // EAP-TLS authenticator state machine + switch (sec_prot_state_get(&data->common)) { + case EAP_TLS_STATE_INIT: + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_CREATE_REQ); + break; + + // Wait KMP-CREATE.request + case EAP_TLS_STATE_CREATE_REQ: + tr_debug("EAP-TLS start"); + + prot->timer_start(prot); + + // KMP-CREATE.confirm + prot->create_conf(prot, SEC_RESULT_OK); + + // Increment sequence ID + auth_eap_tls_sec_prot_seq_id_update(prot); + + // Sends EAP request, Identity + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_IDENTITY, EAP_TLS_EXCHANGE_NONE); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &eap_tls_trickle_params); + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_RESPONSE_ID); + break; + + // Wait EAP response, Identity + case EAP_TLS_STATE_RESPONSE_ID: + + // On timeout + if (sec_prot_result_timeout_check(&data->common)) { + // Re-sends EAP request, Identity + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_IDENTITY, EAP_TLS_EXCHANGE_NONE); + return; + } + + // Handle EAP response (expected Identity) + if (auth_eap_tls_sec_prot_message_handle(prot) != EAP_TLS_MSG_IDENTITY) { + 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); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &eap_tls_trickle_params); + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_RESPONSE_START); + break; + + // Wait EAP response, TLS handshake + case EAP_TLS_STATE_RESPONSE_START: + case EAP_TLS_STATE_RESPONSE: + + // On timeout + if (sec_prot_result_timeout_check(&data->common)) { + if (sec_prot_state_get(&data->common) == EAP_TLS_STATE_RESPONSE_START) { + // Re-sends EAP request, TLS EAP start + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_TLS, EAP_TLS_EXCHANGE_START); + } else { + // Re-sends EAP request, TLS EAP + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_TLS, EAP_TLS_EXCHANGE_ONGOING); + } + return; + } + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_RESPONSE); + + // EAP response + if (data->eap_code == EAP_RESPONSE) { + // Handle EAP response, TLS EAP + result = auth_eap_tls_sec_prot_message_handle(prot); + if (result == EAP_TLS_MSG_DECODE_ERROR) { + return; + } + if (result == EAP_TLS_MSG_IDENTITY) { + // If received EAP response, Identity: re-sends EAP request, TLS EAP start + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_TLS, EAP_TLS_EXCHANGE_START); + return; + } + + // All fragments received for a message + if (result == EAP_TLS_MSG_RECEIVE_DONE) { + auth_eap_tls_sec_prot_init_tls(prot); + + if (data->tls_ongoing) { + // Call TLS + data->tls_prot->receive(data->tls_prot, data->tls_recv.data, data->tls_recv.total_len); + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + sec_prot_timer_trickle_stop(&data->common); + if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_FAILED && !data->send_pending) { + // In case has received alert and aborted can fail already here + eap_tls_sec_prot_lib_message_free(&data->tls_send); + } else { + data->wait_tls = true; + } + } + } else if (result == EAP_TLS_MSG_SEND_DONE) { + // All fragments send for a message, no new fragment received + eap_tls_sec_prot_lib_message_free(&data->tls_send); + } + // Wait TLS to process the received message + if (data->wait_tls) { + return; + } + } else { + // Call from TLS + if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR) { + // Send failure + eap_tls_sec_prot_lib_message_free(&data->tls_send); + } + + // Call from TLS + data->wait_tls = false; + } + + // TLS EAP message to be send + if (data->tls_send.total_len > 0 || result == EAP_TLS_MSG_MORE_FRAG) { + data->send_pending = false; + + // Sends EAP request, TLS EAP, TLS exchange + auth_eap_tls_sec_prot_message_send(prot, EAP_REQ, EAP_TLS, EAP_TLS_EXCHANGE_ONGOING); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &eap_tls_trickle_params); + } else { + // TLS done, indicate success to peer + if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_OVER) { + // Sends EAP success + auth_eap_tls_sec_prot_message_send(prot, EAP_SUCCESS, 0, EAP_TLS_EXCHANGE_NONE); + } else { + // Sends EAP failure + auth_eap_tls_sec_prot_message_send(prot, EAP_FAILURE, 0, EAP_TLS_EXCHANGE_NONE); + sec_prot_result_set(&data->common, SEC_RESULT_ERROR); + } + + // Done + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISH); + } + break; + + case EAP_TLS_STATE_FINISH: + tr_debug("EAP-TLS finish"); + + // 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: + auth_eap_tls_sec_prot_delete_tls(prot); + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static void auth_eap_tls_sec_prot_seq_id_update(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + data->eap_id_seq++; +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.h b/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.h new file mode 100644 index 0000000000..cbe295247a --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.h @@ -0,0 +1,36 @@ +/* + * 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 AUTH_EAP_TLS_SEC_PROT_H_ +#define AUTH_EAP_TLS_SEC_PROT_H_ + +/* + * Authenticator EAP-TLS security protocol. Specified in RFC 5216. + * + */ + +/** + * auth_eap_tls_sec_prot_register register authenticator EAP-TLS protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t auth_eap_tls_sec_prot_register(kmp_service_t *service); + +#endif /* AUTH_EAP_TLS_SEC_PROT_H_ */ 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 new file mode 100644 index 0000000000..c67f304597 --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c @@ -0,0 +1,230 @@ +/* + * 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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "fhss_config.h" +#include "NWK_INTERFACE/Include/protocol.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" +#include "Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "eapl" + +static int8_t eap_tls_sec_prot_lib_ack_update(tls_data_t *tls); +static uint8_t *eap_tls_sec_prot_lib_fragment_write(uint8_t *data, uint16_t total_len, uint16_t handled_len, uint16_t *message_len, uint8_t *flags); +static int8_t eap_tls_sec_prot_lib_fragment_read(tls_data_t *tls, uint8_t *data, uint16_t len); + +const uint8_t eap_msg_trace[4][10] = {"REQ", "RESPONSE", "SUCCESS", "FAILURE"}; + +int8_t eap_tls_sec_prot_lib_message_allocate(tls_data_t *data, uint8_t head_len, uint16_t len) +{ + ns_dyn_mem_free(data->data); + + data->data = ns_dyn_mem_temporary_alloc(head_len + len); + if (!data->data) { + return -1; + } + data->total_len = len; + data->handled_len = 0; + + return 0; +} + +void eap_tls_sec_prot_lib_message_free(tls_data_t *data) +{ + ns_dyn_mem_free(data->data); + data->handled_len = 0; + data->data = 0; + data->total_len = 0; +} + +void eap_tls_sec_prot_lib_message_init(tls_data_t *data) +{ + data->handled_len = 0; + data->data = 0; + data->total_len = 0; +} + +int8_t eap_tls_sec_prot_lib_message_handle(uint8_t *data, uint16_t length, bool new_seq_id, tls_data_t *tls_send, tls_data_t *tls_recv) +{ + int8_t result = EAP_TLS_MSG_CONTINUE; + + // EAP-TLS start + if (data[0] & EAP_TLS_START) { + result = EAP_TLS_MSG_START; + } else if (data[0] & EAP_TLS_MORE_FRAGMENTS) { + // More fragments + eap_tls_sec_prot_lib_message_allocate(tls_send, TLS_HEAD_LEN, 0); + + // Handles the length field + if (data[0] & EAP_TLS_FRAGMENT_LENGTH) { + if (length < 5) { + return EAP_TLS_MSG_DECODE_ERROR; + } + + uint32_t len = common_read_32_bit(&data[1]); + + //For first fragment allocates data for incoming TLS packet + if (!tls_recv->data) { + eap_tls_sec_prot_lib_message_allocate(tls_recv, 0, len); + } + length -= 4; + data += 4; + } + result = EAP_TLS_MSG_MORE_FRAG; + } else if (data[0] == 0) { + // Last (or only) fragment or fragment acknowledge. If sending data + // updates acknowledged fragments. + if (new_seq_id && eap_tls_sec_prot_lib_ack_update(tls_send)) { + // All send, free data + eap_tls_sec_prot_lib_message_allocate(tls_send, TLS_HEAD_LEN, 0); + result = EAP_TLS_MSG_SEND_DONE; + } + } + + length -= 1; // EAP-TLS flags + data += 1; + + // TLS data not included + if (length == 0) { + if (new_seq_id && result == EAP_TLS_MSG_CONTINUE) { + // If received only EAP-TLS header fails, and is not start, + // fragment acknowledge or last frame + result = EAP_TLS_MSG_FAIL; + } + + return result; + } + + // New (not seen) sequence identifier, update received data + if (new_seq_id) { + if (!tls_recv->data) { + eap_tls_sec_prot_lib_message_allocate(tls_recv, 0, length); + } + if (eap_tls_sec_prot_lib_fragment_read(tls_recv, data, length)) { + result = EAP_TLS_MSG_RECEIVE_DONE; + } + } + + 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) +{ + 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); + } + + 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); + + uint8_t *eapol_decoded_data = ns_dyn_mem_temporary_alloc(*length + header_size); + if (!eapol_decoded_data) { + return NULL; + } + + eapol_write_pdu_frame(eapol_decoded_data + header_size, &eapol_pdu); + + return eapol_decoded_data; +} + +static int8_t eap_tls_sec_prot_lib_ack_update(tls_data_t *tls) +{ + if (!tls->data || !tls->total_len) { + return false; + } + + if (tls->handled_len + TLS_FRAGMENT_LEN < tls->total_len) { + tls->handled_len += TLS_FRAGMENT_LEN; + return false; + } + + tls->handled_len = tls->total_len; + return true; +} + +static int8_t eap_tls_sec_prot_lib_fragment_read(tls_data_t *tls, uint8_t *data, uint16_t len) +{ + if (tls->handled_len + len > tls->total_len) { + return true; + } + + memcpy(tls->data + tls->handled_len, data, len); + tls->handled_len += len; + + if (tls->handled_len == tls->total_len) { + return true; + } + + return false; +} + +static uint8_t *eap_tls_sec_prot_lib_fragment_write(uint8_t *data, uint16_t total_len, uint16_t handled_len, uint16_t *message_len, uint8_t *flags) +{ + uint8_t *data_begin = data + handled_len; + + if (*flags != 0xff) { + data_begin -= 1; + *message_len += 1; + data_begin[0] = *flags; + } + + if (total_len - handled_len > TLS_FRAGMENT_LEN) { + *message_len += TLS_FRAGMENT_LEN; + + if (handled_len == 0) { + data_begin -= 4; // length + *message_len += 4; + *flags |= EAP_TLS_MORE_FRAGMENTS | EAP_TLS_FRAGMENT_LENGTH; + data_begin[0] = *flags; + common_write_32_bit(total_len, &data_begin[1]); + } else { + *flags |= EAP_TLS_MORE_FRAGMENTS; + data_begin[0] = *flags; + } + } else { + *message_len += total_len - handled_len; + } + + return data_begin; +} + +#endif /* HAVE_WS */ + 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 new file mode 100644 index 0000000000..37ff0f2741 --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h @@ -0,0 +1,122 @@ +/* + * 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 EAP_TLS_SEC_PROT_LIB_H_ +#define EAP_TLS_SEC_PROT_LIB_H_ + +/* + * EAP-TLS security protocol library + * + */ + +typedef enum { + EAP_TLS_EXCHANGE_NONE = 0, + EAP_TLS_EXCHANGE_START, + EAP_TLS_EXCHANGE_ONGOING +} eap_tls_sec_prot_tls_exchange_e; + +typedef enum { + EAP_TLS_MSG_IDENTITY = 0, + EAP_TLS_MSG_START, + EAP_TLS_MSG_CONTINUE, + EAP_TLS_MSG_SEND_DONE, + EAP_TLS_MSG_MORE_FRAG, + EAP_TLS_MSG_RECEIVE_DONE, + EAP_TLS_MSG_DECODE_ERROR, + EAP_TLS_MSG_FAIL, +} eap_tls_sec_prot_msg_e; + +typedef enum { + EAP_TLS_RESULT_NONE = 0, + EAP_TLS_RESULT_ERROR, + EAP_TLS_RESULT_HANDSHAKE_OVER, + EAP_TLS_RESULT_HANDSHAKE_FAILED, + EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR, +} eap_tls_sec_prot_result_e; + +typedef struct { + uint8_t *data; /**< Data buffer */ + uint16_t total_len; /**< Total length of the data buffer */ + uint16_t handled_len; /**< Handled length of the data buffer (e.g. acked by other end) */ +} tls_data_t; + +#define TLS_FRAGMENT_LEN 1100 //EAP-TLS fragment length +#define TLS_HEAD_LEN 5 //EAP-TLS flags and EAP-TLS length + +extern const uint8_t eap_msg_trace[4][10]; + +/** + * eap_tls_sec_prot_lib_message_allocate allocate message buffer + * + * \param data data buffer (length of the allocated data is header length + data length) + * \param head_len header length + * \param len data len + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t eap_tls_sec_prot_lib_message_allocate(tls_data_t *data, uint8_t head_len, uint16_t len); + +/** + * eap_tls_sec_prot_lib_message_free free message buffer + * + * \param data data buffer + * + */ +void eap_tls_sec_prot_lib_message_free(tls_data_t *data); + +/** + * eap_tls_sec_prot_lib_message_init init message buffer + * + * \param data data buffer + * + */ +void eap_tls_sec_prot_lib_message_init(tls_data_t *data); + +/** + * eap_tls_sec_prot_lib_message_handle decode incoming message EAP-TLS part (from EAP-TLS flags field onward) + * + * \param data message data (points to EAP-TLS flags) + * \param length of the message remaining + * \param new_seq_id EAP sequence identifier is new (message is not re-send by other end) + * \param tls_send EAP-TLS send buffer, when sending data, updates the data (fragments) that has been acknowledged by other end + * \param tls_recv EAP_TLS receive buffer, if receiving data, updates the received data (fragments) + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t eap_tls_sec_prot_lib_message_handle(uint8_t *data, uint16_t length, bool new_seq_id, tls_data_t *tls_send, tls_data_t *tls_recv); + +/** + * eap_tls_sec_prot_lib_message_build builds EAP-TLS message + * + * \param eap_code EAP code + * \param eap_type EAP type + * \param flags EAP-TLS flags + * \param eap_id_seq EAP sequence identifier + * \param header_size header size + * \param tls_send EAP-TLS send buffer, sends either a fragment or full message + * \param length of the message to be send + * + * \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); + +#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 new file mode 100644 index 0000000000..5a61d865aa --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c @@ -0,0 +1,516 @@ +/* + * 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. + */ + +#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 "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" +#include "Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h" +#include "Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "eaps" + +typedef enum { + EAP_TLS_STATE_INIT = SEC_STATE_INIT, + EAP_TLS_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ, + EAP_TLS_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP, + EAP_TLS_STATE_CREATE_IND = SEC_STATE_CREATE_IND, + + EAP_TLS_STATE_REQUEST_ID = SEC_STATE_FIRST, + EAP_TLS_STATE_REQUEST_TLS_EAP, + EAP_TLS_STATE_REQUEST, + + EAP_TLS_STATE_FINISH = SEC_STATE_FINISH, + EAP_TLS_STATE_FINISHED = SEC_STATE_FINISHED +} eap_tls_sec_prot_state_e; + +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 */ + uint8_t eap_id_seq; /**< EAP sequence */ + uint8_t eap_code; /**< Received EAP code */ + uint8_t eap_type; /**< Received EAP type */ + int8_t tls_result; /**< Result of TLS operation */ + bool wait_tls: 1; /**< Wait TLS (ECC calculation) before sending EAP-TLS message */ + bool tls_ongoing: 1; /**< TLS handshake is ongoing */ + bool send_pending: 1; /**< TLS data is not yet send to network */ +} eap_tls_sec_prot_int_t; + +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 +}; + +static uint16_t supp_eap_tls_sec_prot_size(void); +static int8_t supp_eap_tls_sec_prot_init(sec_prot_t *prot); + +static void supp_eap_tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); +static void supp_eap_tls_sec_prot_delete(sec_prot_t *prot); +static int8_t supp_eap_tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); + +static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot); + +static int8_t supp_eap_tls_sec_prot_message_handle(sec_prot_t *prot); +static int8_t supp_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_code, uint8_t eap_type, uint8_t tls_state); + +static void supp_eap_tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); +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); + +static void supp_eap_tls_sec_prot_seq_id_update(sec_prot_t *prot); + +#define eap_tls_sec_prot_get(prot) (eap_tls_sec_prot_int_t *) &prot->data + +int8_t supp_eap_tls_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_1X_MKA, supp_eap_tls_sec_prot_size, supp_eap_tls_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t supp_eap_tls_sec_prot_size(void) +{ + return sizeof(eap_tls_sec_prot_int_t); +} + +static int8_t supp_eap_tls_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = 0; + prot->create_resp = supp_eap_tls_sec_prot_create_response; + prot->receive = supp_eap_tls_sec_prot_receive; + prot->delete = supp_eap_tls_sec_prot_delete; + prot->state_machine = supp_eap_tls_sec_prot_state_machine; + prot->timer_timeout = supp_eap_tls_sec_prot_timer_timeout; + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_INIT); + + data->tls_prot = NULL; + data->eap_id_seq = 0; + data->eap_code = 0; + data->eap_type = 0; + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + data->tls_result = EAP_TLS_RESULT_ERROR; + data->wait_tls = false; + data->tls_ongoing = false; + return 0; +} + +static void supp_eap_tls_sec_prot_delete(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_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); +} + +static void supp_eap_tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + // Call state machine + sec_prot_result_set(&data->common, result); + prot->state_machine_call(prot); +} + +static int8_t supp_eap_tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + int8_t ret_val = -1; + + // Decoding is successful + if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { + // Handle only EAP messages (should never receive EAPOL-key messages during EAP-TLS) + if (data->recv_eapol_pdu.packet_type == EAPOL_EAP_TYPE) { + data->eap_code = data->recv_eapol_pdu.msg.eap.eap_code; + data->eap_type = data->recv_eapol_pdu.msg.eap.type; + + // Call state machine + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->eap_code = 0; + data->eap_type = 0; + + return ret_val; +} + +static int8_t supp_eap_tls_sec_prot_message_handle(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(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_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); + + if (data->eap_type == EAP_IDENTITY) { + return EAP_TLS_MSG_IDENTITY; + } + + if (!data_ptr || length < 6) { + return EAP_TLS_MSG_DECODE_ERROR; + } + + length -= 5; // EAP fields: code, id, length, type + + return eap_tls_sec_prot_lib_message_handle(data_ptr, length, new_seq_id, &data->tls_send, &data->tls_recv); +} + +static int8_t supp_eap_tls_sec_prot_message_send(sec_prot_t *prot, uint8_t eap_code, uint8_t eap_type, uint8_t tls_state) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + uint8_t flags = 0xff; + // EAP-TLS flags field is always present during TLS exchange + if (tls_state == EAP_TLS_EXCHANGE_ONGOING) { + flags = 0x00; + } + + if (eap_code == EAP_RESPONSE) { + if (eap_type == EAP_IDENTITY) { + const uint8_t identity[] = {"Anonymous"}; + const uint8_t identity_size = sizeof(identity) - 1; + if (data->tls_send.total_len != identity_size) { + eap_tls_sec_prot_lib_message_allocate(&data->tls_send, TLS_HEAD_LEN, identity_size); + memcpy(data->tls_send.data + TLS_HEAD_LEN, identity, identity_size); + } + flags = 0xff; + } + } 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); + if (!eapol_decoded_data) { + return -1; + } + + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +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); + sec_prot_timer_timeout_handle(prot, &data->common, &eap_tls_trickle_params, ticks); +} + +static void supp_eap_tls_sec_prot_tls_create_confirm(sec_prot_t *tls_prot, sec_prot_result_e result) +{ + if (result != SEC_RESULT_OK) { + sec_prot_t *prot = tls_prot->type_get(tls_prot, SEC_PROT_TYPE_EAP_TLS); + if (!prot) { + return; + } + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISH); + } +} + +static void supp_eap_tls_sec_prot_tls_finished_indication(sec_prot_t *tls_prot, sec_prot_result_e result, sec_prot_keys_t *sec_keys) +{ + (void) sec_keys; + + sec_prot_t *prot = tls_prot->type_get(tls_prot, SEC_PROT_TYPE_EAP_TLS); + if (!prot) { + return; + } + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + if (result == SEC_RESULT_OK) { + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_OVER; + } else if (result == SEC_RESULT_CONF_ERROR) { + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR; + } else { + // On failure has sent ALERT + data->tls_result = EAP_TLS_RESULT_HANDSHAKE_FAILED; + } + + data->tls_ongoing = false; + + if (data->tls_result == EAP_TLS_RESULT_HANDSHAKE_OVER || data->tls_result == EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR) { + // On fatal error and on success calls state machine to sent empty EAP-TLS message + prot->state_machine_call(prot); + } +} + +static int8_t supp_eap_tls_sec_prot_tls_send(sec_prot_t *tls_prot, void *pdu, uint16_t size) +{ + sec_prot_t *prot = tls_prot->type_get(tls_prot, SEC_PROT_TYPE_EAP_TLS); + if (!prot) { + return -1; + } + + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + + eap_tls_sec_prot_lib_message_free(&data->tls_send); + + data->tls_send.data = pdu; + data->tls_send.total_len = size; + data->tls_send.handled_len = 0; + + data->send_pending = true; + + prot->state_machine_call(prot); + + return 0; +} + +static void supp_eap_tls_sec_prot_init_tls(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + if (data->tls_prot) { + return; + } + + data->tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); + if (!data->tls_prot) { + return; + } + + data->tls_prot->header_size = TLS_HEAD_LEN; + data->tls_prot->sec_keys = prot->sec_keys; + + data->tls_prot->create_conf = supp_eap_tls_sec_prot_tls_create_confirm; + data->tls_prot->create_ind = NULL; + data->tls_prot->finished_ind = supp_eap_tls_sec_prot_tls_finished_indication; + data->tls_prot->send = supp_eap_tls_sec_prot_tls_send; + + data->tls_ongoing = true; +} + +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; + } + + sec_prot_t *tls_prot = prot->type_get(prot, SEC_PROT_TYPE_TLS); + if (tls_prot) { + tls_prot->finished_send(tls_prot); + } +} + +static void supp_eap_tls_sec_prot_state_machine(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + int8_t result; + + // EAP-TLS supplicant state machine + switch (sec_prot_state_get(&data->common)) { + case EAP_TLS_STATE_INIT: + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_REQUEST_ID); + break; + + // Wait EAP request, Identity (starts handshake on supplicant) + case EAP_TLS_STATE_REQUEST_ID: + + // Handle EAP request, Identity + if (supp_eap_tls_sec_prot_message_handle(prot) != EAP_TLS_MSG_IDENTITY) { + return; + } + + // Store sequence ID + supp_eap_tls_sec_prot_seq_id_update(prot); + + tr_debug("EAP-TLS start"); + + prot->timer_start(prot); + + // Send KMP-CREATE.indication + prot->create_ind(prot); + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_CREATE_RESP); + break; + + // Wait KMP-CREATE.response + case EAP_TLS_STATE_CREATE_RESP: + if (sec_prot_result_ok_check(&data->common)) { + // Send EAP response, Identity + supp_eap_tls_sec_prot_message_send(prot, EAP_RESPONSE, EAP_IDENTITY, EAP_TLS_EXCHANGE_NONE); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &eap_tls_trickle_params); + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_REQUEST_TLS_EAP); + } else { + // Ready to be deleted + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISHED); + } + break; + + 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); + return; + } + + // Handle EAP request (expected TLS EAP start) + result = supp_eap_tls_sec_prot_message_handle(prot); + + if (result == EAP_TLS_MSG_IDENTITY) { + // If received EAP request, Identity: re-send EAP response, Identity + supp_eap_tls_sec_prot_message_send(prot, EAP_RESPONSE, EAP_IDENTITY, EAP_TLS_EXCHANGE_NONE); + return; + } + + // Handle EAP request, TLS EAP start + if (result != EAP_TLS_MSG_START) { + return; + } + + // Store sequence ID + supp_eap_tls_sec_prot_seq_id_update(prot); + + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_REQUEST); + + // Initialize TLS protocol + supp_eap_tls_sec_prot_init_tls(prot); + // Request TLS to start (send client hello) + data->tls_prot->create_req(data->tls_prot, prot->sec_keys); + break; + + 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); + return; + } + + // EAP success + if (data->eap_code == EAP_SUCCESS) { + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISH); + return; + } else if (data->eap_code == EAP_FAILURE) { + sec_prot_result_set(&data->common, SEC_RESULT_ERROR); + sec_prot_state_set(prot, &data->common, EAP_TLS_STATE_FINISH); + return; + } else if (data->eap_code == EAP_REQ) { + // EAP request, handle EAP request, TLS EAP + result = supp_eap_tls_sec_prot_message_handle(prot); + if (result == EAP_TLS_MSG_DECODE_ERROR) { + return; + } + + // Store sequence ID + supp_eap_tls_sec_prot_seq_id_update(prot); + + // All fragments received for a message + if (result == EAP_TLS_MSG_RECEIVE_DONE && data->tls_ongoing) { + // Call TLS + data->tls_prot->receive(data->tls_prot, data->tls_recv.data, data->tls_recv.total_len); + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + sec_prot_timer_trickle_stop(&data->common); + if (data->send_pending || data->tls_result != EAP_TLS_RESULT_HANDSHAKE_FAILED) { + data->wait_tls = true; + } + } + // Wait TLS to process the received message + if (data->wait_tls) { + return; + } + } else { + data->wait_tls = false; + if (!data->tls_send.data || data->tls_result == EAP_TLS_RESULT_HANDSHAKE_FATAL_ERROR) { + // If no more data send response, TLS EAP (empty) + eap_tls_sec_prot_lib_message_allocate(&data->tls_send, TLS_HEAD_LEN, 0); + } + } + // Send EAP response + supp_eap_tls_sec_prot_message_send(prot, EAP_RESPONSE, EAP_TLS, EAP_TLS_EXCHANGE_ONGOING); + data->send_pending = false; + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &eap_tls_trickle_params); + break; + + case EAP_TLS_STATE_FINISH: + tr_debug("EAP-TLS finish"); + + // 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); + break; + + case EAP_TLS_STATE_FINISHED: + supp_eap_tls_sec_prot_delete_tls(prot); + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static void supp_eap_tls_sec_prot_seq_id_update(sec_prot_t *prot) +{ + eap_tls_sec_prot_int_t *data = eap_tls_sec_prot_get(prot); + data->eap_id_seq = data->recv_eapol_pdu.msg.eap.id_seq; +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.h b/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.h new file mode 100644 index 0000000000..78ececd7bc --- /dev/null +++ b/source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.h @@ -0,0 +1,37 @@ +/* + * 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 SUPP_EAP_TLS_SEC_PROT_H_ +#define SUPP_EAP_TLS_SEC_PROT_H_ + +/* + * Supplicant (peer) EAP-TLS security protocol. Specified in RFC 5216. + * + */ + +/** + * supp_eap_tls_sec_prot_register register supplicant EAP-TLS protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t supp_eap_tls_sec_prot_register(kmp_service_t *service); + +#endif /* SUPP_EAP_TLS_SEC_PROT_H_ */ + 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 new file mode 100644 index 0000000000..b46db4676c --- /dev/null +++ b/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2016-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. + */ + +#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 "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/eapol/kde_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" +#include "Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h" +#include "Service_Libs/hmac/hmac_sha1.h" +#include "Service_Libs/nist_aes_kw/nist_aes_kw.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "afwh" + +typedef enum { + FWH_STATE_INIT = SEC_STATE_INIT, + FWH_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ, + FWH_STATE_MESSAGE_2 = SEC_STATE_FIRST, + FWH_STATE_MESSAGE_4, + FWH_STATE_FINISH = SEC_STATE_FINISH, + FWH_STATE_FINISHED = SEC_STATE_FINISHED +} fwh_sec_prot_state_e; + +typedef enum { + FWH_MESSAGE_UNKNOWN = 0, + FWH_MESSAGE_1, + FWH_MESSAGE_2, + FWH_MESSAGE_3, + FWH_MESSAGE_4 +} fwh_sec_prot_msg_e; + +typedef struct { + sec_prot_common_t common; /**< Common data */ + eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */ + fwh_sec_prot_msg_e recv_msg; /**< Received message */ + uint8_t nonce[EAPOL_KEY_NONCE_LEN]; /**< Authenticator nonce */ + uint8_t new_ptk[PTK_LEN]; /**< PTK (384 bits) */ + void *recv_pdu; /**< received pdu */ + uint16_t recv_size; /**< received pdu size */ +} 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 auth_fwh_sec_prot_size(void); +static int8_t auth_fwh_sec_prot_init(sec_prot_t *prot); + +static void auth_fwh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static void auth_fwh_sec_prot_delete(sec_prot_t *prot); +static int8_t auth_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); +static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys); +static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot); + +static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg); +static void auth_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); + +static int8_t auth_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static int8_t auth_fwh_sec_prot_mic_validate(sec_prot_t *prot); + +#define fwh_sec_prot_get(prot) (fwh_sec_prot_int_t *) &prot->data + +int8_t auth_fwh_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_11_4WH, auth_fwh_sec_prot_size, auth_fwh_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t auth_fwh_sec_prot_size(void) +{ + return sizeof(fwh_sec_prot_int_t); +} + +static int8_t auth_fwh_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = auth_fwh_sec_prot_create_request; + prot->create_resp = 0; + prot->receive = auth_fwh_sec_prot_receive; + prot->delete = auth_fwh_sec_prot_delete; + prot->state_machine = auth_fwh_sec_prot_state_machine; + prot->timer_timeout = auth_fwh_sec_prot_timer_timeout; + + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, FWH_STATE_INIT); + + data->common.ticks = 15 * 10; // 15 seconds + + uint8_t eui64[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + sec_prot_lib_nonce_init(data->nonce, eui64, 1000); + + return 0; +} + +static void auth_fwh_sec_prot_delete(sec_prot_t *prot) +{ + // No op at the moment + (void) prot; +} + +static void auth_fwh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + prot->sec_keys = sec_keys; + + // Call state machine + prot->state_machine_call(prot); +} + +static int8_t auth_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + int8_t ret_val = -1; + + // Decoding is successful + if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) { + // 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) { + // Call state machine + data->recv_pdu = pdu; + data->recv_size = size; + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->recv_msg = FWH_MESSAGE_UNKNOWN; + data->recv_pdu = 0; + data->recv_size = 0; + + return ret_val; +} + +static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys) +{ + fwh_sec_prot_msg_e msg = FWH_MESSAGE_UNKNOWN; + + if (!eapol_pdu->msg.key.key_information.pairwise_key) { + // This is mismatch between KMP ID indicating 802.11/4WH and key type + return FWH_MESSAGE_UNKNOWN; + } + + uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + + switch (key_mask) { + case KEY_INFO_KEY_MIC: + // 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)) { + msg = FWH_MESSAGE_2; + } + 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)) { + msg = FWH_MESSAGE_4; + } + break; + default: + break; + } + + return msg; +} + +static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + uint16_t kde_len = 0; + + switch (msg) { + case FWH_MESSAGE_1: + kde_len = KDE_PMKID_LEN; + break; + case FWH_MESSAGE_3: + kde_len = KDE_GTK_LEN + KDE_LIFETIME_LEN + KDE_GTKL_LEN; + kde_len = kde_len + 8; // One 64 bit block for AES Key Wrap + kde_len = kde_padded_length_calc(kde_len); + break; + default: + break; + } + + uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len); + + if (!kde_start) { + return -1; + } + + uint8_t *kde_end = kde_start; + + switch (msg) { + case FWH_MESSAGE_1: { + uint8_t pmkid[PMKID_LEN]; + if (sec_prot_lib_pmkid_generate(prot, pmkid, true) < 0) { + ns_dyn_mem_free(kde_start); + return -1; + } + kde_end = kde_pmkid_write(kde_end, pmkid); + } + 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); + 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); + kde_end = kde_gtkl_write(kde_end, gtkl); + kde_padding_write(kde_end, kde_start + kde_len); + } + break; + default: + break; + } + + eapol_pdu_t eapol_pdu; + uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, kde_len, NULL); + + eapol_pdu.msg.key.key_information.pairwise_key = true; + + switch (msg) { + case FWH_MESSAGE_1: + 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_nonce = data->nonce; + break; + case FWH_MESSAGE_3: + 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.install = true; + eapol_pdu.msg.key.key_information.key_ack = true; + eapol_pdu.msg.key.key_information.key_mic = true; + 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; + break; + default: + break; + } + + uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(data->new_ptk, kde_start, kde_len, &eapol_pdu, eapol_pdu_size, prot->header_size); + + ns_dyn_mem_free(kde_start); + + if (eapol_pdu_frame == NULL) { + return -1; + } + + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +static void auth_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); +} + +static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + // 4WH authenticator state machine + switch (sec_prot_state_get(&data->common)) { + case FWH_STATE_INIT: + prot->timer_start(prot); + sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_REQ); + break; + + // Wait KMP-CREATE.request + case FWH_STATE_CREATE_REQ: + tr_debug("4WH start"); + + uint8_t *pmk = sec_prot_keys_pmk_get(prot->sec_keys); + if (!pmk) { // If PMK is not set fails + prot->create_conf(prot, SEC_RESULT_ERROR); + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + return; + } + + // KMP-CREATE.confirm + prot->create_conf(prot, SEC_RESULT_OK); + + // Sends 4WH Message 1 + sec_prot_lib_nonce_generate(data->nonce); + auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_1); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &fwh_trickle_params); + + sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_2); + break; + + // Wait 4WH message 2 + case FWH_STATE_MESSAGE_2: + if (sec_prot_result_timeout_check(&data->common)) { + // Re-sends 4WH Message 1 + sec_prot_lib_nonce_generate(data->nonce); + auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_1); + } else { + if (data->recv_msg != FWH_MESSAGE_2) { + return; + } + + if (auth_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) { + return; + } + if (auth_fwh_sec_prot_mic_validate(prot) < 0) { + memset(data->new_ptk, 0, PTK_LEN); + return; + } + + // Sends 4WH Message 3 + auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_3); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &fwh_trickle_params); + + sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_4); + } + break; + + // Wait 4WH message 4 + case FWH_STATE_MESSAGE_4: + if (sec_prot_result_timeout_check(&data->common)) { + // Re-sends 4WH Message 3 + auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_3); + } else { + if (data->recv_msg != FWH_MESSAGE_4) { + return; + } + if (auth_fwh_sec_prot_mic_validate(prot) < 0) { + return; + } + + 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"); + + // KMP-FINISHED.indication, + prot->finished_ind(prot, sec_prot_result_get(&data->common), 0); + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + break; + + case FWH_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static int8_t auth_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + uint8_t local_eui64[8]; + uint8_t remote_eui64[8]; + + prot->addr_get(prot, local_eui64, remote_eui64); + + uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce; + if (!remote_nonce) { + return 1; + } + + uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); + sec_prot_lib_ptk_calc(pmk, local_eui64, remote_eui64, data->nonce, remote_nonce, data->new_ptk); + + return 0; +} + +static int8_t auth_fwh_sec_prot_mic_validate(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + return sec_prot_lib_mic_validate(data->new_ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size); +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h b/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h new file mode 100644 index 0000000000..efeab123d2 --- /dev/null +++ b/source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-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 AUTH_FWH_SEC_PROT_H_ +#define AUTH_FWH_SEC_PROT_H_ + +/* + * Authenticator Four Way Handshake (4WH) security protocol. 4WH protocol is + * specified in IEEE 802.11 and Wi-SUN FANWG-FANTPS. + * + */ + +/** + * auth_fwh_sec_prot_register register authenticator 4WH protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t auth_fwh_sec_prot_register(kmp_service_t *service); + +#endif /* AUTH_FWH_SEC_PROT_H_ */ 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 new file mode 100644 index 0000000000..97fc30958f --- /dev/null +++ b/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2016-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. + */ + +#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 "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/eapol/kde_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" +#include "Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h" +#include "Service_Libs/hmac/hmac_sha1.h" +#include "Service_Libs/nist_aes_kw/nist_aes_kw.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "sfwh" + +typedef enum { + FWH_STATE_INIT = SEC_STATE_INIT, + FWH_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP, + FWH_STATE_CREATE_IND = SEC_STATE_CREATE_IND, + FWH_STATE_MESSAGE_1 = SEC_STATE_FIRST, + FWH_STATE_MESSAGE_3, + FWH_STATE_CREATE_RESP_SUPP_RETRY, + FWH_STATE_FINISH = SEC_STATE_FINISH, + FWH_STATE_FINISHED = SEC_STATE_FINISHED +} fwh_sec_prot_state_e; + +typedef enum { + FWH_MESSAGE_UNKNOWN = 0, + FWH_MESSAGE_1, + FWH_MESSAGE_2, + FWH_MESSAGE_3, + FWH_MESSAGE_4 +} fwh_sec_prot_msg_e; + +#define KEY_INFO_INSTALL 0x01 +#define KEY_INFO_KEY_ACK 0x02 +#define KEY_INFO_KEY_MIC 0x04 +#define KEY_INFO_SECURED_KEY_FRAME 0x08 + +typedef struct { + sec_prot_common_t common; /**< Common data */ + eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */ + fwh_sec_prot_msg_e recv_msg; /**< Received message */ + uint8_t snonce[EAPOL_KEY_NONCE_LEN]; /**< Supplicant nonce */ + uint8_t anonce[EAPOL_KEY_NONCE_LEN]; /**< Authenticator nonce */ + uint8_t new_ptk[PTK_LEN]; /**< PTK (384 bits) */ + uint8_t remote_eui64[8]; /**< Remote EUI-64 used to calculate PTK */ + void *recv_pdu; /**< received pdu */ + uint16_t recv_size; /**< received pdu size */ + uint64_t recv_replay_cnt; /**< received replay counter */ +} 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 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); +static void supp_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); + +static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +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 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); + +static int8_t supp_fwh_kde_handle(sec_prot_t *prot); + +#define fwh_sec_prot_get(prot) (fwh_sec_prot_int_t *) &prot->data + +int8_t supp_fwh_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_11_4WH, supp_fwh_sec_prot_size, supp_fwh_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t supp_fwh_sec_prot_size(void) +{ + return sizeof(fwh_sec_prot_int_t); +} + +static int8_t supp_fwh_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = 0; + prot->create_resp = supp_fwh_sec_prot_create_response; + prot->receive = supp_fwh_sec_prot_receive; + prot->delete = supp_fwh_sec_prot_delete; + prot->state_machine = supp_fwh_sec_prot_state_machine; + prot->timer_timeout = supp_fwh_sec_prot_timer_timeout; + + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, FWH_STATE_INIT); + + data->common.ticks = 30 * 10; // 30 seconds + + uint8_t eui64[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + sec_prot_lib_nonce_init(data->snonce, eui64, 1000); + + return 0; +} + +static void supp_fwh_sec_prot_delete(sec_prot_t *prot) +{ + // No op at the moment + (void) prot; +} + +static void supp_fwh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + // Call state machine + sec_prot_result_set(&data->common, result); + prot->state_machine_call(prot); +} + +static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + int8_t ret_val = -1; + + // 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); + if (data->recv_msg != FWH_MESSAGE_UNKNOWN) { + // Call state machine + data->recv_pdu = pdu; + data->recv_size = size; + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->recv_msg = FWH_MESSAGE_UNKNOWN; + data->recv_pdu = 0; + data->recv_size = 0; + + 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) +{ + fwh_sec_prot_msg_e msg = FWH_MESSAGE_UNKNOWN; + + if (!eapol_pdu->msg.key.key_information.pairwise_key) { + // This is mismatch between KMP ID indicating 802.11/4WH and key type + return FWH_MESSAGE_UNKNOWN; + } + + uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + + switch (key_mask) { + 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)) { + msg = FWH_MESSAGE_1; + } + break; + 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 (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; + } + } + break; + default: + break; + } + + return msg; +} + +static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + eapol_pdu_t eapol_pdu; + uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, 0, NULL); + + eapol_pdu.msg.key.key_information.pairwise_key = true; + + switch (msg) { + case FWH_MESSAGE_2: + eapol_pdu.msg.key.replay_counter = data->recv_replay_cnt; + eapol_pdu.msg.key.key_information.key_mic = true; + eapol_pdu.msg.key.key_length = 0; + eapol_pdu.msg.key.key_nonce = data->snonce; + break; + case FWH_MESSAGE_4: + eapol_pdu.msg.key.replay_counter = data->recv_replay_cnt; + eapol_pdu.msg.key.key_information.key_mic = true; + eapol_pdu.msg.key.key_information.secured_key_frame = true; + eapol_pdu.msg.key.key_length = 0; + break; + default: + break; + } + + uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(data->new_ptk, 0, 0, &eapol_pdu, eapol_pdu_size, prot->header_size); + + if (eapol_pdu_frame == NULL) { + return -1; + } + + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +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); +} + +static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + // 4WH supplicant state machine + switch (sec_prot_state_get(&data->common)) { + case FWH_STATE_INIT: + prot->timer_start(prot); + sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_1); + break; + + // Wait 4WH message 1 (starts handshake on supplicant) + case FWH_STATE_MESSAGE_1: + if (data->recv_msg != FWH_MESSAGE_1) { + return; + } + + // PMKID must be valid + if (supp_fwh_kde_handle(prot) < 0) { + return; + } + + tr_debug("4WH start"); + + // Store authenticator nonce for check when 4WH Message 3 is received + supp_fwh_sec_prot_anonce_store(prot); + sec_prot_lib_nonce_generate(data->snonce); + if (supp_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) { + return; + } + + supp_fwh_sec_prot_recv_replay_counter_store(prot); + + // Send KMP-CREATE.indication + prot->create_ind(prot); + sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_RESP); + break; + + // Wait KMP-CREATE.response + case FWH_STATE_CREATE_RESP: + if (sec_prot_result_ok_check(&data->common)) { + // Send 4WH message 2 + supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_2); + data->common.ticks = 30 * 10; // 30 seconds + sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_3); + } else { + // Ready to be deleted + sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED); + } + break; + + // Wait 4WH message 3 (message 2 has been sent) + case FWH_STATE_MESSAGE_3: + if (data->recv_msg == FWH_MESSAGE_1) { + + // PMKID must be valid + if (supp_fwh_kde_handle(prot) < 0) { + return; + } + + // Store authenticator nonce for check when 4WH Message 3 is received + supp_fwh_sec_prot_anonce_store(prot); + sec_prot_lib_nonce_generate(data->snonce); + if (supp_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) { + return; + } + // Send 4WH message 2 + supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_2); + data->common.ticks = 30 * 10; // 30 seconds + } else if (data->recv_msg != FWH_MESSAGE_3) { + return; + } + + // MIC must be valid + if (supp_fwh_sec_prot_mic_validate(prot) < 0) { + return; + } + + // Nonce must match to 4WH Message 1 + if (supp_fwh_sec_prot_anonce_validate(prot) < 0) { + return; + } + + // Must have at least GTKL + if (supp_fwh_kde_handle(prot) < 0) { + return; + } + + supp_fwh_sec_prot_recv_replay_counter_store(prot); + supp_fwh_sec_prot_security_replay_counter_update(prot); + + // Sends 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); + break; + + case FWH_STATE_FINISH: + tr_debug("4WH finish"); + + // 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); + break; + + case FWH_STATE_FINISHED: + if (sec_prot_result_timeout_check(&data->common)) { + prot->timer_stop(prot); + prot->finished(prot); + } else { + if (data->recv_msg != FWH_MESSAGE_3) { + return; + } + + // MIC must be valid + if (supp_fwh_sec_prot_mic_validate(prot) < 0) { + return; + } + + // Nonce must match to 4WH Message 1 + if (supp_fwh_sec_prot_anonce_validate(prot) < 0) { + return; + } + + // Must have at least GTKL + if (supp_fwh_kde_handle(prot) < 0) { + return; + } + + supp_fwh_sec_prot_recv_replay_counter_store(prot); + supp_fwh_sec_prot_security_replay_counter_update(prot); + + tr_debug("4WH start again"); + + // Send KMP-CREATE.indication + prot->create_ind(prot); + sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_RESP_SUPP_RETRY); + } + 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); + } + break; + + default: + break; + } +} + +static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + uint8_t local_eui64[8]; + prot->addr_get(prot, local_eui64, data->remote_eui64); + + uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce; + if (!remote_nonce) { + return 1; + } + + uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); + sec_prot_lib_ptk_calc(pmk, local_eui64, data->remote_eui64, data->snonce, remote_nonce, data->new_ptk); + + return 0; +} + +static int8_t supp_fwh_sec_prot_mic_validate(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + return sec_prot_lib_mic_validate(data->new_ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size); +} + +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; +} + +static void supp_fwh_sec_prot_anonce_store(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + memcpy(data->anonce, data->recv_eapol_pdu.msg.key.key_nonce, EAPOL_KEY_NONCE_LEN); +} + +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) { + return -1; + } + return 0; +} + +static void supp_fwh_sec_prot_security_replay_counter_update(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + sec_prot_keys_pmk_replay_cnt_set(prot->sec_keys, data->recv_eapol_pdu.msg.key.replay_counter); +} + +static int8_t supp_fwh_kde_handle(sec_prot_t *prot) +{ + fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot); + + uint16_t kde_len; + uint8_t *kde = sec_prot_lib_message_handle(data->new_ptk, &kde_len, &data->recv_eapol_pdu); + if (!kde) { + return -1; + } + + switch (data->recv_msg) { + case FWH_MESSAGE_1: { + uint8_t recv_pmkid[PMKID_LEN]; + uint8_t calc_pmkid[PMKID_LEN]; + if (kde_pmkid_read(kde, kde_len, recv_pmkid) < 0) { + goto error; + } + if (sec_prot_lib_pmkid_generate(prot, calc_pmkid, false) < 0) { + goto error; + } + if (memcmp(recv_pmkid, calc_pmkid, PMKID_LEN) != 0) { + goto error; + } + } + break; + + 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) { + goto error; + } + break; + + default: + break; + } + + ns_dyn_mem_free(kde); + return 0; + +error: + ns_dyn_mem_free(kde); + return -1; +} + +#endif /* HAVE_WS */ diff --git a/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h b/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h new file mode 100644 index 0000000000..2cb2284e3c --- /dev/null +++ b/source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-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 SUPP_FWH_SEC_PROT_H_ +#define SUPP_FWH_SEC_PROT_H_ + +/* + * Supplicant Four Way Handshake (4WH) security protocol. 4WH protocol is + * specified in IEEE 802.11 and Wi-SUN FANWG-FANTPS. + * + */ + +/** + * supp_fwh_sec_prot_register register supplicant 4WH protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t supp_fwh_sec_prot_register(kmp_service_t *service); + +#endif /* SUPP_FWH_SEC_PROT_H_ */ 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 new file mode 100644 index 0000000000..ce204f082c --- /dev/null +++ b/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "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/eapol/kde_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" +#include "Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "agkh" + +typedef enum { + GKH_STATE_INIT = SEC_STATE_INIT, + GKH_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ, + GKH_STATE_MESSAGE_2 = SEC_STATE_FIRST, + GKH_STATE_FINISH = SEC_STATE_FINISH, + GKH_STATE_FINISHED = SEC_STATE_FINISHED +} gkh_sec_prot_state_e; + +typedef enum { + GKH_MESSAGE_UNKNOWN = 0, + GKH_MESSAGE_1, + GKH_MESSAGE_2 +} gkh_sec_prot_msg_e; + +typedef struct { + sec_prot_common_t common; /**< Common data */ + eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */ + void *recv_pdu; /**< Received pdu */ + uint16_t recv_size; /**< Received pdu size */ +} gkh_sec_prot_int_t; + +static const trickle_params_t gkh_trickle_params = { + .Imin = 50, /* 5000ms; ticks are 100ms */ + .Imax = 150, /* 15000ms */ + .k = 0, /* infinity - no consistency checking */ + .TimerExpirations = 4 +}; + +static uint16_t auth_gkh_sec_prot_size(void); +static int8_t auth_gkh_sec_prot_init(sec_prot_t *prot); + +static void auth_gkh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static void auth_gkh_sec_prot_delete(sec_prot_t *prot); +static int8_t auth_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); +static gkh_sec_prot_msg_e auth_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys); +static void auth_gkh_sec_prot_state_machine(sec_prot_t *prot); + +static int8_t auth_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_e msg); +static void auth_gkh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); +static int8_t auth_gkh_sec_prot_mic_validate(sec_prot_t *prot); + +#define gkh_sec_prot_get(prot) (gkh_sec_prot_int_t *) &prot->data + +int8_t auth_gkh_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_11_GKH, auth_gkh_sec_prot_size, auth_gkh_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t auth_gkh_sec_prot_size(void) +{ + return sizeof(gkh_sec_prot_int_t); +} + +static int8_t auth_gkh_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = auth_gkh_sec_prot_create_request; + prot->create_resp = 0; + prot->receive = auth_gkh_sec_prot_receive; + prot->delete = auth_gkh_sec_prot_delete; + prot->state_machine = auth_gkh_sec_prot_state_machine; + prot->timer_timeout = auth_gkh_sec_prot_timer_timeout; + + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, GKH_STATE_INIT); + + return 0; +} + +static void auth_gkh_sec_prot_delete(sec_prot_t *prot) +{ + // No op at the moment + (void) prot; +} + +static void auth_gkh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + prot->sec_keys = sec_keys; + + // Call state machine + prot->state_machine_call(prot); +} + +static int8_t auth_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + int8_t ret_val = -1; + + // Decoding is successful + 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) { + // Call state machine + data->recv_pdu = pdu; + data->recv_size = size; + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->recv_pdu = 0; + data->recv_size = 0; + + return ret_val; +} + +static gkh_sec_prot_msg_e auth_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys) +{ + gkh_sec_prot_msg_e msg = GKH_MESSAGE_UNKNOWN; + + if (eapol_pdu->msg.key.key_information.pairwise_key) { + // This is mismatch between KMP ID indicating 802.11/GKH and key type + return GKH_MESSAGE_UNKNOWN; + } + + uint8_t key_mask = sec_prot_lib_key_mask_get(eapol_pdu); + + switch (key_mask) { + 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)) { + msg = GKH_MESSAGE_2; + } + break; + default: + break; + } + + return msg; +} + +static int8_t auth_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_e msg) +{ + uint16_t kde_len = 0; + + switch (msg) { + case GKH_MESSAGE_1: + kde_len = KDE_GTK_LEN + KDE_LIFETIME_LEN + KDE_GTKL_LEN; + kde_len = kde_len + 8; // One 64 bit block for AES Key Wrap + kde_len = kde_padded_length_calc(kde_len); + break; + default: + break; + } + + uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len); + + if (!kde_start) { + return -1; + } + + uint8_t *kde_end = kde_start; + + 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); + 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); + kde_end = kde_gtkl_write(kde_end, gtkl); + kde_padding_write(kde_end, kde_start + kde_len); + } + break; + default: + break; + } + + eapol_pdu_t eapol_pdu; + uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, kde_len, NULL); + + switch (msg) { + case GKH_MESSAGE_1: + 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_information.key_mic = true; + 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_length = 0; + break; + default: + break; + } + + uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(prot->sec_keys->ptk, kde_start, kde_len, &eapol_pdu, eapol_pdu_size, prot->header_size); + + ns_dyn_mem_free(kde_start); + + if (eapol_pdu_frame == NULL) { + return -1; + } + + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +static void auth_gkh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + sec_prot_timer_timeout_handle(prot, &data->common, &gkh_trickle_params, ticks); +} + +static void auth_gkh_sec_prot_state_machine(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + + // GKH authenticator state machine + switch (sec_prot_state_get(&data->common)) { + case GKH_STATE_INIT: + sec_prot_state_set(prot, &data->common, GKH_STATE_CREATE_REQ); + break; + + // Wait KMP-CREATE.request + case GKH_STATE_CREATE_REQ: + tr_debug("GKH start"); + + prot->timer_start(prot); + + // KMP-CREATE.confirm + prot->create_conf(prot, SEC_RESULT_OK); + + // Sends 4WH Message 1 + auth_gkh_sec_prot_message_send(prot, GKH_MESSAGE_1); + + // Start trickle timer to re-send if no response + sec_prot_timer_trickle_start(&data->common, &gkh_trickle_params); + + sec_prot_state_set(prot, &data->common, GKH_STATE_MESSAGE_2); + break; + + // Wait GKH message 2 + case GKH_STATE_MESSAGE_2: + + if (sec_prot_result_timeout_check(&data->common)) { + // Re-sends GKH Message 1 + auth_gkh_sec_prot_message_send(prot, GKH_MESSAGE_1); + } else { + if (auth_gkh_sec_prot_mic_validate(prot) < 0) { + return; + } + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISH); + } + break; + + case GKH_STATE_FINISH: + tr_debug("GKH finish"); + + // KMP-FINISHED.indication, + prot->finished_ind(prot, sec_prot_result_get(&data->common), 0); + + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISHED); + break; + + case GKH_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static int8_t auth_gkh_sec_prot_mic_validate(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + return sec_prot_lib_mic_validate(prot->sec_keys->ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size); +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.h b/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.h new file mode 100644 index 0000000000..2ec9829bd0 --- /dev/null +++ b/source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-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 AUTH_GKH_SEC_PROT_H_ +#define AUTH_GKH_SEC_PROT_H_ + +/** + * auth_gkh_sec_prot_register register authenticator GKH protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t auth_gkh_sec_prot_register(kmp_service_t *service); + +#endif /* AUTH_GKH_SEC_PROT_H_ */ 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 new file mode 100644 index 0000000000..5fd7a98d17 --- /dev/null +++ b/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2018-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. + */ + +#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 "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" +#include "Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "sgkh" + +typedef enum { + GKH_STATE_INIT = SEC_STATE_INIT, + GKH_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP, + GKH_STATE_CREATE_IND = SEC_STATE_CREATE_IND, + GKH_STATE_MESSAGE_1 = SEC_STATE_FIRST, + GKH_STATE_FINISH = SEC_STATE_FINISH, + GKH_STATE_FINISHED = SEC_STATE_FINISHED +} gkh_sec_prot_state_e; + +typedef enum { + GKH_MESSAGE_UNKNOWN = 0, + GKH_MESSAGE_1, + GKH_MESSAGE_2 +} gkh_sec_prot_msg_e; + +typedef struct { + sec_prot_common_t common; /**< Common data */ + eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */ + void *recv_pdu; /**< Received pdu */ + uint16_t recv_size; /**< Received pdu size */ +} gkh_sec_prot_int_t; + +static const trickle_params_t gkh_trickle_params = { + .Imin = 50, /* 5000ms; ticks are 100ms */ + .Imax = 150, /* 15000ms */ + .k = 0, /* infinity - no consistency checking */ + .TimerExpirations = 4 +}; + +static uint16_t supp_gkh_sec_prot_size(void); +static int8_t supp_gkh_sec_prot_init(sec_prot_t *prot); + +static void supp_gkh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); +static void supp_gkh_sec_prot_delete(sec_prot_t *prot); +static int8_t supp_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); +static gkh_sec_prot_msg_e supp_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys); +static void supp_gkh_sec_prot_state_machine(sec_prot_t *prot); + +static int8_t supp_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_e msg); +static void supp_gkh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); +static int8_t supp_gkh_sec_prot_mic_validate(sec_prot_t *prot); +static void supp_gkh_sec_prot_security_replay_counter_update(sec_prot_t *prot); +static int8_t supp_gkh_kde_handle(sec_prot_t *prot); + +#define gkh_sec_prot_get(prot) (gkh_sec_prot_int_t *) &prot->data + +int8_t supp_gkh_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_11_GKH, supp_gkh_sec_prot_size, supp_gkh_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t supp_gkh_sec_prot_size(void) +{ + return sizeof(gkh_sec_prot_int_t); +} + +static int8_t supp_gkh_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = 0; + prot->create_resp = supp_gkh_sec_prot_create_response; + prot->receive = supp_gkh_sec_prot_receive; + prot->delete = supp_gkh_sec_prot_delete; + prot->state_machine = supp_gkh_sec_prot_state_machine; + prot->timer_timeout = supp_gkh_sec_prot_timer_timeout; + + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, GKH_STATE_INIT); + + return 0; +} + +static void supp_gkh_sec_prot_delete(sec_prot_t *prot) +{ + // No op at the moment + (void) prot; +} + +static void supp_gkh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + + // Call state machine + sec_prot_result_set(&data->common, result); + prot->state_machine_call(prot); +} + +static int8_t supp_gkh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + int8_t ret_val = -1; + + // Decoding is successful + 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) { + // Call state machine + data->recv_pdu = pdu; + data->recv_size = size; + prot->state_machine(prot); + } + ret_val = 0; + } + + memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t)); + data->recv_pdu = 0; + data->recv_size = 0; + + return ret_val; +} + +static gkh_sec_prot_msg_e supp_gkh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys) +{ + gkh_sec_prot_msg_e msg = GKH_MESSAGE_UNKNOWN; + + if (eapol_pdu->msg.key.key_information.pairwise_key) { + // This is mismatch between KMP ID indicating 802.11/GKH and key type + return GKH_MESSAGE_UNKNOWN; + } + + uint8_t key_mask = sec_prot_lib_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 (eapol_pdu->msg.key.key_information.encrypted_key_data) { + // This should include the GTK KDE, Lifetime KDE and GTKL KDE. + msg = GKH_MESSAGE_1; + } + } + break; + default: + break; + } + + return msg; +} + +static int8_t supp_gkh_sec_prot_message_send(sec_prot_t *prot, gkh_sec_prot_msg_e msg) +{ + eapol_pdu_t eapol_pdu; + uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, 0, NULL); + + switch (msg) { + case GKH_MESSAGE_2: + eapol_pdu.msg.key.replay_counter = sec_prot_keys_pmk_replay_cnt_get(prot->sec_keys); + eapol_pdu.msg.key.key_information.key_mic = true; + eapol_pdu.msg.key.key_information.secured_key_frame = true; + eapol_pdu.msg.key.key_length = 0; + break; + default: + break; + } + + uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(prot->sec_keys->ptk, NULL, 0, &eapol_pdu, eapol_pdu_size, prot->header_size); + + if (eapol_pdu_frame == NULL) { + return -1; + } + + if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) { + return -1; + } + + return 0; +} + +static void supp_gkh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + sec_prot_timer_timeout_handle(prot, &data->common, &gkh_trickle_params, ticks); +} + +static void supp_gkh_sec_prot_state_machine(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + + // GKH supplicant state machine + switch (sec_prot_state_get(&data->common)) { + case GKH_STATE_INIT: + sec_prot_state_set(prot, &data->common, GKH_STATE_MESSAGE_1); + break; + + // Wait GKH message 1 (starts handshake on supplicant) + case GKH_STATE_MESSAGE_1: + if (supp_gkh_sec_prot_mic_validate(prot) < 0) { + return; + } + + if (supp_gkh_kde_handle(prot) < 0) { + return; + } + + supp_gkh_sec_prot_security_replay_counter_update(prot); + + tr_debug("GKH start"); + + prot->timer_start(prot); + + // Send KMP-CREATE.indication + prot->create_ind(prot); + sec_prot_state_set(prot, &data->common, GKH_STATE_CREATE_RESP); + break; + + // Wait KMP-CREATE.response + case GKH_STATE_CREATE_RESP: + if (sec_prot_result_ok_check(&data->common)) { + // Send GKH message 2 + supp_gkh_sec_prot_message_send(prot, GKH_MESSAGE_2); + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISH); + } else { + // Ready to be deleted + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISHED); + } + break; + + case GKH_STATE_FINISH: + tr_debug("GKH finish"); + + // KMP-FINISHED.indication, + prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys); + sec_prot_state_set(prot, &data->common, GKH_STATE_FINISHED); + break; + + case GKH_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static int8_t supp_gkh_sec_prot_mic_validate(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + return sec_prot_lib_mic_validate(prot->sec_keys->ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size); +} + +static void supp_gkh_sec_prot_security_replay_counter_update(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + sec_prot_keys_pmk_replay_cnt_set(prot->sec_keys, data->recv_eapol_pdu.msg.key.replay_counter); +} + +static int8_t supp_gkh_kde_handle(sec_prot_t *prot) +{ + gkh_sec_prot_int_t *data = gkh_sec_prot_get(prot); + + uint16_t kde_len; + uint8_t *kde = sec_prot_lib_message_handle(prot->sec_keys->ptk, &kde_len, &data->recv_eapol_pdu); + if (!kde) { + return -1; + } + + // If a valid new GTK value present, insert it + int8_t ret = sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys->gtks); + + 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; + } +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.h b/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.h new file mode 100644 index 0000000000..a8832db62f --- /dev/null +++ b/source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-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 SUPP_GKH_SEC_PROT_H_ +#define SUPP_GKH_SEC_PROT_H_ + +/** + * supp_gkh_sec_prot_register register supplicant GKH protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t supp_gkh_sec_prot_register(kmp_service_t *service); + +#endif /* SUPP_GKH_SEC_PROT_H_ */ diff --git a/source/Security/protocols/key_sec_prot/key_sec_prot.c b/source/Security/protocols/key_sec_prot/key_sec_prot.c new file mode 100644 index 0000000000..4320d44e9c --- /dev/null +++ b/source/Security/protocols/key_sec_prot/key_sec_prot.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2016-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. + */ + +#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 "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/eapol/kde_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" +#include "Security/protocols/key_sec_prot/key_sec_prot.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "ksep" + +typedef enum { + KEY_INIT = 0, + KEY_CREATE_REQ, + KEY_CREATE_RESP, + KEY_FINISH, +} key_sec_prot_state_e; + +typedef struct { + key_sec_prot_state_e state; /**< Protocol state machine state */ + sec_prot_result_e result; /**< Result for ongoing negotiation */ +} key_sec_prot_int_t; + +static uint16_t key_sec_prot_size(void); +static int8_t key_sec_prot_init(sec_prot_t *prot); + +static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static void key_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); +static void key_sec_prot_delete(sec_prot_t *prot); +static int8_t key_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); +static void key_sec_prot_state_machine(sec_prot_t *prot); + +#define key_sec_prot_get(prot) (key_sec_prot_int_t *) &prot->data + +int8_t key_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_1X_MKA_KEY, key_sec_prot_size, key_sec_prot_init) < 0) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, IEEE_802_11_GKH_KEY, key_sec_prot_size, key_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t key_sec_prot_size(void) +{ + return sizeof(key_sec_prot_int_t); +} + +static int8_t key_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = key_sec_prot_create_request; + prot->create_resp = key_sec_prot_create_response; + + prot->receive = key_sec_prot_receive; + prot->delete = key_sec_prot_delete; + prot->state_machine = key_sec_prot_state_machine; + + key_sec_prot_int_t *data = key_sec_prot_get(prot); + data->state = KEY_INIT; + data->result = SEC_RESULT_OK; + + return 0; +} + +static void key_sec_prot_delete(sec_prot_t *prot) +{ + // No op at the moment + (void) prot; +} + +static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + key_sec_prot_int_t *data = key_sec_prot_get(prot); + + uint16_t kde_len = KDE_GTKL_LEN; + + uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys); + if (pmk) { + kde_len += KDE_PMKID_LEN; + } + + uint8_t *ptk = sec_prot_keys_ptk_get(sec_keys); + if (ptk) { + kde_len += KDE_PTKID_LEN; + } + + uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len); + if (!kde_start) { + return; + } + + 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); + } + } + + 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); + } + } + + uint8_t gtkl = sec_prot_keys_gtkl_get(sec_keys->gtks); + kde_end = kde_gtkl_write(kde_end, gtkl); + + kde_len = kde_end - kde_start; + + eapol_pdu_t eapol_pdu; + + uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, kde_len, kde_start); + + uint8_t *eapol_decoded_data = ns_dyn_mem_temporary_alloc(eapol_pdu_size + prot->header_size); // In future fill with data that defines eapol message + + 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_write_pdu_frame(eapol_decoded_data + prot->header_size, &eapol_pdu); + + if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) { + data->result = SEC_RESULT_ERR_NO_MEM; + } + } + + ns_dyn_mem_free(kde_start); + + data->state = KEY_CREATE_REQ; + prot->state_machine_call(prot); +} + +static void key_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result) +{ + key_sec_prot_int_t *data = key_sec_prot_get(prot); + data->state = KEY_CREATE_RESP; + data->result = result; + prot->state_machine_call(prot); +} + +static int8_t key_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + eapol_pdu_t eapol_pdu; + // Decoding is successful + if (eapol_parse_pdu_header(pdu, size, &eapol_pdu)) { + prot->create_ind(prot); + return 0; + } else { + // No error handling yet, indicate just that ready to be deleted + prot->finished(prot); + return -1; + } +} + +static void key_sec_prot_state_machine(sec_prot_t *prot) +{ + key_sec_prot_int_t *data = key_sec_prot_get(prot); + + // Mixes currently supplicant and authenticator states + switch (data->state) { + case KEY_INIT: + // empty + break; + case KEY_CREATE_REQ: + tr_debug("initial EAPOL-Key send"); + + // KMP-CREATE.confirm + prot->create_conf(prot, data->result); + + 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); + } + // Ready to be deleted + 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); + } + // Ready to be deleted + prot->finished(prot); + break; + default: + break; + } +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/key_sec_prot/key_sec_prot.h b/source/Security/protocols/key_sec_prot/key_sec_prot.h new file mode 100644 index 0000000000..9cb00897f9 --- /dev/null +++ b/source/Security/protocols/key_sec_prot/key_sec_prot.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016-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 KEY_SEC_PROT_H_ +#define KEY_SEC_PROT_H_ + +/* + * EAPOL-Key security protocol. Protocol is used for sending and receiving + * initial EAPOL-Key message that is used to start the supplicant + * authentication. Specified in Wi-SUN FANWG-FANTPS. + * + */ + +/** + * key_sec_prot_register register EAPOL-Key protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t key_sec_prot_register(kmp_service_t *service); + +#endif /* KEY_SEC_PROT_H_ */ diff --git a/source/Security/protocols/sec_prot.h b/source/Security/protocols/sec_prot.h new file mode 100644 index 0000000000..f1dc886200 --- /dev/null +++ b/source/Security/protocols/sec_prot.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2016-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 SEC_PROT_H_ +#define SEC_PROT_H_ + +/* + * Interface between KMP API and key management security protocols. Interface + * provides abstraction for different security protocols for KMP API module. + * + * For security protocols it provides access to network, timing, callback + * security keys and network address services. + * + */ + +typedef enum { + SEC_RESULT_OK = 0, + SEC_RESULT_ERR_NO_MEM = -1, + SEC_RESULT_TIMEOUT = -2, + SEC_RESULT_ERROR = -3, + SEC_RESULT_CONF_ERROR = -4 +} sec_prot_result_e; + +typedef enum { + SEC_STATE_INIT = 0, + SEC_STATE_CREATE_REQ, + SEC_STATE_CREATE_RESP, + SEC_STATE_CREATE_IND, + SEC_STATE_FINISH, + SEC_STATE_FINISHED, + SEC_STATE_FIRST +} sec_prot_state_e; + +typedef enum { + SEC_PROT_TYPE_EAP_TLS = 0, + SEC_PROT_TYPE_TLS +} sec_prot_type_e; + +/** + * sec_prot_create_request KMP-CREATE.request to security protocol + * + * \param prot protocol + * \param sec_keys security keys + * + */ +typedef void sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); + +/** + * sec_prot_create_response KMP-CREATE.response from security protocol + * + * \param prot protocol + * \param result result + * + */ +typedef void sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); + +/** + * sec_prot_create_confirm KMP-CREATE.confirm from security protocol + * + * \param prot protocol + * \param result result + * + */ +typedef void sec_prot_create_confirm(sec_prot_t *prot, sec_prot_result_e result); + +/** + * sec_prot_create_indication KMP-CREATE.indication from security protocol + * + * \param prot protocol + * + */ +typedef void sec_prot_create_indication(sec_prot_t *prot); + +/** + * sec_prot_finished_indication KMP-FINISHED.indication from security protocol + * + * \param prot protocol + * \param result result + * \param sec_keys security keys + * + */ +typedef void sec_prot_finished_indication(sec_prot_t *prot, sec_prot_result_e result, sec_prot_keys_t *sec_keys); + +/** + * sec_prot_finished Security protocol has finished and is ready for delete + * + * \param prot protocol + * + */ +typedef void sec_prot_finished(sec_prot_t *prot); + +/** + * sec_prot_finished_send Security protocol finished send + * + * \param prot protocol + * + */ +typedef void sec_prot_finished_send(sec_prot_t *prot); + +/** + * sec_prot_receive receive a message + * + * \param prot protocol + * \param pdu pdu + * \param size pdu size + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); + +/** + * sec_prot_send send a message + * + * \param prot protocol + * \param pdu pdu + * \param size pdu size + * + * \return < 0 failure + * \return >= 0 success + * + */ +typedef int8_t sec_prot_send(sec_prot_t *prot, void *pdu, uint16_t size); + +/** + * sec_prot_delete delete the protocol data + * + * \param prot protocol + * + */ +typedef void sec_prot_delete(sec_prot_t *prot); + +/** + * sec_prot_state_machine protocol state machine + * + * \param prot protocol + * + */ +typedef void sec_prot_state_machine(sec_prot_t *prot); + +/** + * sec_prot_state_machine_call call protocol state machine + * + * \param prot protocol + * + */ +typedef void sec_prot_state_machine_call(sec_prot_t *prot); + +/** + * sec_prot_timer_start start timer + * + * \param prot protocol + * + */ +typedef void sec_prot_timer_start(sec_prot_t *prot); + +/** + * sec_prot_timer_stop stop timer + * + * \param prot protocol + * + */ +typedef void sec_prot_timer_stop(sec_prot_t *prot); + +/** + * sec_prot_timer_timeout timer timeout + * + * \param prot protocol + * \param ticks timer ticks + * + */ +typedef void sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); + +/** + * sec_prot_eui64_addr_get gets EUI-64 addresses + * + * \param prot protocol + * \param local_eui64 local EUI-64 + * \param remote_eui64 remote EUI-64 + * + */ +typedef void sec_prot_eui64_addr_get(sec_prot_t *prot, uint8_t *local_eui64, uint8_t *remote_eui64); + +/** + * sec_prot_by_type_get gets security protocol + * + * \param prot protocol + * \param type security protocol type + * + * \return security protocol or NULL + * + */ +typedef sec_prot_t *sec_prot_by_type_get(sec_prot_t *prot, uint8_t type); + +// Security protocol data +struct sec_prot_s { + sec_prot_create_request *create_req; /**< Create request */ + sec_prot_create_response *create_resp; /**< Create response */ + + sec_prot_create_confirm *create_conf; /**< Create confirm */ + sec_prot_create_indication *create_ind; /**< Create indication */ + sec_prot_finished_indication *finished_ind; /**< Finished indication */ + sec_prot_finished *finished; /**< Finished i.e. ready to be deleted */ + sec_prot_finished_send *finished_send; /**< Send finished */ + + sec_prot_send *send; /**< Protocol send */ + sec_prot_receive *receive; /**< Protocol receive */ + + sec_prot_delete *delete; /**< Protocol delete */ + + sec_prot_state_machine_call *state_machine_call; /**< Call state machine */ + sec_prot_state_machine *state_machine; /**< Protocol state machine */ + + sec_prot_timer_start *timer_start; /**< Start timer */ + sec_prot_timer_stop *timer_stop; /**< Stop timer */ + sec_prot_timer_timeout *timer_timeout; /**< Timer timeout */ + + 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_keys_t *sec_keys; /**< Security keys storage pointer */ + uint8_t header_size; /**< Header size */ + uint8_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 new file mode 100644 index 0000000000..3414fb272c --- /dev/null +++ b/source/Security/protocols/sec_prot_certs.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "Common_Protocols/ipv6_constants.h" +#include "socket_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "spce" + +int8_t sec_prot_certs_init(sec_prot_certs_t *certs) +{ + if (!certs) { + return -1; + } + + sec_prot_certs_chain_entry_init(&certs->own_cert_chain); + ns_list_init(&certs->trusted_cert_chain_list); + ns_list_init(&certs->cert_revocat_lists); + + return 0; +} + +void sec_prot_certs_delete(sec_prot_certs_t *certs) +{ + if (!certs) { + return; + } + + sec_prot_certs_chain_entry_init(&certs->own_cert_chain); + sec_prot_certs_chain_list_delete(&certs->trusted_cert_chain_list); + sec_prot_certs_revocat_lists_delete(&certs->cert_revocat_lists); +} + +cert_chain_entry_t *sec_prot_certs_chain_entry_create(void) +{ + cert_chain_entry_t *entry = ns_dyn_mem_alloc(sizeof(cert_chain_entry_t)); + if (!entry) { + return NULL; + } + sec_prot_certs_chain_entry_init(entry); + return entry; +} + +void sec_prot_certs_chain_entry_init(cert_chain_entry_t *entry) +{ + memset(entry, 0, sizeof(cert_chain_entry_t)); +} + +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) { + return -1; + } + + entry->cert[index] = cert; + entry->cert_len[index] = cert_len; + + return 0; +} + +uint8_t *sec_prot_certs_cert_get(const cert_chain_entry_t *entry, uint8_t index, uint16_t *cert_len) +{ + if (!entry || index >= SEC_PROT_CERT_CHAIN_DEPTH || !entry->cert[index]) { + return NULL; + } + + *cert_len = entry->cert_len[index]; + return entry->cert[index]; +} + +int8_t sec_prot_certs_priv_key_set(cert_chain_entry_t *entry, uint8_t *key, uint8_t key_len) +{ + if (!entry) { + return -1; + } + + entry->key = key; + entry->key_len = key_len; + + return 0; +} + +uint8_t *sec_prot_certs_priv_key_get(const cert_chain_entry_t *entry, uint8_t *key_len) +{ + if (!entry) { + return NULL; + } + *key_len = entry->key_len; + return entry->key; +} + +void sec_prot_certs_chain_list_add(cert_chain_list_t *cert_chain_list, cert_chain_entry_t *entry) +{ + ns_list_add_to_end(cert_chain_list, entry); +} + +void sec_prot_certs_chain_list_delete(cert_chain_list_t *chain_list) +{ + ns_list_foreach_safe(cert_chain_entry_t, entry, chain_list) { + ns_list_remove(chain_list, entry); + ns_dyn_mem_free(entry); + } +} + +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)); + if (!entry) { + return NULL; + } + sec_prot_certs_revocat_list_entry_init(entry); + return entry; +} + +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) +{ + if (!entry) { + return -1; + } + + entry->crl = crl; + entry->crl_len = crl_len; + + return 0; +} + +uint8_t *sec_prot_certs_revocat_list_get(const cert_revocat_list_entry_t *entry, uint16_t *crl_len) +{ + if (!entry) { + return NULL; + } + *crl_len = entry->crl_len; + return entry->crl; +} + +void sec_prot_certs_revocat_lists_add(cert_revocat_lists_t *cert_revocat_lists, cert_revocat_list_entry_t *entry) +{ + ns_list_add_to_end(cert_revocat_lists, entry); +} + +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) { + ns_list_remove(cert_revocat_lists, entry); + ns_dyn_mem_free(entry); + } +} + +#endif /* HAVE_WS */ diff --git a/source/Security/protocols/sec_prot_certs.h b/source/Security/protocols/sec_prot_certs.h new file mode 100644 index 0000000000..eb53ec5248 --- /dev/null +++ b/source/Security/protocols/sec_prot_certs.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2016-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 SEC_PROT_CERTS_H_ +#define SEC_PROT_CERTS_H_ + +/* + * Security protocols certificate interface. This is used by security protocols to + * access certificate information. + * + * Own certificate chain contains the certificate chain that is sent on TLS handshake + * to remote end. Typically this is one certificate long, and the certificate chains + * to root CA certificate or to intermediate certificate known to other end. It is + * also possible to send chain longer than one certificate. + * + * Key on own certificate chain must be the private key of the certificate used on + * TLS handshake. + * + * Trusted certificate chains contains the root CA certificates and intermediate + * certificates chains that are used to validate remote certificates. + * + */ + +#define SEC_PROT_CERT_CHAIN_DEPTH 4 + +typedef struct { + uint8_t *cert[SEC_PROT_CERT_CHAIN_DEPTH]; /**< Certificate chain (from bottom up) */ + uint16_t cert_len[SEC_PROT_CERT_CHAIN_DEPTH]; /**< Certificate chain length */ + uint8_t *key; /**< Private key */ + uint8_t key_len; /**< Private key length*/ + ns_list_link_t link; /**< Link */ +} cert_chain_entry_t; + +typedef struct { + 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; + +typedef NS_LIST_HEAD(cert_chain_entry_t, link) cert_chain_list_t; +typedef NS_LIST_HEAD(cert_revocat_list_entry_t, link) cert_revocat_lists_t; + +typedef struct { + cert_chain_entry_t own_cert_chain; /**< Own certificate chain */ + cert_chain_list_t trusted_cert_chain_list; /**< Trusted certificate chain lists */ + cert_revocat_lists_t cert_revocat_lists; /**< Certificate Revocation Lists */ +} sec_prot_certs_t; + +/** + * sec_prot_certs_init initialize certificate information + * + * \param certs certificate information + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_certs_init(sec_prot_certs_t *certs); + +/** + * sec_prot_certs_delete delete certificate information + * + * \param certs certificate information + * + */ +void sec_prot_certs_delete(sec_prot_certs_t *certs); + +/** + * sec_prot_certs_chain_entry_create allocate memory for certificate chain entry + * + * \return certificate chain entry or NULL + */ +cert_chain_entry_t *sec_prot_certs_chain_entry_create(void); + +/** + * sec_prot_certs_chain_entry_init initialize certificate chain entry + * + * \param entry certificate chain entry + */ +void sec_prot_certs_chain_entry_init(cert_chain_entry_t *entry); + +/** + * sec_prot_certs_cert_set set certificate to chain entry + * + * \param entry certificate chain entry + * \param index index for certificate + * \param cert certificate + * \param cert_len certificate length + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_certs_cert_set(cert_chain_entry_t *entry, uint8_t index, uint8_t *cert, uint16_t cert_len); + +/** + * sec_prot_certs_cert_get get certificate from chain entry + * + * \param entry certificate chain entry + * \param index index for certificate + * \param cert_len certificate length + * + * \return pointer to certificate or NULL + */ +uint8_t *sec_prot_certs_cert_get(const cert_chain_entry_t *entry, uint8_t index, uint16_t *cert_len); + +/** + * sec_prot_certs_priv_key_set set certificate (chain) private key + * + * \param entry certificate chain entry + * \param key key + * \param key_len key length + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_certs_priv_key_set(cert_chain_entry_t *entry, uint8_t *key, uint8_t key_len); + +/** + * sec_prot_certs_priv_key_get get certificate (chain) private key + * + * \param entry certificate chain entry + * \param key_len key length + * + * \return pointer to key or NULL + */ +uint8_t *sec_prot_certs_priv_key_get(const cert_chain_entry_t *entry, uint8_t *key_len); + +/** + * sec_prot_certs_chain_list_add add certificate chain entry to certificate chain list + * + * \param cert_chain_list certificate chain entry list + * \param entry certificate chain entry + */ +void sec_prot_certs_chain_list_add(cert_chain_list_t *cert_chain_list, cert_chain_entry_t *entry); + +/** + * sec_prot_certs_chain_list_delete delete certificate chain list + * + * \param cert_chain_list certificate chain entry list + */ +void sec_prot_certs_chain_list_delete(cert_chain_list_t *chain_list); + +/** + * sec_prot_certs_revocat_list_entry_create allocate memory for certificate revocation list entry + * + * \return certificate revocation list entry or NULL + */ +cert_revocat_list_entry_t *sec_prot_certs_revocat_list_entry_create(void); + +/** + * sec_prot_certs_revocat_list_entry_init initialize certificate revocation list entry + * + * \param entry certificate revocation list entry + */ +void sec_prot_certs_revocat_list_entry_init(cert_revocat_list_entry_t *entry); + +/** + * sec_prot_certs_revocat_list_set set certificate revocation list to list entry + * + * \param entry certificate revocation list entry + * \param crl certificate revocation list + * \param crl_len certificate revocation list length + * + * \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); + +/** + * sec_prot_certs_revocat_list_set set certificate revocation list from list entry + * + * \param entry certificate revocation list entry + * \param crl_len certificate revocation list length + * + * \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); + +/** + * sec_prot_certs_revocat_lists_add add certificate chain entry to certificate chain list + * + * \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_chain_list_delete delete certificate chain list + * + * \param cert_revocat_lists certificate revocation lists + */ +void sec_prot_certs_revocat_lists_delete(cert_revocat_lists_t *cert_revocat_lists); + +#endif /* SEC_PROT_CERTS_H_ */ diff --git a/source/Security/protocols/sec_prot_keys.c b/source/Security/protocols/sec_prot_keys.c new file mode 100644 index 0000000000..ce7a8dc61e --- /dev/null +++ b/source/Security/protocols/sec_prot_keys.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "NWK_INTERFACE/Include/protocol.h" +#include "Common_Protocols/ipv6_constants.h" +#include "socket_api.h" +#include "6LoWPAN/ws/ws_config.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/sec_prot_keys.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "spke" + +sec_prot_keys_t *sec_prot_keys_create(sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs) +{ + sec_prot_keys_t *sec_keys = ns_dyn_mem_alloc(sizeof(sec_prot_keys_t)); + if (!sec_keys) { + return NULL; + } + + sec_prot_keys_init(sec_keys, gtks, certs); + + return sec_keys; +} + +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_key_replay_cnt = 0; + sec_keys->gtks = gtks; + sec_keys->certs = certs; + sec_keys->pmk_set = false; + sec_keys->ptk_set = false; + sec_keys->updated = false; + sec_keys->ptk_eui_64_set = false; +} + +void sec_prot_keys_delete(sec_prot_keys_t *sec_keys) +{ + ns_dyn_mem_free(sec_keys); +} + +sec_prot_gtk_keys_t *sec_prot_keys_gtks_create(void) +{ + sec_prot_gtk_keys_t *gtks = ns_dyn_mem_alloc(sizeof(sec_prot_gtk_keys_t)); + if (!gtks) { + return NULL; + } + + sec_prot_keys_gtks_init(gtks); + + return gtks; +} + +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; +} + +void sec_prot_keys_gtks_delete(sec_prot_gtk_keys_t *gtks) +{ + ns_dyn_mem_free(gtks); +} + +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_set = true; + sec_keys->updated = true; +} + +uint8_t *sec_prot_keys_pmk_get(sec_prot_keys_t *sec_keys) +{ + if (!sec_keys->pmk_set) { + return NULL; + } + + return sec_keys->pmk; +} + +uint64_t sec_prot_keys_pmk_replay_cnt_get(sec_prot_keys_t *sec_keys) +{ + return sec_keys->pmk_key_replay_cnt; +} + +void sec_prot_keys_pmk_replay_cnt_set(sec_prot_keys_t *sec_keys, uint64_t counter) +{ + sec_keys->pmk_key_replay_cnt = counter; +} + +void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys) +{ + sec_keys->pmk_key_replay_cnt++; +} + +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_set = true; + sec_keys->updated = true; +} + +uint8_t *sec_prot_keys_ptk_get(sec_prot_keys_t *sec_keys) +{ + if (!sec_keys->ptk_set) { + return NULL; + } + + return sec_keys->ptk; +} + +void sec_prot_keys_ptk_eui_64_set(sec_prot_keys_t *sec_keys, uint8_t *eui_64) +{ + sec_keys->ptk_eui_64 = eui_64; +} + +void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, uint8_t *eui_64) +{ + 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; + } +} + +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) { + return NULL; + } + + return sec_keys->ptk_eui_64; +} + +bool sec_prot_keys_are_updated(sec_prot_keys_t *sec_keys) +{ + return sec_keys->updated; +} + +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 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); + + return gtkl; +} + +void sec_prot_keys_gtkl_set(sec_prot_gtk_keys_t *gtks, 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; + } + } + } +} + +bool sec_prot_keys_gtk_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index > GTK_NUM || !gtks->gtk[index].live) { + return false; + } + + return true; +} + +int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index > GTK_NUM || !gtks->gtk[index].set) { + return -1; + } + + gtks->gtk_set_index = index; + return 0; +} + +int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_gtk_keys_t *gtks) +{ + return gtks->gtk_set_index; +} + +void sec_prot_keys_gtk_insert_index_clear(sec_prot_gtk_keys_t *gtks) +{ + gtks->gtk_set_index = -1; +} + +uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_gtk_keys_t *gtks, uint8_t *index) +{ + 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; + } else { + return NULL; + } +} + +int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk) +{ + if (!gtk || index >= GTK_NUM) { + return -1; + } + + if (gtks->gtk[index].set && memcmp(gtks->gtk[index].key, gtk, GTK_LEN) != 0) { + return -1; + } + + 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 + memcpy(gtks->gtk[index].key, gtk, GTK_LEN); + + gtks->updated = true; + + 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) { + return false; + } + + return true; +} + +uint8_t *sec_prot_keys_gtk_get(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return NULL; + } + + return gtks->gtk[index].key; +} + +uint32_t sec_prot_keys_gtk_lifetime_get(sec_prot_gtk_keys_t *gtks, uint8_t index) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return 0; + } + + return gtks->gtk[index].lifetime; +} + +void sec_prot_keys_gtk_lifetime_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint32_t lifetime) +{ + if (index >= GTK_NUM || !gtks->gtk[index].set) { + return; + + } + gtks->gtk[index].lifetime = lifetime; + + gtks->updated = true; +} + +bool sec_prot_keys_gtks_are_updated(sec_prot_gtk_keys_t *gtks) +{ + return gtks->updated; +} + +void sec_prot_keys_gtks_updated_set(sec_prot_gtk_keys_t *gtks) +{ + gtks->updated = true; +} + +void sec_prot_keys_gtks_updated_reset(sec_prot_gtk_keys_t *gtks) +{ + gtks->updated = false; +} + +#endif /* HAVE_WS */ diff --git a/source/Security/protocols/sec_prot_keys.h b/source/Security/protocols/sec_prot_keys.h new file mode 100644 index 0000000000..0f748ad14a --- /dev/null +++ b/source/Security/protocols/sec_prot_keys.h @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2016-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 SEC_PROT_KEYS_H_ +#define SEC_PROT_KEYS_H_ + +/* + * Security protocols EAPOL key storage module. This is used by EAPOL protocols to store + * and update key information. This can be used either as supplicant key storage or as + * an authenticator key storage for a specific supplicant. Storage can be also used to + * access global security data (Group Transient Keys and certificate information). + * + */ + +#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_INDEX 0 +#define KEK_INDEX 16 + +#define PMKID_LEN 16 +#define PTKID_LEN 16 + +#define GTK_DEFAULT_LIFETIME 60 * 60 * 24 * 30 // 30 days + +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 */ +} 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 */ +} sec_prot_gtk_keys_t; + +// Security key data +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 */ + sec_prot_gtk_keys_t *gtks; /**< Group Transient Keys */ + const sec_prot_certs_t *certs; /**< Certificates */ + bool pmk_set: 1; /**< Pairwise Master Key set */ + bool ptk_set: 1; /**< Pairwise Transient Key set */ + bool updated: 1; /**< Keys has been updated */ + bool ptk_eui_64_set: 1; /**< Remote EUI-64 used to derive PTK is set */ +} sec_prot_keys_t; + +/** + * sec_prot_keys_create allocates memory for security keys + * + * \param gtks GTK keys + * \param cert_chain certificates + * + * \return security keys or NULL + */ +sec_prot_keys_t *sec_prot_keys_create(sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs); + +/** + * sec_prot_keys_init initialises security keys + * + * \param sec_keys security keys + * \param gtks GTK keys + * \param cert_chain certificates + * + */ +void sec_prot_keys_init(sec_prot_keys_t *sec_keys, sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs); + +/** + * sec_prot_keys_delete frees security keys memory + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_delete(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_gtks_create allocates memory for GTK keys + * + * \return GTK keys or NULL + * + */ +sec_prot_gtk_keys_t *sec_prot_keys_gtks_create(void); + +/** + * sec_prot_keys_gtks_init initialises GTK keys + * + * \param gtks GTK keys + * + */ +void sec_prot_keys_gtks_init(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtks_delete frees GTK keys memory + * + * \param gtks GTK keys + * + */ +void sec_prot_keys_gtks_delete(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_pmk_write writes Pairwise Master Key + * + * \param sec_keys security keys + * \param pmk Pairwise Master Key + * + */ +void sec_prot_keys_pmk_write(sec_prot_keys_t *sec_keys, uint8_t *pmk); + +/** + * sec_prot_keys_pmk_get gets Pairwise Master Key + * + * \param sec_keys security keys + * + * \return PMK or NULL + * + */ +uint8_t *sec_prot_keys_pmk_get(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_replay_cnt_get gets PMK replay counter value + * + * \param sec_keys security keys + * + * \return replay counter value + * + */ +uint64_t sec_prot_keys_pmk_replay_cnt_get(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_pmk_replay_cnt_set sets PMK replay counter value + * + * \param sec_keys security keys + * \param counter new value for replay counter + * + */ +void sec_prot_keys_pmk_replay_cnt_set(sec_prot_keys_t *sec_keys, uint64_t counter); + +/** + * sec_prot_keys_pmk_replay_cnt_increment increments PMK replay counter value by one + * + * \param sec_keys security keys + * + */ +void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_ptk_write writes Pairwise Transient Key + * + * \param sec_keys security keys + * \param ptk Pairwise Transient Key + * + */ +void sec_prot_keys_ptk_write(sec_prot_keys_t *sec_keys, uint8_t *ptk); + +/** + * sec_prot_keys_ptk_get gets Pairwise Transient Key + * + * \param sec_keys security keys + * + * \return PTK or NULL + * + */ +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 + * + * \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); + +/** + * sec_prot_keys_ptk_eui_64_write writes PTK EUI-64 + * + * \param sec_keys security keys + * \param eui64 EUI-64 + * + */ +void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, uint8_t *eui_64); + +/** + * sec_prot_keys_ptk_eui_64_get gets PTK EUI-64 + * + * \param sec_keys security keys + * + * \return EUI-64 OR NULL + * + */ +uint8_t *sec_prot_keys_ptk_eui_64_get(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_are_updated returns security keys have been updated flag + * + * \param sec_keys security keys + * + * \return TRUE keys have been updated, FALSE keys have not been updated + * + */ +bool sec_prot_keys_are_updated(sec_prot_keys_t *sec_keys); + +/** + * sec_prot_keys_updated_reset resets security keys have been updated flag + * + * \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 + * + * \param gtks GTK keys + * + * \return bit field indicating GTK liveness + * + */ +uint8_t sec_prot_keys_gtkl_get(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtkl_set sets Group Transient Key liveness + * + * \param gtks GTK keys + * \param gtkl bit field indicating GTK liveness + * + */ +void sec_prot_keys_gtkl_set(sec_prot_gtk_keys_t *gtks, uint8_t gtkl); + +/** + * sec_prot_keys_gtk_is_live checks if Group Transient Key is live + * + * \param gtks GTK keys + * \param index GTK index + * + * \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); + +/** + * sec_prot_keys_gtk_insert_index_set sets index of GTK to be inserted + * + * \param gtks GTK 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); + +/** + * sec_prot_keys_gtk_insert_index_get gets index of GTK to be inserted + * + * \param gtks GTK 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); + +/** + * sec_prot_keys_gtk_insert_index_clear clears the index of GTK to be inserted + * + * \param gtks GTK keys + * + */ +void sec_prot_keys_gtk_insert_index_clear(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_get_gtk_to_insert gets GTK that is marked to be inserted + * + * \param gtks GTK 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); + +/** + * sec_prot_keys_gtk_set sets Group Transient Key + * + * \param gtks GTK keys + * \param index index + * \param gtk gtk value + * + * \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); + +/** + * sec_prot_keys_gtk_is_set checks if Group Transient Key is set + * + * \param gtks GTK keys + * \param index index + * + * \return TRUE GTK is set, FALSE GTK is not set + * + */ +bool sec_prot_keys_gtk_is_set(sec_prot_gtk_keys_t *gtks, uint8_t index); + +/** + * sec_prot_keys_gtk_gets gets Group Transient Key + * + * \param gtks GTK keys + * \param index index + * + * \return GTK or NULL + * + */ +uint8_t *sec_prot_keys_gtk_get(sec_prot_gtk_keys_t *gtks, uint8_t index); + +/** + * sec_prot_keys_gtk_lifetime_get gets GTK lifetime + * + * \param gtks GTK keys + * \param index index + * + * \return GTK lifetime + * + */ +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 + * + * \param gtks GTK keys + * \param index index + * \param lifetime GTK lifetime + * + */ +void sec_prot_keys_gtk_lifetime_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint32_t lifetime); + +/** + * sec_prot_keys_gtks_are_updated returns GTKs have been updated flag + * + * \param gtks GTK keys + * + * \return TRUE GTKs have been updated, FALSE GTKs have not been updated + * + */ +bool sec_prot_keys_gtks_are_updated(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtks_updated_set sets GTKs have been updated flag + * + * \param gtks GTK keys + * + * + */ +void sec_prot_keys_gtks_updated_set(sec_prot_gtk_keys_t *gtks); + +/** + * sec_prot_keys_gtks_updated_set resets GTKs have been updated flag + * + * \param gtks GTK keys + * + * + */ +void sec_prot_keys_gtks_updated_reset(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 new file mode 100644 index 0000000000..f5c42d65b7 --- /dev/null +++ b/source/Security/protocols/sec_prot_lib.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#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 "Service_Libs/Trickle/trickle.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/eapol/kde_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" +#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" + +#ifdef HAVE_WS + +#define TRACE_GROUP "secl" + +void sec_prot_init(sec_prot_common_t *data) +{ + data->state = SEC_STATE_INIT; + data->result = SEC_RESULT_OK; + data->ticks = SEC_TOTAL_TIMEOUT; + data->trickle_running = false; +} + +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) { + bool running = trickle_running(&data->trickle_timer, trickle_params); + + // Checks for trickle timer expiration */ + if (trickle_timer(&data->trickle_timer, trickle_params, ticks)) { + sec_prot_result_set(data, SEC_RESULT_TIMEOUT); + prot->state_machine(prot); + } + + // Checks if maximum number of trickle timer expirations has happened + if (running && !trickle_running(&data->trickle_timer, trickle_params)) { + sec_prot_result_set(data, SEC_RESULT_TIMEOUT); + sec_prot_state_set(prot, data, SEC_STATE_FINISH); + } + } + + if (data->ticks > ticks) { + data->ticks -= ticks; + } else { + tr_debug("prot timeout"); + data->ticks = 0; + sec_prot_result_set(data, SEC_RESULT_TIMEOUT); + sec_prot_state_set(prot, data, SEC_STATE_FINISH); + } +} + +void sec_prot_timer_trickle_start(sec_prot_common_t *data, const trickle_params_t *trickle_params) +{ + trickle_start(&data->trickle_timer, trickle_params); + data->trickle_running = true; +} + +void sec_prot_timer_trickle_stop(sec_prot_common_t *data) +{ + trickle_stop(&data->trickle_timer); + data->trickle_running = false; +} + +void sec_prot_state_set(sec_prot_t *prot, sec_prot_common_t *data, uint8_t state) +{ + switch (state) { + case SEC_STATE_FINISH: + if (data->state == SEC_STATE_FINISHED) { + // Already, do not update state; + } else { + data->state = SEC_STATE_FINISH; + } + data->trickle_running = false; + data->ticks = SEC_FINISHED_TIMEOUT; + + // Go to SEC_STATE_FINISH or SEC_STATE_FINISHED + prot->state_machine(prot); + return; + + case SEC_STATE_FINISHED: + // Wait for timeout + data->trickle_running = false; + data->ticks = SEC_FINISHED_TIMEOUT; + // Clear result + sec_prot_result_set(data, SEC_RESULT_OK); + break; + + case SEC_STATE_INIT: + data->state = SEC_STATE_INIT; + prot->state_machine(prot); + return; + + default: + break; + } + + data->state = state; +} + +uint8_t sec_prot_state_get(sec_prot_common_t *data) +{ + return data->state; +} + +void sec_prot_result_set(sec_prot_common_t *data, sec_prot_result_e result) +{ + data->result = result; +} + +sec_prot_result_e sec_prot_result_get(sec_prot_common_t *data) +{ + return data->result; +} + +bool sec_prot_result_timeout_check(sec_prot_common_t *data) +{ + if (data->result == SEC_RESULT_TIMEOUT) { + data->result = SEC_RESULT_OK; + return true; + } + return false; +} + +bool sec_prot_result_ok_check(sec_prot_common_t *data) +{ + if (data->result == SEC_RESULT_OK) { + return true; + } + 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 + 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 +} + +/* + * From IEEE 802.11 how to init nonce calculation by using non-secure random + * + * PRF-256(Random number, “Init Counter”, Local MAC Address || Time) + */ +void sec_prot_lib_nonce_init(uint8_t *nonce, uint8_t *eui64, uint64_t time) +{ + // For now, use randlib + uint8_t random[EAPOL_KEY_NONCE_LEN]; + randLIB_get_n_bytes_random(random, EAPOL_KEY_NONCE_LEN); + + const uint8_t a_string_val[] = {"Init Counter"}; + const uint8_t a_string_val_len = sizeof(a_string_val) - 1; + + ieee_802_11_prf_t prf; + + uint16_t string_len = ieee_802_11_prf_setup(&prf, EAPOL_KEY_NONCE_LEN * 8, a_string_val_len, EUI64_LEN + EUI64_LEN); + uint8_t string[string_len]; + + uint8_t *a_string = ieee_802_11_prf_get_a_string(&prf, string); + memcpy(a_string, a_string_val, a_string_val_len); + + uint8_t *b_string = ieee_802_11_prf_get_b_string(&prf, string); + memcpy(b_string, eui64, EUI64_LEN); + b_string += EUI64_LEN; + memcpy(b_string, &time, sizeof(uint64_t)); + + uint16_t result_len = ieee_802_11_prf_starts(&prf, random, EAPOL_KEY_NONCE_LEN); + + ieee_802_11_prf_update(&prf, string); + + uint8_t result[result_len]; + + ieee_802_11_prf_finish(&prf, result); + + memcpy(nonce, result, EAPOL_KEY_NONCE_LEN); +} + +/* + * PTK = PRF-384(PMK, “Pairwise key expansion”, Min(AUTH EUI-64, SUP EUI-64) || + * Max(AUTH EUI-64, SUP EUI-64) || Min (Anonce, Snonce) || Max(Anonce, Snonce)) + * + * PMK is 256 bits, PTK is 382 bits + */ +int8_t sec_prot_lib_ptk_calc(const uint8_t *pmk, const uint8_t *eui64_1, const uint8_t *eui64_2, const uint8_t *nonce1, const uint8_t *nonce2, uint8_t *ptk) +{ + const uint8_t a_string_val[] = {"Pairwise key expansion"}; + const uint8_t a_string_val_len = sizeof(a_string_val) - 1; + + const uint8_t *min_eui64 = eui64_1; + const uint8_t *max_eui64 = eui64_2; + if (memcmp(eui64_1, eui64_2, EUI64_LEN) > 0) { + min_eui64 = eui64_2; + max_eui64 = eui64_1; + } + + const uint8_t *min_nonce = nonce1; + const uint8_t *max_nonce = nonce2; + if (memcmp(nonce1, nonce2, EAPOL_KEY_NONCE_LEN) > 0) { + min_nonce = nonce2; + max_nonce = nonce1; + } + + ieee_802_11_prf_t prf; + + uint16_t string_len = ieee_802_11_prf_setup(&prf, 384, a_string_val_len, EUI64_LEN + EUI64_LEN + EAPOL_KEY_NONCE_LEN + EAPOL_KEY_NONCE_LEN); + uint8_t string[string_len]; + + uint8_t *a_string = ieee_802_11_prf_get_a_string(&prf, string); + memcpy(a_string, a_string_val, a_string_val_len); + + uint8_t *b_string = ieee_802_11_prf_get_b_string(&prf, string); + memcpy(b_string, min_eui64, EUI64_LEN); + b_string += EUI64_LEN; + memcpy(b_string, max_eui64, EUI64_LEN); + b_string += EUI64_LEN; + + memcpy(b_string, min_nonce, EAPOL_KEY_NONCE_LEN); + b_string += EAPOL_KEY_NONCE_LEN; + memcpy(b_string, max_nonce, EAPOL_KEY_NONCE_LEN); + + uint16_t result_len = ieee_802_11_prf_starts(&prf, pmk, PMK_LEN); + + ieee_802_11_prf_update(&prf, string); + + uint8_t result[result_len]; + + ieee_802_11_prf_finish(&prf, result); + + memcpy(ptk, result, PTK_LEN); + + return 0; +} + +int8_t sec_prot_lib_pmkid_calc(const uint8_t *pmk, const uint8_t *auth_eui64, const uint8_t *supp_eui64, uint8_t *pmkid) +{ + const uint8_t pmk_string_val[] = {"PMK Name"}; + const uint8_t pmk_string_val_len = sizeof(pmk_string_val) - 1; + + const uint8_t data_len = pmk_string_val_len + EUI64_LEN + EUI64_LEN; + uint8_t data[data_len]; + uint8_t *ptr = data; + memcpy(ptr, pmk_string_val, pmk_string_val_len); + ptr += pmk_string_val_len; + memcpy(ptr, auth_eui64, EUI64_LEN); + ptr += EUI64_LEN; + memcpy(ptr, supp_eui64, EUI64_LEN); + + if (hmac_sha1_calc(pmk, PMK_LEN, data, data_len, pmkid) < 0) { + return -1; + } + + return 0; +} + +int8_t sec_prot_lib_ptkid_calc(const uint8_t *ptk, const uint8_t *auth_eui64, const uint8_t *supp_eui64, uint8_t *ptkid) +{ + const uint8_t ptk_string_val[] = {"PTK Name"}; + const uint8_t ptk_string_val_len = sizeof(ptk_string_val) - 1; + + const uint8_t data_len = ptk_string_val_len + EUI64_LEN + EUI64_LEN; + uint8_t data[data_len]; + uint8_t *ptr = data; + memcpy(ptr, ptk_string_val, ptk_string_val_len); + ptr += ptk_string_val_len; + memcpy(ptr, auth_eui64, EUI64_LEN); + ptr += EUI64_LEN; + memcpy(ptr, supp_eui64, EUI64_LEN); + + if (hmac_sha1_calc(ptk, PTK_LEN, data, data_len, ptkid) < 0) { + return -1; + } + + return 0; +} + +uint8_t *sec_prot_lib_message_build(uint8_t *ptk, uint8_t *kde, uint16_t kde_len, eapol_pdu_t *eapol_pdu, uint16_t eapol_pdu_size, uint8_t header_size) +{ + uint8_t *eapol_pdu_frame = ns_dyn_mem_temporary_alloc(header_size + eapol_pdu_size); + + if (!eapol_pdu_frame) { + return NULL; + } + + uint8_t *eapol_kde = eapol_write_pdu_frame(eapol_pdu_frame + header_size, eapol_pdu); + + if (kde) { + if (eapol_pdu->msg.key.key_information.encrypted_key_data) { + size_t output_len = kde_len; + if (nist_aes_key_wrap(1, &ptk[KEK_INDEX], 128, kde, kde_len - 8, eapol_kde, &output_len) < 0 || output_len != kde_len) { + ns_dyn_mem_free(eapol_pdu_frame); + return NULL; + } + } else { + memcpy(eapol_kde, kde, 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) { + ns_dyn_mem_free(eapol_pdu_frame); + return NULL; + } + eapol_write_key_packet_mic(eapol_pdu_frame + header_size, mic); + } + + return eapol_pdu_frame; +} + +uint8_t *sec_prot_lib_message_handle(uint8_t *ptk, uint16_t *kde_len, eapol_pdu_t *eapol_pdu) +{ + if (eapol_pdu->msg.key.key_data_length == 0 || eapol_pdu->msg.key.key_data == NULL) { + return NULL; + } + + uint8_t *key_data = eapol_pdu->msg.key.key_data; + uint16_t key_data_len = eapol_pdu->msg.key.key_data_length; + + uint8_t *kde = ns_dyn_mem_temporary_alloc(key_data_len); + *kde_len = key_data_len; + + 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) { + ns_dyn_mem_free(kde); + return NULL; + } + *kde_len = output_len; + } else { + memcpy(kde, key_data, *kde_len); + } + + return kde; +} + +int8_t sec_prot_lib_gtk_read(uint8_t *kde, uint16_t kde_len, sec_prot_gtk_keys_t *gtks) +{ + // If a valid new GTK value present, insert it + int8_t gtk_set_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; + if (kde_lifetime_read(kde, kde_len, &lifetime) >= 0) { + sec_prot_keys_gtk_lifetime_set(gtks, key_id, lifetime); + } + } + uint8_t gtkl; + if (kde_gtkl_read(kde, kde_len, >kl) >= 0) { + sec_prot_keys_gtkl_set(gtks, gtkl); + } else { + 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_set_index >= 0) { + sec_prot_keys_gtk_insert_index_set(gtks, gtk_set_index); + } + + return 0; +} + +int8_t sec_prot_lib_mic_validate(uint8_t *ptk, uint8_t *mic, uint8_t *pdu, uint8_t pdu_size) +{ + uint8_t recv_mic[EAPOL_KEY_MIC_LEN]; + memcpy(recv_mic, mic, EAPOL_KEY_MIC_LEN); + + 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) { + return -1; + } + if (memcmp(recv_mic, calc_mic, EAPOL_KEY_MIC_LEN) != 0) { + 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; + } + + if (is_auth) { + return sec_prot_lib_pmkid_calc(pmk, local_eui64, remote_eui64, pmkid); + } else { + return sec_prot_lib_pmkid_calc(pmk, remote_eui64, local_eui64, pmkid); + } +} + +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); + uint8_t *ptk = sec_prot_keys_pmk_get(prot->sec_keys); + if (!ptk) { + return -1; + } + + if (is_auth) { + return sec_prot_lib_ptkid_calc(ptk, local_eui64, remote_eui64, ptkid); + } else { + return sec_prot_lib_ptkid_calc(ptk, remote_eui64, local_eui64, ptkid); + } +} + + +#endif /* HAVE_WS */ diff --git a/source/Security/protocols/sec_prot_lib.h b/source/Security/protocols/sec_prot_lib.h new file mode 100644 index 0000000000..ebf32bf809 --- /dev/null +++ b/source/Security/protocols/sec_prot_lib.h @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2016-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 SEC_PROT_LIB_H_ +#define SEC_PROT_LIB_H_ + +/* + * Library functions used by security protocols. These include helper functions + * related to different hash functions, common message handling functions, and + * common timer and state machine functions. + * + */ + +#define EUI64_LEN 8 +#define SEC_TOTAL_TIMEOUT 30 * 60 * 10 // 30 minutes +#define SEC_FINISHED_TIMEOUT 5 * 10 // 5 seconds + +#define FWH_NONCE_LENGTH 32 +#define EUI64_LEN 8 +#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 { + trickle_t trickle_timer; /**< Trickle timer for re-sending */ + uint16_t ticks; /**< Timer ticks */ + int8_t state; /**< Protocol state machine state */ + sec_prot_result_e result; /**< Result for ongoing negotiation */ + bool trickle_running; /**< Trickle running */ +} sec_prot_common_t; + +/** + * sec_prot_lib_nonce_init init nonce + * + * \param nonce nonce + * \param eui64 EUI-64 + * \param time current time + * + */ +void sec_prot_lib_nonce_init(uint8_t *nonce, uint8_t *eui64, uint64_t time); + +/** + * sec_prot_lib_nonce_generate generates nonce + * + * \param nonce nonce + * + */ +void sec_prot_lib_nonce_generate(uint8_t *nonce); + +/** + * sec_prot_lib_pmkid_calc calculates PKMID from PMK + * + * \param pmk PMK + * \param auth_eui64 authenticator EUI-64 + * \param supp_eui64 supplicant EUI-64 + * \param pmkid PMKID + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_pmkid_calc(const uint8_t *pmk, const uint8_t *auth_eui64, const uint8_t *supp_eui64, uint8_t *pmkid); + +/** + * sec_prot_lib_ptkid_calc calculates PTMID from PTK + * + * \param pmk PTK + * \param auth_eui64 authenticator EUI-64 + * \param supp_eui64 supplicant EUI-64 + * \param pmkid PTKID + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_ptkid_calc(const uint8_t *ptk, const uint8_t *auth_eui64, const uint8_t *supp_eui64, uint8_t *ptkid); + +/** + * sec_prot_lib_ptk_calc calculates PTK from KMP, EUI-64s and nonces + * + * \param pmk PMK + * \param eui64_1 first EUI-64 + * \param eui64_2 second EUI-64 + * \param nonce1 first nonce + * \param nonce2 second nonce + * \param ptk PTK + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_ptk_calc(const uint8_t *pmk, const uint8_t *eui64_1, const uint8_t *eui64_2, const uint8_t *nonce1, const uint8_t *nonce2, uint8_t *ptk); + +/** + * sec_prot_lib_message_build builds a message + * + * \param ptk PTK for MIC calculation and encryption + * \param kde KDEs + * \param kde_len length of the KDEs + * \param eapol_pdu EAPOL PDU + * \param eapol_pdu_size EAPOL PDU size + * \param header_size lower level header size + * + * \return < 0 failure + * \return >= 0 success + */ +uint8_t *sec_prot_lib_message_build(uint8_t *ptk, uint8_t *kde, uint16_t kde_len, eapol_pdu_t *eapol_pdu, uint16_t eapol_pdu_size, uint8_t header_size); + +/** + * sec_prot_lib_message_handle handles a message + * + * \param ptk PTK for decryption + * \param kde_len length of the KDEs + * \param eapol_pdu EAPOL PDU + * + * \return pointer to start of the KDEs + * \return NULL failure + */ +uint8_t *sec_prot_lib_message_handle(uint8_t *ptk, uint16_t *kde_len, eapol_pdu_t *eapol_pdu); + +/** + * sec_prot_lib_gtk_read reads GTK, GTKL and lifetime KDEs + * + * \param kde KDEs + * \param kde_len length of the KDEs + * \param gtks GTKs + * + * \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); + +/** + * sec_prot_lib_mic_validate validates MIC + * + * \param ptk PTK for MIC validation + * \param kde_len length of the KDEs + * \param pdu pointer to message + * \param pdu_size message size + * + * \return < 0 failure + * \return >= 0 success + */ +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 + * + * \param prot security protocol + * \param pmkid PMK ID + * \param is_auth set for authenticator + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_pmkid_generate(sec_prot_t *prot, uint8_t *pmkid, bool is_auth); + +/** + * sec_prot_lib_ptkid_generate generate PTK ID from PTK + * + * \param prot security protocol + * \param ptkid PTK ID + * \param is_auth set for authenticator + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t sec_prot_lib_ptkid_generate(sec_prot_t *prot, uint8_t *ptkid, bool is_auth); + +/** + * sec_prot_init initiates common data + * + * \param data common data + * + */ +void sec_prot_init(sec_prot_common_t *data); + +/** + * sec_prot_init timeout handler + * + * \param prot protocol + * \param data common data + * \param trickle_params trickle parameters + * + */ +void sec_prot_timer_timeout_handle(sec_prot_t *prot, sec_prot_common_t *data, const trickle_params_t *trickle_params, uint16_t ticks); + +/** + * sec_prot_timer_trickle_start starts trickle timer + * + * \param data common data + * \param trickle_params trickle parameters + * + */ +void sec_prot_timer_trickle_start(sec_prot_common_t *data, const trickle_params_t *trickle_params); + +/** + * sec_prot_timer_trickle_stop stops trickle timer + * + * \param data common data + * + */ +void sec_prot_timer_trickle_stop(sec_prot_common_t *data); + +/** + * sec_prot_state_set sets state machine state + * + * \param prot protocol + * \param data common data + * \param state new state + * + */ +void sec_prot_state_set(sec_prot_t *prot, sec_prot_common_t *data, uint8_t state); + +/** + * sec_prot_state_get gets state machine state + * + * \param data common data + * + * \return state + * + */ +uint8_t sec_prot_state_get(sec_prot_common_t *data); + +/** + * sec_prot_result_set sets result for operation + * + * \param data common data + * \param result result + * + */ +void sec_prot_result_set(sec_prot_common_t *data, sec_prot_result_e result); + +/** + * sec_prot_result_get gets result for operation + * + * \param data common data + * + * \return result + * + */ +sec_prot_result_e sec_prot_result_get(sec_prot_common_t *data); + +/** + * sec_prot_result_timeout_check checks if result is timeout + * + * \param data common data + * + * \return true result is timeout + * \return false result is not timeout + * + */ +bool sec_prot_result_timeout_check(sec_prot_common_t *data); + +/** + * sec_prot_result_ok_check checks if result is ok + * + * \param data common data + * + * \return true result is ok + * \return false result is not ok + * + */ +bool sec_prot_result_ok_check(sec_prot_common_t *data); + +#endif /* SEC_PROT_LIB_H_ */ diff --git a/source/Security/protocols/tls_sec_prot/tls_sec_prot.c b/source/Security/protocols/tls_sec_prot/tls_sec_prot.c new file mode 100644 index 0000000000..b4cac8675c --- /dev/null +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot.c @@ -0,0 +1,551 @@ +/* + * 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. + */ + +#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 "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" +#include "Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.h" +#include "Security/protocols/tls_sec_prot/tls_sec_prot.h" +#include "Security/protocols/tls_sec_prot/tls_sec_prot_lib.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "tlsp" + +typedef enum { + TLS_STATE_INIT = SEC_STATE_INIT, + TLS_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ, + TLS_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP, + TLS_STATE_CREATE_IND = SEC_STATE_CREATE_IND, + + TLS_STATE_CLIENT_HELLO = SEC_STATE_FIRST, + TLS_STATE_CONFIGURE, + TLS_STATE_PROCESS, + + TLS_STATE_FINISH = SEC_STATE_FINISH, + TLS_STATE_FINISHED = SEC_STATE_FINISHED +} eap_tls_sec_prot_state_e; + +typedef struct { + sec_prot_common_t common; /**< Common data */ + uint8_t new_pmk[PMK_LEN]; /**< New Pair Wise Master Key */ + tls_data_t tls_send; /**< TLS send buffer */ + 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 */ +} tls_sec_prot_int_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); + +static void tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys); +static void tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result); +static void tls_sec_prot_delete(sec_prot_t *prot); +static int8_t tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size); +static void tls_sec_prot_finished_send(sec_prot_t *prot); + +static void client_tls_sec_prot_state_machine(sec_prot_t *prot); +static void server_tls_sec_prot_state_machine(sec_prot_t *prot); + +static void tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks); + +static int16_t tls_sec_prot_tls_send(void *handle, const void *buf, size_t len); +static int16_t tls_sec_prot_tls_receive(void *handle, unsigned char *buf, size_t len); +static void tls_sec_prot_tls_export_keys(void *handle, const uint8_t *master_secret, const uint8_t *eap_tls_key_material); +static void tls_sec_prot_tls_set_timer(void *handle, uint32_t inter, uint32_t fin); +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); + +#define tls_sec_prot_get(prot) (tls_sec_prot_int_t *) &prot->data + +int8_t client_tls_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, TLS_PROT, tls_sec_prot_size, client_tls_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +int8_t server_tls_sec_prot_register(kmp_service_t *service) +{ + if (!service) { + return -1; + } + + if (kmp_service_sec_protocol_register(service, TLS_PROT, tls_sec_prot_size, server_tls_sec_prot_init) < 0) { + return -1; + } + + return 0; +} + +static uint16_t tls_sec_prot_size(void) +{ + return sizeof(tls_sec_prot_int_t) + tls_sec_prot_lib_size(); +} + +static int8_t client_tls_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = tls_sec_prot_create_request; + prot->create_resp = NULL; + prot->receive = tls_sec_prot_receive; + prot->delete = tls_sec_prot_delete; + prot->state_machine = client_tls_sec_prot_state_machine; + prot->timer_timeout = tls_sec_prot_timer_timeout; + prot->finished_send = tls_sec_prot_finished_send; + + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, TLS_STATE_INIT); + + memset(data->new_pmk, 0, PMK_LEN); + data->finished = false; + // Set from security parameters + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + data->int_timer = 0; + data->fin_timer = 0; + data->timer_running = false; + data->calculating = false; + return 0; +} + +static int8_t server_tls_sec_prot_init(sec_prot_t *prot) +{ + prot->create_req = NULL; + prot->create_resp = tls_sec_prot_create_response; + prot->receive = tls_sec_prot_receive; + prot->delete = tls_sec_prot_delete; + prot->state_machine = server_tls_sec_prot_state_machine; + prot->timer_timeout = tls_sec_prot_timer_timeout; + prot->finished_send = tls_sec_prot_finished_send; + + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + sec_prot_init(&data->common); + sec_prot_state_set(prot, &data->common, TLS_STATE_INIT); + + memset(data->new_pmk, 0, PMK_LEN); + data->finished = false; + // Set from security parameters + eap_tls_sec_prot_lib_message_init(&data->tls_recv); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + data->int_timer = 0; + data->fin_timer = 0; + data->timer_running = false; + data->calculating = false; + return 0; +} + +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); +} + +static void tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys) +{ + prot->sec_keys = sec_keys; + + // Call state machine + prot->state_machine_call(prot); +} + +static void tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + // Call state machine + sec_prot_result_set(&data->common, result); + prot->state_machine_call(prot); +} + +static int8_t tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + // Discards old data + eap_tls_sec_prot_lib_message_free(&data->tls_recv); + + data->tls_recv.data = pdu; + data->tls_recv.total_len = size; + + prot->state_machine(prot); + + return 0; +} + +static void tls_sec_prot_finished_send(sec_prot_t *prot) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + prot->timer_start(prot); + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED); +} + +static void tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (data->timer_running) { + if (data->int_timer > ticks) { + data->int_timer -= ticks; + } else { + data->int_timer = 0; + } + + if (data->fin_timer > ticks) { + data->fin_timer -= ticks; + } else { + data->fin_timer = 0; + prot->state_machine_call(prot); + } + } + + if (data->calculating) { + prot->state_machine(prot); + } + + sec_prot_timer_timeout_handle(prot, &data->common, NULL, ticks); +} + +static void client_tls_sec_prot_state_machine(sec_prot_t *prot) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + int8_t result; + + switch (sec_prot_state_get(&data->common)) { + case TLS_STATE_INIT: + sec_prot_state_set(prot, &data->common, TLS_STATE_CREATE_REQ); + break; + + // Wait KMP-CREATE.request + case TLS_STATE_CREATE_REQ: + tr_debug("TLS start"); + + prot->timer_start(prot); + + prot->create_conf(prot, SEC_RESULT_OK); + + sec_prot_state_set(prot, &data->common, TLS_STATE_CONFIGURE); + + prot->state_machine_call(prot); + break; + + case TLS_STATE_CONFIGURE: + if (tls_sec_prot_tls_configure_and_connect(prot, false) < 0) { + sec_prot_result_set(&data->common, SEC_RESULT_CONF_ERROR); + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); + return; + } + sec_prot_state_set(prot, &data->common, TLS_STATE_PROCESS); + prot->state_machine(prot); + break; + + case TLS_STATE_PROCESS: + result = tls_sec_prot_lib_process((tls_security_t *) &data->tls_sec_inst); + + if (result == TLS_SEC_PROT_LIB_CALCULATING) { + data->calculating = true; + prot->state_machine_call(prot); + return; + } else { + data->calculating = false; + } + + if (data->tls_send.data) { + prot->send(prot, data->tls_send.data, data->tls_send.handled_len); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + } + + if (result != TLS_SEC_PROT_LIB_CONTINUE) { + if (result == TLS_SEC_PROT_LIB_ERROR) { + sec_prot_result_set(&data->common, SEC_RESULT_ERROR); + } + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); + } + break; + + case TLS_STATE_FINISH: + tr_debug("TLS finish"); + + data->calculating = false; + + if (sec_prot_result_ok_check(&data->common)) { + sec_prot_keys_pmk_write(prot->sec_keys, data->new_pmk); + } + + // KMP-FINISHED.indication, + 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_lib_free((tls_security_t *) &data->tls_sec_inst); + break; + + case TLS_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +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; + + switch (sec_prot_state_get(&data->common)) { + case TLS_STATE_INIT: + sec_prot_state_set(prot, &data->common, TLS_STATE_CLIENT_HELLO); + break; + + // Wait EAP request, Identity (starts handshake on supplicant) + case TLS_STATE_CLIENT_HELLO: + + tr_debug("TLS start"); + + prot->timer_start(prot); + + sec_prot_state_set(prot, &data->common, TLS_STATE_CREATE_RESP); + + // Send KMP-CREATE.indication + prot->create_ind(prot); + break; + + // Wait KMP-CREATE.response + case TLS_STATE_CREATE_RESP: + if (sec_prot_result_ok_check(&data->common)) { + sec_prot_state_set(prot, &data->common, TLS_STATE_CONFIGURE); + prot->state_machine_call(prot); + } else { + // Ready to be deleted + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED); + } + break; + + case TLS_STATE_CONFIGURE: + if (tls_sec_prot_tls_configure_and_connect(prot, true) < 0) { + sec_prot_result_set(&data->common, SEC_RESULT_CONF_ERROR); + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); + return; + } + sec_prot_state_set(prot, &data->common, TLS_STATE_PROCESS); + prot->state_machine(prot); + break; + + case TLS_STATE_PROCESS: + result = tls_sec_prot_lib_process((tls_security_t *) &data->tls_sec_inst); + + if (result == TLS_SEC_PROT_LIB_CALCULATING) { + data->calculating = true; + prot->state_machine_call(prot); + return; + } else { + data->calculating = false; + } + + if (data->tls_send.data) { + prot->send(prot, data->tls_send.data, data->tls_send.handled_len); + eap_tls_sec_prot_lib_message_init(&data->tls_send); + } + + if (result != TLS_SEC_PROT_LIB_CONTINUE) { + if (result == TLS_SEC_PROT_LIB_ERROR) { + sec_prot_result_set(&data->common, SEC_RESULT_ERROR); + } + sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH); + } + break; + + case TLS_STATE_FINISH: + tr_debug("TLS finish"); + + data->calculating = false; + + if (sec_prot_result_ok_check(&data->common)) { + sec_prot_keys_pmk_write(prot->sec_keys, data->new_pmk); + } + + // KMP-FINISHED.indication, + 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_lib_free((tls_security_t *) &data->tls_sec_inst); + break; + + case TLS_STATE_FINISHED: + prot->timer_stop(prot); + prot->finished(prot); + break; + + default: + break; + } +} + +static int16_t tls_sec_prot_tls_send(void *handle, const void *buf, size_t len) +{ + sec_prot_t *prot = handle; + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (!data->tls_send.data) { + eap_tls_sec_prot_lib_message_allocate(&data->tls_send, prot->header_size, TLS_SEC_PROT_BUFFER_SIZE); + } + + memcpy(data->tls_send.data + prot->header_size + data->tls_send.handled_len, buf, len); + data->tls_send.handled_len += len; + + return len; +} + +static int16_t tls_sec_prot_tls_receive(void *handle, unsigned char *buf, size_t len) +{ + sec_prot_t *prot = handle; + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (data->tls_recv.data && len > 0) { + + uint16_t copy_len = len; + bool all_copied = false; + + if ((uint16_t) copy_len >= data->tls_recv.total_len - data->tls_recv.handled_len) { + copy_len = data->tls_recv.total_len - data->tls_recv.handled_len; + all_copied = true; + } + + memcpy(buf, data->tls_recv.data + data->tls_recv.handled_len, copy_len); + + data->tls_recv.handled_len += copy_len; + + if (all_copied) { + eap_tls_sec_prot_lib_message_free(&data->tls_recv); + } + + return copy_len; + } + + return TLS_SEC_PROT_LIB_NO_DATA; +} + +static void tls_sec_prot_tls_export_keys(void *handle, const uint8_t *master_secret, const uint8_t *eap_tls_key_material) +{ + (void) master_secret; + + sec_prot_t *prot = handle; + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (eap_tls_key_material) { + memcpy(data->new_pmk, eap_tls_key_material, PMK_LEN); + } + +#ifdef EXTRA_DEBUG_INFO + const uint8_t *print_data = eap_tls_key_material; + uint16_t print_data_len = 128; + while (true) { + tr_debug("EAP-TLS key material %s\n", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len)); + if (print_data_len > 32) { + print_data_len -= 32; + print_data += 32; + } else { + break; + } + } +#endif +} + +static void tls_sec_prot_tls_set_timer(void *handle, uint32_t inter, uint32_t fin) +{ + sec_prot_t *prot = handle; + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (fin == 0) { + data->timer_running = false; + data->int_timer = 0; + data->fin_timer = 0; + return; + } + + data->timer_running = true; + data->int_timer = inter / 100; + data->fin_timer = fin / 100; +} + +static int8_t tls_sec_prot_tls_get_timer(void *handle) +{ + sec_prot_t *prot = handle; + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (!data->timer_running) { + return TLS_SEC_PROT_LIB_TIMER_CANCELLED; + } else if (data->fin_timer == 0) { + return TLS_SEC_PROT_LIB_TIMER_FIN_EXPIRY; + } else if (data->int_timer == 0) { + return TLS_SEC_PROT_LIB_TIMER_INT_EXPIRY; + } + + return TLS_SEC_PROT_LIB_TIMER_NO_EXPIRY; +} + +static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_server) +{ + tls_sec_prot_int_t *data = tls_sec_prot_get(prot); + + if (tls_sec_prot_lib_init((tls_security_t *)&data->tls_sec_inst) < 0) { + return -1; + } + + tls_sec_prot_lib_set_cb_register((tls_security_t *)&data->tls_sec_inst, prot, + tls_sec_prot_tls_send, tls_sec_prot_tls_receive, tls_sec_prot_tls_export_keys, + 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) { + return -1; + } + + return 0; +} + +#endif /* HAVE_WS */ diff --git a/source/Security/protocols/tls_sec_prot/tls_sec_prot.h b/source/Security/protocols/tls_sec_prot/tls_sec_prot.h new file mode 100644 index 0000000000..5ed9621ea7 --- /dev/null +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot.h @@ -0,0 +1,49 @@ +/* + * 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 TLS_SEC_PROT_H_ +#define TLS_SEC_PROT_H_ + +/* + * TLS security protocol + * + */ + +#define TLS_SEC_PROT_BUFFER_SIZE 1200 // Send buffer size (maximum size for a TLS data for a flight) + +/** + * client_tls_sec_prot_register register client TLS protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t client_tls_sec_prot_register(kmp_service_t *service); + +/** + * server_tls_sec_prot_register register server TLS protocol to KMP service + * + * \param service KMP service + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t server_tls_sec_prot_register(kmp_service_t *service); + + +#endif /* TLS_SEC_PROT_H_ */ 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 new file mode 100644 index 0000000000..9ce586ba97 --- /dev/null +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c @@ -0,0 +1,442 @@ +/* + * 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. + */ + +#include "nsconfig.h" +#include +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "Security/protocols/sec_prot_certs.h" +#include "Security/protocols/tls_sec_prot/tls_sec_prot_lib.h" +#include "mbedtls/sha256.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" +#include "mbedtls/ssl_cookie.h" +#include "mbedtls/entropy.h" +#include "mbedtls/entropy_poll.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/ssl_ciphersuites.h" +#include "mbedtls/debug.h" + +#include "mbedtls/ssl_internal.h" + +#ifdef HAVE_WS + +#define TRACE_GROUP "tlsl" + +#define TLS_HANDSHAKE_TIMEOUT_MIN 25000 +#define TLS_HANDSHAKE_TIMEOUT_MAX 201000 + +//#define TLS_SEC_PROT_LIB_TLS_DEBUG // Enable mbed TLS debug traces + +struct tls_security_s { + mbedtls_ssl_config conf; /**< mbed TLS SSL configuration */ + mbedtls_ssl_context ssl; /**< mbed TLS SSL context */ + + mbedtls_ctr_drbg_context ctr_drbg; /**< mbed TLS pseudo random number generator context */ + mbedtls_entropy_context entropy; /**< mbed TLS entropy context */ + + mbedtls_x509_crt cacert; /**< CA certificate(s) */ + 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) */ + + 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 */ + tls_sec_prot_lib_export_keys *export_keys; /**< Export keys callback */ + tls_sec_prot_lib_set_timer *set_timer; /**< Set timer callback */ + tls_sec_prot_lib_get_timer *get_timer; /**< Get timer callback */ +}; + +static void tls_sec_prot_lib_ssl_set_timer(void *ctx, uint32_t int_ms, uint32_t fin_ms); +static int tls_sec_prot_lib_ssl_get_timer(void *ctx); +static int tls_sec_lib_entropy_poll(void *data, unsigned char *output, size_t len, size_t *olen); +static int tls_sec_prot_lib_ssl_send(void *ctx, const unsigned char *buf, size_t len); +static int tls_sec_prot_lib_ssl_recv(void *ctx, unsigned char *buf, size_t len); +static int tls_sec_prot_lib_ssl_export_keys(void *ctx, const unsigned char *ms, + const unsigned char *kb, size_t maclen, size_t keylen, size_t ivlen); +static void tls_sec_prot_lib_random_extract(tls_security_t *sec, const uint8_t *buf, uint16_t len); +#ifdef TLS_SEC_PROT_LIB_TLS_DEBUG +static void tls_sec_prot_lib_debug(void *ctx, int level, const char *file, int line, const char *string); +#endif + +int8_t tls_sec_prot_lib_init(tls_security_t *sec) +{ + const char *pers = "ws_tls"; + + 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); + + if (mbedtls_entropy_add_source(&sec->entropy, tls_sec_lib_entropy_poll, NULL, + 128, MBEDTLS_ENTROPY_SOURCE_WEAK) < 0) { + return -1; + } + + if ((mbedtls_ctr_drbg_seed(&sec->ctr_drbg, mbedtls_entropy_func, &sec->entropy, + (const unsigned char *) pers, strlen(pers))) != 0) { + return -1; + } + + return 0; +} + +uint16_t tls_sec_prot_lib_size(void) +{ + return sizeof(tls_security_t); +} + +void tls_sec_prot_lib_set_cb_register(tls_security_t *sec, void *handle, + tls_sec_prot_lib_send *send, tls_sec_prot_lib_receive *receive, + tls_sec_prot_lib_export_keys *export_keys, tls_sec_prot_lib_set_timer *set_timer, + tls_sec_prot_lib_get_timer *get_timer) +{ + if (!sec) { + return; + } + + sec->handle = handle; + sec->send = send; + sec->receive = receive; + sec->export_keys = export_keys; + sec->set_timer = set_timer; + sec->get_timer = get_timer; +} + +void tls_sec_prot_lib_free(tls_security_t *sec) +{ + mbedtls_x509_crt_free(&sec->cacert); + mbedtls_x509_crl_free(&sec->crl); + mbedtls_x509_crt_free(&sec->owncert); + mbedtls_pk_free(&sec->pkey); + mbedtls_entropy_free(&sec->entropy); + mbedtls_ctr_drbg_free(&sec->ctr_drbg); + mbedtls_ssl_config_free(&sec->conf); + mbedtls_ssl_free(&sec->ssl); +} + +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]) { + return -1; + } + + // Parse own certificate chain + uint8_t index = 0; + while (true) { + uint16_t cert_len; + uint8_t *cert = sec_prot_certs_cert_get(&certs->own_cert_chain, index, &cert_len); + if (!cert) { + if (index == 0) { + return -1; + } + break; + } + if (mbedtls_x509_crt_parse(&sec->owncert, cert, cert_len) < 0) { + return -1; + } + index++; + } + + // Parse private key + uint8_t key_len; + uint8_t *key = sec_prot_certs_priv_key_get(&certs->own_cert_chain, &key_len); + if (!key) { + return -1; + } + + if (mbedtls_pk_parse_key(&sec->pkey, key, key_len, NULL, 0) < 0) { + return -1; + } + + // Configure own certificate chain and private key + if (mbedtls_ssl_conf_own_cert(&sec->conf, &sec->owncert, &sec->pkey) != 0) { + return -1; + } + + // Parse trusted certificate chains + ns_list_foreach(cert_chain_entry_t, entry, &certs->trusted_cert_chain_list) { + index = 0; + while (true) { + uint16_t cert_len; + uint8_t *cert = sec_prot_certs_cert_get(entry, index, &cert_len); + if (!cert) { + if (index == 0) { + return -1; + } + break; + } + if (mbedtls_x509_crt_parse(&sec->cacert, cert, cert_len) < 0) { + return -1; + } + index++; + } + } + + // 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); + if (!crl) { + break; + } + if (mbedtls_x509_crl_parse(&sec->crl, crl, crl_len) < 0) { + return -1; + } + } + + // Configure trusted certificates and certificate revocation lists + 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); + + return 0; +} + +int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_prot_certs_t *certs) +{ + if (!sec) { + return -1; + } + + int endpoint = MBEDTLS_SSL_IS_CLIENT; + if (is_server) { + endpoint = MBEDTLS_SSL_IS_SERVER; + } + + if ((mbedtls_ssl_config_defaults(&sec->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, 0)) != 0) { + return -1; + } + + // Configure random number generator + mbedtls_ssl_conf_rng(&sec->conf, mbedtls_ctr_drbg_random, &sec->ctr_drbg); + +#ifdef MBEDTLS_ECP_RESTARTABLE + // Set ECC calculation maximum operations (affects only client) + mbedtls_ecp_set_max_ops(ECC_CALCULATION_MAX_OPS); +#endif + + if ((mbedtls_ssl_setup(&sec->ssl, &sec->conf)) != 0) { + return -1; + } + + // Set calbacks + mbedtls_ssl_set_bio(&sec->ssl, sec, tls_sec_prot_lib_ssl_send, tls_sec_prot_lib_ssl_recv, NULL); + mbedtls_ssl_set_timer_cb(&sec->ssl, sec, tls_sec_prot_lib_ssl_set_timer, tls_sec_prot_lib_ssl_get_timer); + + // Configure certificates, keys and certificate revocation list + if (tls_sec_prot_lib_configure_certificates(sec, certs) != 0) { + tr_debug("security credential configure failed"); + return -1; + } + + + // Configure ciphersuites + static const int sec_suites[] = { + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + 0, + 0, + 0 + }; + mbedtls_ssl_conf_ciphersuites(&sec->conf, sec_suites); + +#ifdef TLS_SEC_PROT_LIB_TLS_DEBUG + mbedtls_ssl_conf_dbg(&sec->conf, tls_sec_prot_lib_debug, sec); + mbedtls_debug_set_threshold(5); +#endif + + // Export keys callback + mbedtls_ssl_conf_export_keys_cb(&sec->conf, tls_sec_prot_lib_ssl_export_keys, sec); + + 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); + + return 0; +} + +#ifdef TLS_SEC_PROT_LIB_TLS_DEBUG +static void tls_sec_prot_lib_debug(void *ctx, int level, const char *file, int line, const char *string) +{ + (void) ctx; + tr_debug("%i %s %i %s", level, file, line, string); +} +#endif + +int8_t tls_sec_prot_lib_process(tls_security_t *sec) +{ + int ret = -1; + + while (ret != MBEDTLS_ERR_SSL_WANT_READ) { + ret = mbedtls_ssl_handshake_step(&sec->ssl); + +#ifdef MBEDTLS_ECP_RESTARTABLE + if (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS /* || ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS */) { + return TLS_SEC_PROT_LIB_CALCULATING; + } +#endif + + if (ret && (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { + return TLS_SEC_PROT_LIB_ERROR; + } + + if (sec->ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER) { + return TLS_SEC_PROT_LIB_HANDSHAKE_OVER; + } + } + + return TLS_SEC_PROT_LIB_CONTINUE; +} + +static void tls_sec_prot_lib_ssl_set_timer(void *ctx, uint32_t int_ms, uint32_t fin_ms) +{ + tls_security_t *sec = (tls_security_t *)ctx; + sec->set_timer(sec->handle, int_ms, fin_ms); +} + +static int tls_sec_prot_lib_ssl_get_timer(void *ctx) +{ + tls_security_t *sec = (tls_security_t *)ctx; + return sec->get_timer(sec->handle); +} + +static int tls_sec_prot_lib_ssl_send(void *ctx, const unsigned char *buf, size_t len) +{ + tls_security_t *sec = (tls_security_t *)ctx; + + tls_sec_prot_lib_random_extract(sec, buf, len); + + return sec->send(sec->handle, buf, len); +} + +static int tls_sec_prot_lib_ssl_recv(void *ctx, unsigned char *buf, size_t len) +{ + tls_security_t *sec = (tls_security_t *)ctx; + int16_t ret = sec->receive(sec->handle, buf, len); + + if (ret == TLS_SEC_PROT_LIB_NO_DATA) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + + tls_sec_prot_lib_random_extract(sec, buf, len); + + return ret; +} + +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 (*buf++ != 22 && len < 5) { + return; + } + + buf++; // version + buf++; + + buf++; // length + buf++; + + step++; + + if (len < 6) { + return; + } + } + + if (step == 1) { + if (*buf == 0x01) { // Client hello + random_ptr = sec->client_random; + } else if (*buf == 0x02) { // Server hello + random_ptr = sec->server_random; + } else { + return; + } + buf++; + + buf++; // length + buf++; + buf++; + + buf++; // version + buf++; + + memcpy(random_ptr, buf, 32); + + step = 0; + } +} + +static int tls_sec_prot_lib_ssl_export_keys(void *ctx, const unsigned char *ms, + const unsigned char *kb, size_t maclen, + size_t keylen, size_t ivlen) +{ + (void) kb; + (void) maclen; + (void) keylen; + (void) ivlen; + + tls_security_t *sec = (tls_security_t *)ctx; + + uint8_t eap_tls_key_material[128]; + uint8_t random[64]; + memcpy(random, sec->client_random, 32); + memcpy(&random[32], sec->server_random, 32); + + sec->ssl.handshake->tls_prf(ms, 48, "client EAP encryption", + random, 64, eap_tls_key_material, 128); + + sec->export_keys(sec->handle, ms, eap_tls_key_material); + return 0; +} + +static int tls_sec_lib_entropy_poll(void *ctx, unsigned char *output, size_t len, size_t *olen) +{ + (void)ctx; + + char *c = (char *)ns_dyn_mem_temporary_alloc(len); + if (!c) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + memset(c, 0, len); + for (uint16_t i = 0; i < len; i++) { + *(c + i) = (char)randLIB_get_8bit(); + } + memmove(output, c, len); + *olen = len; + + ns_dyn_mem_free(c); + return (0); +} + +#endif /* HAVE_WS */ + diff --git a/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.h b/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.h new file mode 100644 index 0000000000..fc4a40b9f0 --- /dev/null +++ b/source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.h @@ -0,0 +1,169 @@ +/* + * 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 TLS_SEC_PROT_LIB_H_ +#define TLS_SEC_PROT_LIB_H_ + +/* + * TLS security protocol library to connect to mbed TLS + * + */ + +typedef struct tls_security_s tls_security_t; + +typedef enum { + TLS_SEC_PROT_LIB_ERROR = -2, + TLS_SEC_PROT_LIB_NO_DATA = -1, + TLS_SEC_PROT_LIB_CONTINUE = 0, + TLS_SEC_PROT_LIB_CALCULATING, + TLS_SEC_PROT_LIB_HANDSHAKE_OVER, +} tls_sec_prot_lib_ret_e; + +typedef enum { + TLS_SEC_PROT_LIB_TIMER_CANCELLED = -1, + TLS_SEC_PROT_LIB_TIMER_NO_EXPIRY = 0, + TLS_SEC_PROT_LIB_TIMER_INT_EXPIRY = 1, + TLS_SEC_PROT_LIB_TIMER_FIN_EXPIRY = 2, +} tls_sec_prot_lib_timer_e; + +// Maximum operations made on one round of ECC calculation +#define ECC_CALCULATION_MAX_OPS 200 + +/** + * tls_sec_prot_lib_init initialize security library + * + * \param sec security library instance + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t tls_sec_prot_lib_init(tls_security_t *sec); + +/** + * tls_sec_prot_lib_size get security library instance size + * + * \return size + */ +uint16_t tls_sec_prot_lib_size(void); + +/** + * tls_sec_prot_lib_send send data callback + * + * \param handle caller defined handle + * \param buf buffer to be send + * \param len length of the buffer + * + * \return length of the send data + */ +typedef int16_t tls_sec_prot_lib_send(void *handle, const void *buf, size_t len); + +/** + * tls_sec_prot_lib_receive receive data callback + * + * \param handle caller defined handle + * \param buf receive buffer + * \param len receive buffer length + * + * \return length of the received data written to receive buffer + * \return TLS_SEC_PROT_LIB_NO_DATA no more data received + */ +typedef int16_t tls_sec_prot_lib_receive(void *handle, unsigned char *buf, size_t len); + +/** + * tls_sec_prot_lib_set_timer set timer callback + * + * \param handle caller defined handle + * \param inter intermediate timeout + * \param fin final timeout + * + */ +typedef void tls_sec_prot_lib_set_timer(void *handle, uint32_t inter, uint32_t fin); + +/** + * tls_sec_prot_lib_get_timer get timer callback + * + * \param handle caller defined handle + * + * \return TLS_SEC_PROT_LIB_TIMER_CANCELLED timer cancelled + * \return TLS_SEC_PROT_LIB_TIMER_FIN_EXPIRY final timeout has expired + * \return TLS_SEC_PROT_LIB_TIMER_INT_EXPIRY intermediate timeout has expired + * \retunt TLS_SEC_PROT_LIB_TIMER_NO_EXPIRY timer has not expired + * + */ +typedef int8_t tls_sec_prot_lib_get_timer(void *handle); + +/** + * tls_sec_prot_lib_export_keys export key material after handshake is completed + * + * \param handle caller defined handle + * \param master_secret TLS master secret, 48 bytes + * \param eap_tls_key_material EAP TLS key material, 128 bytes + * + */ +typedef void tls_sec_prot_lib_export_keys(void *handle, const uint8_t *master_secret, const uint8_t *eap_tls_key_material); + +/** + * tls_sec_prot_lib_set_cb_register register callbacks to library + * + * \param sec security library instance + * \param handle caller defined handle + * \param send send data callback + * \param receive receive data callback + * \param export_keys export keys callback + * \param set_timer set timer callback + * \param get_timer get timer callback + * + */ +void tls_sec_prot_lib_set_cb_register(tls_security_t *sec, void *handle, + tls_sec_prot_lib_send *send, tls_sec_prot_lib_receive *receive, + tls_sec_prot_lib_export_keys *export_keys, tls_sec_prot_lib_set_timer *set_timer, + tls_sec_prot_lib_get_timer *get_timer); + +/** + * tls_sec_prot_lib_free free security library internal data (e.g. TLS data) + * + * \param sec security library instance + * + */ +void tls_sec_prot_lib_free(tls_security_t *sec); + +/** + * tls_sec_prot_lib_connect start TLS handshake + * + * \param sec security library instance + * \param is_server TRUE if TLS server, FALSE for TLS client + * \param certs certificates + * + * \return < 0 failure + * \return >= 0 success + */ +int8_t tls_sec_prot_lib_connect(tls_security_t *sec, bool is_server, const sec_prot_certs_t *certs); + +/** + * tls_sec_prot_lib_process process TLS (call e.g. after incoming message) + * + * \param sec Security library instance + * + * \return TLS_SEC_PROT_LIB_ERROR failure, failure, stop TLS negotiation + * \return TLS_SEC_PROT_LIB_CONTINUE continue processing (send output message) + * \return TLS_SEC_PROT_LIB_CALCULATING calculation ongoing, call process again + * \return TLS_SEC_PROT_LIB_HANDSHAKE_OVER handshake completed successfully + * + */ +int8_t tls_sec_prot_lib_process(tls_security_t *sec); + +#endif /* TLS_SEC_PROT_LIB_H_ */ diff --git a/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls.c b/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls.c index 1f94ba0aa6..36bdd0f590 100644 --- a/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls.c +++ b/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2017, Arm Limited and affiliates. + * Copyright (c) 2006-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls_adapter.c b/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls_adapter.c index e0b0514faf..afc9132db8 100644 --- a/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls_adapter.c +++ b/source/Service_Libs/CCM_lib/mbedOS/aes_mbedtls_adapter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -83,7 +83,9 @@ arm_aes_context_t *arm_aes_start(const uint8_t key[static 16]) arm_aes_context_t *context = mbed_tls_context_get(); if (context) { mbedtls_aes_init(&context->ctx); - mbedtls_aes_setkey_enc(&context->ctx, key, 128); + if (0 != mbedtls_aes_setkey_enc(&context->ctx, key, 128)) { + return NULL; + } } return context; } diff --git a/source/Service_Libs/Neighbor_cache/neighbor_cache.c b/source/Service_Libs/Neighbor_cache/neighbor_cache.c index 63258e70a8..4f057a5574 100644 --- a/source/Service_Libs/Neighbor_cache/neighbor_cache.c +++ b/source/Service_Libs/Neighbor_cache/neighbor_cache.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/SHA256_Lib/ns_sha256.c b/source/Service_Libs/SHA256_Lib/ns_sha256.c index 7973bfc4f2..10fce9c06a 100644 --- a/source/Service_Libs/SHA256_Lib/ns_sha256.c +++ b/source/Service_Libs/SHA256_Lib/ns_sha256.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2017, Arm Limited and affiliates. + * Copyright (c) 2006-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/Trickle/trickle.c b/source/Service_Libs/Trickle/trickle.c index 6c4c28f922..4640b49371 100644 --- a/source/Service_Libs/Trickle/trickle.c +++ b/source/Service_Libs/Trickle/trickle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/blacklist/blacklist.c b/source/Service_Libs/blacklist/blacklist.c index 5deb976aed..a614ed12c1 100644 --- a/source/Service_Libs/blacklist/blacklist.c +++ b/source/Service_Libs/blacklist/blacklist.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/etx/etx.c b/source/Service_Libs/etx/etx.c index 735a6e1774..c7b3b63a32 100644 --- a/source/Service_Libs/etx/etx.c +++ b/source/Service_Libs/etx/etx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "platform/arm_hal_phy.h" #include "net_interface.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "MLE/mle.h" #include "NWK_INTERFACE/Include/protocol.h" #include "NWK_INTERFACE/Include/protocol_stats.h" diff --git a/source/Service_Libs/fhss/channel_functions.c b/source/Service_Libs/fhss/channel_functions.c new file mode 100644 index 0000000000..1d2d0c372c --- /dev/null +++ b/source/Service_Libs/fhss/channel_functions.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018-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. + */ +#include "nsconfig.h" + +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b, 14); \ + a ^= c; a -= rot(c, 11); \ + b ^= a; b -= rot(a, 25); \ + c ^= b; c -= rot(b, 16); \ + a ^= c; a -= rot(c, 4); \ + b ^= a; b -= rot(a, 14); \ + c ^= b; c -= rot(b, 24); \ +} + +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c, 16); c += b; \ + b -= a; b ^= rot(a, 19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +static uint32_t global_seed = 1; + + +uint16_t tr51_calc_nearest_prime_number(uint16_t start_value) +{ + if (start_value < 2) { + return 0; + } + uint16_t divider = start_value - 1; + while (start_value) { + if (start_value % divider--) { + if (divider == 1) { + break; + } + } else { + divider = ++start_value - 1; + } + } + return start_value; +} + +static void tr51_seed_rand(uint32_t seed) +{ + if (!seed) { + seed = 1; + } + global_seed = seed; +} + +static int32_t tr51_get_rand(void) +{ + uint32_t random_val = ((global_seed * 1103515245) + 12345) & 0x7fffffff; + global_seed = random_val; + return random_val; +} + +/** + * @brief Calculate channel table based on TR51 channel function. + * @param number_of_channels Number of channels in table. + * @param nearest_prime Nearest prime number. Must be equal to or larger than number_of_channels. + * @param channel_table Output channel table. Has to be at least nearest_prime in length. + */ +static void tr51_calculate_channel_table(uint16_t number_of_channels, uint16_t nearest_prime, int16_t *channel_table) +{ + int32_t i, j, k; + tr51_seed_rand(1); + for (i = 0; i < nearest_prime; i++) { + channel_table[i] = -1; + } + for (i = 0; i < number_of_channels; i++) { + j = tr51_get_rand() % number_of_channels; + k = 0; + while (k <= i) { + if (j == channel_table[k]) { + j = tr51_get_rand() % number_of_channels; + k = 0; + } else { + k = k + 1; + } + } + channel_table[i] = j; + } +} + +static void tr51_compute_cfd(uint8_t *mac, uint8_t *first_element, uint8_t *step_size, uint16_t channel_table_length) +{ + *first_element = (mac[5] ^ mac[6] ^ mac[7]) % channel_table_length; + *step_size = (mac[7] % (channel_table_length - 1)) + 1; +} + +static uint8_t tr51_find_excluded(int32_t channel, uint32_t *excluded_channels) +{ + if (excluded_channels != NULL) { + uint8_t index = channel / 32; + channel %= 32; + if (excluded_channels[index] & ((uint32_t)1 << channel)) { + return true; + } + } + return false; +} + +/** + * @brief Calculate hopping sequence for a specific peer using tr51 channel function. + * @param channel_table Used channel table. + * @param channel_table_length Length of the used channel table. + * @param first_element Start generated by CFD function. + * @param step_size Step size generated by CFD function. + * @param output_table Output hopping sequence table. + * @param excluded_channels Bit mask where excluded channels are set to 1. + * @return Number of channels in sequence. + */ +static uint16_t tr51_calculate_hopping_sequence(int16_t *channel_table, uint16_t channel_table_length, uint8_t first_element, uint8_t step_size, uint8_t *output_table, uint32_t *excluded_channels) +{ + uint16_t cntr = channel_table_length; + uint8_t index = first_element; + uint8_t slot = 0; + while (cntr--) { + if (channel_table[index] != -1) { + if (tr51_find_excluded(channel_table[index], excluded_channels) == false) { + output_table[slot] = channel_table[index]; + slot++; + } + } + index += step_size; + index %= channel_table_length; + } + return slot; +} + +static uint32_t dh1cf_hashword(const uint32_t *key, size_t key_length, uint32_t init_value) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (((uint32_t)key_length) << 2) + init_value; + while (key_length > 3) { + a += key[0]; + b += key[1]; + c += key[2]; + mix(a, b, c); + key_length -= 3; + key += 3; + } + switch (key_length) { + case 3: + c += key[2]; + /* fall through */ + case 2: + b += key[1]; + /* fall through */ + case 1: + a += key[0]; + final(a, b, c); + /* fall through */ + case 0: + break; + } + return c; +} + +int32_t dh1cf_get_uc_channel_index(uint16_t slot_number, uint8_t *mac, int16_t number_of_channels) +{ + 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]; + channel_number = dh1cf_hashword(key, 3, 0) % number_of_channels; + return channel_number; +} + +int32_t dh1cf_get_bc_channel_index(uint16_t slot_number, uint16_t bsi, int16_t number_of_channels) +{ + int32_t channel_number; + uint32_t key[3]; + key[0] = (uint32_t)slot_number; + key[1] = bsi << 16; + key[2] = 0; + channel_number = dh1cf_hashword(key, 3, 0) % number_of_channels; + return channel_number; +} + +int tr51_init_channel_table(int16_t *channel_table, int16_t number_of_channels) +{ + uint16_t nearest_prime = tr51_calc_nearest_prime_number(number_of_channels); + tr51_calculate_channel_table(number_of_channels, nearest_prime, channel_table); + return 0; +} + +int32_t tr51_get_uc_channel_index(int16_t *channel_table, uint8_t *output_table, uint16_t slot_number, uint8_t *mac, int16_t number_of_channels, uint32_t *excluded_channels) +{ + uint16_t nearest_prime = tr51_calc_nearest_prime_number(number_of_channels); + uint8_t first_element; + uint8_t step_size; + tr51_compute_cfd(mac, &first_element, &step_size, nearest_prime); + tr51_calculate_hopping_sequence(channel_table, nearest_prime, first_element, step_size, output_table, excluded_channels); + return output_table[slot_number]; +} + +int32_t tr51_get_bc_channel_index(int16_t *channel_table, uint8_t *output_table, uint16_t slot_number, uint16_t bsi, int16_t number_of_channels, uint32_t *excluded_channels) +{ + uint16_t nearest_prime = tr51_calc_nearest_prime_number(number_of_channels); + uint8_t mac[8] = {0, 0, 0, 0, 0, 0, (uint8_t)(bsi >> 8), (uint8_t)bsi}; + uint8_t first_element; + uint8_t step_size; + tr51_compute_cfd(mac, &first_element, &step_size, nearest_prime); + tr51_calculate_hopping_sequence(channel_table, nearest_prime, first_element, step_size, output_table, excluded_channels); + return output_table[slot_number]; +} diff --git a/source/Service_Libs/fhss/fhss.c b/source/Service_Libs/fhss/fhss.c index 400e458557..8ee00ed7f6 100644 --- a/source/Service_Libs/fhss/fhss.c +++ b/source/Service_Libs/fhss/fhss.c @@ -45,6 +45,7 @@ static int8_t fhss_beacon_create_tasklet(fhss_structure_t *fhss_structure); static void fhss_beacon_tasklet_func(arm_event_s *event); static int fhss_beacon_periodic_start(fhss_structure_t *fhss_structure, uint32_t time_to_first_beacon); static void fhss_beacon_periodic_stop(fhss_structure_t *fhss_structure); +static int fhss_reset_synch_monitor(fhss_synch_monitor_s *synch_monitor); fhss_structure_t *fhss_enable(fhss_api_t *fhss_api, const fhss_configuration_t *fhss_configuration, const fhss_timer_t *fhss_timer, fhss_statistics_t *fhss_statistics) { @@ -80,6 +81,7 @@ fhss_structure_t *fhss_enable(fhss_api_t *fhss_api, const fhss_configuration_t * if (!fhss_struct->bs->fhss_configuration.fhss_max_synch_interval) { fhss_struct->bs->fhss_configuration.fhss_max_synch_interval = 240; } + fhss_reset_synch_monitor(&fhss_struct->bs->synch_monitor); ns_list_init(&fhss_struct->fhss_failed_tx_list); fhss_struct->own_hop = 0xff; fhss_reset(fhss_struct); @@ -263,6 +265,11 @@ static int fhss_update_txrx_slots(fhss_structure_t *fhss_structure) tx_slot_up_limit += (tx_slot_length * 2); } } +#ifdef FHSS_CHANNEL_DEBUG_CBS + if (fhss_bc_switch && fhss_structure->bs->tx_allowed != tx_allowed) { + fhss_bc_switch(); + } +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ fhss_structure->bs->tx_allowed = tx_allowed; return 0; } @@ -735,7 +742,7 @@ static int16_t fhss_synch_state_set_callback(const fhss_api_t *api, fhss_states memcpy(fhss_structure->synch_parent, beacon_info->source_address, 8); platform_enter_critical(); // Calculate time since the Beacon was received - uint32_t elapsed_time = fhss_structure->fhss_api->read_timestamp(fhss_structure->fhss_api) - beacon_info->timestamp; + uint32_t elapsed_time = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api) - beacon_info->timestamp; // Synchronize to given PAN fhss_beacon_received(fhss_structure, beacon_info->synch_info, elapsed_time); platform_exit_critical(); @@ -978,7 +985,6 @@ static int fhss_tx_handle_callback(const fhss_api_t *api, bool is_broadcast_addr if (frame_type == FHSS_DATA_FRAME) { if (is_broadcast_addr == true) { if (fhss_is_current_channel_broadcast(fhss_structure) == false) { - tr_info("Broadcast on UC channel -> Back to queue"); return -3; } } @@ -1059,7 +1065,7 @@ static uint8_t *fhss_beacon_encode_raw(uint8_t *buffer, const fhss_synchronizati return buffer; } -static void fhss_beacon_build(fhss_structure_t *fhss_structure, uint8_t *dest) +static void fhss_beacon_build(fhss_structure_t *fhss_structure, uint8_t *dest, uint32_t tx_time) { fhss_synchronization_beacon_payload_s temp_payload; platform_enter_critical(); @@ -1075,8 +1081,12 @@ static void fhss_beacon_build(fhss_structure_t *fhss_structure, uint8_t *dest) temp_payload.number_of_broadcast_channels = config->fhss_number_of_bc_channels; temp_payload.number_of_tx_slots = config->fhss_number_of_tx_slots; temp_payload.time_since_last_beacon = 0; // XXX not available yet - uint32_t tx_time = fhss_get_tx_time(fhss_structure, 71, 0, 0); - temp_payload.processing_delay = fhss_structure->bs->fhss_configuration.fhss_tuning_parameters.tx_processing_delay + tx_time; + uint32_t time_to_tx = 0; + uint32_t cur_time = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); + if (cur_time < tx_time) { + time_to_tx = tx_time - cur_time; + } + temp_payload.processing_delay = fhss_structure->bs->fhss_configuration.fhss_tuning_parameters.tx_processing_delay + time_to_tx; temp_payload.superframe_length = config->fhss_superframe_length; temp_payload.number_of_superframes_per_channel = config->fhss_number_of_superframes; platform_exit_critical(); @@ -1091,7 +1101,7 @@ static int16_t fhss_write_synch_info_callback(const fhss_api_t *api, uint8_t *pt if (!fhss_structure || !ptr || (frame_type != FHSS_SYNCH_FRAME)) { return -1; } - fhss_beacon_build(fhss_structure, ptr); + fhss_beacon_build(fhss_structure, ptr, tx_time); return FHSS_SYNCH_INFO_LENGTH; } @@ -1123,6 +1133,9 @@ static bool fhss_data_tx_fail_callback(const fhss_api_t *api, uint8_t handle, in if (fhss_structure->fhss_state == FHSS_UNSYNCHRONIZED) { return false; } +#ifdef FHSS_CHANNEL_DEBUG + tr_info("TX failed on ch: %u", debug_destination_channel); +#endif /*FHSS_CHANNEL_DEBUG*/ // Channel retries are disabled -> return if (fhss_structure->bs->fhss_configuration.fhss_number_of_channel_retries == 0) { return false; @@ -1162,7 +1175,7 @@ static void fhss_receive_frame_callback(const fhss_api_t *api, uint16_t pan_id, fhss_update_synch_parent_address(fhss_structure); platform_enter_critical(); // Calculate time since the Beacon was received - uint32_t elapsed_time = api->read_timestamp(api) - timestamp; + uint32_t elapsed_time = fhss_structure->callbacks.read_timestamp(api) - timestamp; // Synchronize to given PAN fhss_beacon_received(fhss_structure, synch_info, elapsed_time); platform_exit_critical(); @@ -1422,6 +1435,6 @@ static void fhss_beacon_tasklet_func(arm_event_s *event) } // Update Beacon info lifetimes else if (event->event_type == FHSS_UPDATE_SYNCH_INFO_STORAGE) { - fhss_update_beacon_info_lifetimes(fhss_structure, fhss_read_timestamp_cb(fhss_structure->fhss_api)); + fhss_update_beacon_info_lifetimes(fhss_structure, fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api)); } } diff --git a/source/Service_Libs/fhss/fhss_channel.c b/source/Service_Libs/fhss/fhss_channel.c index d799d910c3..8ad6c3cf52 100644 --- a/source/Service_Libs/fhss/fhss_channel.c +++ b/source/Service_Libs/fhss/fhss_channel.c @@ -27,8 +27,14 @@ #define TRACE_GROUP "fhss" -// Enable this flag to use channel traces -// #define FHSS_CHANNEL_DEBUG +#ifdef FHSS_CHANNEL_DEBUG_CBS +void (*fhss_uc_switch)(void) = NULL; +void (*fhss_bc_switch)(void) = NULL; +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ + +#ifdef FHSS_CHANNEL_DEBUG +uint8_t debug_destination_channel = 0; +#endif /*FHSS_CHANNEL_DEBUG*/ static uint8_t fhss_get_bc_index(const fhss_structure_t *fhss_structure); @@ -138,13 +144,22 @@ bool fhss_change_to_next_channel(fhss_structure_t *fhss_structure) next_channel = channel_list_get_channel(fhss_structure->bs->fhss_configuration.channel_mask, channel_index_tmp); fhss_structure->rx_channel = next_channel; -#ifdef FHSS_CHANNEL_DEBUG + if (fhss_is_current_channel_broadcast(fhss_structure) == true) { +#ifdef FHSS_CHANNEL_DEBUG tr_info("%"PRIu32" BC %u", fhss_structure->platform_functions.fhss_get_timestamp(fhss_structure->fhss_api), next_channel); - } else { - tr_info("%"PRIu32" UC %u", fhss_structure->platform_functions.fhss_get_timestamp(fhss_structure->fhss_api), next_channel); - } #endif /*FHSS_CHANNEL_DEBUG*/ + } else { +#ifdef FHSS_CHANNEL_DEBUG_CBS + if (fhss_uc_switch) { + fhss_uc_switch(); + } +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ +#ifdef FHSS_CHANNEL_DEBUG + tr_info("%"PRIu32" UC %u", fhss_structure->platform_functions.fhss_get_timestamp(fhss_structure->fhss_api), next_channel); +#endif /*FHSS_CHANNEL_DEBUG*/ + } + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, next_channel); return broadcast_channel; } @@ -231,7 +246,7 @@ int fhss_change_to_tx_channel(fhss_structure_t *fhss_structure, uint8_t *destina uint8_t destination_channel = fhss_get_destination_channel(fhss_structure, destination_address); fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, destination_channel); #ifdef FHSS_CHANNEL_DEBUG - tr_info("TX channel: %u", destination_channel); + debug_destination_channel = destination_channel; #endif /*FHSS_CHANNEL_DEBUG*/ } } diff --git a/source/Service_Libs/fhss/fhss_channel.h b/source/Service_Libs/fhss/fhss_channel.h index 42094aee0a..af35ba6475 100644 --- a/source/Service_Libs/fhss/fhss_channel.h +++ b/source/Service_Libs/fhss/fhss_channel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,4 +24,18 @@ int fhss_change_to_tx_channel(fhss_structure_t *fhss_structure, uint8_t *destina int fhss_change_to_parent_channel(fhss_structure_t *fhss_structure); int fhss_change_to_rx_channel(fhss_structure_t *fhss_structure); + +// Enable this flag to use channel traces +// #define FHSS_CHANNEL_DEBUG +// Enable this flag to use debug callbacks +// #define FHSS_CHANNEL_DEBUG_CBS + +#ifdef FHSS_CHANNEL_DEBUG +extern uint8_t debug_destination_channel; +#endif /*FHSS_CHANNEL_DEBUG*/ +#ifdef FHSS_CHANNEL_DEBUG_CBS +extern void (*fhss_uc_switch)(void); +extern void (*fhss_bc_switch)(void); +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ + #endif /*FHSS_CHANNEL_H_*/ diff --git a/source/Service_Libs/fhss/fhss_ws.c b/source/Service_Libs/fhss/fhss_ws.c new file mode 100644 index 0000000000..04f7bfa050 --- /dev/null +++ b/source/Service_Libs/fhss/fhss_ws.c @@ -0,0 +1,844 @@ +/* + * Copyright (c) 2018-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. + */ +#include "nsconfig.h" +#include "ns_types.h" +#include "fhss_api.h" +#include "fhss_config.h" +#include "fhss.h" +#include "fhss_common.h" +#include "channel_list.h" +#include "channel_functions.h" +#include "fhss_ws.h" +#include "nsdynmemLIB.h" +#include "common_functions.h" +#include "eventOS_callback_timer.h" +#include "randLIB.h" +#include "ns_trace.h" +#include "platform/arm_hal_interrupt.h" +#include + +#define TRACE_GROUP "fhss" + +// Enable this flag to use channel traces +// #define FHSS_CHANNEL_DEBUG +// Enable this flag to use debug callbacks +// #define FHSS_CHANNEL_DEBUG_CBS + +#ifdef FHSS_CHANNEL_DEBUG_CBS +void (*fhss_uc_switch)(void) = NULL; +void (*fhss_bc_switch)(void) = NULL; +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ +// Seconds to milliseconds +#define S_TO_MS(x) (((int32_t)x)*1000) +// Milliseconds to seconds +#define MS_TO_S(x) divide_integer(x, 1000) +// Seconds to microseconds +#define S_TO_US(x) (((int32_t)x)*1000000) +// Microseconds to seconds +#define US_TO_S(x) divide_integer(x, 1000000) +// Milliseconds to microseconds +#define MS_TO_US(x) (((int32_t)x)*1000) +// Microseconds to milliseconds +#define US_TO_MS(x) divide_integer(x, 1000) +// Milliseconds to nanoseconds +#define MS_TO_NS(x) (((int32_t)x)*1000000) +// Nanoseconds to milliseconds +#define NS_TO_MS(x) divide_integer(x, 1000000) +// Microseconds to nanoseconds +#define US_TO_NS(x) (((int32_t)x)*1000) +// Nanoseconds to microseconds +#define NS_TO_US(x) divide_integer(x, 1000) +#define DEF_2E24 0x1000000 +#define IE_HEADER_LENGTH_MASK 0x007f +#define IE_HEADER_ID_MASK 0x7f80 +#define WH_IE_ID 0x2a +#define WH_SUB_ID_UTT 1 +#define WH_SUB_ID_BT 2 + +#ifdef HAVE_WS + +struct ws_ie_t { + uint8_t *content_ptr; + unsigned length: 7; + uint8_t id; +}; + +static int fhss_ws_manage_channel_table_allocation(fhss_structure_t *fhss_structure, uint16_t channel_count); +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); + +// This function supports rounding up +static int32_t divide_integer(int32_t dividend, int32_t divisor) +{ + if (dividend < 0) { + return (dividend - divisor / 2) / divisor; + } + return (dividend + divisor / 2) / divisor; +} + +static uint32_t get_remaining_slots_us(fhss_structure_t *fhss_structure, void (*callback)(const fhss_api_t *api, uint16_t), uint32_t max_timeout_us) +{ + uint32_t remaining_time_us = fhss_structure->platform_functions.fhss_get_remaining_slots(callback, fhss_structure->fhss_api); + // When remaining time goes negative, use 0. + if (remaining_time_us > max_timeout_us) { + remaining_time_us = 0; + } + return remaining_time_us; +} + +void fhss_ws_start_timer(fhss_structure_t *fhss_structure, uint32_t time, void (*callback)(const fhss_api_t *fhss_api, uint16_t)) +{ + // Number of millisecond slots in timeout(us) + int32_t time_in_ms = divide_integer(time, 1000); + // Reduce the compensation (per millisecond) from timeout. + time -= NS_TO_US(time_in_ms * fhss_structure->ws->drift_per_millisecond_ns); + fhss_start_timer(fhss_structure, time, callback); +} + +fhss_structure_t *fhss_ws_enable(fhss_api_t *fhss_api, const fhss_ws_configuration_t *fhss_configuration, const fhss_timer_t *fhss_timer) +{ + if (!fhss_api || !fhss_configuration || !fhss_timer) { + tr_err("Invalid FHSS enable configuration"); + return NULL; + } + int channel_count = channel_list_count_channels(fhss_configuration->channel_mask); + if (channel_count <= 0) { + // There must be at least one configured channel in channel list + return NULL; + } + fhss_structure_t *fhss_struct = fhss_allocate_instance(fhss_api, fhss_timer); + if (!fhss_struct) { + return NULL; + } + fhss_struct->ws = ns_dyn_mem_alloc(sizeof(fhss_ws_t)); + if (!fhss_struct->ws) { + fhss_free_instance(fhss_api); + return NULL; + } + memset(fhss_struct->ws, 0, sizeof(fhss_ws_t)); + if (fhss_ws_manage_channel_table_allocation(fhss_struct, channel_count)) { + ns_dyn_mem_free(fhss_struct->ws); + fhss_free_instance(fhss_api); + tr_error("Failed to allocate channel tables"); + return NULL; + } + + fhss_struct->fhss_event_timer = eventOS_callback_timer_register(fhss_event_timer_cb); + fhss_struct->ws->fhss_configuration = *fhss_configuration; + fhss_struct->number_of_channels = channel_count; + fhss_struct->own_hop = 0xff; + fhss_struct->rx_channel = fhss_configuration->unicast_fixed_channel; + fhss_struct->ws->min_synch_interval = DEFAULT_MIN_SYNCH_INTERVAL; + fhss_set_txrx_slot_length(fhss_struct); + ns_list_init(&fhss_struct->fhss_failed_tx_list); + return fhss_struct; +} + +static int fhss_ws_manage_channel_table_allocation(fhss_structure_t *fhss_structure, uint16_t channel_count) +{ + // Must allocate channel table for TR51 + if (!fhss_structure->ws->tr51_channel_table && !fhss_structure->ws->tr51_output_table) { + fhss_structure->ws->tr51_channel_table = ns_dyn_mem_alloc(sizeof(int16_t) * tr51_calc_nearest_prime_number(channel_count)); + if (!fhss_structure->ws->tr51_channel_table) { + return -1; + } + fhss_structure->ws->tr51_output_table = ns_dyn_mem_alloc(sizeof(int16_t) * channel_count); + if (!fhss_structure->ws->tr51_output_table) { + ns_dyn_mem_free(fhss_structure->ws->tr51_channel_table); + return -1; + } + tr51_init_channel_table(fhss_structure->ws->tr51_channel_table, channel_count); + } + return 0; +} + +static uint8_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; + if (!number_of_tx_slots) { + return 0; + } + fhss_structure->ws->txrx_slot_length_ms = (fhss_structure->ws->fhss_configuration.fhss_broadcast_interval - fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) / (number_of_tx_slots * 2); + return number_of_tx_slots; +} + +static int32_t fhss_ws_calc_bc_channel(fhss_structure_t *fhss_structure) +{ + int32_t next_channel = fhss_structure->ws->fhss_configuration.broadcast_fixed_channel; + + if (fhss_structure->ws->fhss_configuration.ws_bc_channel_function == WS_TR51CF) { + next_channel = tr51_get_bc_channel_index(fhss_structure->ws->tr51_channel_table, fhss_structure->ws->tr51_output_table, fhss_structure->ws->bc_slot, fhss_structure->ws->fhss_configuration.bsi, fhss_structure->number_of_channels, NULL); + if (++fhss_structure->ws->bc_slot == fhss_structure->number_of_channels) { + fhss_structure->ws->bc_slot = 0; + } + } else if (fhss_structure->ws->fhss_configuration.ws_bc_channel_function == WS_DH1CF) { + next_channel = dh1cf_get_bc_channel_index(fhss_structure->ws->bc_slot, fhss_structure->ws->fhss_configuration.bsi, fhss_structure->number_of_channels); + fhss_structure->ws->bc_slot++; + } else if (fhss_structure->ws->fhss_configuration.ws_bc_channel_function == WS_VENDOR_DEF_CF) { + if (fhss_structure->ws->fhss_configuration.vendor_defined_cf) { + next_channel = fhss_structure->ws->fhss_configuration.vendor_defined_cf(fhss_structure->fhss_api, fhss_structure->ws->bc_slot, NULL, fhss_structure->ws->fhss_configuration.bsi, fhss_structure->number_of_channels); + } + } +#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); +#endif /*FHSS_CHANNEL_DEBUG*/ + return next_channel; +} + +static void fhss_broadcast_handler(const fhss_api_t *fhss_api, uint16_t delay) +{ + int32_t next_channel; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(fhss_api); + if (!fhss_structure) { + return; + } + + 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; + return; + } + if (fhss_structure->ws->is_on_bc_channel == false) { + fhss_ws_start_timer(fhss_structure, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) - (delay * fhss_structure->platform_functions.fhss_resolution_divider), fhss_broadcast_handler); + fhss_structure->ws->is_on_bc_channel = true; + next_channel = fhss_structure->ws->bc_channel = fhss_ws_calc_bc_channel(fhss_structure); + + /* Start timer with random timeout to trigger broadcast TX queue poll event. + * Min random is 1/50 of the channel dwell interval. + * Max random is 1/10 of the channel dwell interval. + * Event timer resolution is 50us. + */ + uint32_t bc_dwell_us = MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval); + uint16_t bc_min_random = (bc_dwell_us / 50) / 50; + 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 { + 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. + * Event timer resolution is 50us. + */ + 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; + bool tx_allowed = fhss_ws_check_tx_allowed(fhss_structure); + if (!tx_allowed) { + uc_min_random += (txrx_slot_length_us) / 50; + uc_max_random += (txrx_slot_length_us) / 50; + } + eventOS_callback_timer_start(fhss_structure->fhss_event_timer, randLIB_get_random_in_range(uc_min_random, uc_max_random)); + +#ifdef FHSS_CHANNEL_DEBUG + tr_info("%"PRIu32" UC %u", fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api), fhss_structure->rx_channel); +#endif /*FHSS_CHANNEL_DEBUG*/ + } + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, next_channel); +#ifdef FHSS_CHANNEL_DEBUG_CBS + if (fhss_bc_switch) { + fhss_bc_switch(); + } +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ +} + +static int own_floor(float value) +{ + return (int)value; +} + +static int own_ceil(float value) +{ + int ivalue = (int)value; + if (value == (float)ivalue) { + return ivalue; + } + return ivalue + 1; +} + +static void fhss_event_timer_cb(int8_t timer_id, uint16_t slots) +{ + (void) slots; + uint16_t queue_size = 0; + fhss_structure_t *fhss_structure = fhss_get_object_with_timer_id(timer_id); + + + if (fhss_structure->ws->is_on_bc_channel == true) { + queue_size = fhss_structure->callbacks.read_tx_queue_size(fhss_structure->fhss_api, true); + } 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; + 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); + } + queue_size = fhss_structure->callbacks.read_tx_queue_size(fhss_structure->fhss_api, false); + } + if (queue_size) { + fhss_structure->callbacks.tx_poll(fhss_structure->fhss_api); + } +} + +static uint32_t fhss_ws_calculate_ufsi(fhss_structure_t *fhss_structure, uint32_t tx_time) +{ + uint8_t dwell_time = fhss_structure->ws->fhss_configuration.fhss_uc_dwell_interval; + uint16_t cur_slot = fhss_structure->ws->uc_slot; + if (cur_slot == 0) { + cur_slot = fhss_structure->number_of_channels; + } + cur_slot--; + uint32_t remaining_time_ms = US_TO_MS(get_remaining_slots_us(fhss_structure, fhss_unicast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_uc_dwell_interval))); + uint32_t time_to_tx = 0; + uint32_t cur_time = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); + if (cur_time < tx_time) { + time_to_tx = US_TO_MS(tx_time - cur_time); + } + uint64_t ms_since_seq_start = (cur_slot * dwell_time) + (dwell_time - remaining_time_ms) + time_to_tx; + uint32_t seq_length = 0x10000; + if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_TR51CF) { + ms_since_seq_start %= (dwell_time * fhss_structure->number_of_channels); + seq_length = fhss_structure->number_of_channels; + } + return own_floor((float)(ms_since_seq_start * DEF_2E24) / (seq_length * dwell_time)); +} + +static uint32_t fhss_ws_calculate_broadcast_interval_offset(fhss_structure_t *fhss_structure, uint32_t tx_time) +{ + uint8_t dwell_time = fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval; + uint32_t broadcast_interval = fhss_structure->ws->fhss_configuration.fhss_broadcast_interval; + uint32_t remaining_time_ms = US_TO_MS(get_remaining_slots_us(fhss_structure, fhss_broadcast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval))); + if (fhss_structure->ws->is_on_bc_channel == true) { + remaining_time_ms += (broadcast_interval - dwell_time); + } + uint32_t time_to_tx = 0; + uint32_t cur_time = fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api); + if (cur_time < tx_time) { + time_to_tx = US_TO_MS(tx_time - cur_time); + } + return (broadcast_interval - remaining_time_ms) + time_to_tx; +} + +static uint16_t fhss_ws_calculate_destination_slot(fhss_ws_neighbor_timing_info_t *neighbor_timing_info, uint32_t tx_time) +{ + uint_fast24_t ufsi = neighbor_timing_info->uc_timing_info.ufsi; + uint32_t ufsi_timestamp = neighbor_timing_info->uc_timing_info.utt_rx_timestamp; + uint8_t dwell_time = neighbor_timing_info->uc_timing_info.unicast_dwell_interval; + uint32_t seq_length = 0x10000; + if (neighbor_timing_info->uc_timing_info.unicast_channel_function == WS_TR51CF) { + seq_length = neighbor_timing_info->uc_timing_info.unicast_number_of_channels; + } + uint32_t dest_ms_since_seq_start = own_ceil((float)((uint64_t)ufsi * seq_length * dwell_time) / DEF_2E24); + return (own_floor(((float)(US_TO_MS(tx_time - ufsi_timestamp) + dest_ms_since_seq_start) / dwell_time)) % seq_length); +} + +static uint32_t fhss_ws_get_sf_timeout_callback(fhss_structure_t *fhss_structure) +{ + return MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_uc_dwell_interval); +} + +static int16_t fhss_ws_synch_state_set_callback(const fhss_api_t *api, fhss_states fhss_state, uint16_t pan_id) +{ + (void) pan_id; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return -1; + } + if (fhss_state == FHSS_SYNCHRONIZED) { + uint32_t fhss_broadcast_interval = fhss_structure->ws->fhss_configuration.fhss_broadcast_interval; + uint8_t fhss_bc_dwell_interval = fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval; + + // Start broadcast schedule when BC intervals are known + if (fhss_broadcast_interval && fhss_bc_dwell_interval) { + fhss_broadcast_handler(fhss_structure->fhss_api, 0); + } + // Start unicast schedule + if ((fhss_structure->ws->fhss_configuration.ws_uc_channel_function != WS_FIXED_CHANNEL)) { + fhss_ws_update_uc_channel_callback(fhss_structure); + 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; + } + } + + fhss_structure->fhss_state = fhss_state; + int16_t current_channel = fhss_structure->rx_channel; + if (fhss_structure->ws->is_on_bc_channel == true) { + current_channel = fhss_structure->ws->bc_channel; + } + return current_channel; +} + +static void fhss_ws_update_uc_channel_callback(fhss_structure_t *fhss_structure) +{ + uint8_t mac_address[8]; + int32_t next_channel = fhss_structure->rx_channel; + fhss_structure->callbacks.read_mac_address(fhss_structure->fhss_api, mac_address); + if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_FIXED_CHANNEL) { + return; + } else if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_TR51CF) { + next_channel = fhss_structure->rx_channel = tr51_get_uc_channel_index(fhss_structure->ws->tr51_channel_table, fhss_structure->ws->tr51_output_table, fhss_structure->ws->uc_slot, mac_address, fhss_structure->number_of_channels, NULL); + if (++fhss_structure->ws->uc_slot == fhss_structure->number_of_channels) { + fhss_structure->ws->uc_slot = 0; + } + } else if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_DH1CF) { + next_channel = fhss_structure->rx_channel = dh1cf_get_uc_channel_index(fhss_structure->ws->uc_slot, mac_address, fhss_structure->number_of_channels); + fhss_structure->ws->uc_slot++; + } else if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_VENDOR_DEF_CF) { + if (fhss_structure->ws->fhss_configuration.vendor_defined_cf) { + next_channel = fhss_structure->rx_channel = fhss_structure->ws->fhss_configuration.vendor_defined_cf(fhss_structure->fhss_api, fhss_structure->ws->bc_slot, mac_address, fhss_structure->ws->fhss_configuration.bsi, fhss_structure->number_of_channels); + } + } + // Do not switch unicast channel when broadcast channel is active. + if (fhss_structure->ws->is_on_bc_channel == true) { + return; + } +#ifdef FHSS_CHANNEL_DEBUG + tr_info("%"PRIu32" UC %u %u", fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api), next_channel, fhss_structure->ws->uc_slot); +#endif /*FHSS_CHANNEL_DEBUG*/ + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, next_channel); +#ifdef FHSS_CHANNEL_DEBUG_CBS + if (fhss_uc_switch) { + fhss_uc_switch(); + } +#endif /*FHSS_CHANNEL_DEBUG_CBS*/ +} + +static int fhss_ws_tx_handle_callback(const fhss_api_t *api, bool is_broadcast_addr, uint8_t *destination_address, int frame_type, uint16_t frame_length, uint8_t phy_header_length, uint8_t phy_tail_length, uint32_t tx_time) +{ + (void) frame_type; + (void) frame_length; + (void) phy_header_length; + (void) phy_tail_length; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return -1; + } + if (is_broadcast_addr) { + return 0; + } + // Do not allow unicast destination on broadcast channel + if (!is_broadcast_addr && (fhss_structure->ws->is_on_bc_channel == true)) { + return -1; + } + // Check TX/RX slot + if (!fhss_ws_check_tx_allowed(fhss_structure)) { + return -1; + } + 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 + if (neighbor_timing_info->uc_timing_info.unicast_number_of_channels == 0) { + neighbor_timing_info->uc_timing_info.unicast_number_of_channels = fhss_structure->number_of_channels; + } + uint16_t destination_slot = fhss_ws_calculate_destination_slot(neighbor_timing_info, tx_time); + int32_t tx_channel = neighbor_timing_info->uc_timing_info.fixed_channel; + if (neighbor_timing_info->uc_timing_info.unicast_channel_function == WS_TR51CF) { + tx_channel = tr51_get_uc_channel_index(fhss_structure->ws->tr51_channel_table, fhss_structure->ws->tr51_output_table, destination_slot, destination_address, neighbor_timing_info->uc_timing_info.unicast_number_of_channels, NULL); + } else if (neighbor_timing_info->uc_timing_info.unicast_channel_function == WS_DH1CF) { + tx_channel = dh1cf_get_uc_channel_index(destination_slot, destination_address, neighbor_timing_info->uc_timing_info.unicast_number_of_channels); + } else if (neighbor_timing_info->uc_timing_info.unicast_channel_function == WS_VENDOR_DEF_CF) { + if (fhss_structure->ws->fhss_configuration.vendor_defined_cf) { + tx_channel = fhss_structure->ws->fhss_configuration.vendor_defined_cf(fhss_structure->fhss_api, fhss_structure->ws->bc_slot, destination_address, fhss_structure->ws->fhss_configuration.bsi, neighbor_timing_info->uc_timing_info.unicast_number_of_channels); + } else { + tr_err("FHSS: vendor defined configuration failed"); + return -1; + } + } +#ifdef FHSS_CHANNEL_DEBUG + tr_debug("TX channel: %u %u", tx_channel, destination_slot + 1); +#endif /*FHSS_CHANNEL_DEBUG*/ + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, tx_channel); + } + return 0; +} + +static bool fhss_check_bad_channel(fhss_structure_t *fhss_structure, uint8_t handle) +{ + // When operating on fixed channel, we must ignore the bad channel check. + if (fhss_structure->ws->fhss_configuration.ws_uc_channel_function == WS_FIXED_CHANNEL) { + return true; + } + fhss_failed_tx_t *failed_tx = fhss_failed_handle_find(fhss_structure, handle); + if (!failed_tx) { + return true; + } + if (failed_tx->bad_channel == fhss_structure->rx_channel) { + return false; + } + return true; +} + +static bool fhss_ws_check_tx_allowed(fhss_structure_t *fhss_structure) +{ + // Ignore TX/RX slots + if (fhss_structure->own_hop == 0xff) { + return true; + } + // Currently on broadcast channel + if (fhss_structure->ws->is_on_bc_channel == true) { + return true; + } + uint8_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; + } + + return false; +} + +static bool fhss_ws_check_tx_time(fhss_structure_t *fhss_structure, uint16_t tx_length, uint8_t phy_header_length, uint8_t phy_tail_length) +{ + if (!fhss_structure->ws->fhss_configuration.fhss_broadcast_interval || !fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) { + return true; + } + uint32_t tx_time = fhss_get_tx_time(fhss_structure, tx_length, phy_header_length, phy_tail_length); + uint32_t time_to_bc_channel_us = get_remaining_slots_us(fhss_structure, fhss_broadcast_handler, MS_TO_US(fhss_structure->ws->fhss_configuration.fhss_broadcast_interval)); + if (tx_time > time_to_bc_channel_us) { + return false; + } + return true; +} + +static bool fhss_ws_check_tx_conditions_callback(const fhss_api_t *api, bool is_broadcast_addr, uint8_t handle, int frame_type, uint16_t frame_length, uint8_t phy_header_length, uint8_t phy_tail_length) +{ + (void) frame_type; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return true; + } + // Do not allow broadcast destination on unicast channel + if (is_broadcast_addr && (fhss_structure->ws->is_on_bc_channel == false)) { + return false; + } + // Do not allow unicast destination on broadcast channel + if (!is_broadcast_addr && (fhss_structure->ws->is_on_bc_channel == true)) { + return false; + } + // This condition will check that message is not sent on bad channel + if (fhss_check_bad_channel(fhss_structure, handle) == false) { + return false; + } + // Check that there is enough unicast TX time before next broadcast channel. We try to avoid delaying the change to broadcast channel because of ongoing transmission. + if (!is_broadcast_addr && !fhss_ws_check_tx_time(fhss_structure, frame_length, phy_header_length, phy_tail_length)) { + return false; + } + // Check TX/RX slot for unicast frames + if (!is_broadcast_addr && !fhss_ws_check_tx_allowed(fhss_structure)) { + return false; + } + return true; +} + +static uint8_t fhss_ws_ie_header_discover(uint8_t *header_ptr, uint16_t length, struct ws_ie_t *header_ie, uint8_t sub_id) +{ + struct ws_ie_t ie_element; + uint8_t *sub_id_ptr; + uint16_t ie_dummy; + while (length > 2) { + ie_dummy = common_read_16_bit_inverse(header_ptr); + ie_element.length = (ie_dummy & IE_HEADER_LENGTH_MASK); + ie_element.id = ((ie_dummy & IE_HEADER_ID_MASK) >> 7); + ie_element.content_ptr = header_ptr + 2; + sub_id_ptr = ie_element.content_ptr; + if (ie_element.length && (header_ie->id == ie_element.id) && (*sub_id_ptr == sub_id)) { + sub_id_ptr++; + ie_element.length--; + header_ie->content_ptr = sub_id_ptr; + header_ie->length = ie_element.length; + return ie_element.length; + } + length -= ie_element.length + 2; + header_ptr += ie_element.length + 2; + } + return 0; +} + +static int16_t fhss_ws_write_synch_info_callback(const fhss_api_t *api, uint8_t *ptr, uint8_t length, int frame_type, uint32_t tx_time) +{ + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure || !ptr || (frame_type != FHSS_DATA_FRAME)) { + return -1; + } + platform_enter_critical(); + struct ws_ie_t header_ie; + header_ie.id = WH_IE_ID; + if (fhss_ws_ie_header_discover(ptr, length, &header_ie, WH_SUB_ID_UTT)) { + uint32_t ufsi = fhss_ws_calculate_ufsi(fhss_structure, tx_time); + common_write_24_bit_inverse(ufsi, header_ie.content_ptr + 1); + } + if (fhss_ws_ie_header_discover(ptr, length, &header_ie, WH_SUB_ID_BT)) { + uint32_t broadcast_interval_offset = fhss_ws_calculate_broadcast_interval_offset(fhss_structure, tx_time); + common_write_16_bit_inverse(fhss_structure->ws->bc_slot, header_ie.content_ptr); + common_write_24_bit_inverse(broadcast_interval_offset, header_ie.content_ptr + 2); + } + platform_exit_critical(); + //TODO return destination channel here + return fhss_structure->rx_channel; +} + +static void fhss_ws_data_tx_done_callback(const fhss_api_t *api, bool waiting_ack, bool tx_completed, uint8_t handle) +{ + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return; + } + if (fhss_structure->fhss_state == FHSS_SYNCHRONIZED) { + if (waiting_ack == false) { + if (fhss_structure->ws->is_on_bc_channel == false) { + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, fhss_structure->rx_channel); + } else { + fhss_structure->callbacks.change_channel(fhss_structure->fhss_api, fhss_structure->ws->bc_channel); + } + } + } + // Buffer was successfully transmitted. Remove stored failure handle if exists. + if (tx_completed == true) { + fhss_failed_tx_t *fhss_failed_tx = fhss_failed_handle_find(fhss_structure, handle); + if (fhss_failed_tx) { + fhss_failed_handle_remove(fhss_structure, handle); + } + } +} + +static bool fhss_ws_data_tx_fail_callback(const fhss_api_t *api, uint8_t handle, int frame_type) +{ + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return false; + } + // Only use channel retries when device is synchronized + if (fhss_structure->fhss_state == FHSS_UNSYNCHRONIZED) { + return false; + } + + // Use channel retries only for data frames + if (FHSS_DATA_FRAME != frame_type) { + return false; + } + + fhss_failed_tx_t *fhss_failed_tx = fhss_failed_handle_find(fhss_structure, handle); + if (fhss_failed_tx) { + fhss_failed_tx->retries_done++; + if (fhss_failed_tx->retries_done >= WS_NUMBER_OF_CHANNEL_RETRIES) { + // No more retries. Return false to stop retransmitting. + fhss_failed_handle_remove(fhss_structure, handle); + return false; + } + fhss_failed_tx->bad_channel = fhss_structure->rx_channel; + } else { + // Create new failure handle and return true to retransmit + fhss_failed_handle_add(fhss_structure, handle, fhss_structure->rx_channel); + } + return true; +} + +static void fhss_ws_receive_frame_callback(const fhss_api_t *api, uint16_t pan_id, uint8_t *source_address, uint32_t timestamp, uint8_t *synch_info, int frame_type) +{ + (void) api; + (void) pan_id; + (void) source_address; + (void) timestamp; + (void) synch_info; + (void) frame_type; +} + +static bool fhss_ws_is_broadcast_channel_callback(const fhss_api_t *api) +{ + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return true; + } + return fhss_structure->ws->is_on_bc_channel; +} + +static bool fhss_ws_use_broadcast_queue_cb(const fhss_api_t *api, bool is_broadcast_addr, int frame_type) +{ + (void) frame_type; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(api); + if (!fhss_structure) { + return false; + } + // Broadcast packets are stored in broadcast queue + return is_broadcast_addr; +} + +static void fhss_unicast_handler(const fhss_api_t *fhss_api, uint16_t delay) +{ + uint32_t timeout = 0; + fhss_structure_t *fhss_structure = fhss_get_object_with_api(fhss_api); + if (!fhss_structure) { + return; + } + timeout = fhss_ws_get_sf_timeout_callback(fhss_structure); + if (!timeout) { + fhss_stop_timer(fhss_structure, fhss_unicast_handler); + fhss_structure->ws->unicast_timer_running = false; + return; + } + fhss_ws_start_timer(fhss_structure, timeout - (delay * fhss_structure->platform_functions.fhss_resolution_divider), fhss_unicast_handler); + fhss_structure->ws->unicast_timer_running = true; + fhss_ws_update_uc_channel_callback(fhss_structure); + // Unless we have broadcast schedule, we have to poll unicast queue when changing channel. This is randomized by the unicast schedule. + if (!fhss_structure->ws->fhss_configuration.fhss_broadcast_interval || !fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval) { + if (fhss_structure->callbacks.read_tx_queue_size(fhss_structure->fhss_api, false)) { + fhss_structure->callbacks.tx_poll(fhss_structure->fhss_api); + } + } +} + +int fhss_ws_set_callbacks(fhss_structure_t *fhss_structure) +{ + // Set external API + fhss_structure->fhss_api->is_broadcast_channel = &fhss_ws_is_broadcast_channel_callback; + fhss_structure->fhss_api->use_broadcast_queue = &fhss_ws_use_broadcast_queue_cb; + fhss_structure->fhss_api->tx_handle = &fhss_ws_tx_handle_callback; + fhss_structure->fhss_api->check_tx_conditions = &fhss_ws_check_tx_conditions_callback; + fhss_structure->fhss_api->receive_frame = &fhss_ws_receive_frame_callback; + fhss_structure->fhss_api->data_tx_done = &fhss_ws_data_tx_done_callback; + 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->write_synch_info = &fhss_ws_write_synch_info_callback; + fhss_structure->fhss_api->init_callbacks = &fhss_init_callbacks_cb; + return 0; +} + +int fhss_ws_set_parent(fhss_structure_t *fhss_structure, const uint8_t eui64[8], const broadcast_timing_info_t *bc_timing_info, const bool force_synch) +{ + (void) eui64; + if (!fhss_structure->ws) { + return -1; + } + if (!bc_timing_info->broadcast_interval || !bc_timing_info->broadcast_dwell_interval) { + return -1; + } + if (((uint32_t)S_TO_US(fhss_structure->ws->min_synch_interval) > (fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api) - fhss_structure->ws->synchronization_time)) && !force_synch) { + return 0; + } + platform_enter_critical(); + 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; + uint32_t own_bc_interval_offset = fhss_ws_calculate_broadcast_interval_offset(fhss_structure, fhss_structure->ws->synchronization_time); + fhss_stop_timer(fhss_structure, fhss_broadcast_handler); + uint32_t time_from_reception_ms = US_TO_MS(fhss_structure->callbacks.read_timestamp(fhss_structure->fhss_api) - bc_timing_info->bt_rx_timestamp); + uint32_t true_bc_interval_offset = (bc_timing_info->broadcast_interval_offset + time_from_reception_ms) % bc_timing_info->broadcast_interval; + if (true_bc_interval_offset >= bc_timing_info->broadcast_dwell_interval) { + fhss_structure->ws->is_on_bc_channel = false; + } + uint32_t timeout = MS_TO_US(bc_timing_info->broadcast_interval - true_bc_interval_offset); + + if (fhss_structure->ws->is_on_bc_channel) { + timeout -= MS_TO_US(bc_timing_info->broadcast_interval - bc_timing_info->broadcast_dwell_interval); + } + fhss_ws_start_timer(fhss_structure, timeout, fhss_broadcast_handler); + uint16_t slots_since_reception = (bc_timing_info->broadcast_interval_offset + time_from_reception_ms) / bc_timing_info->broadcast_interval; + // TODO: Calculate drift error + 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_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 (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)); + } + 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); + } + return 0; +} + +int fhss_ws_remove_parent(fhss_structure_t *fhss_structure, const uint8_t eui64[8]) +{ + (void) eui64; + if (!fhss_structure->ws) { + return -1; + } + fhss_structure->ws->parent_bc_info = NULL; + return 0; +} + +int fhss_ws_configuration_set(fhss_structure_t *fhss_structure, const fhss_ws_configuration_t *fhss_configuration) +{ + int channel_count = channel_list_count_channels(fhss_configuration->channel_mask); + if (channel_count <= 0) { + return -1; + } + platform_enter_critical(); + if (fhss_configuration->ws_uc_channel_function == WS_FIXED_CHANNEL || fhss_configuration->fhss_uc_dwell_interval == 0) { + fhss_stop_timer(fhss_structure, fhss_unicast_handler); + fhss_structure->ws->unicast_timer_running = false; + } + 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; + } + fhss_structure->ws->fhss_configuration = *fhss_configuration; + fhss_structure->number_of_channels = channel_count; + if (fhss_configuration->ws_uc_channel_function == WS_FIXED_CHANNEL) { + fhss_structure->rx_channel = fhss_configuration->unicast_fixed_channel; + } + platform_exit_critical(); + tr_info("fhss Configuration set, UC channel: %d, BC channel: %d, UC CF: %d, BC CF: %d, channels: %d, uc dwell: %d, bc dwell: %d, bc interval: %"PRIu32", bsi:%d", + fhss_structure->ws->fhss_configuration.unicast_fixed_channel, + fhss_structure->ws->fhss_configuration.broadcast_fixed_channel, + fhss_structure->ws->fhss_configuration.ws_uc_channel_function, + fhss_structure->ws->fhss_configuration.ws_bc_channel_function, + fhss_structure->number_of_channels, + fhss_structure->ws->fhss_configuration.fhss_uc_dwell_interval, + fhss_structure->ws->fhss_configuration.fhss_bc_dwell_interval, + fhss_structure->ws->fhss_configuration.fhss_broadcast_interval, + fhss_structure->ws->fhss_configuration.bsi); + return 0; +} + +int fhss_ws_set_hop_count(fhss_structure_t *fhss_structure, const uint8_t hop_count) +{ + 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 5af1b2c40e..c86898c2b1 100644 --- a/source/Service_Libs/fhss/fhss_ws.h +++ b/source/Service_Libs/fhss/fhss_ws.h @@ -25,6 +25,8 @@ #define WS_MAX_TXRX_SLOT_LEN_MS 100 // Default minimum broadcast synchronization interval in seconds #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 typedef struct fhss_ws fhss_ws_t; struct fhss_ws { @@ -34,6 +36,7 @@ struct fhss_ws { uint16_t min_synch_interval; uint32_t txrx_slot_length_ms; uint32_t synchronization_time; + int32_t drift_per_millisecond_ns; int16_t *tr51_channel_table; uint8_t *tr51_output_table; bool unicast_timer_running; diff --git a/source/Service_Libs/fhss/fhss_ws_empty_functions.c b/source/Service_Libs/fhss/fhss_ws_empty_functions.c index 07597662f9..11896b55c2 100644 --- a/source/Service_Libs/fhss/fhss_ws_empty_functions.c +++ b/source/Service_Libs/fhss/fhss_ws_empty_functions.c @@ -51,6 +51,7 @@ int fhss_ws_set_parent(fhss_structure_t *fhss_structure, const uint8_t eui64[8], (void) fhss_structure; (void) eui64; (void) bc_timing_info; + (void) force_synch; return -1; } diff --git a/source/Service_Libs/hmac/hmac_sha1.c b/source/Service_Libs/hmac/hmac_sha1.c new file mode 100644 index 0000000000..f4c79d2d21 --- /dev/null +++ b/source/Service_Libs/hmac/hmac_sha1.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "mbedtls/md.h" +#include "Service_Libs/hmac/hmac_sha1.h" + +#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) +{ +#ifdef EXTRA_DEBUG_INFO + // Extensive debug for now, to be disabled later + tr_debug("hmac_sha_1 key %s\n", trace_array(key, key_len)); + + const uint8_t *print_data = data; + uint16_t print_data_len = data_len; + while (true) { + tr_debug("hmac_sha_1 data %s\n", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len)); + if (print_data_len > 32) { + print_data_len -= 32; + print_data += 32; + } else { + break; + } + } +#endif + + const mbedtls_md_type_t md_type = MBEDTLS_MD_SHA1; + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); + if (md_info == NULL) { + return -1; + } + + mbedtls_md_context_t ctx; + + mbedtls_md_init(&ctx); + if (mbedtls_md_setup(&ctx, md_info, 1) != 0) { + return -1; + } + if (mbedtls_md_hmac_starts(&ctx, (const unsigned char *) key, key_len) != 0) { + goto error; + } + if (mbedtls_md_hmac_update(&ctx, (const unsigned char *) data, data_len) != 0) { + goto error; + } + if (mbedtls_md_hmac_finish(&ctx, result) != 0) { + goto error; + } + mbedtls_md_free(&ctx); + +#ifdef EXTRA_DEBUG_INFO + tr_debug("hmac_sha_1 result %s\n", trace_array(result, 20)); +#endif + return 0; + +error: + mbedtls_md_free(&ctx); + return -1; +} diff --git a/source/Service_Libs/hmac/hmac_sha1.h b/source/Service_Libs/hmac/hmac_sha1.h new file mode 100644 index 0000000000..e387e46781 --- /dev/null +++ b/source/Service_Libs/hmac/hmac_sha1.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef HMAC_SHA1_ +#define HMAC_SHA1_ + +/** + * \brief Calculate HMAC-SHA1-160 + * + * Calculate HMAC-SHA1-160 + * + * \param key pointer to key + * \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 + * + * \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); + +#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 new file mode 100644 index 0000000000..544586f890 --- /dev/null +++ b/source/Service_Libs/ieee_802_11/ieee_802_11.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#include "mbedtls/md.h" +#include "Service_Libs/ieee_802_11/ieee_802_11.h" +#include "Service_Libs/hmac/hmac_sha1.h" + +#define TRACE_GROUP "iprf" + +uint16_t ieee_802_11_prf_setup(ieee_802_11_prf_t *prf, uint16_t bits, uint16_t a_len, uint16_t b_len) +{ + prf->bits = bits; + prf->a_len = a_len; + prf->b_len = b_len; + return a_len + 1 + b_len + 1; // A string + Y + B string + X (index) +} + +uint8_t *ieee_802_11_prf_get_a_string(ieee_802_11_prf_t *prf, uint8_t *string) +{ + (void) prf; + return string; +} + +uint8_t *ieee_802_11_prf_get_b_string(ieee_802_11_prf_t *prf, uint8_t *string) +{ + return string + prf->a_len + 1; +} + +uint16_t ieee_802_11_prf_starts(ieee_802_11_prf_t *prf, const uint8_t *key, uint16_t key_len) +{ + prf->key = key; + prf->key_len = key_len; + + uint8_t iterations = (prf->bits + 159) / 160; + uint16_t result_len = 160 / 8 * iterations; + return result_len; +} + +void ieee_802_11_prf_update(ieee_802_11_prf_t *prf, uint8_t *string) +{ + prf->string = string; + prf->string[prf->a_len] = 0x00; /* Y (0) */ +} + +int8_t ieee_802_11_prf_finish(ieee_802_11_prf_t *prf, uint8_t *result) +{ + uint16_t string_len = prf->a_len + 1 + prf->b_len + 1; + + 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) { + return -1; + } + result += 160 / 8; + } + + return 0; +} diff --git a/source/Service_Libs/ieee_802_11/ieee_802_11.h b/source/Service_Libs/ieee_802_11/ieee_802_11.h new file mode 100644 index 0000000000..80dc118633 --- /dev/null +++ b/source/Service_Libs/ieee_802_11/ieee_802_11.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef IEEE_802_11_ +#define IEEE_802_11_ + +typedef struct { + const uint8_t *key; /**< Key string */ + uint8_t *string; /**< Data string (A string + Y + B string + X) */ + uint16_t bits; /**< Number of PRF bits */ + uint16_t key_len; /**< Key string length */ + uint16_t a_len; /**< A string length */ + uint16_t b_len; /**< B string length */ +} ieee_802_11_prf_t; + +/* + From IEEE 802.11 chapter 11.6.1.2 PRF: + + In the following, K is a key; A is a unique label for each different purpose + of the PRF; B is a variable-length string; Y is a single octet containing 0; + X is a single octet containing the loop parameter i; and || denotes concatenation: + + H-SHA-1(K, A, B, X) ← HMAC-SHA-1(K, A || Y || B || X) + + PRF(K, A, B, Len) + for i ← 0 to (Len+159)/160 do + R ← R || H-SHA-1(K, A, B, i) + return L(R, 0, Len) + + PRF-128(K, A, B) = PRF(K, A, B, 128) + PRF-192(K, A, B) = PRF(K, A, B, 192) + PRF-256(K, A, B) = PRF(K, A, B, 256) + PRF-384(K, A, B) = PRF(K, A, B, 384) + PRF-512(K, A, B) = PRF(K, A, B, 512) +*/ + +/** + * \brief Setup IEEE 802.11 PRF + * + * Update is made based on failed and successful message sending + * attempts for a message. + * + * \param prf pointer to PRF data + * \param bits number of bits + * \param a_len A string length + * \param b_len B string length + * + * \return length of the combined string that is input parameter for further functions + * (this is length of the A string + Y + B string + X) + */ +uint16_t ieee_802_11_prf_setup(ieee_802_11_prf_t *prf, uint16_t bits, uint16_t a_len, uint16_t b_len); + +/** + * \brief Get A string pointer + * + * Get a pointer to A string start that is used to write A string contents + * + * \param prf pointer to PRF data + * \param string pointer to input string + * + * \return pointer to A string part of the input string + */ +uint8_t *ieee_802_11_prf_get_a_string(ieee_802_11_prf_t *prf, uint8_t *string); + +/** + * \brief Get B string pointer + * + * Get a pointer to B string start that is used to write B string contents + * + * \param prf pointer to PRF data + * \param string pointer to input string + * + * \return pointer to B string part of the input string + */ +uint8_t *ieee_802_11_prf_get_b_string(ieee_802_11_prf_t *prf, uint8_t *string); + +/** + * \brief Start PRF process + * + * Start PRF process + * + * \param prf pointer to PRF data + * \param key key + * \param key_len key length + * + * \return length of the return string + */ +uint16_t ieee_802_11_prf_starts(ieee_802_11_prf_t *prf, const uint8_t *key, uint16_t key_len); + +/** + * \brief Update PRF process + * + * Update PRF process + * + * \param prf pointer to PRF data + * \param string pointer to input string + * + */ +void ieee_802_11_prf_update(ieee_802_11_prf_t *prf, uint8_t *string); + +/** + * \brief Finish PRF process + * + * Finish PRF process + * + * \param prf pointer to PRF data + * \param result pointer to result string + * + * \return < 0 failure + * \return >= 0 success + * + */ +int8_t ieee_802_11_prf_finish(ieee_802_11_prf_t *prf, uint8_t *result); + +#endif /* IEEE_802_11_*/ diff --git a/source/Service_Libs/load_balance/load_balance.c b/source/Service_Libs/load_balance/load_balance.c index 76b734bd5c..74d02ae442 100644 --- a/source/Service_Libs/load_balance/load_balance.c +++ b/source/Service_Libs/load_balance/load_balance.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/load_balance/load_balance_api.h b/source/Service_Libs/load_balance/load_balance_api.h index 58498c5199..498847da8f 100644 --- a/source/Service_Libs/load_balance/load_balance_api.h +++ b/source/Service_Libs/load_balance/load_balance_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/mac_neighbor_table/mac_neighbor_table.c b/source/Service_Libs/mac_neighbor_table/mac_neighbor_table.c index 21d66058ac..89ebcce39e 100644 --- a/source/Service_Libs/mac_neighbor_table/mac_neighbor_table.c +++ b/source/Service_Libs/mac_neighbor_table/mac_neighbor_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,7 @@ #include "common_functions.h" #include "nsdynmemLIB.h" #include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "platform/topo_trace.h" #define TRACE_GROUP "mnei" diff --git a/source/Service_Libs/mdns/ns_fnet_events.c b/source/Service_Libs/mdns/ns_fnet_events.c index 883dfd2f1e..1749e7b258 100644 --- a/source/Service_Libs/mdns/ns_fnet_events.c +++ b/source/Service_Libs/mdns/ns_fnet_events.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/mdns/ns_fnet_port.c b/source/Service_Libs/mdns/ns_fnet_port.c index d6907489d6..456a2f200e 100644 --- a/source/Service_Libs/mdns/ns_fnet_port.c +++ b/source/Service_Libs/mdns/ns_fnet_port.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +33,7 @@ #include "socket_api.h" #include "net_interface.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "ns_fnet_events.h" diff --git a/source/Service_Libs/mdns/ns_mdns_api.c b/source/Service_Libs/mdns/ns_mdns_api.c index 435c049afa..e0c8958146 100644 --- a/source/Service_Libs/mdns/ns_mdns_api.c +++ b/source/Service_Libs/mdns/ns_mdns_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/mle_service/mle_service.c b/source/Service_Libs/mle_service/mle_service.c index 75a4b87ba0..37e53276fe 100644 --- a/source/Service_Libs/mle_service/mle_service.c +++ b/source/Service_Libs/mle_service/mle_service.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +26,7 @@ #include "ns_list.h" #include "randLIB.h" #include "socket_api.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "net_interface.h" #include "common_functions.h" #include "Common_Protocols/ipv6_constants.h" @@ -228,6 +228,11 @@ static void mle_service_tr_timeout_handler(mle_service_msg_buf_t *cur_ptr) cur_ptr->tokens_delay = false; } + // Randomise Challenge TLV value + if (cur_ptr->challengePtr) { + randLIB_get_n_bytes_random(cur_ptr->challengePtr, cur_ptr->challengeLen); + } + //Trig Buffer to socket mle_service_build_packet_send(srv_ptr, sec_params, cur_ptr); diff --git a/source/Service_Libs/mle_service/mle_service_api.h b/source/Service_Libs/mle_service/mle_service_api.h index 27b5d0e732..76a91950dc 100644 --- a/source/Service_Libs/mle_service/mle_service_api.h +++ b/source/Service_Libs/mle_service/mle_service_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -804,6 +804,6 @@ void mle_service_frame_counter_entry_delete(int8_t interface_id, uint8_t attribu */ void mle_service_receive_filter_cb_set(mle_service_filter_cb *filter_cb); #else -#define mle_service_receive_filter_cb_set(filter_cb) ((void) 0) +#define mle_service_receive_filter_cb_set(filter_cb) ((void) filter_cb) #endif /* MLE_TEST */ #endif /* MLE_SERVICE_API_H_ */ diff --git a/source/Service_Libs/mle_service/mle_service_buffer.c b/source/Service_Libs/mle_service/mle_service_buffer.c index e8a90a4674..834f96edf0 100644 --- a/source/Service_Libs/mle_service/mle_service_buffer.c +++ b/source/Service_Libs/mle_service/mle_service_buffer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/mle_service/mle_service_buffer.h b/source/Service_Libs/mle_service/mle_service_buffer.h index 2d5c4f8999..4f10475920 100644 --- a/source/Service_Libs/mle_service/mle_service_buffer.h +++ b/source/Service_Libs/mle_service/mle_service_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/mle_service/mle_service_frame_counter_table.c b/source/Service_Libs/mle_service/mle_service_frame_counter_table.c index 3f48ed637f..14af37f30d 100644 --- a/source/Service_Libs/mle_service/mle_service_frame_counter_table.c +++ b/source/Service_Libs/mle_service/mle_service_frame_counter_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Arm Limited and affiliates. + * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ #include "common_functions.h" #include "ccmLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Core/include/ns_buffer.h" #include "MLE/mle.h" #include "mac_common_defines.h" diff --git a/source/Service_Libs/mle_service/mle_service_security.c b/source/Service_Libs/mle_service/mle_service_security.c index 37d8a8f101..33c11d2e48 100644 --- a/source/Service_Libs/mle_service/mle_service_security.c +++ b/source/Service_Libs/mle_service/mle_service_security.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,7 @@ #include "common_functions.h" #include "ccmLIB.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Core/include/ns_buffer.h" #include "MLE/mle.h" #include "mac_common_defines.h" diff --git a/source/Service_Libs/mle_service/mle_service_security.h b/source/Service_Libs/mle_service/mle_service_security.h index 674bfef76e..1d3e3f033e 100644 --- a/source/Service_Libs/mle_service/mle_service_security.h +++ b/source/Service_Libs/mle_service/mle_service_security.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/nist_aes_kw/nist_aes_kw.c b/source/Service_Libs/nist_aes_kw/nist_aes_kw.c new file mode 100644 index 0000000000..6164731010 --- /dev/null +++ b/source/Service_Libs/nist_aes_kw/nist_aes_kw.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016-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. + */ + +#include "nsconfig.h" +#include +#include "ns_types.h" +#include "ns_list.h" +#include "ns_trace.h" +#if 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 + +#define TRACE_GROUP "naes" + +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) + + int8_t ret_val = 0; + mbedtls_nist_kw_context ctx; + +#ifdef EXTRA_DEBUG_INFO + const uint8_t *print_data = key; + uint16_t print_data_len = key_bits / 8; + while (true) { + tr_debug("nist_aes_key_wrap key %s\n", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len)); + if (print_data_len > 32) { + print_data_len -= 32; + print_data += 32; + } else { + break; + } + } + + print_data = input; + print_data_len = input_len; + while (true) { + tr_debug("nist_aes_key_wrap in %s\n", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len)); + if (print_data_len > 32) { + print_data_len -= 32; + print_data += 32; + } else { + break; + } + } +#endif + + mbedtls_nist_kw_init(&ctx); + + if (mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, key_bits, is_wrap) != 0) { + ret_val = -1; + goto error; + } + + size_t out_size = *output_len; + + if (is_wrap) { + if (mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, input, input_len, output, output_len, out_size) != 0) { + ret_val = -1; + goto error; + } + } else { + if (mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, input, input_len, output, output_len, out_size) != 0) { + ret_val = -1; + goto error; + } + } + +#ifdef EXTRA_DEBUG_INFO + print_data = output; + print_data_len = *output_len; + while (true) { + tr_debug("nist_aes_key_wrap out %s\n", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len)); + if (print_data_len > 32) { + print_data_len -= 32; + print_data += 32; + } else { + break; + } + } +#endif + +error: + mbedtls_nist_kw_free(&ctx); + + return ret_val; +#else + (void) is_wrap; + (void) key; + (void) key_bits; + (void) input; + (void) input_len; + (void) output; + (void) output_len; + return 0; +#endif +} + +#endif diff --git a/source/Service_Libs/nist_aes_kw/nist_aes_kw.h b/source/Service_Libs/nist_aes_kw/nist_aes_kw.h new file mode 100644 index 0000000000..9f2eed8aab --- /dev/null +++ b/source/Service_Libs/nist_aes_kw/nist_aes_kw.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef NIST_AES_KW_ +#define NIST_AES_KW_ + +/** + * \brief NIST AES Key Wrap encode + * + * NIST AES Key Wrap encode + * + * \param key pointer to key + * \param key_bits key length in bits + * \param input pointer to input data + * \param input_len input data length + * \param output pointer to output data, storage must be at least 8 bytes longer than input_len + * + * \return < 0 failure + * \return >= 0 success + * + */ +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); + +#endif /* NIST_AES_KW_ */ diff --git a/source/Service_Libs/pan_blacklist/pan_blacklist.c b/source/Service_Libs/pan_blacklist/pan_blacklist.c index 3de3fbfc2b..0efcffc970 100644 --- a/source/Service_Libs/pan_blacklist/pan_blacklist.c +++ b/source/Service_Libs/pan_blacklist/pan_blacklist.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/utils/isqrt.c b/source/Service_Libs/utils/isqrt.c index 78039f9c2b..85ca0c07f3 100644 --- a/source/Service_Libs/utils/isqrt.c +++ b/source/Service_Libs/utils/isqrt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/utils/isqrt.h b/source/Service_Libs/utils/isqrt.h index 9502ffd955..373a74244a 100644 --- a/source/Service_Libs/utils/isqrt.h +++ b/source/Service_Libs/utils/isqrt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 2017, Arm Limited and affiliates. + * Copyright (c) 2014-2015, 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/utils/ns_crc.c b/source/Service_Libs/utils/ns_crc.c index 155d5ef288..40e85f96f2 100644 --- a/source/Service_Libs/utils/ns_crc.c +++ b/source/Service_Libs/utils/ns_crc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Arm Limited and affiliates. + * Copyright (c) 2015, 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/utils/ns_crc.h b/source/Service_Libs/utils/ns_crc.h index b7b47289e3..f229b97e67 100644 --- a/source/Service_Libs/utils/ns_crc.h +++ b/source/Service_Libs/utils/ns_crc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Arm Limited and affiliates. + * Copyright (c) 2015-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/utils/ns_file_system.c b/source/Service_Libs/utils/ns_file_system.c index a547c12be6..5ea3cca5fd 100644 --- a/source/Service_Libs/utils/ns_file_system.c +++ b/source/Service_Libs/utils/ns_file_system.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Arm Limited and affiliates. + * Copyright (c) 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/whiteboard/whiteboard.c b/source/Service_Libs/whiteboard/whiteboard.c index a7be8ba8f3..eb6c205bdb 100644 --- a/source/Service_Libs/whiteboard/whiteboard.c +++ b/source/Service_Libs/whiteboard/whiteboard.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/Service_Libs/whiteboard/whiteboard.h b/source/Service_Libs/whiteboard/whiteboard.h index 01b9e90f18..e3e22db395 100644 --- a/source/Service_Libs/whiteboard/whiteboard.h +++ b/source/Service_Libs/whiteboard/whiteboard.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Arm Limited and affiliates. + * Copyright (c) 2013-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,6 +38,7 @@ extern uint16_t whiteboard_size_get(void); #define whiteboard_table_check_address(address) NULL #define whiteboard_table_update(address, eui64, status) NULL #define whiteboard_interface_unregister_all_address(nwk_id) +#define whiteboard_interface_register(address, nwk_id) #define whiteboard_interface_address_cmp(address) false #endif diff --git a/source/configs/base/cfg_ws_border_router.h b/source/configs/base/cfg_ws_border_router.h new file mode 100644 index 0000000000..cfcb5f7bc2 --- /dev/null +++ b/source/configs/base/cfg_ws_border_router.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/* Config Flags for WS Border router */ +#include "cfg_ws_router.h" + +#define HAVE_RPL_ROOT +#define HAVE_RPL_DAO_HANDLING +#define HAVE_6LOWPAN_BORDER_ROUTER +#define HAVE_WS_BORDER_ROUTER +#define HAVE_ND_PROXY +#define MULTICAST_FORWARDING diff --git a/source/configs/base/cfg_ws_router.h b/source/configs/base/cfg_ws_router.h new file mode 100644 index 0000000000..4a0e906133 --- /dev/null +++ b/source/configs/base/cfg_ws_router.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#define HAVE_6LOWPAN_ROUTER +#define HAVE_WS_ROUTER +#define HAVE_RPL +#define HAVE_IPV6_ND +#define HAVE_6LOWPAN_ND +#define HAVE_MPL +#define HAVE_WS diff --git a/source/configs/cfg_generic.h b/source/configs/cfg_generic.h index 762997f420..7e645ef43e 100644 --- a/source/configs/cfg_generic.h +++ b/source/configs/cfg_generic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2014, 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ #include "base/cfg_lowpan_border_router.h" #include "base/cfg_local_socket.h" #include "base/cfg_rf_tunnel.h" +#include "base/cfg_ws_border_router.h" #define FEA_TRACE_SUPPORT #define EXTRA_CONSISTENCY_CHECKS @@ -30,4 +31,8 @@ #define HAVE_DHCPV6_SERVER #define TCP_TEST #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 new file mode 100644 index 0000000000..638fd8f3b9 --- /dev/null +++ b/source/configs/cfg_ws_border_router.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#include "base/cfg_ethernet.h" +#include "base/cfg_local_socket.h" +#include "base/cfg_ws_border_router.h" + +#define FEA_TRACE_SUPPORT +#define HAVE_DHCPV6_SERVER +#define EXTRA_CONSISTENCY_CHECKS + +//#define HAVE_PAE_SUPP +//#define HAVE_PAE_AUTH +//#define HAVE_EAPOL_RELAY diff --git a/source/configs/cfg_ws_router.h b/source/configs/cfg_ws_router.h new file mode 100644 index 0000000000..b3ce93f785 --- /dev/null +++ b/source/configs/cfg_ws_router.h @@ -0,0 +1,23 @@ +/* + * 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. + */ + + +#include "base/cfg_ws_router.h" + +#define FEA_TRACE_SUPPORT +#define EXTRA_CONSISTENCY_CHECKS + diff --git a/source/ipv6_stack/ipv6_routing_table.c b/source/ipv6_stack/ipv6_routing_table.c index 5b99738bd8..804ec63049 100644 --- a/source/ipv6_stack/ipv6_routing_table.c +++ b/source/ipv6_stack/ipv6_routing_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Arm Limited and affiliates. + * Copyright (c) 2012-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,7 +37,7 @@ #include "randLIB.h" #include "ns_trace.h" #include "string.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "ipv6_stack/ipv6_routing_table.h" #include "Common_Protocols/ipv6_constants.h" #include "Common_Protocols/icmpv6.h" @@ -172,6 +172,7 @@ void ipv6_neighbour_cache_init(ipv6_neighbour_cache_t *cache, int8_t interface_i cache->recv_addr_reg = false; cache->send_addr_reg = false; cache->send_nud_probes = true; + cache->probe_avoided_routers = true; cache->recv_na_aro = false; cache->recv_ns_aro = false; cache->route_if_info.metric = 0; @@ -409,6 +410,16 @@ void ipv6_neighbour_delete_registered_by_eui64(ipv6_neighbour_cache_t *cache, co } } +bool ipv6_neighbour_has_registered_by_eui64(ipv6_neighbour_cache_t *cache, const uint8_t *eui64) +{ + ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { + if (cur->type != IP_NEIGHBOUR_GARBAGE_COLLECTIBLE && memcmp(ipv6_neighbour_eui64(cache, cur), eui64, 8) == 0) { + return true; + } + } + return false; +} + void ipv6_neighbour_set_state(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, ip_neighbour_cache_state_t state) { if (!ipv6_neighbour_state_is_probably_reachable(entry->state) && @@ -1099,6 +1110,7 @@ static const char *route_src_names[] = { /* (Others are assumed to be always reachable) */ static const bool ipv6_route_probing[ROUTE_MAX] = { [ROUTE_RADV] = true, + [ROUTE_ARO] = true, [ROUTE_RPL_DAO] = true, [ROUTE_RPL_DIO] = true, [ROUTE_RPL_ROOT] = true, @@ -1225,7 +1237,7 @@ static bool ipv6_route_same_router(const ipv6_route_t *a, const ipv6_route_t *b) static void ipv6_route_probe(ipv6_route_t *route) { ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(route->info.interface_id); - if (!ncache || !ncache->send_nud_probes || route->probe_timer) { + if (!ncache || !ncache->probe_avoided_routers || route->probe_timer) { return; } @@ -1378,7 +1390,7 @@ ipv6_route_t *ipv6_route_choose_next_hop(const uint8_t *dest, int8_t interface_i continue; } - if (ncache->send_nud_probes && ipv6_route_probing[route->info.source]) { + if (ncache->probe_avoided_routers && ipv6_route_probing[route->info.source]) { /* Going via a router - check reachability, as per RFC 4191. * This only applies for certain routes (currently those from RAs) */ reachable = ipv6_neighbour_addr_is_probably_reachable(ncache, route->info.next_hop_addr); diff --git a/source/ipv6_stack/ipv6_routing_table.h b/source/ipv6_stack/ipv6_routing_table.h index 19f1beabc8..8d2eb0250e 100644 --- a/source/ipv6_stack/ipv6_routing_table.h +++ b/source/ipv6_stack/ipv6_routing_table.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2012, 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ #define IPV6_ROUTING_TABLE_H_ #include "ns_list.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" /* Address Resolution and Neighbour Unreachablity Detection constants from * RFC 4861, updated by RFC 7048. @@ -119,6 +119,7 @@ typedef struct ipv6_neighbour_cache { bool send_addr_reg : 1; bool recv_addr_reg : 1; bool send_nud_probes : 1; + bool probe_avoided_routers : 1; bool recv_ns_aro : 1; bool recv_na_aro : 1; bool use_eui64_as_slla_in_aro : 1; @@ -159,6 +160,7 @@ extern bool ipv6_neighbour_addr_is_probably_reachable(ipv6_neighbour_cache_t *ca extern bool ipv6_neighbour_ll_addr_match(const ipv6_neighbour_t *entry, addrtype_t ll_type, const uint8_t *ll_address); extern void ipv6_neighbour_invalidate_ll_addr(ipv6_neighbour_cache_t *cache, addrtype_t ll_type, const uint8_t *ll_address); extern void ipv6_neighbour_delete_registered_by_eui64(ipv6_neighbour_cache_t *cache, const uint8_t *eui64); +extern bool ipv6_neighbour_has_registered_by_eui64(ipv6_neighbour_cache_t *cache, const uint8_t *eui64); extern void ipv6_neighbour_entry_update_unsolicited(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, addrtype_t type, const uint8_t *ll_address/*, bool tentative*/); extern ipv6_neighbour_t *ipv6_neighbour_update_unsolicited(ipv6_neighbour_cache_t *cache, const uint8_t *ip_address, addrtype_t ll_type, const uint8_t *ll_address); extern void ipv6_neighbour_reachability_confirmation(const uint8_t ip_address[__static 16], int8_t interface_id); diff --git a/source/ipv6_stack/protocol_ipv6.c b/source/ipv6_stack/protocol_ipv6.c index b351eb8178..ca528daf62 100644 --- a/source/ipv6_stack/protocol_ipv6.c +++ b/source/ipv6_stack/protocol_ipv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Arm Limited and affiliates. + * Copyright (c) 2012-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "ns_list.h" #include "ns_trace.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ipv6.h" #include "Common_Protocols/icmpv6.h" diff --git a/source/libDHCPv6/libDHCPv6.c b/source/libDHCPv6/libDHCPv6.c index 0ca070faf1..3bc2ec7844 100644 --- a/source/libDHCPv6/libDHCPv6.c +++ b/source/libDHCPv6/libDHCPv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -220,7 +220,8 @@ dhcpv6_client_server_data_t *libdhcpv6_nonTemporal_entry_get_by_prefix(int8_t in uint16_t libdhcpv6_duid_option_size(uint16_t linkType) { uint16_t length = 8; // Type & Length header part *2 - if (linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE) { + if (linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE || + linkType == DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) { length += 8; } else { length += 6; @@ -452,7 +453,8 @@ int libdhcpv6_compare_DUID(dhcp_link_options_params_t *targetId, dhcp_link_optio { if (targetId->linkType == parsedId->linkType) { uint8_t cmpLen; - if (targetId->linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE) { + if (targetId->linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE || + targetId->linkType == DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) { //Compare Current Interface EUID64 cmpLen = 8; } else { @@ -625,6 +627,8 @@ int libdhcpv6_get_duid_by_selected_type_id_opt(uint8_t *ptr, uint16_t data_lengt return 0; } else if ((params->linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE) && (option_msg.len == DHCPV6_SERVER_ID_MAC64_OPTION_LEN)) { return 0; + } else if ((params->linkType == DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) && (option_msg.len == DHCPV6_SERVER_ID_MAC64_OPTION_LEN)) { + return 0; } } } @@ -683,6 +687,11 @@ int libdhcpv6_get_IA_address(uint8_t *ptr, uint16_t data_length, dhcp_ia_non_tem return 0; } } + } else if (length == 0) { + params->nonTemporalAddress = NULL; + params->preferredValidLifeTime = 0; + params->validLifeTime = 0; + return 0; } } return -1; diff --git a/source/libDHCPv6/libDHCPv6.h b/source/libDHCPv6/libDHCPv6.h index 7722e8ff8d..a32f46c1c0 100644 --- a/source/libDHCPv6/libDHCPv6.h +++ b/source/libDHCPv6/libDHCPv6.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 2017, Arm Limited and affiliates. + * Copyright (c) 2014-2015, 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -189,6 +189,7 @@ typedef struct dhcpv6_relay_msg { /** Server Identifier END */ /** Common for server and Client Identifier option */ #define DHCPV6_DUID_LINK_LAYER_TYPE 0x0003 +#define DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE 0x0006 #define DHCPV6_DUID_HARDWARE_EUI64_TYPE 0x001b #define DHCPV6_DUID_HARDWARE_EUI48_TYPE 0x0001 @@ -340,7 +341,7 @@ uint8_t *libdhcvp6_request_option_write(uint8_t *ptr, uint8_t optionCnt, uint16_ * \param ptr payload pointer * \param duidPtr pointer id * \param duidRole supported values DHCPV6_SERVER_ID_OPTION & DHCPV6_CLIENT_ID_OPTION - * \param linkType supported values DHCPV6_DUID_HARDWARE_EUI64_TYPE & DHCPV6_DUID_HARDWARE_EUI48_TYPE + * \param linkType supported values DHCPV6_DUID_HARDWARE_EUI64_TYPE & DHCPV6_DUID_HARDWARE_EUI48_TYPE & DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE * * return incremented pointer after write */ diff --git a/source/libDHCPv6/libDHCPv6_server.c b/source/libDHCPv6/libDHCPv6_server.c index bac446af7a..69cd434737 100644 --- a/source/libDHCPv6/libDHCPv6_server.c +++ b/source/libDHCPv6/libDHCPv6_server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -60,7 +60,8 @@ static void libdhcpv6_address_generate(dhcpv6_gua_server_entry_s *serverInfo, dh memcpy(ptr, serverInfo->guaPrefix, 8); ptr += 8; if (serverInfo->enableAddressAutonous) { - if (entry->linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE) { + if (entry->linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE || + entry->linkType == DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) { memcpy(ptr, entry->linkId, 8); *ptr ^= 2; } else if (entry->linkType == DHCPV6_DUID_HARDWARE_EUI48_TYPE) { @@ -131,7 +132,8 @@ dhcpv6_gua_server_entry_s *libdhcpv6_server_data_get_by_prefix_and_socketinstanc { ns_list_foreach(dhcpv6_gua_server_entry_s, cur, &dhcpv6_gua_server_list) { if (cur->socketInstance_id == socketInstance) { - if (memcmp(cur->guaPrefix, prefixPtr, 8) == 0) { + + if (!prefixPtr || memcmp(cur->guaPrefix, prefixPtr, 8) == 0) { return cur; } } @@ -211,7 +213,8 @@ dhcpv6_alloacted_address_entry_t *libdhcpv6_address_allocated_list_scan(dhcpv6_g { dhcpv6_alloacted_address_entry_t *newEntry = NULL; uint16_t duiLength = 6; - if (linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE) { + if (linkType == DHCPV6_DUID_HARDWARE_EUI64_TYPE || + linkType == DHCPV6_DUID_HARDWARE_IEEE_802_NETWORKS_TYPE) { duiLength = 8; } ns_list_foreach(dhcpv6_alloacted_address_entry_t, cur, &serverInfo->allocatedAddressList) { diff --git a/source/libDHCPv6/libDHCPv6_server.h b/source/libDHCPv6/libDHCPv6_server.h index 1aa0079c53..02977f3b07 100644 --- a/source/libDHCPv6/libDHCPv6_server.h +++ b/source/libDHCPv6/libDHCPv6_server.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 2017, Arm Limited and affiliates. + * Copyright (c) 2014-2015, 2017-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/source/libNET/src/net_ipv6.c b/source/libNET/src/net_ipv6.c index 302e2950dd..3320a07924 100644 --- a/source/libNET/src/net_ipv6.c +++ b/source/libNET/src/net_ipv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,7 @@ #include "ns_types.h" #include "nsdynmemLIB.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "Common_Protocols/ipv6_flow.h" #include "Common_Protocols/ipv6_fragmentation.h" #include "NWK_INTERFACE/Include/protocol.h" diff --git a/source/libNET/src/net_load_balance.c b/source/libNET/src/net_load_balance.c index 4440532bcc..71d072b547 100644 --- a/source/libNET/src/net_load_balance.c +++ b/source/libNET/src/net_load_balance.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, Arm Limited and affiliates. + * Copyright (c) 2016-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -313,9 +313,13 @@ int8_t net_load_balance_load_level_update_enable(int8_t interface_id, uint16_t e return load_balance_network_load_monitor_enable(interface_ptr->lb_api, expected_device_count, RPL_DODAG_PREF_MASK + 1, net_load_balance_api_get_node_count_cb, net_load_balance_api_get_set_load_level_cb); #else + (void)interface_id; + (void)expected_device_count; return -1; #endif #else + (void)interface_id; + (void)expected_device_count; return -1; #endif } @@ -338,9 +342,11 @@ int8_t net_load_balance_load_level_update_disable(int8_t interface_id) return load_balance_network_load_monitor_disable(interface_ptr->lb_api); #else + (void)interface_id; return -1; #endif #else + (void)interface_id; return -1; #endif } diff --git a/source/libNET/src/net_mle.c b/source/libNET/src/net_mle.c index ef4f5b46bd..39be3ee252 100644 --- a/source/libNET/src/net_mle.c +++ b/source/libNET/src/net_mle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Arm Limited and affiliates. + * Copyright (c) 2016-2017, 2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,7 @@ #include "string.h" #include "ns_trace.h" #include "net_mle_api.h" -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "net_interface.h" #include "MLE/mle.h" #include "NWK_INTERFACE/Include/protocol.h" diff --git a/source/libNET/src/net_rpl.c b/source/libNET/src/net_rpl.c index 5037894a1a..70e26a5c29 100644 --- a/source/libNET/src/net_rpl.c +++ b/source/libNET/src/net_rpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #include "ns_types.h" #include -#include "Core/include/address.h" +#include "Core/include/ns_address_internal.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "RPL/rpl_protocol.h" @@ -74,6 +74,11 @@ int8_t arm_nwk_6lowpan_rpl_dodag_init(int8_t interface_id, const uint8_t *dodag_ } return 0; #else // !HAVE_RPL_ROOT + (void)dodag_id; + (void)config; + (void)instance_id; + (void)flags; + return -1; #endif } diff --git a/source/libNET/src/ns_net.c b/source/libNET/src/ns_net.c index 7cf133b82c..8f5c498917 100644 --- a/source/libNET/src/ns_net.c +++ b/source/libNET/src/ns_net.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,7 @@ #include "socket_api.h" #include "nsdynmemLIB.h" #include "NWK_INTERFACE/Include/protocol.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #ifdef HAVE_RPL #include "RPL/rpl_of0.h" #include "RPL/rpl_mrhof.h" @@ -67,6 +67,9 @@ #include "6LoWPAN/Thread/thread_bootstrap.h" #include "6LoWPAN/Thread/thread_management_internal.h" #include "6LoWPAN/ws/ws_bootstrap.h" +#ifdef HAVE_WS +#include "6LoWPAN/ws/ws_pae_controller.h" +#endif #include "BorderRouter/border_router.h" #include "Service_Libs/mle_service/mle_service_api.h" #include "6LoWPAN/MAC/mac_data_poll.h" @@ -692,6 +695,8 @@ int8_t arm_nwk_interface_ethernet_init(eth_mac_api_t *api, const char *interface cur->interface_name = interface_name_ptr; return cur->id; #else + (void)api; + (void)interface_name_ptr; return -2; #endif } @@ -928,12 +933,41 @@ int8_t arm_nwk_link_layer_security_mode(int8_t interface_id, net_6lowpan_link_la int8_t arm_network_certificate_chain_set(const arm_certificate_chain_entry_s *chain_info) { -#ifndef PANA +#if !defined(PANA) && !defined(HAVE_WS) (void)chain_info; #endif + +#ifdef HAVE_WS + ws_pae_controller_certificate_chain_set(chain_info); +#endif + return pana_interface_certificate_chain_set(chain_info); } +int8_t arm_network_trusted_certificate_add(const arm_certificate_entry_s *cert) +{ + (void) cert; + return -1; +} + +int8_t arm_network_trusted_certificate_remove(const arm_certificate_entry_s *cert) +{ + (void) cert; + return -1; +} + +int8_t arm_network_certificate_revocation_list_add(const arm_cert_revocation_list_entry_s *crl) +{ + (void) crl; + return -1; +} + +int8_t arm_network_certificate_revocation_list_remove(const arm_cert_revocation_list_entry_s *crl) +{ + (void) crl; + return -1; +} + /** * \brief Read Pana server security key material * @@ -1016,11 +1050,20 @@ int8_t arm_pana_client_library_init(int8_t interface_id, net_tls_cipher_e cipher int8_t arm_nwk_interface_configure_ipv6_bootstrap_set(int8_t interface_id, net_ipv6_mode_e bootstrap_mode, const uint8_t *ipv6_prefix_pointer) { +#ifndef HAVE_ETHERNET + (void)interface_id; + (void)bootstrap_mode; + (void)ipv6_prefix_pointer; +#endif return ipv6_interface_configure_ipv6_bootstrap_set(interface_id, bootstrap_mode, ipv6_prefix_pointer); } int8_t arm_nwk_interface_accept_ipv6_ra(int8_t interface_id, net_ipv6_accept_ra_e accept_ra) { +#ifndef HAVE_ETHERNET + (void)interface_id; + (void)accept_ra; +#endif return ipv6_interface_accept_ra(interface_id, accept_ra); } diff --git a/source/libNET/src/socket_api.c b/source/libNET/src/socket_api.c index 8609d577ab..65b192145d 100644 --- a/source/libNET/src/socket_api.c +++ b/source/libNET/src/socket_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +33,7 @@ #include "ns_trace.h" #include "string.h" #include "nsdynmemLIB.h" -#include "Core/include/socket.h" +#include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "Common_Protocols/ipv6_constants.h" #include "Common_Protocols/ipv6_flow.h" diff --git a/source/nsconfig.h b/source/nsconfig.h index 59e3dc1a6f..9a6db864a2 100644 --- a/source/nsconfig.h +++ b/source/nsconfig.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, Arm Limited and affiliates. + * Copyright (c) 2014-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,11 +32,6 @@ #define _ns_cfg_header(x) __ns_cfg_header(configs/cfg_##x.h) #define ns_cfg_header(x) _ns_cfg_header(x) - -#ifdef YOTTA_CFG_NANOSTACK_CONFIGURATION -#define NSCONFIG YOTTA_CFG_NANOSTACK_CONFIGURATION -#endif - #ifdef MBED_CONF_NANOSTACK_CONFIGURATION #define NSCONFIG MBED_CONF_NANOSTACK_CONFIGURATION #endif diff --git a/sources.mk b/sources.mk index e6461345da..4d0628b2f5 100644 --- a/sources.mk +++ b/sources.mk @@ -18,6 +18,26 @@ SRCS += \ source/6LoWPAN/Mesh/mesh.c \ source/6LoWPAN/ND/nd_router_object.c \ source/6LoWPAN/NVM/nwk_nvm.c \ + source/6LoWPAN/ws/ws_ie_lib.c \ + source/6LoWPAN/ws/ws_llc_data_service.c \ + source/6LoWPAN/ws/ws_mpx_header.c \ + source/6LoWPAN/ws/ws_neighbor_class.c \ + source/6LoWPAN/ws/ws_bootstrap.c \ + source/6LoWPAN/ws/ws_common.c \ + source/6LoWPAN/ws/ws_management_api.c \ + source/6LoWPAN/ws/ws_bbr_api.c \ + source/6LoWPAN/ws/ws_test_api.c \ + source/6LoWPAN/ws/ws_empty_functions.c \ + source/6LoWPAN/ws/ws_pae_controller.c \ + source/6LoWPAN/ws/ws_pae_supp.c \ + source/6LoWPAN/ws/ws_pae_auth.c \ + 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_eapol_relay.c \ + source/6LoWPAN/ws/ws_eapol_auth_relay.c \ + source/6LoWPAN/ws/ws_eapol_relay_lib.c \ + source/6LoWPAN/ws/ws_eapol_pdu.c \ source/BorderRouter/border_router.c \ source/Common_Protocols/icmpv6.c \ source/Common_Protocols/icmpv6_prefix.c \ @@ -29,7 +49,7 @@ SRCS += \ source/Common_Protocols/mld.c \ source/Common_Protocols/tcp.c \ source/Common_Protocols/udp.c \ - source/Core/address.c \ + source/Core/ns_address_internal.c \ source/Core/buffer_dyn.c \ source/Core/sockbuf.c \ source/Core/ns_socket.c \ @@ -75,6 +95,25 @@ SRCS += \ source/RPL/rpl_mrhof.c \ source/RPL/rpl_policy.c \ source/Security/Common/security_lib.c \ + source/Security/eapol/eapol_helper.c \ + source/Security/eapol/kde_helper.c \ + source/Security/kmp/kmp_api.c \ + source/Security/kmp/kmp_addr.c \ + source/Security/kmp/kmp_eapol_pdu_if.c \ + source/Security/kmp/kmp_socket_if.c \ + source/Security/protocols/sec_prot_lib.c \ + source/Security/protocols/sec_prot_keys.c \ + source/Security/protocols/sec_prot_certs.c \ + source/Security/protocols/key_sec_prot/key_sec_prot.c \ + source/Security/protocols/eap_tls_sec_prot/auth_eap_tls_sec_prot.c \ + source/Security/protocols/eap_tls_sec_prot/supp_eap_tls_sec_prot.c \ + source/Security/protocols/eap_tls_sec_prot/eap_tls_sec_prot_lib.c \ + source/Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.c \ + source/Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.c \ + source/Security/protocols/gkh_sec_prot/auth_gkh_sec_prot.c \ + source/Security/protocols/gkh_sec_prot/supp_gkh_sec_prot.c \ + source/Security/protocols/tls_sec_prot/tls_sec_prot.c \ + source/Security/protocols/tls_sec_prot/tls_sec_prot_lib.c \ source/Security/PANA/eap_protocol.c \ source/Security/PANA/pana.c \ source/Security/PANA/pana_avp.c \ @@ -89,10 +128,15 @@ SRCS += \ source/Service_Libs/fhss/fhss_configuration_interface.c \ source/Service_Libs/fhss/fhss_statistics.c \ source/Service_Libs/fhss/fhss.c \ + source/Service_Libs/fhss/fhss_ws.c \ source/Service_Libs/fhss/fhss_ws_empty_functions.c \ source/Service_Libs/fhss/fhss_common.c \ + source/Service_Libs/fhss/channel_functions.c \ source/Service_Libs/fhss/channel_list.c \ source/Service_Libs/fnv_hash/fnv_hash.c \ + source/Service_Libs/hmac/hmac_sha1.c \ + source/Service_Libs/ieee_802_11/ieee_802_11.c \ + source/Service_Libs/nist_aes_kw/nist_aes_kw.c \ source/Service_Libs/mac_neighbor_table/mac_neighbor_table.c \ source/Service_Libs/mle_service/mle_service.c \ source/Service_Libs/mle_service/mle_service_buffer.c \