dhcp_prot.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,431 行 · 第 1/4 页

C
1,431
字号
    memcpy(&route.rt_gateway, addrp, sizeof(*addrp));    addrp->sin_addr.s_addr = INADDR_ANY;    memcpy(&route.rt_dst, addrp, sizeof(*addrp));    memcpy(&route.rt_genmask, addrp, sizeof(*addrp));    route.rt_dev = ifrp->ifr_name;    route.rt_flags = RTF_UP|RTF_GATEWAY;    route.rt_metric = 0;    if (ioctl(s, SIOCADDRT, &route)) { /* add route */        if (errno != EEXIST) {            perror("SIOCADDRT 3");            goto out;        }    }    retcode = true; out:    if (s != -1)       close(s);    return retcode;}// ------------------------------------------------------------------------// 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;    }    // If longer, too many tries...    return pstate->secs < CYGNUM_NET_DHCP_MIN_RETRY_TIME; }// ------------------------------------------------------------------------// 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 = 0xffffffff;        lease->t2     = 0xffffffff;        lease->t1     = 0xffffffff;        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 CYGNUM_NET_SNTP_UNICAST_MAXDHCP        TAG_NTP_SERVER        ,     //     NTP Server Addresses(es)#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 CYGOPT_NET_DHCP_OPTION_HOST_NAME{    int nlen = strlen(dhcp_hostname);    if (nlen > 0)    	set_variable_tag( xmit, TAG_HOST_NAME, dhcp_hostname, nlen + 1);}#endif#ifdef CYGOPT_NET_DHCP_OPTION_DHCP_CLIENTID_MAC{	cyg_uint8 id[16+1];	/* sizeof bp_chaddr[] + 1 */	id[0] = 1;  /* 1-byte hardware type: 1=ethernet. */    CYG_ASSERT( xmit->bp_hlen<=(sizeof(id)-1), "HW address invalid" );    memcpy(&id[1], &xmit->bp_chaddr, xmit->bp_hlen);    set_variable_tag( xmit, TAG_DHCP_CLIENTID, id, xmit->bp_hlen+1);}#endif    // 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 = -1, 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;        lease->which = lease->next = 0;    }    s = socket(AF_INET, SOCK_DGRAM, 0);    if (s < 0) {        perror("socket");        goto out;    }    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {        perror("setsockopt");        goto out;    }    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");        goto out;    }    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {        perror("setsockopt SO_REUSEADDR");        goto out;    }    if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {        perror("setsockopt SO_REUSEPORT");        goto out;    }        // 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");        goto out;    }    // 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,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?