Statistics
| Branch: | Revision:

root / arduino-1.0 / libraries / Ethernet / Dhcp.cpp @ 58d82c77

History | View | Annotate | Download (10.2 KB)

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 "Arduino.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 - (int)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
                    for (int i = 0; i < opt_len-4; i++)
275
                    {
276
                        _dhcpUdpSocket.read();
277
                    }
278
                    break;
279
                
280
                case dns :
281
                    opt_len = _dhcpUdpSocket.read();
282
                    _dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
283
                    for (int i = 0; i < opt_len-4; i++)
284
                    {
285
                        _dhcpUdpSocket.read();
286
                    }
287
                    break;
288
                
289
                case dhcpServerIdentifier :
290
                    opt_len = _dhcpUdpSocket.read();
291
                    if( *((uint32_t*)_dhcpDhcpServerIp) == 0 || 
292
                        IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() )
293
                    {
294
                        _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
295
                    }
296
                    else
297
                    {
298
                        // Skip over the rest of this option
299
                        while (opt_len--)
300
                        {
301
                            _dhcpUdpSocket.read();
302
                        }
303
                    }
304
                    break;
305
                
306
                case dhcpIPaddrLeaseTime :
307
                default :
308
                    opt_len = _dhcpUdpSocket.read();
309
                    // Skip over the rest of this option
310
                    while (opt_len--)
311
                    {
312
                        _dhcpUdpSocket.read();
313
                    }
314
                    break;
315
            }
316
        }
317
    }
318

    
319
    // Need to skip to end of the packet regardless here
320
    _dhcpUdpSocket.flush();
321

    
322
    return type;
323
}
324

    
325
IPAddress DhcpClass::getLocalIp()
326
{
327
    return IPAddress(_dhcpLocalIp);
328
}
329

    
330
IPAddress DhcpClass::getSubnetMask()
331
{
332
    return IPAddress(_dhcpSubnetMask);
333
}
334

    
335
IPAddress DhcpClass::getGatewayIp()
336
{
337
    return IPAddress(_dhcpGatewayIp);
338
}
339

    
340
IPAddress DhcpClass::getDhcpServerIp()
341
{
342
    return IPAddress(_dhcpDhcpServerIp);
343
}
344

    
345
IPAddress DhcpClass::getDnsServerIp()
346
{
347
    return IPAddress(_dhcpDnsServerIp);
348
}
349