2009-07-11 16:37:11 +02:00
|
|
|
/* Adapted by Simon Berg from net/dhcpc.c */
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2005, Swedish Institute of Computer Science
|
|
|
|
* 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. Neither the name of the Institute nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE OR CONTRIBUTORS 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.
|
|
|
|
*
|
|
|
|
* This file is part of the Contiki operating system.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <uip_arp.h>
|
|
|
|
#include "contiki.h"
|
|
|
|
#include "contiki-net.h"
|
|
|
|
#include "dhcps.h"
|
|
|
|
|
|
|
|
struct dhcp_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];
|
|
|
|
#ifndef UIP_CONF_DHCP_LIGHT
|
|
|
|
uint8_t sname[64];
|
|
|
|
uint8_t file[128];
|
|
|
|
#endif
|
|
|
|
uint8_t options[312];
|
|
|
|
} CC_BYTE_ALIGNED;
|
|
|
|
|
|
|
|
#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 DHCPINFORM 8
|
|
|
|
|
|
|
|
#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_REQ_LIST 55
|
|
|
|
#define DHCP_OPTION_END 255
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LEASE_FLAGS_ALLOCATED 0x01 /* Lease with an allocated address*/
|
|
|
|
#define LEASE_FLAGS_VALID 0x02 /* Contains a valid but
|
|
|
|
possibly outdated lease */
|
|
|
|
|
|
|
|
|
|
|
|
static const struct dhcps_config *config;
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t *
|
|
|
|
find_option(uint8_t option)
|
|
|
|
{
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
uint8_t *optptr = &m->options[4];
|
|
|
|
uint8_t *end = (uint8_t*)uip_appdata + uip_datalen();
|
|
|
|
while(optptr < end && *optptr != DHCP_OPTION_END) {
|
|
|
|
if(*optptr == option) {
|
|
|
|
return optptr;
|
|
|
|
}
|
|
|
|
optptr += optptr[1] + 2;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_cookie(void)
|
|
|
|
{
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
return memcmp(m->options, magic_cookie, 4) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finds any valid lease for a given MAC address */
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
lookup_lease_mac(const uint8_t *chaddr, uint8_t hlen)
|
|
|
|
{
|
|
|
|
struct dhcps_client_lease *lease = config->leases;
|
|
|
|
struct dhcps_client_lease *end = config->leases + config->num_leases;
|
|
|
|
while(lease != end) {
|
|
|
|
if (lease->flags & LEASE_FLAGS_VALID
|
|
|
|
&& memcmp(lease->chaddr, chaddr, hlen) == 0) {
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
lease++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
lookup_lease_ip(const uip_ipaddr_t *ip)
|
|
|
|
{
|
|
|
|
struct dhcps_client_lease *lease = config->leases;
|
|
|
|
struct dhcps_client_lease *end = config->leases + config->num_leases;
|
|
|
|
while(lease != end) {
|
|
|
|
if (uip_ipaddr_cmp(&lease->ipaddr, ip)) {
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
lease++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
find_free_lease(void)
|
|
|
|
{
|
|
|
|
struct dhcps_client_lease *found = NULL;
|
|
|
|
struct dhcps_client_lease *lease = config->leases;
|
|
|
|
struct dhcps_client_lease *end = config->leases + config->num_leases;
|
|
|
|
while(lease != end) {
|
|
|
|
if (!(lease->flags & LEASE_FLAGS_VALID)) return lease;
|
|
|
|
if (!(lease->flags & LEASE_FLAGS_ALLOCATED)) found = lease;
|
|
|
|
lease++;
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dhcps_client_lease *
|
|
|
|
init_lease(struct dhcps_client_lease *lease,
|
|
|
|
const uint8_t *chaddr, uint8_t hlen)
|
|
|
|
{
|
|
|
|
if (lease) {
|
|
|
|
memcpy(lease->chaddr, chaddr, hlen);
|
|
|
|
lease->flags = LEASE_FLAGS_VALID;
|
|
|
|
}
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
choose_address()
|
|
|
|
{
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
struct dhcps_client_lease *lease;
|
|
|
|
lease = lookup_lease_mac(m->chaddr, m->hlen);
|
|
|
|
if (lease) {
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uint8_t *opt;
|
|
|
|
opt = find_option(DHCP_OPTION_REQ_IPADDR);
|
|
|
|
if (opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
|
|
|
|
&& !(lease->flags & LEASE_FLAGS_ALLOCATED)) {
|
|
|
|
return init_lease(lease, m->chaddr,m->hlen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lease = find_free_lease();
|
|
|
|
if (lease) {
|
|
|
|
return init_lease(lease, m->chaddr,m->hlen);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
allocate_address()
|
|
|
|
{
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
struct dhcps_client_lease *lease;
|
|
|
|
lease = lookup_lease_mac(m->chaddr, m->hlen);
|
|
|
|
if (!lease) {
|
|
|
|
uint8_t *opt;
|
|
|
|
opt = find_option(DHCP_OPTION_REQ_IPADDR);
|
|
|
|
if (!(opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2]))
|
|
|
|
&& !(lease->flags & LEASE_FLAGS_ALLOCATED))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lease->lease_end = clock_seconds()+config->default_lease_time;
|
|
|
|
lease->flags |= LEASE_FLAGS_ALLOCATED;
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dhcps_client_lease *
|
|
|
|
release_address()
|
|
|
|
{
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
struct dhcps_client_lease *lease;
|
|
|
|
lease = lookup_lease_mac(m->chaddr, m->hlen);
|
|
|
|
if (!lease) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
lease->flags &= ~LEASE_FLAGS_ALLOCATED;
|
|
|
|
return lease;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static uint8_t *
|
|
|
|
add_msg_type(uint8_t *optptr, uint8_t type)
|
|
|
|
{
|
|
|
|
*optptr++ = DHCP_OPTION_MSG_TYPE;
|
|
|
|
*optptr++ = 1;
|
|
|
|
*optptr++ = type;
|
|
|
|
return optptr;
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static uint8_t *
|
|
|
|
add_server_id(uint8_t *optptr)
|
|
|
|
{
|
|
|
|
*optptr++ = DHCP_OPTION_SERVER_ID;
|
|
|
|
*optptr++ = 4;
|
|
|
|
memcpy(optptr, &uip_hostaddr, 4);
|
|
|
|
return optptr + 4;
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static uint8_t *
|
|
|
|
add_lease_time(uint8_t *optptr)
|
|
|
|
{
|
|
|
|
uint32_t lt;
|
|
|
|
*optptr++ = DHCP_OPTION_LEASE_TIME;
|
|
|
|
*optptr++ = 4;
|
2010-10-19 20:29:03 +02:00
|
|
|
lt = UIP_HTONL(config->default_lease_time);
|
2009-07-11 16:37:11 +02:00
|
|
|
memcpy(optptr, <, 4);
|
|
|
|
return optptr + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static uint8_t *
|
|
|
|
add_end(uint8_t *optptr)
|
|
|
|
{
|
|
|
|
*optptr++ = DHCP_OPTION_END;
|
|
|
|
return optptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t *
|
|
|
|
add_config(uint8_t *optptr)
|
|
|
|
{
|
|
|
|
if (config->flags & DHCP_CONF_NETMASK) {
|
|
|
|
*optptr++ = DHCP_OPTION_SUBNET_MASK;
|
|
|
|
*optptr++ = 4;
|
|
|
|
memcpy(optptr, &config->netmask, 4);
|
|
|
|
optptr += 4;
|
|
|
|
}
|
|
|
|
if (config->flags & DHCP_CONF_DNSADDR) {
|
|
|
|
*optptr++ = DHCP_OPTION_DNS_SERVER;
|
|
|
|
*optptr++ = 4;
|
|
|
|
memcpy(optptr, &config->dnsaddr, 4);
|
|
|
|
optptr += 4;
|
|
|
|
}
|
|
|
|
if (config->flags & DHCP_CONF_DEFAULT_ROUTER) {
|
|
|
|
*optptr++ = DHCP_OPTION_ROUTER;
|
|
|
|
*optptr++ = 4;
|
|
|
|
memcpy(optptr, &config->default_router, 4);
|
|
|
|
optptr += 4;
|
|
|
|
}
|
|
|
|
return optptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
create_msg(CC_REGISTER_ARG struct dhcp_msg *m)
|
|
|
|
{
|
|
|
|
m->op = DHCP_REPLY;
|
|
|
|
/* m->htype = DHCP_HTYPE_ETHERNET; */
|
|
|
|
/* m->hlen = DHCP_HLEN_ETHERNET; */
|
|
|
|
/* memcpy(m->chaddr, &uip_ethaddr,DHCP_HLEN_ETHERNET); */
|
|
|
|
m->hops = 0;
|
|
|
|
m->secs = 0;
|
|
|
|
memcpy(m->siaddr, &uip_hostaddr, 4);
|
|
|
|
m->sname[0] = '\0';
|
|
|
|
m->file[0] = '\0';
|
|
|
|
memcpy(m->options, magic_cookie, sizeof(magic_cookie));
|
|
|
|
}
|
|
|
|
|
|
|
|
static uip_ipaddr_t any_addr;
|
|
|
|
static uip_ipaddr_t bcast_addr;
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
|
|
send_offer(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
|
|
|
|
{
|
2012-02-20 19:42:51 +01:00
|
|
|
uint8_t *end;
|
2009-07-11 16:37:11 +02:00
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
|
|
|
|
create_msg(m);
|
|
|
|
memcpy(&m->yiaddr, &lease->ipaddr,4);
|
|
|
|
|
|
|
|
end = add_msg_type(&m->options[4], DHCPOFFER);
|
|
|
|
end = add_server_id(end);
|
|
|
|
end = add_lease_time(end);
|
|
|
|
end = add_config(end);
|
|
|
|
end = add_end(end);
|
|
|
|
uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
|
2012-02-20 19:42:51 +01:00
|
|
|
uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
|
2009-07-11 16:37:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
send_ack(struct uip_udp_conn *conn, struct dhcps_client_lease *lease)
|
|
|
|
{
|
2012-02-20 19:42:51 +01:00
|
|
|
uint8_t *end;
|
2009-07-11 16:37:11 +02:00
|
|
|
uip_ipaddr_t ciaddr;
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
|
|
|
|
create_msg(m);
|
|
|
|
memcpy(&m->yiaddr, &lease->ipaddr,4);
|
|
|
|
|
|
|
|
end = add_msg_type(&m->options[4], DHCPACK);
|
|
|
|
end = add_server_id(end);
|
|
|
|
end = add_lease_time(end);
|
|
|
|
end = add_config(end);
|
|
|
|
end = add_end(end);
|
|
|
|
memcpy(&ciaddr, &lease->ipaddr,4);
|
|
|
|
uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
|
2012-02-20 19:42:51 +01:00
|
|
|
uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
|
2009-07-11 16:37:11 +02:00
|
|
|
printf("ACK\n");
|
|
|
|
}
|
|
|
|
static void
|
|
|
|
send_nack(struct uip_udp_conn *conn)
|
|
|
|
{
|
2012-02-20 19:42:51 +01:00
|
|
|
uint8_t *end;
|
2009-07-11 16:37:11 +02:00
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
|
|
|
|
create_msg(m);
|
|
|
|
memset(&m->yiaddr, 0, 4);
|
|
|
|
|
|
|
|
end = add_msg_type(&m->options[4], DHCPNAK);
|
|
|
|
end = add_server_id(end);
|
|
|
|
end = add_end(end);
|
|
|
|
|
|
|
|
uip_ipaddr_copy(&conn->ripaddr, &bcast_addr);
|
2012-02-20 19:42:51 +01:00
|
|
|
uip_send(uip_appdata, (int)(end - (uint8_t *)uip_appdata));
|
2009-07-11 16:37:11 +02:00
|
|
|
printf("NACK\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
PROCESS(dhcp_server_process, "DHCP server");
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
PROCESS_THREAD(dhcp_server_process, ev , data)
|
|
|
|
{
|
|
|
|
static struct uip_udp_conn *conn;
|
|
|
|
static struct uip_udp_conn *send_conn;
|
|
|
|
static struct dhcps_client_lease *lease;
|
|
|
|
PROCESS_BEGIN();
|
|
|
|
printf("DHCP server starting\n");
|
|
|
|
uip_ipaddr(&any_addr, 0,0,0,0);
|
|
|
|
uip_ipaddr(&bcast_addr, 255,255,255,255);
|
2010-10-19 20:29:03 +02:00
|
|
|
conn = udp_new(&any_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
|
2009-07-11 16:37:11 +02:00
|
|
|
if (!conn) goto exit;
|
2010-10-19 20:29:03 +02:00
|
|
|
send_conn = udp_new(&bcast_addr, UIP_HTONS(DHCPS_CLIENT_PORT), NULL);
|
2009-07-11 16:37:11 +02:00
|
|
|
if (!send_conn) goto exit;
|
|
|
|
|
2010-10-19 20:29:03 +02:00
|
|
|
uip_udp_bind(conn, UIP_HTONS(DHCPS_SERVER_PORT));
|
|
|
|
uip_udp_bind(send_conn, UIP_HTONS(DHCPS_SERVER_PORT));
|
2009-07-11 16:37:11 +02:00
|
|
|
while(1) {
|
|
|
|
PROCESS_WAIT_EVENT();
|
|
|
|
if(ev == tcpip_event) {
|
|
|
|
if (uip_newdata()) {
|
|
|
|
struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata;
|
|
|
|
struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN];
|
|
|
|
|
|
|
|
if (m->op == DHCP_REQUEST && check_cookie() && m->hlen <= MAX_HLEN) {
|
|
|
|
uint8_t *opt = find_option(DHCP_OPTION_MSG_TYPE);
|
|
|
|
if (opt) {
|
|
|
|
uint8_t mtype = opt[2];
|
|
|
|
if (opt[2] == DHCPDISCOVER) {
|
|
|
|
printf("Discover\n");
|
|
|
|
lease = choose_address();
|
|
|
|
if (lease) {
|
|
|
|
lease->lease_end = clock_seconds()+config->default_lease_time;
|
|
|
|
tcpip_poll_udp(send_conn);
|
|
|
|
PROCESS_WAIT_EVENT_UNTIL(uip_poll());
|
|
|
|
send_offer(conn,lease);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint8_t *opt = find_option(DHCP_OPTION_SERVER_ID);
|
|
|
|
if (!opt || uip_ipaddr_cmp((uip_ipaddr_t*)&opt[2], &uip_hostaddr)) {
|
|
|
|
if (mtype == DHCPREQUEST) {
|
|
|
|
printf("Request\n");
|
|
|
|
lease = allocate_address();
|
|
|
|
tcpip_poll_udp(send_conn);
|
|
|
|
PROCESS_WAIT_EVENT_UNTIL(uip_poll());
|
|
|
|
if (!lease) {
|
|
|
|
send_nack(send_conn);
|
|
|
|
} else {
|
|
|
|
send_ack(send_conn,lease);
|
|
|
|
}
|
|
|
|
} else if (mtype == DHCPRELEASE) {
|
|
|
|
printf("Release\n");
|
|
|
|
release_address();
|
|
|
|
} else if (mtype == DHCPDECLINE) {
|
|
|
|
printf("Decline\n");
|
|
|
|
} else if (mtype == DHCPINFORM) {
|
|
|
|
printf("Inform\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (uip_poll()) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
printf("DHCP server exiting\n");
|
|
|
|
PROCESS_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcps_init(const struct dhcps_config *conf)
|
|
|
|
{
|
|
|
|
config = conf;
|
|
|
|
process_start(&dhcp_server_process,NULL);
|
|
|
|
}
|