⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dhcp_prot.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 4 页
字号:
        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 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 ,&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 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 ^= (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 + -