?? dhcp_prot.c
字號:
if (ioctl(s, SIOCADDRT, &route))
{ /* add route */
if (errno != EEXIST)
{
perror("SIOCADDRT 3");
return false;
}
}
close(s);
return true;
}
// ------------------------------------------------------------------------
// DHCP retransmission timeouts and number of tries
//
// To work better with simulated failures (or real ones!) so that the rest
// of the system is tested, rather than DHCP renewal failures pulling
// everything down, we try a little more zealously than the RFC suggests.
static unsigned char timeout_random = 0;
struct timeout_state
{
unsigned int secs;
int countdown;
};
static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
{
timeout_random++;
pstate->countdown = 4; // initial fast retries
pstate->secs = 3 + (timeout_random & 3);
ptv->tv_sec = 0;
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 )
{
if ( 0 < pstate->countdown-- )
return true;
if ( 0 == ptv->tv_sec )
ptv->tv_sec = pstate->secs;
else
{
timeout_random++;
pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
pstate->countdown = 2; // later fast retries
ptv->tv_sec = 0;
}
return (pstate->secs < DHCP_TIMEOUT_IN_SEC); // If longer, too many tries...
}
// ------------------------------------------------------------------------
// Lease expiry and alarms to notify it
static cyg_alarm_t alarm_function;
static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
{
struct dhcp_lease *lease = (struct dhcp_lease *)data;
lease->which |= lease->next;
if ( lease->needs_attention )
cyg_semaphore_post( lease->needs_attention );
// Step the lease on into its next state of being alarmed ;-)
if ( lease->next & DHCP_LEASE_EX )
{
cyg_alarm_disable( alarm );
}
else if ( lease->next & DHCP_LEASE_T2 )
{
lease->next = DHCP_LEASE_EX;
cyg_alarm_initialize( lease->alarm, lease->expiry, 0 );
cyg_alarm_enable( lease->alarm );
}
else if ( lease->next & DHCP_LEASE_T1 )
{
lease->next = DHCP_LEASE_T2;
cyg_alarm_initialize( lease->alarm, lease->t2, 0 );
cyg_alarm_enable( lease->alarm );
}
}
static inline void no_lease( struct dhcp_lease *lease )
{
if ( lease->alarm )
{
// Already set: delete this.
cyg_alarm_disable( lease->alarm );
cyg_alarm_delete( lease->alarm );
lease->alarm = 0;
}
}
static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
{
cyg_tick_count_t now = cyg_current_time();
cyg_tick_count_t then;
cyg_uint32 tag = 0;
cyg_uint32 expiry_then;
cyg_resolution_t resolution =
cyg_clock_get_resolution(cyg_real_time_clock());
cyg_handle_t h;
unsigned int length;
// Silence any jabbering from past lease on this interface
no_lease( lease );
lease->which = lease->next = 0;
cyg_clock_to_counter(cyg_real_time_clock(), &h);
cyg_alarm_create( h, alarm_function, (cyg_addrword_t)lease,
&lease->alarm, &lease->alarm_obj );
// extract the lease time and scale it &c to now.
length = sizeof(tag);
if (!get_bootp_option( bootp, TAG_DHCP_LEASE_TIME, &tag ))
tag = 0xffffffff;
if ( 0xffffffff == tag )
{
lease->expiry = 0xffffffffffffffff;
lease->t2 = 0xffffffffffffffff;
lease->t1 = 0xffffffffffffffff;
return ; // it's an infinite lease, hurrah!
}
then = (cyg_uint64)(ntohl(tag));
expiry_then = then;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->expiry = now + then;
length = sizeof(tag);
if (get_bootp_option( bootp, TAG_DHCP_REBIND_TIME, &tag ))
then = (cyg_uint64)(ntohl(tag));
else
then = expiry_then - expiry_then / 4;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->t2 = now + then;
length = sizeof(tag);
if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag ))
then = (cyg_uint64)(ntohl(tag));
else
then = expiry_then / 2;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->t1 = now + then;
#if 0 // for testing this mechanism
lease->expiry = now + 5000; // 1000 here makes for failure in the DHCP test
lease->t2 = now + 3500;
lease->t1 = now + 2500;
#endif
#ifdef CYGDBG_NET_DHCP_CHATTER
diag_printf("new_lease:\n");
diag_printf(" expiry = %d\n", lease->expiry);
diag_printf(" t1 = %d\n", lease->t1);
diag_printf(" t2 = %d\n", lease->t2);
#endif
lease->next = DHCP_LEASE_T1;
cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
cyg_alarm_enable( lease->alarm );
}
// ------------------------------------------------------------------------
// Set all the tags we want to use when sending a packet.
// This has expanded to a large, explicit set to interwork better
// with a variety of DHCP servers.
static void set_default_dhcp_tags( struct bootp *xmit )
{
// Explicitly request full set of params that are default for LINUX
// dhcp servers, but not default for others. This is rather arbitrary,
// but it preserves behaviour for people using those servers.
// Perhaps configury of this set will be needed in future?
//
// Here's the set:
static cyg_uint8 req_list[] = {
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
#else
TAG_DHCP_SERVER_ID , // DHCP server id: 10.16.19.66
TAG_DHCP_LEASE_TIME , // DHCP time 51: 60
TAG_DHCP_RENEWAL_TIME , // DHCP time 58: 30
TAG_DHCP_REBIND_TIME , // DHCP time 59: 52
TAG_SUBNET_MASK , // subnet mask: 255.255.255.0
// TAG_GATEWAY , // gateway: 10.16.19.66
TAG_DOMAIN_SERVER , // domain server: 10.16.19.66
TAG_DOMAIN_NAME , // domain name: hmt10.cambridge.redhat.com
TAG_IP_BROADCAST , // IP broadcast: 10.16.19.255
#endif
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
#endif
};
if ( req_list[0] ) // So that one may easily turn it all off by configury
set_variable_tag( xmit, TAG_DHCP_PARM_REQ_LIST,
&req_list[0], sizeof( req_list ) );
#ifdef DHCP_MAGIC_OPTION
set_variable_tag( xmit, 232, DHCP_MAGIC_OPTION, 7 );
#endif
// Explicitly specify our max message size.
#ifndef BP_MAXPKTSZ
#define BP_MAXPKTSZ 364
#endif
set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MAXPKTSZ, 2 );
}
// ------------------------------------------------------------------------
// the DHCP state machine - this does all the work
int
do_dhcp(const char *intf, struct bootp *res,
cyg_uint8 *pstate, struct dhcp_lease *lease)
{
struct ifreq ifr;
struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
int s, addrlen;
int one = 1;
unsigned char mincookie[] = {99, 130, 83, 99, 255} ;
struct timeval tv;
struct timeout_state timeout_scratch;
cyg_uint8 oldstate = *pstate;
cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
unsigned int length;
cyg_uint32 xid;
#define CHECK_XID() ( /* and other details */ \
received->bp_xid != xid || /* not the same transaction */ \
received->bp_htype != xmit->bp_htype || /* not the same ESA type */ \
received->bp_hlen != xmit->bp_hlen || /* not the same length */ \
bcmp( &received->bp_chaddr, &xmit->bp_chaddr, xmit->bp_hlen ) \
)
// IMPORTANT: xmit is the same as res throughout this; *received is a
// scratch buffer for reception; its contents are always copied to res
// when we are happy with them. So we always transmit from the
// existing state.
struct bootp rx_local;
struct bootp *received = &rx_local;
struct bootp *xmit = res;
struct bootp xmit2;
int xlen;
// 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.
if ( DHCPSTATE_INIT == oldstate
|| DHCPSTATE_FAILED == oldstate
|| 0 == oldstate )
{
// either explicit init state or the beginning of time or retry
if ( ! bring_half_up( intf, &ifr ) )
return false;
*pstate = DHCPSTATE_INIT;
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
perror("socket");
return false;
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)))
{
perror("setsockopt");
return false;
}
memset((char *) &cli_addr, 0, sizeof(cli_addr));
cli_addr.sin_family = AF_INET;
cli_addr.sin_len = sizeof(cli_addr);
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(IPPORT_BOOTPC);
memset((char *) &broadcast_addr, 0, sizeof(broadcast_addr));
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_len = sizeof(broadcast_addr);
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
broadcast_addr.sin_port = htons(IPPORT_BOOTPS);
memset((char *) &server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_len = sizeof(server_addr);
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // overwrite later
server_addr.sin_port = htons(IPPORT_BOOTPS);
if (bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0)
{
perror("bind error");
return false;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
{
perror("setsockopt SO_REUSEADDR");
return false;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)))
{
perror("setsockopt SO_REUSEPORT");
return false;
}
// 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
// when all is well, or utterly failed.
reset_timeout( &tv, &timeout_scratch );
// Choose a new XID: first get the ESA as a basis:
strcpy(&ifr.ifr_name[0], intf);
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
{
perror("SIOCGIFHWADDR 2");
return false;
}
// Choose from scratch depending on ifr_hwaddr...[]
xid = ifr.ifr_hwaddr.sa_data[5];
xid |= (ifr.ifr_hwaddr.sa_data[4]) << 8;
xid |= (ifr.ifr_hwaddr.sa_data[3]) << 16;
xid |= (ifr.ifr_hwaddr.sa_data[2]) << 24;
xid ^= (arc4random() & 0xffff0000);
// Avoid adjacent ESAs colliding by increment
#define NEW_XID(_xid) CYG_MACRO_START (_xid)+= 0x10000; CYG_MACRO_END
while ( 1 )
{
// If we are active rather than in the process of shutting down,
// check for any lease expiry every time round, so that alarms
// *can* change the course of events even when already renewing,
// for example.
if ( DHCPSTATE_DO_RELEASE != *pstate
&& DHCPSTATE_NOTBOUND != *pstate
&& DHCPSTATE_FAILED != *pstate )
{
cyg_uint8 lease_state;
cyg_scheduler_lock();
lease_state = lease->which;
lease->which = 0; // flag that we have noticed it
cyg_scheduler_unlock();
if ( lease_state & DHCP_LEASE_EX )
{
// then the lease has expired completely!
*pstate = DHCPSTATE_NOTBOUND;
}
else if ( lease_state & DHCP_LEASE_T2 )
{
// Time to renew
reset_timeout( &tv, &timeout_scratch ); // next conversation
*pstate = DHCPSTATE_REBINDING;
}
else if ( lease_state & DHCP_LEASE_T1 )
{
// Time to renew
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -