📄 dhcp_prot.c
字号:
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 < 100; // If longer, too many tries...}// ------------------------------------------------------------------------// Lease expiry and alarms to notify itstatic 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 ,&length)) 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, &length )) 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, &length )) 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 ) ); // Explicitly specify our max message size. set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );}// ------------------------------------------------------------------------// the DHCP state machine - this does all the workintdo_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 ^= (cyg_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 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -