Add support for DHCP server with WHD

pull/11367/head
Kyle Kearney 2019-08-23 11:10:27 -07:00
parent 13f216d23d
commit 26940e5c22
7 changed files with 1172 additions and 1 deletions

View File

@ -0,0 +1,447 @@
/*
* Copyright (c) 2018-2019 ARM Limited
* 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 "CyDhcpServer.h"
#include "cy_utils.h"
#include "Callback.h"
#include "def.h"
#include "whd_types.h"
#ifdef DHCP_EXTENSIVE_DEBUG
extern "C" void dhcp_server_print_header_info(dhcp_packet_t *header, uint32_t datalen, const char *title);
#endif
/* UDP port numbers for DHCP server and client */
#define IP_PORT_DHCP_SERVER (67)
#define IP_PORT_DHCP_CLIENT (68)
/* BOOTP operations */
#define BOOTP_OP_REQUEST (1)
#define BOOTP_OP_REPLY (2)
/* DCHP message types */
#define DHCP_MSG_TYPE_DISCOVER (1)
#define DHCP_MSG_TYPE_OFFER (2)
#define DHCP_MSG_TYPE_REQUEST (3)
#define DHCP_MSG_TYPE_DECLINE (4)
#define DHCP_MSG_TYPE_ACK (5)
#define DHCP_MSG_TYPE_NACK (6)
#define DHCP_MSG_TYPE_RELEASE (7)
#define DHCP_MSG_TYPE_INFORM (8)
#define DHCP_MSG_TYPE_INVALID (255)
#define DHCP_MSG_MAGIC_COOKIE (0x63825363)
#define DHCP_STACK_SIZE (8*1024)
/********************* Options manipulation functions ***********************************/
static void addOption(dhcp_packet_t *dhcp, uint32_t &index, uint8_t optype)
{
if (index + sizeof(dhcp_packet_t) - 1 + 1 >= DHCP_PACKET_SIZE) {
printf("DHCP ERROR: Option index %d (Optype: %d) written to exceeds size of the packet", (int)index, (int)optype);
return;
}
dhcp->Options[index++] = optype;
return;
}
static void addOption(dhcp_packet_t *dhcp, uint32_t &index, uint8_t optype, uint8_t value)
{
if (index + sizeof(dhcp_packet_t) - 1 + 3 >= DHCP_PACKET_SIZE) {
printf("DHCP ERROR: Option index %d (Optype: %d) written to exceeds size of the packet", (int)index, (int)optype);
return;
}
dhcp->Options[index++] = optype;
dhcp->Options[index++] = 0x01;
dhcp->Options[index++] = value;
return;
}
static void addOption(dhcp_packet_t *dhcp, uint32_t &index, uint8_t optype, uint16_t value)
{
if (index + sizeof(dhcp_packet_t) - 1 + 4 >= DHCP_PACKET_SIZE) {
printf("DHCP ERROR: Option index %d (Optype: %d) written to exceeds size of the packet", (int)index, (int)optype);
return;
}
dhcp->Options[index++] = optype;
dhcp->Options[index++] = 0x02;
dhcp->Options[index++] = static_cast<uint8_t>((value >> 0) & 0xFF);
dhcp->Options[index++] = static_cast<uint8_t>((value >> 8) & 0xFF);
return;
}
static void addOption(dhcp_packet_t *dhcp, uint32_t &index, uint8_t optype, uint32_t value)
{
if (index + sizeof(dhcp_packet_t) - 1 + 6 >= DHCP_PACKET_SIZE) {
printf("DHCP ERROR: Option index %d (Optype: %d) written to exceeds size of the packet", (int)index, (int)optype);
return;
}
dhcp->Options[index++] = optype;
dhcp->Options[index++] = 0x04;
dhcp->Options[index++] = static_cast<uint8_t>((value >> 0) & 0xFF);
dhcp->Options[index++] = static_cast<uint8_t>((value >> 8) & 0xFF);
dhcp->Options[index++] = static_cast<uint8_t>((value >> 16) & 0xFF);
dhcp->Options[index++] = static_cast<uint8_t>((value >> 24) & 0xFF);
return;
}
static void addOption(dhcp_packet_t *dhcp, uint32_t &index, uint8_t optype, uint8_t *value, uint32_t size)
{
if (index + sizeof(dhcp_packet_t) - 1 + 2 + size >= DHCP_PACKET_SIZE) {
printf("DHCP ERROR: Option index %d (Optype: %d) written to exceeds size of the packet", (int)index, (int)optype);
return;
}
dhcp->Options[index++] = optype;
dhcp->Options[index++] = size;
memcpy(&dhcp->Options[index], value, size);
index += size;
return;
}
static const uint8_t *findOption(const dhcp_packet_t *request, uint8_t option_num)
{
const uint8_t *option_ptr = request->Options;
while ((option_ptr[0] != DHCP_END_OPTION_CODE) &&
(option_ptr[0] != option_num) &&
(option_ptr < ((const uint8_t *)request) + DHCP_PACKET_SIZE)) {
option_ptr += option_ptr[1] + 2;
}
/* Was the option found? */
if (option_ptr[0] == option_num) {
return &option_ptr[2];
}
return NULL;
}
static void addCommonOptions(dhcp_packet_t *dhcp, uint32_t &index, const uint32_t server_addr, const uint32_t netmask)
{
/* Prepare the Web proxy auto discovery URL */
char wpad_sample_url[] = "http://xxx.xxx.xxx.xxx/wpad.dat";
char ip_str[16];
ipv4_to_string(ip_str, htonl(server_addr));
memcpy(&wpad_sample_url[7], &ip_str[0], 15);
/* Server identifier */
addOption(dhcp, index, DHCP_SERVER_IDENTIFIER_OPTION_CODE, server_addr);
/* Lease Time */
addOption(dhcp, index, DHCP_LEASETIME_OPTION_CODE, static_cast<uint32_t>(0x00015180));
/* Subnet Mask */
addOption(dhcp, index, DHCP_SUBNETMASK_OPTION_CODE, htonl(netmask));
/* Web proxy auto discovery URL */
addOption(dhcp, index, DHCP_WPAD_OPTION_CODE, (uint8_t *)&wpad_sample_url[0], strlen(wpad_sample_url));
/* Router (gateway) */
addOption(dhcp, index, DHCP_ROUTER_OPTION_CODE, htonl(server_addr));
/* DNS server */
addOption(dhcp, index, DHCP_DNS_SERVER_OPTION_CODE, htonl(server_addr));
/* Interface MTU */
addOption(dhcp, index, DHCP_MTU_OPTION_CODE, static_cast<uint16_t>(WHD_PAYLOAD_MTU));
}
static void sendPacket(UDPSocket *socket, dhcp_packet_t *dhcp, uint32_t size)
{
nsapi_size_or_error_t err;
uint32_t broadcast_ip = 0xFFFFFFFF;
char string_addr[16];
ipv4_to_string(string_addr, htonl(broadcast_ip));
err = socket->sendto(string_addr, IP_PORT_DHCP_CLIENT, reinterpret_cast<uint8_t *>(dhcp), size);
if (err < 0) {
printf("DHCP ERROR: Packet send failure with error %d.", err);
} else if (err != (int)size) {
printf("DHCP ERROR: Could not send entire packet. Only %d bytes were sent.", err);
}
}
/********************* Cache utility functions ***********************************/
void CyDhcpServer::setAddress(const cy_mac_addr_t &mac_id, const cy_ip_addr_t &addr)
{
uint32_t a;
uint32_t first_empty_slot;
uint32_t cached_slot;
char empty_cache[NSAPI_IPv6_SIZE] = "";
/* Search for empty slot in cache */
for (a = 0, first_empty_slot = DHCP_IP_ADDRESS_CACHE_MAX, cached_slot = DHCP_IP_ADDRESS_CACHE_MAX; a < DHCP_IP_ADDRESS_CACHE_MAX; a++) {
/* Check for matching MAC address */
if (memcmp(&_mac_addr_cache[a], &mac_id, sizeof(mac_id)) == 0) {
/* Cached device found */
cached_slot = a;
break;
} else if (first_empty_slot == DHCP_IP_ADDRESS_CACHE_MAX && memcmp(&_mac_addr_cache[a], &empty_cache, sizeof(cy_mac_addr_t)) == 0) {
/* Device not found in cache. Return the first empty slot */
first_empty_slot = a;
}
}
if (cached_slot != DHCP_IP_ADDRESS_CACHE_MAX) {
/* Update IP address of cached device */
_ip_addr_cache[cached_slot] = addr;
} else if (first_empty_slot != DHCP_IP_ADDRESS_CACHE_MAX) {
/* Add device to the first empty slot */
_mac_addr_cache[first_empty_slot] = mac_id;
_ip_addr_cache[first_empty_slot] = addr;
} else {
/* Cache is full. Add device to slot 0 */
_mac_addr_cache[0] = mac_id;
_ip_addr_cache [0] = addr;
}
}
bool CyDhcpServer::lookupAddress(const cy_mac_addr_t &mac_id, cy_ip_addr_t &addr)
{
/* Check whether device is already cached */
for (uint32_t a = 0; a < DHCP_IP_ADDRESS_CACHE_MAX; a++) {
if (memcmp(&_mac_addr_cache[a], &mac_id, sizeof(mac_id)) == 0) {
addr = _ip_addr_cache[a];
return true;
}
}
return false;
}
void CyDhcpServer::freeAddress(const cy_mac_addr_t &mac_id)
{
/* Check whether device is already cached */
for (uint32_t a = 0; a < DHCP_IP_ADDRESS_CACHE_MAX; a++) {
if (memcmp(&_mac_addr_cache[a], &mac_id, sizeof(mac_id)) == 0) {
memset(&_mac_addr_cache[a], 0, sizeof(_mac_addr_cache[a]));
memset(&_ip_addr_cache[a], 0, sizeof(_ip_addr_cache[a]));
}
}
}
void CyDhcpServer::handleDiscover(dhcp_packet_t *dhcp)
{
#ifdef DHCP_EXTENSIVE_DEBUG
dhcp_server_print_header_info(dhcp, DHCP_PACKET_SIZE, "\n\nDHCP DISCOVER RECEIVED");
#endif
uint32_t index;
cy_mac_addr_t client_mac;
cy_ip_addr_t client_ip;
memcpy(&client_mac, dhcp->ClientHwAddr, sizeof(client_mac));
if (!lookupAddress(client_mac, client_ip)) {
client_ip = _available_addr;
}
memset(&dhcp->Legacy, 0, sizeof(dhcp->Legacy));
memset(&dhcp->Options[0], 0, DHCP_PACKET_SIZE - sizeof(dhcp_packet_t) + 3);
dhcp->Opcode = BOOTP_OP_REPLY;
dhcp->YourIpAddr = htonl(client_ip.addrv4.addr);
dhcp->MagicCookie = htonl(static_cast<uint32_t>(DHCP_MSG_MAGIC_COOKIE));
/* Add options */
index = 0;
addOption(dhcp, index, DHCP_MESSAGETYPE_OPTION_CODE, static_cast<uint8_t>(DHCP_MSG_TYPE_OFFER));
addCommonOptions(dhcp, index, _server_addr.addrv4.addr, _netmask.addrv4.addr);
addOption(dhcp, index, static_cast<uint8_t>(DHCP_END_OPTION_CODE));
uint32_t size = sizeof(dhcp_packet_t) + index - 1;
CY_ASSERT(size <= DHCP_PACKET_SIZE);
#ifdef DHCP_EXTENSIVE_DEBUG
dhcp_server_print_header_info(dhcp, size, "\n\nDHCP OFFER SENT");
#endif
sendPacket(&_socket, dhcp, size);
}
void CyDhcpServer::handleRequest(dhcp_packet_t *dhcp)
{
#ifdef DHCP_EXTENSIVE_DEBUG
dhcp_server_print_header_info(dhcp, DHCP_PACKET_SIZE, "\n\nDHCP REQUEST RECEIVED");
#endif
cy_mac_addr_t client_mac;
cy_ip_addr_t client_ip;
cy_ip_addr_t req_ip;
bool increment = false;
uint32_t index;
/* Check that the REQUEST is for this server */
uint32_t *server_id_req = (uint32_t *)findOption(dhcp, DHCP_SERVER_IDENTIFIER_OPTION_CODE);
if ((server_id_req == NULL) || ((server_id_req != NULL) && (_server_addr.addrv4.addr != *server_id_req))) {
return; /* Server ID was not found or does not match local IP address */
}
/* Locate the requested address in the options and keep requested address */
req_ip.addrv4.addr = ntohl(*(uint32_t *)findOption(dhcp, DHCP_REQUESTED_IP_ADDRESS_OPTION_CODE));
memcpy(&client_mac, dhcp->ClientHwAddr, sizeof(client_mac));
if (!lookupAddress(client_mac, client_ip)) {
client_ip = _available_addr;
increment = true;
}
memset(&dhcp->Legacy, 0, sizeof(dhcp->Legacy));
memset(&dhcp->Options[0], 0, DHCP_PACKET_SIZE - sizeof(dhcp_packet_t) + 3);
dhcp->Opcode = BOOTP_OP_REPLY;
dhcp->MagicCookie = htonl(static_cast<uint32_t>(DHCP_MSG_MAGIC_COOKIE));
index = 0;
/* Check if the requested IP address matches one we have assigned */
if (req_ip.addrv4.addr != client_ip.addrv4.addr) {
/* Request is not for the assigned IP - force client to take next available IP by sending NAK */
addOption(dhcp, index, DHCP_MESSAGETYPE_OPTION_CODE, static_cast<uint8_t>(DHCP_MSG_TYPE_NACK));
addOption(dhcp, index, DHCP_SERVER_IDENTIFIER_OPTION_CODE, _server_addr.addrv4.addr);
printf("\n\nDHCP_THREAD: %d REQUEST NAK\n", __LINE__);
} else {
dhcp->YourIpAddr = htonl(client_ip.addrv4.addr);
addOption(dhcp, index, DHCP_MESSAGETYPE_OPTION_CODE, static_cast<uint8_t>(DHCP_MSG_TYPE_ACK));
addCommonOptions(dhcp, index, _server_addr.addrv4.addr, _netmask.addrv4.addr);
if (increment) {
uint32_t ip_mask = ~(_netmask.addrv4.addr);
uint32_t subnet = _server_addr.addrv4.addr & _netmask.addrv4.addr;
do {
_available_addr.addrv4.addr = subnet | ((_available_addr.addrv4.addr + 1) & ip_mask);
} while (_available_addr.addrv4.addr == _server_addr.addrv4.addr);
}
setAddress(client_mac, client_ip);
}
addOption(dhcp, index, static_cast<uint8_t>(DHCP_END_OPTION_CODE));
uint32_t size = sizeof(dhcp_packet_t) + index - 1;
CY_ASSERT(size <= DHCP_PACKET_SIZE);
#ifdef DHCP_EXTENSIVE_DEBUG
dhcp_server_print_header_info(dhcp, DHCP_PACKET_SIZE, "\n\nDHCP REQUEST REPLY SENT");
#endif
sendPacket(&_socket, dhcp, size);
}
void CyDhcpServer::runServer(void)
{
nsapi_size_or_error_t err_or_size;
_running = true;
/* Create receive DHCP socket */
_socket.open(_nstack);
_socket.bind((uint16_t)IP_PORT_DHCP_SERVER);
/* Save the current netmask to be sent in DHCP packets as the 'subnet mask option' */
_server_addr.addrv4.addr = string_to_ipv4(_niface->get_ip_address());
_netmask.addrv4.addr = string_to_ipv4(_niface->get_netmask());
#ifdef DHCP_EXTENSIVE_DEBUG
printf("DHCP Server started.\n");
printf("DHCP Server: IP : %s\n", _niface->get_ip_address());
printf("DHCP Server: Netmask: %s\n", _niface->get_netmask());
printf("DHCP Server: Gateway: %s\n", _niface->get_gateway());
printf("DHCP Server: MAC : %s\n\n", _niface->get_mac_address());
#endif
/* Calculate the first available IP address which will be served - based on the netmask and the local IP */
uint32_t ip_mask = ~(_netmask.addrv4.addr);
uint32_t subnet = _server_addr.addrv4.addr & _netmask.addrv4.addr;
_available_addr.addrv4.addr = subnet | ((_server_addr.addrv4.addr + 1) & ip_mask);
while (_running) {
/* Sleep until data is received from socket. */
err_or_size = _socket.recv(_buff, DHCP_PACKET_SIZE);
/* Options field in DHCP header is variable length. We are looking for option "DHCP Message Type" that is 3 octets in size (code, length and type) */
/* If the return value is <0, it is an error; if it is >=0, it is the received length */
if (err_or_size < 0 || err_or_size < (int32_t)sizeof(dhcp_packet_t)) {
continue;
}
dhcp_packet_t *dhcp = reinterpret_cast<dhcp_packet_t *>(_buff);
/* Check if the option in the dhcp header is "DHCP Message Type", code value for option "DHCP Message Type" is 53 as per rfc2132 */
if (dhcp->Options[0] != DHCP_MESSAGETYPE_OPTION_CODE) {
printf("%d: %s received option code wrong: %d != %d\n", __LINE__, __func__, dhcp->Options[0], DHCP_MESSAGETYPE_OPTION_CODE);
continue;
}
uint8_t msg_type = dhcp->Options[2];
switch (msg_type) {
case DHCP_MSG_TYPE_DISCOVER:
handleDiscover(dhcp);
break;
case DHCP_MSG_TYPE_REQUEST:
handleRequest(dhcp);
break;
default:
printf("DHCP ERROR: Unhandled dhcp packet type, %d", msg_type);
break;
}
}
}
void CyDhcpServer::threadWrapper(CyDhcpServer *obj)
{
obj->runServer();
}
CyDhcpServer::CyDhcpServer(NetworkStack *nstack, NetworkInterface *niface)
: _nstack(nstack),
_niface(niface),
_thread(osPriorityNormal, DHCP_STACK_SIZE, NULL, "DHCPserver") {}
CyDhcpServer::~CyDhcpServer()
{
stop();
}
cy_rslt_t CyDhcpServer::start(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
if (!_running) {
CY_ASSERT(_nstack != NULL);
/* Clear cache */
memset(_mac_addr_cache, 0, sizeof(_mac_addr_cache));
memset(_ip_addr_cache, 0, sizeof(_ip_addr_cache));
/* Start DHCP server */
if (osOK != _thread.start(mbed::callback(threadWrapper, this))) {
result = CY_DHCP_THREAD_CREATION_FAILED;
}
}
return result;
}
cy_rslt_t CyDhcpServer::stop(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
if (_running) {
_running = false;
if (NSAPI_ERROR_OK != _socket.close()) {
printf("DHCP ERROR: DHCP socket closure failed.\n");
result = CY_DHCP_STOP_FAILED;
}
if (osOK != _thread.join()) {
printf("DHCP ERROR: DHCP thread join failed.\n");
result = CY_DHCP_STOP_FAILED;
}
}
return result;
}

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2018-2019 ARM Limited
* 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 WHD_DHCP_SERVER_H
#define WHD_DHCP_SERVER_H
#include "cy_result.h"
#include "cynetwork_utils.h"
#include "mbed.h"
/* DHCP data structure */
typedef struct {
uint8_t Opcode; /* packet opcode type */
uint8_t HwType; /* hardware addr type */
uint8_t HwLen; /* hardware addr length */
uint8_t Hops; /* gateway hops */
uint32_t TransactionId; /* transaction ID */
uint16_t SecsElapsed; /* seconds since boot began */
uint16_t Flags;
uint32_t ClientIpAddr; /* client IP address */
uint32_t YourIpAddr; /* 'your' IP address */
uint32_t ServerIpAddr; /* server IP address */
uint32_t GatewayIpAddr; /* gateway IP address */
uint8_t ClientHwAddr[16]; /* client hardware address */
uint8_t Legacy[192]; /* SName, File */
uint32_t MagicCookie;
uint8_t Options[3]; /* options area */
/* as of RFC2131 it is variable length */
} dhcp_packet_t;
#define DHCP_SUBNETMASK_OPTION_CODE (1)
#define DHCP_ROUTER_OPTION_CODE (3)
#define DHCP_DNS_SERVER_OPTION_CODE (6)
#define DHCP_HOST_NAME_OPTION_CODE (12)
#define DHCP_MTU_OPTION_CODE (26)
#define DHCP_REQUESTED_IP_ADDRESS_OPTION_CODE (50)
#define DHCP_LEASETIME_OPTION_CODE (51)
#define DHCP_MESSAGETYPE_OPTION_CODE (53)
#define DHCP_SERVER_IDENTIFIER_OPTION_CODE (54)
#define DHCP_PARAM_REQUEST_LIST_OPTION_CODE (55)
#define DHCP_WPAD_OPTION_CODE (252)
#define DHCP_END_OPTION_CODE (255)
#define DHCP_IP_ADDRESS_CACHE_MAX (5)
#define ADDITIONAL_OPTION_BYTES (272)
#define DHCP_PACKET_SIZE (sizeof(dhcp_packet_t) + ADDITIONAL_OPTION_BYTES)
/** DHCP thread could not be started */
#define CY_DHCP_THREAD_CREATION_FAILED CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_BASE, 0)
/** Error while trying to stop the DHCP server */
#define CY_DHCP_STOP_FAILED CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_MIDDLEWARE_BASE, 1)
/**
* Implementation of a DHCP sever
*/
class CyDhcpServer {
public:
/**
* Create a DHCP server.
*/
CyDhcpServer(NetworkStack *nstack, NetworkInterface *niface);
/**
* Delete the DHCP server.
*/
virtual ~CyDhcpServer();
/**
* Start a DHCP server instance.
* @return CY_RSLT_SUCCESS on success otherwise error.
*/
cy_rslt_t start(void);
/**
* Stop a DHCP server instance.
* @return CY_RSLT_SUCCESS on success otherwise error.
*/
cy_rslt_t stop(void);
private:
NetworkStack *_nstack = NULL;
NetworkInterface *_niface = NULL;
UDPSocket _socket;
Thread _thread;
bool _running = false;
cy_ip_addr_t _available_addr;
cy_ip_addr_t _server_addr;
cy_ip_addr_t _netmask;
cy_mac_addr_t _mac_addr_cache[DHCP_IP_ADDRESS_CACHE_MAX];
cy_ip_addr_t _ip_addr_cache[DHCP_IP_ADDRESS_CACHE_MAX];
uint8_t _buff[DHCP_PACKET_SIZE];
static void threadWrapper(CyDhcpServer *obj);
void runServer(void);
void setAddress(const cy_mac_addr_t &mac_id, const cy_ip_addr_t &addr);
bool lookupAddress(const cy_mac_addr_t &mac_id, cy_ip_addr_t &addr);
void freeAddress(const cy_mac_addr_t &mac_id);
void handleDiscover(dhcp_packet_t *dhcp);
void handleRequest(dhcp_packet_t *dhcp);
};
#endif /* WHD_DHCP_SERVER_H */

View File

@ -155,6 +155,13 @@ int WhdSoftAPInterface::start(const char *ssid, const char *pass, nsapi_security
if (err != NSAPI_ERROR_OK) {
printf("bringup() ERROR: %d\n", err);
}
if (start_dhcp_server) {
_dhcp_server = std::make_unique<CyDhcpServer>(get_stack(), this);
if (CY_RSLT_SUCCESS != _dhcp_server->start()) {
err = NSAPI_ERROR_DHCP_FAILURE;
}
}
return err;
}
@ -162,6 +169,10 @@ int WhdSoftAPInterface::start(const char *ssid, const char *pass, nsapi_security
int WhdSoftAPInterface::stop(void)
{
if (_dhcp_server && CY_RSLT_SUCCESS != _dhcp_server->stop()) {
return NSAPI_ERROR_DHCP_FAILURE;
}
_dhcp_server.reset();
return whd_wifi_stop_ap(_whd_emac.ifp);
}

View File

@ -15,13 +15,15 @@
* limitations under the License.
*/
#ifndef WHD_SOFTAP_INTERFACE_H
#define WHD_SOFTAP_INTERFACE_H
#include "netsocket/EMACInterface.h"
#include "netsocket/OnboardNetworkStack.h"
#include "whd_emac.h"
#include "CyDhcpServer.h"
#include <memory>
/**
* Vendor IE details
@ -132,6 +134,7 @@ public:
protected:
WHD_EMAC &_whd_emac;
std::unique_ptr<CyDhcpServer> _dhcp_server;
};
#endif

View File

@ -0,0 +1,405 @@
#include "CyDhcpServer.h"
#if defined(__cplusplus)
extern "C"
{
#endif
typedef struct DHCP_options_table_s {
uint8_t code;
uint32_t length; /* 0x80000000 means variable */
const char *name;
} dhcp_options_table_t;
static dhcp_options_table_t dhcp_options_lookup_table[] = {
{ 0, 0, "Pad" },
{ 1, 4, "Subnet Mask" },
{ 2, 4, "Time Offset" },
{ 3, 0, "Router" },
{ 4, 0, "Time Server" },
{ 5, 0, "Name Server" },
{ 6, 0, "Domain Server" },
{ 7, 0, "Log Server" },
{ 8, 0, "Quotes Server" },
{ 9, 0, "LPR Server" },
{ 10, 0, "Impress Server" },
{ 11, 0, "RLP Server" },
{ 12, 0, "Hostname" },
{ 13, 2, "Boot File Size" },
{ 14, 0, "Merit Dump File" },
{ 15, 0, "Domain Name" },
{ 16, 0, "Swap Server" },
{ 17, 0, "Root Path" },
{ 18, 0, "Extension File" },
{ 19, 1, "Forward On/Off" },
{ 20, 1, "SrcRte On/Off" },
{ 21, 0, "Policy Filter" },
{ 22, 2, "Max DG Assembly" },
{ 23, 1, "Default IP TTL" },
{ 24, 4, "MTU Timeout" },
{ 25, 0, "MTU Plateau" },
{ 26, 2, "MTU Interface" },
{ 27, 1, "MTU Subnet" },
{ 28, 4, "Broadcast Address" },
{ 29, 1, "Mask Discovery" },
{ 30, 1, "Mask Supplier" },
{ 31, 1, "Router Discovery" },
{ 32, 4, "Router Request" },
{ 33, 0, "Static Route" },
{ 34, 1, "Trailers" },
{ 35, 4, "ARP Timeout" },
{ 36, 1, "Ethernet" },
{ 37, 1, "Default TCP TTL" },
{ 38, 4, "Keepalive Time" },
{ 39, 1, "Keepalive Data" },
{ 40, 0, "NIS Domain" },
{ 41, 0, "NIS Servers" },
{ 42, 0, "NTP Servers" },
{ 43, 0, "Vendor Specific" },
{ 44, 0, "NETBIOS Name Srv" },
{ 45, 0, "NETBIOS Dist Srv" },
{ 46, 1, "NETBIOS Node Type" },
{ 47, 0, "NETBIOS Scope" },
{ 48, 0, "X Window Font" },
{ 49, 0, "X Window Manager" },
{ 50, 4, "Address Request" },
{ 51, 4, "Address Time" },
{ 52, 1, "Overload" },
{ 53, 1, "DHCP Msg Type" },
{ 54, 4, "DHCP Server Id" },
{ 55, 0, "Parameter List" },
{ 56, 0, "DHCP Message" },
{ 57, 2, "DHCP Max Msg Size" },
{ 58, 4, "Renewal Time" },
{ 59, 4, "Rebinding Time" },
{ 60, 0, "Class Id" },
{ 61, 0, "Client Id" },
{ 62, 0, "NetWare/IP Domain" },
{ 63, 0, "NetWare/IP Option" },
{ 64, 0, "NIS-Domain-Name" },
{ 65, 0, "NIS-Server-Addr" },
{ 66, 0, "Server-Name" },
{ 67, 0, "Bootfile-Name" },
{ 68, 0, "Home-Agent-Addrs" },
{ 69, 0, "SMTP-Server" },
{ 70, 0, "POP3-Server" },
{ 71, 0, "NNTP-Server" },
{ 72, 0, "WWW-Server" },
{ 73, 0, "Finger-Server" },
{ 74, 0, "IRC-Server" },
{ 75, 0, "StreetTalk-Server" },
{ 76, 0, "STDA-Server" },
{ 77, 0, "User-Class" },
{ 78, 0, "Directory Agent" },
{ 79, 0, "Service Scope" },
{ 80, 0, "Rapid Commit" },
{ 81, 0, "Client FQDN" },
{ 82, 0, "Relay Agent Information" },
{ 83, 0, "iSNS" },
{ 85, 0, "NDS Servers" },
{ 86, 0, "NDS Tree Name" },
{ 87, 0, "NDS Context" },
{ 88, 0x80000000, "BCMCS Controller Domain Name list" },
{ 89, 0x80000000, "BCMCS Controller IPv4 address option" },
{ 90, 0, "Authentication" },
{ 91, 0x80000000, "client-last-transaction-time option" },
{ 92, 0x80000000, "associated-ip option" },
{ 93, 0, "Client System" },
{ 94, 0, "Client NDI" },
{ 95, 0, "LDAP" },
{ 97, 0, "UUID/GUID" },
{ 98, 0, "User-Auth" },
{ 99, 0x80000000, "GEOCONF_CIVIC" },
{100, 0, "PCode" },
{101, 0, "TCode" },
{109, 16, "OPTION_DHCP4O6_S46_SADDR" },
{112, 0, "Netinfo Address" },
{113, 0, "Netinfo Tag" },
{114, 0, "URL" },
{116, 0, "Auto-Config" },
{117, 0, "Name Service Search" },
{118, 4, "Subnet Selection Option" },
{119, 0, "Domain Search" },
{120, 0, "SIP Servers DHCP Option" },
{121, 0, "Classless Static Route Option" },
{122, 0, "CCC" },
{123, 16, "GeoConf Option" },
{124, 0, "V-I Vendor Class" },
{125, 0, "V-I Vendor-Specific Information" },
{128, 0, "Etherboot signature. 6 bytes: E4:45:74:68:00:00" },
{129, 4, "Call Server IP address" },
{130, 0x80000000, "Ethernet interface. Variable" },
{131, 0, "Remote statistics server IP address" },
{132, 0, "IEEE 802.1Q VLAN ID" },
{133, 0, "IEEE 802.1D/p Layer 2 Priority" },
{134, 0, "Diffserv Code Point (DSCP) for" },
{135, 0, "HTTP Proxy for phone-specific" },
{136, 0, "OPTION_PANA_AGENT" },
{137, 0, "OPTION_V4_LOST" },
{138, 0, "OPTION_CAPWAP_AC_V4" },
{139, 0, "OPTION-IPv4_Address-MoS" },
{140, 0, "OPTION-IPv4_FQDN-MoS" },
{141, 0, "SIP UA Configuration Service Domains" },
{142, 0, "OPTION-IPv4_Address-ANDSF" },
{143, 0, "OPTION_V4_SZTP_REDIRECT" },
{144, 16, "GeoLoc" },
{145, 1, "FORCERENEW_NONCE_CAPABLE" },
{146, 0, "RDNSS Selection" },
{151, 0x80000000, "N+1 status-code" },
{152, 4, "base-time" },
{153, 4, "start-time-of-state" },
{154, 4, "query-start-time" },
{155, 4, "query-end-time" },
{156, 1, "dhcp-state" },
{157, 1, "data-source" },
{158, 0x80000000, " Variable; the minimum length is 5. OPTION_V4_PCP_SERVER" },
{159, 4, "OPTION_V4_PORTPARAMS" },
{160, 0, "DHCP Captive-Portal" },
{161, 0x80000000, "(variable) OPTION_MUD_URL_V4" },
{208, 4, "PXELINUX Magic" },
{209, 0, "Configuration File" },
{210, 0, "Path Prefix" },
{211, 4, "Reboot Time" },
{212, 0x80000000, "18+ N OPTION_6RD" },
{213, 0, "OPTION_V4_ACCESS_DOMAIN" },
{220, 0, "Subnet Allocation Option" },
};
#define isprint(c) ((c) >= 0x20 && (c) < 0x7f)
int hex_dump_print(const void *data_ptr, uint16_t length, int show_ascii)
{
uint8_t *data = (uint8_t *)data_ptr;
uint8_t *char_ptr;
int i, count;
if ((data == NULL) || (length == 0)) {
return -1;
}
count = 0;
char_ptr = data;
while (length > 0) {
i = 0;
while ((length > 0) && (i < 16)) {
printf(" %02x", *data);
i++;
data++;
length--;
count++;
}
if (show_ascii != 0) {
int fill = 16 - i;
/* fill in for < 16 */
while (fill > 0) {
printf(" ");
fill--;
}
/* space between numbers and chars */
printf(" ");
while (i > 0) {
printf("%c", (isprint(*char_ptr) ? *char_ptr : '.'));
char_ptr++;
i--;
}
}
printf("\n");
}
return count;
}
void dhcp_server_print_header_info(dhcp_packet_t *header, uint32_t datalen, const char *title)
{
uint8_t *ptr;
if (title != NULL) {
printf("%s:\n", title);
}
printf("Opcode :%2d : %s\n", header->Opcode, (header->Opcode == 1) ? "Request" : (header->Opcode == 2) ? "Reply" : "Unknown");
printf("HwType :%2d : %s\n", header->HwType, (header->HwType == 1) ? "Ethernet" : "Unknown");
printf("HwLength : : %d\n", header->HwLen);
printf("Hops : : %d\n", header->Hops);
printf("TransactionId : : 0x%lx\n", header->TransactionId);
printf("Elapsed time : : %d\n", header->SecsElapsed);
printf("Flags : : 0x%08x\n", header->Flags);
uint8_t *ip_ptr = (uint8_t *)&header->ClientIpAddr;
printf("from client IP : : %d.%d.%d.%d\n", ip_ptr[0], ip_ptr[1], ip_ptr[2], ip_ptr[3]);
ip_ptr = (uint8_t *)&header->YourIpAddr;
printf("from us YOUR IP: : %d.%d.%d.%d\n", ip_ptr[0], ip_ptr[1], ip_ptr[2], ip_ptr[3]);
ip_ptr = (uint8_t *)&header->ServerIpAddr;
printf("DHCP server IP : : %d.%d.%d.%d\n", ip_ptr[0], ip_ptr[1], ip_ptr[2], ip_ptr[3]);
ip_ptr = (uint8_t *)&header->GatewayIpAddr;
printf("gateway IP : : %d.%d.%d.%d\n", ip_ptr[0], ip_ptr[1], ip_ptr[2], ip_ptr[3]);
printf("Client MAC : :");
hex_dump_print(header->ClientHwAddr, 16, 0);
ip_ptr = (uint8_t *)&header->MagicCookie;
printf("Magic : : %2x %2x %2x %2x\n", ip_ptr[0], ip_ptr[1], ip_ptr[2], ip_ptr[3]);
printf("Options :\n");
ptr = (uint8_t *)header->Options;
printf("Hex Dump:\n");
hex_dump_print(ptr, 64, 1);
printf("\n");
while ((ptr != NULL) && (*ptr != DHCP_END_OPTION_CODE) && ((uint32_t)(ptr - &header->Options[0]) < datalen)) {
int len;
switch (*ptr) {
case DHCP_SUBNETMASK_OPTION_CODE: // (1)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d SUBNET MASK : ", DHCP_SUBNETMASK_OPTION_CODE, len);
hex_dump_print(ptr, len, 1);
ptr += len;
break;
case DHCP_ROUTER_OPTION_CODE: // (3)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d ROUTER : ", DHCP_ROUTER_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_DNS_SERVER_OPTION_CODE: // (6)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d DNS SERVER : ", DHCP_DNS_SERVER_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_HOST_NAME_OPTION_CODE:
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d HOST NAME : ", DHCP_HOST_NAME_OPTION_CODE, len);
hex_dump_print(ptr, len, 1);
ptr += len;
break;
case DHCP_MTU_OPTION_CODE: // (26)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d MTU : ", DHCP_MTU_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_REQUESTED_IP_ADDRESS_OPTION_CODE: // (50)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d REQUESTED IP : ", DHCP_REQUESTED_IP_ADDRESS_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_LEASETIME_OPTION_CODE: // (51)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d LEASE TIME : ", DHCP_LEASETIME_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_MESSAGETYPE_OPTION_CODE: { // (53)
ptr++;
len = *ptr++;
int code = *ptr;
printf(" Code:%d Length:%d MESSAGE : ", DHCP_MESSAGETYPE_OPTION_CODE, len);
switch (code) {
case 1:
printf(" %d -- DHCP DISCOVER\n", code);
break;
case 2:
printf(" %d -- DHCP OFFER\n", code);
break;
case 3:
printf(" %d -- DHCP REQUEST\n", code);
break;
case 4:
printf(" %d -- DHCP DECLINE\n", code);
break;
case 5:
printf(" %d -- DHCP ACK\n", code);
break;
case 6:
printf(" %d -- DHCP NACK\n", code);
break;
case 7:
printf(" %d -- DHCP RELEASE\n", code);
break;
case 8:
printf(" %d -- DHCP INFORM\n", code);
break;
default:
printf(" %d -- INVALID\n", code);
break;
}
ptr += len;
break;
}
case DHCP_SERVER_IDENTIFIER_OPTION_CODE: // (54)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d SERVER ID : ", DHCP_SERVER_IDENTIFIER_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
ptr += len;
break;
case DHCP_PARAM_REQUEST_LIST_OPTION_CODE:
// 9.8. Parameter Request List
//
// This option is used by a DHCP client to request values for specified
// configuration parameters. The list of requested parameters is
// specified as n octets, where each octet is a valid DHCP option code
// as defined in this document.
//
// The client MAY list the options in order of preference. The DHCP
// server is not required to return the options in the requested order,
// but MUST try to insert the requested options in the order requested
// by the client.
//
// The code for this option is 55. Its minimum length is 1.
//
// Code Len Option Codes
// +-----+-----+-----+-----+---
// | 55 | n | c1 | c2 | ...
// +-----+-----+-----+-----+---
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d PARAM REQ : ", DHCP_PARAM_REQUEST_LIST_OPTION_CODE, len);
hex_dump_print(ptr, len, 0);
{
int i;
for (i = 0; i < len; i++) {
uint8_t sub_code;
sub_code = *ptr++;
uint32_t lookup_index;
uint32_t max_lookup = (sizeof(dhcp_options_lookup_table) / sizeof(dhcp_options_lookup_table[0]));
for (lookup_index = 0; lookup_index < max_lookup; lookup_index++) {
if (dhcp_options_lookup_table[lookup_index].code == sub_code) {
uint32_t length = dhcp_options_lookup_table[lookup_index].length;
if (length != 0) {
/* length is variable, in the length field ? */
length = *ptr;
}
printf(" Code:%3d : %s\n", dhcp_options_lookup_table[lookup_index].code, dhcp_options_lookup_table[lookup_index].name);
break;
}
}
if (lookup_index >= max_lookup) {
printf(" Code:%3d : UNKNOWN\n", dhcp_options_lookup_table[lookup_index].code);
}
}
}
break;
case DHCP_WPAD_OPTION_CODE: // (252)
ptr++;
len = *ptr++;
printf(" Code:%d Length:%d WPAD : ", DHCP_WPAD_OPTION_CODE, len);
hex_dump_print(ptr, len, 1);
ptr += len;
break;
default:
ptr++;
break;
}
}
}
#if defined(__cplusplus)
}
#endif

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2019 ARM Limited
* 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 "cynetwork_utils.h"
uint8_t unsigned_to_decimal_string(uint32_t value, char *output, uint8_t min_length, uint8_t max_length)
{
uint8_t digits_left;
char buffer[] = "0000000000";
if (output == NULL) {
return 0;
}
max_length = (uint8_t) MIN(max_length, sizeof(buffer));
digits_left = max_length;
while ((value != 0) && (digits_left != 0)) {
--digits_left;
buffer[digits_left] = (char)((value % 10) + '0');
value = value / 10;
}
digits_left = (uint8_t) MIN((max_length - min_length), digits_left);
memcpy(output, &buffer[digits_left], (size_t)(max_length - digits_left));
/* Add terminating null */
output[(max_length - digits_left)] = '\x00';
return (uint8_t)(max_length - digits_left);
}
void ipv4_to_string(char buffer[16], uint32_t ipv4_address)
{
uint8_t *ip = (uint8_t *)&ipv4_address;
/* unsigned_to_decimal_string() null-terminates the string
* Save the original last character and replace it */
char last_char = buffer[16];
unsigned_to_decimal_string(ip[0], &buffer[0], 3, 3);
buffer[3] = '.';
unsigned_to_decimal_string(ip[1], &buffer[4], 3, 3);
buffer[7] = '.';
unsigned_to_decimal_string(ip[2], &buffer[8], 3, 3);
buffer[11] = '.';
unsigned_to_decimal_string(ip[3], &buffer[12], 3, 3);
buffer[16] = last_char;
}
uint32_t string_to_ipv4(const char *buffer)
{
uint32_t temp = 0;
int char_count = 0;
const char *ptr = buffer;
while ((ptr != NULL) && (*ptr != 0) && (char_count++ < 16)) {
uint8_t byte = 0;
while ((*ptr != 0) && (*ptr != '.') && (char_count++ < 16)) {
byte *= 10;
if ((*ptr >= '0') && (*ptr <= '9')) {
byte += (*ptr - '0');
} else {
break;
}
ptr++;
}
temp <<= 8;
temp |= byte;
if (*ptr == '.') {
ptr++; /* skip '.' */
}
}
return temp;
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018-2019 ARM Limited
* 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.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#if defined(__cplusplus)
extern "C" {
#endif
#ifndef MIN
extern int MIN(/*@sef@*/ int x, /*@sef@*/ int y); /* LINT : This tells lint that the parameter must be side-effect free. i.e. evaluation does not change any values (since it is being evaulated more than once */
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#endif /* ifndef MIN */
#define FX_IPTYPE_IPV4 (0)
#define FX_IPTYPE_IPV6 (1)
typedef union {
uint32_t addr;
uint8_t addrs[4];
} cy_ip_addr_v4_t;
typedef struct {
uint32_t addr[4];
} cy_ip_addr_v6_t;
typedef struct {
uint8_t type;
union {
cy_ip_addr_v4_t addrv4;
cy_ip_addr_v6_t addrv6;
};
} cy_ip_addr_t;
/**
* Structure for storing a MAC address (Wi-Fi Media Access Control address).
*/
typedef struct {
uint8_t octet[6]; /**< Unique 6-byte MAC address */
} cy_mac_addr_t;
/**
* Converts a unsigned long int to a decimal string
*
* @param value[in] : The unsigned long to be converted
* @param output[out] : The buffer which will receive the decimal string
* @param min_length[in] : the minimum number of characters to output (zero padding will apply if required).
* @param max_length[in] : the maximum number of characters to output (up to 10 ). There must be space for terminating NULL.
*
* @note: A terminating NULL is added. Wnsure that there is space in the buffer for this.
*
* @return the number of characters returned (excluding terminating null)
*
*/
uint8_t unsigned_to_decimal_string(uint32_t value, char *output, uint8_t min_length, uint8_t max_length);
/**
* Convert a IPv4 address to a string
*
* @note: String is 16 bytes including terminating null
*
* @param[out] buffer : Buffer which will recieve the IPv4 string
* @param[in] ipv4_address : IPv4 address to convert
*/
void ipv4_to_string(char buffer[16], uint32_t ipv4_address);
/**
* Convert a IPv4 address to a string
*
* @note: String is 16 bytes including terminating null
*
* @param[in] buffer : Buffer which has the IPv4 string
* @return ipv4_address (0 on failure)
*/
uint32_t string_to_ipv4(const char *buffer);
#if defined(__cplusplus)
}
#endif