📄 dhcp_prot.c
字号:
// Choose a new XID: first get the ESA as a basis: // 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 ) {#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf("do_dhcp: state %d\n",*pstate);#endif // 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 reset_timeout( &tv, &timeout_scratch ); // next conversation *pstate = DHCPSTATE_RENEWING; } } switch ( *pstate ) { case DHCPSTATE_INIT: // Send the DHCPDISCOVER packet // Fill in the BOOTP request - DHCPDISCOVER packet bzero(xmit, sizeof(*xmit)); xmit->bp_op = BOOTREQUEST; xmit->bp_htype = HTYPE_ETHERNET; xmit->bp_hlen = IFHWADDRLEN; xmit->bp_xid = xid; xmit->bp_secs = 0; xmit->bp_flags = htons(0x8000); // BROADCAST FLAG bcopy(ifr.ifr_hwaddr.sa_data, &xmit->bp_chaddr, xmit->bp_hlen); bcopy(mincookie, xmit->bp_vend, sizeof(mincookie)); // remove the next line to test ability to handle bootp packets. set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPDISCOVER, 1 ); // Set all the tags we want to use when sending a packet set_default_dhcp_tags( xmit );#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf( "---------DHCPSTATE_INIT sending:\n" ); show_bootp( intf, xmit );#endif#ifdef ORIG_DHCP_CODE if (sendto(s, xmit, dhcp_size_for_send(xmit), 0, (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0)#else if(dhcp_send_broadcast_to_driver(dhcp_size_for_send(xmit),(cyg_uint8 *) xmit,ifr.ifr_hwaddr.sa_data) < 0 )#endif { *pstate = DHCPSTATE_FAILED;#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf("do_dhcp: send packet ERROR N %d\n", errno);#endif break; } seen_bootp_reply = 0; *pstate = DHCPSTATE_SELECTING; break; case DHCPSTATE_SELECTING: // This is a separate state so that we can listen again // *without* retransmitting. // listen for the DHCPOFFER reply setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); addrlen = sizeof(rx_addr); if (recvfrom(s, received, sizeof(struct bootp), 0, (struct sockaddr *)&rx_addr, &addrlen) < 0) { // No packet arrived (this time) if ( seen_bootp_reply ) { // then already have a bootp reply // Save the good packet in *xmit bcopy( received, xmit, dhcp_size(received) ); *pstate = DHCPSTATE_BOOTP_FALLBACK; NEW_XID( xid ); // Happy to advance, so new XID reset_timeout( &tv, &timeout_scratch ); break; } // go to the next larger timeout and re-send: if ( ! next_timeout( &tv, &timeout_scratch ) ) { *pstate = DHCPSTATE_FAILED;#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf("do_dhcp: no receive \n");#endif break; } *pstate = DHCPSTATE_INIT; // to retransmit break; } // Check for well-formed packet with correct termination (not truncated) length = dhcp_size( received );#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf( "---------DHCPSTATE_SELECTING received:\n" ); if ( length <= 0 ) diag_printf( "WARNING! malformed or truncated packet\n" ); diag_printf( "...rx_addr is family %d, addr %08x, port %d\n", rx_addr.sin_family, rx_addr.sin_addr.s_addr, rx_addr.sin_port ); show_bootp( intf, received );#endif if ( length <= 0 ) break; if ( CHECK_XID() ) // XID and ESA matches? break; // listen again... if ( 0 == received->bp_siaddr.s_addr ) { // then fill in from the options... length = sizeof(received->bp_siaddr.s_addr); get_bootp_option( received, TAG_DHCP_SERVER_ID, &received->bp_siaddr.s_addr); } // see if it was a DHCP reply or a bootp reply; it could be // either. length = sizeof(msgtype); if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype) ) { if ( DHCPOFFER == msgtype ) { // all is well // Save the good packet in *xmit bcopy( received, xmit, dhcp_size(received) ); // we like the packet, so reset the timeout for next time reset_timeout( &tv, &timeout_scratch ); *pstate = DHCPSTATE_REQUESTING; NEW_XID( xid ); // Happy to advance, so new XID } } else // No TAG_DHCP_MESS_TYPE entry so it's a bootp reply seen_bootp_reply = 1; // (keep the bootp packet in received) // If none of the above state changes occurred, we got a packet // that "should not happen", OR we have a bootp reply in our // hand; so listen again with the same timeout, without // retrying the send, in the hope of getting a DHCP reply. break; case DHCPSTATE_REQUESTING: // Just send what you got with a DHCPREQUEST in the message type. // then wait for an ACK in DHCPSTATE_REQUEST_RECV. // Fill in the BOOTP request - DHCPREQUEST packet xmit->bp_xid = xid; xmit->bp_op = BOOTREQUEST; xmit->bp_flags = htons(0x8000); // BROADCAST FLAG set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 ); // Set all the tags we want to use when sending a packet set_default_dhcp_tags( xmit ); // And this will be a new one: set_fixed_tag( xmit, TAG_DHCP_REQ_IP, ntohl(xmit->bp_yiaddr.s_addr), 4 );#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf( "---------DHCPSTATE_REQUESTING sending:\n" ); show_bootp( intf, xmit );#endif // Send back a [modified] copy. Note that some fields are explicitly // cleared, as per the RFC. We need the copy because these fields are // still useful to us (and currently stored in the 'result' structure) xlen = dhcp_size_for_send( xmit ); bcopy( xmit, &xmit2, xlen ); xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0; xmit2.bp_hops = 0;#ifdef ORIG_DHCP_CODE if (sendto(s, &xmit2, xlen, 0, (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0)#else if(dhcp_send_broadcast_to_driver(xlen,(cyg_uint8 *) &xmit2,ifr.ifr_hwaddr.sa_data) < 0 )#endif { *pstate = DHCPSTATE_FAILED; break; } *pstate = DHCPSTATE_REQUEST_RECV; break; case DHCPSTATE_REQUEST_RECV: // wait for an ACK or a NACK - retry by going back to // DHCPSTATE_REQUESTING; NACK means go back to INIT. setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); addrlen = sizeof(rx_addr); if (recvfrom(s, received, sizeof(struct bootp), 0, (struct sockaddr *)&rx_addr, &addrlen) < 0) { // No packet arrived // go to the next larger timeout and re-send: if ( ! next_timeout( &tv, &timeout_scratch ) ) { *pstate = DHCPSTATE_FAILED; break; } *pstate = DHCPSTATE_REQUESTING; break; } // Check for well-formed packet with correct termination (not truncated) length = dhcp_size( received );#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf( "---------DHCPSTATE_REQUEST_RECV received:\n" ); if ( length <= 0 ) diag_printf( "WARNING! malformed or truncated packet\n" ); diag_printf( "...rx_addr is family %d, addr %08x, port %d\n", rx_addr.sin_family, rx_addr.sin_addr.s_addr, rx_addr.sin_port ); show_bootp( intf, received );#endif if ( length <= 0 ) break; if ( CHECK_XID() ) // not the same transaction; break; // listen again... if ( 0 == received->bp_siaddr.s_addr ) { // then fill in from the options... length = sizeof(received->bp_siaddr.s_addr ); get_bootp_option( received, TAG_DHCP_SERVER_ID, &received->bp_siaddr.s_addr); } // check it was a DHCP reply length = sizeof(msgtype); if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype) ) { if ( DHCPACK == msgtype // Same offer & server? && received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr && received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) { // we like the packet, so reset the timeout for next time reset_timeout( &tv, &timeout_scratch ); // Record the new lease and set up timers &c new_lease( received, lease ); *pstate = DHCPSTATE_BOUND; break; } if ( DHCPNAK == msgtype // Same server? && received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) { // we're bounced! *pstate = DHCPSTATE_INIT; // So back the start of the rigmarole. NEW_XID( xid ); // Unhappy to advance, so new XID reset_timeout( &tv, &timeout_scratch ); break; } // otherwise it's something else, maybe another offer, or a bogus // NAK from someone we are not asking! // Just listen again, which implicitly discards it. } break; case DHCPSTATE_BOUND: // We are happy now, we have our address. // All done with socket close(s); // Re-initialize the interface with the new state if ( DHCPSTATE_BOUND != oldstate ) { // Then need to go down and up do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used if ( 0 != oldstate ) { // Then not called from init_all_network_interfaces() // so we must initialize the interface ourselves if (!init_net(intf, res)) { do_dhcp_down_net( intf, res, pstate, lease ); *pstate = DHCPSTATE_FAILED; return false; } } } // Otherwise, nothing whatsoever to do... return true; case DHCPSTATE_RENEWING: // Just send what you got with a DHCPREQUEST in the message // type UNICAST straight to the server. Then wait for an ACK. // Fill in the BOOTP request - DHCPREQUEST packet xmit->bp_xid = xid; xmit->bp_op = BOOTREQUEST; xmit->bp_flags = htons(0); // No BROADCAST FLAG // Use the *client* address here: xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr; set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 ); // These must not be set in this context unset_tag( xmit, TAG_DHCP_REQ_IP ); unset_tag( xmit, TAG_DHCP_SERVER_ID ); // Set all the tags we want to use when sending a packet set_default_dhcp_tags( xmit ); // Set unicast address to *server* server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;#ifdef CYGDBG_NET_DHCP_CHATTER diag_printf( "---------DHCPSTATE_RENEWING sending:\n" ); diag_printf( "UNICAST to family %d, addr %08x, port %d\n", server_addr.sin_family, server_addr.sin_addr.s_addr, server_addr.sin_port ); show_bootp( intf, xmit );#endif // Send back a [modified] copy. Note that some fields are explicitly // cleared, as per the RFC. We need the copy because these fields are // still useful to us (and currently stored in the 'result' structure) xlen = dhcp_size_for_send(xmit); bcopy( xmit, &xmit2, xlen ); xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0; xmit2.bp_hops = 0; if (sendto(s, &xmit2, xlen, 0, // UNICAST address of the server: (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { *pstate = DHCPSTATE_FAILED; break; } *pstate = DHCPSTATE_RENEW_RECV; break; case DHCPSTATE_RENEW_RECV: // wait for an ACK or a NACK - retry by going back to // DHCPSTATE_RENEWING; NACK means go to NOTBOUND. // No answer means just wait for T2, to broadcast. setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); addrlen = sizeof(rx_addr); if (recvfrom(s, received, sizeof(struct bootp), 0, (struct sockaddr *)&rx_addr, &addrlen) < 0) { // No packet arrived // go to the next larger timeout and re-send: if ( ! next_timeout( &tv, &timeout_scratch ) ) { // If we timed out completely, just give up until T2 // expires - retain the lease meanwhile. The normal // lease mechanism will invoke REBINDING as and when // necessary. *pstate = DHCPSTATE_BOUND; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -