Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: f8310ce9d0
Fetching contributors…

Cannot retrieve contributors at this time

232 lines (203 sloc) 7.132 kb
// DHCP look-up functions based on the udp client
// http://www.ietf.org/rfc/rfc2131.txt
//
// Author: Andrew Lindsay
// Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/
//
// Copyright: GPL V2
// See http://www.gnu.org/licenses/gpl.html
#include "EtherCard.h"
#include "net.h"
#define gPB ether.buffer
#define DHCP_BOOTREQUEST 1
#define DHCP_BOOTRESPONSE 2
// DHCP States for access in applications
enum {
DHCP_STATE_INIT,
DHCP_STATE_DISCOVER,
DHCP_STATE_OFFER,
DHCP_STATE_REQUEST,
DHCP_STATE_ACK,
DHCP_STATE_OK,
DHCP_STATE_RENEW,
};
// size 236
typedef struct {
byte op, htype, hlen, hops;
uint32_t xid;
word secs, flags;
byte ciaddr[4], yiaddr[4], siaddr[4], giaddr[4];
byte chaddr[16], sname[64], file[128];
// options
} DHCPdata;
// src port high byte must be a a0 or higher:
#define DHCPCLIENT_SRC_PORT_H 0xe0
#define DHCP_SRC_PORT 67
#define DHCP_DEST_PORT 68
static byte dhcpState;
static char hostname[] = "Arduino-00";
static uint32_t currentXid;
static uint32_t leaseStart;
static uint32_t leaseTime;
static byte* bufPtr;
static const byte allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static void addToBuf (byte b) {
*bufPtr++ = b;
}
static void addBytes (byte len, const byte* data) {
while (len-- > 0)
addToBuf(*data++);
}
static byte dhcp_ready () {
if (dhcpState == DHCP_STATE_OK && millis() >= leaseStart + leaseTime)
dhcpState = DHCP_STATE_RENEW;
return dhcpState == DHCP_STATE_OK;
}
// Main DHCP sending function, either DHCP_STATE_DISCOVER or DHCP_STATE_REQUEST
static void dhcp_send (byte request) {
if (!EtherCard::isLinkUp())
return;
dhcpState = request;
memset(gPB, 0, UDP_DATA_P + sizeof( DHCPdata ));
EtherCard::udpPrepare(DHCP_DEST_PORT, EtherCard::myip, DHCP_SRC_PORT);
EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac);
EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes);
gPB[IP_TOTLEN_L_P]=0x82;
gPB[IP_PROTO_P]=IP_PROTO_UDP_V;
EtherCard::copyIp(gPB + IP_DST_P, allOnes);
// Build DHCP Packet from buf[UDP_DATA_P]
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
dhcpPtr->op = DHCP_BOOTREQUEST;
dhcpPtr->htype = 1;
dhcpPtr->hlen = 6;
dhcpPtr->xid = currentXid;
EtherCard::copyMac(dhcpPtr->chaddr, EtherCard::mymac);
// options defined as option, length, value
bufPtr = gPB + UDP_DATA_P + sizeof( DHCPdata );
// DHCP magic cookie, followed by message type
static byte cookie[] = { 99, 130, 83, 99, 53, 1 };
addBytes(sizeof cookie, cookie);
// addToBuf(53); // DHCP_STATE_DISCOVER, DHCP_STATE_REQUEST
// addToBuf(1); // Length
addToBuf(dhcpState);
// Client Identifier Option, this is the client mac address
addToBuf(61); // Client identifier
addToBuf(7); // Length
addToBuf(0x01); // Ethernet
addBytes(6, EtherCard::mymac);
addToBuf(12); // Host name Option
addToBuf(10);
addBytes(10, (byte*) hostname);
if( dhcpState == DHCP_STATE_REQUEST) {
addToBuf(50); // Request IP address
addToBuf(4);
addBytes(4, EtherCard::myip);
// Request using server ip address
addToBuf(54); // Server IP address
addToBuf(4);
addBytes(4, EtherCard::dhcpip);
}
// Additional info in parameter list - minimal list for what we need
static byte tail[] = { 55, 3, 1, 3, 6, 255 };
addBytes(sizeof tail, tail);
// addToBuf(55); // Parameter request list
// addToBuf(3); // Length
// addToBuf(1); // Subnet mask
// addToBuf(3); // Route/Gateway
// addToBuf(6); // DNS Server
// addToBuf(255); // end option
// packet size will be under 300 bytes
EtherCard::udpTransmit((bufPtr - gPB) - UDP_DATA_P);
}
static void have_dhcpoffer (word len) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
// Offered IP address is in yiaddr
EtherCard::copyIp(EtherCard::myip, dhcpPtr->yiaddr);
// Scan through variable length option list identifying options we want
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
switch (option) {
case 1: EtherCard::copyIp(EtherCard::mymask, ptr);
break;
case 3: EtherCard::copyIp(EtherCard::gwip, ptr);
break;
case 6: EtherCard::copyIp(EtherCard::dnsip, ptr);
break;
case 51: leaseTime = 0;
for (byte i = 0; i<4; i++)
leaseTime = (leaseTime + ptr[i]) << 8;
leaseTime *= 1000; // milliseconds
break;
case 54: EtherCard::copyIp(EtherCard::dhcpip, ptr);
break;
}
ptr += optionLen;
} while (ptr < gPB + len);
dhcp_send(DHCP_STATE_REQUEST);
}
static void have_dhcpack (word /*len*/) {
dhcpState = DHCP_STATE_OK;
leaseStart = millis();
}
static void check_for_dhcp_answer (word len) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
if (len >= 70 && gPB[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT &&
dhcpPtr->op == DHCP_BOOTRESPONSE && dhcpPtr->xid == currentXid ) {
int optionIndex = UDP_DATA_P + sizeof( DHCPdata ) + 4;
if (gPB[optionIndex] == 53) {
switch( gPB[optionIndex+2] ) {
case DHCP_STATE_OFFER: have_dhcpoffer(len); break;
case DHCP_STATE_OK: have_dhcpack(len); break;
}
}
}
}
// use during setup, as this discards all incoming requests until it returns
bool EtherCard::dhcpSetup () {
currentXid = millis();
// Set a unique hostname, use Arduino-?? with last octet of mac address
hostname[8] = 'A' + (mymac[5] >> 4);
hostname[9] = 'A' + (mymac[5] & 0x0F);
myip[0] = 0; // force invalid IP address
// Enable reception of broadcast packets as some DHCP servers
// use this to send responses
enableBroadcast();
for (byte i = 0; i < 3; ++i) {
dhcpState = DHCP_STATE_INIT;
word start = millis();
while (myip[0] == 0 && (word) (millis() - start) < 10000) {
if (!isLinkUp())
continue;
word len = packetReceive();
if (dhcpState != DHCP_STATE_INIT && (len == 0 || packetLoop(len) > 0))
continue;
switch (dhcpState) {
case DHCP_STATE_INIT:
case DHCP_STATE_RENEW:
dhcp_send(DHCP_STATE_DISCOVER);
break;
case DHCP_STATE_DISCOVER:
case DHCP_STATE_REQUEST:
check_for_dhcp_answer(len);
break;
case DHCP_STATE_OK:
; //TODO wait for lease expiration
}
if (myip[0] != 0) {
if (gwip[0] != 0)
setGwIp(gwip);
disableBroadcast();
return true;
}
}
}
disableBroadcast();
return false;
}
bool EtherCard::dhcpExpired () {
return !dhcp_ready() && dhcpState == DHCP_STATE_RENEW;
}
Jump to Line
Something went wrong with that request. Please try again.