1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00
esp8266/cores/esp8266/LwipDhcpServer.cpp
2022-03-04 02:28:47 +03:00

1594 lines
44 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
lwIPDhcpServer.c - DHCP server
Copyright (c) 2016 Espressif. All rights reserved.
Copyright (c) 2020 esp8266 arduino. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// original sources (no license provided)
// ESP8266_NONOS_SDK/third_party/lwip/app/dhcpserver.c
// ESP8266_NONOS_SDK/third_party/include/lwip/app/dhcpserver.h
*/
// lwIPDhcpServer.{cc,h} encapsulate original nonos-sdk dhcp server
// nearly as-is. This is an initial version to guaranty legacy behavior
// with same default values.
// vv this comment is supposed to be removed after the first commit
// Logic and coding style in this file can be wrong but left to the closest
// of the initial version for easier issue tracking.
// (better is enemy of [good = already working])
// ^^ this comment is supposed to be removed after the first commit
#include <lwip/init.h> // LWIP_VERSION
#define DHCPS_LEASE_TIME_DEF (120)
#define USE_DNS
#include "lwip/inet.h"
#include "lwip/err.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/mem.h"
#include "osapi.h"
#include "LwipDhcpServer.h"
#include "user_interface.h"
#include "mem.h"
typedef struct dhcps_state
{
sint16_t state;
} dhcps_state;
typedef struct dhcps_msg
{
uint8_t op, htype, hlen, hops;
uint8_t xid[4];
uint16_t secs, flags;
uint8_t ciaddr[4];
uint8_t yiaddr[4];
uint8_t siaddr[4];
uint8_t giaddr[4];
uint8_t chaddr[16];
uint8_t sname[64];
uint8_t file[128];
uint8_t options[312];
} dhcps_msg;
#ifndef LWIP_OPEN_SRC
struct dhcps_lease
{
bool enable;
struct ipv4_addr start_ip;
struct ipv4_addr end_ip;
};
enum dhcps_offer_option
{
OFFER_START = 0x00,
OFFER_ROUTER = 0x01,
OFFER_END
};
#endif
typedef enum
{
DHCPS_TYPE_DYNAMIC,
DHCPS_TYPE_STATIC
} dhcps_type_t;
typedef enum
{
DHCPS_STATE_ONLINE,
DHCPS_STATE_OFFLINE
} dhcps_state_t;
struct dhcps_pool
{
struct ipv4_addr ip;
uint8 mac[6];
uint32 lease_timer;
dhcps_type_t type;
dhcps_state_t state;
};
#define DHCPS_LEASE_TIMER dhcps_lease_time // 0x05A0
#define DHCPS_MAX_LEASE 0x64
#define BOOTP_BROADCAST 0x8000
#define DHCP_REQUEST 1
#define DHCP_REPLY 2
#define DHCP_HTYPE_ETHERNET 1
#define DHCP_HLEN_ETHERNET 6
#define DHCP_MSG_LEN 236
#define DHCPS_SERVER_PORT 67
#define DHCPS_CLIENT_PORT 68
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCP_OPTION_SUBNET_MASK 1
#define DHCP_OPTION_ROUTER 3
#define DHCP_OPTION_DNS_SERVER 6
#define DHCP_OPTION_REQ_IPADDR 50
#define DHCP_OPTION_LEASE_TIME 51
#define DHCP_OPTION_MSG_TYPE 53
#define DHCP_OPTION_SERVER_ID 54
#define DHCP_OPTION_INTERFACE_MTU 26
#define DHCP_OPTION_PERFORM_ROUTER_DISCOVERY 31
#define DHCP_OPTION_BROADCAST_ADDRESS 28
#define DHCP_OPTION_REQ_LIST 55
#define DHCP_OPTION_END 255
//#define USE_CLASS_B_NET 1
#define DHCPS_DEBUG 0
#define MAX_STATION_NUM 8
#define DHCPS_STATE_OFFER 1
#define DHCPS_STATE_DECLINE 2
#define DHCPS_STATE_ACK 3
#define DHCPS_STATE_NAK 4
#define DHCPS_STATE_IDLE 5
#define DHCPS_STATE_RELEASE 6
#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0)
#ifdef MEMLEAK_DEBUG
const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif
#if DHCPS_DEBUG
#define LWIP_IS_OK(what, err) \
({ \
int ret = 1, errval = (err); \
if (errval != ERR_OK) \
{ \
os_printf("DHCPS ERROR: %s (lwip:%d)\n", what, errval); \
ret = 0; \
} \
ret; \
})
#else
#define LWIP_IS_OK(what, err) ((err) == ERR_OK)
#endif
const uint32 DhcpServer::magic_cookie = 0x63538263; // https://tools.ietf.org/html/rfc1497
int fw_has_started_softap_dhcps = 0;
////////////////////////////////////////////////////////////////////////////////////
DhcpServer::DhcpServer(netif* netif) : _netif(netif)
{
pcb_dhcps = nullptr;
dns_address.addr = 0;
plist = nullptr;
offer = 0xFF;
renew = false;
dhcps_lease_time = DHCPS_LEASE_TIME_DEF; // minute
if (netif->num == SOFTAP_IF && fw_has_started_softap_dhcps == 1)
{
// When nonos-sdk starts DHCPS at boot:
// 1. `fw_has_started_softap_dhcps` is already initialized to 1
// 2. global ctor DhcpServer's `dhcpSoftAP(&netif_git[SOFTAP_IF])` is called
// 3. (that's here) => begin(legacy-values) is called
ip_info ip = {
{ 0x0104a8c0 }, // IP 192.168.4.1
{ 0x00ffffff }, // netmask 255.255.255.0
{ 0 } // gateway 0.0.0.0
};
begin(&ip);
fw_has_started_softap_dhcps = 2; // not 1, ending initial boot sequence
}
};
// wifi_softap_set_station_info is missing in user_interface.h:
extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*);
void DhcpServer::dhcps_set_dns(int num, const ipv4_addr_t* dns)
{
(void)num;
if (!ip4_addr_isany(dns))
{
ip4_addr_copy(dns_address, *dns);
}
}
/******************************************************************************
FunctionName : node_insert_to_list
Description : insert the node to the list
Parameters : arg -- Additional argument to pass to the callback function
Returns : none
*******************************************************************************/
void DhcpServer::node_insert_to_list(list_node** phead, list_node* pinsert)
{
list_node* plist = nullptr;
struct dhcps_pool* pdhcps_pool = nullptr;
struct dhcps_pool* pdhcps_node = nullptr;
if (*phead == nullptr)
{
*phead = pinsert;
}
else
{
plist = *phead;
pdhcps_node = (struct dhcps_pool*)pinsert->pnode;
pdhcps_pool = (struct dhcps_pool*)plist->pnode;
if (pdhcps_node->ip.addr < pdhcps_pool->ip.addr)
{
pinsert->pnext = plist;
*phead = pinsert;
}
else
{
while (plist->pnext != nullptr)
{
pdhcps_pool = (struct dhcps_pool*)plist->pnext->pnode;
if (pdhcps_node->ip.addr < pdhcps_pool->ip.addr)
{
pinsert->pnext = plist->pnext;
plist->pnext = pinsert;
break;
}
plist = plist->pnext;
}
if (plist->pnext == nullptr)
{
plist->pnext = pinsert;
}
}
}
// pinsert->pnext = nullptr;
}
/******************************************************************************
FunctionName : node_delete_from_list
Description : remove the node from list
Parameters : arg -- Additional argument to pass to the callback function
Returns : none
*******************************************************************************/
void DhcpServer::node_remove_from_list(list_node** phead, list_node* pdelete)
{
list_node* plist = nullptr;
plist = *phead;
if (plist == nullptr)
{
*phead = nullptr;
}
else
{
if (plist == pdelete)
{
*phead = plist->pnext;
pdelete->pnext = nullptr;
}
else
{
while (plist != nullptr)
{
if (plist->pnext == pdelete)
{
plist->pnext = pdelete->pnext;
pdelete->pnext = nullptr;
}
plist = plist->pnext;
}
}
}
}
/******************************************************************************
FunctionName : add_dhcps_lease
Description : add static lease on the list, this will be the next available @
Parameters : mac address
Returns : true if ok and false if this mac already exist or if all ip are already reserved
*******************************************************************************/
bool DhcpServer::add_dhcps_lease(uint8* macaddr)
{
struct dhcps_pool* pdhcps_pool = nullptr;
list_node* pback_node = nullptr;
uint32 start_ip = dhcps_lease.start_ip.addr;
uint32 end_ip = dhcps_lease.end_ip.addr;
for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext)
{
pdhcps_pool = (dhcps_pool*)pback_node->pnode;
if (memcmp(pdhcps_pool->mac, macaddr, sizeof(pdhcps_pool->mac)) == 0)
{
#if DHCPS_DEBUG
os_printf("this mac already exist");
#endif
return false;
}
else
{
start_ip = htonl((ntohl(start_ip) + 1));
}
}
if (start_ip > end_ip)
{
#if DHCPS_DEBUG
os_printf("no more ip available");
#endif
return false;
}
pdhcps_pool = (struct dhcps_pool*)zalloc(sizeof(struct dhcps_pool));
pdhcps_pool->ip.addr = start_ip;
memcpy(pdhcps_pool->mac, macaddr, sizeof(pdhcps_pool->mac));
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
pdhcps_pool->type = DHCPS_TYPE_STATIC;
pdhcps_pool->state = DHCPS_STATE_ONLINE;
pback_node = (list_node*)zalloc(sizeof(list_node));
pback_node->pnode = pdhcps_pool;
pback_node->pnext = nullptr;
node_insert_to_list(&plist, pback_node);
return true;
}
///////////////////////////////////////////////////////////////////////////////////
/*
DHCP msg
@param optptr -- DHCP msg
@param type -- option
@return uint8_t* DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
uint8_t* DhcpServer::add_msg_type(uint8_t* optptr, uint8_t type)
{
*optptr++ = DHCP_OPTION_MSG_TYPE;
*optptr++ = 1;
*optptr++ = type;
return optptr;
}
///////////////////////////////////////////////////////////////////////////////////
/*
DHCP msg offer
@param optptr -- DHCP msg
@return uint8_t* DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
uint8_t* DhcpServer::add_offer_options(uint8_t* optptr)
{
// struct ipv4_addr ipadd;
// ipadd.addr = server_address.addr;
#define ipadd (_netif->ip_addr)
// struct ip_info if_ip;
// bzero(&if_ip, sizeof(struct ip_info));
// wifi_get_ip_info(SOFTAP_IF, &if_ip);
#define if_ip (*_netif)
*optptr++ = DHCP_OPTION_SUBNET_MASK;
*optptr++ = 4;
*optptr++ = ip4_addr1(ip_2_ip4(&if_ip.netmask));
*optptr++ = ip4_addr2(ip_2_ip4(&if_ip.netmask));
*optptr++ = ip4_addr3(ip_2_ip4(&if_ip.netmask));
*optptr++ = ip4_addr4(ip_2_ip4(&if_ip.netmask));
*optptr++ = DHCP_OPTION_LEASE_TIME;
*optptr++ = 4;
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 24) & 0xFF;
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 16) & 0xFF;
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 8) & 0xFF;
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 0) & 0xFF;
*optptr++ = DHCP_OPTION_SERVER_ID;
*optptr++ = 4;
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr4(ip_2_ip4(&ipadd));
if (dhcps_router_enabled(offer) && ip_2_ip4(&if_ip.gw)->addr)
{
*optptr++ = DHCP_OPTION_ROUTER;
*optptr++ = 4;
*optptr++ = ip4_addr1(ip_2_ip4(&if_ip.gw));
*optptr++ = ip4_addr2(ip_2_ip4(&if_ip.gw));
*optptr++ = ip4_addr3(ip_2_ip4(&if_ip.gw));
*optptr++ = ip4_addr4(ip_2_ip4(&if_ip.gw));
}
#ifdef USE_DNS
*optptr++ = DHCP_OPTION_DNS_SERVER;
*optptr++ = 4;
if (dns_address.addr == 0)
{
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr4(ip_2_ip4(&ipadd));
}
else
{
*optptr++ = ip4_addr1(&dns_address);
*optptr++ = ip4_addr2(&dns_address);
*optptr++ = ip4_addr3(&dns_address);
*optptr++ = ip4_addr4(&dns_address);
}
#endif
*optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
*optptr++ = 4;
// XXXFIXME do better than that, we have netmask
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
*optptr++ = 255;
*optptr++ = DHCP_OPTION_INTERFACE_MTU;
*optptr++ = 2;
*optptr++ = 0x05;
*optptr++ = 0xdc; // 1500
*optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY;
*optptr++ = 1;
*optptr++ = 0x00;
#if 0 // vendor specific uninitialized (??)
*optptr++ = 43; // vendor specific
*optptr++ = 6;
// uninitialized ?
#endif
#if 0 // already set (DHCP_OPTION_SUBNET_MASK==1) (??)
*optptr++ = 0x01;
*optptr++ = 4;
*optptr++ = 0;
*optptr++ = 0;
*optptr++ = 0;
*optptr++ = 2;
#endif
return optptr;
#undef ipadd
#undef if_ip
}
///////////////////////////////////////////////////////////////////////////////////
/*
DHCP msg
@param optptr -- DHCP msg
@return uint8_t* DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
uint8_t* DhcpServer::add_end(uint8_t* optptr)
{
*optptr++ = DHCP_OPTION_END;
return optptr;
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::create_msg(struct dhcps_msg* m)
{
struct ipv4_addr client;
client.addr = client_address.addr;
m->op = DHCP_REPLY;
m->htype = DHCP_HTYPE_ETHERNET;
m->hlen = 6;
m->hops = 0;
m->secs = 0;
m->flags = htons(BOOTP_BROADCAST);
memcpy((char*)m->yiaddr, (char*)&client.addr, sizeof(m->yiaddr));
memset((char*)m->ciaddr, 0, sizeof(m->ciaddr));
memset((char*)m->siaddr, 0, sizeof(m->siaddr));
memset((char*)m->giaddr, 0, sizeof(m->giaddr));
memset((char*)m->sname, 0, sizeof(m->sname));
memset((char*)m->file, 0, sizeof(m->file));
memset((char*)m->options, 0, sizeof(m->options));
memcpy((char*)m->options, &magic_cookie, sizeof(magic_cookie));
}
///////////////////////////////////////////////////////////////////////////////////
/*
OFFER
@param -- m DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::send_offer(struct dhcps_msg* m)
{
uint8_t* end;
struct pbuf *p, *q;
u8_t* data;
u16_t cnt = 0;
u16_t i;
create_msg(m);
end = add_msg_type(&m->options[4], DHCPOFFER);
end = add_offer_options(end);
end = add_end(end);
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
#if DHCPS_DEBUG
os_printf("udhcp: send_offer>>p->ref = %d\n", p->ref);
#endif
if (p != nullptr)
{
#if DHCPS_DEBUG
os_printf("dhcps: send_offer>>pbuf_alloc succeed\n");
os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len);
os_printf("dhcps: send_offer>>p->len = %d\n", p->len);
#endif
q = p;
while (q != nullptr)
{
data = (u8_t*)q->payload;
for (i = 0; i < q->len; i++)
{
data[i] = ((u8_t*)m)[cnt++];
}
q = q->next;
}
}
else
{
#if DHCPS_DEBUG
os_printf("dhcps: send_offer>>pbuf_alloc failed\n");
#endif
return;
}
if (!LWIP_IS_OK("send_offer", udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT)))
{
#if DHCPS_DEBUG
os_printf("dhcps: send_offer>>udp_sendto\n");
#endif
}
if (p->ref != 0)
{
#if DHCPS_DEBUG
os_printf("udhcp: send_offer>>free pbuf\n");
#endif
pbuf_free(p);
}
}
///////////////////////////////////////////////////////////////////////////////////
/*
NAK
@param m DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::send_nak(struct dhcps_msg* m)
{
u8_t* end;
struct pbuf *p, *q;
u8_t* data;
u16_t cnt = 0;
u16_t i;
create_msg(m);
end = add_msg_type(&m->options[4], DHCPNAK);
end = add_end(end);
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
#if DHCPS_DEBUG
os_printf("udhcp: send_nak>>p->ref = %d\n", p->ref);
#endif
if (p != nullptr)
{
#if DHCPS_DEBUG
os_printf("dhcps: send_nak>>pbuf_alloc succeed\n");
os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len);
os_printf("dhcps: send_nak>>p->len = %d\n", p->len);
#endif
q = p;
while (q != nullptr)
{
data = (u8_t*)q->payload;
for (i = 0; i < q->len; i++)
{
data[i] = ((u8_t*)m)[cnt++];
}
q = q->next;
}
}
else
{
#if DHCPS_DEBUG
os_printf("dhcps: send_nak>>pbuf_alloc failed\n");
#endif
return;
}
LWIP_IS_OK("dhcps send nak", udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT));
if (p->ref != 0)
{
#if DHCPS_DEBUG
os_printf("udhcp: send_nak>>free pbuf\n");
#endif
pbuf_free(p);
}
}
///////////////////////////////////////////////////////////////////////////////////
/*
ACK DHCP
@param m DHCP msg
*/
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::send_ack(struct dhcps_msg* m)
{
u8_t* end;
struct pbuf *p, *q;
u8_t* data;
u16_t cnt = 0;
u16_t i;
create_msg(m);
end = add_msg_type(&m->options[4], DHCPACK);
end = add_offer_options(end);
end = add_end(end);
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
#if DHCPS_DEBUG
os_printf("udhcp: send_ack>>p->ref = %d\n", p->ref);
#endif
if (p != nullptr)
{
#if DHCPS_DEBUG
os_printf("dhcps: send_ack>>pbuf_alloc succeed\n");
os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len);
os_printf("dhcps: send_ack>>p->len = %d\n", p->len);
#endif
q = p;
while (q != nullptr)
{
data = (u8_t*)q->payload;
for (i = 0; i < q->len; i++)
{
data[i] = ((u8_t*)m)[cnt++];
}
q = q->next;
}
}
else
{
#if DHCPS_DEBUG
os_printf("dhcps: send_ack>>pbuf_alloc failed\n");
#endif
return;
}
if (!LWIP_IS_OK("dhcps send ack",
udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT)))
{
#if DHCPS_DEBUG
os_printf("dhcps: send_ack>>udp_sendto\n");
#endif
}
if (p->ref != 0)
{
#if DHCPS_DEBUG
os_printf("udhcp: send_ack>>free pbuf\n");
#endif
pbuf_free(p);
}
}
///////////////////////////////////////////////////////////////////////////////////
/*
DHCP
@param optptr DHCP msg е
@param len
@return uint8_t* DHCP Server
*/
///////////////////////////////////////////////////////////////////////////////////
uint8_t DhcpServer::parse_options(uint8_t* optptr, sint16_t len)
{
struct ipv4_addr client;
bool is_dhcp_parse_end = false;
struct dhcps_state s;
client.addr = client_address.addr;
u8_t* end = optptr + len;
u16_t type = 0;
s.state = DHCPS_STATE_IDLE;
while (optptr < end)
{
#if DHCPS_DEBUG
os_printf("dhcps: (sint16_t)*optptr = %d\n", (sint16_t)*optptr);
#endif
switch ((sint16_t)*optptr)
{
case DHCP_OPTION_MSG_TYPE: // 53
type = *(optptr + 2);
break;
case DHCP_OPTION_REQ_IPADDR: // 50
// os_printf("dhcps:0x%08x,0x%08x\n",client.addr,*(uint32*)(optptr+2));
if (memcmp((char*)&client.addr, (char*)optptr + 2, 4) == 0)
{
#if DHCPS_DEBUG
os_printf("dhcps: DHCP_OPTION_REQ_IPADDR = 0 ok\n");
#endif
s.state = DHCPS_STATE_ACK;
}
else
{
#if DHCPS_DEBUG
os_printf("dhcps: DHCP_OPTION_REQ_IPADDR != 0 err\n");
#endif
s.state = DHCPS_STATE_NAK;
}
break;
case DHCP_OPTION_END:
{
is_dhcp_parse_end = true;
}
break;
}
if (is_dhcp_parse_end)
{
break;
}
optptr += optptr[1] + 2;
}
switch (type)
{
case DHCPDISCOVER: // 1
s.state = DHCPS_STATE_OFFER;
#if DHCPS_DEBUG
os_printf("dhcps: DHCPD_STATE_OFFER\n");
#endif
break;
case DHCPREQUEST: // 3
if (!(s.state == DHCPS_STATE_ACK || s.state == DHCPS_STATE_NAK))
{
if (renew == true)
{
s.state = DHCPS_STATE_ACK;
}
else
{
s.state = DHCPS_STATE_NAK;
}
#if DHCPS_DEBUG
os_printf("dhcps: DHCPD_STATE_NAK\n");
#endif
}
break;
case DHCPDECLINE: // 4
s.state = DHCPS_STATE_IDLE;
#if DHCPS_DEBUG
os_printf("dhcps: DHCPD_STATE_IDLE\n");
#endif
break;
case DHCPRELEASE: // 7
s.state = DHCPS_STATE_RELEASE;
#if DHCPS_DEBUG
os_printf("dhcps: DHCPD_STATE_IDLE\n");
#endif
break;
}
#if DHCPS_DEBUG
os_printf("dhcps: return s.state = %d\n", s.state);
#endif
return s.state;
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
sint16_t DhcpServer::parse_msg(struct dhcps_msg* m, u16_t len)
{
if (memcmp((char*)m->options, &magic_cookie, sizeof(magic_cookie)) == 0)
{
struct ipv4_addr ip;
memcpy(&ip.addr, m->ciaddr, sizeof(ip.addr));
client_address.addr = dhcps_client_update(m->chaddr, &ip);
sint16_t ret = parse_options(&m->options[4], len);
if (ret == DHCPS_STATE_RELEASE)
{
dhcps_client_leave(m->chaddr, &ip, true); // force to delete
client_address.addr = ip.addr;
}
return ret;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////
/*
DHCP
udp_recv() callback
@param arg
@param pcb
@param p
@param addr
@param port
*/
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::S_handle_dhcp(void* arg, struct udp_pcb* pcb, struct pbuf* p,
const ip_addr_t* addr, uint16_t port)
{
DhcpServer* instance = reinterpret_cast<DhcpServer*>(arg);
instance->handle_dhcp(pcb, p, addr, port);
}
void DhcpServer::handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr,
uint16_t port)
{
(void)pcb;
(void)addr;
(void)port;
struct dhcps_msg* pmsg_dhcps = nullptr;
sint16_t tlen = 0;
u16_t i = 0;
u16_t dhcps_msg_cnt = 0;
u8_t* p_dhcps_msg = nullptr;
u8_t* data = nullptr;
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> receive a packet\n");
#endif
if (p == nullptr)
{
return;
}
pmsg_dhcps = (struct dhcps_msg*)zalloc(sizeof(struct dhcps_msg));
if (nullptr == pmsg_dhcps)
{
pbuf_free(p);
return;
}
p_dhcps_msg = (u8_t*)pmsg_dhcps;
tlen = p->tot_len;
data = (u8_t*)p->payload;
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> p->tot_len = %d\n", tlen);
os_printf("dhcps: handle_dhcp-> p->len = %d\n", p->len);
#endif
for (i = 0; i < p->len; i++)
{
p_dhcps_msg[dhcps_msg_cnt++] = data[i];
}
if (p->next != nullptr)
{
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> p->next != nullptr\n");
os_printf("dhcps: handle_dhcp-> p->next->tot_len = %d\n", p->next->tot_len);
os_printf("dhcps: handle_dhcp-> p->next->len = %d\n", p->next->len);
#endif
data = (u8_t*)p->next->payload;
for (i = 0; i < p->next->len; i++)
{
p_dhcps_msg[dhcps_msg_cnt++] = data[i];
}
}
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> parse_msg(p)\n");
#endif
switch (parse_msg(pmsg_dhcps, tlen - 240))
{
case DHCPS_STATE_OFFER: // 1
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n");
#endif
send_offer(pmsg_dhcps);
break;
case DHCPS_STATE_ACK: // 3
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> DHCPD_STATE_ACK\n");
#endif
send_ack(pmsg_dhcps);
if (_netif->num == SOFTAP_IF)
{
wifi_softap_set_station_info(pmsg_dhcps->chaddr, &client_address);
}
break;
case DHCPS_STATE_NAK: // 4
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> DHCPD_STATE_NAK\n");
#endif
send_nak(pmsg_dhcps);
break;
default:
break;
}
#if DHCPS_DEBUG
os_printf("dhcps: handle_dhcp-> pbuf_free(p)\n");
#endif
pbuf_free(p);
free(pmsg_dhcps);
pmsg_dhcps = nullptr;
}
///////////////////////////////////////////////////////////////////////////////////
void DhcpServer::init_dhcps_lease(uint32 ip)
{
uint32 softap_ip = 0, local_ip = 0;
uint32 start_ip = 0;
uint32 end_ip = 0;
if (dhcps_lease.enable == true)
{
softap_ip = htonl(ip);
start_ip = htonl(dhcps_lease.start_ip.addr);
end_ip = htonl(dhcps_lease.end_ip.addr);
/*config ip information can't contain local ip*/
if ((start_ip <= softap_ip) && (softap_ip <= end_ip))
{
dhcps_lease.enable = false;
}
else
{
/*config ip information must be in the same segment as the local ip*/
softap_ip >>= 8;
if (((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip))
|| (end_ip - start_ip > DHCPS_MAX_LEASE))
{
dhcps_lease.enable = false;
}
}
}
if (dhcps_lease.enable == false)
{
local_ip = softap_ip = htonl(ip);
softap_ip &= 0xFFFFFF00;
local_ip &= 0xFF;
if (local_ip >= 0x80)
{
local_ip -= DHCPS_MAX_LEASE;
}
else
{
local_ip++;
}
bzero(&dhcps_lease, sizeof(dhcps_lease));
dhcps_lease.start_ip.addr = softap_ip | local_ip;
dhcps_lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1);
dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr);
dhcps_lease.end_ip.addr = htonl(dhcps_lease.end_ip.addr);
}
// dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr);
// dhcps_lease.end_ip.addr= htonl(dhcps_lease.end_ip.addr);
// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip);
}
///////////////////////////////////////////////////////////////////////////////////
bool DhcpServer::begin(struct ip_info* info)
{
if (pcb_dhcps != nullptr)
{
udp_remove(pcb_dhcps);
}
pcb_dhcps = udp_new();
if (pcb_dhcps == nullptr || info == nullptr)
{
os_printf("dhcps_start(): could not obtain pcb\n");
return false;
}
// wrong: answer will go to sta IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255);
// good: going to ap IP4_ADDR(&broadcast_dhcps, 192, 168, 4, 255);
// semi proper way:
broadcast_dhcps = _netif->ip_addr;
ip_2_ip4(&broadcast_dhcps)->addr &= ip_2_ip4(&_netif->netmask)->addr;
ip_2_ip4(&broadcast_dhcps)->addr |= ~ip_2_ip4(&_netif->netmask)->addr;
// XXXFIXMEIPV6 broadcast address?
server_address = info->ip;
init_dhcps_lease(server_address.addr);
udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT);
udp_recv(pcb_dhcps, S_handle_dhcp, this);
#if DHCPS_DEBUG
os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB "
"pcb_dhcps\n");
#endif
if (_netif->num == SOFTAP_IF)
{
wifi_set_ip_info(SOFTAP_IF, info); // added for lwip-git, not sure whether useful
}
_netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP; // added for lwip-git
return true;
}
DhcpServer::~DhcpServer()
{
end();
}
void DhcpServer::end()
{
if (!pcb_dhcps)
{
return;
}
udp_disconnect(pcb_dhcps);
udp_remove(pcb_dhcps);
pcb_dhcps = nullptr;
// udp_remove(pcb_dhcps);
list_node* pnode = nullptr;
list_node* pback_node = nullptr;
struct dhcps_pool* dhcp_node = nullptr;
struct ipv4_addr ip_zero;
memset(&ip_zero, 0x0, sizeof(ip_zero));
pnode = plist;
while (pnode != nullptr)
{
pback_node = pnode;
pnode = pback_node->pnext;
node_remove_from_list(&plist, pback_node);
dhcp_node = (struct dhcps_pool*)pback_node->pnode;
// dhcps_client_leave(dhcp_node->mac,&dhcp_node->ip,true); // force to delete
if (_netif->num == SOFTAP_IF)
{
wifi_softap_set_station_info(dhcp_node->mac, &ip_zero);
}
free(pback_node->pnode);
pback_node->pnode = nullptr;
free(pback_node);
pback_node = nullptr;
}
}
bool DhcpServer::isRunning()
{
return !!_netif->state;
}
/******************************************************************************
FunctionName : set_dhcps_lease
Description : set the lease information of DHCP server
Parameters : please -- Additional argument to set the lease information,
Little-Endian.
Returns : true or false
*******************************************************************************/
bool DhcpServer::set_dhcps_lease(struct dhcps_lease* please)
{
uint32 softap_ip = 0;
uint32 start_ip = 0;
uint32 end_ip = 0;
if (_netif->num == SOFTAP_IF || _netif->num == STATION_IF)
{
uint8 opmode = wifi_get_opmode();
if (opmode == STATION_MODE || opmode == NULL_MODE)
{
return false;
}
}
if (please == nullptr || isRunning())
{
return false;
}
if (please->enable)
{
// logic below is subject for improvement
// - is wrong
// - limited to /24 address plans
softap_ip = htonl(ip_2_ip4(&_netif->ip_addr)->addr);
start_ip = htonl(please->start_ip.addr);
end_ip = htonl(please->end_ip.addr);
/*config ip information can't contain local ip*/
if ((start_ip <= softap_ip) && (softap_ip <= end_ip))
{
return false;
}
/*config ip information must be in the same segment as the local ip*/
softap_ip >>= 8;
if ((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip))
{
return false;
}
if (end_ip - start_ip > DHCPS_MAX_LEASE)
{
return false;
}
bzero(&dhcps_lease, sizeof(dhcps_lease));
// dhcps_lease.start_ip.addr = start_ip;
// dhcps_lease.end_ip.addr = end_ip;
dhcps_lease.start_ip.addr = please->start_ip.addr;
dhcps_lease.end_ip.addr = please->end_ip.addr;
}
dhcps_lease.enable = please->enable;
// dhcps_lease_flag = false;
return true;
}
/******************************************************************************
FunctionName : get_dhcps_lease
Description : get the lease information of DHCP server
Parameters : please -- Additional argument to get the lease information,
Little-Endian.
Returns : true or false
*******************************************************************************/
bool DhcpServer::get_dhcps_lease(struct dhcps_lease* please)
{
if (_netif->num == SOFTAP_IF)
{
uint8 opmode = wifi_get_opmode();
if (opmode == STATION_MODE || opmode == NULL_MODE)
{
return false;
}
}
if (nullptr == please)
{
return false;
}
// if (dhcps_lease_flag){
if (dhcps_lease.enable == false)
{
if (isRunning())
{
return false;
}
}
else
{
// bzero(please, sizeof(dhcps_lease));
// if (!isRunning()){
// please->start_ip.addr = htonl(dhcps_lease.start_ip.addr);
// please->end_ip.addr = htonl(dhcps_lease.end_ip.addr);
// }
}
// if (isRunning()){
// bzero(please, sizeof(dhcps_lease));
// please->start_ip.addr = dhcps_lease.start_ip.addr;
// please->end_ip.addr = dhcps_lease.end_ip.addr;
// }
please->start_ip.addr = dhcps_lease.start_ip.addr;
please->end_ip.addr = dhcps_lease.end_ip.addr;
return true;
}
void DhcpServer::kill_oldest_dhcps_pool(void)
{
list_node * pre = nullptr, *p = nullptr;
list_node * minpre = nullptr, *minp = nullptr;
struct dhcps_pool *pdhcps_pool = nullptr, *pmin_pool = nullptr;
pre = plist;
p = pre->pnext;
minpre = pre;
minp = p;
while (p != nullptr)
{
pdhcps_pool = (struct dhcps_pool*)p->pnode;
pmin_pool = (struct dhcps_pool*)minp->pnode;
if (pdhcps_pool->lease_timer < pmin_pool->lease_timer)
{
minp = p;
minpre = pre;
}
pre = p;
p = p->pnext;
}
minpre->pnext = minp->pnext;
pdhcps_pool->state = DHCPS_STATE_OFFLINE;
free(minp->pnode);
minp->pnode = nullptr;
free(minp);
minp = nullptr;
}
void DhcpServer::dhcps_coarse_tmr(void)
{
uint8 num_dhcps_pool = 0;
list_node* pback_node = nullptr;
list_node* pnode = nullptr;
struct dhcps_pool* pdhcps_pool = nullptr;
pnode = plist;
while (pnode != nullptr)
{
pdhcps_pool = (struct dhcps_pool*)pnode->pnode;
if (pdhcps_pool->type == DHCPS_TYPE_DYNAMIC)
{
pdhcps_pool->lease_timer--;
}
if (pdhcps_pool->lease_timer == 0)
{
pback_node = pnode;
pnode = pback_node->pnext;
node_remove_from_list(&plist, pback_node);
free(pback_node->pnode);
pback_node->pnode = nullptr;
free(pback_node);
pback_node = nullptr;
}
else
{
pnode = pnode->pnext;
num_dhcps_pool++;
}
}
if (num_dhcps_pool >= MAX_STATION_NUM)
{
kill_oldest_dhcps_pool();
}
}
bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg)
{
bool offer_flag = true;
// uint8 option = 0;
if (optarg == nullptr && !isRunning())
{
return false;
}
if (level <= OFFER_START || level >= OFFER_END)
{
return false;
}
switch (level)
{
case OFFER_ROUTER:
offer = (*(uint8*)optarg) & 0x01;
offer_flag = true;
break;
default:
offer_flag = false;
break;
}
return offer_flag;
}
bool DhcpServer::set_dhcps_lease_time(uint32 minute)
{
if (_netif->num == SOFTAP_IF)
{
uint8 opmode = wifi_get_opmode();
if (opmode == STATION_MODE || opmode == NULL_MODE)
{
return false;
}
}
if (isRunning())
{
return false;
}
if (minute == 0)
{
return false;
}
dhcps_lease_time = minute;
return true;
}
bool DhcpServer::reset_dhcps_lease_time(void)
{
if (_netif->num == SOFTAP_IF)
{
uint8 opmode = wifi_get_opmode();
if (opmode == STATION_MODE || opmode == NULL_MODE)
{
return false;
}
}
if (isRunning())
{
return false;
}
dhcps_lease_time = DHCPS_LEASE_TIME_DEF;
return true;
}
uint32 DhcpServer::get_dhcps_lease_time(void) // minute
{
return dhcps_lease_time;
}
void DhcpServer::dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force)
{
struct dhcps_pool* pdhcps_pool = nullptr;
list_node* pback_node = nullptr;
if ((bssid == nullptr) || (ip == nullptr))
{
return;
}
for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext)
{
pdhcps_pool = (struct dhcps_pool*)pback_node->pnode;
if (memcmp(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac)) == 0)
{
if (memcmp(&pdhcps_pool->ip.addr, &ip->addr, sizeof(pdhcps_pool->ip.addr)) == 0)
{
if ((pdhcps_pool->type == DHCPS_TYPE_STATIC) || (force))
{
if (pback_node != nullptr)
{
node_remove_from_list(&plist, pback_node);
free(pback_node);
pback_node = nullptr;
}
if (pdhcps_pool != nullptr)
{
free(pdhcps_pool);
pdhcps_pool = nullptr;
}
}
else
{
pdhcps_pool->state = DHCPS_STATE_OFFLINE;
}
struct ipv4_addr ip_zero;
memset(&ip_zero, 0x0, sizeof(ip_zero));
if (_netif->num == SOFTAP_IF)
{
wifi_softap_set_station_info(bssid, &ip_zero);
}
break;
}
}
}
}
uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip)
{
struct dhcps_pool* pdhcps_pool = nullptr;
list_node* pback_node = nullptr;
list_node* pmac_node = nullptr;
list_node* pip_node = nullptr;
bool flag = false;
uint32 start_ip = dhcps_lease.start_ip.addr;
uint32 end_ip = dhcps_lease.end_ip.addr;
dhcps_type_t type = DHCPS_TYPE_DYNAMIC;
if (bssid == nullptr)
{
return IPADDR_ANY;
}
if (ip)
{
if (IPADDR_BROADCAST == ip->addr)
{
return IPADDR_ANY;
}
else if (IPADDR_ANY == ip->addr)
{
ip = nullptr;
}
else
{
type = DHCPS_TYPE_STATIC;
}
}
renew = false;
for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext)
{
pdhcps_pool = (struct dhcps_pool*)pback_node->pnode;
// os_printf("mac:"MACSTR"bssid:"MACSTR"\r\n",MAC2STR(pdhcps_pool->mac),MAC2STR(bssid));
if (memcmp(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac)) == 0)
{
pmac_node = pback_node;
if (ip == nullptr)
{
flag = true;
break;
}
}
if (ip != nullptr)
{
if (memcmp(&pdhcps_pool->ip.addr, &ip->addr, sizeof(pdhcps_pool->ip.addr)) == 0)
{
pip_node = pback_node;
}
}
else if (flag == false)
{
if (memcmp(&pdhcps_pool->ip.addr, &start_ip, sizeof(pdhcps_pool->ip.addr)) != 0)
{
flag = true;
}
else
{
start_ip = htonl((ntohl(start_ip) + 1));
}
}
}
if ((ip == nullptr) && (flag == false))
{
if (plist == nullptr)
{
if (start_ip <= end_ip)
{
flag = true;
}
else
{
return IPADDR_ANY;
}
}
else
{
if (start_ip > end_ip)
{
return IPADDR_ANY;
}
// start_ip = htonl((ntohl(start_ip) + 1));
flag = true;
}
}
if (pmac_node != nullptr) // update new ip
{
if (pip_node != nullptr)
{
pdhcps_pool = (struct dhcps_pool*)pip_node->pnode;
if (pip_node != pmac_node)
{
if (pdhcps_pool->state != DHCPS_STATE_OFFLINE) // ip is used
{
return IPADDR_ANY;
}
// mac exists and ip exists in other node,delete mac
node_remove_from_list(&plist, pmac_node);
free(pmac_node->pnode);
pmac_node->pnode = nullptr;
free(pmac_node);
pmac_node = pip_node;
memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac));
}
else
{
renew = true;
type = DHCPS_TYPE_DYNAMIC;
}
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
pdhcps_pool->type = type;
pdhcps_pool->state = DHCPS_STATE_ONLINE;
}
else
{
pdhcps_pool = (struct dhcps_pool*)pmac_node->pnode;
if (ip != nullptr)
{
pdhcps_pool->ip.addr = ip->addr;
}
else if (flag == true)
{
pdhcps_pool->ip.addr = start_ip;
}
else // no ip to distribute
{
return IPADDR_ANY;
}
node_remove_from_list(&plist, pmac_node);
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
pdhcps_pool->type = type;
pdhcps_pool->state = DHCPS_STATE_ONLINE;
node_insert_to_list(&plist, pmac_node);
}
}
else // new station
{
if (pip_node != nullptr) // maybe ip has used
{
pdhcps_pool = (struct dhcps_pool*)pip_node->pnode;
if (pdhcps_pool->state != DHCPS_STATE_OFFLINE)
{
return IPADDR_ANY;
}
memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac));
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
pdhcps_pool->type = type;
pdhcps_pool->state = DHCPS_STATE_ONLINE;
}
else
{
pdhcps_pool = (struct dhcps_pool*)zalloc(sizeof(struct dhcps_pool));
if (ip != nullptr)
{
pdhcps_pool->ip.addr = ip->addr;
}
else if (flag == true)
{
pdhcps_pool->ip.addr = start_ip;
}
else // no ip to distribute
{
free(pdhcps_pool);
return IPADDR_ANY;
}
if (pdhcps_pool->ip.addr > end_ip)
{
free(pdhcps_pool);
return IPADDR_ANY;
}
memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac));
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
pdhcps_pool->type = type;
pdhcps_pool->state = DHCPS_STATE_ONLINE;
pback_node = (list_node*)zalloc(sizeof(list_node));
pback_node->pnode = pdhcps_pool;
pback_node->pnext = nullptr;
node_insert_to_list(&plist, pback_node);
}
}
return pdhcps_pool->ip.addr;
}