void dhcp_send_request(uint32_t xid, uint32_t requested_ip, uint32_t server_ip, int offer_answer) { DhcpHead dhcp_request; uint8_t *op; memset(&dhcp_request, 0, sizeof(DhcpHead)); dhcp_request.op = BOOTREQUEST; dhcp_request.htype = 1; /* ethernet */ dhcp_request.hlen = 6; dhcp_request.hops = 0; dhcp_request.xid = xid; if (offer_answer) dhcp_request.ciaddr = requested_ip; dhcp_request.siaddr = server_ip; memcpy(dhcp_request.chaddr, simple_net_get_mac(), 6); dhcp_request.options[0] = 99; dhcp_request.options[1] = 130; dhcp_request.options[2] = 83; dhcp_request.options[3] = 99; op = dhcp_create_option_simple(dhcp_request.options+4, OPTION_DHCP_MESSAGE_TYPE, 1, DHCPREQUEST); op = dhcp_create_option_simple(op, OPTION_DHCP_REQUESTED_IP, 4, ntohl(requested_ip)); op = dhcp_create_option_simple(op, OPTION_DHCP_SERVER_IDENTIFIER, 4, ntohl(dhcp_request.siaddr)); op[0] = OPTION_END; dhcp_send(&dhcp_request, requested_ip, 0xffffffff, _bmac); }
void dhcp_discover(void) { DhcpHead dhcp; uint8_t *op; memset(&dhcp, 0, sizeof(DhcpHead)); dhcp.op = BOOTREQUEST; dhcp.htype = 1; /* ethernet */ dhcp.hlen = 6; dhcp.hops = 0; dhcp.xid = rand(); memcpy(dhcp.chaddr, simple_net_get_mac(), 6); /* DHCP Magic Cookie */ dhcp.options[0] = 99; dhcp.options[1] = 130; dhcp.options[2] = 83; dhcp.options[3] = 99; op = dhcp_create_option_simple(dhcp.options+4, OPTION_DHCP_MESSAGE_TYPE, 1, DHCPDISCOVER); op[0] = OPTION_END; dhcp_send(&dhcp, 0, 0xffffffff, _bmac); }
// ------------------------------------------------ // Function: dhcp_discover() // ------------------------------------------------ // Input: - // Output: TRUE if succesful // ------------------------------------------------ // Description: Find out a DHCP server address // ------------------------------------------------ BOOL dhcp_discover(void) { BYTE opt; PPBUF pbuf; retry = 0; while(retry < MAX_RETRIES) { if(!dhcp_send(DHCPDISCOVER, TRUE)) return FALSE; os_set_timeout(TIMEOUT_DHCP_DISCOVER); if(udp_listen(SOCKET_DHCP, UDP_DHCP_CLI)) { pbuf = udp_read(SOCKET_DHCP); opt = parse_dhcp(pbuf); release_buffer(pbuf); if(opt == DHCPOFFER) break; } retry++; } if(retry >= MAX_RETRIES) return FALSE; return TRUE; }
// Update the DHCP lease if necessary. // // Returns: True Success // False Otherwise // bool EtherCard::dhcpLease () { // If leaseStart is invalid, just quit if (leaseStart == 0) return false; // Find out when we are uint32_t now = millis(); uint32_t renew = leaseStart + leaseTime / 2; uint32_t rebind = leaseStart + (leaseTime / 8 ) * 7; uint32_t restart = leaseStart + leaseTime; uint32_t newStart; byte rc = false; // The unsigned arithmetic makes rollover calculations tricky. // Re-assign leaseStart to (leaseStart + leaseTime) as soon as // the new tick is large enough. OTOH, rollover is ~50 days with // a msec tick - do we care ??? if (now < leaseStart) { newStart = leaseTime - ((UINT32_MAX - leaseStart) % leaseTime); if (now < newStart) { if (now > leaseTime / 2) renew = newStart - leaseTime / 2; if (now > (leaseTime / 8)) rebind = newStart - leaseTime / 8; restart = newStart; } else leaseStart = newStart; } // Update dhcpState according to the timers switch (dhcpState) { case DHCP_STATE_BAD: case DHCP_STATE_INIT: case DHCP_STATE_SELECT: case DHCP_STATE_REQUEST: break; case DHCP_STATE_BOUND: // At > 50% of the lease time, // move to DHCP_STATE_RENEW if (now > renew) dhcpState = DHCP_STATE_RENEW; break; case DHCP_STATE_RENEW: // At > 87.5% of the lease time, // move to DHCP_STATE_REBIND if (now > rebind) dhcpState = DHCP_STATE_REBIND; dhcp_send(DHCP_MSG_REQUEST, false); rc = true; break; case DHCP_STATE_REBIND: // At a 100% of the lease time, // move to DHCP_STATE_INIT if (now > restart) dhcpState = DHCP_STATE_INIT; else // This is a broadcast message. dhcp_send(DHCP_MSG_REQUEST, true); rc = true; break; default: // Never happen break; } // Call the main DHCP state machine // to handle the actual work. return rc ? dhcp_fsm() : true; }
// This handles both the initialisation of DHCP, and subsequent // renegotiations. Lacking a dedicated timer, EtherCard::dhcpLease() // must be called on a regular basis to keep things on track. // // Returns: True Success // False Otherwise // static bool dhcp_fsm () { // Enable reception of broadcast packets as some DHCP servers // use this to send responses. Use only in DHCP_STATE_INIT ??? EtherCard::enableBroadcast(); // We typically wait up to 20 seconds for an answer uint32_t end = millis() + DHCP_WAIT; while (dhcpState != DHCP_STATE_BOUND && millis() < end) { byte rc = DHCP_STATE_BAD; word len = 0; // We have no hardware link, so no further point if (!EtherCard::isLinkUp()) continue; // Get a packet, and check it's ok. packetReceive returns the // sum of the source address (6), the destination address (6), // the type/length (2), and the data/padding (46-1500) fields. // The trailing CRC field (4) is dropped by the software. // packetLoop(), unfortunately, is strange and undocumented. if (dhcpState != DHCP_STATE_INIT) { len = EtherCard::packetReceive(); // Reject inadequate packets if (len == 0 || EtherCard::packetLoop(len) > 0) continue; #if 0 // These are a waste of space // Reject ARP packets if (gPB[ETHTYPE_IP_H_V] == ETHTYPE_ARP_H_V && gPB[ETHTYPE_IP_L_V] == ETHTYPE_ARP_L_V) continue; // Reject ICMP packets (why are they here ???) if (gPB[IP_PROTO_P] == IP_PROTO_ICMP_V) continue; #endif // Reject everything but UDP packets if (gPB[IP_PROTO_P] != IP_PROTO_UDP_V) continue; } // Switch between DHCP states. This is a pretty // minimal DHCP state machine, but it should be // reliable, if slow. switch (dhcpState) { case DHCP_STATE_INIT: EtherCard::copyIp(EtherCard::myip, allZeros); EtherCard::copyIp(EtherCard::dhcpip, allZeros); currentXid = millis(); currentXid = (currentXid << 16) + millis(); dhcp_send(DHCP_MSG_DISCOVER, true); dhcpState = DHCP_STATE_SELECT; leaseStart = 0; // Set an invalid start time break; case DHCP_STATE_SELECT: if (check_for_dhcp_answer(len) == DHCP_STATE_SELECT) { parse_dhcpoffer(len); dhcp_send(DHCP_MSG_REQUEST, false); dhcpState = DHCP_STATE_REQUEST; } else dhcpState = DHCP_STATE_INIT; break; case DHCP_STATE_REQUEST: if (check_for_dhcp_answer(len) == DHCP_STATE_REQUEST) { dhcpState = DHCP_STATE_BOUND; leaseStart = millis(); } else dhcpState = DHCP_STATE_INIT; break; case DHCP_STATE_BOUND: // Lease timed at 50% : move to DHCP_STATE_RENEW // We're only bounced out of this state by a timer. // Otherwise just hang on to the lease. break; case DHCP_STATE_RENEW: // Lease timed at 87.5% : move to DHCP_STATE_REBIND // Otherwise just hang on to the lease if possible. rc = check_for_dhcp_answer(len); if (rc == DHCP_STATE_BOUND) { dhcpState = DHCP_STATE_BOUND; leaseStart = millis(); } else if (rc == DHCP_STATE_INIT) dhcpState = DHCP_STATE_INIT; break; case DHCP_STATE_REBIND: // Lease timed at 100% : move to DHCP_STATE_INIT // Otherwise just hang on to the lease if possible. rc = check_for_dhcp_answer(len); if (rc == DHCP_STATE_BOUND) { dhcpState = DHCP_STATE_BOUND; leaseStart = millis(); } else if (rc == DHCP_STATE_INIT) dhcpState = DHCP_STATE_INIT; break; // Never happen default: dhcpState = DHCP_STATE_INIT; break; } } EtherCard::disableBroadcast(); // Did we get here with an IP or a timeout? if (EtherCard::myip[0] != 0) { if (EtherCard::gwip[0] != 0) EtherCard::setGwIp(EtherCard::gwip); return true; } return false; }