You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1433 lines
41 KiB
1433 lines
41 KiB
/**************************************************************************** |
|
* netutils/dhcpd/dhcpd.c |
|
* |
|
* Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <gnutt@nuttx.org> |
|
* |
|
* 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 NuttX 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 COPYRIGHT HOLDERS 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 |
|
* COPYRIGHT OWNER 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. |
|
* |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Included Files |
|
****************************************************************************/ |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_HOST |
|
# include <stdio.h> |
|
|
|
# define HTONS(a) htons(a) |
|
# define HTONL(a) htonl(a) |
|
|
|
# define CONFIG_CPP_HAVE_WARNING 1 |
|
# define FAR |
|
|
|
# define ndbg(...) printf(__VA_ARGS__) |
|
# define nvdbg(...) printf(__VA_ARGS__) |
|
|
|
# define ERROR (-1) |
|
# define OK (0) |
|
#else |
|
# include <nuttx/config.h> /* NuttX configuration */ |
|
# include <debug.h> /* For ndbg, vdbg */ |
|
# include <nuttx/compiler.h> /* For CONFIG_CPP_HAVE_WARNING */ |
|
# include <arch/irq.h> /* For irqstore() and friends -- REVISIT */ |
|
# include <nuttx/net/uip/uip-arp.h> /* For low-level ARP interfaces -- REVISIT */ |
|
# include <apps/netutils/dhcpd.h> /* Advertised DHCPD APIs */ |
|
#endif |
|
|
|
#include <sys/socket.h> |
|
#include <sys/ioctl.h> |
|
|
|
#include <stdint.h> |
|
#include <stdbool.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <time.h> |
|
#include <errno.h> |
|
|
|
#include <net/if.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
|
|
/**************************************************************************** |
|
* Private Data |
|
****************************************************************************/ |
|
|
|
#define DHCP_SERVER_PORT 67 |
|
#define DHCP_CLIENT_PORT 68 |
|
|
|
/* Option codes understood in this file */ |
|
/* Code Data Description */ |
|
/* Length */ |
|
#define DHCP_OPTION_PAD 1 /* 1 Pad */ |
|
#define DHCP_OPTION_REQ_IPADDR 50 /* 4 Requested IP Address */ |
|
#define DHCP_OPTION_LEASE_TIME 51 /* 4 IP address lease time */ |
|
#define DHCP_OPTION_OVERLOAD 52 /* 1 Option overload */ |
|
#define DHCP_OPTION_MSG_TYPE 53 /* 1 DHCP message type */ |
|
#define DHCP_OPTION_SERVER_ID 54 /* 4 Server identifier */ |
|
#define DHCP_OPTION_END 255 /* 0 End */ |
|
|
|
/* Values for the dhcp msg 'op' field */ |
|
|
|
#define DHCP_REQUEST 1 |
|
#define DHCP_REPLY 2 |
|
|
|
/* DHCP message types understood in this file */ |
|
|
|
#define DHCPDISCOVER 1 /* Received from client only */ |
|
#define DHCPOFFER 2 /* Sent from server only */ |
|
#define DHCPREQUEST 3 /* Received from client only */ |
|
#define DHCPDECLINE 4 /* Received from client only */ |
|
#define DHCPACK 5 /* Sent from server only */ |
|
#define DHCPNAK 6 /* Sent from server only */ |
|
#define DHCPRELEASE 7 /* Received from client only */ |
|
#define DHCPINFORM 8 /* Not used */ |
|
|
|
/* The form of an option is: |
|
* code - 1 byte |
|
* length - 1 byte |
|
* data - variable number of bytes |
|
*/ |
|
|
|
#define DHCPD_OPTION_CODE 0 |
|
#define DHCPD_OPTION_LENGTH 1 |
|
#define DHCPD_OPTION_DATA 2 |
|
|
|
/* Size of options in DHCP message */ |
|
|
|
#define DHCPD_OPTIONS_SIZE 312 |
|
|
|
/* Values for htype and hlen field */ |
|
|
|
#define DHCP_HTYPE_ETHERNET 1 |
|
#define DHCP_HLEN_ETHERNET 6 |
|
|
|
/* Values for flags field */ |
|
|
|
#define BOOTP_BROADCAST 0x8000 |
|
|
|
/* Legal values for this option are: |
|
* |
|
* 1 the 'file' field is used to hold options |
|
* 2 the 'sname' field is used to hold options |
|
* 3 both fields are used to hold options |
|
*/ |
|
|
|
#define DHCPD_OPTION_FIELD 0 |
|
#define DHCPD_FILE_FIELD 1 |
|
#define DHCPD_SNAME_FIELD 2 |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_LEASETIME |
|
# define CONFIG_NETUTILS_DHCPD_LEASETIME (60*60*24*10) /* 10 days */ |
|
# undef CONFIG_NETUTILS_DHCPD_MINLEASETIME |
|
# undef CONFIG_NETUTILS_DHCPD_MAXLEASETIME |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_MINLEASETIME |
|
# define CONFIG_NETUTILS_DHCPD_MINLEASETIME (60*60*24*1) /* 1 days */ |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_MAXLEASETIME |
|
# define CONFIG_NETUTILS_DHCPD_MAXLEASETIME (60*60*24*30) /* 30 days */ |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_INTERFACE |
|
# define CONFIG_NETUTILS_DHCPD_INTERFACE "eth0" |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_MAXLEASES |
|
# define CONFIG_NETUTILS_DHCPD_MAXLEASES 16 |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_STARTIP |
|
# define CONFIG_NETUTILS_DHCPD_STARTIP (10L<<24|0L<<16|0L<<16|2L) |
|
#endif |
|
|
|
#undef CONFIG_NETUTILS_DHCP_OPTION_ENDIP |
|
#define CONFIG_NETUTILS_DHCP_OPTION_ENDIP \ |
|
(CONFIG_NETUTILS_DHCPD_STARTIP + CONFIG_NETUTILS_DHCPD_MAXLEASES - 1) |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_OFFERTIME |
|
# define CONFIG_NETUTILS_DHCPD_OFFERTIME (60*60) /* 1 hour */ |
|
#endif |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_DECLINETIME |
|
# define CONFIG_NETUTILS_DHCPD_DECLINETIME (60*60) /* 1 hour */ |
|
#endif |
|
|
|
#undef HAVE_LEASE_TIME |
|
#if defined(CONFIG_NETUTILS_DHCPD_HOST) || !defined(CONFIG_DISABLE_POSIX_TIMERS) |
|
# define HAVE_LEASE_TIME 1 |
|
#endif |
|
|
|
/**************************************************************************** |
|
* Private Types |
|
****************************************************************************/ |
|
|
|
/* This structure describes one element in the lease table. There is one slot |
|
* in the lease table for each assign-able IP address (hence, the IP address |
|
* itself does not have to be in the table. |
|
*/ |
|
|
|
struct lease_s |
|
{ |
|
uint8_t mac[DHCP_HLEN_ETHERNET]; /* MAC address (network order) -- could be larger! */ |
|
bool allocated; /* true: IP address is allocated */ |
|
#ifdef HAVE_LEASE_TIME |
|
time_t expiry; /* Lease expiration time (seconds past Epoch) */ |
|
#endif |
|
}; |
|
|
|
struct dhcpmsg_s |
|
{ |
|
uint8_t op; |
|
uint8_t htype; |
|
uint8_t hlen; |
|
uint8_t hops; |
|
uint8_t xid[4]; |
|
uint16_t secs; |
|
uint16_t flags; |
|
uint8_t ciaddr[4]; |
|
uint8_t yiaddr[4]; |
|
uint8_t siaddr[4]; |
|
uint8_t giaddr[4]; |
|
uint8_t chaddr[16]; |
|
#ifndef CONFIG_NET_DHCP_LIGHT |
|
uint8_t sname[64]; |
|
uint8_t file[128]; |
|
#endif |
|
uint8_t options[312]; |
|
}; |
|
|
|
struct dhcpd_state_s |
|
{ |
|
/* Server configuration */ |
|
|
|
in_addr_t ds_serverip; /* The server IP address */ |
|
|
|
/* Message buffers */ |
|
|
|
struct dhcpmsg_s ds_inpacket; /* Holds the incoming DHCP client message */ |
|
struct dhcpmsg_s ds_outpacket; /* Holds the outgoing DHCP server message */ |
|
|
|
/* Parsed options from the incoming DHCP client message */ |
|
|
|
uint8_t ds_optmsgtype; /* Incoming DHCP message type */ |
|
in_addr_t ds_optreqip; /* Requested IP address (host order) */ |
|
in_addr_t ds_optserverip; /* Serverip IP address (host order) */ |
|
time_t ds_optleasetime; /* Requested lease time (host order) */ |
|
|
|
/* End option pointer for outgoing DHCP server message */ |
|
|
|
uint8_t *ds_optend; |
|
|
|
/* Leases */ |
|
|
|
struct lease_s ds_leases[CONFIG_NETUTILS_DHCPD_MAXLEASES]; |
|
}; |
|
|
|
/**************************************************************************** |
|
* Private Data |
|
****************************************************************************/ |
|
|
|
static const uint8_t g_magiccookie[4] = {99, 130, 83, 99}; |
|
static const uint8_t g_anyipaddr[4] = {0, 0, 0, 0}; |
|
static struct dhcpd_state_s g_state; |
|
|
|
/**************************************************************************** |
|
* Private Functions |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_arpupdate |
|
****************************************************************************/ |
|
|
|
#ifndef CONFIG_NETUTILS_DHCPD_HOST |
|
static inline void dhcpd_arpupdate(uint16_t *pipaddr, uint8_t *phwaddr) |
|
{ |
|
uip_lock_t flags; |
|
|
|
/* Disable interrupts and update the ARP table -- very non-portable hack. |
|
* REVISIT -- switch to the SIOCSARP ioctl call if/when it is implemented. |
|
*/ |
|
|
|
flags = uip_lock(); |
|
uip_arp_update(pipaddr, phwaddr); |
|
uip_unlock(flags); |
|
} |
|
#else |
|
# define dhcpd_arpupdate(pipaddr,phwaddr) |
|
#endif |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_time |
|
****************************************************************************/ |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_HOST |
|
# define dhcpd_time() time(0) |
|
#elif defined(HAVE_LEASE_TIME) |
|
static time_t dhcpd_time(void) |
|
{ |
|
struct timespec ts; |
|
time_t ret = 0; |
|
|
|
if (clock_gettime(CLOCK_REALTIME, &ts) == OK) |
|
{ |
|
ret = ts.tv_sec; |
|
} |
|
return ret; |
|
} |
|
#else |
|
# define dhcpd_time() (0) |
|
#endif |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_leaseexpired |
|
****************************************************************************/ |
|
|
|
#ifdef HAVE_LEASE_TIME |
|
static inline bool dhcpd_leaseexpired(struct lease_s *lease) |
|
{ |
|
if (lease->expiry < dhcpd_time()) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
memset(lease, 0, sizeof(struct lease_s)); |
|
return true; |
|
} |
|
} |
|
#else |
|
# define dhcpd_leaseexpired(lease) (false) |
|
#endif |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_setlease |
|
****************************************************************************/ |
|
|
|
struct lease_s *dhcpd_setlease(const uint8_t *mac, in_addr_t ipaddr, time_t expiry) |
|
{ |
|
/* Calculate the offset from the first IP address managed by DHCPD. |
|
* ipaddr must be in host order! |
|
*/ |
|
|
|
int ndx = ipaddr - CONFIG_NETUTILS_DHCPD_STARTIP; |
|
struct lease_s *ret = NULL; |
|
|
|
nvdbg("ipaddr: %08x ipaddr: %08x ndx: %d MAX: %d\n", |
|
ipaddr, CONFIG_NETUTILS_DHCPD_STARTIP, ndx, |
|
CONFIG_NETUTILS_DHCPD_MAXLEASES); |
|
|
|
/* Verify that the address offset is within the supported range */ |
|
|
|
if (ndx >= 0 && ndx < CONFIG_NETUTILS_DHCPD_MAXLEASES) |
|
{ |
|
ret = &g_state.ds_leases[ndx]; |
|
memcpy(ret->mac, mac, DHCP_HLEN_ETHERNET); |
|
ret->allocated = true; |
|
#ifdef HAVE_LEASE_TIME |
|
ret->expiry = dhcpd_time() + expiry; |
|
#endif |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcp_leaseipaddr |
|
****************************************************************************/ |
|
|
|
static inline in_addr_t dhcp_leaseipaddr( struct lease_s *lease) |
|
{ |
|
/* Return IP address in host order */ |
|
|
|
return (g_state.ds_leases - lease)/sizeof(struct lease_s) + CONFIG_NETUTILS_DHCPD_STARTIP; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_findbymac |
|
****************************************************************************/ |
|
|
|
static struct lease_s *dhcpd_findbymac(const uint8_t *mac) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < CONFIG_NETUTILS_DHCPD_MAXLEASES; i++) |
|
{ |
|
if (memcmp(g_state.ds_leases[i].mac, mac, DHCP_HLEN_ETHERNET) == 0) |
|
{ |
|
return &(g_state.ds_leases[i]); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_findbyipaddr |
|
****************************************************************************/ |
|
|
|
static struct lease_s *dhcpd_findbyipaddr(in_addr_t ipaddr) |
|
{ |
|
if (ipaddr >= CONFIG_NETUTILS_DHCPD_STARTIP && |
|
ipaddr <= CONFIG_NETUTILS_DHCP_OPTION_ENDIP) |
|
{ |
|
struct lease_s *lease = &g_state.ds_leases[ipaddr - CONFIG_NETUTILS_DHCPD_STARTIP]; |
|
if (lease->allocated > 0) |
|
{ |
|
return lease; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_allocipaddr |
|
****************************************************************************/ |
|
|
|
in_addr_t dhcpd_allocipaddr(void) |
|
{ |
|
struct lease_s *lease = NULL; |
|
in_addr_t ipaddr; |
|
|
|
ipaddr = CONFIG_NETUTILS_DHCPD_STARTIP; |
|
for (; ipaddr <= CONFIG_NETUTILS_DHCP_OPTION_ENDIP; ipaddr++) |
|
{ |
|
if ((ipaddr & 0xff) == 0 || (ipaddr & 0xff) == 0xff) |
|
{ |
|
continue; |
|
} |
|
|
|
lease = dhcpd_findbyipaddr(ipaddr); |
|
if ((!lease || dhcpd_leaseexpired(lease))) |
|
{ |
|
#ifdef CONFIG_CPP_HAVE_WARNING |
|
# warning "FIXME: Should check if anything responds to an ARP request or ping" |
|
# warning " to verify that there is no other user of this IP address" |
|
#endif |
|
memset(g_state.ds_leases[ipaddr - CONFIG_NETUTILS_DHCPD_STARTIP].mac, 0, DHCP_HLEN_ETHERNET); |
|
g_state.ds_leases[ipaddr - CONFIG_NETUTILS_DHCPD_STARTIP].allocated = true; |
|
#ifdef HAVE_LEASE_TIME |
|
g_state.ds_leases[ipaddr - CONFIG_NETUTILS_DHCPD_STARTIP].expiry = dhcpd_time() + CONFIG_NETUTILS_DHCPD_OFFERTIME; |
|
#endif |
|
return ntohl(ipaddr); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_parseoptions |
|
****************************************************************************/ |
|
|
|
static inline bool dhcpd_parseoptions(void) |
|
{ |
|
uint32_t tmp; |
|
uint8_t *ptr; |
|
uint8_t overloaded; |
|
uint8_t currfield; |
|
int optlen = 0; |
|
int remaining; |
|
|
|
/* Verify that the option field starts with a valid magic number */ |
|
|
|
ptr = g_state.ds_inpacket.options; |
|
if (memcmp(ptr, g_magiccookie, 4) != 0) |
|
{ |
|
/* Bad magic number... skip g_state.ds_outpacket */ |
|
|
|
ndbg("Bad magic: %d,%d,%d,%d\n", ptr[0], ptr[1], ptr[2], ptr[3]); |
|
return false; |
|
} |
|
|
|
/* Set up to parse the options */ |
|
|
|
ptr += 4; |
|
remaining = DHCPD_OPTIONS_SIZE - 4; |
|
overloaded = DHCPD_OPTION_FIELD; |
|
currfield = DHCPD_OPTION_FIELD; |
|
|
|
/* Set all options to the default value */ |
|
|
|
g_state.ds_optmsgtype = 0; /* Incoming DHCP message type */ |
|
g_state.ds_optreqip = 0; /* Requested IP address (host order) */ |
|
g_state.ds_optserverip = 0; /* Serverip IP address (host order) */ |
|
g_state.ds_optleasetime = 0; /* Requested lease time (host order) */ |
|
g_state.ds_optend = NULL; |
|
|
|
do |
|
{ |
|
/* The form of an option is: |
|
* code - 1 byte |
|
* length - 1 byte |
|
* data - variable number of bytes |
|
*/ |
|
|
|
switch (ptr[DHCPD_OPTION_CODE]) |
|
{ |
|
/* Skip over any padding bytes */ |
|
|
|
case DHCP_OPTION_PAD: |
|
optlen = 1; |
|
break; |
|
|
|
/* the Overload option is used to indicate that the DHCP 'sname' or 'file' |
|
* fields are being overloaded by using them to carry DHCP options. A DHCP |
|
* server inserts this option if the returned parameters will exceed the |
|
* usual space allotted for options. |
|
* |
|
* If this option is present, the client interprets the specified additional |
|
* fields after it concludes interpretation of the standard option fields. |
|
* |
|
* Legal values for this option are: |
|
* |
|
* 1 the 'file' field is used to hold options |
|
* 2 the 'sname' field is used to hold options |
|
* 3 both fields are used to hold options |
|
*/ |
|
|
|
#ifndef CONFIG_NET_DHCP_LIGHT |
|
case DHCP_OPTION_OVERLOAD: |
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
if (optlen >= 1 && optlen < remaining) |
|
{ |
|
overloaded = ptr[DHCPD_OPTION_DATA]; |
|
} |
|
break; |
|
#endif |
|
|
|
case DHCP_OPTION_END: |
|
#ifndef CONFIG_NET_DHCP_LIGHT |
|
if (currfield == DHCPD_OPTION_FIELD && |
|
(overloaded & DHCPD_FILE_FIELD) != 0) |
|
{ |
|
ptr = g_state.ds_inpacket.file; |
|
remaining = sizeof(g_state.ds_inpacket.file); |
|
currfield = DHCPD_FILE_FIELD; |
|
} |
|
else if (currfield == DHCPD_FILE_FIELD && |
|
(overloaded & DHCPD_SNAME_FIELD) != 0) |
|
{ |
|
ptr = g_state.ds_inpacket.sname; |
|
remaining = sizeof(g_state.ds_inpacket.sname); |
|
currfield = DHCPD_SNAME_FIELD; |
|
} |
|
else |
|
{ |
|
return true; |
|
} |
|
break; |
|
#else |
|
return true; |
|
#endif |
|
|
|
case DHCP_OPTION_REQ_IPADDR: /* Requested IP Address */ |
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
if (optlen >= 4 && optlen < remaining) |
|
{ |
|
memcpy(&tmp, &ptr[DHCPD_OPTION_DATA], 4); |
|
g_state.ds_optreqip = (in_addr_t)ntohl(tmp); |
|
} |
|
break; |
|
|
|
case DHCP_OPTION_LEASE_TIME: /* IP address lease time */ |
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
if (optlen >= 4 && optlen < remaining) |
|
{ |
|
memcpy(&tmp, &ptr[DHCPD_OPTION_DATA], 4); |
|
g_state.ds_optleasetime = (time_t)ntohl(tmp); |
|
} |
|
break; |
|
|
|
case DHCP_OPTION_MSG_TYPE: /* DHCP message type */ |
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
if (optlen >= 1 && optlen < remaining) |
|
{ |
|
g_state.ds_optmsgtype = ptr[DHCPD_OPTION_DATA]; |
|
} |
|
break; |
|
|
|
case DHCP_OPTION_SERVER_ID: /* Server identifier */ |
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
if (optlen >= 4 && optlen < remaining) |
|
{ |
|
memcpy(&tmp, &ptr[DHCPD_OPTION_DATA], 4); |
|
g_state.ds_optserverip = (in_addr_t)ntohl(tmp); |
|
} |
|
break; |
|
|
|
default: |
|
/* Skip over unsupported options */ |
|
|
|
optlen = ptr[DHCPD_OPTION_LENGTH] + 2; |
|
break; |
|
} |
|
|
|
/* Advance to the next option */ |
|
|
|
ptr += optlen; |
|
remaining -= optlen; |
|
} |
|
while (remaining > 0); |
|
return false; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_verifyreqip |
|
****************************************************************************/ |
|
|
|
static inline bool dhcpd_verifyreqip(void) |
|
{ |
|
struct lease_s *lease; |
|
|
|
/* Verify that the requested IP address is within the supported lease range */ |
|
|
|
if (g_state.ds_optreqip > 0 && |
|
g_state.ds_optreqip >= CONFIG_NETUTILS_DHCPD_STARTIP && |
|
g_state.ds_optreqip <= CONFIG_NETUTILS_DHCP_OPTION_ENDIP) |
|
{ |
|
/* And verify that the lease has not already been taken or offered |
|
* (unless the lease/offer is expired, then the address is free game). |
|
*/ |
|
|
|
lease = dhcpd_findbyipaddr(g_state.ds_optreqip); |
|
if (!lease || dhcpd_leaseexpired(lease)) |
|
{ |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_verifyreqleasetime |
|
****************************************************************************/ |
|
|
|
static inline bool dhcpd_verifyreqleasetime(uint32_t *leasetime) |
|
{ |
|
uint32_t tmp = g_state.ds_optleasetime; |
|
|
|
/* Did the client request a specific lease time? */ |
|
|
|
if (tmp != 0) |
|
{ |
|
/* Yes.. Verify that the requested lease time is within a valid range */ |
|
|
|
if (tmp > CONFIG_NETUTILS_DHCPD_MAXLEASETIME) |
|
{ |
|
tmp = CONFIG_NETUTILS_DHCPD_MAXLEASETIME; |
|
} |
|
else if (tmp < CONFIG_NETUTILS_DHCPD_MINLEASETIME) |
|
{ |
|
tmp = CONFIG_NETUTILS_DHCPD_MINLEASETIME; |
|
} |
|
|
|
/* Return the clipped lease time */ |
|
|
|
*leasetime = tmp; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_addoption |
|
****************************************************************************/ |
|
|
|
static int dhcpd_addoption(uint8_t *option) |
|
{ |
|
int offset; |
|
int len = 4; |
|
|
|
if (g_state.ds_optend) |
|
{ |
|
offset = g_state.ds_outpacket.options - g_state.ds_optend; |
|
len = option[DHCPD_OPTION_LENGTH] + 2; |
|
|
|
/* Check if the option will fit into the options array */ |
|
|
|
if (offset + len + 1 < DHCPD_OPTIONS_SIZE) |
|
{ |
|
/* Copy the option into the option array */ |
|
|
|
memcpy(g_state.ds_optend, option, len); |
|
g_state.ds_optend += len; |
|
*g_state.ds_optend = DHCP_OPTION_END; |
|
} |
|
} |
|
return len; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_addoption8 |
|
****************************************************************************/ |
|
|
|
static int dhcpd_addoption8(uint8_t code, uint8_t value) |
|
{ |
|
uint8_t option[3]; |
|
|
|
/* Construct the option sequence */ |
|
|
|
option[DHCPD_OPTION_CODE] = code; |
|
option[DHCPD_OPTION_LENGTH] = 1; |
|
option[DHCPD_OPTION_DATA] = value; |
|
|
|
/* Add the option sequence to the response */ |
|
|
|
return dhcpd_addoption(option); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_addoption32 |
|
****************************************************************************/ |
|
|
|
static int dhcpd_addoption32(uint8_t code, uint32_t value) |
|
{ |
|
uint8_t option[6]; |
|
|
|
/* Construct the option sequence */ |
|
|
|
option[DHCPD_OPTION_CODE] = code; |
|
option[DHCPD_OPTION_LENGTH] = 4; |
|
memcpy(&option[DHCPD_OPTION_DATA], &value, 4); |
|
|
|
/* Add the option sequence to the response */ |
|
|
|
return dhcpd_addoption(option); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_soclet |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_socket(void) |
|
{ |
|
int sockfd; |
|
#if defined(HAVE_SO_REUSEADDR) || defined(HAVE_SO_BROADCAST) |
|
int optval; |
|
int ret; |
|
#endif |
|
|
|
/* Create a socket to listen for requests from DHCP clients */ |
|
|
|
sockfd = socket(PF_INET, SOCK_DGRAM, 0); |
|
if (sockfd < 0) |
|
{ |
|
ndbg("socket failed: %d\n", errno); |
|
return ERROR; |
|
} |
|
|
|
/* Configure the socket */ |
|
|
|
#ifdef HAVE_SO_REUSEADDR |
|
optval = 1; |
|
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(int)); |
|
if (ret < 0) |
|
{ |
|
ndbg("setsockopt SO_REUSEADDR failed: %d\n", errno); |
|
close(sockfd); |
|
return ERROR; |
|
} |
|
#endif |
|
|
|
#ifdef HAVE_SO_BROADCAST |
|
optval = 1; |
|
ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof(int)); |
|
if (ret < 0) |
|
{ |
|
ndbg("setsockopt SO_BROADCAST failed: %d\n", errno); |
|
close(sockfd); |
|
return ERROR; |
|
} |
|
#endif |
|
|
|
return sockfd; |
|
|
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_openresponder |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_openresponder(void) |
|
{ |
|
struct sockaddr_in addr; |
|
int sockfd; |
|
int ret; |
|
|
|
nvdbg("Responder: %08lx\n", ntohl(g_state.ds_serverip)); |
|
|
|
/* Create a socket to listen for requests from DHCP clients */ |
|
|
|
sockfd = dhcpd_socket(); |
|
if (sockfd < 0) |
|
{ |
|
ndbg("socket failed: %d\n", errno); |
|
return ERROR; |
|
} |
|
|
|
/* Bind the socket to a local port.*/ |
|
|
|
addr.sin_family = AF_INET; |
|
addr.sin_port = 0; |
|
addr.sin_addr.s_addr = g_state.ds_serverip; |
|
|
|
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); |
|
if (ret < 0) |
|
{ |
|
ndbg("bind failed, port=%d addr=%08lx: %d\n", |
|
addr.sin_port, (long)addr.sin_addr.s_addr, errno); |
|
close(sockfd); |
|
return ERROR; |
|
} |
|
|
|
return sockfd; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_initpacket |
|
****************************************************************************/ |
|
|
|
static void dhcpd_initpacket(uint8_t mtype) |
|
{ |
|
uint32_t nulladdr = 0; |
|
|
|
/* Set up the generic parts of the DHCP server message */ |
|
|
|
memset(&g_state.ds_outpacket, 0, sizeof(struct dhcpmsg_s)); |
|
|
|
g_state.ds_outpacket.op = DHCP_REPLY; |
|
g_state.ds_outpacket.htype = g_state.ds_inpacket.htype; |
|
g_state.ds_outpacket.hlen = g_state.ds_inpacket.hlen; |
|
|
|
memcpy(&g_state.ds_outpacket.xid, &g_state.ds_inpacket.xid, 4); |
|
memcpy(g_state.ds_outpacket.chaddr, g_state.ds_inpacket.chaddr, 16); |
|
|
|
if (memcmp(g_state.ds_outpacket.giaddr, &nulladdr, 4) != 0) |
|
{ |
|
g_state.ds_outpacket.flags = g_state.ds_inpacket.flags; |
|
} |
|
else |
|
{ |
|
g_state.ds_outpacket.flags = 0; |
|
} |
|
memset(g_state.ds_outpacket.giaddr, 0, 4); |
|
|
|
/* Add the generic options */ |
|
|
|
memcpy(g_state.ds_outpacket.options, g_magiccookie, 4); |
|
g_state.ds_optend = &g_state.ds_outpacket.options[4]; |
|
*g_state.ds_optend = DHCP_OPTION_END; |
|
dhcpd_addoption8(DHCP_OPTION_MSG_TYPE, mtype); |
|
dhcpd_addoption32(DHCP_OPTION_SERVER_ID, g_state.ds_serverip); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_sendpacket |
|
****************************************************************************/ |
|
|
|
static int dhcpd_sendpacket(int bbroadcast) |
|
{ |
|
struct sockaddr_in addr; |
|
in_addr_t ipaddr; |
|
int sockfd; |
|
int len; |
|
int ret = ERROR; |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_IGNOREBROADCAST |
|
/* This is a hack. I've had problems with Windows machines responding |
|
* to unicast. I think this is associated with a Windows registry key in |
|
* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DHCPServer\Parameters: |
|
* The IgnoreBroadcastFlag value controls this behavior: A value of 1 will |
|
* cause the server to ignore the client broadcast flag and always respond |
|
* with multicast; the value 0 to allows clients to request unicast. |
|
*/ |
|
|
|
ipaddr = INADDR_BROADCAST; |
|
#else |
|
/* Determine which address to respond to (or if we need to broadcast the response) |
|
* |
|
* (1) If he caller know that it needs to multicast the response, it will set bbroadcast. |
|
* (2) Otherwise, if the client already has and address (ciaddr), then use that for uni-cast |
|
* (3) Broadcast if the client says it can't handle uni-cast (BOOTP_BROADCAST set) |
|
* (4) Otherwise, the client claims it can handle the uni-casst response and we |
|
* will uni-cast to the offered address (yiaddr). |
|
* |
|
* NOTE: We really should also check the giaddr field. If no zero, the server should |
|
* send any return messages to the 'DHCP server' port on the BOOTP relay agent whose |
|
* address appears in 'giaddr'. |
|
*/ |
|
|
|
if (bbroadcast) |
|
{ |
|
ipaddr = INADDR_BROADCAST; |
|
} |
|
else if (memcmp(g_state.ds_outpacket.ciaddr, g_anyipaddr, 4) != 0) |
|
{ |
|
dhcpd_arpupdate((uint16_t*)g_state.ds_outpacket.ciaddr, g_state.ds_outpacket.chaddr); |
|
memcpy(&ipaddr, g_state.ds_outpacket.ciaddr, 4); |
|
} |
|
else if (g_state.ds_outpacket.flags & HTONS(BOOTP_BROADCAST)) |
|
{ |
|
ipaddr = INADDR_BROADCAST; |
|
} |
|
else |
|
{ |
|
dhcpd_arpupdate((uint16_t*)g_state.ds_outpacket.yiaddr, g_state.ds_outpacket.chaddr); |
|
memcpy(&ipaddr, g_state.ds_outpacket.yiaddr, 4); |
|
} |
|
#endif |
|
|
|
/* Create a socket to respond with a packet to the client. We |
|
* cannot re-use the listener socket because it is not bound correctly |
|
*/ |
|
|
|
sockfd = dhcpd_openresponder(); |
|
if (sockfd >= 0) |
|
{ |
|
/* Then send the reponse to the DHCP client port at that address */ |
|
|
|
memset(&addr, 0, sizeof(struct sockaddr_in)); |
|
addr.sin_family = AF_INET; |
|
addr.sin_port = HTONS(DHCP_CLIENT_PORT); |
|
addr.sin_addr.s_addr = ipaddr; |
|
|
|
/* Send the minimum sized packet that includes the END option */ |
|
|
|
len = (g_state.ds_optend - (uint8_t*)&g_state.ds_outpacket) + 1; |
|
nvdbg("sendto %08lx:%04x len=%d\n", |
|
(long)ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port), len); |
|
|
|
ret = sendto(sockfd, &g_state.ds_outpacket, len, 0, |
|
(struct sockaddr *)&addr, sizeof(struct sockaddr_in)); |
|
close(sockfd); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_sendoffer |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_sendoffer(in_addr_t ipaddr, uint32_t leasetime) |
|
{ |
|
in_addr_t netaddr; |
|
|
|
/* IP address is in host order */ |
|
|
|
nvdbg("Sending offer: %08lx\n", (long)ipaddr); |
|
|
|
/* Initialize the outgoing packet */ |
|
|
|
dhcpd_initpacket(DHCPOFFER); |
|
|
|
/* Add the address offered to the client (converting to network order) */ |
|
|
|
netaddr = htonl(ipaddr); |
|
memcpy(g_state.ds_outpacket.yiaddr, &netaddr, 4); |
|
|
|
/* Add the leasetime to the response options */ |
|
|
|
dhcpd_addoption32(DHCP_OPTION_LEASE_TIME, htonl(leasetime)); |
|
|
|
/* Send the offer response */ |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_IGNOREBROADCAST |
|
return dhcpd_sendpacket(true); |
|
#else |
|
return dhcpd_sendpacket(false); |
|
#endif |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_sendnak |
|
****************************************************************************/ |
|
|
|
static int dhcpd_sendnak(void) |
|
{ |
|
/* Initialize and send the NAK response */ |
|
|
|
dhcpd_initpacket(DHCPNAK); |
|
memcpy(g_state.ds_outpacket.ciaddr, g_state.ds_inpacket.ciaddr, 4); |
|
return dhcpd_sendpacket(true); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_sendack |
|
****************************************************************************/ |
|
|
|
int dhcpd_sendack(in_addr_t ipaddr) |
|
{ |
|
uint32_t leasetime = CONFIG_NETUTILS_DHCPD_LEASETIME; |
|
in_addr_t netaddr; |
|
|
|
/* Initialize the ACK response */ |
|
|
|
dhcpd_initpacket(DHCPACK); |
|
memcpy(g_state.ds_outpacket.ciaddr, g_state.ds_inpacket.ciaddr, 4); |
|
|
|
/* Add the IP address assigned to the client */ |
|
|
|
netaddr = htonl(ipaddr); |
|
memcpy(g_state.ds_outpacket.yiaddr, &netaddr, 4); |
|
|
|
/* Did the client request a specific lease time? */ |
|
|
|
(void)dhcpd_verifyreqleasetime(&leasetime); |
|
|
|
/* Add the lease time to the response */ |
|
|
|
dhcpd_addoption32(DHCP_OPTION_LEASE_TIME, htonl(leasetime)); |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_IGNOREBROADCAST |
|
if (dhcpd_sendpacket(true) < 0) |
|
#else |
|
if (dhcpd_sendpacket(false) < 0) |
|
#endif |
|
{ |
|
return ERROR; |
|
} |
|
|
|
dhcpd_setlease(g_state.ds_inpacket.chaddr, ipaddr, leasetime); |
|
return OK; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_discover |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_discover(void) |
|
{ |
|
struct lease_s *lease; |
|
in_addr_t ipaddr; |
|
uint32_t leasetime = CONFIG_NETUTILS_DHCPD_LEASETIME; |
|
|
|
/* Check if the client is aleady in the lease table */ |
|
|
|
lease = dhcpd_findbymac(g_state.ds_inpacket.chaddr); |
|
if (lease) |
|
{ |
|
/* Yes... get the remaining time on the lease */ |
|
|
|
#ifdef HAVE_LEASE_TIME |
|
if (!dhcpd_leaseexpired(lease)) |
|
{ |
|
leasetime = lease->expiry - dhcpd_time(); |
|
if (leasetime < CONFIG_NETUTILS_DHCPD_MINLEASETIME) |
|
{ |
|
leasetime = CONFIG_NETUTILS_DHCPD_MINLEASETIME; |
|
} |
|
} |
|
#endif |
|
/* Get the IP address associated with the lease (host order) */ |
|
|
|
ipaddr = dhcp_leaseipaddr(lease); |
|
nvdbg("Already have lease for IP %08lx\n", (long)ipaddr); |
|
} |
|
|
|
/* Check if the client has requested a specific IP address */ |
|
|
|
else if (dhcpd_verifyreqip()) |
|
{ |
|
/* Use the requested IP address (host order) */ |
|
|
|
ipaddr = g_state.ds_optreqip; |
|
nvdbg("User requested IP %08lx\n", (long)ipaddr); |
|
} |
|
else |
|
{ |
|
/* No... allocate a new IP address (host order)*/ |
|
|
|
ipaddr = dhcpd_allocipaddr(); |
|
nvdbg("Allocated IP %08lx\n", (long)ipaddr); |
|
} |
|
|
|
/* Did we get any IP address? */ |
|
|
|
if (!ipaddr) |
|
{ |
|
/* Nope... return failure */ |
|
|
|
ndbg("Failed to get IP address\n"); |
|
return ERROR; |
|
} |
|
|
|
/* Reserve the leased IP for a shorter time for the offer */ |
|
|
|
if (!dhcpd_setlease(g_state.ds_inpacket.chaddr, ipaddr, CONFIG_NETUTILS_DHCPD_OFFERTIME)) |
|
{ |
|
ndbg("Failed to set lease\n"); |
|
return ERROR; |
|
} |
|
|
|
/* Check if the client has requested a specific lease time */ |
|
|
|
(void)dhcpd_verifyreqleasetime(&leasetime); |
|
|
|
/* Send the offer response */ |
|
|
|
return dhcpd_sendoffer(ipaddr, leasetime); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_request |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_request(void) |
|
{ |
|
struct lease_s *lease; |
|
in_addr_t ipaddr = 0; |
|
uint8_t response = 0; |
|
|
|
/* Check if this client already holds a lease. This can happen when the client (1) |
|
* the IP is reserved for the client from a previous offer, or (2) the client is |
|
* re-initializing or rebooting while the lease is still valid. |
|
*/ |
|
|
|
lease = dhcpd_findbymac(g_state.ds_inpacket.chaddr); |
|
if (lease) |
|
{ |
|
/* Yes.. the client already holds a lease. Verify that the request is consistent |
|
* with the existing lease (host order). |
|
*/ |
|
|
|
ipaddr = dhcp_leaseipaddr(lease); |
|
nvdbg("Lease ipaddr: %08x Server IP: %08x Requested IP: %08x\n", |
|
ipaddr, g_state.ds_optserverip, g_state.ds_optreqip); |
|
|
|
if (g_state.ds_optserverip) |
|
{ |
|
/* ACK if the serverip is correct and the requested IP address is the one |
|
* already offered to the client. |
|
*/ |
|
|
|
if (g_state.ds_optserverip == ntohl(g_state.ds_serverip) && |
|
(g_state.ds_optreqip != 0 || g_state.ds_optreqip == ipaddr)) |
|
{ |
|
response = DHCPACK; |
|
} |
|
else |
|
{ |
|
response = DHCPNAK; |
|
} |
|
} |
|
|
|
/* We have the lease and no server IP was requested. Was a specific IP address |
|
* requested? (host order) |
|
*/ |
|
|
|
else if (g_state.ds_optreqip) |
|
{ |
|
/* Yes..ACK if the requested IP address is the one already leased. |
|
* Both addresses are in host order. |
|
*/ |
|
|
|
if (ipaddr == g_state.ds_optreqip) |
|
{ |
|
response = DHCPACK; |
|
} |
|
else |
|
{ |
|
response = DHCPNAK; |
|
} |
|
} |
|
|
|
/* The client has specified neither a server IP nor requested IP address */ |
|
|
|
else |
|
{ |
|
/* ACK if the IP used by the client is the one already assigned to it. |
|
* NOTE ipaddr is in host order; ciaddr is network order! |
|
*/ |
|
|
|
uint32_t tmp = htonl(ipaddr); |
|
if (memcmp(&tmp, g_state.ds_inpacket.ciaddr, 4) == 0) |
|
{ |
|
response = DHCPACK; |
|
} |
|
else |
|
{ |
|
response = DHCPNAK; |
|
} |
|
} |
|
} |
|
|
|
/* The client does not hold a lease (referenced by its MAC address) and is |
|
* requesting a specific IP address that was, apparently, never offered to |
|
* to the client. Perform some sanity checks before sending the NAK. |
|
*/ |
|
|
|
else if (g_state.ds_optreqip && !g_state.ds_optserverip) |
|
{ |
|
nvdbg("Server IP: %08x Requested IP: %08x\n", |
|
g_state.ds_optserverip, g_state.ds_optreqip); |
|
|
|
/* Is this IP address already assigned? */ |
|
|
|
lease = dhcpd_findbyipaddr(g_state.ds_optreqip); |
|
if (lease) |
|
{ |
|
/* Yes.. Send NAK unless the lease has expired */ |
|
|
|
if (!dhcpd_leaseexpired(lease)) |
|
{ |
|
response = DHCPNAK; |
|
} |
|
} |
|
|
|
/* No.. is the requested IP address in range? NAK if not */ |
|
|
|
else if (g_state.ds_optreqip < CONFIG_NETUTILS_DHCPD_STARTIP || |
|
g_state.ds_optreqip > CONFIG_NETUTILS_DHCP_OPTION_ENDIP) |
|
{ |
|
response = DHCPNAK; |
|
} |
|
} |
|
|
|
/* Otherwise, the client does not hold a lease and is not requesting any |
|
* specific IP address. |
|
*/ |
|
|
|
/* Finally, either (1) send the ACK, (2) send a NAK, or (3) remain silent |
|
* based on the checks above. |
|
*/ |
|
|
|
if (response == DHCPACK) |
|
{ |
|
nvdbg("ACK IP %08lx\n", (long)ipaddr); |
|
dhcpd_sendack(ipaddr); |
|
} |
|
else if (response == DHCPNAK) |
|
{ |
|
nvdbg("NAK IP %08lx\n", (long)ipaddr); |
|
dhcpd_sendnak(); |
|
} |
|
else |
|
{ |
|
nvdbg("Remaining silent IP %08lx\n", (long)ipaddr); |
|
} |
|
|
|
return OK; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_decline |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_decline(void) |
|
{ |
|
struct lease_s *lease; |
|
|
|
/* Find the lease associated with this hardware address */ |
|
|
|
lease = dhcpd_findbymac(g_state.ds_inpacket.chaddr); |
|
if (lease) |
|
{ |
|
/* Disassociate the IP from the MAC, but prevent re-used of this |
|
* address for a period of time. |
|
*/ |
|
|
|
memset(lease->mac, 0, DHCP_HLEN_ETHERNET); |
|
#ifdef HAVE_LEASE_TIME |
|
lease->expiry = dhcpd_time() + CONFIG_NETUTILS_DHCPD_DECLINETIME; |
|
#endif |
|
} |
|
return OK; |
|
} |
|
|
|
static inline int dhcpd_release(void) |
|
{ |
|
struct lease_s *lease; |
|
|
|
/* Find the lease associated with this hardware address */ |
|
|
|
lease = dhcpd_findbymac(g_state.ds_inpacket.chaddr); |
|
if (lease) |
|
{ |
|
/* Release the IP address now */ |
|
|
|
memset(lease, 0, sizeof(struct lease_s)); |
|
} |
|
return OK; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_openlistener |
|
****************************************************************************/ |
|
|
|
static inline int dhcpd_openlistener(void) |
|
{ |
|
struct sockaddr_in addr; |
|
struct ifreq req; |
|
int sockfd; |
|
int ret; |
|
|
|
/* Create a socket to listen for requests from DHCP clients */ |
|
|
|
sockfd = dhcpd_socket(); |
|
if (sockfd < 0) |
|
{ |
|
ndbg("socket failed: %d\n", errno); |
|
return ERROR; |
|
} |
|
|
|
/* Get the IP address of the selected device */ |
|
|
|
strncpy(req.ifr_name, CONFIG_NETUTILS_DHCPD_INTERFACE, IFNAMSIZ); |
|
ret = ioctl(sockfd, SIOCGIFADDR, (unsigned long)&req); |
|
if (ret < 0) |
|
{ |
|
ndbg("setsockopt SIOCGIFADDR failed: %d\n", errno); |
|
close(sockfd); |
|
return ERROR; |
|
} |
|
g_state.ds_serverip = ((struct sockaddr_in*)&req.ifr_addr)->sin_addr.s_addr; |
|
nvdbg("serverip: %08lx\n", ntohl(g_state.ds_serverip)); |
|
|
|
/* Bind the socket to a local port. We have to bind to INADDRY_ANY to |
|
* receive broadcast messages. |
|
*/ |
|
|
|
addr.sin_family = AF_INET; |
|
addr.sin_port = htons(DHCP_SERVER_PORT); |
|
addr.sin_addr.s_addr = INADDR_ANY; |
|
|
|
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); |
|
if (ret < 0) |
|
{ |
|
ndbg("bind failed, port=%d addr=%08lx: %d\n", |
|
addr.sin_port, (long)addr.sin_addr.s_addr, errno); |
|
close(sockfd); |
|
return ERROR; |
|
} |
|
|
|
return sockfd; |
|
} |
|
|
|
/**************************************************************************** |
|
* Public Functions |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Name: dhcpd_run |
|
****************************************************************************/ |
|
|
|
int dhcpd_run(void) |
|
{ |
|
int sockfd; |
|
int nbytes; |
|
|
|
nvdbg("Started\n"); |
|
|
|
/* Initialize everything to zero */ |
|
|
|
memset(&g_state, 0, sizeof(struct dhcpd_state_s)); |
|
|
|
/* Now loop indefinitely, reading packets from the DHCP server socket */ |
|
|
|
sockfd = -1; |
|
for (;;) |
|
{ |
|
/* Create a socket to listen for requests from DHCP clients */ |
|
|
|
if (sockfd < 0) |
|
{ |
|
sockfd = dhcpd_openlistener(); |
|
if (sockfd < 0) |
|
{ |
|
ndbg("Failed to create socket\n"); |
|
break; |
|
} |
|
} |
|
|
|
/* Read the next g_state.ds_outpacket */ |
|
|
|
nbytes = recv(sockfd, &g_state.ds_inpacket, sizeof(struct dhcpmsg_s), 0); |
|
if (nbytes < 0) |
|
{ |
|
/* On errors (other EINTR), close the socket and try again */ |
|
|
|
ndbg("recv failed: %d\n", errno); |
|
if (errno != EINTR) |
|
{ |
|
close(sockfd); |
|
sockfd = -1; |
|
} |
|
continue; |
|
} |
|
|
|
/* Parse the incoming message options */ |
|
|
|
if (!dhcpd_parseoptions()) |
|
{ |
|
/* Failed to parse the message options */ |
|
|
|
ndbg("No msg type\n"); |
|
continue; |
|
} |
|
|
|
#ifdef CONFIG_NETUTILS_DHCPD_HOST |
|
/* Get the poor little uC a change to get its recvfrom in place */ |
|
|
|
usleep(500*1000); |
|
#endif |
|
|
|
/* Now process the incoming DHCP message by its message type */ |
|
|
|
switch (g_state.ds_optmsgtype) |
|
{ |
|
case DHCPDISCOVER: |
|
nvdbg("DHCPDISCOVER\n"); |
|
dhcpd_discover(); |
|
break; |
|
|
|
case DHCPREQUEST: |
|
nvdbg("DHCPREQUEST\n"); |
|
dhcpd_request(); |
|
break; |
|
|
|
case DHCPDECLINE: |
|
nvdbg("DHCPDECLINE\n"); |
|
dhcpd_decline(); |
|
break; |
|
|
|
case DHCPRELEASE: |
|
nvdbg("DHCPRELEASE\n"); |
|
dhcpd_release(); |
|
break; |
|
|
|
case DHCPINFORM: /* Not supported */ |
|
default: |
|
ndbg("Unsupported message type: %d\n", g_state.ds_optmsgtype); |
|
break; |
|
} |
|
} |
|
return OK; |
|
}
|
|
|