2015-08-13 07:10:38 +00:00
|
|
|
/* Very basic LWIP & FreeRTOS-based DHCP server
|
2015-09-09 22:15:33 +00:00
|
|
|
*
|
|
|
|
* Based on RFC2131 http://www.ietf.org/rfc/rfc2131.txt
|
|
|
|
* ... although not fully RFC compliant yet.
|
|
|
|
*
|
2017-10-11 10:26:43 +00:00
|
|
|
* TODO
|
|
|
|
* * Allow binding on a single interface only (for mixed AP/client mode), lwip seems to make it hard to
|
|
|
|
* listen for or send broadcasts on a specific interface only.
|
|
|
|
*
|
|
|
|
* * Probably allocates more memory than it should, it should be possible to reuse netbufs in most cases.
|
2015-09-09 22:15:33 +00:00
|
|
|
*
|
|
|
|
* Part of esp-open-rtos
|
|
|
|
* Copyright (C) 2015 Superhouse Automation Pty Ltd
|
|
|
|
* BSD Licensed as described in the file LICENSE
|
2015-08-13 07:10:38 +00:00
|
|
|
*/
|
|
|
|
#include <string.h>
|
2017-06-06 02:47:21 +00:00
|
|
|
#include <strings.h>
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
#include <FreeRTOS.h>
|
|
|
|
#include <task.h>
|
|
|
|
#include <lwip/netif.h>
|
|
|
|
#include <lwip/api.h>
|
2017-06-06 02:47:21 +00:00
|
|
|
#include "esplibs/libmain.h"
|
2015-09-09 22:15:33 +00:00
|
|
|
|
2017-10-08 06:47:23 +00:00
|
|
|
#if (DHCP_DEBUG == LWIP_DBG_ON)
|
|
|
|
#define debug(s, ...) printf("%s: " s "\n", "DHCP", ## __VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define debug(s, ...)
|
|
|
|
#endif
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
/* Grow the size of the lwip dhcp_msg struct's options field, as LWIP
|
|
|
|
defaults to a 68 octet options field for its DHCP client, and most
|
|
|
|
full-sized clients send us more than this. */
|
|
|
|
#define DHCP_OPTIONS_LEN 312
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
#include <lwip/ip.h>
|
|
|
|
#include <lwip/prot/dhcp.h>
|
|
|
|
#include <lwip/prot/iana.h>
|
2015-09-09 22:15:33 +00:00
|
|
|
|
|
|
|
_Static_assert(sizeof(struct dhcp_msg) == offsetof(struct dhcp_msg, options) + 312, "dhcp_msg_t should have extended options size");
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
#include <lwip/netbuf.h>
|
|
|
|
|
|
|
|
#include "dhcpserver.h"
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t hwaddr[NETIF_MAX_HWADDR_LEN];
|
2017-06-06 02:47:21 +00:00
|
|
|
uint8_t active;
|
2015-08-13 07:10:38 +00:00
|
|
|
uint32_t expires;
|
|
|
|
} dhcp_lease_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
struct netconn *nc;
|
2015-09-09 22:15:33 +00:00
|
|
|
uint8_t max_leases;
|
2017-06-06 02:47:21 +00:00
|
|
|
ip4_addr_t first_client_addr;
|
2015-08-13 07:10:38 +00:00
|
|
|
struct netif *server_if;
|
2015-09-09 22:15:33 +00:00
|
|
|
dhcp_lease_t *leases; /* length max_leases */
|
2017-06-06 02:47:21 +00:00
|
|
|
/* Optional router */
|
|
|
|
ip4_addr_t router;
|
|
|
|
/* Optional DNS server */
|
|
|
|
ip4_addr_t dns;
|
2015-08-13 07:10:38 +00:00
|
|
|
} server_state_t;
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
/* Only one DHCP server task can run at once, so we have global state
|
|
|
|
for it.
|
|
|
|
*/
|
2016-11-05 10:04:03 +00:00
|
|
|
static TaskHandle_t dhcpserver_task_handle = NULL;
|
2015-09-09 22:15:33 +00:00
|
|
|
static server_state_t *state;
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
/* Handlers for various kinds of incoming DHCP messages */
|
2015-09-09 22:15:33 +00:00
|
|
|
static void handle_dhcp_discover(struct dhcp_msg *received);
|
|
|
|
static void handle_dhcp_request(struct dhcp_msg *dhcpmsg);
|
|
|
|
static void handle_dhcp_release(struct dhcp_msg *dhcpmsg);
|
2015-08-24 06:13:24 +00:00
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
static void send_dhcp_nak(struct dhcp_msg *dhcpmsg);
|
|
|
|
|
|
|
|
static void dhcpserver_task(void *pxParameter);
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
/* Utility functions */
|
|
|
|
static uint8_t *find_dhcp_option(struct dhcp_msg *msg, uint8_t option_num, uint8_t min_length, uint8_t *length);
|
|
|
|
static uint8_t *add_dhcp_option_byte(uint8_t *opt, uint8_t type, uint8_t value);
|
|
|
|
static uint8_t *add_dhcp_option_bytes(uint8_t *opt, uint8_t type, void *value, uint8_t len);
|
2015-09-23 12:24:25 +00:00
|
|
|
static dhcp_lease_t *find_lease_slot(uint8_t *hwaddr);
|
|
|
|
|
|
|
|
/* Copy IP address as dotted decimal to 'dest', must be at least 16 bytes long */
|
2017-06-06 02:47:21 +00:00
|
|
|
inline static void sprintf_ipaddr(const ip4_addr_t *addr, char *dest)
|
2015-09-23 12:24:25 +00:00
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
if (addr == NULL)
|
2015-09-23 12:24:25 +00:00
|
|
|
sprintf(dest, "NULL");
|
|
|
|
else
|
|
|
|
sprintf(dest, "%d.%d.%d.%d", ip4_addr1(addr),
|
|
|
|
ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr));
|
|
|
|
}
|
2015-08-13 07:10:38 +00:00
|
|
|
|
2017-10-11 10:26:43 +00:00
|
|
|
void dhcpserver_start(const ip4_addr_t *first_client_addr, uint8_t max_leases)
|
2015-09-09 22:15:33 +00:00
|
|
|
{
|
|
|
|
/* Stop any existing running dhcpserver */
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpserver_task_handle)
|
2015-09-09 22:15:33 +00:00
|
|
|
dhcpserver_stop();
|
|
|
|
|
|
|
|
state = malloc(sizeof(server_state_t));
|
2018-03-12 05:56:10 +00:00
|
|
|
memset(state, 0, sizeof(*state));
|
2015-09-09 22:15:33 +00:00
|
|
|
state->max_leases = max_leases;
|
|
|
|
state->leases = calloc(max_leases, sizeof(dhcp_lease_t));
|
2017-06-06 02:47:21 +00:00
|
|
|
bzero(state->leases, max_leases * sizeof(dhcp_lease_t));
|
2015-09-09 22:15:33 +00:00
|
|
|
// state->server_if is assigned once the task is running - see comment in dhcpserver_task()
|
2017-06-06 02:47:21 +00:00
|
|
|
ip4_addr_copy(state->first_client_addr, *first_client_addr);
|
2015-09-09 22:15:33 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* Clear options */
|
|
|
|
ip4_addr_set_zero(&state->router);
|
|
|
|
ip4_addr_set_zero(&state->dns);
|
|
|
|
|
2017-10-11 10:26:43 +00:00
|
|
|
xTaskCreate(dhcpserver_task, "DHCP Server", 448, NULL, 2, &dhcpserver_task_handle);
|
2015-09-09 22:15:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dhcpserver_stop(void)
|
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpserver_task_handle) {
|
2015-09-09 22:15:33 +00:00
|
|
|
vTaskDelete(dhcpserver_task_handle);
|
|
|
|
dhcpserver_task_handle = NULL;
|
2018-03-12 05:56:10 +00:00
|
|
|
|
|
|
|
if (state->nc)
|
|
|
|
netconn_delete(state->nc);
|
|
|
|
free(state->leases);
|
|
|
|
free(state);
|
|
|
|
state = NULL;
|
2015-09-09 22:15:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
void dhcpserver_set_router(const ip4_addr_t *router)
|
|
|
|
{
|
|
|
|
ip4_addr_copy(state->router, *router);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dhcpserver_set_dns(const ip4_addr_t *dns)
|
|
|
|
{
|
|
|
|
ip4_addr_copy(state->dns, *dns);
|
|
|
|
}
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
static void dhcpserver_task(void *pxParameter)
|
|
|
|
{
|
2015-09-09 22:15:33 +00:00
|
|
|
/* netif_list isn't assigned until after user_init completes, which is why we do it inside the task */
|
2017-10-11 10:26:43 +00:00
|
|
|
state->server_if = netif_list; /* TODO: Make this configurable */
|
2015-08-13 07:10:38 +00:00
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
state->nc = netconn_new (NETCONN_UDP);
|
2017-10-08 06:47:23 +00:00
|
|
|
if(!state->nc) {
|
|
|
|
debug("DHCP Server Error: Failed to allocate socket.");
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
netconn_bind(state->nc, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
|
2017-10-11 10:26:43 +00:00
|
|
|
netconn_bind_if (state->nc, netif_get_index(state->server_if));
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
struct netbuf *netbuf;
|
|
|
|
struct dhcp_msg received = { 0 };
|
2015-08-24 06:13:24 +00:00
|
|
|
|
|
|
|
/* Receive a DHCP packet */
|
2015-09-09 22:15:33 +00:00
|
|
|
err_t err = netconn_recv(state->nc, &netbuf);
|
2017-10-08 06:47:23 +00:00
|
|
|
if(err != ERR_OK) {
|
|
|
|
debug("DHCP Server Error: Failed to receive DHCP packet. err=%d", err);
|
2015-08-13 07:10:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-08-24 06:13:24 +00:00
|
|
|
/* expire any leases that have passed */
|
|
|
|
uint32_t now = xTaskGetTickCount();
|
2017-06-06 02:47:21 +00:00
|
|
|
for (int i = 0; i < state->max_leases; i++) {
|
|
|
|
if (state->leases[i].active) {
|
|
|
|
uint32_t expires = state->leases[i].expires - now;
|
|
|
|
if (expires >= 0x80000000) {
|
|
|
|
state->leases[i].active = 0;
|
|
|
|
}
|
|
|
|
}
|
2015-08-24 06:13:24 +00:00
|
|
|
}
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
ip_addr_t received_ip;
|
|
|
|
u16_t port;
|
2015-09-09 22:15:33 +00:00
|
|
|
netconn_addr(state->nc, &received_ip, &port);
|
2015-08-13 07:10:38 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
if (netbuf_len(netbuf) < offsetof(struct dhcp_msg, options)) {
|
2015-08-13 07:10:38 +00:00
|
|
|
/* too short to be a valid DHCP client message */
|
|
|
|
netbuf_delete(netbuf);
|
|
|
|
continue;
|
|
|
|
}
|
2017-10-08 06:47:23 +00:00
|
|
|
if(netbuf_len(netbuf) >= sizeof(struct dhcp_msg)) {
|
|
|
|
debug("DHCP Server Warning: Client sent more options than we know how to parse. len=%d", netbuf_len(netbuf));
|
2015-08-13 07:10:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
netbuf_copy(netbuf, &received, sizeof(struct dhcp_msg));
|
|
|
|
netbuf_delete(netbuf);
|
|
|
|
|
|
|
|
uint8_t *message_type = find_dhcp_option(&received, DHCP_OPTION_MESSAGE_TYPE,
|
|
|
|
DHCP_OPTION_MESSAGE_TYPE_LEN, NULL);
|
2017-10-08 06:47:23 +00:00
|
|
|
if(!message_type) {
|
|
|
|
debug("DHCP Server Error: No message type field found");
|
2015-08-13 07:10:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-23 12:24:25 +00:00
|
|
|
|
2017-10-08 06:47:23 +00:00
|
|
|
#if (DHCP_DEBUG == LWIP_DBG_ON)
|
|
|
|
debug("State dump. Message type %d", *message_type);
|
|
|
|
for(int i = 0; i < state->max_leases; i++) {
|
2015-09-23 12:24:25 +00:00
|
|
|
dhcp_lease_t *lease = &state->leases[i];
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("lease slot %d expiry %d hwaddr %02x:%02x:%02x:%02x:%02x:%02x", i, lease->expires, lease->hwaddr[0],
|
|
|
|
lease->hwaddr[1], lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
|
|
|
|
lease->hwaddr[5]);
|
2015-09-23 12:24:25 +00:00
|
|
|
}
|
2017-10-08 06:47:23 +00:00
|
|
|
#endif
|
2015-09-23 12:24:25 +00:00
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
switch(*message_type) {
|
|
|
|
case DHCP_DISCOVER:
|
2015-09-09 22:15:33 +00:00
|
|
|
handle_dhcp_discover(&received);
|
2015-08-13 07:10:38 +00:00
|
|
|
break;
|
|
|
|
case DHCP_REQUEST:
|
2015-09-09 22:15:33 +00:00
|
|
|
handle_dhcp_request(&received);
|
2015-08-13 07:10:38 +00:00
|
|
|
break;
|
2015-08-24 06:13:24 +00:00
|
|
|
case DHCP_RELEASE:
|
2015-09-09 22:15:33 +00:00
|
|
|
handle_dhcp_release(&received);
|
2017-10-10 15:01:07 +00:00
|
|
|
break;
|
2015-08-13 07:10:38 +00:00
|
|
|
default:
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("DHCP Server Error: Unsupported message type %d", *message_type);
|
2015-08-13 07:10:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
static void handle_dhcp_discover(struct dhcp_msg *dhcpmsg)
|
2015-08-13 07:10:38 +00:00
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpmsg->htype != LWIP_IANA_HWTYPE_ETHERNET)
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
|
2015-09-23 12:24:25 +00:00
|
|
|
dhcp_lease_t *freelease = find_lease_slot(dhcpmsg->chaddr);
|
2017-10-08 06:47:23 +00:00
|
|
|
if(!freelease) {
|
|
|
|
debug("DHCP Server: All leases taken.");
|
2015-08-13 07:10:38 +00:00
|
|
|
return; /* Nothing available, so do nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reuse the DISCOVER buffer for the OFFER response */
|
|
|
|
dhcpmsg->op = DHCP_BOOTREPLY;
|
|
|
|
bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
dhcpmsg->yiaddr.addr = lwip_htonl(lwip_ntohl(state->first_client_addr.addr) + (freelease - state->leases));
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
uint8_t *opt = (uint8_t *)&dhcpmsg->options;
|
|
|
|
opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_OFFER);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
|
2017-06-06 02:47:21 +00:00
|
|
|
if (!ip4_addr_isany_val(state->router)) {
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_ROUTER, &state->router, 4);
|
|
|
|
}
|
|
|
|
if (!ip4_addr_isany_val(state->dns)) {
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->dns, 4);
|
|
|
|
}
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
|
|
|
|
|
|
|
|
struct netbuf *netbuf = netbuf_new();
|
|
|
|
netbuf_alloc(netbuf, sizeof(struct dhcp_msg));
|
|
|
|
netbuf_take(netbuf, dhcpmsg, sizeof(struct dhcp_msg));
|
|
|
|
netconn_sendto(state->nc, netbuf, IP_ADDR_BROADCAST, 68);
|
|
|
|
netbuf_delete(netbuf);
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
|
2015-08-13 07:10:38 +00:00
|
|
|
{
|
2015-09-23 12:24:25 +00:00
|
|
|
static char ipbuf[16];
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpmsg->htype != LWIP_IANA_HWTYPE_ETHERNET)
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
ip4_addr_t requested_ip;
|
2015-08-13 07:10:38 +00:00
|
|
|
uint8_t *requested_ip_opt = find_dhcp_option(dhcpmsg, DHCP_OPTION_REQUESTED_IP, 4, NULL);
|
2017-06-06 02:47:21 +00:00
|
|
|
if (requested_ip_opt) {
|
|
|
|
memcpy(&requested_ip.addr, requested_ip_opt, 4);
|
|
|
|
} else if (ip4_addr_cmp(&requested_ip, IP4_ADDR_ANY4)) {
|
|
|
|
ip4_addr_copy(requested_ip, dhcpmsg->ciaddr);
|
2015-08-13 07:10:38 +00:00
|
|
|
} else {
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("DHCP Server Error: No requested IP");
|
2015-09-09 22:15:33 +00:00
|
|
|
send_dhcp_nak(dhcpmsg);
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the first 4 octets match */
|
2017-06-06 02:47:21 +00:00
|
|
|
if (ip4_addr1(&requested_ip) != ip4_addr1(&state->first_client_addr)
|
2015-09-09 22:15:33 +00:00
|
|
|
|| ip4_addr2(&requested_ip) != ip4_addr2(&state->first_client_addr)
|
|
|
|
|| ip4_addr3(&requested_ip) != ip4_addr3(&state->first_client_addr)) {
|
2015-09-23 12:24:25 +00:00
|
|
|
sprintf_ipaddr(&requested_ip, ipbuf);
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("DHCP Server Error: %s not an allowed IP", ipbuf);
|
2015-09-09 22:15:33 +00:00
|
|
|
send_dhcp_nak(dhcpmsg);
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Test the last octet is in the MAXCLIENTS range */
|
2015-09-09 22:15:33 +00:00
|
|
|
int16_t octet_offs = ip4_addr4(&requested_ip) - ip4_addr4(&state->first_client_addr);
|
2017-10-08 06:47:23 +00:00
|
|
|
if(octet_offs < 0 || octet_offs >= state->max_leases) {
|
|
|
|
debug("DHCP Server Error: Address out of range");
|
2015-09-09 22:15:33 +00:00
|
|
|
send_dhcp_nak(dhcpmsg);
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dhcp_lease_t *requested_lease = state->leases + octet_offs;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (requested_lease->active && memcmp(requested_lease->hwaddr, dhcpmsg->chaddr,dhcpmsg->hlen))
|
2015-08-13 07:10:38 +00:00
|
|
|
{
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("DHCP Server Error: Lease for address already taken");
|
2015-09-09 22:15:33 +00:00
|
|
|
send_dhcp_nak(dhcpmsg);
|
2015-08-13 07:10:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(requested_lease->hwaddr, dhcpmsg->chaddr, dhcpmsg->hlen);
|
2015-09-23 12:24:25 +00:00
|
|
|
sprintf_ipaddr(&requested_ip, ipbuf);
|
2017-10-08 06:47:23 +00:00
|
|
|
debug("DHCP lease addr %s assigned to MAC %02x:%02x:%02x:%02x:%02x:%02x", ipbuf, requested_lease->hwaddr[0],
|
2015-08-13 07:10:38 +00:00
|
|
|
requested_lease->hwaddr[1], requested_lease->hwaddr[2], requested_lease->hwaddr[3], requested_lease->hwaddr[4],
|
|
|
|
requested_lease->hwaddr[5]);
|
2017-06-06 02:47:21 +00:00
|
|
|
uint32_t now = xTaskGetTickCount();
|
|
|
|
requested_lease->expires = now + DHCPSERVER_LEASE_TIME * configTICK_RATE_HZ;
|
|
|
|
requested_lease->active = 1;
|
|
|
|
|
|
|
|
sdk_wifi_softap_set_station_info(requested_lease->hwaddr, &requested_ip);
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
/* Reuse the REQUEST message as the ACK message */
|
|
|
|
dhcpmsg->op = DHCP_BOOTREPLY;
|
|
|
|
bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
ip4_addr_copy(dhcpmsg->yiaddr, requested_ip);
|
2015-08-13 07:10:38 +00:00
|
|
|
|
|
|
|
uint8_t *opt = (uint8_t *)&dhcpmsg->options;
|
|
|
|
opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_ACK);
|
|
|
|
uint32_t expiry = htonl(DHCPSERVER_LEASE_TIME);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_LEASE_TIME, &expiry, 4);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
|
2015-09-09 22:15:33 +00:00
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
|
2017-06-06 02:47:21 +00:00
|
|
|
if (!ip4_addr_isany_val(state->router)) {
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_ROUTER, &state->router, 4);
|
|
|
|
}
|
|
|
|
if (!ip4_addr_isany_val(state->dns)) {
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->dns, 4);
|
|
|
|
}
|
|
|
|
|
2015-08-13 07:10:38 +00:00
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
|
|
|
|
|
|
|
|
struct netbuf *netbuf = netbuf_new();
|
|
|
|
netbuf_alloc(netbuf, sizeof(struct dhcp_msg));
|
|
|
|
netbuf_take(netbuf, dhcpmsg, sizeof(struct dhcp_msg));
|
2015-08-24 06:13:24 +00:00
|
|
|
netconn_sendto(state->nc, netbuf, IP_ADDR_BROADCAST, 68);
|
|
|
|
netbuf_delete(netbuf);
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
static void handle_dhcp_release(struct dhcp_msg *dhcpmsg)
|
2015-08-24 06:13:24 +00:00
|
|
|
{
|
2015-09-23 12:24:25 +00:00
|
|
|
dhcp_lease_t *lease = find_lease_slot(dhcpmsg->chaddr);
|
2017-06-06 02:47:21 +00:00
|
|
|
if (lease) {
|
|
|
|
lease->active = 0;
|
2015-08-24 06:13:24 +00:00
|
|
|
lease->expires = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:15:33 +00:00
|
|
|
static void send_dhcp_nak(struct dhcp_msg *dhcpmsg)
|
2015-08-24 06:13:24 +00:00
|
|
|
{
|
|
|
|
/* Reuse 'dhcpmsg' for the NAK */
|
|
|
|
dhcpmsg->op = DHCP_BOOTREPLY;
|
|
|
|
bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);
|
|
|
|
|
|
|
|
uint8_t *opt = (uint8_t *)&dhcpmsg->options;
|
|
|
|
opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_NAK);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
|
|
|
|
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
|
|
|
|
|
|
|
|
struct netbuf *netbuf = netbuf_new();
|
|
|
|
netbuf_alloc(netbuf, sizeof(struct dhcp_msg));
|
|
|
|
netbuf_take(netbuf, dhcpmsg, sizeof(struct dhcp_msg));
|
2015-08-13 07:10:38 +00:00
|
|
|
netconn_sendto(state->nc, netbuf, IP_ADDR_BROADCAST, 68);
|
|
|
|
netbuf_delete(netbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t *find_dhcp_option(struct dhcp_msg *msg, uint8_t option_num, uint8_t min_length, uint8_t *length)
|
|
|
|
{
|
|
|
|
uint8_t *start = (uint8_t *)&msg->options;
|
|
|
|
uint8_t *msg_end = (uint8_t *)msg + sizeof(struct dhcp_msg);
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
for (uint8_t *p = start; p < msg_end-2;) {
|
2015-08-13 07:10:38 +00:00
|
|
|
uint8_t type = *p++;
|
|
|
|
uint8_t len = *p++;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (type == DHCP_OPTION_END)
|
2015-08-13 07:10:38 +00:00
|
|
|
return NULL;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (p+len >= msg_end)
|
2015-08-13 07:10:38 +00:00
|
|
|
break; /* We've overrun our valid DHCP message size, or this isn't a valid option */
|
2017-06-06 02:47:21 +00:00
|
|
|
if (type == option_num) {
|
|
|
|
if (len < min_length)
|
2015-08-13 07:10:38 +00:00
|
|
|
break;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (length)
|
2015-08-13 07:10:38 +00:00
|
|
|
*length = len;
|
|
|
|
return p; /* start of actual option data */
|
|
|
|
}
|
|
|
|
p += len;
|
|
|
|
}
|
|
|
|
return NULL; /* Not found */
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t *add_dhcp_option_byte(uint8_t *opt, uint8_t type, uint8_t value)
|
|
|
|
{
|
|
|
|
*opt++ = type;
|
|
|
|
*opt++ = 1;
|
|
|
|
*opt++ = value;
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t *add_dhcp_option_bytes(uint8_t *opt, uint8_t type, void *value, uint8_t len)
|
|
|
|
{
|
|
|
|
*opt++ = type;
|
2017-06-06 02:47:21 +00:00
|
|
|
if (len) {
|
2015-08-13 07:10:38 +00:00
|
|
|
*opt++ = len;
|
|
|
|
memcpy(opt, value, len);
|
|
|
|
}
|
|
|
|
return opt+len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a free DHCP lease, or a lease already assigned to 'hwaddr' */
|
2015-09-23 12:24:25 +00:00
|
|
|
static dhcp_lease_t *find_lease_slot(uint8_t *hwaddr)
|
2015-08-13 07:10:38 +00:00
|
|
|
{
|
|
|
|
dhcp_lease_t *empty_lease = NULL;
|
2017-06-06 02:47:21 +00:00
|
|
|
for (int i = 0; i < state->max_leases; i++) {
|
|
|
|
if (!state->leases[i].active && !empty_lease)
|
2015-09-23 12:24:25 +00:00
|
|
|
empty_lease = &state->leases[i];
|
|
|
|
else if (memcmp(hwaddr, state->leases[i].hwaddr, 6) == 0)
|
|
|
|
return &state->leases[i];
|
2015-08-13 07:10:38 +00:00
|
|
|
}
|
|
|
|
return empty_lease;
|
|
|
|
}
|