/*==========================================================================
|
/*==========================================================================
|
//
|
//
|
// dhcp_prot.c
|
// dhcp_prot.c
|
//
|
//
|
// DHCP protocol implementation for DHCP client
|
// DHCP protocol implementation for DHCP client
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): hmt
|
// Author(s): hmt
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2000-07-01
|
// Date: 2000-07-01
|
// Purpose: DHCP support
|
// Purpose: DHCP support
|
// Description:
|
// Description:
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//========================================================================*/
|
//========================================================================*/
|
|
|
#include <pkgconf/system.h>
|
#include <pkgconf/system.h>
|
#include <pkgconf/net.h>
|
#include <pkgconf/net.h>
|
|
|
#ifdef CYGPKG_NET_DHCP
|
#ifdef CYGPKG_NET_DHCP
|
|
|
#if 0
|
#if 0
|
#define perror( txt ) // nothing
|
#define perror( txt ) // nothing
|
#endif
|
#endif
|
|
|
#include <network.h>
|
#include <network.h>
|
#include <dhcp.h>
|
#include <dhcp.h>
|
#include <errno.h>
|
#include <errno.h>
|
|
|
#include <cyg/infra/cyg_ass.h>
|
#include <cyg/infra/cyg_ass.h>
|
|
|
#ifdef INET6
|
#ifdef INET6
|
#include <net/if_var.h>
|
#include <net/if_var.h>
|
#include <netinet6/in6_var.h>
|
#include <netinet6/in6_var.h>
|
#endif
|
#endif
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Returns a pointer to the end of dhcp message (or NULL if invalid)
|
// Returns a pointer to the end of dhcp message (or NULL if invalid)
|
// meaning the address of the byte *after* the TAG_END token in the vendor
|
// meaning the address of the byte *after* the TAG_END token in the vendor
|
// data.
|
// data.
|
|
|
static unsigned char *
|
static unsigned char *
|
scan_dhcp_size( struct bootp *ppkt )
|
scan_dhcp_size( struct bootp *ppkt )
|
{
|
{
|
unsigned char *op;
|
unsigned char *op;
|
|
|
op = &ppkt->bp_vend[0];
|
op = &ppkt->bp_vend[0];
|
// First check for the cookie!
|
// First check for the cookie!
|
if ( op[0] != 99 ||
|
if ( op[0] != 99 ||
|
op[1] != 130 ||
|
op[1] != 130 ||
|
op[2] != 83 ||
|
op[2] != 83 ||
|
op[3] != 99 ) {
|
op[3] != 99 ) {
|
CYG_FAIL( "Bad DHCP cookie" );
|
CYG_FAIL( "Bad DHCP cookie" );
|
return NULL;
|
return NULL;
|
}
|
}
|
op += 4;
|
op += 4;
|
while (*op != TAG_END) {
|
while (*op != TAG_END) {
|
op += *(op+1)+2;
|
op += *(op+1)+2;
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in dhcp_size" );
|
CYG_FAIL( "Oversize DHCP packet in dhcp_size" );
|
return NULL;
|
return NULL;
|
}
|
}
|
}
|
}
|
// Check op has not gone wild
|
// Check op has not gone wild
|
CYG_ASSERT( op > (unsigned char *)(&ppkt[0]), "op pointer underflow!" );
|
CYG_ASSERT( op > (unsigned char *)(&ppkt[0]), "op pointer underflow!" );
|
// Compare op with non-existent "next" struct bootp in the array.
|
// Compare op with non-existent "next" struct bootp in the array.
|
CYG_ASSERT( op < (unsigned char *)(&ppkt[1]), "op pointer overflow!" );
|
CYG_ASSERT( op < (unsigned char *)(&ppkt[1]), "op pointer overflow!" );
|
return op + 1; // Address of first invalid byte
|
return op + 1; // Address of first invalid byte
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Get the actual packet size of an initialized buffer
|
// Get the actual packet size of an initialized buffer
|
|
|
static int
|
static int
|
dhcp_size( struct bootp *ppkt )
|
dhcp_size( struct bootp *ppkt )
|
{
|
{
|
unsigned char *op;
|
unsigned char *op;
|
|
|
op = scan_dhcp_size( ppkt );
|
op = scan_dhcp_size( ppkt );
|
if ( !op ) return 0;
|
if ( !op ) return 0;
|
return (op - (unsigned char *)ppkt);
|
return (op - (unsigned char *)ppkt);
|
}
|
}
|
|
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Get the actual packet size of an initialized buffer
|
// Get the actual packet size of an initialized buffer
|
// This will also pad the packet with 0 if length is less
|
// This will also pad the packet with 0 if length is less
|
// than BP_STD_TX_MINPKTSZ.
|
// than BP_STD_TX_MINPKTSZ.
|
|
|
static int
|
static int
|
dhcp_size_for_send( struct bootp *ppkt )
|
dhcp_size_for_send( struct bootp *ppkt )
|
{
|
{
|
unsigned char *op;
|
unsigned char *op;
|
|
|
op = scan_dhcp_size( ppkt );
|
op = scan_dhcp_size( ppkt );
|
if ( !op ) return 0; // Better not scribble!
|
if ( !op ) return 0; // Better not scribble!
|
// Zero extra bytes until the packet is large enough.
|
// Zero extra bytes until the packet is large enough.
|
for ( ; op < (((unsigned char *)ppkt) + BP_STD_TX_MINPKTSZ); op++ )
|
for ( ; op < (((unsigned char *)ppkt) + BP_STD_TX_MINPKTSZ); op++ )
|
*op = 0;
|
*op = 0;
|
return (op - (unsigned char *)ppkt);
|
return (op - (unsigned char *)ppkt);
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Insert/set an option value in an initialized buffer
|
// Insert/set an option value in an initialized buffer
|
|
|
static int
|
static int
|
set_fixed_tag( struct bootp *ppkt,
|
set_fixed_tag( struct bootp *ppkt,
|
unsigned char tag,
|
unsigned char tag,
|
cyg_uint32 value,
|
cyg_uint32 value,
|
int len)
|
int len)
|
{
|
{
|
unsigned char *op;
|
unsigned char *op;
|
|
|
// Initially this will only scan the options field.
|
// Initially this will only scan the options field.
|
|
|
op = &ppkt->bp_vend[4];
|
op = &ppkt->bp_vend[4];
|
while (*op != TAG_END) {
|
while (*op != TAG_END) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag" );
|
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag" );
|
return false;
|
return false;
|
}
|
}
|
if (*op == tag) // Found it...
|
if (*op == tag) // Found it...
|
break;
|
break;
|
op += *(op+1)+2;
|
op += *(op+1)+2;
|
}
|
}
|
|
|
if (*op == tag) { // Found it...
|
if (*op == tag) { // Found it...
|
if ( *(op+1) != len ) {
|
if ( *(op+1) != len ) {
|
CYG_FAIL( "Wrong size in set_fixed_tag" );
|
CYG_FAIL( "Wrong size in set_fixed_tag" );
|
return false; // wrong size
|
return false; // wrong size
|
}
|
}
|
}
|
}
|
else { // overwrite the end tag and install a new one
|
else { // overwrite the end tag and install a new one
|
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag append" );
|
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag append" );
|
return false;
|
return false;
|
}
|
}
|
*op = tag;
|
*op = tag;
|
*(op+1) = len;
|
*(op+1) = len;
|
*(op + len + 2) = TAG_END;
|
*(op + len + 2) = TAG_END;
|
}
|
}
|
// and insert the value. Net order is BE.
|
// and insert the value. Net order is BE.
|
op += len + 2 - 1; // point to end of value
|
op += len + 2 - 1; // point to end of value
|
while ( len-- > 0 ) {
|
while ( len-- > 0 ) {
|
*op-- = (unsigned char)(value & 255);
|
*op-- = (unsigned char)(value & 255);
|
value >>= 8;
|
value >>= 8;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
// Note that this does not permit changing the size of an extant tag.
|
// Note that this does not permit changing the size of an extant tag.
|
static int
|
static int
|
set_variable_tag( struct bootp *ppkt,
|
set_variable_tag( struct bootp *ppkt,
|
unsigned char tag,
|
unsigned char tag,
|
cyg_uint8 *pvalue,
|
cyg_uint8 *pvalue,
|
int len)
|
int len)
|
{
|
{
|
unsigned char *op;
|
unsigned char *op;
|
|
|
// Initially this will only scan the options field.
|
// Initially this will only scan the options field.
|
op = &ppkt->bp_vend[4];
|
op = &ppkt->bp_vend[4];
|
while (*op != TAG_END) {
|
while (*op != TAG_END) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in set_variable_tag" );
|
CYG_FAIL( "Oversize DHCP packet in set_variable_tag" );
|
return false;
|
return false;
|
}
|
}
|
if (*op == tag) // Found it...
|
if (*op == tag) // Found it...
|
break;
|
break;
|
op += *(op+1)+2;
|
op += *(op+1)+2;
|
}
|
}
|
|
|
if (*op == tag) { // Found it...
|
if (*op == tag) { // Found it...
|
if ( *(op+1) != len ) {
|
if ( *(op+1) != len ) {
|
CYG_FAIL( "Wrong size in set_variable_tag" );
|
CYG_FAIL( "Wrong size in set_variable_tag" );
|
return false; // wrong size
|
return false; // wrong size
|
}
|
}
|
}
|
}
|
else { // overwrite the end tag and install a new one
|
else { // overwrite the end tag and install a new one
|
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in set_variable_tag append" );
|
CYG_FAIL( "Oversize DHCP packet in set_variable_tag append" );
|
return false;
|
return false;
|
}
|
}
|
*op = tag;
|
*op = tag;
|
*(op+1) = len;
|
*(op+1) = len;
|
*(op + len + 2) = TAG_END;
|
*(op + len + 2) = TAG_END;
|
}
|
}
|
// and insert the value. No order is implied.
|
// and insert the value. No order is implied.
|
op += 2; // point to start of value
|
op += 2; // point to start of value
|
while ( len-- > 0 ) {
|
while ( len-- > 0 ) {
|
*op++ = *pvalue++;
|
*op++ = *pvalue++;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
static int
|
static int
|
unset_tag( struct bootp *ppkt,
|
unset_tag( struct bootp *ppkt,
|
unsigned char tag )
|
unsigned char tag )
|
{
|
{
|
unsigned char *op, *nextp = 0, *killp = 0;
|
unsigned char *op, *nextp = 0, *killp = 0;
|
|
|
// Initially this will only scan the options field.
|
// Initially this will only scan the options field.
|
|
|
op = &ppkt->bp_vend[4];
|
op = &ppkt->bp_vend[4];
|
while (*op != TAG_END) {
|
while (*op != TAG_END) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
|
CYG_FAIL( "Oversize DHCP packet in unset_tag" );
|
CYG_FAIL( "Oversize DHCP packet in unset_tag" );
|
return false;
|
return false;
|
}
|
}
|
if (*op == tag) { // Found it...
|
if (*op == tag) { // Found it...
|
killp = op; // item to kill
|
killp = op; // item to kill
|
nextp = op + *(op+1)+2; // next item address
|
nextp = op + *(op+1)+2; // next item address
|
}
|
}
|
op += *(op+1)+2; // scan to the end
|
op += *(op+1)+2; // scan to the end
|
}
|
}
|
|
|
if ( !killp )
|
if ( !killp )
|
return false;
|
return false;
|
|
|
// Obliterate the found op by copying down: *op is the end.
|
// Obliterate the found op by copying down: *op is the end.
|
while( nextp <= op ) // <= to copy the TAG_END too.
|
while( nextp <= op ) // <= to copy the TAG_END too.
|
*killp++ = *nextp++;
|
*killp++ = *nextp++;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Bring up an interface enough to broadcast, before we know who we are
|
// Bring up an interface enough to broadcast, before we know who we are
|
|
|
static int
|
static int
|
bring_half_up(const char *intf, struct ifreq *ifrp )
|
bring_half_up(const char *intf, struct ifreq *ifrp )
|
{
|
{
|
int s;
|
int s;
|
int one = 1;
|
int one = 1;
|
|
|
struct sockaddr_in *addrp;
|
struct sockaddr_in *addrp;
|
struct ecos_rtentry route;
|
struct ecos_rtentry route;
|
|
|
// Ensure clean slate
|
// Ensure clean slate
|
cyg_route_reinit(); // Force any existing routes to be forgotten
|
cyg_route_reinit(); // Force any existing routes to be forgotten
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
if (s < 0) {
|
if (s < 0) {
|
perror("socket");
|
perror("socket");
|
return false;
|
return false;
|
}
|
}
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
|
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
|
perror("setsockopt");
|
perror("setsockopt");
|
return false;
|
return false;
|
}
|
}
|
|
|
addrp = (struct sockaddr_in *) &ifrp->ifr_addr;
|
addrp = (struct sockaddr_in *) &ifrp->ifr_addr;
|
memset(addrp, 0, sizeof(*addrp));
|
memset(addrp, 0, sizeof(*addrp));
|
addrp->sin_family = AF_INET;
|
addrp->sin_family = AF_INET;
|
addrp->sin_len = sizeof(*addrp);
|
addrp->sin_len = sizeof(*addrp);
|
addrp->sin_port = 0;
|
addrp->sin_port = 0;
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
|
|
strcpy(ifrp->ifr_name, intf);
|
strcpy(ifrp->ifr_name, intf);
|
if (ioctl(s, SIOCSIFADDR, ifrp)) { /* set ifnet address */
|
if (ioctl(s, SIOCSIFADDR, ifrp)) { /* set ifnet address */
|
perror("SIOCSIFADDR");
|
perror("SIOCSIFADDR");
|
return false;
|
return false;
|
}
|
}
|
|
|
if (ioctl(s, SIOCSIFNETMASK, ifrp)) { /* set net addr mask */
|
if (ioctl(s, SIOCSIFNETMASK, ifrp)) { /* set net addr mask */
|
perror("SIOCSIFNETMASK");
|
perror("SIOCSIFNETMASK");
|
return false;
|
return false;
|
}
|
}
|
|
|
/* the broadcast address is 255.255.255.255 */
|
/* the broadcast address is 255.255.255.255 */
|
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
|
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
|
if (ioctl(s, SIOCSIFBRDADDR, ifrp)) { /* set broadcast addr */
|
if (ioctl(s, SIOCSIFBRDADDR, ifrp)) { /* set broadcast addr */
|
perror("SIOCSIFBRDADDR");
|
perror("SIOCSIFBRDADDR");
|
return false;
|
return false;
|
}
|
}
|
|
|
ifrp->ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
|
ifrp->ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
|
if (ioctl(s, SIOCSIFFLAGS, ifrp)) { /* set ifnet flags */
|
if (ioctl(s, SIOCSIFFLAGS, ifrp)) { /* set ifnet flags */
|
perror("SIOCSIFFLAGS up");
|
perror("SIOCSIFFLAGS up");
|
return false;
|
return false;
|
}
|
}
|
|
|
if (ioctl(s, SIOCGIFHWADDR, ifrp) < 0) { /* get MAC address */
|
if (ioctl(s, SIOCGIFHWADDR, ifrp) < 0) { /* get MAC address */
|
perror("SIOCGIFHWADDR 1");
|
perror("SIOCGIFHWADDR 1");
|
return false;
|
return false;
|
}
|
}
|
|
|
// Set up routing
|
// Set up routing
|
addrp->sin_family = AF_INET;
|
addrp->sin_family = AF_INET;
|
addrp->sin_port = 0;
|
addrp->sin_port = 0;
|
addrp->sin_len = sizeof(*addrp); // Size of address
|
addrp->sin_len = sizeof(*addrp); // Size of address
|
|
|
/* the broadcast address is 255.255.255.255 */
|
/* the broadcast address is 255.255.255.255 */
|
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
|
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
|
memset(&route, 0, sizeof(route));
|
memset(&route, 0, sizeof(route));
|
memcpy(&route.rt_gateway, addrp, sizeof(*addrp));
|
memcpy(&route.rt_gateway, addrp, sizeof(*addrp));
|
|
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
memcpy(&route.rt_dst, addrp, sizeof(*addrp));
|
memcpy(&route.rt_dst, addrp, sizeof(*addrp));
|
memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
|
memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
|
|
|
route.rt_dev = ifrp->ifr_name;
|
route.rt_dev = ifrp->ifr_name;
|
route.rt_flags = RTF_UP|RTF_GATEWAY;
|
route.rt_flags = RTF_UP|RTF_GATEWAY;
|
route.rt_metric = 0;
|
route.rt_metric = 0;
|
|
|
if (ioctl(s, SIOCADDRT, &route)) { /* add route */
|
if (ioctl(s, SIOCADDRT, &route)) { /* add route */
|
if (errno != EEXIST) {
|
if (errno != EEXIST) {
|
perror("SIOCADDRT 3");
|
perror("SIOCADDRT 3");
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
close(s);
|
close(s);
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// DHCP retransmission timeouts and number of tries
|
// DHCP retransmission timeouts and number of tries
|
//
|
//
|
// To work better with simulated failures (or real ones!) so that the rest
|
// To work better with simulated failures (or real ones!) so that the rest
|
// of the system is tested, rather than DHCP renewal failures pulling
|
// of the system is tested, rather than DHCP renewal failures pulling
|
// everything down, we try a little more zealously than the RFC suggests.
|
// everything down, we try a little more zealously than the RFC suggests.
|
|
|
static unsigned char timeout_random = 0;
|
static unsigned char timeout_random = 0;
|
|
|
struct timeout_state {
|
struct timeout_state {
|
unsigned int secs;
|
unsigned int secs;
|
int countdown;
|
int countdown;
|
};
|
};
|
|
|
static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
|
static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
|
{
|
{
|
timeout_random++;
|
timeout_random++;
|
pstate->countdown = 4; // initial fast retries
|
pstate->countdown = 4; // initial fast retries
|
pstate->secs = 3 + (timeout_random & 3);
|
pstate->secs = 3 + (timeout_random & 3);
|
ptv->tv_sec = 0;
|
ptv->tv_sec = 0;
|
ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
|
ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
|
}
|
}
|
|
|
static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
|
static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
|
{
|
{
|
if ( 0 < pstate->countdown-- )
|
if ( 0 < pstate->countdown-- )
|
return true;
|
return true;
|
if ( 0 == ptv->tv_sec )
|
if ( 0 == ptv->tv_sec )
|
ptv->tv_sec = pstate->secs;
|
ptv->tv_sec = pstate->secs;
|
else {
|
else {
|
timeout_random++;
|
timeout_random++;
|
pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
|
pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
|
pstate->countdown = 2; // later fast retries
|
pstate->countdown = 2; // later fast retries
|
ptv->tv_sec = 0;
|
ptv->tv_sec = 0;
|
}
|
}
|
return pstate->secs < 100; // If longer, too many tries...
|
return pstate->secs < 100; // If longer, too many tries...
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Lease expiry and alarms to notify it
|
// Lease expiry and alarms to notify it
|
|
|
static cyg_alarm_t alarm_function;
|
static cyg_alarm_t alarm_function;
|
|
|
static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
|
static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
|
{
|
{
|
struct dhcp_lease *lease = (struct dhcp_lease *)data;
|
struct dhcp_lease *lease = (struct dhcp_lease *)data;
|
lease->which |= lease->next;
|
lease->which |= lease->next;
|
if ( lease->needs_attention )
|
if ( lease->needs_attention )
|
cyg_semaphore_post( lease->needs_attention );
|
cyg_semaphore_post( lease->needs_attention );
|
|
|
// Step the lease on into its next state of being alarmed ;-)
|
// Step the lease on into its next state of being alarmed ;-)
|
if ( lease->next & DHCP_LEASE_EX ) {
|
if ( lease->next & DHCP_LEASE_EX ) {
|
cyg_alarm_disable( alarm );
|
cyg_alarm_disable( alarm );
|
}
|
}
|
else if ( lease->next & DHCP_LEASE_T2 ) {
|
else if ( lease->next & DHCP_LEASE_T2 ) {
|
lease->next = DHCP_LEASE_EX;
|
lease->next = DHCP_LEASE_EX;
|
cyg_alarm_initialize( lease->alarm, lease->expiry, 0 );
|
cyg_alarm_initialize( lease->alarm, lease->expiry, 0 );
|
cyg_alarm_enable( lease->alarm );
|
cyg_alarm_enable( lease->alarm );
|
}
|
}
|
else if ( lease->next & DHCP_LEASE_T1 ) {
|
else if ( lease->next & DHCP_LEASE_T1 ) {
|
lease->next = DHCP_LEASE_T2;
|
lease->next = DHCP_LEASE_T2;
|
cyg_alarm_initialize( lease->alarm, lease->t2, 0 );
|
cyg_alarm_initialize( lease->alarm, lease->t2, 0 );
|
cyg_alarm_enable( lease->alarm );
|
cyg_alarm_enable( lease->alarm );
|
}
|
}
|
}
|
}
|
|
|
static inline void no_lease( struct dhcp_lease *lease )
|
static inline void no_lease( struct dhcp_lease *lease )
|
{
|
{
|
if ( lease->alarm ) {
|
if ( lease->alarm ) {
|
// Already set: delete this.
|
// Already set: delete this.
|
cyg_alarm_disable( lease->alarm );
|
cyg_alarm_disable( lease->alarm );
|
cyg_alarm_delete( lease->alarm );
|
cyg_alarm_delete( lease->alarm );
|
lease->alarm = 0;
|
lease->alarm = 0;
|
}
|
}
|
}
|
}
|
|
|
static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
|
static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
|
{
|
{
|
cyg_tick_count_t now = cyg_current_time();
|
cyg_tick_count_t now = cyg_current_time();
|
cyg_tick_count_t then;
|
cyg_tick_count_t then;
|
cyg_uint32 tag = 0;
|
cyg_uint32 tag = 0;
|
cyg_uint32 expiry_then;
|
cyg_uint32 expiry_then;
|
cyg_resolution_t resolution =
|
cyg_resolution_t resolution =
|
cyg_clock_get_resolution(cyg_real_time_clock());
|
cyg_clock_get_resolution(cyg_real_time_clock());
|
cyg_handle_t h;
|
cyg_handle_t h;
|
unsigned int length;
|
unsigned int length;
|
|
|
// Silence any jabbering from past lease on this interface
|
// Silence any jabbering from past lease on this interface
|
no_lease( lease );
|
no_lease( lease );
|
lease->which = lease->next = 0;
|
lease->which = lease->next = 0;
|
cyg_clock_to_counter(cyg_real_time_clock(), &h);
|
cyg_clock_to_counter(cyg_real_time_clock(), &h);
|
cyg_alarm_create( h, alarm_function, (cyg_addrword_t)lease,
|
cyg_alarm_create( h, alarm_function, (cyg_addrword_t)lease,
|
&lease->alarm, &lease->alarm_obj );
|
&lease->alarm, &lease->alarm_obj );
|
|
|
// extract the lease time and scale it &c to now.
|
// extract the lease time and scale it &c to now.
|
length = sizeof(tag);
|
length = sizeof(tag);
|
if(!get_bootp_option( bootp, TAG_DHCP_LEASE_TIME, &tag ,&length))
|
if(!get_bootp_option( bootp, TAG_DHCP_LEASE_TIME, &tag ,&length))
|
tag = 0xffffffff;
|
tag = 0xffffffff;
|
|
|
if ( 0xffffffff == tag ) {
|
if ( 0xffffffff == tag ) {
|
lease->expiry = 0xffffffffffffffff;
|
lease->expiry = 0xffffffffffffffff;
|
lease->t2 = 0xffffffffffffffff;
|
lease->t2 = 0xffffffffffffffff;
|
lease->t1 = 0xffffffffffffffff;
|
lease->t1 = 0xffffffffffffffff;
|
return; // it's an infinite lease, hurrah!
|
return; // it's an infinite lease, hurrah!
|
}
|
}
|
|
|
then = (cyg_uint64)(ntohl(tag));
|
then = (cyg_uint64)(ntohl(tag));
|
expiry_then = then;
|
expiry_then = then;
|
|
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
lease->expiry = now + then;
|
lease->expiry = now + then;
|
length = sizeof(tag);
|
length = sizeof(tag);
|
if (get_bootp_option( bootp, TAG_DHCP_REBIND_TIME, &tag, &length ))
|
if (get_bootp_option( bootp, TAG_DHCP_REBIND_TIME, &tag, &length ))
|
then = (cyg_uint64)(ntohl(tag));
|
then = (cyg_uint64)(ntohl(tag));
|
else
|
else
|
then = expiry_then - expiry_then/4;
|
then = expiry_then - expiry_then/4;
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
lease->t2 = now + then;
|
lease->t2 = now + then;
|
|
|
length = sizeof(tag);
|
length = sizeof(tag);
|
if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag, &length ))
|
if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag, &length ))
|
then = (cyg_uint64)(ntohl(tag));
|
then = (cyg_uint64)(ntohl(tag));
|
else
|
else
|
then = expiry_then/2;
|
then = expiry_then/2;
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then *= 1000000000; // into nS - we know there is room in a tick_count_t
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
|
lease->t1 = now + then;
|
lease->t1 = now + then;
|
|
|
#if 0 // for testing this mechanism
|
#if 0 // for testing this mechanism
|
lease->expiry = now + 5000; // 1000 here makes for failure in the DHCP test
|
lease->expiry = now + 5000; // 1000 here makes for failure in the DHCP test
|
lease->t2 = now + 3500;
|
lease->t2 = now + 3500;
|
lease->t1 = now + 2500;
|
lease->t1 = now + 2500;
|
#endif
|
#endif
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf("new_lease:\n");
|
diag_printf("new_lease:\n");
|
diag_printf(" expiry = %d\n",lease->expiry);
|
diag_printf(" expiry = %d\n",lease->expiry);
|
diag_printf(" t1 = %d\n",lease->t1);
|
diag_printf(" t1 = %d\n",lease->t1);
|
diag_printf(" t2 = %d\n",lease->t2);
|
diag_printf(" t2 = %d\n",lease->t2);
|
#endif
|
#endif
|
|
|
lease->next = DHCP_LEASE_T1;
|
lease->next = DHCP_LEASE_T1;
|
|
|
cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
|
cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
|
cyg_alarm_enable( lease->alarm );
|
cyg_alarm_enable( lease->alarm );
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Set all the tags we want to use when sending a packet.
|
// Set all the tags we want to use when sending a packet.
|
// This has expanded to a large, explicit set to interwork better
|
// This has expanded to a large, explicit set to interwork better
|
// with a variety of DHCP servers.
|
// with a variety of DHCP servers.
|
|
|
static void set_default_dhcp_tags( struct bootp *xmit )
|
static void set_default_dhcp_tags( struct bootp *xmit )
|
{
|
{
|
// Explicitly request full set of params that are default for LINUX
|
// Explicitly request full set of params that are default for LINUX
|
// dhcp servers, but not default for others. This is rather arbitrary,
|
// dhcp servers, but not default for others. This is rather arbitrary,
|
// but it preserves behaviour for people using those servers.
|
// but it preserves behaviour for people using those servers.
|
// Perhaps configury of this set will be needed in future?
|
// Perhaps configury of this set will be needed in future?
|
//
|
//
|
// Here's the set:
|
// Here's the set:
|
static cyg_uint8 req_list[] = {
|
static cyg_uint8 req_list[] = {
|
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
|
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
|
CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
|
CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
|
#else
|
#else
|
TAG_DHCP_SERVER_ID , // DHCP server id: 10.16.19.66
|
TAG_DHCP_SERVER_ID , // DHCP server id: 10.16.19.66
|
TAG_DHCP_LEASE_TIME , // DHCP time 51: 60
|
TAG_DHCP_LEASE_TIME , // DHCP time 51: 60
|
TAG_DHCP_RENEWAL_TIME , // DHCP time 58: 30
|
TAG_DHCP_RENEWAL_TIME , // DHCP time 58: 30
|
TAG_DHCP_REBIND_TIME , // DHCP time 59: 52
|
TAG_DHCP_REBIND_TIME , // DHCP time 59: 52
|
TAG_SUBNET_MASK , // subnet mask: 255.255.255.0
|
TAG_SUBNET_MASK , // subnet mask: 255.255.255.0
|
TAG_GATEWAY , // gateway: 10.16.19.66
|
TAG_GATEWAY , // gateway: 10.16.19.66
|
TAG_DOMAIN_SERVER , // domain server: 10.16.19.66
|
TAG_DOMAIN_SERVER , // domain server: 10.16.19.66
|
TAG_DOMAIN_NAME , // domain name: hmt10.cambridge.redhat.com
|
TAG_DOMAIN_NAME , // domain name: hmt10.cambridge.redhat.com
|
TAG_IP_BROADCAST , // IP broadcast: 10.16.19.255
|
TAG_IP_BROADCAST , // IP broadcast: 10.16.19.255
|
#endif
|
#endif
|
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
|
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
|
CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
|
CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
|
#endif
|
#endif
|
};
|
};
|
|
|
if ( req_list[0] ) // So that one may easily turn it all off by configury
|
if ( req_list[0] ) // So that one may easily turn it all off by configury
|
set_variable_tag( xmit, TAG_DHCP_PARM_REQ_LIST,
|
set_variable_tag( xmit, TAG_DHCP_PARM_REQ_LIST,
|
&req_list[0], sizeof( req_list ) );
|
&req_list[0], sizeof( req_list ) );
|
|
|
// Explicitly specify our max message size.
|
// Explicitly specify our max message size.
|
set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );
|
set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// the DHCP state machine - this does all the work
|
// the DHCP state machine - this does all the work
|
|
|
int
|
int
|
do_dhcp(const char *intf, struct bootp *res,
|
do_dhcp(const char *intf, struct bootp *res,
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
{
|
{
|
struct ifreq ifr;
|
struct ifreq ifr;
|
struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
|
struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
|
int s, addrlen;
|
int s, addrlen;
|
int one = 1;
|
int one = 1;
|
unsigned char mincookie[] = {99,130,83,99,255} ;
|
unsigned char mincookie[] = {99,130,83,99,255} ;
|
struct timeval tv;
|
struct timeval tv;
|
struct timeout_state timeout_scratch;
|
struct timeout_state timeout_scratch;
|
cyg_uint8 oldstate = *pstate;
|
cyg_uint8 oldstate = *pstate;
|
cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
|
cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
|
unsigned int length;
|
unsigned int length;
|
|
|
cyg_uint32 xid;
|
cyg_uint32 xid;
|
|
|
#define CHECK_XID() ( /* and other details */ \
|
#define CHECK_XID() ( /* and other details */ \
|
received->bp_xid != xid || /* not the same transaction */ \
|
received->bp_xid != xid || /* not the same transaction */ \
|
received->bp_htype != xmit->bp_htype || /* not the same ESA type */ \
|
received->bp_htype != xmit->bp_htype || /* not the same ESA type */ \
|
received->bp_hlen != xmit->bp_hlen || /* not the same length */ \
|
received->bp_hlen != xmit->bp_hlen || /* not the same length */ \
|
bcmp( &received->bp_chaddr, &xmit->bp_chaddr, xmit->bp_hlen ) \
|
bcmp( &received->bp_chaddr, &xmit->bp_chaddr, xmit->bp_hlen ) \
|
)
|
)
|
|
|
// IMPORTANT: xmit is the same as res throughout this; *received is a
|
// IMPORTANT: xmit is the same as res throughout this; *received is a
|
// scratch buffer for reception; its contents are always copied to res
|
// scratch buffer for reception; its contents are always copied to res
|
// when we are happy with them. So we always transmit from the
|
// when we are happy with them. So we always transmit from the
|
// existing state.
|
// existing state.
|
struct bootp rx_local;
|
struct bootp rx_local;
|
struct bootp *received = &rx_local;
|
struct bootp *received = &rx_local;
|
struct bootp *xmit = res;
|
struct bootp *xmit = res;
|
struct bootp xmit2;
|
struct bootp xmit2;
|
int xlen;
|
int xlen;
|
|
|
// First, get a socket on the interface in question. But Zeroth, if
|
// First, get a socket on the interface in question. But Zeroth, if
|
// needs be, bring it to the half-up broadcast only state if needs be.
|
// needs be, bring it to the half-up broadcast only state if needs be.
|
|
|
if ( DHCPSTATE_INIT == oldstate
|
if ( DHCPSTATE_INIT == oldstate
|
|| DHCPSTATE_FAILED == oldstate
|
|| DHCPSTATE_FAILED == oldstate
|
|| 0 == oldstate ) {
|
|| 0 == oldstate ) {
|
// either explicit init state or the beginning of time or retry
|
// either explicit init state or the beginning of time or retry
|
if ( ! bring_half_up( intf, &ifr ) )
|
if ( ! bring_half_up( intf, &ifr ) )
|
return false;
|
return false;
|
|
|
*pstate = DHCPSTATE_INIT;
|
*pstate = DHCPSTATE_INIT;
|
}
|
}
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
if (s < 0) {
|
if (s < 0) {
|
perror("socket");
|
perror("socket");
|
return false;
|
return false;
|
}
|
}
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
|
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
|
perror("setsockopt");
|
perror("setsockopt");
|
return false;
|
return false;
|
}
|
}
|
|
|
memset((char *) &cli_addr, 0, sizeof(cli_addr));
|
memset((char *) &cli_addr, 0, sizeof(cli_addr));
|
cli_addr.sin_family = AF_INET;
|
cli_addr.sin_family = AF_INET;
|
cli_addr.sin_len = sizeof(cli_addr);
|
cli_addr.sin_len = sizeof(cli_addr);
|
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
cli_addr.sin_port = htons(IPPORT_BOOTPC);
|
cli_addr.sin_port = htons(IPPORT_BOOTPC);
|
|
|
memset((char *) &broadcast_addr, 0, sizeof(broadcast_addr));
|
memset((char *) &broadcast_addr, 0, sizeof(broadcast_addr));
|
broadcast_addr.sin_family = AF_INET;
|
broadcast_addr.sin_family = AF_INET;
|
broadcast_addr.sin_len = sizeof(broadcast_addr);
|
broadcast_addr.sin_len = sizeof(broadcast_addr);
|
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
broadcast_addr.sin_port = htons(IPPORT_BOOTPS);
|
broadcast_addr.sin_port = htons(IPPORT_BOOTPS);
|
|
|
memset((char *) &server_addr, 0, sizeof(server_addr));
|
memset((char *) &server_addr, 0, sizeof(server_addr));
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // overwrite later
|
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // overwrite later
|
server_addr.sin_port = htons(IPPORT_BOOTPS);
|
server_addr.sin_port = htons(IPPORT_BOOTPS);
|
|
|
if(bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
|
if(bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
|
perror("bind error");
|
perror("bind error");
|
return false;
|
return false;
|
}
|
}
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
|
perror("setsockopt SO_REUSEADDR");
|
perror("setsockopt SO_REUSEADDR");
|
return false;
|
return false;
|
}
|
}
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
|
perror("setsockopt SO_REUSEPORT");
|
perror("setsockopt SO_REUSEPORT");
|
return false;
|
return false;
|
}
|
}
|
|
|
// Now, we can launch into the DHCP state machine. I think this will
|
// Now, we can launch into the DHCP state machine. I think this will
|
// be the neatest way to do it; it returns from within the switch arms
|
// be the neatest way to do it; it returns from within the switch arms
|
// when all is well, or utterly failed.
|
// when all is well, or utterly failed.
|
|
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
|
|
// Choose a new XID: first get the ESA as a basis:
|
// Choose a new XID: first get the ESA as a basis:
|
strcpy(&ifr.ifr_name[0], intf);
|
strcpy(&ifr.ifr_name[0], intf);
|
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
|
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
|
perror("SIOCGIFHWADDR 2");
|
perror("SIOCGIFHWADDR 2");
|
return false;
|
return false;
|
}
|
}
|
|
|
// Choose from scratch depending on ifr_hwaddr...[]
|
// Choose from scratch depending on ifr_hwaddr...[]
|
xid = ifr.ifr_hwaddr.sa_data[5];
|
xid = ifr.ifr_hwaddr.sa_data[5];
|
xid |= (ifr.ifr_hwaddr.sa_data[4]) << 8;
|
xid |= (ifr.ifr_hwaddr.sa_data[4]) << 8;
|
xid |= (ifr.ifr_hwaddr.sa_data[3]) << 16;
|
xid |= (ifr.ifr_hwaddr.sa_data[3]) << 16;
|
xid |= (ifr.ifr_hwaddr.sa_data[2]) << 24;
|
xid |= (ifr.ifr_hwaddr.sa_data[2]) << 24;
|
xid ^= (cyg_arc4random() & 0xffff0000);
|
xid ^= (cyg_arc4random() & 0xffff0000);
|
|
|
// Avoid adjacent ESAs colliding by increment
|
// Avoid adjacent ESAs colliding by increment
|
#define NEW_XID(_xid) CYG_MACRO_START (_xid)+= 0x10000; CYG_MACRO_END
|
#define NEW_XID(_xid) CYG_MACRO_START (_xid)+= 0x10000; CYG_MACRO_END
|
|
|
while ( 1 ) {
|
while ( 1 ) {
|
|
|
// If we are active rather than in the process of shutting down,
|
// If we are active rather than in the process of shutting down,
|
// check for any lease expiry every time round, so that alarms
|
// check for any lease expiry every time round, so that alarms
|
// *can* change the course of events even when already renewing,
|
// *can* change the course of events even when already renewing,
|
// for example.
|
// for example.
|
if ( DHCPSTATE_DO_RELEASE != *pstate
|
if ( DHCPSTATE_DO_RELEASE != *pstate
|
&& DHCPSTATE_NOTBOUND != *pstate
|
&& DHCPSTATE_NOTBOUND != *pstate
|
&& DHCPSTATE_FAILED != *pstate ) {
|
&& DHCPSTATE_FAILED != *pstate ) {
|
cyg_uint8 lease_state;
|
cyg_uint8 lease_state;
|
|
|
cyg_scheduler_lock();
|
cyg_scheduler_lock();
|
lease_state = lease->which;
|
lease_state = lease->which;
|
lease->which = 0; // flag that we have noticed it
|
lease->which = 0; // flag that we have noticed it
|
cyg_scheduler_unlock();
|
cyg_scheduler_unlock();
|
|
|
if ( lease_state & DHCP_LEASE_EX ) {
|
if ( lease_state & DHCP_LEASE_EX ) {
|
// then the lease has expired completely!
|
// then the lease has expired completely!
|
*pstate = DHCPSTATE_NOTBOUND;
|
*pstate = DHCPSTATE_NOTBOUND;
|
}
|
}
|
else if ( lease_state & DHCP_LEASE_T2 ) {
|
else if ( lease_state & DHCP_LEASE_T2 ) {
|
// Time to renew
|
// Time to renew
|
reset_timeout( &tv, &timeout_scratch ); // next conversation
|
reset_timeout( &tv, &timeout_scratch ); // next conversation
|
*pstate = DHCPSTATE_REBINDING;
|
*pstate = DHCPSTATE_REBINDING;
|
}
|
}
|
else if ( lease_state & DHCP_LEASE_T1 ) {
|
else if ( lease_state & DHCP_LEASE_T1 ) {
|
// Time to renew
|
// Time to renew
|
reset_timeout( &tv, &timeout_scratch ); // next conversation
|
reset_timeout( &tv, &timeout_scratch ); // next conversation
|
*pstate = DHCPSTATE_RENEWING;
|
*pstate = DHCPSTATE_RENEWING;
|
}
|
}
|
}
|
}
|
|
|
switch ( *pstate ) {
|
switch ( *pstate ) {
|
|
|
case DHCPSTATE_INIT:
|
case DHCPSTATE_INIT:
|
|
|
// Send the DHCPDISCOVER packet
|
// Send the DHCPDISCOVER packet
|
|
|
// Fill in the BOOTP request - DHCPDISCOVER packet
|
// Fill in the BOOTP request - DHCPDISCOVER packet
|
bzero(xmit, sizeof(*xmit));
|
bzero(xmit, sizeof(*xmit));
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_htype = HTYPE_ETHERNET;
|
xmit->bp_htype = HTYPE_ETHERNET;
|
xmit->bp_hlen = IFHWADDRLEN;
|
xmit->bp_hlen = IFHWADDRLEN;
|
xmit->bp_xid = xid;
|
xmit->bp_xid = xid;
|
xmit->bp_secs = 0;
|
xmit->bp_secs = 0;
|
xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
|
xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
|
bcopy(ifr.ifr_hwaddr.sa_data, &xmit->bp_chaddr, xmit->bp_hlen);
|
bcopy(ifr.ifr_hwaddr.sa_data, &xmit->bp_chaddr, xmit->bp_hlen);
|
bcopy(mincookie, xmit->bp_vend, sizeof(mincookie));
|
bcopy(mincookie, xmit->bp_vend, sizeof(mincookie));
|
|
|
// remove the next line to test ability to handle bootp packets.
|
// remove the next line to test ability to handle bootp packets.
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPDISCOVER, 1 );
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPDISCOVER, 1 );
|
// Set all the tags we want to use when sending a packet
|
// Set all the tags we want to use when sending a packet
|
set_default_dhcp_tags( xmit );
|
set_default_dhcp_tags( xmit );
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_INIT sending:\n" );
|
diag_printf( "---------DHCPSTATE_INIT sending:\n" );
|
show_bootp( intf, xmit );
|
show_bootp( intf, xmit );
|
#endif
|
#endif
|
if(sendto(s, xmit, dhcp_size_for_send(xmit), 0,
|
if(sendto(s, xmit, dhcp_size_for_send(xmit), 0,
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
|
|
seen_bootp_reply = 0;
|
seen_bootp_reply = 0;
|
*pstate = DHCPSTATE_SELECTING;
|
*pstate = DHCPSTATE_SELECTING;
|
break;
|
break;
|
|
|
case DHCPSTATE_SELECTING:
|
case DHCPSTATE_SELECTING:
|
// This is a separate state so that we can listen again
|
// This is a separate state so that we can listen again
|
// *without* retransmitting.
|
// *without* retransmitting.
|
|
|
// listen for the DHCPOFFER reply
|
// listen for the DHCPOFFER reply
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
addrlen = sizeof(rx_addr);
|
addrlen = sizeof(rx_addr);
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
// No packet arrived (this time)
|
// No packet arrived (this time)
|
if ( seen_bootp_reply ) { // then already have a bootp reply
|
if ( seen_bootp_reply ) { // then already have a bootp reply
|
// Save the good packet in *xmit
|
// Save the good packet in *xmit
|
bcopy( received, xmit, dhcp_size(received) );
|
bcopy( received, xmit, dhcp_size(received) );
|
*pstate = DHCPSTATE_BOOTP_FALLBACK;
|
*pstate = DHCPSTATE_BOOTP_FALLBACK;
|
NEW_XID( xid ); // Happy to advance, so new XID
|
NEW_XID( xid ); // Happy to advance, so new XID
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
break;
|
break;
|
}
|
}
|
// go to the next larger timeout and re-send:
|
// go to the next larger timeout and re-send:
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
*pstate = DHCPSTATE_INIT; // to retransmit
|
*pstate = DHCPSTATE_INIT; // to retransmit
|
break;
|
break;
|
}
|
}
|
// Check for well-formed packet with correct termination (not truncated)
|
// Check for well-formed packet with correct termination (not truncated)
|
length = dhcp_size( received );
|
length = dhcp_size( received );
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_SELECTING received:\n" );
|
diag_printf( "---------DHCPSTATE_SELECTING received:\n" );
|
if ( length <= 0 )
|
if ( length <= 0 )
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
rx_addr.sin_family,
|
rx_addr.sin_family,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_port );
|
rx_addr.sin_port );
|
show_bootp( intf, received );
|
show_bootp( intf, received );
|
#endif
|
#endif
|
if ( length <= 0 )
|
if ( length <= 0 )
|
break;
|
break;
|
if ( CHECK_XID() ) // XID and ESA matches?
|
if ( CHECK_XID() ) // XID and ESA matches?
|
break; // listen again...
|
break; // listen again...
|
|
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
// then fill in from the options...
|
// then fill in from the options...
|
length = sizeof(received->bp_siaddr.s_addr);
|
length = sizeof(received->bp_siaddr.s_addr);
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
&received->bp_siaddr.s_addr,
|
&received->bp_siaddr.s_addr,
|
&length);
|
&length);
|
}
|
}
|
|
|
// see if it was a DHCP reply or a bootp reply; it could be
|
// see if it was a DHCP reply or a bootp reply; it could be
|
// either.
|
// either.
|
length = sizeof(msgtype);
|
length = sizeof(msgtype);
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
&length) ) {
|
&length) ) {
|
if ( DHCPOFFER == msgtype ) { // all is well
|
if ( DHCPOFFER == msgtype ) { // all is well
|
// Save the good packet in *xmit
|
// Save the good packet in *xmit
|
bcopy( received, xmit, dhcp_size(received) );
|
bcopy( received, xmit, dhcp_size(received) );
|
// we like the packet, so reset the timeout for next time
|
// we like the packet, so reset the timeout for next time
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
*pstate = DHCPSTATE_REQUESTING;
|
*pstate = DHCPSTATE_REQUESTING;
|
NEW_XID( xid ); // Happy to advance, so new XID
|
NEW_XID( xid ); // Happy to advance, so new XID
|
}
|
}
|
}
|
}
|
else // No TAG_DHCP_MESS_TYPE entry so it's a bootp reply
|
else // No TAG_DHCP_MESS_TYPE entry so it's a bootp reply
|
seen_bootp_reply = 1; // (keep the bootp packet in received)
|
seen_bootp_reply = 1; // (keep the bootp packet in received)
|
|
|
// If none of the above state changes occurred, we got a packet
|
// If none of the above state changes occurred, we got a packet
|
// that "should not happen", OR we have a bootp reply in our
|
// that "should not happen", OR we have a bootp reply in our
|
// hand; so listen again with the same timeout, without
|
// hand; so listen again with the same timeout, without
|
// retrying the send, in the hope of getting a DHCP reply.
|
// retrying the send, in the hope of getting a DHCP reply.
|
break;
|
break;
|
|
|
case DHCPSTATE_REQUESTING:
|
case DHCPSTATE_REQUESTING:
|
// Just send what you got with a DHCPREQUEST in the message type.
|
// Just send what you got with a DHCPREQUEST in the message type.
|
// then wait for an ACK in DHCPSTATE_REQUEST_RECV.
|
// then wait for an ACK in DHCPSTATE_REQUEST_RECV.
|
|
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
xmit->bp_xid = xid;
|
xmit->bp_xid = xid;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
|
xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
|
|
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
// Set all the tags we want to use when sending a packet
|
// Set all the tags we want to use when sending a packet
|
set_default_dhcp_tags( xmit );
|
set_default_dhcp_tags( xmit );
|
// And this will be a new one:
|
// And this will be a new one:
|
set_fixed_tag( xmit, TAG_DHCP_REQ_IP, ntohl(xmit->bp_yiaddr.s_addr), 4 );
|
set_fixed_tag( xmit, TAG_DHCP_REQ_IP, ntohl(xmit->bp_yiaddr.s_addr), 4 );
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_REQUESTING sending:\n" );
|
diag_printf( "---------DHCPSTATE_REQUESTING sending:\n" );
|
show_bootp( intf, xmit );
|
show_bootp( intf, xmit );
|
#endif
|
#endif
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// cleared, as per the RFC. We need the copy because these fields are
|
// cleared, as per the RFC. We need the copy because these fields are
|
// still useful to us (and currently stored in the 'result' structure)
|
// still useful to us (and currently stored in the 'result' structure)
|
xlen = dhcp_size_for_send( xmit );
|
xlen = dhcp_size_for_send( xmit );
|
bcopy( xmit, &xmit2, xlen );
|
bcopy( xmit, &xmit2, xlen );
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_hops = 0;
|
xmit2.bp_hops = 0;
|
if(sendto(s, &xmit2, xlen, 0,
|
if(sendto(s, &xmit2, xlen, 0,
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
|
|
*pstate = DHCPSTATE_REQUEST_RECV;
|
*pstate = DHCPSTATE_REQUEST_RECV;
|
break;
|
break;
|
|
|
case DHCPSTATE_REQUEST_RECV:
|
case DHCPSTATE_REQUEST_RECV:
|
// wait for an ACK or a NACK - retry by going back to
|
// wait for an ACK or a NACK - retry by going back to
|
// DHCPSTATE_REQUESTING; NACK means go back to INIT.
|
// DHCPSTATE_REQUESTING; NACK means go back to INIT.
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
addrlen = sizeof(rx_addr);
|
addrlen = sizeof(rx_addr);
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
// No packet arrived
|
// No packet arrived
|
// go to the next larger timeout and re-send:
|
// go to the next larger timeout and re-send:
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
*pstate = DHCPSTATE_REQUESTING;
|
*pstate = DHCPSTATE_REQUESTING;
|
break;
|
break;
|
}
|
}
|
// Check for well-formed packet with correct termination (not truncated)
|
// Check for well-formed packet with correct termination (not truncated)
|
length = dhcp_size( received );
|
length = dhcp_size( received );
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_REQUEST_RECV received:\n" );
|
diag_printf( "---------DHCPSTATE_REQUEST_RECV received:\n" );
|
if ( length <= 0 )
|
if ( length <= 0 )
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
rx_addr.sin_family,
|
rx_addr.sin_family,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_port );
|
rx_addr.sin_port );
|
show_bootp( intf, received );
|
show_bootp( intf, received );
|
#endif
|
#endif
|
if ( length <= 0 )
|
if ( length <= 0 )
|
break;
|
break;
|
if ( CHECK_XID() ) // not the same transaction;
|
if ( CHECK_XID() ) // not the same transaction;
|
break; // listen again...
|
break; // listen again...
|
|
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
// then fill in from the options...
|
// then fill in from the options...
|
length = sizeof(received->bp_siaddr.s_addr );
|
length = sizeof(received->bp_siaddr.s_addr );
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
&received->bp_siaddr.s_addr,
|
&received->bp_siaddr.s_addr,
|
&length);
|
&length);
|
}
|
}
|
|
|
// check it was a DHCP reply
|
// check it was a DHCP reply
|
length = sizeof(msgtype);
|
length = sizeof(msgtype);
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
&length) ) {
|
&length) ) {
|
if ( DHCPACK == msgtype // Same offer & server?
|
if ( DHCPACK == msgtype // Same offer & server?
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr
|
&& received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
|
&& received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
|
// we like the packet, so reset the timeout for next time
|
// we like the packet, so reset the timeout for next time
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
// Record the new lease and set up timers &c
|
// Record the new lease and set up timers &c
|
new_lease( received, lease );
|
new_lease( received, lease );
|
*pstate = DHCPSTATE_BOUND;
|
*pstate = DHCPSTATE_BOUND;
|
break;
|
break;
|
}
|
}
|
if ( DHCPNAK == msgtype // Same server?
|
if ( DHCPNAK == msgtype // Same server?
|
&& received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
|
&& received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
|
// we're bounced!
|
// we're bounced!
|
*pstate = DHCPSTATE_INIT; // So back the start of the rigmarole.
|
*pstate = DHCPSTATE_INIT; // So back the start of the rigmarole.
|
NEW_XID( xid ); // Unhappy to advance, so new XID
|
NEW_XID( xid ); // Unhappy to advance, so new XID
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
break;
|
break;
|
}
|
}
|
// otherwise it's something else, maybe another offer, or a bogus
|
// otherwise it's something else, maybe another offer, or a bogus
|
// NAK from someone we are not asking!
|
// NAK from someone we are not asking!
|
// Just listen again, which implicitly discards it.
|
// Just listen again, which implicitly discards it.
|
}
|
}
|
break;
|
break;
|
|
|
case DHCPSTATE_BOUND:
|
case DHCPSTATE_BOUND:
|
|
|
// We are happy now, we have our address.
|
// We are happy now, we have our address.
|
|
|
// All done with socket
|
// All done with socket
|
close(s);
|
close(s);
|
|
|
// Re-initialize the interface with the new state
|
// Re-initialize the interface with the new state
|
if ( DHCPSTATE_BOUND != oldstate ) {
|
if ( DHCPSTATE_BOUND != oldstate ) {
|
// Then need to go down and up
|
// Then need to go down and up
|
do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
|
do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
|
if ( 0 != oldstate ) {
|
if ( 0 != oldstate ) {
|
// Then not called from init_all_network_interfaces()
|
// Then not called from init_all_network_interfaces()
|
// so we must initialize the interface ourselves
|
// so we must initialize the interface ourselves
|
if (!init_net(intf, res)) {
|
if (!init_net(intf, res)) {
|
do_dhcp_down_net( intf, res, pstate, lease );
|
do_dhcp_down_net( intf, res, pstate, lease );
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
// Otherwise, nothing whatsoever to do...
|
// Otherwise, nothing whatsoever to do...
|
return true;
|
return true;
|
|
|
case DHCPSTATE_RENEWING:
|
case DHCPSTATE_RENEWING:
|
// Just send what you got with a DHCPREQUEST in the message
|
// Just send what you got with a DHCPREQUEST in the message
|
// type UNICAST straight to the server. Then wait for an ACK.
|
// type UNICAST straight to the server. Then wait for an ACK.
|
|
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
xmit->bp_xid = xid;
|
xmit->bp_xid = xid;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_flags = htons(0); // No BROADCAST FLAG
|
xmit->bp_flags = htons(0); // No BROADCAST FLAG
|
// Use the *client* address here:
|
// Use the *client* address here:
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
|
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
// These must not be set in this context
|
// These must not be set in this context
|
unset_tag( xmit, TAG_DHCP_REQ_IP );
|
unset_tag( xmit, TAG_DHCP_REQ_IP );
|
unset_tag( xmit, TAG_DHCP_SERVER_ID );
|
unset_tag( xmit, TAG_DHCP_SERVER_ID );
|
// Set all the tags we want to use when sending a packet
|
// Set all the tags we want to use when sending a packet
|
set_default_dhcp_tags( xmit );
|
set_default_dhcp_tags( xmit );
|
|
|
// Set unicast address to *server*
|
// Set unicast address to *server*
|
server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
|
server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_RENEWING sending:\n" );
|
diag_printf( "---------DHCPSTATE_RENEWING sending:\n" );
|
diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
|
diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
|
server_addr.sin_family,
|
server_addr.sin_family,
|
server_addr.sin_addr.s_addr,
|
server_addr.sin_addr.s_addr,
|
server_addr.sin_port );
|
server_addr.sin_port );
|
show_bootp( intf, xmit );
|
show_bootp( intf, xmit );
|
#endif
|
#endif
|
|
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// cleared, as per the RFC. We need the copy because these fields are
|
// cleared, as per the RFC. We need the copy because these fields are
|
// still useful to us (and currently stored in the 'result' structure)
|
// still useful to us (and currently stored in the 'result' structure)
|
xlen = dhcp_size_for_send(xmit);
|
xlen = dhcp_size_for_send(xmit);
|
bcopy( xmit, &xmit2, xlen );
|
bcopy( xmit, &xmit2, xlen );
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_hops = 0;
|
xmit2.bp_hops = 0;
|
if(sendto(s, &xmit2, xlen, 0,
|
if(sendto(s, &xmit2, xlen, 0,
|
// UNICAST address of the server:
|
// UNICAST address of the server:
|
(struct sockaddr *)&server_addr,
|
(struct sockaddr *)&server_addr,
|
sizeof(server_addr)) < 0) {
|
sizeof(server_addr)) < 0) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
|
|
*pstate = DHCPSTATE_RENEW_RECV;
|
*pstate = DHCPSTATE_RENEW_RECV;
|
break;
|
break;
|
|
|
case DHCPSTATE_RENEW_RECV:
|
case DHCPSTATE_RENEW_RECV:
|
// wait for an ACK or a NACK - retry by going back to
|
// wait for an ACK or a NACK - retry by going back to
|
// DHCPSTATE_RENEWING; NACK means go to NOTBOUND.
|
// DHCPSTATE_RENEWING; NACK means go to NOTBOUND.
|
// No answer means just wait for T2, to broadcast.
|
// No answer means just wait for T2, to broadcast.
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
addrlen = sizeof(rx_addr);
|
addrlen = sizeof(rx_addr);
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
// No packet arrived
|
// No packet arrived
|
// go to the next larger timeout and re-send:
|
// go to the next larger timeout and re-send:
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
// If we timed out completely, just give up until T2
|
// If we timed out completely, just give up until T2
|
// expires - retain the lease meanwhile. The normal
|
// expires - retain the lease meanwhile. The normal
|
// lease mechanism will invoke REBINDING as and when
|
// lease mechanism will invoke REBINDING as and when
|
// necessary.
|
// necessary.
|
*pstate = DHCPSTATE_BOUND;
|
*pstate = DHCPSTATE_BOUND;
|
break;
|
break;
|
}
|
}
|
*pstate = DHCPSTATE_RENEWING;
|
*pstate = DHCPSTATE_RENEWING;
|
break;
|
break;
|
}
|
}
|
// Check for well-formed packet with correct termination (not truncated)
|
// Check for well-formed packet with correct termination (not truncated)
|
length = dhcp_size( received );
|
length = dhcp_size( received );
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_RENEW_RECV received:\n" );
|
diag_printf( "---------DHCPSTATE_RENEW_RECV received:\n" );
|
if ( length <= 0 )
|
if ( length <= 0 )
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
rx_addr.sin_family,
|
rx_addr.sin_family,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_port );
|
rx_addr.sin_port );
|
show_bootp( intf, received );
|
show_bootp( intf, received );
|
#endif
|
#endif
|
if ( length <= 0 )
|
if ( length <= 0 )
|
break;
|
break;
|
if ( CHECK_XID() ) // not the same transaction;
|
if ( CHECK_XID() ) // not the same transaction;
|
break; // listen again...
|
break; // listen again...
|
|
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
// then fill in from the options...
|
// then fill in from the options...
|
length = sizeof(received->bp_siaddr.s_addr);
|
length = sizeof(received->bp_siaddr.s_addr);
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
&received->bp_siaddr.s_addr,
|
&received->bp_siaddr.s_addr,
|
&length);
|
&length);
|
}
|
}
|
|
|
// check it was a DHCP reply
|
// check it was a DHCP reply
|
length = sizeof(msgtype);
|
length = sizeof(msgtype);
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
&length) ) {
|
&length) ) {
|
if ( DHCPACK == msgtype // Same offer?
|
if ( DHCPACK == msgtype // Same offer?
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
|
// we like the packet, so reset the timeout for next time
|
// we like the packet, so reset the timeout for next time
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
// Record the new lease and set up timers &c
|
// Record the new lease and set up timers &c
|
new_lease( received, lease );
|
new_lease( received, lease );
|
*pstate = DHCPSTATE_BOUND;
|
*pstate = DHCPSTATE_BOUND;
|
break;
|
break;
|
}
|
}
|
if ( DHCPNAK == msgtype ) { // we're bounced!
|
if ( DHCPNAK == msgtype ) { // we're bounced!
|
*pstate = DHCPSTATE_NOTBOUND; // So quit out.
|
*pstate = DHCPSTATE_NOTBOUND; // So quit out.
|
break;
|
break;
|
}
|
}
|
// otherwise it's something else, maybe another offer.
|
// otherwise it's something else, maybe another offer.
|
// Just listen again, which implicitly discards it.
|
// Just listen again, which implicitly discards it.
|
}
|
}
|
break;
|
break;
|
|
|
case DHCPSTATE_REBINDING:
|
case DHCPSTATE_REBINDING:
|
// Just send what you got with a DHCPREQUEST in the message type.
|
// Just send what you got with a DHCPREQUEST in the message type.
|
// Then wait for an ACK. This one is BROADCAST.
|
// Then wait for an ACK. This one is BROADCAST.
|
|
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
// Fill in the BOOTP request - DHCPREQUEST packet
|
xmit->bp_xid = xid;
|
xmit->bp_xid = xid;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_flags = htons(0); // no BROADCAST FLAG
|
xmit->bp_flags = htons(0); // no BROADCAST FLAG
|
// Use the *client* address here:
|
// Use the *client* address here:
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
|
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
|
// These must not be set in this context
|
// These must not be set in this context
|
unset_tag( xmit, TAG_DHCP_REQ_IP );
|
unset_tag( xmit, TAG_DHCP_REQ_IP );
|
unset_tag( xmit, TAG_DHCP_SERVER_ID );
|
unset_tag( xmit, TAG_DHCP_SERVER_ID );
|
// Set all the tags we want to use when sending a packet
|
// Set all the tags we want to use when sending a packet
|
set_default_dhcp_tags( xmit );
|
set_default_dhcp_tags( xmit );
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_REBINDING sending:\n" );
|
diag_printf( "---------DHCPSTATE_REBINDING sending:\n" );
|
show_bootp( intf, xmit );
|
show_bootp( intf, xmit );
|
#endif
|
#endif
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// cleared, as per the RFC. We need the copy because these fields are
|
// cleared, as per the RFC. We need the copy because these fields are
|
// still useful to us (and currently stored in the 'result' structure)
|
// still useful to us (and currently stored in the 'result' structure)
|
xlen = dhcp_size_for_send( xmit );
|
xlen = dhcp_size_for_send( xmit );
|
bcopy( xmit, &xmit2, xlen );
|
bcopy( xmit, &xmit2, xlen );
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_hops = 0;
|
xmit2.bp_hops = 0;
|
if(sendto(s, &xmit2, xlen, 0,
|
if(sendto(s, &xmit2, xlen, 0,
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
|
|
*pstate = DHCPSTATE_REBIND_RECV;
|
*pstate = DHCPSTATE_REBIND_RECV;
|
break;
|
break;
|
|
|
case DHCPSTATE_REBIND_RECV:
|
case DHCPSTATE_REBIND_RECV:
|
// wait for an ACK or a NACK - retry by going back to
|
// wait for an ACK or a NACK - retry by going back to
|
// DHCPSTATE_REBINDING; NACK means go to NOTBOUND.
|
// DHCPSTATE_REBINDING; NACK means go to NOTBOUND.
|
// No answer means just wait for expiry; we tried!
|
// No answer means just wait for expiry; we tried!
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
addrlen = sizeof(rx_addr);
|
addrlen = sizeof(rx_addr);
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
if (recvfrom(s, received, sizeof(struct bootp), 0,
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
(struct sockaddr *)&rx_addr, &addrlen) < 0) {
|
// No packet arrived
|
// No packet arrived
|
// go to the next larger timeout and re-send:
|
// go to the next larger timeout and re-send:
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
|
// If we timed out completely, just give up until EX
|
// If we timed out completely, just give up until EX
|
// expires - retain the lease meanwhile. The normal
|
// expires - retain the lease meanwhile. The normal
|
// lease mechanism will invoke NOTBOUND state as and
|
// lease mechanism will invoke NOTBOUND state as and
|
// when necessary.
|
// when necessary.
|
*pstate = DHCPSTATE_BOUND;
|
*pstate = DHCPSTATE_BOUND;
|
break;
|
break;
|
}
|
}
|
*pstate = DHCPSTATE_REBINDING;
|
*pstate = DHCPSTATE_REBINDING;
|
break;
|
break;
|
}
|
}
|
// Check for well-formed packet with correct termination (not truncated)
|
// Check for well-formed packet with correct termination (not truncated)
|
length = dhcp_size( received );
|
length = dhcp_size( received );
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_REBIND_RECV received:\n" );
|
diag_printf( "---------DHCPSTATE_REBIND_RECV received:\n" );
|
if ( length <= 0 )
|
if ( length <= 0 )
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "WARNING! malformed or truncated packet\n" );
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
|
rx_addr.sin_family,
|
rx_addr.sin_family,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_addr.s_addr,
|
rx_addr.sin_port );
|
rx_addr.sin_port );
|
show_bootp( intf, received );
|
show_bootp( intf, received );
|
#endif
|
#endif
|
if ( length <= 0 )
|
if ( length <= 0 )
|
break;
|
break;
|
if ( CHECK_XID() ) // not the same transaction;
|
if ( CHECK_XID() ) // not the same transaction;
|
break; // listen again...
|
break; // listen again...
|
|
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
if ( 0 == received->bp_siaddr.s_addr ) {
|
// then fill in from the options...
|
// then fill in from the options...
|
int length = sizeof(received->bp_siaddr.s_addr );
|
int length = sizeof(received->bp_siaddr.s_addr );
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
get_bootp_option( received, TAG_DHCP_SERVER_ID,
|
&received->bp_siaddr.s_addr,
|
&received->bp_siaddr.s_addr,
|
&length);
|
&length);
|
}
|
}
|
|
|
// check it was a DHCP reply
|
// check it was a DHCP reply
|
length = sizeof(msgtype);
|
length = sizeof(msgtype);
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
|
&length) ) {
|
&length) ) {
|
if ( DHCPACK == msgtype // Same offer?
|
if ( DHCPACK == msgtype // Same offer?
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
|
&& received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
|
// we like the packet, so reset the timeout for next time
|
// we like the packet, so reset the timeout for next time
|
reset_timeout( &tv, &timeout_scratch );
|
reset_timeout( &tv, &timeout_scratch );
|
// Record the new lease and set up timers &c
|
// Record the new lease and set up timers &c
|
new_lease( received, lease );
|
new_lease( received, lease );
|
*pstate = DHCPSTATE_BOUND;
|
*pstate = DHCPSTATE_BOUND;
|
break;
|
break;
|
}
|
}
|
else if ( DHCPNAK == msgtype ) { // we're bounced!
|
else if ( DHCPNAK == msgtype ) { // we're bounced!
|
*pstate = DHCPSTATE_NOTBOUND; // So back the start of the rigmarole.
|
*pstate = DHCPSTATE_NOTBOUND; // So back the start of the rigmarole.
|
break;
|
break;
|
}
|
}
|
// otherwise it's something else, maybe another offer.
|
// otherwise it's something else, maybe another offer.
|
// Just listen again, which implicitly discards it.
|
// Just listen again, which implicitly discards it.
|
}
|
}
|
break;
|
break;
|
|
|
case DHCPSTATE_BOOTP_FALLBACK:
|
case DHCPSTATE_BOOTP_FALLBACK:
|
// All done with socket
|
// All done with socket
|
close(s);
|
close(s);
|
// And no lease should have become active, but JIC
|
// And no lease should have become active, but JIC
|
no_lease( lease );
|
no_lease( lease );
|
// Re-initialize the interface with the new state
|
// Re-initialize the interface with the new state
|
if ( DHCPSTATE_BOOTP_FALLBACK != oldstate ) {
|
if ( DHCPSTATE_BOOTP_FALLBACK != oldstate ) {
|
// Then need to go down and up
|
// Then need to go down and up
|
do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
|
do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
|
if ( 0 != oldstate ) {
|
if ( 0 != oldstate ) {
|
// Then not called from init_all_network_interfaces()
|
// Then not called from init_all_network_interfaces()
|
// so we must initialize the interface ourselves
|
// so we must initialize the interface ourselves
|
if (!init_net(intf, res)) {
|
if (!init_net(intf, res)) {
|
do_dhcp_down_net( intf, res, pstate, lease );
|
do_dhcp_down_net( intf, res, pstate, lease );
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
// Otherwise, nothing whatsoever to do...
|
// Otherwise, nothing whatsoever to do...
|
return true;
|
return true;
|
|
|
case DHCPSTATE_NOTBOUND:
|
case DHCPSTATE_NOTBOUND:
|
// All done with socket
|
// All done with socket
|
close(s);
|
close(s);
|
// No lease active
|
// No lease active
|
no_lease( lease );
|
no_lease( lease );
|
// Leave interface up so app can tidy.
|
// Leave interface up so app can tidy.
|
return false;
|
return false;
|
|
|
case DHCPSTATE_FAILED:
|
case DHCPSTATE_FAILED:
|
// All done with socket
|
// All done with socket
|
close(s);
|
close(s);
|
// No lease active
|
// No lease active
|
no_lease( lease );
|
no_lease( lease );
|
// Unconditionally down the interface.
|
// Unconditionally down the interface.
|
do_dhcp_down_net( intf, res, &oldstate, lease );
|
do_dhcp_down_net( intf, res, &oldstate, lease );
|
return false;
|
return false;
|
|
|
case DHCPSTATE_DO_RELEASE:
|
case DHCPSTATE_DO_RELEASE:
|
// We have been forced here by external means, to release the
|
// We have been forced here by external means, to release the
|
// lease for graceful shutdown.
|
// lease for graceful shutdown.
|
|
|
// Just send what you got with a DHCPRELEASE in the message
|
// Just send what you got with a DHCPRELEASE in the message
|
// type UNICAST straight to the server. No ACK. Then go to
|
// type UNICAST straight to the server. No ACK. Then go to
|
// NOTBOUND state.
|
// NOTBOUND state.
|
NEW_XID( xid );
|
NEW_XID( xid );
|
xmit->bp_xid = xid;
|
xmit->bp_xid = xid;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_op = BOOTREQUEST;
|
xmit->bp_flags = htons(0); // no BROADCAST FLAG
|
xmit->bp_flags = htons(0); // no BROADCAST FLAG
|
// Use the *client* address here:
|
// Use the *client* address here:
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
|
|
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPRELEASE, 1 );
|
set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPRELEASE, 1 );
|
|
|
// Set unicast address to *server*
|
// Set unicast address to *server*
|
server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
|
server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
|
|
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
#ifdef CYGDBG_NET_DHCP_CHATTER
|
diag_printf( "---------DHCPSTATE_DO_RELEASE sending:\n" );
|
diag_printf( "---------DHCPSTATE_DO_RELEASE sending:\n" );
|
diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
|
diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
|
server_addr.sin_family,
|
server_addr.sin_family,
|
server_addr.sin_addr.s_addr,
|
server_addr.sin_addr.s_addr,
|
server_addr.sin_port );
|
server_addr.sin_port );
|
show_bootp( intf, xmit );
|
show_bootp( intf, xmit );
|
#endif
|
#endif
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// Send back a [modified] copy. Note that some fields are explicitly
|
// cleared, as per the RFC. We need the copy because these fields are
|
// cleared, as per the RFC. We need the copy because these fields are
|
// still useful to us (and currently stored in the 'result' structure)
|
// still useful to us (and currently stored in the 'result' structure)
|
xlen = dhcp_size_for_send( xmit );
|
xlen = dhcp_size_for_send( xmit );
|
bcopy( xmit, &xmit2, xlen );
|
bcopy( xmit, &xmit2, xlen );
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
|
xmit2.bp_hops = 0;
|
xmit2.bp_hops = 0;
|
if(sendto(s, &xmit2, xlen, 0,
|
if(sendto(s, &xmit2, xlen, 0,
|
// UNICAST address of the server:
|
// UNICAST address of the server:
|
(struct sockaddr *)&server_addr,
|
(struct sockaddr *)&server_addr,
|
sizeof(server_addr)) < 0) {
|
sizeof(server_addr)) < 0) {
|
*pstate = DHCPSTATE_FAILED;
|
*pstate = DHCPSTATE_FAILED;
|
break;
|
break;
|
}
|
}
|
|
|
*pstate = DHCPSTATE_NOTBOUND;
|
*pstate = DHCPSTATE_NOTBOUND;
|
break;
|
break;
|
|
|
default:
|
default:
|
no_lease( lease );
|
no_lease( lease );
|
close(s);
|
close(s);
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
/* NOTREACHED */
|
/* NOTREACHED */
|
return false;
|
return false;
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Bring an interface down, failed to initialize it or lease is expired
|
// Bring an interface down, failed to initialize it or lease is expired
|
// Also part of normal startup, bring down for proper reinitialization
|
// Also part of normal startup, bring down for proper reinitialization
|
|
|
int
|
int
|
do_dhcp_down_net(const char *intf, struct bootp *res,
|
do_dhcp_down_net(const char *intf, struct bootp *res,
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
{
|
{
|
struct sockaddr_in *addrp;
|
struct sockaddr_in *addrp;
|
struct ifreq ifr;
|
struct ifreq ifr;
|
int s;
|
int s;
|
|
|
// Ensure clean slate
|
// Ensure clean slate
|
cyg_route_reinit(); // Force any existing routes to be forgotten
|
cyg_route_reinit(); // Force any existing routes to be forgotten
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
if (s < 0) {
|
if (s < 0) {
|
perror("socket");
|
perror("socket");
|
return false;
|
return false;
|
}
|
}
|
|
|
addrp = (struct sockaddr_in *) &ifr.ifr_addr;
|
addrp = (struct sockaddr_in *) &ifr.ifr_addr;
|
|
|
// Remove any existing address
|
// Remove any existing address
|
if ( DHCPSTATE_FAILED == *pstate
|
if ( DHCPSTATE_FAILED == *pstate
|
|| DHCPSTATE_INIT == *pstate
|
|| DHCPSTATE_INIT == *pstate
|
|| 0 == *pstate ) {
|
|| 0 == *pstate ) {
|
// it was configured for broadcast only, "half-up"
|
// it was configured for broadcast only, "half-up"
|
memset(addrp, 0, sizeof(*addrp));
|
memset(addrp, 0, sizeof(*addrp));
|
addrp->sin_family = AF_INET;
|
addrp->sin_family = AF_INET;
|
addrp->sin_len = sizeof(*addrp);
|
addrp->sin_len = sizeof(*addrp);
|
addrp->sin_port = 0;
|
addrp->sin_port = 0;
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
addrp->sin_addr.s_addr = INADDR_ANY;
|
}
|
}
|
else {
|
else {
|
// get the specific address that was used
|
// get the specific address that was used
|
strcpy(ifr.ifr_name, intf);
|
strcpy(ifr.ifr_name, intf);
|
if (ioctl(s, SIOCGIFADDR, &ifr)) {
|
if (ioctl(s, SIOCGIFADDR, &ifr)) {
|
perror("SIOCGIFADDR 1");
|
perror("SIOCGIFADDR 1");
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
strcpy(ifr.ifr_name, intf);
|
strcpy(ifr.ifr_name, intf);
|
if (ioctl(s, SIOCDIFADDR, &ifr)) { /* delete IF addr */
|
if (ioctl(s, SIOCDIFADDR, &ifr)) { /* delete IF addr */
|
perror("SIOCDIFADDR1");
|
perror("SIOCDIFADDR1");
|
}
|
}
|
|
|
#ifdef INET6
|
#ifdef INET6
|
{
|
{
|
int s6;
|
int s6;
|
|
|
s6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
s6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
if (s6 < 0) {
|
if (s6 < 0) {
|
perror("socket AF_INET6");
|
perror("socket AF_INET6");
|
return false;
|
return false;
|
}
|
}
|
// Now delete the ipv6 addr
|
// Now delete the ipv6 addr
|
strcpy(ifr.ifr_name, intf);
|
strcpy(ifr.ifr_name, intf);
|
if (ioctl(s6, SIOCGLIFADDR, &ifr)) {
|
if (ioctl(s6, SIOCGLIFADDR, &ifr)) {
|
perror("SIOCGIFADDR_IN6 1");
|
perror("SIOCGIFADDR_IN6 1");
|
return false;
|
return false;
|
}
|
}
|
|
|
strcpy(ifr.ifr_name, intf);
|
strcpy(ifr.ifr_name, intf);
|
if (ioctl(s6, SIOCDLIFADDR, &ifr)) { /* delete IF addr */
|
if (ioctl(s6, SIOCDLIFADDR, &ifr)) { /* delete IF addr */
|
perror("SIOCDIFADDR_IN61");
|
perror("SIOCDIFADDR_IN61");
|
}
|
}
|
close(s6);
|
close(s6);
|
}
|
}
|
#endif /* IP6 */
|
#endif /* IP6 */
|
|
|
// Shut down interface so it can be reinitialized
|
// Shut down interface so it can be reinitialized
|
ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
|
ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
|
if (ioctl(s, SIOCSIFFLAGS, &ifr)) { /* set ifnet flags */
|
if (ioctl(s, SIOCSIFFLAGS, &ifr)) { /* set ifnet flags */
|
perror("SIOCSIFFLAGS down");
|
perror("SIOCSIFFLAGS down");
|
return false;
|
return false;
|
}
|
}
|
|
|
// All done with socket
|
// All done with socket
|
close(s);
|
close(s);
|
|
|
if ( 0 != *pstate ) // preserve initial state
|
if ( 0 != *pstate ) // preserve initial state
|
*pstate = DHCPSTATE_INIT;
|
*pstate = DHCPSTATE_INIT;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
// Release (relinquish) a leased address - if we have one - and bring down
|
// Release (relinquish) a leased address - if we have one - and bring down
|
// the interface.
|
// the interface.
|
int
|
int
|
do_dhcp_release(const char *intf, struct bootp *res,
|
do_dhcp_release(const char *intf, struct bootp *res,
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
cyg_uint8 *pstate, struct dhcp_lease *lease)
|
{
|
{
|
if ( 0 != *pstate
|
if ( 0 != *pstate
|
&& DHCPSTATE_INIT != *pstate
|
&& DHCPSTATE_INIT != *pstate
|
&& DHCPSTATE_NOTBOUND != *pstate
|
&& DHCPSTATE_NOTBOUND != *pstate
|
&& DHCPSTATE_FAILED != *pstate
|
&& DHCPSTATE_FAILED != *pstate
|
&& DHCPSTATE_BOOTP_FALLBACK != *pstate ) {
|
&& DHCPSTATE_BOOTP_FALLBACK != *pstate ) {
|
*pstate = DHCPSTATE_DO_RELEASE;
|
*pstate = DHCPSTATE_DO_RELEASE;
|
do_dhcp( intf, res, pstate, lease ); // to send the release packet
|
do_dhcp( intf, res, pstate, lease ); // to send the release packet
|
cyg_thread_delay( 100 ); // to let it leave the building
|
cyg_thread_delay( 100 ); // to let it leave the building
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
|
|
#endif // CYGPKG_NET_DHCP
|
#endif // CYGPKG_NET_DHCP
|
|
|
// EOF dhcp_prot.c
|
// EOF dhcp_prot.c
|
|
|