BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
e8658d5
Branches
Tags
master
flaneth
firmware.dartboard
dhcp.c
initial commit after making CF identify work
Stefan Schuermans
commited
e8658d5
at 2012-04-15 19:57:57
dhcp.c
Blame
History
Raw
/* flaneth - flash and ethernet - dartboard mod * version 0.1 date 2008-11-09 * Copyright (C) 2007-2008 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; } }