]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/net/bootp.c
RedBoot Release TX53-v3 2012-02-08
[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                 // Quietly 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 }