]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/net/bootp.c
unified MX27, MX25, MX37 trees
[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, void *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 = 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(void)
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
199 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
200     dhcpState = DHCP_NONE;
201 #endif
202
203     // Where we want the results saved
204     bp_info = info;
205     // Preserve any IP address we currently have, just in case
206     memcpy(saved_ip_addr, __local_ip_addr, sizeof(__local_ip_addr));
207
208     // fill out route for a broadcast
209     r.ip_addr[0] = 255;
210     r.ip_addr[1] = 255;
211     r.ip_addr[2] = 255;
212     r.ip_addr[3] = 255;
213     r.enet_addr[0] = 255;
214     r.enet_addr[1] = 255;
215     r.enet_addr[2] = 255;
216     r.enet_addr[3] = 255;
217     r.enet_addr[4] = 255;
218     r.enet_addr[5] = 255;
219
220     // setup a socket listener for bootp replies
221     __udp_install_listener(&udp_skt, IPPORT_BOOTPC, bootp_handler);
222
223     retry = MAX_RETRIES;  
224     do {
225         start = MS_TICKS();
226
227         // Build up the BOOTP/DHCP request
228         memset(&b, 0, sizeof(b));
229         b.bp_op = BOOTREQUEST;
230         b.bp_htype = HTYPE_ETHERNET;
231         b.bp_hlen = 6;
232         b.bp_xid = get_xid();
233         memcpy(b.bp_chaddr, __local_enet_addr, 6);
234         memset(__local_ip_addr, 0, sizeof(__local_ip_addr));
235          
236 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
237         p = b.bp_vend;
238         switch (dhcpState) {
239         case DHCP_NONE:
240         case DHCP_DISCOVER:
241             AddOption(p,dhcpCookie);
242             AddOption(p,dhcpDiscover);
243             AddOption(p,dhcpParamRequestList);
244             AddOption(p,dhcpEnd);
245             dhcpState = DHCP_DISCOVER;
246             break;
247         case DHCP_OFFER:
248             retry = MAX_RETRIES;
249         case DHCP_REQUEST:
250             b.bp_xid = bp_info->bp_xid;  // Match what server sent
251             AddOption(p,dhcpCookie);
252             AddOption(p,dhcpRequest);
253             AddOption(p,dhcpRequestIP);
254             memcpy(p, &bp_info->bp_yiaddr, 4);  p += 4;  // Ask for the address just given
255             AddOption(p,dhcpParamRequestList);
256             AddOption(p,dhcpEnd);
257             dhcpState = DHCP_REQUEST;
258             memset(&b.bp_yiaddr, 0xFF, 4);
259             memset(&b.bp_siaddr, 0xFF, 4);
260             memset(&b.bp_yiaddr, 0x00, 4);
261             memset(&b.bp_siaddr, 0x00, 4);
262             break;
263         case DHCP_ACK:
264             // Ignore these states (they won't happen)
265             break;
266         }
267      
268         // Some servers insist on a minimum amount of "vendor" data
269         if (p < &b.bp_vend[BP_MIN_VEND_SIZE]) p = &b.bp_vend[BP_MIN_VEND_SIZE];
270         txSize = p - (unsigned char*)&b;
271         oldState = dhcpState;
272 #else
273         txSize = sizeof(b);
274 #endif
275
276         __udp_send((char *)&b, txSize, &r, IPPORT_BOOTPS, IPPORT_BOOTPC);
277
278         // If we're retrying, inform the user
279         if (retry == (MAX_RETRIES - 1))
280             diag_printf("... waiting for BOOTP information\n");
281
282         do {
283             __enet_poll();
284 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
285             if (dhcpState != oldState) {
286                 if (dhcpState == DHCP_ACK) {
287                     unsigned char *end;
288                     int optlen;
289                     // Address information has now arrived!
290                     memcpy(__local_ip_addr, &bp_info->bp_yiaddr, 4);
291 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
292                     memcpy(__local_ip_gate, &bp_info->bp_giaddr, 4);
293 #endif
294                     p = bp_info->bp_vend+4;
295                     end = (unsigned char *)bp_info+sizeof(*bp_info);
296                     while (p < end) {
297                         unsigned char tag = *p;
298                         if (tag == TAG_END)
299                             break;
300                         if (tag == TAG_PAD)
301                             optlen = 1;
302                         else {
303                             optlen = p[1];
304                             p += 2;
305                             switch (tag) {
306 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
307                             case TAG_SUBNET_MASK:  // subnet mask
308                                 memcpy(__local_ip_mask,p,4); 
309                                 break;
310                             case TAG_GATEWAY:  // router
311                                 memcpy(__local_ip_gate,p,4); 
312                                 break;
313 #endif
314 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS
315                             case TAG_DOMAIN_SERVER:
316 //                              diag_printf(" DNS server found!\n");
317                                 memcpy(&__bootp_dns_addr, p, 4);
318                                 __bootp_dns_set = 1;
319                                 break;
320 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS_DHCP_DOMAIN
321                                                         case TAG_DOMAIN_NAME:
322                                                                 if(optlen < sizeof(__bootp_dns_domain)) {
323                                                                         memcpy(__bootp_dns_domain, p, optlen);
324                                                                         __bootp_dns_domain[optlen] = '\0';
325                                                                         __bootp_dns_domain_set = 1;
326                                                                 } else {
327                                                                         diag_printf("DNS domain name too long\n");
328                                                                 }
329                                                                 break;
330 #endif //CYGPKG_REDBOOT_NETWORKING_DNS_DHCP_DOMAIN
331 #endif //CYGPKG_REDBOOT_NETWORKING_DNS
332                             default:
333                                 break;
334                             }
335                         }
336                         p += optlen;
337                     }
338                     __udp_remove_listener(IPPORT_BOOTPC);
339                     return 0;
340                 } else {
341                     break;  // State changed, handle it
342                 }
343             }
344 #else
345             // All done, if address response has arrived
346             if (__local_ip_addr[0] || __local_ip_addr[1] ||
347                 __local_ip_addr[2] || __local_ip_addr[3]) {
348                 /* success */
349                 __udp_remove_listener(IPPORT_BOOTPC);
350                 return 0;
351             }
352 #endif
353             if (retry < MAX_RETRIES) {
354                 if (_rb_break(1)) {
355                     // The user typed ^C on the console
356                     abort = true;
357                     break;
358                 }
359                 start--; /* account for time spent in _rb_break() */
360             }
361         } while ((int)(MS_TICKS_DELAY() - start) < RETRY_TIME);
362     } while (!abort && (retry-- > 0));
363
364     // timed out
365     __udp_remove_listener(IPPORT_BOOTPC);
366     // Restore any previous IP address
367     memcpy(__local_ip_addr, saved_ip_addr, sizeof(__local_ip_addr));
368     return -1;
369 }
370
371