From 62812abf090dd4d73bd3a5cd11d2ce03d603118f Mon Sep 17 00:00:00 2001 From: Juha Heiskanen Date: Wed, 10 Oct 2018 14:04:54 +0300 Subject: [PATCH 1/2] Libdhcpv6 Relay support Added support to parse and write DHCPv6 Forward and reply messages. Read opearation support Relay option discover. --- source/libDHCPv6/libDHCPv6.c | 39 +++++++++++++++++++ source/libDHCPv6/libDHCPv6.h | 19 ++++++++- test/nanostack/unittest/stub/libDHCPv6_stub.c | 15 +++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/source/libDHCPv6/libDHCPv6.c b/source/libDHCPv6/libDHCPv6.c index f89f66e5e3cd..ceb7dc24f15d 100644 --- a/source/libDHCPv6/libDHCPv6.c +++ b/source/libDHCPv6/libDHCPv6.c @@ -795,4 +795,43 @@ uint16_t libdhcpv6_solication_message_length(uint16_t clientLinkType, bool addre return length; } + +uint8_t *libdhcpv6_dhcp_relay_msg_write(uint8_t *ptr, uint8_t type, uint8_t hop_limit, uint8_t *peer_addres, uint8_t *link_address) +{ + *ptr++ = type; + *ptr++ = hop_limit; + memcpy(ptr, link_address, 16); + ptr += 16; + memcpy(ptr, peer_addres, 16); + ptr += 16; + return ptr; +} + +uint8_t *libdhcpv6_dhcp_option_header_write(uint8_t *ptr, uint16_t length) +{ + ptr = common_write_16_bit(DHCPV6_OPTION_RELAY, ptr); + ptr = common_write_16_bit(length, ptr); + return ptr; +} + +bool libdhcpv6_relay_msg_read(uint8_t *ptr, uint16_t length, dhcpv6_relay_msg_t *relay_msg) +{ + if (length < DHCPV6_RELAY_LENGTH + 4) { + return false; + } + // Relay message base first + relay_msg->type = *ptr++; + relay_msg->hop_limit = *ptr++; + relay_msg->link_address = ptr; + relay_msg->peer_address = ptr + 16; + ptr += 32; + //Discover + if (libdhcpv6_message_option_discover(ptr, length - 34, DHCPV6_OPTION_RELAY, &relay_msg->relay_options) <= 0) { + return false; + } + + + return true; +} + #endif diff --git a/source/libDHCPv6/libDHCPv6.h b/source/libDHCPv6/libDHCPv6.h index f13a24887d5d..d5a522a6cb5d 100644 --- a/source/libDHCPv6/libDHCPv6.h +++ b/source/libDHCPv6/libDHCPv6.h @@ -112,6 +112,14 @@ typedef struct dhcpv6_client_server_entry_s { typedef NS_LIST_HEAD(dhcpv6_client_server_data_t, link) dhcpv6_client_server_entry_s; +typedef struct dhcpv6_relay_msg { + uint8_t type; + uint8_t hop_limit; + uint8_t *link_address; + uint8_t *peer_address; + dhcp_options_msg_t relay_options; +} dhcpv6_relay_msg_t; + /** UDP Port Number definition */ #define DHCPV6_SERVER_PORT 547 #define DHCPV6_CLIENT_PORT 546 @@ -123,6 +131,8 @@ typedef NS_LIST_HEAD(dhcpv6_client_server_data_t, link) dhcpv6_client_server_ent #define DHCPV6_RENEW_TYPE 5 #define DHCPV6_REPLY_TYPE 7 #define DHCPV6_RELEASE_TYPE 8 +#define DHCPV6_RELAY_FORWARD 12 +#define DHCPV6_RELAY_REPLY 13 #define DHCPV6_LEASEQUERY_TYPE 14 #define DHCPV6_LEASEQUERY_REPLY_TYPE 15 @@ -209,6 +219,11 @@ typedef NS_LIST_HEAD(dhcpv6_client_server_data_t, link) dhcpv6_client_server_ent #define DHCPV6_OPTION_CLT_TIME 0x002e +#define DHCPV6_RELAY_LENGTH 34 +#define DHCPV6_OPTION_RELAY 0x0009 + + + /** DHCPv6 client Nontemporal address and server data allocate, free and search */ dhcpv6_client_server_data_t *libdhcvp6_nontemporalAddress_server_data_allocate(int8_t interfaceId, uint8_t instanceId, uint8_t *duiId, uint16_t duiLinkType, uint8_t *nonTemporalPrefix, uint8_t *serverIPv6Address); void libdhcvp6_nontemporalAddress_server_data_free(dhcpv6_client_server_data_t *removedEntry); @@ -259,7 +274,8 @@ uint16_t libdhcpv6_address_reply_message_len(uint16_t clientLinkType, uint16_t s uint8_t *libdhcpv6_generic_nontemporal_address_message_write(uint8_t *ptr, dhcpv6_solication_base_packet_s *packet, dhcpv6_ia_non_temporal_address_s *nonTemporalAddress, dhcp_link_options_params_t *serverLink); uint8_t *libdhcpv6_reply_message_write(uint8_t *ptr, dhcpv6_reply_packet_s *replyPacket, dhcpv6_ia_non_temporal_address_s *nonTemporalAddress, dhcpv6_vendor_data_packet_s *vendorData); - +uint8_t *libdhcpv6_dhcp_relay_msg_write(uint8_t *ptr, uint8_t type, uint8_t hop_limit, uint8_t *peer_addres, uint8_t *link_address); +uint8_t *libdhcpv6_dhcp_option_header_write(uint8_t *ptr, uint16_t length); int libdhcpv6_get_IA_address(uint8_t *ptr, uint16_t data_length, dhcp_ia_non_temporal_params_t *params); int libdhcpv6_get_duid_by_selected_type_id_opt(uint8_t *ptr, uint16_t data_length, uint16_t type , dhcp_link_options_params_t *params); @@ -347,5 +363,6 @@ int libdhcpv6_solication_message_options_validate(uint8_t *ptr, uint16_t data_le int libdhcpv6_advertisment_message_option_validate(dhcp_link_options_params_t *clientId, dhcp_link_options_params_t *serverId, dhcp_ia_non_temporal_params_t *dhcp_ia_non_temporal_params, uint8_t *ptr, uint16_t data_length); bool libdhcpv6_rapid_commit_option_at_packet(uint8_t *ptr, uint16_t length); bool libdhcpv6_time_elapsed_option_at_packet(uint8_t *ptr, uint16_t length); +bool libdhcpv6_relay_msg_read(uint8_t *ptr, uint16_t length, dhcpv6_relay_msg_t *relay_msg); #endif /* LIBDHCPV6_H_ */ diff --git a/test/nanostack/unittest/stub/libDHCPv6_stub.c b/test/nanostack/unittest/stub/libDHCPv6_stub.c index 81af73537883..32d17c360a68 100644 --- a/test/nanostack/unittest/stub/libDHCPv6_stub.c +++ b/test/nanostack/unittest/stub/libDHCPv6_stub.c @@ -223,7 +223,22 @@ uint16_t libdhcpv6_solication_message_length(uint16_t clientLinkType, bool addre return 0; } +uint8_t *libdhcpv6_dhcp_relay_msg_write(uint8_t *ptr, uint8_t type, uint8_t hop_limit, uint8_t *peer_addres, uint8_t *link_address) +{ + return ptr +34; +} + bool libdhcpv6_gua_server_list_empty(void) { return true; } + +uint8_t *libdhcpv6_dhcp_option_header_write(uint8_t *ptr, uint16_t length) +{ + return ptr +4; +} + +bool libdhcpv6_relay_msg_read(uint8_t *ptr, uint16_t length, dhcpv6_relay_msg_t *relay_msg) +{ + return false; +} From 08155b144be11486c24670381006d52c6541c83a Mon Sep 17 00:00:00 2001 From: Juha Heiskanen Date: Tue, 9 Oct 2018 11:13:47 +0300 Subject: [PATCH 2/2] DHCPv6 service relay support Support for enable and init DHCPv6 Relay agent instance. Server and relay cant work same time Relay instance have own socket callback which support to forward data mesages between client and server Server support Relay reply message build Change-Id: Ia9f53d8b2e40d0560d73090bd539cfc20f65e35b --- nanostack/dhcp_service_api.h | 13 +- source/DHCPv6_client/dhcpv6_client_api.h | 85 +++++ source/DHCPv6_client/dhcpv6_client_service.c | 332 ++++++++++++++++++ source/libDHCPv6/dhcp_service_api.c | 293 +++++++++++++++- .../libDHCPv6/dhcp_service_api/Makefile | 1 + .../unittest/stub/dhcp_service_api_stub.c | 5 + 6 files changed, 722 insertions(+), 7 deletions(-) create mode 100644 source/DHCPv6_client/dhcpv6_client_api.h create mode 100644 source/DHCPv6_client/dhcpv6_client_service.c diff --git a/nanostack/dhcp_service_api.h b/nanostack/dhcp_service_api.h index d8b9b5b1168b..3efae996b45e 100644 --- a/nanostack/dhcp_service_api.h +++ b/nanostack/dhcp_service_api.h @@ -69,7 +69,8 @@ typedef enum dhcp_instance_type { DHCP_INSTANCE_CLIENT, - DHCP_INSTANCE_SERVER + DHCP_INSTANCE_SERVER, + DHCP_INTANCE_RELAY_AGENT } dhcp_instance_type_e; /** @@ -124,6 +125,16 @@ typedef int (dhcp_service_receive_resp_cb)(uint16_t instance_id, void *ptr, uint uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_type, dhcp_service_receive_req_cb *receive_req_cb); +/** +* \brief Enable DHCPv6 Relay Agent to server. +* +* +* \param instance The instance ID of the registered server. +* \param server_address global server IPv6 address +*/ +void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address); + + /** * \brief Deletes a server instance. * diff --git a/source/DHCPv6_client/dhcpv6_client_api.h b/source/DHCPv6_client/dhcpv6_client_api.h new file mode 100644 index 000000000000..48b535aab829 --- /dev/null +++ b/source/DHCPv6_client/dhcpv6_client_api.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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 DHCPV6_CLIENT_API_H_ +#define DHCPV6_CLIENT_API_H_ +#include + +/* DHCP client implementation. + * + * Responsibilities of this module are: + * - handle Global address queries and refresh inside network. + * + */ + +/* Initialize dhcp client. + * + * This instance needs to bee initialized once for each network interface. + * if only one thread instance is supported this is needed to call only once. + * + * /param interface interface id of this instance. + * + */ + +void dhcp_client_init(int8_t interface); + +/* Delete dhcp client. + * + * When this is called all addressed assigned by this module are removed from stack. + */ +void dhcp_client_delete(int8_t interface); + +/* Global address handler. + * + * This module updates the addresses from dhcp server and sets them in stack. + * this module makes refresh of address when needed. + * + */ + + +/* give dhcp server and prefix for global address assignment + * + * /param interface interface where address is got + * /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 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); + +/* Renew all leased adddresses might be used when short address changes + * + * /param interface interface where address is got + */ +void dhcp_client_global_address_renew(int8_t interface); + +/* Delete address from device + * if prefix is NULL all are deleted + * + * /param interface interface where address is got + * /param prefix dhcp server ML16 address where address is registered. + * + */ +void dhcp_client_global_address_delete(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16]); + + + +#endif /* DHCPV6_CLIENT_API_H_ */ diff --git a/source/DHCPv6_client/dhcpv6_client_service.c b/source/DHCPv6_client/dhcpv6_client_service.c new file mode 100644 index 000000000000..e22259b49950 --- /dev/null +++ b/source/DHCPv6_client/dhcpv6_client_service.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2018, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nsconfig.h" +#include +#include +#include +#include "nsdynmemLIB.h" +#include "ns_list.h" +#include "common_functions.h" + + +#include "dhcp_service_api.h" +#include "dhcpv6_client_api.h" +#include "libDHCPv6/libDHCPv6.h" +#include "NWK_INTERFACE/Include/protocol.h" + +#define TRACE_GROUP "DHP" + +typedef struct { + dhcp_client_global_adress_cb *global_address_cb; + uint16_t service_instance; + uint8_t libDhcp_instance; + int8_t interface; +} dhcp_client_class_t; + +static dhcp_client_class_t dhcp_client; + +void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr); + + +void dhcp_client_init(int8_t interface) +{ + // No support for multible thread instances yet. + 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(); + + return; +} + +void dhcp_client_delete(int8_t interface) +{ + protocol_interface_info_entry_t *cur = NULL; + dhcpv6_client_server_data_t *srv_data_ptr; + + dhcp_service_delete(dhcp_client.service_instance); + + + cur = protocol_stack_interface_info_get_by_id(interface); + + if (!cur) { + return; + } + + do { + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_instance(dhcp_client.libDhcp_instance); + if (srv_data_ptr != NULL) { + tr_debug("Free DHCPv6 Client\n"); + dhcp_service_req_remove(srv_data_ptr->transActionId);// remove all pending retransmissions + addr_delete(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); + } + } while (srv_data_ptr != NULL); + dhcp_client.service_instance = 0; + return; +} + + +void dhcpv6_client_send_error_cb(dhcpv6_client_server_data_t *srv_data_ptr) +{ + if (srv_data_ptr != NULL) { + + // error for Global address + if (dhcp_client.global_address_cb != NULL) { + dhcp_client.global_address_cb(dhcp_client.interface, srv_data_ptr->server_address, srv_data_ptr->iaNontemporalAddress.addressPrefix, false); + } + } +} + + +/* solication responce received for either global address or routter id assignment */ +int dhcp_solicit_resp_cb(uint16_t instance_id, void *ptr, uint8_t msg_name, uint8_t *msg_ptr, uint16_t msg_len) +{ + dhcp_ia_non_temporal_params_t dhcp_ia_non_temporal_params; + dhcp_link_options_params_t clientId; + dhcp_link_options_params_t serverId; + dhcp_link_options_params_t interfaceId; + dhcpv6_client_server_data_t *srv_data_ptr = NULL; + (void)instance_id; + + srv_data_ptr = ptr; + + if (srv_data_ptr == NULL) { + tr_error("server data not found"); + goto error_exit; + } + + // Validate message + if (msg_name != DHCPV6_REPLY_TYPE) { + tr_error("invalid response"); + goto error_exit; + } + + if (libdhcpv6_reply_message_option_validate(&clientId, &serverId, &dhcp_ia_non_temporal_params, msg_ptr, msg_len) != 0) { + tr_error("Sol Not include all Options"); + goto error_exit; + } + + if (libdhcpv6_nonTemporal_entry_get_by_iaid(dhcp_ia_non_temporal_params.iaId) != srv_data_ptr) { + /* Validate server data availability */ + tr_error("Valid instance not found"); + goto error_exit; + } + + if (srv_data_ptr->IAID != dhcp_ia_non_temporal_params.iaId) { + tr_error("Wrong IAID"); + goto error_exit; + } + + interfaceId.linkID = srv_data_ptr->clientId; + interfaceId.linkType = srv_data_ptr->clientLinkIdType; + if (libdhcpv6_compare_DUID(&interfaceId, &clientId) != 0) { + tr_error("Not Valid Client Id"); + goto error_exit; + } + + memcpy(srv_data_ptr->iaNontemporalAddress.addressPrefix, dhcp_ia_non_temporal_params.nonTemporalAddress, 16); + srv_data_ptr->iaNontemporalAddress.preferredTime = dhcp_ia_non_temporal_params.preferredValidLifeTime; + srv_data_ptr->iaNontemporalAddress.validLifetime = dhcp_ia_non_temporal_params.validLifeTime; + memcpy(srv_data_ptr->serverLinkId, serverId.linkID, 8); + srv_data_ptr->serverLinkType = serverId.linkType; + srv_data_ptr->serverLinkType = serverId.linkType; + srv_data_ptr->T0 = dhcp_ia_non_temporal_params.T0; + srv_data_ptr->T1 = dhcp_ia_non_temporal_params.T1; + srv_data_ptr->iaNonTemporalStructValid = true; + + thread_dhcpv6_client_set_address(dhcp_client.interface, srv_data_ptr); + + + if (dhcp_client.global_address_cb) { + dhcp_client.global_address_cb(dhcp_client.interface, srv_data_ptr->server_address, srv_data_ptr->iaNontemporalAddress.addressPrefix, true); + } + return RET_MSG_ACCEPTED; +error_exit: + dhcpv6_client_send_error_cb(srv_data_ptr); + 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) +{ + 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) { + 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 (!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_ptr = ns_dyn_mem_temporary_alloc(payload_len); + if (!payload_ptr) { + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); + tr_error("OOM payload_ptr"); + return -1; + } + + dhcp_client.global_address_cb = error_cb; //TODO Only supporting one instance globaly if we need more for different instances needs code. + srv_data_ptr->GlobalAddress = true; + // Build solicit + solPacket.clientDUID.linkID = mac64; + solPacket.clientDUID.linkType = DHCPV6_DUID_HARDWARE_EUI64_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); + + // 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); + if (srv_data_ptr->transActionId == 0) { + ns_dyn_mem_free(payload_ptr); + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); + return -1; + } + + return 0; +} + +void dhcp_client_global_address_renew(int8_t interface) +{ + (void)interface; + return; +} + +void dhcp_client_global_address_delete(int8_t interface, uint8_t dhcp_addr[static 16], uint8_t prefix[static 16]) +{ + protocol_interface_info_entry_t *cur; + dhcpv6_client_server_data_t *srv_data_ptr; + (void) dhcp_addr; + + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface, prefix); + cur = protocol_stack_interface_info_get_by_id(interface); + + do { + if (cur == NULL || srv_data_ptr == NULL) { + return; + } + dhcp_service_req_remove(srv_data_ptr->transActionId);// remove all pending retransmissions + tr_debug("Deleting address: %s", trace_ipv6(srv_data_ptr->iaNontemporalAddress.addressPrefix)); + + addr_delete(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); + srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface, prefix); + } while (srv_data_ptr); + + return; +} + +void dhcpv6_renew(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason) +{ + dhcpv6_ia_non_temporal_address_s nonTemporalAddress = {0}; + dhcp_link_options_params_t serverLink; + uint8_t *payload_ptr; + uint32_t payload_len; + dhcpv6_client_server_data_t *srv_data_ptr = libdhcpv6_nonTemporal_entry_get_by_prefix(interface->id, addr->address); + + if (srv_data_ptr == NULL) { + tr_warn("Dhcp address lost"); + return ; + } + if (reason == ADDR_CALLBACK_INVALIDATED) { + dhcp_service_req_remove(srv_data_ptr->transActionId);//stop retransmissions of renew + libdhcvp6_nontemporalAddress_server_data_free(srv_data_ptr); + tr_warn("Dhcp address lost"); + return; + } + + if (reason != ADDR_CALLBACK_TIMER) { + return; + } + + payload_len = libdhcpv6_address_request_message_len(srv_data_ptr->clientLinkIdType, srv_data_ptr->serverLinkType, 0); + payload_ptr = ns_dyn_mem_temporary_alloc(payload_len); + if (payload_ptr == NULL) { + addr->state_timer = 200;//Retry after? should there be maximum 20 second retry + tr_error("Out of memory"); + return ; + } + dhcpv6_solication_base_packet_s packetReq = { + .messageType = DHCPV6_RENEW_TYPE, + .clientDUID.linkID = srv_data_ptr->clientId, + .clientDUID.linkType = srv_data_ptr->clientLinkIdType, + .requestedOptionCnt = 0, + .iaID = srv_data_ptr->IAID, + .timerT0 = srv_data_ptr->T0, + .timerT1 = srv_data_ptr->T1, + .requestedOptionList = NULL, + }; + + // Set Address information + nonTemporalAddress.requestedAddress = srv_data_ptr->iaNontemporalAddress.addressPrefix; + nonTemporalAddress.preferredLifeTime = srv_data_ptr->iaNontemporalAddress.preferredTime; + nonTemporalAddress.validLifeTime = srv_data_ptr->iaNontemporalAddress.validLifetime; + serverLink.linkID = srv_data_ptr->serverLinkId; + serverLink.linkType = srv_data_ptr->serverLinkType; + libdhcpv6_generic_nontemporal_address_message_write(payload_ptr, &packetReq, &nonTemporalAddress, &serverLink); + // send solicit + srv_data_ptr->transActionId = dhcp_service_send_req(dhcp_client.service_instance, 0, srv_data_ptr, srv_data_ptr->server_address, payload_ptr, payload_len, dhcp_solicit_resp_cb); +} + +void dhcpv6_client_set_address(int8_t interface_id, dhcpv6_client_server_data_t *srv_data_ptr) +{ + protocol_interface_info_entry_t *cur = NULL; + if_address_entry_t *address_entry = NULL; + uint32_t renewTimer; + + cur = protocol_stack_interface_info_get_by_id(interface_id); + if (!cur) { + return; + } + renewTimer = libdhcpv6_renew_time_define(srv_data_ptr); + + address_entry = addr_get_entry(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix); + if (address_entry == NULL) { + // create new + address_entry = addr_add(cur, srv_data_ptr->iaNontemporalAddress.addressPrefix, 64, ADDR_SOURCE_DHCP, srv_data_ptr->iaNontemporalAddress.validLifetime, srv_data_ptr->iaNontemporalAddress.preferredTime, false); + } else { + addr_set_valid_lifetime(cur, address_entry, srv_data_ptr->iaNontemporalAddress.validLifetime); + addr_set_preferred_lifetime(cur, address_entry, srv_data_ptr->iaNontemporalAddress.preferredTime); + } + + if (address_entry == NULL) { + tr_error("Address add failed"); + return; + } + if (renewTimer) { + // translate seconds to 100ms ticks + if (renewTimer < 0xffffffff / 10) { + renewTimer *= 10; + } else { + renewTimer = 0xfffffffe; + } + } + address_entry->state_timer = renewTimer; + address_entry->cb = dhcpv6_renew; +} diff --git a/source/libDHCPv6/dhcp_service_api.c b/source/libDHCPv6/dhcp_service_api.c index 1adb7fa7a5fd..4eb68d260dd7 100644 --- a/source/libDHCPv6/dhcp_service_api.c +++ b/source/libDHCPv6/dhcp_service_api.c @@ -53,6 +53,16 @@ typedef struct { } server_instance_t; typedef NS_LIST_HEAD(server_instance_t, link) server_instance_list_t; + +typedef struct { + uint16_t instance_id; + int8_t interface_id; + uint8_t server_address[16]; + bool relay_activated; + ns_list_link_t link; +} relay_instance_t; +typedef NS_LIST_HEAD(relay_instance_t, link) relay_instance_list_t; + typedef struct { ns_address_t addr; dhcp_service_receive_resp_cb *recv_resp_cb; @@ -71,6 +81,7 @@ typedef struct { uint8_t retrans; uint8_t *msg_ptr; uint16_t msg_len; + uint8_t *relay_start; ns_list_link_t link; } msg_tr_t; typedef NS_LIST_HEAD(msg_tr_t, link) tr_list_t; @@ -78,9 +89,11 @@ typedef NS_LIST_HEAD(msg_tr_t, link) tr_list_t; typedef struct { ns_address_t src_address; server_instance_list_t srv_list; + relay_instance_list_t relay_list; tr_list_t tr_list; int8_t dhcp_server_socket; int8_t dhcp_client_socket; + int8_t dhcp_relay_socket; int8_t dhcpv6_socket_service_tasklet; } dhcp_service_class_t; @@ -120,9 +133,11 @@ bool dhcp_service_allocate(void) dhcp_service = ns_dyn_mem_alloc(sizeof(dhcp_service_class_t)); if (dhcp_service) { ns_list_init(&dhcp_service->srv_list); + ns_list_init(&dhcp_service->relay_list); ns_list_init(&dhcp_service->tr_list); dhcp_service->dhcp_client_socket = -1; dhcp_service->dhcp_server_socket = -1; + dhcp_service->dhcp_relay_socket = -1; dhcp_service->dhcpv6_socket_service_tasklet = eventOS_event_handler_create(DHCPv6_socket_service_tasklet, DHCPV6_SOCKET_SERVICE_TASKLET_INIT); if (dhcp_service->dhcpv6_socket_service_tasklet < 0) { ns_dyn_mem_free(dhcp_service); @@ -229,13 +244,50 @@ server_instance_t *dhcp_service_client_find(uint16_t instance_id) } +static uint16_t dhcp_service_relay_interface_get(int8_t interface_id) +{ + ns_list_foreach(server_instance_t, cur_ptr, &dhcp_service->srv_list) { + if (cur_ptr->interface_id == interface_id && cur_ptr->instance_type == DHCP_INTANCE_RELAY_AGENT) { + return cur_ptr->instance_id; + } + } + + return 0; +} + + + +static relay_instance_t *dhcp_service_relay_find(uint16_t instance_id) +{ + relay_instance_t *result = NULL; + ns_list_foreach(relay_instance_t, cur_ptr, &dhcp_service->relay_list) { + if (cur_ptr->instance_id == instance_id) { + result = cur_ptr; + } + } + return result; +} + +static relay_instance_t *dhcp_service_relay_interface(int8_t interface_id) +{ + relay_instance_t *result = NULL; + ns_list_foreach(relay_instance_t, cur_ptr, &dhcp_service->relay_list) { + if (cur_ptr->interface_id == interface_id) { + result = cur_ptr; + } + } + return result; +} + + void recv_dhcp_server_msg(void *cb_res) { socket_callback_t *sckt_data; server_instance_t *srv_ptr = NULL; msg_tr_t *msg_tr_ptr; - uint8_t *msg_ptr; + uint8_t *msg_ptr, *allocated_ptr; uint16_t msg_len; + dhcpv6_relay_msg_t relay_msg; sckt_data = cb_res; @@ -245,12 +297,32 @@ void recv_dhcp_server_msg(void *cb_res) tr_debug("dhcp Server recv request"); msg_tr_ptr = dhcp_tr_create(); msg_ptr = ns_dyn_mem_temporary_alloc(sckt_data->d_len); + allocated_ptr = msg_ptr; if (msg_ptr == NULL || msg_tr_ptr == NULL) { // read actual message tr_error("Out of resources"); goto cleanup; } msg_len = socket_read(sckt_data->socket_id, &msg_tr_ptr->addr, msg_ptr, sckt_data->d_len); + + uint8_t msg_type = *msg_ptr; + if (msg_type == DHCPV6_RELAY_FORWARD) { + if ( !libdhcpv6_relay_msg_read(msg_ptr, msg_len, &relay_msg) ) { + tr_error("Relay forward not correct"); + goto cleanup; + } + //Update Source and data + msg_tr_ptr->relay_start = msg_ptr; + memcpy(msg_tr_ptr->addr.address,relay_msg.peer_address , 16); + msg_ptr = relay_msg.relay_options.msg_ptr; + msg_len = relay_msg.relay_options.len; + + + } else if (msg_type == DHCPV6_RELAY_REPLY) { + tr_error("Relay reply drop at server"); + goto cleanup; + } + //TODO use real function from lib also call validity check msg_tr_ptr->message_tr_id = common_read_24_bit(&msg_ptr[1]); @@ -265,7 +337,7 @@ void recv_dhcp_server_msg(void *cb_res) msg_tr_ptr->instance_id = cur_ptr->instance_id; msg_tr_ptr->interface_id = sckt_data->interface_id; if ((RET_MSG_ACCEPTED == - cur_ptr->recv_req_cb(cur_ptr->instance_id, msg_tr_ptr->msg_tr_id, *msg_ptr, msg_ptr + 4, msg_len - 4))) { + cur_ptr->recv_req_cb(cur_ptr->instance_id, msg_tr_ptr->msg_tr_id, msg_type, msg_ptr + 4, msg_len - 4))) { // should not modify pointers but library requires. msg_tr_ptr = NULL; srv_ptr = cur_ptr; @@ -276,7 +348,7 @@ void recv_dhcp_server_msg(void *cb_res) cleanup: dhcp_tr_delete(msg_tr_ptr); - ns_dyn_mem_free(msg_ptr); + ns_dyn_mem_free(allocated_ptr); if (srv_ptr == NULL) { //no owner found tr_warn("No handler for this message found"); @@ -285,6 +357,118 @@ void recv_dhcp_server_msg(void *cb_res) return; } +void recv_dhcp_relay_msg(void *cb_res) +{ + socket_callback_t *sckt_data; + uint16_t msg_len; + + sckt_data = cb_res; + + if (sckt_data->event_type != SOCKET_DATA || sckt_data->d_len < 4) { + return; + } + + protocol_interface_info_entry_t *interface_ptr = protocol_stack_interface_info_get_by_id(sckt_data->interface_id); + + relay_instance_t *relay_srv =dhcp_service_relay_interface(sckt_data->interface_id); + + if (!interface_ptr || !relay_srv || !relay_srv->relay_activated) { + return; + } + ns_address_t src_address; + + uint8_t relay_frame[DHCPV6_RELAY_LENGTH + 4]; + ns_iovec_t msg_iov[2]; + msg_iov[0].iov_base = relay_frame; + msg_iov[0].iov_len = 34; + msg_iov[1].iov_base = ns_dyn_mem_temporary_alloc(sckt_data->d_len); + msg_iov[1].iov_len = sckt_data->d_len; + if (msg_iov[2].iov_base == NULL ) { + // read actual message + tr_error("Out of resources"); + goto cleanup; + } + + ns_msghdr_t msghdr; + //Set messages name buffer + msghdr.msg_name = &src_address; + msghdr.msg_namelen = sizeof(src_address); + msghdr.msg_iov = &msg_iov[1]; + msghdr.msg_iovlen = 1; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + + msg_len = socket_recvmsg(sckt_data->socket_id, &msghdr, NS_MSG_LEGACY0); + + + tr_debug("dhcp Relay recv msg"); + + //Parse type + uint8_t *ptr = msg_iov[1].iov_base; + uint8_t msg_type = *ptr; + + + if (msg_type == DHCPV6_RELAY_FORWARD) { + tr_error("Drop not supported DHCPv6 forward at Agent"); + goto cleanup; + + } else if (msg_type == DHCPV6_RELAY_REPLY) { + //Parse and validate Relay + dhcpv6_relay_msg_t relay_msg; + if (!libdhcpv6_relay_msg_read(ptr, msg_len, &relay_msg) ) { + tr_error("Not valid relay"); + goto cleanup; + } + if (0 != libdhcpv6_message_malformed_check(relay_msg.relay_options.msg_ptr, relay_msg.relay_options.len)) { + tr_error("Malformed packet"); + goto cleanup; + } + //Copy DST address + memcpy(src_address.address, relay_msg.peer_address, 16); + src_address.type = ADDRESS_IPV6; + src_address.identifier = DHCPV6_SERVER_PORT; + msghdr.msg_iov = &msg_iov[0]; + msghdr.msg_iovlen = 1; + msg_iov[0].iov_base = relay_msg.relay_options.msg_ptr; + msg_iov[0].iov_len = relay_msg.relay_options.len; + tr_debug("Forward Original relay msg to client"); + + } else { + if (0 != libdhcpv6_message_malformed_check(ptr, msg_len)) { + tr_error("Malformed packet"); + goto cleanup; + } + uint8_t gp_address[16]; + //Get blobal address from interface + if (arm_net_address_get(sckt_data->interface_id, ADDR_IPV6_GP, gp_address) != 0) { + // No global prefix available + tr_error("No GP address"); + goto cleanup; + } + + //Build + libdhcpv6_dhcp_relay_msg_write(relay_frame, DHCPV6_RELAY_FORWARD, 0, src_address.address, gp_address); + libdhcpv6_dhcp_option_header_write(relay_frame + 34, msg_len); + + //Copy DST address + memcpy(src_address.address, relay_srv->server_address, 16); + src_address.type = ADDRESS_IPV6; + src_address.identifier = DHCPV6_SERVER_PORT; + //ADD relay frame vector front of original data + msghdr.msg_iov = &msg_iov[0]; + msghdr.msg_iovlen = 2; + msg_iov[0].iov_base = relay_frame; + msg_iov[0].iov_len = 38; + msg_iov[1].iov_len = msg_len; + tr_debug("Forward Client msg to server"); + } + socket_sendmsg(sckt_data->socket_id, &msghdr, NS_MSG_LEGACY0); +cleanup: + ns_dyn_mem_free(msg_iov[1].iov_base); + + return; +} + void recv_dhcp_client_msg(void *cb_res) { ns_address_t address; @@ -351,8 +535,19 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty return 0; } if (instance_type == DHCP_INSTANCE_SERVER && dhcp_service->dhcp_server_socket < 0) { + if (dhcp_service->dhcp_relay_socket >= 0) { + tr_error("dhcp Server socket can't open because Agent open already"); + } dhcp_service->dhcp_server_socket = socket_open(SOCKET_UDP, DHCPV6_SERVER_PORT, recv_dhcp_server_msg); } + + if (instance_type == DHCP_INTANCE_RELAY_AGENT && dhcp_service->dhcp_relay_socket < 0) { + if (dhcp_service->dhcp_server_socket >= 0) { + tr_error("dhcp Relay agent can't open because server open already"); + } + dhcp_service->dhcp_relay_socket = socket_open(SOCKET_UDP, DHCPV6_SERVER_PORT, recv_dhcp_server_msg); + } + if (instance_type == DHCP_INSTANCE_CLIENT && dhcp_service->dhcp_client_socket < 0) { dhcp_service->dhcp_client_socket = socket_open(SOCKET_UDP, DHCPV6_CLIENT_PORT, recv_dhcp_client_msg); } @@ -364,6 +559,18 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty tr_error("No sockets available for DHCP client"); return 0; } + + if (instance_type == DHCP_INTANCE_RELAY_AGENT) { + if (dhcp_service->dhcp_relay_socket < 0) { + tr_error("No sockets available for DHCP server"); + } + + uint16_t temp_id = dhcp_service_relay_interface_get(interface_id); + if (temp_id) { + return temp_id; + } + } + for (; id < MAX_SERVERS; id++) { if (dhcp_service_client_find(id) == NULL) { break; @@ -375,6 +582,22 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty ns_dyn_mem_free(srv_ptr); return 0; } + + if (instance_type == DHCP_INTANCE_RELAY_AGENT) { + //Allocate Realay Agent + relay_instance_t *relay_srv = ns_dyn_mem_alloc(sizeof(relay_instance_t)); + if (!relay_srv) { + tr_error("Out of realy instances"); + ns_dyn_mem_free(srv_ptr); + return 0; + } + ns_list_add_to_start(&dhcp_service->relay_list, relay_srv); + relay_srv->instance_id = id; + relay_srv->interface_id = interface_id; + relay_srv->relay_activated = false; + + } + ns_list_add_to_start(&dhcp_service->srv_list, srv_ptr); srv_ptr->instance_id = id; srv_ptr->instance_type = instance_type; @@ -383,6 +606,15 @@ uint16_t dhcp_service_init(int8_t interface_id, dhcp_instance_type_e instance_ty return srv_ptr->instance_id; } +void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address) +{ + relay_instance_t * realay_srv = dhcp_service_relay_find(instance); + if (realay_srv) { + realay_srv->relay_activated = true; + memcpy(realay_srv->server_address, server_address, 16); + } +} + void dhcp_service_delete(uint16_t instance) { server_instance_t *srv_ptr; @@ -393,7 +625,16 @@ void dhcp_service_delete(uint16_t instance) //TODO delete all transactions if (srv_ptr != NULL) { ns_list_remove(&dhcp_service->srv_list, srv_ptr); + if (srv_ptr->instance_type == DHCP_INTANCE_RELAY_AGENT) { + //Free relay service + relay_instance_t *relay = dhcp_service_relay_find(instance); + if (relay) { + ns_list_remove(&dhcp_service->relay_list, relay); + ns_dyn_mem_free(relay); + } + } ns_dyn_mem_free(srv_ptr); + } ns_list_foreach_safe(msg_tr_t, cur_ptr, &dhcp_service->tr_list) { if (cur_ptr->instance_id == instance) { @@ -401,17 +642,19 @@ void dhcp_service_delete(uint16_t instance) } } - int8_t server_instances = 0, client_instances = 0; + int8_t server_instances = 0, client_instances = 0, relay_instances = 0; ns_list_foreach(server_instance_t, srv, &dhcp_service->srv_list) { if (srv->instance_type == DHCP_INSTANCE_SERVER) { ++server_instances; } else if (srv->instance_type == DHCP_INSTANCE_CLIENT) { ++client_instances; + } else if (srv->instance_type == DHCP_INTANCE_RELAY_AGENT) { + ++relay_instances; } } - if (server_instances == 0 && dhcp_service->dhcp_server_socket > -1) { + if ((server_instances == 0 || relay_instances == 0) && dhcp_service->dhcp_server_socket > -1) { socket_close(dhcp_service->dhcp_server_socket); dhcp_service->dhcp_server_socket = -1; } @@ -530,7 +773,38 @@ void dhcp_service_send_message(msg_tr_t *msg_tr_ptr) } socket_setsockopt(msg_tr_ptr->socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_MULTICAST_HOPS, &multicast_hop_limit, sizeof multicast_hop_limit); socket_setsockopt(msg_tr_ptr->socket, SOCKET_IPPROTO_IPV6, SOCKET_INTERFACE_SELECT, &msg_tr_ptr->interface_id, sizeof(int8_t)); - retval = socket_sendto(msg_tr_ptr->socket, &msg_tr_ptr->addr, msg_tr_ptr->msg_ptr, msg_tr_ptr->msg_len); + + if (msg_tr_ptr->relay_start) { + //Build Relay Reply only server do this + ns_iovec_t data_vector[2]; + ns_msghdr_t msghdr; + memcpy(msg_tr_ptr->addr.address, msg_tr_ptr->relay_start + 2, 16); + msg_tr_ptr->addr.identifier = DHCPV6_SERVER_PORT; + //SET IOV vectors + //Relay Reply + data_vector[0].iov_base = (void *) msg_tr_ptr->relay_start; + data_vector[0].iov_len = DHCPV6_RELAY_LENGTH + 4; + //DHCPV normal message vector + data_vector[1].iov_base = (void *) msg_tr_ptr->msg_ptr; + data_vector[1].iov_len = msg_tr_ptr->msg_len; + + //Set message name + msghdr.msg_name = (void *) &msg_tr_ptr->addr; + msghdr.msg_namelen = sizeof(ns_address_t); + msghdr.msg_iov = &data_vector[0]; + msghdr.msg_iovlen = 2; + //No ancillary data + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + + uint8_t *ptr = msg_tr_ptr->relay_start; + *ptr = DHCPV6_RELAY_REPLY; + libdhcpv6_dhcp_option_header_write(ptr +34, msg_tr_ptr->msg_len); + retval = socket_sendmsg(msg_tr_ptr->socket, &msghdr, NS_MSG_LEGACY0); + + } else { + retval = socket_sendto(msg_tr_ptr->socket, &msg_tr_ptr->addr, msg_tr_ptr->msg_ptr, msg_tr_ptr->msg_len); + } if (retval != 0) { tr_warn("dhcp service socket_sendto fails: %i", retval); } @@ -588,6 +862,12 @@ void dhcp_service_delete(uint16_t instance) (void)instance; } +void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address) +{ + (void)instance; + (void)server_address; +} + int dhcp_service_send_resp(uint32_t msg_tr_id, uint8_t options, uint8_t *msg_ptr, uint16_t msg_len) { (void)msg_tr_id; @@ -626,4 +906,5 @@ bool dhcp_service_timer_tick(uint16_t ticks) (void)ticks; return false; } + #endif diff --git a/test/nanostack/unittest/libDHCPv6/dhcp_service_api/Makefile b/test/nanostack/unittest/libDHCPv6/dhcp_service_api/Makefile index cbfda90bd716..b471ca01f8bf 100644 --- a/test/nanostack/unittest/libDHCPv6/dhcp_service_api/Makefile +++ b/test/nanostack/unittest/libDHCPv6/dhcp_service_api/Makefile @@ -21,6 +21,7 @@ TEST_SRC_FILES = \ ../../stub/system_timer_stub.c \ ../../stub/libDHCPv6_stub.c \ ../../stub/protocol_core_stub.c \ + ../../stub/net_stub.c \ include ../../MakefileWorker.mk diff --git a/test/nanostack/unittest/stub/dhcp_service_api_stub.c b/test/nanostack/unittest/stub/dhcp_service_api_stub.c index 96a54555a056..4f8da5a96443 100644 --- a/test/nanostack/unittest/stub/dhcp_service_api_stub.c +++ b/test/nanostack/unittest/stub/dhcp_service_api_stub.c @@ -169,3 +169,8 @@ bool dhcp_service_timer_tick(uint16_t ticks) return false; } + +void dhcp_service_relay_instance_enable(uint16_t instance, uint8_t *server_address) +{ + +}