BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
d48e86e
Branches
Tags
master
flaneth
firmware
dhcp.c
header fix
Stefan Schuermans
commited
d48e86e
at 2012-05-02 18:39:39
dhcp.c
Blame
History
Raw
/* flaneth - flash and ethernet Copyright (C) 2007-2012 Stefan Schuermans <stefan@schuermans.info> Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html a BlinkenArea project - http://www.blinkenarea.org/ */ #include <string.h> #include "config.h" #include "checksum.h" #include "debug.h" #include "dhcp.h" #include "ethernet.h" #include "ip.h" #include "macros.h" #include "nethelp.h" #include "random.h" #include "udp.h" // configuration static const unsigned char DhcpRetrySecsMax = 10; // avarage timeout // after which to // retry DHCP action static const unsigned char DhcpRetriesMax = 5; // maximum number of retries // before aborting DHCP // action static const unsigned long DhcpLeaseRestMin = 300; // when to ask to // extend lease static const unsigned char DhcpLeaseRenewFraction = 8; // to renw lease when // only this fraction // of lease is left // DHCP tick counter to get full seconds static unsigned char DhcpTicks = 0; // time of current DHCP action static unsigned char DhcpInProgress = 0; // if DHCP action is in // progress static unsigned char DhcpRetries = 0; // number of retries left static unsigned char DhcpRetrySecs = 0; // rest of time for current retry // current XId static unsigned char DhcpHaveXId = 0; // if an XId exists at the moment static unsigned long DhcpXId = 0; // the current XId data static unsigned int DhcpXIdSecs = 0; // time of XId // DHCP server and lease time static unsigned char DhcpActive = 0; // if own IP is leased from DHCP static unsigned char DhcpServer[4]; // IP address of DHCP server own IP // is leased from static unsigned long DhcpLeaseTime = 0; // total lease time static unsigned long DhcpLeaseRest = 0; // rest of lease time // send a DHCP packet // pData must point to a struct DhcpPacket with IpHdr.Dest, DhcpHdr.XId // DhcpHdr.Secs, DhcpHdr.Flags and DhcpHdr.YIAddr already initialized static void DhcpSend(unsigned char *pData, unsigned short Length) // (extern) { struct DhcpPacket *pDhcpPack; // packet too short if (Length < sizeof(struct DhcpPacket)) return; // convert pointer to DHCP packet // (this saves us from always casting pData) pDhcpPack = (struct DhcpPacket *)pData; debug_dhcp_printf("send xid=%08lX len=%d", ntohl(pDhcpPack->DhcpHdr.XId), Length); // we are DHCP client // - source port must be 68 // - destination port must be 67 pDhcpPack->UdpHdr.SrcPort = htons(68); pDhcpPack->UdpHdr.DestPort = htons(67); // set up DHCP header fields as client pDhcpPack->DhcpHdr.Op = 1; pDhcpPack->DhcpHdr.HType = 1; pDhcpPack->DhcpHdr.HLen = 6; pDhcpPack->DhcpHdr.HOps = 0; memset(pDhcpPack->DhcpHdr.CIAddr, 0, 4); memset(pDhcpPack->DhcpHdr.SIAddr, 0, 4); memset(pDhcpPack->DhcpHdr.GIAddr, 0, 4); mac_cpy(pDhcpPack->DhcpHdr.CHAddr, ConfigMac); memset(pDhcpPack->DhcpHdr.CHAddr + 6, 0, 10); memset(pDhcpPack->DhcpHdr.SName, 0, 64); memset(pDhcpPack->DhcpHdr.File, 0, 128); pDhcpPack->DhcpHdr.MCookie = htonl(0x63825363); // send DHCP packet UdpSend(pData, Length); } // send a DHCP discover static void DhcpDiscover(void) { struct { struct DhcpPacket DhcpPack; unsigned char OptType[3]; unsigned char OptEnd[2]; } DhcpDiscover; debug_dhcp_printf("discover"); ip_cpy(DhcpDiscover.DhcpPack.IpHdr.Dest, "\xFF\xFF\xFF\xFF"); // broadcast DhcpDiscover.DhcpPack.DhcpHdr.XId = htonl(DhcpXId); DhcpDiscover.DhcpPack.DhcpHdr.Secs = htons(DhcpXIdSecs); // use time // of XId DhcpDiscover.DhcpPack.DhcpHdr.Flags = htons(0x8000); // broadcast memset(DhcpDiscover.DhcpPack.DhcpHdr.YIAddr, 0, 4); DhcpDiscover.OptType[0] = 0x35; // DHCP discover DhcpDiscover.OptType[1] = 0x01; DhcpDiscover.OptType[2] = 0x01; DhcpDiscover.OptEnd[0] = 0xFF; // end of options DhcpDiscover.OptEnd[1] = 0x00; DhcpSend((unsigned char *)&DhcpDiscover, sizeof(DhcpDiscover)); } // send a DHCP request static void DhcpRequest(void) { struct { struct DhcpPacket DhcpPack; unsigned char OptType[3]; unsigned char OptReq[6]; unsigned char OptServer[6]; unsigned char OptEnd[2]; } DhcpRequest; debug_dhcp_printf("request ip=%d.%d.%d.%d", ConfigIp[0], ConfigIp[1], ConfigIp[2], ConfigIp[3]); ip_cpy(DhcpRequest.DhcpPack.IpHdr.Dest, DhcpServer); // send to DHCP // server DhcpRequest.DhcpPack.DhcpHdr.XId = htonl(DhcpXId); DhcpRequest.DhcpPack.DhcpHdr.Secs = htons(DhcpXIdSecs); // use time // of XId DhcpRequest.DhcpPack.DhcpHdr.Flags = htons(0x0000); ip_cpy(DhcpRequest.DhcpPack.DhcpHdr.YIAddr, ConfigIp); DhcpRequest.OptType[0] = 0x35; // DHCP request DhcpRequest.OptType[1] = 0x01; DhcpRequest.OptType[2] = 0x03; DhcpRequest.OptReq[0] = 0x32; // requested IP DhcpRequest.OptReq[1] = 0x04; ip_cpy(&DhcpRequest.OptReq[2], ConfigIp); DhcpRequest.OptServer[0] = 0x36; // DHCP server DhcpRequest.OptServer[1] = 0x04; ip_cpy(&DhcpRequest.OptServer[2], DhcpServer); DhcpRequest.OptEnd[0] = 0xFF; // end of options DhcpRequest.OptEnd[1] = 0x00; DhcpSend((unsigned char *)&DhcpRequest, sizeof(DhcpRequest)); } // abort DHCP action static void DhcpAbort(void) { // abort DHCP action DhcpInProgress = 0; DhcpRetries = 0; DhcpRetrySecs = 0; // forget XId DhcpHaveXId = 0; DhcpXId = 0; DhcpXIdSecs = 0; // give up IP address if it was leased if (DhcpActive) { ip_cpy(ConfigIp, "\0\0\0\0"); ip_cpy(ConfigMask, "\0\0\0\0"); ip_cpy(ConfigGw, "\0\0\0\0"); DhcpActive = 0; DhcpLeaseTime = 0; DhcpLeaseRest = 0; } } // retry DHCP action static void DhcpRetry(void) { // DHCP lease not active ---> re-send DHCP discover if (!DhcpActive) { // get time for next try RandomGetData((unsigned char *)&DhcpRetrySecs, sizeof(DhcpRetrySecs)); DhcpRetrySecs = DhcpRetrySecs % DhcpRetrySecsMax + DhcpRetrySecsMax / 2; // re-send DHCP discover DhcpDiscover(); } // DHCP lease active and lease time almost over ---> re-send DHCP request if (DhcpActive && (DhcpLeaseRest < DhcpLeaseRestMin || DhcpLeaseRest < DhcpLeaseTime / DhcpLeaseRenewFraction)) { // get time for next try RandomGetData((unsigned char *)&DhcpRetrySecs, sizeof(DhcpRetrySecs)); DhcpRetrySecs = DhcpRetrySecs % DhcpRetrySecsMax + DhcpRetrySecsMax / 2; // re-send DHCP request DhcpRequest(); } } // start new DHCP action static void DhcpStart(void) { // DHCP lease not active ---> DHCP discover if (!DhcpActive) { // DHCP operation starts DhcpInProgress = 1; DhcpRetries = DhcpRetriesMax; RandomGetData((unsigned char *)&DhcpRetrySecs, sizeof(DhcpRetrySecs)); DhcpRetrySecs = DhcpRetrySecs % DhcpRetrySecsMax + DhcpRetrySecsMax / 2; // get new XId RandomGetData((unsigned char *)&DhcpXId, sizeof(DhcpXId)); DhcpXIdSecs++; DhcpHaveXId = 1; // send DHCP discover DhcpDiscover(); } // DHCP lease active and lease time almost over ---> DHCP request if (DhcpActive && (DhcpLeaseRest < DhcpLeaseRestMin || DhcpLeaseRest < DhcpLeaseTime / DhcpLeaseRenewFraction)) { // DHCP operation starts DhcpInProgress = 1; DhcpRetries = DhcpRetriesMax; RandomGetData((unsigned char *)&DhcpRetrySecs, sizeof(DhcpRetrySecs)); DhcpRetrySecs = DhcpRetrySecs % DhcpRetrySecsMax + DhcpRetrySecsMax / 2; // get new XId if none available if (!DhcpHaveXId) { RandomGetData((unsigned char *)&DhcpXId, sizeof(DhcpXId)); DhcpXIdSecs++; DhcpHaveXId = 1; } // send DHCP request DhcpRequest(); } } // tick procedure - every 1000ms static void DhcpTick1000(void) { // DHCP operation in progress if (DhcpInProgress) { // count down time of current try of current action DhcpRetrySecs--; if (DhcpRetrySecs <= 0) { // count down retries DhcpRetries--; // no more retries left ---> abort, retry otherwise if (DhcpRetries <= 0) DhcpAbort(); else DhcpRetry(); } } // increase time of XId DhcpXIdSecs++; // dcrease remaining lease time DhcpLeaseRest--; // lease timed out ---> abort DHCP (will also invalidate IP) if (DhcpLeaseRest <= 0) { debug_dhcp_printf("lease expired"); DhcpAbort(); } } // tick procedure - call every 200ms void DhcpTick200(void) // (extern) { // get 1 second interval DhcpTicks++; if (DhcpTicks >= 5) { DhcpTicks = 0; DhcpTick1000(); } // no DHCP operation in progress if (!DhcpInProgress) { // DHCP lease active or no IP address ---> DHCP allowed (i.e. no static // IP) if (DhcpActive || ip_eq(ConfigIp, "\0\0\0\0")) { // start new DHCP action DhcpStart(); } } } // process a received DHCP offer static void DhcpOffer(unsigned char addr[4], unsigned char mask[4], unsigned char gateway[4], unsigned long time, unsigned char server[4]) { debug_dhcp_printf("offer addr=%d.%d.%d.%d mask=%d.%d.%d.%d gw=%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3], mask[0], mask[1], mask[2], mask[3], gateway[0], gateway[1], gateway[2], gateway[3]); debug_dhcp_printf("offer time=%ld server=%d.%d.%d.%d", time, server[0], server[1], server[2], server[3]); // use this IP address, mask and gateway ip_cpy(ConfigIp, addr); ip_cpy(ConfigMask, mask); ip_cpy(ConfigGw, gateway); // store DHCP server and lease time DhcpActive = 1; ip_cpy(DhcpServer, server); DhcpLeaseTime = time; DhcpLeaseRest = DhcpLeaseRestMin; // schedule DHCP request // DHCP action finished DhcpInProgress = 0; DhcpRetries = 0; DhcpRetrySecs = 0; } // process a received DHCP ack static void DhcpAck(unsigned char addr[4], unsigned char mask[4], unsigned char gateway[4], unsigned long time, unsigned char server[4]) { debug_dhcp_printf("ack addr=%d.%d.%d.%d mask=%d.%d.%d.%d gw=%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3], mask[0], mask[1], mask[2], mask[3], gateway[0], gateway[1], gateway[2], gateway[3]); debug_dhcp_printf("ack time=%ld server=%d.%d.%d.%d", time, server[0], server[1], server[2], server[3]); // check if IP address, mask and gateway match if (!ip_eq(ConfigIp, addr) || !ip_eq(ConfigMask, mask) || !ip_eq(ConfigGw, gateway)) { // mismatch ---> something is wrong, abort DhcpAbort(); return; } // store DHCP server and lease time DhcpActive = 1; ip_cpy(DhcpServer, server); DhcpLeaseTime = time; DhcpLeaseRest = time; // DHCP action finished DhcpInProgress = 0; DhcpRetries = 0; DhcpRetrySecs = 0; // forget XId DhcpHaveXId = 0; DhcpXId = 0; DhcpXIdSecs = 0; } // process a received DHCP packet void DhcpRecv(unsigned char *pData, unsigned short Length) // (extern) { struct DhcpPacket *pDhcpPack; unsigned char *opt_ptr, tag, len; unsigned short opt_len; unsigned char type; unsigned char have_mask, mask[4], have_gateway, gateway[4]; unsigned char have_time, have_server, server[4]; unsigned long time; // packet too short if (Length < sizeof(struct DhcpPacket)) return; // convert pointer to UDP packet // (this saves us from always casting pData) pDhcpPack = (struct DhcpPacket *)pData; // we are DHCP client // - source port must be 67 // - destination port must be 68 if (pDhcpPack->UdpHdr.SrcPort != htons(67) || pDhcpPack->UdpHdr.DestPort != htons(68)) return; // check DHCP fields if (pDhcpPack->DhcpHdr.Op != 2 || pDhcpPack->DhcpHdr.HType != 1 || pDhcpPack->DhcpHdr.HLen != 6 || pDhcpPack->DhcpHdr.HOps != 0 || pDhcpPack->DhcpHdr.MCookie != htonl(0x63825363)) return; debug_dhcp_printf("recv xid=%08lX len=%d", ntohl(pDhcpPack->DhcpHdr.XId), Length); // check XId if (!DhcpHaveXId || ntohl(pDhcpPack->DhcpHdr.XId) != DhcpXId) return; // get options opt_ptr = pData + sizeof(struct DhcpPacket); opt_len = Length - sizeof(struct DhcpPacket); // parse options type = 0; have_mask = 0; have_gateway = 0; time = 0; have_time = 0; have_server = 0; while (opt_len > 2) { // get tag and length tag = *opt_ptr++; len = *opt_ptr++; if (opt_len < 2 + len) break; // get option switch (tag) { // type case 0x35: if (len >= 1) type = opt_ptr[0]; break; // subnet mask case 0x01: if (len >= 4) memcpy(mask, opt_ptr, 4); have_mask = 1; break; // gateway case 0x03: if (len >= 4) { memcpy(gateway, opt_ptr, 4); have_gateway = 1; } break; // lease time case 0x33: if (len >= 4) { time = ntohl(*(unsigned long *)opt_ptr); have_time = 1; } break; // DHCP server case 0x36: if (len >= 4) { memcpy(server, opt_ptr, 4); have_server = 1; } break; } // skip option opt_ptr += len; opt_len -= len; } debug_dhcp_printf("type=%d%s%s%s%s", type, have_mask ? " mask" : "", have_gateway ? " gateway" : "", have_time ? " time" : "", have_server ? " server" : ""); // fill in server from source address if not present if (!have_server) { ip_cpy(server, pDhcpPack->IpHdr.Src); have_server = 1; } // process DHCP response switch (type) { // DHCP offer case 2: if (have_mask && have_gateway && have_time && have_server) DhcpOffer(pDhcpPack->DhcpHdr.YIAddr, mask, gateway, time, server); break; // DHCP ack case 5: if (have_mask && have_gateway && have_time && have_server) DhcpAck(pDhcpPack->DhcpHdr.YIAddr, mask, gateway, time, server); break; } }