]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/net/bootp.c
3df9606695343cb2b88f00f727c8b987a94343ad
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / net / bootp.c
1 //==========================================================================
2 //
3 //      net/bootp.c
4 //
5 //      Stand-alone minimal BOOTP support for RedBoot
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2002, 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         2000-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <pkgconf/isoinfra.h>
57 #if CYGINT_ISO_RAND
58 #include <stdlib.h> /* for rand() prototype */
59 #else
60 #define SHOULD_BE_RANDOM  0x12345555
61 #endif
62 #include <redboot.h>
63 #include <net/net.h>
64 #include <net/bootp.h>
65
66
67 /* How many milliseconds to wait before retrying the request */
68 #define RETRY_TIME  2000
69 #define MAX_RETRIES    8
70
71 static bootp_header_t *bp_info;
72   
73 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
74 static const unsigned char dhcpCookie[] = {99,130,83,99};
75 static const unsigned char dhcpEnd[] = {255};
76 static const unsigned char dhcpDiscover[] = {53,1,1};
77 static const unsigned char dhcpRequest[] = {53,1,3};
78 static const unsigned char dhcpRequestIP[] = {50,4};
79 static const unsigned char dhcpParamRequestList[] = {55,3,1,3,6};
80 static enum {
81     DHCP_NONE = 0,
82     DHCP_DISCOVER,
83     DHCP_OFFER,
84     DHCP_REQUEST,
85     DHCP_ACK
86 } dhcpState;
87 #endif
88
89 static void
90 bootp_handler(udp_socket_t *skt, char *buf, int len,
91               ip_route_t *src_route, word src_port)
92 {
93     bootp_header_t *b;
94 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
95     unsigned char *p, expected = 0;
96 #endif
97
98     b = (bootp_header_t *)buf;
99     if (bp_info) {
100         memset(bp_info,0,sizeof *bp_info);
101         if (len > sizeof *bp_info)
102             len = sizeof *bp_info;
103         memcpy(bp_info, b, len);
104     }
105
106     // Only accept pure REPLY responses
107     if (b->bp_op != BOOTREPLY)
108       return;
109     
110     // Must be sent to me, as well!
111     if (memcmp(b->bp_chaddr, __local_enet_addr, 6))
112       return;
113         
114 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
115     p = b->bp_vend;
116     if (memcmp(p, dhcpCookie, sizeof(dhcpCookie)))
117       return;
118     p += 4;
119
120     // Find the DHCP Message Type tag
121     while (*p != TAG_DHCP_MESS_TYPE) {
122         p += p[1] + 2;
123         if (p >= (unsigned char*)b + sizeof(*bp_info))
124             return;
125     }
126
127     p += 2;
128
129     switch (dhcpState) {
130     case DHCP_DISCOVER:
131         // The discover message has been sent, only accept an offer reply
132         if (*p == DHCP_MESS_TYPE_OFFER) {
133             dhcpState = DHCP_OFFER;
134             return;
135         } else {
136             expected = DHCP_MESS_TYPE_OFFER;
137         }
138         break;
139     case DHCP_REQUEST:
140         // The request message has been sent, only accept an ack reply
141         if (*p == DHCP_MESS_TYPE_ACK) {
142             dhcpState = DHCP_ACK;
143             return;
144         } else {
145             expected = DHCP_MESS_TYPE_ACK;
146         }
147         break;
148     case DHCP_NONE:
149     case DHCP_OFFER:
150     case DHCP_ACK:
151         // Quitely ignore these - they indicate repeated message from server
152         return;
153     }
154     // See if we've been NAK'd - if so, give up and try again
155     if (*p == DHCP_MESS_TYPE_NAK) {
156         dhcpState = DHCP_NONE;
157         return;
158     }
159     diag_printf("DHCP reply: %d, not %d\n", (int)*p, (int)expected);
160     return;
161 #else
162     // Simple BOOTP - this is all there is!
163     memcpy(__local_ip_addr, &b->bp_yiaddr, 4);
164 #endif
165 }
166
167 #define AddOption(p,d) do {memcpy(p,d,sizeof d); p += sizeof d;} while (0)
168
169 static int get_xid()
170 {
171 #if CYGINT_ISO_RAND
172         return rand();
173 #else
174         static int xid = SHOULD_BE_RANDOM;
175         return xid++;
176 #endif
177 }
178
179 /*
180  * Find our IP address and copy to __local_ip_addr.
181  * Return zero if successful, -1 if not.
182  */
183 int
184 __bootp_find_local_ip(bootp_header_t *info)
185 {
186     udp_socket_t udp_skt;
187     bootp_header_t b;
188     ip_route_t     r;
189     int            retry;
190     unsigned long  start;
191     ip_addr_t saved_ip_addr;
192 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
193     unsigned char *p;
194     int oldState;
195 #endif
196     int txSize;
197     bool abort = false;
198     int xid;
199
200 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
201     dhcpState = DHCP_NONE;
202 #endif
203
204     // Where we want the results saved
205     bp_info = info;
206     // Preserve any IP address we currently have, just in case
207     memcpy(saved_ip_addr, __local_ip_addr, sizeof(__local_ip_addr));
208
209     // fill out route for a broadcast
210     r.ip_addr[0] = 255;
211     r.ip_addr[1] = 255;
212     r.ip_addr[2] = 255;
213     r.ip_addr[3] = 255;
214     r.enet_addr[0] = 255;
215     r.enet_addr[1] = 255;
216     r.enet_addr[2] = 255;
217     r.enet_addr[3] = 255;
218     r.enet_addr[4] = 255;
219     r.enet_addr[5] = 255;
220
221     // setup a socket listener for bootp replies
222     __udp_install_listener(&udp_skt, IPPORT_BOOTPC, bootp_handler);
223
224     retry = MAX_RETRIES;  
225     do {
226         start = MS_TICKS();
227
228         // Build up the BOOTP/DHCP request
229         memset(&b, 0, sizeof(b));
230         b.bp_op = BOOTREQUEST;
231         b.bp_htype = HTYPE_ETHERNET;
232         b.bp_hlen = 6;
233         b.bp_xid = get_xid();
234         memcpy(b.bp_chaddr, __local_enet_addr, 6);
235         memset(__local_ip_addr, 0, sizeof(__local_ip_addr));
236          
237 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
238         p = b.bp_vend;
239         switch (dhcpState) {
240         case DHCP_NONE:
241         case DHCP_DISCOVER:
242             AddOption(p,dhcpCookie);
243             AddOption(p,dhcpDiscover);
244             AddOption(p,dhcpParamRequestList);
245             AddOption(p,dhcpEnd);
246             dhcpState = DHCP_DISCOVER;
247             break;
248         case DHCP_OFFER:
249             retry = MAX_RETRIES;
250         case DHCP_REQUEST:
251             b.bp_xid = bp_info->bp_xid;  // Match what server sent
252             AddOption(p,dhcpCookie);
253             AddOption(p,dhcpRequest);
254             AddOption(p,dhcpRequestIP);
255             memcpy(p, &bp_info->bp_yiaddr, 4);  p += 4;  // Ask for the address just given
256             AddOption(p,dhcpParamRequestList);
257             AddOption(p,dhcpEnd);
258             dhcpState = DHCP_REQUEST;
259             memset(&b.bp_yiaddr, 0xFF, 4);
260             memset(&b.bp_siaddr, 0xFF, 4);
261             memset(&b.bp_yiaddr, 0x00, 4);
262             memset(&b.bp_siaddr, 0x00, 4);
263             break;
264         case DHCP_ACK:
265             // Ignore these states (they won't happen)
266             break;
267         }
268      
269         // Some servers insist on a minimum amount of "vendor" data
270         if (p < &b.bp_vend[BP_MIN_VEND_SIZE]) p = &b.bp_vend[BP_MIN_VEND_SIZE];
271         txSize = p - (unsigned char*)&b;
272         oldState = dhcpState;
273 #else
274         txSize = sizeof(b);
275 #endif
276
277         __udp_send((char *)&b, txSize, &r, IPPORT_BOOTPS, IPPORT_BOOTPC);
278
279         // If we're retrying, inform the user
280         if (retry == (MAX_RETRIES-1))
281             diag_printf("... waiting for BOOTP information\n");
282
283         do {
284             __enet_poll();
285 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
286             if (dhcpState != oldState) {
287                 if (dhcpState == DHCP_ACK) {
288                     unsigned char *end;
289                     int optlen;
290                     // Address information has now arrived!
291                     memcpy(__local_ip_addr, &bp_info->bp_yiaddr, 4);
292 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
293                     memcpy(__local_ip_gate, &bp_info->bp_giaddr, 4);
294 #endif
295                     p = bp_info->bp_vend+4;
296                     end = (unsigned char *)bp_info+sizeof(*bp_info);
297                     while (p < end) {
298                         unsigned char tag = *p;
299                         if (tag == TAG_END)
300                             break;
301                         if (tag == TAG_PAD)
302                             optlen = 1;
303                         else {
304                             optlen = p[1];
305                             p += 2;
306                             switch (tag) {
307 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
308                             case TAG_SUBNET_MASK:  // subnet mask
309                                 memcpy(__local_ip_mask,p,4); 
310                                 break;
311                             case TAG_GATEWAY:  // router
312                                 memcpy(__local_ip_gate,p,4); 
313                                 break;
314 #endif
315 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS
316                             case TAG_DOMAIN_SERVER:
317 //                              diag_printf(" DNS server found!\n");
318                                 memcpy(&__bootp_dns_addr, p, 4);
319                                 __bootp_dns_set = 1;
320                                 break;
321 #endif
322                             default:
323                                 break;
324                             }
325                         }
326                         p += optlen;
327                     }
328                     __udp_remove_listener(IPPORT_BOOTPC);
329                     return 0;
330                 } else {
331                     break;  // State changed, handle it
332                 }
333             }
334 #else
335             // All done, if address response has arrived
336             if (__local_ip_addr[0] || __local_ip_addr[1] ||
337                 __local_ip_addr[2] || __local_ip_addr[3]) {
338                 /* success */
339                 __udp_remove_listener(IPPORT_BOOTPC);
340                 return 0;
341             }
342 #endif
343             if (_rb_break(1)) {
344                 // The user typed ^C on the console
345                 abort = true;
346                 break;
347             }
348             start--; /* account for time spent in _rb_break() */
349         } while ((int)(MS_TICKS_DELAY() - start) < RETRY_TIME);
350     } while (!abort && (retry-- > 0));
351
352     // timed out
353     __udp_remove_listener(IPPORT_BOOTPC);
354     // Restore any previous IP address
355     memcpy(__local_ip_addr, saved_ip_addr, sizeof(__local_ip_addr));
356     return -1;
357 }
358
359