WIZNet W5500 with additional enhancements

Fork of WIZnetInterface by WIZnet

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers DHCPClient.cpp Source File

DHCPClient.cpp

00001 // DHCPClient.cpp 2013/4/10
00002 #include "mbed.h"
00003 #include "mbed_debug.h"
00004 #include "UDPSocket.h"
00005 #include "DHCPClient.h"
00006 
00007 #define DBG_DHCP 0
00008 
00009 #if DBG_DHCP
00010 #define DBG(...) do{debug("[%s:%d]", __PRETTY_FUNCTION__,__LINE__);debug(__VA_ARGS__);} while(0);
00011 #define DBG_HEX(A,B) do{debug("[%s:%d]\r\n", __PRETTY_FUNCTION__,__LINE__);debug_hex(A,B);} while(0);
00012 #else
00013 #define DBG(...) while(0);
00014 #define DBG_HEX(A,B) while(0);
00015 #endif
00016 
00017 int DHCPClient::discover()
00018 {
00019     m_pos = 0;
00020     const uint8_t zero[] = { 0, 0, 0, 0 };
00021     const uint8_t header[] = {0x01,0x01,0x06,0x00};
00022     add_buf((uint8_t*)header, sizeof(header));
00023     uint32_t x = time(NULL) + rand();
00024     xid[0] = x>>24; xid[1] = x>>16; xid[2] = x>>8; xid[3] = x;
00025     add_buf(xid, 4);            // transaction id
00026     add_buf((uint8_t*)zero, 2); // seconds elapsed
00027     add_buf((uint8_t*)zero, 2); // bootp flags
00028     add_buf((uint8_t*)zero, 4); // requester ip address
00029     add_buf((uint8_t*)zero, 4); // client ip address
00030     add_buf((uint8_t*)zero, 4); // next server ip address
00031     add_buf((uint8_t*)zero, 4); // relay agent ip address
00032     add_buf(chaddr, 6);         // MAC address
00033     fill_buf(10, 0x00);         // padding
00034     fill_buf(192, 0x00);
00035     const uint8_t options[] = {0x63,0x82,0x53,0x63, // DHCP_MAGIC_COOKIE
00036         OPT_DHCP_MESSAGE, 1, DHCPDISCOVER,   // // DHCP message, len, discover
00037         OPT_PARAMETER_REQ, 5, OPT_SUBNET_MASK, OPT_ROUTER, OPT_TIME_SERVER, OPT_DOMAIN_NAME, OPT_DNS };
00038     add_buf((uint8_t*)options, sizeof(options));
00039     if (_hostname) {
00040         int hlen = strlen(_hostname);
00041         add_buf(OPT_HOSTNAME);
00042         add_buf(hlen);
00043         add_buf((uint8_t *)_hostname, hlen);
00044     }
00045     add_option(OPT_END);
00046     
00047     return m_pos;
00048 }
00049 
00050 int DHCPClient::request()
00051 {
00052     m_pos = 0;
00053     const uint8_t zero[] = { 0, 0, 0, 0 };
00054     const uint8_t header[] = {0x01,0x01,0x06,0x00};
00055     add_buf((uint8_t*)header, sizeof(header));
00056     add_buf(xid, 4);    // transaction id
00057     add_buf((uint8_t*)zero, 2); // seconds elapsed
00058     add_buf((uint8_t*)zero, 2); // bootp flags
00059     add_buf((uint8_t*)zero, 4); // requester ip address
00060     add_buf((uint8_t*)zero, 4); // client ip address
00061     add_buf(siaddr, 4);         // next server ip address
00062     add_buf((uint8_t*)zero, 4); // relay agent ip address (giaddr)
00063     add_buf(chaddr, 6);         // MAC address
00064     fill_buf(10, 0x00);         // padding
00065     fill_buf(192, 0x00);
00066     const uint8_t options[] = {0x63,0x82,0x53,0x63, // DHCP_MAGIC_COOKIE
00067         OPT_DHCP_MESSAGE,1, DHCPREQUEST,    // DHCP message, len, request
00068         OPT_PARAMETER_REQ, 5, OPT_SUBNET_MASK, OPT_ROUTER, OPT_TIME_SERVER, OPT_DOMAIN_NAME, OPT_DNS };
00069     add_buf((uint8_t*)options, sizeof(options));
00070     add_option(OPT_IP_ADDR_REQ, yiaddr, 4);
00071     add_option(OPT_SERVER_IDENT, siaddr, 4);
00072     if (_hostname) {
00073         int hlen = strlen(_hostname);
00074         add_buf(OPT_HOSTNAME);
00075         add_buf(hlen);
00076         add_buf((uint8_t *)_hostname, hlen);
00077     }
00078     add_option(OPT_END);
00079     return m_pos;
00080 }
00081 
00082 int DHCPClient::offer(uint8_t buf[], int size) {
00083     memcpy(yiaddr, buf+DHCP_OFFSET_YIADDR, 4);   
00084     memcpy(siaddr, buf+DHCP_OFFSET_SIADDR, 4);
00085  
00086     memset(dnsaddr, 0, sizeof(dnsaddr));
00087     memset(gateway, 0, sizeof(gateway));
00088     memset(netmask, 0, sizeof(netmask));
00089     memset(timesrv, 0, sizeof(timesrv));
00090     memset(leaseTime, 0, sizeof(leaseTime));
00091     
00092     uint8_t *p;
00093     int msg_type = -1;
00094     p = buf + DHCP_OFFSET_OPTIONS;
00095     while(*p != OPT_END && p < (buf+size)) {
00096         uint8_t code = *p++;
00097         if (code == 0) { // Pad Option
00098             continue;
00099         }
00100         int len = *p++;
00101  
00102         DBG("DHCP option: %d\r\n", code);
00103         DBG_HEX(p, len);
00104 
00105         switch(code) {
00106             case OPT_DHCP_MESSAGE:
00107                 msg_type = *p;
00108                 break;
00109             case OPT_SUBNET_MASK:
00110                 memcpy(netmask, p, 4);
00111                 break;
00112             case OPT_ROUTER:
00113                 memcpy(gateway, p, 4);
00114                 break; 
00115             case OPT_DNS:
00116                 memcpy(dnsaddr, p, 4);
00117                 break;
00118             case OPT_TIME_SERVER:
00119                 memcpy(timesrv, p, 4);
00120                 break;
00121             case OPT_ADDR_LEASE_TIME:
00122                 memcpy(leaseTime, p, 4);
00123                 break;
00124             case OPT_DOMAIN_NAME:
00125                 {
00126                     int cplen = len;
00127                     if (cplen > 63)
00128                         cplen = 63; // max domain name
00129                     if (domainName) {
00130                         free(domainName);
00131                         domainName = NULL;
00132                     }
00133                     if (cplen) {
00134                         domainName = (char *)calloc(1, cplen+1);
00135                         memcpy(domainName, p, cplen); // zero term is already here via calloc
00136                     }
00137                 }
00138                 break;
00139             case OPT_SERVER_IDENT:
00140                 memcpy(siaddr, p, 4);
00141                 break;
00142         }
00143         p += len;
00144     }
00145     return msg_type;
00146 }
00147 
00148 bool DHCPClient::verify(uint8_t buf[], int len) {
00149     if (len < DHCP_OFFSET_OPTIONS) {
00150         return false;
00151     }
00152     if (buf[DHCP_OFFSET_OP] != 0x02) {
00153         return false;
00154     }
00155     if (memcmp(buf+DHCP_OFFSET_XID, xid, 4) != 0) {
00156         return false;
00157     }
00158     return true;
00159 }
00160 
00161 void DHCPClient::callback()
00162 {
00163     Endpoint host;
00164     int recv_len = m_udp->receiveFrom(host, (char*)m_buf, sizeof(m_buf));
00165     if (recv_len < 0) {
00166         return;
00167     }
00168     if (!verify(m_buf, recv_len)) {
00169         return;
00170     }
00171     int r = offer(m_buf, recv_len);
00172     if (r == DHCPOFFER) {
00173         int send_size = request();
00174         m_udp->sendTo(m_server, (char*)m_buf, send_size);
00175     } else if (r == DHCPACK) {
00176         exit_flag = true;
00177     }
00178 }
00179 
00180 void  DHCPClient::add_buf(uint8_t c)
00181 {
00182     m_buf[m_pos++] = c;
00183 }
00184 
00185 void  DHCPClient::add_buf(uint8_t* buf, int len)
00186 {
00187     for(int i = 0; i < len; i++) {
00188         add_buf(buf[i]);
00189     }
00190 }
00191 
00192 void DHCPClient::fill_buf(int len, uint8_t data)
00193 {
00194     while(len-- > 0) {
00195         add_buf(data);
00196     }
00197 }
00198 
00199 void  DHCPClient::add_option(uint8_t code, uint8_t* buf, int len)
00200 {
00201     add_buf(code);
00202     if (len > 0) {
00203         add_buf((uint8_t)len);
00204         add_buf(buf, len);
00205     }
00206 }
00207 
00208 int DHCPClient::setup(const char *hostnane, int timeout_ms)
00209 {
00210     eth = WIZnet_Chip::getInstance();
00211     if (eth == NULL) {
00212         return -1;
00213     }
00214     _hostname = hostnane;
00215     eth->reg_rd_mac(SHAR, chaddr);
00216     int interval_ms = 5*1000; // 5000msec
00217     if (timeout_ms < interval_ms) {
00218         interval_ms = timeout_ms;
00219     }
00220     m_udp = new UDPSocket;
00221     m_udp->init();
00222     m_udp->set_blocking(false);
00223     eth->reg_wr<uint32_t>(SIPR, 0x00000000); // local ip "0.0.0.0"
00224     m_udp->bind(DHCP_CLIENT_PORT); // local port
00225     m_server.set_address("255.255.255.255", DHCP_SERVER_PORT); // DHCP broadcast
00226     exit_flag = false;
00227     int err = 0;
00228     int seq = 0;
00229     int send_size;
00230     while(!exit_flag) {
00231         switch(seq) {
00232             case 0:
00233                 m_retry = 0;
00234                 seq++;
00235                 break;
00236             case 1:
00237                 send_size = discover();
00238                 m_udp->sendTo(m_server, (char*)m_buf, send_size);
00239                 m_interval.reset();
00240                 m_interval.start();
00241                 seq++;
00242                 break;
00243             case 2:
00244                 callback();
00245                 if (m_interval.read_ms() > interval_ms) {
00246                     DBG("m_retry: %d\n", m_retry);
00247                     if (++m_retry >= (timeout_ms/interval_ms)) {
00248                         err = -1;
00249                         exit_flag = true;
00250                     }
00251                     seq--;
00252                 }
00253                 break;
00254         }
00255     }
00256     DBG("m_retry: %d, m_interval: %d\n", m_retry, m_interval.read_ms());
00257     delete m_udp;
00258     return err;
00259 }
00260 
00261 DHCPClient::DHCPClient() {
00262     domainName = NULL;
00263 }
00264 
00265 DHCPClient::~DHCPClient() {
00266     if (domainName)
00267         free(domainName);
00268 }