833 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			833 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * File      : dhcp_server_raw.c
 | |
|  *             A simple DHCP server implementation
 | |
|  * COPYRIGHT (C) 2011-2023, Shanghai Real-Thread Technology Co., Ltd
 | |
|  * http://www.rt-thread.com
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without modification,
 | |
|  * are permitted provided that the following conditions are met:
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright notice,
 | |
|  *    this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright notice,
 | |
|  *    this list of conditions and the following disclaimer in the documentation
 | |
|  *    and/or other materials provided with the distribution.
 | |
|  * 3. The name of the author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | |
|  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
|  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 | |
|  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | |
|  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 | |
|  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 | |
|  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 | |
|  * OF SUCH DAMAGE.
 | |
|  *
 | |
|  * Change Logs:
 | |
|  * Date           Author       Notes
 | |
|  * 2014-04-01     Ren.Haibo    the first version
 | |
|  * 2018-06-12     aozima       ignore DHCP_OPTION_SERVER_ID.
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdint.h>
 | |
| 
 | |
| #include <rtthread.h>
 | |
| 
 | |
| #include <lwip/opt.h>
 | |
| #include <lwip/sockets.h>
 | |
| #include <lwip/inet_chksum.h>
 | |
| #include <netif/etharp.h>
 | |
| #include <netif/ethernetif.h>
 | |
| #include <lwip/ip.h>
 | |
| #include <lwip/init.h>
 | |
| 
 | |
| #if (LWIP_VERSION) < 0x02000000U
 | |
|     #error "not support old LWIP"
 | |
| #endif
 | |
| 
 | |
| #if !LWIP_IPV4
 | |
|     #error "must enable IPV4"
 | |
| #endif
 | |
| 
 | |
| #if (LWIP_VERSION) >= 0x02000000U
 | |
|     #include <lwip/prot/dhcp.h>
 | |
| #endif
 | |
| 
 | |
| /* DHCP server option */
 | |
| 
 | |
| /* allocated client ip range */
 | |
| #ifndef DHCPD_CLIENT_IP_MIN
 | |
|     #define DHCPD_CLIENT_IP_MIN     2
 | |
| #endif
 | |
| #ifndef DHCPD_CLIENT_IP_MAX
 | |
|     #define DHCPD_CLIENT_IP_MAX     254
 | |
| #endif
 | |
| 
 | |
| /* the DHCP server address */
 | |
| #ifndef DHCPD_SERVER_IP
 | |
|     #define DHCPD_SERVER_IP "192.168.169.1"
 | |
| #endif
 | |
| 
 | |
| #define DHCP_DEBUG_PRINTF
 | |
| 
 | |
| #ifdef  DHCP_DEBUG_PRINTF
 | |
|     #define DEBUG_PRINTF        rt_kprintf("[DHCP] "); rt_kprintf
 | |
| #else
 | |
|     #define DEBUG_PRINTF(...)
 | |
| #endif /* DHCP_DEBUG_PRINTF */
 | |
| 
 | |
| /* we need some routines in the DHCP of lwIP */
 | |
| #undef  LWIP_DHCP
 | |
| #define LWIP_DHCP   1
 | |
| #include <lwip/dhcp.h>
 | |
| 
 | |
| /** Mac address length  */
 | |
| #define DHCP_MAX_HLEN               6
 | |
| /** dhcp default live time */
 | |
| #define DHCP_DEFAULT_LIVE_TIME      0x80510100
 | |
| 
 | |
| /** Minimum length for request before packet is parsed */
 | |
| #define DHCP_MIN_REQUEST_LEN        44
 | |
| 
 | |
| #define LWIP_NETIF_LOCK(...)
 | |
| #define LWIP_NETIF_UNLOCK(...)
 | |
| 
 | |
| #ifndef DHCP_SERVER_PORT
 | |
| #define DHCP_SERVER_PORT 67
 | |
| #endif
 | |
| 
 | |
| /**
 | |
| * The dhcp client node struct.
 | |
| */
 | |
| struct dhcp_client_node
 | |
| {
 | |
|     struct dhcp_client_node *next;
 | |
|     u8_t chaddr[DHCP_MAX_HLEN];
 | |
|     ip4_addr_t ipaddr;
 | |
|     u32_t lease_end;
 | |
| };
 | |
| 
 | |
| /**
 | |
| * The dhcp server struct.
 | |
| */
 | |
| struct dhcp_server
 | |
| {
 | |
|     struct dhcp_server *next;
 | |
|     struct netif *netif;
 | |
|     struct udp_pcb *pcb;
 | |
|     struct dhcp_client_node *node_list;
 | |
|     ip4_addr_t start;
 | |
|     ip4_addr_t end;
 | |
|     ip4_addr_t current;
 | |
| };
 | |
| 
 | |
| static u8_t *dhcp_server_option_find(u8_t *buf, u16_t len, u8_t option);
 | |
| 
 | |
| /**
 | |
| * The dhcp server struct list.
 | |
| */
 | |
| static struct dhcp_server *lw_dhcp_server;
 | |
| 
 | |
| /**
 | |
| * Find a dhcp client node by mac address
 | |
| *
 | |
| * @param dhcpserver The dhcp server
 | |
| * @param chaddr Mac address
 | |
| * @param hlen   Mac address length
 | |
| * @return dhcp client node
 | |
| */
 | |
| static struct dhcp_client_node *
 | |
| dhcp_client_find_by_mac(struct dhcp_server *dhcpserver, const u8_t *chaddr, u8_t hlen)
 | |
| {
 | |
|     struct dhcp_client_node *node;
 | |
| 
 | |
|     for (node = dhcpserver->node_list; node != NULL; node = node->next)
 | |
|     {
 | |
|         if (memcmp(node->chaddr, chaddr, hlen) == 0)
 | |
|         {
 | |
|             return node;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
| * Find a dhcp client node by ip address
 | |
| *
 | |
| * @param dhcpserver The dhcp server
 | |
| * @param ip IP address
 | |
| * @return dhcp client node
 | |
| */
 | |
| static struct dhcp_client_node *
 | |
| dhcp_client_find_by_ip(struct dhcp_server *dhcpserver, const ip4_addr_t *ip)
 | |
| {
 | |
|     struct dhcp_client_node *node;
 | |
| 
 | |
|     for (node = dhcpserver->node_list; node != NULL; node = node->next)
 | |
|     {
 | |
|         if (ip4_addr_cmp(&node->ipaddr, ip))
 | |
|         {
 | |
|             return node;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
| * Find a dhcp client node by dhcp message
 | |
| *
 | |
| * @param dhcpserver is the dhcp server
 | |
| * @param msg is the dhcp message
 | |
| * @param opt_buf is the optional buffer
 | |
| * @param len is the buffer length
 | |
| * @return dhcp client node
 | |
| */
 | |
| static struct dhcp_client_node *
 | |
| dhcp_client_find(struct dhcp_server *dhcpserver, struct dhcp_msg *msg,
 | |
|                  u8_t *opt_buf, u16_t len)
 | |
| {
 | |
|     u8_t *opt;
 | |
|     //u32_t ipaddr;
 | |
|     struct dhcp_client_node *node;
 | |
| 
 | |
|     node = dhcp_client_find_by_mac(dhcpserver, msg->chaddr, msg->hlen);
 | |
|     if (node != NULL)
 | |
|     {
 | |
|         return node;
 | |
|     }
 | |
| 
 | |
|     opt = dhcp_server_option_find(opt_buf, len, DHCP_OPTION_REQUESTED_IP);
 | |
|     if (opt != NULL)
 | |
|     {
 | |
|         node = dhcp_client_find_by_ip(dhcpserver, (ip4_addr_t *)(&opt[2]));
 | |
|         if (node != NULL)
 | |
|         {
 | |
|             return node;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
| * Allocate a dhcp client node by dhcp message
 | |
| *
 | |
| * @param dhcpserver is the dhcp server
 | |
| * @param msg is the dhcp message
 | |
| * @param opt_buf is the optional buffer
 | |
| * @param len is the buffer length
 | |
| * @return dhcp client node
 | |
| */
 | |
| static struct dhcp_client_node *
 | |
| dhcp_client_alloc(struct dhcp_server *dhcpserver, struct dhcp_msg *msg,
 | |
|                   u8_t *opt_buf, u16_t len)
 | |
| {
 | |
|     u8_t *opt;
 | |
|     u32_t ipaddr;
 | |
|     struct dhcp_client_node *node;
 | |
| 
 | |
|     node = dhcp_client_find_by_mac(dhcpserver, msg->chaddr, msg->hlen);
 | |
|     if (node != NULL)
 | |
|     {
 | |
|         return node;
 | |
|     }
 | |
| 
 | |
|     opt = dhcp_server_option_find(opt_buf, len, DHCP_OPTION_REQUESTED_IP);
 | |
|     if (opt != NULL)
 | |
|     {
 | |
|         node = dhcp_client_find_by_ip(dhcpserver, (ip4_addr_t *)(&opt[2]));
 | |
|         if (node != NULL)
 | |
|         {
 | |
|             return node;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| dhcp_alloc_again:
 | |
|     node = dhcp_client_find_by_ip(dhcpserver, &dhcpserver->current);
 | |
|     if (node != NULL)
 | |
|     {
 | |
|         ipaddr = (ntohl(dhcpserver->current.addr) + 1);
 | |
|         if (ipaddr > ntohl(dhcpserver->end.addr))
 | |
|         {
 | |
|             ipaddr = ntohl(dhcpserver->start.addr);
 | |
|         }
 | |
|         dhcpserver->current.addr = htonl(ipaddr);
 | |
|         goto dhcp_alloc_again;
 | |
|     }
 | |
|     node = (struct dhcp_client_node *)mem_malloc(sizeof(struct dhcp_client_node));
 | |
|     if (node == NULL)
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
|     SMEMCPY(node->chaddr, msg->chaddr, msg->hlen);
 | |
|     node->ipaddr = dhcpserver->current;
 | |
| 
 | |
|     node->next = dhcpserver->node_list;
 | |
|     dhcpserver->node_list = node;
 | |
| 
 | |
|     return node;
 | |
| }
 | |
| 
 | |
| /**
 | |
| * find option from buffer.
 | |
| *
 | |
| * @param buf The buffer to find option
 | |
| * @param len The buffer length
 | |
| * @param option Which option to find
 | |
| * @return dhcp option buffer
 | |
| */
 | |
| static u8_t *
 | |
| dhcp_server_option_find(u8_t *buf, u16_t len, u8_t option)
 | |
| {
 | |
|     u8_t *end = buf + len;
 | |
|     while ((buf < end) && (*buf != DHCP_OPTION_END))
 | |
|     {
 | |
|         if (*buf == option)
 | |
|         {
 | |
|             return buf;
 | |
|         }
 | |
|         buf += (buf[1] + 2);
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
| * If an incoming DHCP message is in response to us, then trigger the state machine
 | |
| */
 | |
| static void
 | |
| dhcp_server_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *recv_addr, u16_t port)
 | |
| {
 | |
|     struct dhcp_server *dhcp_server = (struct dhcp_server *)arg;
 | |
|     struct dhcp_msg *msg;
 | |
|     struct pbuf *q;
 | |
|     u8_t *opt_buf;
 | |
|     u8_t *opt;
 | |
|     struct dhcp_client_node *node;
 | |
|     u8_t msg_type;
 | |
|     u16_t length;
 | |
|     ip_addr_t addr = *recv_addr;
 | |
|     u32_t tmp;
 | |
| 
 | |
|     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("[%s:%d] %c%c recv %d\n", __FUNCTION__, __LINE__, dhcp_server->netif->name[0], dhcp_server->netif->name[1], p->tot_len));
 | |
|     /* prevent warnings about unused arguments */
 | |
|     LWIP_UNUSED_ARG(pcb);
 | |
|     LWIP_UNUSED_ARG(addr);
 | |
|     LWIP_UNUSED_ARG(port);
 | |
| 
 | |
|     if (p->len < DHCP_MIN_REQUEST_LEN)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP request message or pbuf too short\n"));
 | |
|         pbuf_free(p);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     q = pbuf_alloc(PBUF_TRANSPORT, 1500, PBUF_RAM);
 | |
|     if (q == NULL)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloc dhcp_msg failed!\n"));
 | |
|         pbuf_free(p);
 | |
|         return;
 | |
|     }
 | |
|     if (q->tot_len < p->tot_len)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloc dhcp_msg too small %d:%d\n", q->tot_len, p->tot_len));
 | |
|         pbuf_free(p);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     pbuf_copy(q, p);
 | |
|     pbuf_free(p);
 | |
| 
 | |
|     msg = (struct dhcp_msg *)q->payload;
 | |
|     if (msg->op != DHCP_BOOTREQUEST)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP request message, but type %"U16_F"\n", (u16_t)msg->op));
 | |
|         goto free_pbuf_and_return;
 | |
|     }
 | |
| 
 | |
|     if (msg->cookie != PP_HTONL(DHCP_MAGIC_COOKIE))
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("bad DHCP_MAGIC_COOKIE!\n"));
 | |
|         goto free_pbuf_and_return;
 | |
|     }
 | |
| 
 | |
|     if (msg->hlen > DHCP_MAX_HLEN)
 | |
|     {
 | |
|         goto free_pbuf_and_return;
 | |
|     }
 | |
| 
 | |
|     opt_buf = (u8_t *)msg + DHCP_OPTIONS_OFS;
 | |
|     length = q->tot_len - DHCP_OPTIONS_OFS;
 | |
|     opt = dhcp_server_option_find(opt_buf, length, DHCP_OPTION_MESSAGE_TYPE);
 | |
|     if (opt)
 | |
|     {
 | |
|         msg_type = *(opt + 2);
 | |
|         if (msg_type == DHCP_DISCOVER)
 | |
|         {
 | |
|             node = dhcp_client_alloc(dhcp_server, msg, opt_buf, length);
 | |
|             if (node == NULL)
 | |
|             {
 | |
|                 goto free_pbuf_and_return;
 | |
|             }
 | |
|             node->lease_end = DHCP_DEFAULT_LIVE_TIME;
 | |
|             /* create dhcp offer and send */
 | |
|             msg->op = DHCP_BOOTREPLY;
 | |
|             msg->hops = 0;
 | |
|             msg->secs = 0;
 | |
|             SMEMCPY(&msg->siaddr, &(dhcp_server->netif->ip_addr), 4);
 | |
|             msg->sname[0] = '\0';
 | |
|             msg->file[0] = '\0';
 | |
|             msg->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
 | |
|             SMEMCPY(&msg->yiaddr, &node->ipaddr, 4);
 | |
| 
 | |
|             opt_buf = (u8_t *)msg + DHCP_OPTIONS_OFS;
 | |
|             /* add msg type */
 | |
|             *opt_buf++ = DHCP_OPTION_MESSAGE_TYPE;
 | |
|             *opt_buf++ = 1;
 | |
|             *opt_buf++ = DHCP_OFFER;
 | |
| 
 | |
|             /* add server id */
 | |
|             *opt_buf++ = DHCP_OPTION_SERVER_ID;
 | |
|             *opt_buf++ = 4;
 | |
|             SMEMCPY(opt_buf, &(dhcp_server->netif->ip_addr), 4);
 | |
|             opt_buf += 4;
 | |
| 
 | |
|             /* add_lease_time */
 | |
|             *opt_buf++ = DHCP_OPTION_LEASE_TIME;
 | |
|             *opt_buf++ = 4;
 | |
|             tmp = PP_HTONL(DHCP_DEFAULT_LIVE_TIME);
 | |
|             SMEMCPY(opt_buf, &tmp, 4);
 | |
|             opt_buf += 4;
 | |
| 
 | |
|             /* add config */
 | |
|             *opt_buf++ = DHCP_OPTION_SUBNET_MASK;
 | |
|             *opt_buf++ = 4;
 | |
|             SMEMCPY(opt_buf, &ip_2_ip4(&dhcp_server->netif->netmask)->addr, 4);
 | |
|             opt_buf += 4;
 | |
| 
 | |
|             *opt_buf++ = DHCP_OPTION_DNS_SERVER;
 | |
|             *opt_buf++ = 4;
 | |
| #ifdef DHCP_DNS_SERVER_IP
 | |
|             {
 | |
|                 ip_addr_t dns_addr;
 | |
|                 ipaddr_aton(DHCP_DNS_SERVER_IP, &dns_addr);
 | |
|                 SMEMCPY(opt_buf, &ip_2_ip4(&dns_addr)->addr, 4);
 | |
|             }
 | |
| #else
 | |
|             /* default use gatewary dns server */
 | |
|             SMEMCPY(opt_buf, &(dhcp_server->netif->ip_addr), 4);
 | |
| #endif /* DHCP_DNS_SERVER_IP */
 | |
|             opt_buf += 4;
 | |
| 
 | |
|             *opt_buf++ = DHCP_OPTION_ROUTER;
 | |
|             *opt_buf++ = 4;
 | |
|             SMEMCPY(opt_buf, &ip_2_ip4(&dhcp_server->netif->ip_addr)->addr, 4);
 | |
|             opt_buf += 4;
 | |
| 
 | |
|             /* add option end */
 | |
|             *opt_buf++ = DHCP_OPTION_END;
 | |
| 
 | |
|             length = (u32_t)opt_buf - (u32_t)msg;
 | |
|             if (length < q->tot_len)
 | |
|             {
 | |
|                 pbuf_realloc(q, length);
 | |
|             }
 | |
| 
 | |
|             ip_2_ip4(&addr)->addr = INADDR_BROADCAST;
 | |
|             udp_sendto_if(pcb, q, &addr, port, dhcp_server->netif);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if (1)
 | |
|             {
 | |
|                 if (msg_type == DHCP_REQUEST)
 | |
|                 {
 | |
|                     node = dhcp_client_find(dhcp_server, msg, opt_buf, length);
 | |
|                     if (node != NULL)
 | |
|                     {
 | |
|                         /* Send ack */
 | |
|                         node->lease_end = DHCP_DEFAULT_LIVE_TIME;
 | |
|                         /* create dhcp offer and send */
 | |
|                         msg->op = DHCP_BOOTREPLY;
 | |
|                         msg->hops = 0;
 | |
|                         msg->secs = 0;
 | |
|                         SMEMCPY(&msg->siaddr, &(dhcp_server->netif->ip_addr), 4);
 | |
|                         msg->sname[0] = '\0';
 | |
|                         msg->file[0] = '\0';
 | |
|                         msg->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
 | |
|                         SMEMCPY(&msg->yiaddr, &node->ipaddr, 4);
 | |
|                         opt_buf = (u8_t *)msg + DHCP_OPTIONS_OFS;
 | |
| 
 | |
|                         /* add msg type */
 | |
|                         *opt_buf++ = DHCP_OPTION_MESSAGE_TYPE;
 | |
|                         *opt_buf++ = 1;
 | |
|                         *opt_buf++ = DHCP_ACK;
 | |
| 
 | |
|                         /* add server id */
 | |
|                         *opt_buf++ = DHCP_OPTION_SERVER_ID;
 | |
|                         *opt_buf++ = 4;
 | |
|                         SMEMCPY(opt_buf, &(dhcp_server->netif->ip_addr), 4);
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         /* add_lease_time */
 | |
|                         *opt_buf++ = DHCP_OPTION_LEASE_TIME;
 | |
|                         *opt_buf++ = 4;
 | |
|                         tmp = PP_HTONL(DHCP_DEFAULT_LIVE_TIME);
 | |
|                         SMEMCPY(opt_buf, &tmp, 4);
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         /* add config */
 | |
|                         *opt_buf++ = DHCP_OPTION_SUBNET_MASK;
 | |
|                         *opt_buf++ = 4;
 | |
|                         SMEMCPY(opt_buf, &ip_2_ip4(&dhcp_server->netif->netmask)->addr, 4);
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         *opt_buf++ = DHCP_OPTION_DNS_SERVER;
 | |
|                         *opt_buf++ = 4;
 | |
| #ifdef DHCP_DNS_SERVER_IP
 | |
|                         {
 | |
|                             ip_addr_t dns_addr;
 | |
|                             ipaddr_aton(DHCP_DNS_SERVER_IP, &dns_addr);
 | |
|                             SMEMCPY(opt_buf, &ip_2_ip4(&dns_addr)->addr, 4);
 | |
|                         }
 | |
| #else
 | |
|                         /* default use gatewary dns server */
 | |
|                         SMEMCPY(opt_buf, &(dhcp_server->netif->ip_addr), 4);
 | |
| #endif /* DHCP_DNS_SERVER_IP */
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         *opt_buf++ = DHCP_OPTION_ROUTER;
 | |
|                         *opt_buf++ = 4;
 | |
|                         SMEMCPY(opt_buf, &ip_2_ip4(&dhcp_server->netif->ip_addr)->addr, 4);
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         /* add option end */
 | |
|                         *opt_buf++ = DHCP_OPTION_END;
 | |
| 
 | |
|                         length = (u32_t)opt_buf - (u32_t)msg;
 | |
|                         if (length < q->tot_len)
 | |
|                         {
 | |
|                             pbuf_realloc(q, length);
 | |
|                         }
 | |
| 
 | |
|                         ip_2_ip4(&addr)->addr = INADDR_BROADCAST;
 | |
|                         udp_sendto_if(pcb, q, &addr, port, dhcp_server->netif);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         /* Send no ack */
 | |
|                         /* create dhcp offer and send */
 | |
|                         msg->op = DHCP_BOOTREPLY;
 | |
|                         msg->hops = 0;
 | |
|                         msg->secs = 0;
 | |
|                         SMEMCPY(&msg->siaddr, &(dhcp_server->netif->ip_addr), 4);
 | |
|                         msg->sname[0] = '\0';
 | |
|                         msg->file[0] = '\0';
 | |
|                         msg->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
 | |
|                         memset(&msg->yiaddr, 0, 4);
 | |
|                         opt_buf = (u8_t *)msg + DHCP_OPTIONS_OFS;
 | |
| 
 | |
|                         /* add msg type */
 | |
|                         *opt_buf++ = DHCP_OPTION_MESSAGE_TYPE;
 | |
|                         *opt_buf++ = 1;
 | |
|                         *opt_buf++ = DHCP_NAK;
 | |
| 
 | |
|                         /* add server id */
 | |
|                         *opt_buf++ = DHCP_OPTION_SERVER_ID;
 | |
|                         *opt_buf++ = 4;
 | |
|                         SMEMCPY(opt_buf, &(dhcp_server->netif->ip_addr), 4);
 | |
|                         opt_buf += 4;
 | |
| 
 | |
|                         /* add option end */
 | |
|                         *opt_buf++ = DHCP_OPTION_END;
 | |
|                         length = (u32_t)opt_buf - (u32_t)msg;
 | |
|                         if (length < q->tot_len)
 | |
|                         {
 | |
|                             pbuf_realloc(q, length);
 | |
|                         }
 | |
| 
 | |
|                         ip_2_ip4(&addr)->addr = INADDR_BROADCAST;
 | |
|                         udp_sendto_if(pcb, q, &addr, port, dhcp_server->netif);
 | |
|                     }
 | |
|                 }
 | |
|                 else if (msg_type == DHCP_RELEASE)
 | |
|                 {
 | |
|                     struct dhcp_client_node *node_prev = NULL;
 | |
| 
 | |
|                     for (node = dhcp_server->node_list; node != NULL; node = node->next)
 | |
|                     {
 | |
|                         if (memcmp(node->chaddr, msg->chaddr, msg->hlen) == 0)
 | |
|                         {
 | |
|                             if (node == dhcp_server->node_list)
 | |
|                             {
 | |
|                                 dhcp_server->node_list = node->next;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 node_prev->next = node->next;
 | |
|                             }
 | |
|                             break;
 | |
|                         }
 | |
|                         node_prev = node;
 | |
|                         node = node->next;
 | |
|                     }
 | |
| 
 | |
|                     if (node != NULL)
 | |
|                     {
 | |
|                         mem_free(node);
 | |
|                     }
 | |
|                 }
 | |
|                 else if (msg_type ==  DHCP_DECLINE)
 | |
|                 {
 | |
|                     ;
 | |
|                 }
 | |
|                 else if (msg_type == DHCP_INFORM)
 | |
|                 {
 | |
|                     ;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| free_pbuf_and_return:
 | |
|     pbuf_free(q);
 | |
| }
 | |
| 
 | |
| /**
 | |
| * start dhcp server for a netif
 | |
| *
 | |
| * @param netif The netif which use dhcp server
 | |
| * @param start The Start IP address
 | |
| * @param end The End IP address
 | |
| * @return lwIP error code
 | |
| * - ERR_OK - No error
 | |
| * - ERR_MEM - Out of memory
 | |
| */
 | |
| err_t
 | |
| dhcp_server_start(struct netif *netif, ip4_addr_t *start, ip4_addr_t *end)
 | |
| {
 | |
|     struct dhcp_server *dhcp_server;
 | |
| 
 | |
|     /* If this netif alreday use the dhcp server. */
 | |
|     for (dhcp_server = lw_dhcp_server; dhcp_server != NULL; dhcp_server = dhcp_server->next)
 | |
|     {
 | |
|         if (dhcp_server->netif == netif)
 | |
|         {
 | |
|             dhcp_server->start = *start;
 | |
|             dhcp_server->end = *end;
 | |
|             dhcp_server->current = *start;
 | |
|             return ERR_OK;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     dhcp_server = NULL;
 | |
|     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_server_start(): starting new DHCP server\n"));
 | |
|     dhcp_server = (struct dhcp_server *)mem_malloc(sizeof(struct dhcp_server));
 | |
|     if (dhcp_server == NULL)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_server_start(): could not allocate dhcp\n"));
 | |
|         return ERR_MEM;
 | |
|     }
 | |
| 
 | |
|     /* clear data structure */
 | |
|     memset(dhcp_server, 0, sizeof(struct dhcp_server));
 | |
| 
 | |
|     /* store this dhcp server to list */
 | |
|     dhcp_server->next = lw_dhcp_server;
 | |
|     lw_dhcp_server = dhcp_server;
 | |
|     dhcp_server->netif = netif;
 | |
|     dhcp_server->node_list = NULL;
 | |
|     dhcp_server->start = *start;
 | |
|     dhcp_server->end = *end;
 | |
|     dhcp_server->current = *start;
 | |
| 
 | |
|     /* allocate UDP PCB */
 | |
|     dhcp_server->pcb = udp_new();
 | |
|     if (dhcp_server->pcb == NULL)
 | |
|     {
 | |
|         LWIP_DEBUGF(DHCP_DEBUG  | LWIP_DBG_TRACE, ("dhcp_server_start(): could not obtain pcb\n"));
 | |
|         return ERR_MEM;
 | |
|     }
 | |
| 
 | |
|     ip_set_option(dhcp_server->pcb, SOF_BROADCAST);
 | |
|     /* set up local and remote port for the pcb */
 | |
|     udp_bind(dhcp_server->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
 | |
|     //udp_connect(dhcp_server->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
 | |
|     /* set up the recv callback and argument */
 | |
|     udp_recv(dhcp_server->pcb, dhcp_server_recv, dhcp_server);
 | |
|     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_server_start(): starting DHCP server\n"));
 | |
| 
 | |
|     return ERR_OK;
 | |
| }
 | |
| 
 | |
| extern void set_if(const char *netif_name, const char *ip_addr, const char *gw_addr, const char *nm_addr);
 | |
| 
 | |
| void dhcpd_start(const char *netif_name)
 | |
| {
 | |
|     struct netif *netif = netif_list;
 | |
|     err_t res;
 | |
| 
 | |
|     DEBUG_PRINTF("%s: %s\r\n", __FUNCTION__, netif_name);
 | |
| 
 | |
|     LWIP_NETIF_LOCK();
 | |
|     if (strlen(netif_name) > sizeof(netif->name))
 | |
|     {
 | |
|         DEBUG_PRINTF("network interface name too long!\r\n");
 | |
|         goto _exit;
 | |
|     }
 | |
| 
 | |
|     while (netif != RT_NULL)
 | |
|     {
 | |
|         if (strncmp(netif_name, netif->name, sizeof(netif->name)) == 0)
 | |
|             break;
 | |
| 
 | |
|         netif = netif->next;
 | |
|         if (netif == RT_NULL)
 | |
|         {
 | |
|             DEBUG_PRINTF("network interface: %s not found!\r\n", netif_name);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (netif == RT_NULL)
 | |
|     {
 | |
|         goto _exit;
 | |
|     }
 | |
| 
 | |
|     if (1)
 | |
|     {
 | |
|         dhcp_stop(netif);
 | |
| 
 | |
|         set_if(netif_name, DHCPD_SERVER_IP, "0.0.0.0", "255.255.255.0");
 | |
| 
 | |
|         netif_set_up(netif);
 | |
|     }
 | |
| 
 | |
|     {
 | |
|         char str_tmp[4 * 4 + 4] = DHCPD_SERVER_IP;
 | |
|         char *p = str_tmp;
 | |
|         ip4_addr_t ip_start, ip_end;
 | |
| 
 | |
|         p = strchr(str_tmp, '.');
 | |
|         if (p)
 | |
|         {
 | |
|             p = strchr(p + 1, '.');
 | |
|             if (p)
 | |
|             {
 | |
|                 p = strchr(p + 1, '.');
 | |
|             }
 | |
|         }
 | |
|         if (!p)
 | |
|         {
 | |
|             DEBUG_PRINTF("DHCPD_SERVER_IP: %s error!\r\n", str_tmp);
 | |
|             goto _exit;
 | |
|         }
 | |
|         p = p + 1; /* move to xxx.xxx.xxx.^ */
 | |
| 
 | |
|         sprintf(p, "%d", DHCPD_CLIENT_IP_MIN);
 | |
|         ip4addr_aton(str_tmp, &ip_start);
 | |
|         DEBUG_PRINTF("ip_start: [%s]\r\n", str_tmp);
 | |
|         sprintf(p, "%d", DHCPD_CLIENT_IP_MAX);
 | |
|         ip4addr_aton(str_tmp, &ip_end);
 | |
|         DEBUG_PRINTF("ip_start: [%s]\r\n", str_tmp);
 | |
| 
 | |
|         res = dhcp_server_start(netif, &ip_start, &ip_end);
 | |
|         if (res != 0)
 | |
|         {
 | |
|             DEBUG_PRINTF("dhcp_server_start res: %s.\r\n", res);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| _exit:
 | |
|     LWIP_NETIF_UNLOCK();
 | |
|     return;
 | |
| }
 | |
| 
 | |
| void dhcpd_stop(const char *netif_name)
 | |
| {
 | |
|     struct dhcp_server *dhcp_server, *server_node;
 | |
|     struct netif *netif = netif_list;
 | |
|     struct dhcp_client_node *node, *next;
 | |
| 
 | |
|     DEBUG_PRINTF("%s: %s\r\n", __FUNCTION__, netif_name);
 | |
| 
 | |
|     LWIP_NETIF_LOCK();
 | |
|     if (strlen(netif_name) > sizeof(netif->name))
 | |
|     {
 | |
|         DEBUG_PRINTF("network interface name too long!\r\n");
 | |
|         goto _exit;
 | |
|     }
 | |
| 
 | |
|     while (netif != RT_NULL)
 | |
|     {
 | |
|         if (strncmp(netif_name, netif->name, sizeof(netif->name)) == 0)
 | |
|             break;
 | |
| 
 | |
|         netif = netif->next;
 | |
|         if (netif == RT_NULL)
 | |
|         {
 | |
|             DEBUG_PRINTF("network interface: %s not found!\r\n", netif_name);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (netif == RT_NULL)
 | |
|     {
 | |
|         goto _exit;
 | |
|     }
 | |
| 
 | |
|     /* If this netif alreday use the dhcp server. */
 | |
|     for (dhcp_server = lw_dhcp_server; dhcp_server != NULL; dhcp_server = dhcp_server->next)
 | |
|     {
 | |
|         if (dhcp_server->netif == netif)
 | |
|         {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     if (dhcp_server == RT_NULL)
 | |
|     {
 | |
|         goto _exit;
 | |
|     }
 | |
| 
 | |
|     /* remove dhcp server */
 | |
|     if (dhcp_server == lw_dhcp_server)
 | |
|     {
 | |
|         lw_dhcp_server = lw_dhcp_server->next;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         server_node = lw_dhcp_server;
 | |
|         while (server_node->next && server_node->next != dhcp_server)
 | |
|         {
 | |
|             server_node = server_node->next;
 | |
|         }
 | |
|         if (server_node->next != RT_NULL)
 | |
|         {
 | |
|             server_node->next = server_node->next->next;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     udp_disconnect(dhcp_server->pcb);
 | |
|     udp_remove(dhcp_server->pcb);
 | |
| 
 | |
|     /* remove all client node */
 | |
|     for (node = dhcp_server->node_list; node != NULL; node = next)
 | |
|     {
 | |
|         next = node->next;
 | |
|         mem_free(node);
 | |
|     }
 | |
| 
 | |
|     mem_free(dhcp_server);
 | |
|     set_if(netif_name, "0.0.0.0", "0.0.0.0", "0.0.0.0");
 | |
| 
 | |
| _exit:
 | |
|     LWIP_NETIF_UNLOCK();
 | |
| }
 |