|
| 1 | +// DHCP Library v0.3 - April 25, 2009 |
| 2 | +// Author: Jordan Terrell - blog.jordanterrell.com |
| 3 | + |
| 4 | +#include "w5100.h" |
| 5 | + |
| 6 | +#include <string.h> |
| 7 | +#include <stdlib.h> |
| 8 | +#include "Dhcp.h" |
| 9 | +#include "wiring.h" |
| 10 | +#include "util.h" |
| 11 | + |
| 12 | +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) |
| 13 | +{ |
| 14 | + uint8_t dhcp_state = STATE_DHCP_START; |
| 15 | + uint8_t messageType = 0; |
| 16 | + |
| 17 | + // zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp |
| 18 | + memset(_dhcpMacAddr, 0, 26); |
| 19 | + |
| 20 | + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); |
| 21 | + |
| 22 | + // Pick an initial transaction ID |
| 23 | + _dhcpTransactionId = random(1UL, 2000UL); |
| 24 | + _dhcpInitialTransactionId = _dhcpTransactionId; |
| 25 | + |
| 26 | + if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) |
| 27 | + { |
| 28 | + // Couldn't get a socket |
| 29 | + return 0; |
| 30 | + } |
| 31 | + |
| 32 | + presend_DHCP(); |
| 33 | + |
| 34 | + int result = 0; |
| 35 | + |
| 36 | + unsigned long startTime = millis(); |
| 37 | + |
| 38 | + while(dhcp_state != STATE_DHCP_LEASED) |
| 39 | + { |
| 40 | + if(dhcp_state == STATE_DHCP_START) |
| 41 | + { |
| 42 | + _dhcpTransactionId++; |
| 43 | + |
| 44 | + send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); |
| 45 | + dhcp_state = STATE_DHCP_DISCOVER; |
| 46 | + } |
| 47 | + else if(dhcp_state == STATE_DHCP_DISCOVER) |
| 48 | + { |
| 49 | + uint32_t respId; |
| 50 | + messageType = parseDHCPResponse(responseTimeout, respId); |
| 51 | + if(messageType == DHCP_OFFER) |
| 52 | + { |
| 53 | + // We'll use the transaction ID that the offer came with, |
| 54 | + // rather than the one we were up to |
| 55 | + _dhcpTransactionId = respId; |
| 56 | + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); |
| 57 | + dhcp_state = STATE_DHCP_REQUEST; |
| 58 | + } |
| 59 | + } |
| 60 | + else if(dhcp_state == STATE_DHCP_REQUEST) |
| 61 | + { |
| 62 | + uint32_t respId; |
| 63 | + messageType = parseDHCPResponse(responseTimeout, respId); |
| 64 | + if(messageType == DHCP_ACK) |
| 65 | + { |
| 66 | + dhcp_state = STATE_DHCP_LEASED; |
| 67 | + result = 1; |
| 68 | + } |
| 69 | + else if(messageType == DHCP_NAK) |
| 70 | + dhcp_state = STATE_DHCP_START; |
| 71 | + } |
| 72 | + |
| 73 | + if(messageType == 255) |
| 74 | + { |
| 75 | + messageType = 0; |
| 76 | + dhcp_state = STATE_DHCP_START; |
| 77 | + } |
| 78 | + |
| 79 | + if(result != 1 && ((millis() - startTime) > timeout)) |
| 80 | + break; |
| 81 | + } |
| 82 | + |
| 83 | + // We're done with the socket now |
| 84 | + _dhcpUdpSocket.stop(); |
| 85 | + _dhcpTransactionId++; |
| 86 | + |
| 87 | + return result; |
| 88 | +} |
| 89 | + |
| 90 | +void DhcpClass::presend_DHCP() |
| 91 | +{ |
| 92 | +} |
| 93 | + |
| 94 | +void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) |
| 95 | +{ |
| 96 | + uint8_t buffer[32]; |
| 97 | + memset(buffer, 0, 32); |
| 98 | + IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address |
| 99 | + |
| 100 | + if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) |
| 101 | + { |
| 102 | + // FIXME Need to return errors |
| 103 | + return; |
| 104 | + } |
| 105 | + |
| 106 | + buffer[0] = DHCP_BOOTREQUEST; // op |
| 107 | + buffer[1] = DHCP_HTYPE10MB; // htype |
| 108 | + buffer[2] = DHCP_HLENETHERNET; // hlen |
| 109 | + buffer[3] = DHCP_HOPS; // hops |
| 110 | + |
| 111 | + // xid |
| 112 | + unsigned long xid = htonl(_dhcpTransactionId); |
| 113 | + memcpy(buffer + 4, &(xid), 4); |
| 114 | + |
| 115 | + // 8, 9 - seconds elapsed |
| 116 | + buffer[8] = ((secondsElapsed & 0xff00) >> 8); |
| 117 | + buffer[9] = (secondsElapsed & 0x00ff); |
| 118 | + |
| 119 | + // flags |
| 120 | + unsigned short flags = htons(DHCP_FLAGSBROADCAST); |
| 121 | + memcpy(buffer + 10, &(flags), 2); |
| 122 | + |
| 123 | + // ciaddr: already zeroed |
| 124 | + // yiaddr: already zeroed |
| 125 | + // siaddr: already zeroed |
| 126 | + // giaddr: already zeroed |
| 127 | + |
| 128 | + //put data in W5100 transmit buffer |
| 129 | + _dhcpUdpSocket.write(buffer, 28); |
| 130 | + |
| 131 | + memset(buffer, 0, 32); // clear local buffer |
| 132 | + |
| 133 | + memcpy(buffer, _dhcpMacAddr, 6); // chaddr |
| 134 | + |
| 135 | + //put data in W5100 transmit buffer |
| 136 | + _dhcpUdpSocket.write(buffer, 16); |
| 137 | + |
| 138 | + memset(buffer, 0, 32); // clear local buffer |
| 139 | + |
| 140 | + // leave zeroed out for sname && file |
| 141 | + // put in W5100 transmit buffer x 6 (192 bytes) |
| 142 | + |
| 143 | + for(int i = 0; i < 6; i++) { |
| 144 | + _dhcpUdpSocket.write(buffer, 32); |
| 145 | + } |
| 146 | + |
| 147 | + // OPT - Magic Cookie |
| 148 | + buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); |
| 149 | + buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); |
| 150 | + buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); |
| 151 | + buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); |
| 152 | + |
| 153 | + // OPT - message type |
| 154 | + buffer[4] = dhcpMessageType; |
| 155 | + buffer[5] = 0x01; |
| 156 | + buffer[6] = messageType; //DHCP_REQUEST; |
| 157 | + |
| 158 | + // OPT - client identifier |
| 159 | + buffer[7] = dhcpClientIdentifier; |
| 160 | + buffer[8] = 0x07; |
| 161 | + buffer[9] = 0x01; |
| 162 | + memcpy(buffer + 10, _dhcpMacAddr, 6); |
| 163 | + |
| 164 | + // OPT - host name |
| 165 | + buffer[16] = hostName; |
| 166 | + buffer[17] = strlen(HOST_NAME) + 3; // length of hostname + last 3 bytes of mac address |
| 167 | + strcpy((char*)&(buffer[18]), HOST_NAME); |
| 168 | + |
| 169 | + buffer[24] = _dhcpMacAddr[3]; |
| 170 | + buffer[25] = _dhcpMacAddr[4]; |
| 171 | + buffer[26] = _dhcpMacAddr[5]; |
| 172 | + |
| 173 | + //put data in W5100 transmit buffer |
| 174 | + _dhcpUdpSocket.write(buffer, 27); |
| 175 | + |
| 176 | + if(messageType == DHCP_REQUEST) |
| 177 | + { |
| 178 | + buffer[0] = dhcpRequestedIPaddr; |
| 179 | + buffer[1] = 0x04; |
| 180 | + buffer[2] = _dhcpLocalIp[0]; |
| 181 | + buffer[3] = _dhcpLocalIp[1]; |
| 182 | + buffer[4] = _dhcpLocalIp[2]; |
| 183 | + buffer[5] = _dhcpLocalIp[3]; |
| 184 | + |
| 185 | + buffer[6] = dhcpServerIdentifier; |
| 186 | + buffer[7] = 0x04; |
| 187 | + buffer[8] = _dhcpDhcpServerIp[0]; |
| 188 | + buffer[9] = _dhcpDhcpServerIp[1]; |
| 189 | + buffer[10] = _dhcpDhcpServerIp[2]; |
| 190 | + buffer[11] = _dhcpDhcpServerIp[3]; |
| 191 | + |
| 192 | + //put data in W5100 transmit buffer |
| 193 | + _dhcpUdpSocket.write(buffer, 12); |
| 194 | + } |
| 195 | + |
| 196 | + buffer[0] = dhcpParamRequest; |
| 197 | + buffer[1] = 0x06; |
| 198 | + buffer[2] = subnetMask; |
| 199 | + buffer[3] = routersOnSubnet; |
| 200 | + buffer[4] = dns; |
| 201 | + buffer[5] = domainName; |
| 202 | + buffer[6] = dhcpT1value; |
| 203 | + buffer[7] = dhcpT2value; |
| 204 | + buffer[8] = endOption; |
| 205 | + |
| 206 | + //put data in W5100 transmit buffer |
| 207 | + _dhcpUdpSocket.write(buffer, 9); |
| 208 | + |
| 209 | + _dhcpUdpSocket.endPacket(); |
| 210 | +} |
| 211 | + |
| 212 | +uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) |
| 213 | +{ |
| 214 | + uint8_t type = 0; |
| 215 | + uint8_t opt_len = 0; |
| 216 | + |
| 217 | + unsigned long startTime = millis(); |
| 218 | + |
| 219 | + while(_dhcpUdpSocket.parsePacket() <= 0) |
| 220 | + { |
| 221 | + if((millis() - startTime) > responseTimeout) |
| 222 | + { |
| 223 | + return 255; |
| 224 | + } |
| 225 | + delay(50); |
| 226 | + } |
| 227 | + // start reading in the packet |
| 228 | + RIP_MSG_FIXED fixedMsg; |
| 229 | + _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); |
| 230 | + |
| 231 | + if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) |
| 232 | + { |
| 233 | + transactionId = ntohl(fixedMsg.xid); |
| 234 | + if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) |
| 235 | + { |
| 236 | + // Need to read the rest of the packet here regardless |
| 237 | + _dhcpUdpSocket.flush(); |
| 238 | + return 0; |
| 239 | + } |
| 240 | + |
| 241 | + memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); |
| 242 | + |
| 243 | + // Skip to the option part |
| 244 | + // Doing this a byte at a time so we don't have to put a big buffer |
| 245 | + // on the stack (as we don't have lots of memory lying around) |
| 246 | + for (int i =0; i < (240 - sizeof(RIP_MSG_FIXED)); i++) |
| 247 | + { |
| 248 | + _dhcpUdpSocket.read(); // we don't care about the returned byte |
| 249 | + } |
| 250 | + |
| 251 | + while (_dhcpUdpSocket.available() > 0) |
| 252 | + { |
| 253 | + switch (_dhcpUdpSocket.read()) |
| 254 | + { |
| 255 | + case endOption : |
| 256 | + break; |
| 257 | + |
| 258 | + case padOption : |
| 259 | + break; |
| 260 | + |
| 261 | + case dhcpMessageType : |
| 262 | + opt_len = _dhcpUdpSocket.read(); |
| 263 | + type = _dhcpUdpSocket.read(); |
| 264 | + break; |
| 265 | + |
| 266 | + case subnetMask : |
| 267 | + opt_len = _dhcpUdpSocket.read(); |
| 268 | + _dhcpUdpSocket.read(_dhcpSubnetMask, 4); |
| 269 | + break; |
| 270 | + |
| 271 | + case routersOnSubnet : |
| 272 | + opt_len = _dhcpUdpSocket.read(); |
| 273 | + _dhcpUdpSocket.read(_dhcpGatewayIp, 4); |
| 274 | + break; |
| 275 | + |
| 276 | + case dns : |
| 277 | + opt_len = _dhcpUdpSocket.read(); |
| 278 | + _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); |
| 279 | + break; |
| 280 | + |
| 281 | + case dhcpServerIdentifier : |
| 282 | + opt_len = _dhcpUdpSocket.read(); |
| 283 | + if( *((uint32_t*)_dhcpDhcpServerIp) == 0 || |
| 284 | + IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) |
| 285 | + { |
| 286 | + _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); |
| 287 | + } |
| 288 | + else |
| 289 | + { |
| 290 | + // Skip over the rest of this option |
| 291 | + while (opt_len--) |
| 292 | + { |
| 293 | + _dhcpUdpSocket.read(); |
| 294 | + } |
| 295 | + } |
| 296 | + break; |
| 297 | + |
| 298 | + case dhcpIPaddrLeaseTime : |
| 299 | + default : |
| 300 | + opt_len = _dhcpUdpSocket.read(); |
| 301 | + // Skip over the rest of this option |
| 302 | + while (opt_len--) |
| 303 | + { |
| 304 | + _dhcpUdpSocket.read(); |
| 305 | + } |
| 306 | + break; |
| 307 | + } |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + // Need to skip to end of the packet regardless here |
| 312 | + _dhcpUdpSocket.flush(); |
| 313 | + |
| 314 | + return type; |
| 315 | +} |
| 316 | + |
| 317 | +IPAddress DhcpClass::getLocalIp() |
| 318 | +{ |
| 319 | + return IPAddress(_dhcpLocalIp); |
| 320 | +} |
| 321 | + |
| 322 | +IPAddress DhcpClass::getSubnetMask() |
| 323 | +{ |
| 324 | + return IPAddress(_dhcpSubnetMask); |
| 325 | +} |
| 326 | + |
| 327 | +IPAddress DhcpClass::getGatewayIp() |
| 328 | +{ |
| 329 | + return IPAddress(_dhcpGatewayIp); |
| 330 | +} |
| 331 | + |
| 332 | +IPAddress DhcpClass::getDhcpServerIp() |
| 333 | +{ |
| 334 | + return IPAddress(_dhcpDhcpServerIp); |
| 335 | +} |
| 336 | + |
| 337 | +IPAddress DhcpClass::getDnsServerIp() |
| 338 | +{ |
| 339 | + return IPAddress(_dhcpDnsServerIp); |
| 340 | +} |
| 341 | + |
0 commit comments