1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  DHCP Client 				File: net_dhcp.c
5    *
6    *  This module contains a DHCP client.
7    *
8    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47#include "lib_types.h"
48#include "lib_string.h"
49#include "lib_queue.h"
50#include "lib_malloc.h"
51#include "lib_printf.h"
52
53#include "env_subr.h"
54
55#include "cfe_iocb.h"
56#include "cfe_devfuncs.h"
57#include "cfe_ioctl.h"
58#include "cfe_timer.h"
59#include "cfe_error.h"
60
61#include "net_ebuf.h"
62#include "net_ether.h"
63
64#include "cfe.h"
65
66#include "net_api.h"
67
68/*  *********************************************************************
69    *  Constants
70    ********************************************************************* */
71
72
73#define UDP_PORT_BOOTPS	67
74#define UDP_PORT_BOOTPC	68
75
76#define DHCP_REQ_TIMEOUT	(1*CFE_HZ)
77#define DHCP_NUM_RETRIES	8
78
79#define DHCP_OP_BOOTREQUEST	1
80#define DHCP_OP_BOOTREPLY	2
81
82#define DHCP_HWTYPE_ETHERNET	1
83
84#define DHCP_TAG_FUNCTION	53
85#define DHCP_FUNCTION_DISCOVER	1
86#define DHCP_FUNCTION_OFFER	2
87#define DHCP_FUNCTION_REQUEST	3
88#define DHCP_FUNCTION_ACK	5
89
90#define DHCP_TAG_NETMASK	1
91#define DHCP_TAG_DOMAINNAME	0x0F
92#define DHCP_TAG_GATEWAY	0x03
93#define DHCP_TAG_NAMESERVER	0x06
94#define DHCP_TAG_SWAPSERVER	0x10
95#define DHCP_TAG_ROOTPATH	0x11
96#define DHCP_TAG_EXTENSIONS	0x12
97#define DHCP_TAG_SERVERIDENT	54
98#define DHCP_TAG_PARAMLIST	55
99#define DHCP_TAG_CLIENTID	61
100#define DHCP_TAG_CLASSID	60
101#define DHCP_TAG_REQADDR        50
102#define DHCP_TAG_LEASE_TIME	51
103#define DHCP_TAG_SCRIPT		130		/* CFE extended option */
104#define DHCP_TAG_OPTIONS	131		/* CFE extended option */
105
106#define DHCP_TAG_END		0xFF
107
108#define DHCP_MAGIC_NUMBER	0x63825363
109
110/*#define _DEBUG_*/
111
112
113/*  *********************************************************************
114    *  dhcp_set_envvars(reply)
115    *
116    *  Using the supplied DHCP reply data, set environment variables
117    *  to contain data from the reply.
118    *
119    *  Input parameters:
120    *  	   reply - dhcp reply
121    *
122    *  Return value:
123    *  	   nothing
124    ********************************************************************* */
125
126void dhcp_set_envvars(dhcpreply_t *reply)
127{
128    char buffer[50];
129
130    env_delenv("BOOT_SERVER");
131    env_delenv("BOOT_FILE");
132    env_delenv("BOOT_SCRIPT");
133    env_delenv("BOOT_OPTIONS");
134
135    if (reply->dr_bootserver[0] | reply->dr_bootserver[1] |
136	reply->dr_bootserver[2] | reply->dr_bootserver[3]) {
137	sprintf(buffer,"%I",reply->dr_bootserver);
138	env_setenv("BOOT_SERVER",buffer,ENV_FLG_BUILTIN);
139	}
140
141    if (reply->dr_bootfile && reply->dr_bootfile[0]) {
142	env_setenv("BOOT_FILE",reply->dr_bootfile,ENV_FLG_BUILTIN);
143	}
144
145    if (reply->dr_script && reply->dr_script[0]) {
146	env_setenv("BOOT_SCRIPT",reply->dr_script,ENV_FLG_BUILTIN);
147	}
148
149    if (reply->dr_options && reply->dr_options[0]) {
150	env_setenv("BOOT_OPTIONS",reply->dr_options,ENV_FLG_BUILTIN);
151	}
152}
153
154/*  *********************************************************************
155    *  dhcp_dumptag(tag,len,buf)
156    *
157    *  Dump out information from a DHCP tag
158    *
159    *  Input parameters:
160    *  	   tag - tag ID
161    *  	   len - length of data
162    *  	   buf - data
163    *
164    *  Return value:
165    *  	   nothing
166    ********************************************************************* */
167
168#ifdef _DEBUG_
169static void dhcp_dumptag(uint8_t tag,uint8_t len,uint8_t *buf)
170{
171    unsigned int idx;
172
173    xprintf("DHCP: ");
174
175    switch (tag) {
176	case DHCP_TAG_FUNCTION:
177	    xprintf("DHCP Function: %d\n",buf[0]);
178	    break;
179	case DHCP_TAG_LEASE_TIME:
180	    idx = (((unsigned int) buf[0]) << 24) |
181		(((unsigned int) buf[1]) << 16) |
182		(((unsigned int) buf[2]) << 8) |
183		(((unsigned int) buf[3]) << 0);
184	    xprintf("Lease Time: %d seconds\n",idx);
185	    break;
186	case DHCP_TAG_SERVERIDENT:
187	    xprintf("DHCP Server ID: %d.%d.%d.%d\n",
188		   buf[0],buf[1],buf[2],buf[3]);
189	    break;
190	case DHCP_TAG_NETMASK:
191	    xprintf("Netmask: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]);
192	    break;
193	case DHCP_TAG_DOMAINNAME:
194	    buf[len] = 0;
195	    xprintf("Domain: %s\n",buf);
196	    break;
197	case DHCP_TAG_GATEWAY:
198	    xprintf("Gateway: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]);
199	    break;
200	case DHCP_TAG_NAMESERVER:
201	    xprintf("Nameserver: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]);
202	    break;
203	case DHCP_TAG_SWAPSERVER:
204	    buf[len] = 0;
205	    xprintf("Swapserver: %s\n",buf);
206	    break;
207	case DHCP_TAG_ROOTPATH:
208	    buf[len] = 0;
209	    xprintf("Rootpath: %s\n",buf);
210	    break;
211	case DHCP_TAG_SCRIPT:
212	    buf[len] = 0;
213	    xprintf("CFE Script: %s\n",buf);
214	    break;
215	case DHCP_TAG_OPTIONS:
216	    buf[len] = 0;
217	    xprintf("CFE Boot Options: %s\n",buf);
218	    break;
219	default:
220	    xprintf("Tag %d len %d [",tag,len);
221	    for (idx = 0; idx < len; idx++) {
222		if ((buf[idx] >= 32) && (buf[idx] < 127)) xprintf("%c",buf[idx]);
223		}
224	    xprintf("]\n");
225	    break;
226
227	}
228}
229#endif
230
231/*  *********************************************************************
232    *  dhcp_free_reply(reply)
233    *
234    *  Free memory associated with a DHCP reply.
235    *
236    *  Input parameters:
237    *  	   reply - pointer to DHCP reply
238    *
239    *  Return value:
240    *  	   nothing
241    ********************************************************************* */
242
243void dhcp_free_reply(dhcpreply_t *reply)
244{
245    if (reply->dr_hostname)   KFREE(reply->dr_hostname);
246    if (reply->dr_domainname) KFREE(reply->dr_domainname);
247    if (reply->dr_bootfile)   KFREE(reply->dr_bootfile);
248    if (reply->dr_rootpath)   KFREE(reply->dr_rootpath);
249    if (reply->dr_swapserver) KFREE(reply->dr_swapserver);
250    if (reply->dr_script)     KFREE(reply->dr_script);
251    if (reply->dr_options)    KFREE(reply->dr_options);
252    KFREE(reply);
253}
254
255/*  *********************************************************************
256    *  dhcp_build_discover()
257    *
258    *  Build a DHCP DISCOVER packet
259    *
260    *  Input parameters:
261    *  	   hwaddr - our hardware address
262    *      idptr - pointer to int to receive the DHCP packet ID
263    *      serveraddr - pointer to server address (REQUEST) or
264    *                   NULL (DISCOVER)
265    *      ebufptr - receives pointer to ebuf
266    *
267    *  Return value:
268    *  	   0 if ok, else error code
269    ********************************************************************* */
270
271static int dhcp_build_discover(uint8_t *hwaddr,
272			       uint32_t id,
273			       ebuf_t **ebufptr)
274{
275    uint8_t ipaddr[IP_ADDR_LEN];
276    ebuf_t *buf;
277    uint8_t junk[128];
278
279    /*
280     * Get a buffer and fill it in.
281     */
282
283    ipaddr[0] = 0; ipaddr[1] = 0; ipaddr[2] = 0; ipaddr[3] = 0;
284    memset(junk,0,sizeof(junk));
285
286    buf = udp_alloc();
287
288    if (buf == NULL) {
289	return CFE_ERR_NOMEM;
290	}
291
292    memset(buf->eb_ptr,0,548);
293
294    ebuf_append_u8(buf,DHCP_OP_BOOTREQUEST);
295    ebuf_append_u8(buf,DHCP_HWTYPE_ETHERNET);
296    ebuf_append_u8(buf,ENET_ADDR_LEN);
297    ebuf_append_u8(buf,0);			/* hops */
298    ebuf_append_u32_be(buf,id);
299    ebuf_append_u16_be(buf,0);			/* sec since boot */
300    ebuf_append_u16_be(buf,0);			/* flags */
301
302    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* ciaddr */
303    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* yiaddr */
304    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* siaddr */
305    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* giaddr */
306
307    ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); /* chaddr */
308    ebuf_append_bytes(buf,junk,10);		 /* rest of chaddr */
309    ebuf_append_bytes(buf,junk,64);		/* sname */
310    ebuf_append_bytes(buf,junk,128);		/* file */
311
312    ebuf_append_u32_be(buf,DHCP_MAGIC_NUMBER);
313
314    ebuf_append_u8(buf,DHCP_TAG_FUNCTION);	/* function code */
315    ebuf_append_u8(buf,1);
316    ebuf_append_u8(buf,DHCP_FUNCTION_DISCOVER);
317
318    ebuf_append_u8(buf,DHCP_TAG_PARAMLIST);
319    ebuf_append_u8(buf,8);		/* count of tags that follow */
320    ebuf_append_u8(buf,DHCP_TAG_NETMASK);
321    ebuf_append_u8(buf,DHCP_TAG_DOMAINNAME);
322    ebuf_append_u8(buf,DHCP_TAG_GATEWAY);
323    ebuf_append_u8(buf,DHCP_TAG_NAMESERVER);
324    ebuf_append_u8(buf,DHCP_TAG_SWAPSERVER);
325    ebuf_append_u8(buf,DHCP_TAG_ROOTPATH);
326    ebuf_append_u8(buf,DHCP_TAG_SCRIPT);
327    ebuf_append_u8(buf,DHCP_TAG_OPTIONS);
328
329
330    ebuf_append_u8(buf,DHCP_TAG_END);		/* terminator */
331
332    /*
333     * Return the packet
334     */
335
336    *ebufptr = buf;
337
338    return 0;
339}
340
341/*  *********************************************************************
342    *  dhcp_build_request()
343    *
344    *  Build a DHCP DISCOVER or REQUEST packet
345    *
346    *  Input parameters:
347    *  	   hwaddr - our hardware address
348    *      idptr - pointer to int to receive the DHCP packet ID
349    *      serveraddr - pointer to server address (REQUEST) or
350    *                   NULL (DISCOVER)
351    *      ebufptr - receives pointer to ebuf
352    *
353    *  Return value:
354    *  	   0 if ok, else error code
355    ********************************************************************* */
356
357static int dhcp_build_request(uint8_t *hwaddr,
358			      uint32_t id,
359			      uint8_t *serveraddr,
360			      uint8_t *reqip,
361			      ebuf_t **ebufptr)
362{
363    uint8_t ipaddr[IP_ADDR_LEN];
364    ebuf_t *buf;
365    uint8_t junk[128];
366
367    /*
368     * Get a buffer and fill it in.
369     */
370
371    ipaddr[0] = 0; ipaddr[1] = 0; ipaddr[2] = 0; ipaddr[3] = 0;
372    memset(junk,0,sizeof(junk));
373
374    buf = udp_alloc();
375
376    if (buf == NULL) {
377	return CFE_ERR_NOMEM;
378	}
379
380    memset(buf->eb_ptr,0,548);
381
382    ebuf_append_u8(buf,DHCP_OP_BOOTREQUEST);
383    ebuf_append_u8(buf,DHCP_HWTYPE_ETHERNET);
384    ebuf_append_u8(buf,ENET_ADDR_LEN);
385    ebuf_append_u8(buf,0);			/* hops */
386    ebuf_append_u32_be(buf,id);
387    ebuf_append_u16_be(buf,0);			/* sec since boot */
388    ebuf_append_u16_be(buf,0);			/* flags */
389
390    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* ciaddr */
391    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* yiaddr */
392    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* siaddr */
393    ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN);	/* giaddr */
394
395    ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); /* chaddr */
396    ebuf_append_bytes(buf,junk,10);		 /* rest of chaddr */
397
398    ebuf_append_bytes(buf,junk,64);		/* sname */
399
400    ebuf_append_bytes(buf,junk,128);		/* file */
401
402    ebuf_append_u32_be(buf,DHCP_MAGIC_NUMBER);
403
404    ebuf_append_u8(buf,DHCP_TAG_FUNCTION);	/* function code */
405    ebuf_append_u8(buf,1);
406
407    ebuf_append_u8(buf,DHCP_FUNCTION_REQUEST);
408
409    ebuf_append_u8(buf,DHCP_TAG_REQADDR);
410    ebuf_append_u8(buf,IP_ADDR_LEN);
411    ebuf_append_bytes(buf,reqip,IP_ADDR_LEN);
412
413    ebuf_append_u8(buf,DHCP_TAG_SERVERIDENT); /* server ID */
414    ebuf_append_u8(buf,IP_ADDR_LEN);
415    ebuf_append_bytes(buf,serveraddr,IP_ADDR_LEN);
416
417    ebuf_append_u8(buf,DHCP_TAG_CLIENTID);	/* client ID */
418    ebuf_append_u8(buf,7);
419    ebuf_append_u8(buf,1);
420    ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN);
421
422    ebuf_append_u8(buf,DHCP_TAG_PARAMLIST);
423    ebuf_append_u8(buf,8);		/* count of tags that follow */
424    ebuf_append_u8(buf,DHCP_TAG_NETMASK);
425    ebuf_append_u8(buf,DHCP_TAG_DOMAINNAME);
426    ebuf_append_u8(buf,DHCP_TAG_GATEWAY);
427    ebuf_append_u8(buf,DHCP_TAG_NAMESERVER);
428    ebuf_append_u8(buf,DHCP_TAG_SWAPSERVER);
429    ebuf_append_u8(buf,DHCP_TAG_ROOTPATH);
430    ebuf_append_u8(buf,DHCP_TAG_SCRIPT);
431    ebuf_append_u8(buf,DHCP_TAG_OPTIONS);
432
433
434    ebuf_append_u8(buf,DHCP_TAG_END);		/* terminator */
435
436    /*
437     * Return the packet
438     */
439
440    *ebufptr = buf;
441
442    return 0;
443}
444
445
446/*  *********************************************************************
447    *  dhcp_wait_reply(s,id,reply,serveraddr)
448    *
449    *  Wait for a reply from the DHCP server
450    *
451    *  Input parameters:
452    *  	   s - socket
453    *  	   id - ID of request we sent
454    *  	   reply - structure to store results in
455    *	   expfcode - expected DHCP_FUNCTION tag value
456    *
457    *  Return value:
458    *  	   0 if ok (reply found)
459    *  	   else error
460    ********************************************************************* */
461
462static int dhcp_wait_reply(int s,uint32_t id,dhcpreply_t *reply,int expfcode)
463{
464    uint32_t tmpd;
465    uint8_t tmpb;
466    int64_t timer;
467    int nres = 0;
468    uint8_t ciaddr[IP_ADDR_LEN];
469    uint8_t yiaddr[IP_ADDR_LEN];
470    uint8_t siaddr[IP_ADDR_LEN];
471    uint8_t giaddr[IP_ADDR_LEN];
472    uint8_t junk[128];
473    char *hostname;
474    char *bootfile;
475    ebuf_t *buf;
476    int fcode = -1;
477
478    /*
479     * Set a timer for the response
480     */
481
482    TIMER_SET(timer,DHCP_REQ_TIMEOUT);
483
484    /*
485     * Start waiting...
486     */
487
488    while (!TIMER_EXPIRED(timer)) {
489	POLL();
490
491	buf = udp_recv(s);
492	if (!buf) continue;
493
494	ebuf_get_u8(buf,tmpb);
495	if (tmpb != DHCP_OP_BOOTREPLY) {
496	    goto drop;
497	    }
498
499	ebuf_get_u8(buf,tmpb);
500	if (tmpb != DHCP_HWTYPE_ETHERNET) {
501	    goto drop;
502	    }
503
504	ebuf_get_u8(buf,tmpb);
505	if (tmpb != ENET_ADDR_LEN) {
506	    goto drop;
507	    }
508
509	ebuf_skip(buf,1);			/* hops */
510
511	ebuf_get_u32_be(buf,tmpd);		/* check ID */
512	if (tmpd != id) {
513	    goto drop;
514	    }
515
516	ebuf_skip(buf,2);			/* seconds since boot */
517	ebuf_skip(buf,2);			/* flags */
518
519	ebuf_get_bytes(buf,ciaddr,IP_ADDR_LEN);
520	ebuf_get_bytes(buf,yiaddr,IP_ADDR_LEN);
521	ebuf_get_bytes(buf,siaddr,IP_ADDR_LEN);
522	ebuf_get_bytes(buf,giaddr,IP_ADDR_LEN);
523
524	ebuf_skip(buf,16);			/* hardware address */
525	hostname = ebuf_ptr(buf);
526	ebuf_skip(buf,64);
527	bootfile = ebuf_ptr(buf);
528
529	ebuf_skip(buf,128);
530
531#ifdef _DEBUG_
532	xprintf("Client  IP: %d.%d.%d.%d\n",ciaddr[0],ciaddr[1],ciaddr[2],ciaddr[3]);
533	xprintf("Your    IP: %d.%d.%d.%d\n",yiaddr[0],yiaddr[1],yiaddr[2],yiaddr[3]);
534	xprintf("Server  IP: %d.%d.%d.%d\n",siaddr[0],siaddr[1],siaddr[2],siaddr[3]);
535	xprintf("Gateway IP: %d.%d.%d.%d\n",giaddr[0],giaddr[1],giaddr[2],giaddr[3]);
536	xprintf("hostname: %s\n",hostname);
537	xprintf("boot file: %s\n",bootfile);
538#endif
539
540	memcpy(reply->dr_ipaddr,yiaddr,IP_ADDR_LEN);
541	memcpy(reply->dr_gateway,giaddr,IP_ADDR_LEN);
542	memcpy(reply->dr_bootserver,siaddr,IP_ADDR_LEN);
543	if (*hostname) reply->dr_hostname = strdup(hostname);
544	if (*bootfile) reply->dr_bootfile = strdup(bootfile);
545
546	/*
547	 * Test for options - look for magic number
548	 */
549
550	ebuf_get_u32_be(buf,tmpd);
551
552	memcpy(reply->dr_dhcpserver,buf->eb_usrptr,IP_ADDR_LEN);
553
554	if (tmpd == DHCP_MAGIC_NUMBER) {
555	    uint8_t tag;
556	    uint8_t len;
557
558	    while (buf->eb_length > 0) {
559		ebuf_get_u8(buf,tag);
560		if (tag == DHCP_TAG_END) break;
561		ebuf_get_u8(buf,len);
562		ebuf_get_bytes(buf,junk,len);
563
564#ifdef _DEBUG_
565		dhcp_dumptag(tag,len,junk);
566#endif
567
568		switch (tag) {
569		    case DHCP_TAG_FUNCTION:
570			fcode = (uint8_t) junk[0];
571			break;
572		    case DHCP_TAG_NETMASK:
573			memcpy(reply->dr_netmask,junk,IP_ADDR_LEN);
574			break;
575		    case DHCP_TAG_GATEWAY:
576			memcpy(reply->dr_gateway,junk,IP_ADDR_LEN);
577			break;
578		    case DHCP_TAG_NAMESERVER:
579			memcpy(reply->dr_nameserver,junk,IP_ADDR_LEN);
580			break;
581		    case DHCP_TAG_DOMAINNAME:
582			junk[len] = 0;
583			if (len) reply->dr_domainname = strdup(junk);
584			break;
585		    case DHCP_TAG_SWAPSERVER:
586			junk[len] = 0;
587			if (len) reply->dr_swapserver = strdup(junk);
588			break;
589		    case DHCP_TAG_SERVERIDENT:
590			if (len == IP_ADDR_LEN) {
591			    memcpy(reply->dr_dhcpserver,junk,len);
592			    }
593			break;
594		    case DHCP_TAG_ROOTPATH:
595			junk[len] = 0;
596			if (len) reply->dr_rootpath = strdup(junk);
597			break;
598		    case DHCP_TAG_SCRIPT:
599			junk[len] = 0;
600			if (len) reply->dr_script = strdup(junk);
601			break;
602		    case DHCP_TAG_OPTIONS:
603			junk[len] = 0;
604			if (len) reply->dr_options = strdup(junk);
605			break;
606		    }
607		}
608	    }
609
610	if (fcode != expfcode) {
611	    goto drop;
612	    }
613
614	udp_free(buf);
615	nres++;
616	break;
617
618    drop:
619	udp_free(buf);
620	}
621
622    if (nres > 0) return 0;
623    else return CFE_ERR_TIMEOUT;
624}
625
626
627/*  *********************************************************************
628    *  dhcp_do_dhcpdiscover(s,hwaddr,reply)
629    *
630    *  Request an IP address from the DHCP server.  On success, the
631    *  dhcpreply_t structure will be filled in
632    *
633    *  Input parameters:
634    *      s - udp socket
635    *      hwaddr - our hardware address
636    *  	   reply - pointer to reply buffer.
637    *
638    *  Return value:
639    *  	   0 if a response was received
640    *  	   else error code
641    ********************************************************************* */
642
643static int dhcp_do_dhcpdiscover(int s,uint8_t *hwaddr,dhcpreply_t *reply)
644{
645    ebuf_t *buf;
646    uint32_t id;
647    uint8_t ipaddr[IP_ADDR_LEN];
648    int res;
649
650    /*
651     * Packet ID is the current time
652     */
653
654    id = (uint32_t)cfe_ticks;
655
656    /*
657     * Build the DISCOVER request
658     */
659
660    res = dhcp_build_discover(hwaddr,id,&buf);
661    if (res != 0) return res;
662
663    /*
664     * Send the packet to the IP broadcast (255.255.255.255)
665     */
666
667    ipaddr[0] = 0xFF; ipaddr[1] = 0xFF; ipaddr[2] = 0xFF; ipaddr[3] = 0xFF;
668    udp_send(s,buf,ipaddr);
669
670    /*
671     * Wait for a reply
672     */
673
674    res = dhcp_wait_reply(s,id,reply,DHCP_FUNCTION_OFFER);
675
676    return res;
677}
678
679
680/*  *********************************************************************
681    *  dhcp_do_dhcprequest(s,hwaddr,reply,discover_reply)
682    *
683    *  Request an IP address from the DHCP server.  On success, the
684    *  dhcpreply_t structure will be filled in
685    *
686    *  Input parameters:
687    *      s - udp socket
688    *      hwaddr - our hardware address
689    *  	   reply - pointer to reply buffer.
690    *	   discover_reply - pointer to previously received DISCOVER data
691    *
692    *  Return value:
693    *  	   0 if a response was received
694    *  	   else error code
695    ********************************************************************* */
696
697static int dhcp_do_dhcprequest(int s,uint8_t *hwaddr,
698			       dhcpreply_t *reply,
699			       dhcpreply_t *discover_reply)
700{
701    ebuf_t *buf;
702    uint32_t id;
703    uint8_t ipaddr[IP_ADDR_LEN];
704    int res;
705
706    /*
707     * Packet ID is the current time
708     */
709
710    id = (uint32_t)cfe_ticks;
711
712    /*
713     * Build the DHCP REQUEST request
714     */
715
716    res = dhcp_build_request(hwaddr,
717			     id,
718			     discover_reply->dr_dhcpserver,
719			     discover_reply->dr_ipaddr,
720			     &buf);
721
722    if (res != 0) return res;
723
724    /*
725     * Send the packet to the IP broadcast (255.255.255.255)
726     */
727
728    ipaddr[0] = 0xFF; ipaddr[1] = 0xFF; ipaddr[2] = 0xFF; ipaddr[3] = 0xFF;
729    udp_send(s,buf,ipaddr);
730
731    /*
732     * Wait for a reply
733     */
734
735    res = dhcp_wait_reply(s,id,reply,DHCP_FUNCTION_ACK);
736
737    return res;
738}
739
740
741/*  *********************************************************************
742    *  dhcp_bootrequest(reply)
743    *
744    *  Request an IP address from the DHCP server.  On success, the
745    *  dhcpreply_t structure will be allocated.
746    *
747    *  Input parameters:
748    *  	   reply - pointer to pointer to reply.
749    *
750    *  Return value:
751    *  	   0 if no responses received
752    *     >0 for some responses received
753    *  	   else error code
754    ********************************************************************* */
755
756int dhcp_bootrequest(dhcpreply_t **rep)
757{
758    uint8_t *hwaddr;
759    int s;
760    dhcpreply_t *discover_reply;
761    dhcpreply_t *request_reply;
762    int nres = 0;
763    int retries;
764    uint32_t id;
765
766    id = (uint32_t) cfe_ticks;
767
768    /*
769     * Start with empty reply buffers.  Since we use a portion of the
770     * discover reply in the request, we'll keep two of them.
771     */
772
773    discover_reply = KMALLOC(sizeof(dhcpreply_t),0);
774    if (discover_reply == NULL) {
775	return CFE_ERR_NOMEM;
776	}
777    memset(discover_reply,0,sizeof(dhcpreply_t));
778
779    request_reply = KMALLOC(sizeof(dhcpreply_t),0);
780    if (request_reply == NULL) {
781	KFREE(discover_reply);
782	return CFE_ERR_NOMEM;
783	}
784    memset(request_reply,0,sizeof(dhcpreply_t));
785
786
787    /*
788     * Get our hw addr
789     */
790
791    hwaddr = net_getparam(NET_HWADDR);
792    if (!hwaddr) {
793	KFREE(discover_reply);
794	KFREE(request_reply);
795	return CFE_ERR_NETDOWN;
796	}
797
798    /*
799     * Open UDP port
800     */
801
802    s = udp_socket(UDP_PORT_BOOTPS);
803    if (s < 0) {
804	KFREE(discover_reply);
805	KFREE(request_reply);
806	return CFE_ERR_NOHANDLES;
807	}
808
809    udp_bind(s,UDP_PORT_BOOTPC);
810
811    /*
812     * Do the boot request.  Start by sending the OFFER message
813     */
814
815    nres = CFE_ERR_TIMEOUT;
816    for (retries = 0; retries < DHCP_NUM_RETRIES; retries++) {
817	nres = dhcp_do_dhcpdiscover(s,hwaddr,discover_reply);
818	if (nres == 0) break;
819	if (nres == CFE_ERR_TIMEOUT) continue;
820	break;
821	}
822
823    /*
824     * If someone sent us a response, send the REQUEST message
825     * to get a lease.
826     */
827
828    if (nres == 0) {
829
830	/*
831	 * Now, send the REQUEST message and get a response.
832	 */
833
834	for (retries = 0; retries < DHCP_NUM_RETRIES; retries++) {
835	    nres = dhcp_do_dhcprequest(s,hwaddr,
836				       request_reply,
837				       discover_reply);
838	    if (nres == 0) break;
839	    if (nres == CFE_ERR_TIMEOUT) continue;
840	    break;
841	    }
842	}
843
844    /*
845     * All done with the discover reply.
846     */
847
848    dhcp_free_reply(discover_reply);
849
850    /*
851     * All done with UDP
852     */
853
854    udp_close(s);
855
856    /*
857     * Return the reply info.
858     */
859
860    if (nres == 0) {
861	*rep = request_reply;
862	}
863    else {
864	*rep = NULL;
865	dhcp_free_reply(request_reply);
866	}
867
868    return nres;
869}
870