]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/common/v2_0/src/dhcp_prot.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / common / v2_0 / src / dhcp_prot.c
1 /*==========================================================================
2 //
3 //      dhcp_prot.c
4 //
5 //      DHCP protocol implementation for DHCP client
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) 2003 Andrew Lunn
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):   hmt
45 // Contributors: gthomas, andrew.lunn@ascom.ch
46 // Date:        2000-07-01
47 // Purpose:     DHCP support
48 // Description: 
49 //
50 //####DESCRIPTIONEND####
51 //
52 //========================================================================*/
53
54 #include <pkgconf/system.h>
55 #include <pkgconf/net.h>
56
57 #ifdef CYGPKG_NET_DHCP
58
59 #ifdef CYGPKG_NET_SNTP
60 #include <pkgconf/net_sntp.h>
61 #endif /* CYGPKG_NET_SNTP */
62
63 #if 0
64 #define perror( txt ) // nothing
65 #endif
66
67 #include <network.h>
68 #include <dhcp.h>
69 #include <errno.h>
70
71 #include <cyg/infra/cyg_ass.h>
72
73 #ifdef INET6
74 #include <net/if_var.h>
75 #include <netinet6/in6_var.h>
76 #endif
77
78 extern int  cyg_arc4random(void);
79
80 #ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
81 static char dhcp_hostname[CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN+1];
82
83 // Set the hostname used by the DHCP TAG_HOST_NAME option.  We
84 // copy the callers name into a private buffer, since we don't
85 // know the context in which the callers hostname was allocated.
86 void dhcp_set_hostname(char *hostname)
87 {
88     CYG_ASSERT( (strlen(hostname)<=CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN), "dhcp hostname too long" );
89     strncpy(dhcp_hostname, hostname, CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN);
90 }
91 #endif
92
93 /* Forward reference prototypes. */
94 static int unset_tag( struct bootp *ppkt, unsigned char tag );
95
96 // ------------------------------------------------------------------------
97 // Returns a pointer to the end of dhcp message (or NULL if invalid)
98 // meaning the address of the byte *after* the TAG_END token in the vendor
99 // data.
100
101 static unsigned char *
102 scan_dhcp_size( struct bootp *ppkt )
103 {
104     unsigned char *op;
105     
106     op = &ppkt->bp_vend[0];
107     // First check for the cookie!
108     if ( op[0] !=  99 ||
109          op[1] != 130 ||
110          op[2] !=  83 ||
111          op[3] !=  99 ) {
112         CYG_FAIL( "Bad DHCP cookie" );
113         return NULL;
114     }
115     op += 4;
116
117     // This will only scan the options field.
118     while (*op != TAG_END) {
119         if ( *op == TAG_PAD ) {
120             op++;
121         } else {
122           op += *(op+1)+2;
123         }
124         if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
125             CYG_FAIL( "Oversize DHCP packet in dhcp_size" );
126             return NULL;
127         }
128     }
129     // Check op has not gone wild
130     CYG_ASSERT( op > (unsigned char *)(&ppkt[0]), "op pointer underflow!" );
131     // Compare op with non-existent "next" struct bootp in the array.
132     CYG_ASSERT( op < (unsigned char *)(&ppkt[1]), "op pointer overflow!" );
133     return op + 1; // Address of first invalid byte
134 }
135
136 // ------------------------------------------------------------------------
137 // Get the actual packet size of an initialized buffer
138
139 static int
140 dhcp_size( struct bootp *ppkt )
141 {
142     unsigned char *op;
143
144     op = scan_dhcp_size( ppkt );
145     if ( !op ) return 0;
146     return (op - (unsigned char *)ppkt);
147 }
148
149
150 // ------------------------------------------------------------------------
151 // Get the actual packet size of an initialized buffer
152 // This will also pad the packet with 0 if length is less
153 // than BP_STD_TX_MINPKTSZ.
154
155 static int
156 dhcp_size_for_send( struct bootp *ppkt )
157 {
158     unsigned char *op;
159
160     op = scan_dhcp_size( ppkt );
161     if ( !op ) return 0; // Better not scribble!
162     // Zero extra bytes until the packet is large enough.
163     for ( ; op < (((unsigned char *)ppkt) + BP_STD_TX_MINPKTSZ); op++ )
164         *op = 0;
165     return (op - (unsigned char *)ppkt);
166 }
167
168 // ------------------------------------------------------------------------
169 // Insert/set an option value in an initialized buffer
170
171 static int
172 set_fixed_tag( struct bootp *ppkt,
173                unsigned char tag,
174                cyg_uint32 value,
175                int len)
176 {
177     unsigned char *op;
178
179     // Initially this will only scan the options field.
180     
181     op = &ppkt->bp_vend[4];
182     while (*op != TAG_END) {
183         if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
184             CYG_FAIL( "Oversize DHCP packet in set_fixed_tag" );
185             return false;
186         }
187         if (*op == tag)                 // Found it...
188             break;
189         if ( *op == TAG_PAD ) {
190             op++;
191         } else {
192           op += *(op+1)+2;
193         }
194     }
195     
196     if (*op == tag) { // Found it...
197         /* There are three possibilities:
198          * 1) *(op+1) == len
199          * 2) *(op+1) > len
200          * 3) *(op+1) < len
201          * For 1, just overwrite the existing option data.
202          * For 2, overwrite the existing option data and pullup the
203          *        remaining option data (if any).
204          * For 3, pullup any remaining option data to remove the option
205          *        and then add the option to the end.
206          * For simplicity, for case 2 and 3, we just call unset_tag()
207          * and re-add the option to the end.
208          */
209         if ( *(op+1) != len ) {
210             /* Remove existing option entry. */
211             unset_tag(ppkt, tag);
212             /* Adjust the op pointer to re-add at the end. */
213             op = scan_dhcp_size(ppkt);
214             CYG_ASSERT(op!=NULL, "Invalid options size in set_fixed_tag" );
215             op--;
216             CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_fixed_tag");
217             if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
218                 CYG_FAIL( "Oversize DHCP packet in set_fixed_tag replace" );
219                 return false;
220             }
221             *op = tag;
222             *(op+1) = len;
223             *(op + len + 2) = TAG_END;
224         }
225     }
226     else { // overwrite the end tag and install a new one
227         if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
228             CYG_FAIL( "Oversize DHCP packet in set_fixed_tag append" );
229             return false;
230         }
231         *op = tag;
232         *(op+1) = len;
233         *(op + len + 2) = TAG_END;
234     }
235     // and insert the value.  Net order is BE.
236     op += len + 2 - 1;              // point to end of value
237     while ( len-- > 0 ) {
238         *op-- = (unsigned char)(value & 255);
239         value >>= 8;
240     }
241     return true;
242 }
243
244 static int
245 set_variable_tag( struct bootp *ppkt,
246                unsigned char tag,
247                cyg_uint8 *pvalue,
248                int len)
249 {
250     unsigned char *op;
251
252     // Initially this will only scan the options field.
253     op = &ppkt->bp_vend[4];
254     while (*op != TAG_END) {
255         if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
256             CYG_FAIL( "Oversize DHCP packet in set_variable_tag" );
257             return false;
258         }
259         if (*op == tag)                 // Found it...
260             break;
261         if ( *op == TAG_PAD ) {
262             op++;
263         } else {
264           op += *(op+1)+2;
265         }
266     }
267     
268     if (*op == tag) { // Found it...
269         /* There are three possibilities:
270          * 1) *(op+1) == len
271          * 2) *(op+1) > len
272          * 3) *(op+1) < len
273          * For 1, just overwrite the existing option data.
274          * For 2, overwrite the existing option data and pullup the
275          *        remaining option data (if any).
276          * For 3, pullup any remaining option data to remove the option
277          *        and then add the option to the end.
278          * For simplicity, for case 2 and 3, we just call unset_tag()
279          * and re-add the option to the end.
280          */
281         if ( *(op+1) != len ) {
282             /* Remove existing option entry. */
283             unset_tag(ppkt, tag);
284             /* Adjust the op pointer to re-add at the end. */
285             op = scan_dhcp_size(ppkt);
286             CYG_ASSERT(op!=NULL, "Invalid options size in set_variable_tag" );
287             op--;
288             CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_variable_tag");
289             if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
290                 CYG_FAIL( "Oversize DHCP packet in set_variable_tag replace" );
291                 return false;
292             }
293             *op = tag;
294             *(op+1) = len;
295             *(op + len + 2) = TAG_END;
296         }
297     }
298     else { // overwrite the end tag and install a new one
299         if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
300             CYG_FAIL( "Oversize DHCP packet in set_variable_tag append" );
301             return false;
302         }
303         *op = tag;
304         *(op+1) = len;
305         *(op + len + 2) = TAG_END;
306     }
307     // and insert the value.  No order is implied.
308     op += 2;               // point to start of value
309     while ( len-- > 0 ) {
310         *op++ = *pvalue++;
311     }
312     return true;
313 }
314
315 static int
316 unset_tag( struct bootp *ppkt,
317            unsigned char tag )
318 {
319     unsigned char *op, *nextp = 0, *killp = 0;
320
321     // Initially this will only scan the options field.
322     
323     op = &ppkt->bp_vend[4];
324     while (*op != TAG_END) {
325         if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
326             CYG_FAIL( "Oversize DHCP packet in unset_tag" );
327             return false;
328         }
329         if (*op == tag) {               // Found it...
330             killp = op;                 // item to kill
331             nextp = op + *(op+1)+2;     // next item address
332         }
333         if ( *op == TAG_PAD ) {
334             op++;
335         } else {
336           op += *(op+1)+2;
337         }
338     }
339
340     if ( !killp )
341         return false;
342
343     // Obliterate the found op by copying down: *op is the end.
344     while( nextp <= op )                // <= to copy the TAG_END too.
345         *killp++ = *nextp++;
346     
347     return true;
348 }
349
350 // ------------------------------------------------------------------------
351 // Bring up an interface enough to broadcast, before we know who we are
352
353 static int
354 bring_half_up(const char *intf, struct ifreq *ifrp )
355 {
356     int s = -1;
357     int one = 1;
358
359     struct sockaddr_in *addrp;
360     struct ecos_rtentry route;
361     int retcode = false;
362
363     // Ensure clean slate
364     cyg_route_reinit();  // Force any existing routes to be forgotten
365
366     s = socket(AF_INET, SOCK_DGRAM, 0);
367     if (s < 0) {
368         perror("socket");
369         goto out;
370     }
371
372     if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
373         perror("setsockopt");
374         goto out;
375     }
376
377     addrp = (struct sockaddr_in *) &ifrp->ifr_addr;
378     memset(addrp, 0, sizeof(*addrp));
379     addrp->sin_family = AF_INET;
380     addrp->sin_len = sizeof(*addrp);
381     addrp->sin_port = 0;
382     addrp->sin_addr.s_addr = INADDR_ANY;
383
384     strcpy(ifrp->ifr_name, intf);
385     if (ioctl(s, SIOCSIFADDR, ifrp)) { /* set ifnet address */
386         perror("SIOCSIFADDR");
387         goto out;
388     }
389
390     if (ioctl(s, SIOCSIFNETMASK, ifrp)) { /* set net addr mask */
391         perror("SIOCSIFNETMASK");
392         goto out;
393     }
394
395     /* the broadcast address is 255.255.255.255 */
396     memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
397     if (ioctl(s, SIOCSIFBRDADDR, ifrp)) { /* set broadcast addr */
398         perror("SIOCSIFBRDADDR");
399         goto out;
400     }
401
402     ifrp->ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
403     if (ioctl(s, SIOCSIFFLAGS, ifrp)) { /* set ifnet flags */
404         perror("SIOCSIFFLAGS up");
405         goto out;
406     }
407
408     if (ioctl(s, SIOCGIFHWADDR, ifrp) < 0) { /* get MAC address */
409         perror("SIOCGIFHWADDR 1");
410         goto out;
411     }
412
413     // Set up routing
414     addrp->sin_family = AF_INET;
415     addrp->sin_port = 0;
416     addrp->sin_len = sizeof(*addrp);  // Size of address
417
418     /* the broadcast address is 255.255.255.255 */
419     memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
420     memset(&route, 0, sizeof(route));
421     memcpy(&route.rt_gateway, addrp, sizeof(*addrp));
422
423     addrp->sin_addr.s_addr = INADDR_ANY;
424     memcpy(&route.rt_dst, addrp, sizeof(*addrp));
425     memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
426
427     route.rt_dev = ifrp->ifr_name;
428     route.rt_flags = RTF_UP|RTF_GATEWAY;
429     route.rt_metric = 0;
430
431     if (ioctl(s, SIOCADDRT, &route)) { /* add route */
432         if (errno != EEXIST) {
433             perror("SIOCADDRT 3");
434             goto out;
435         }
436     }
437     retcode = true;
438  out:
439     if (s != -1) 
440       close(s);
441
442     return retcode;
443 }
444
445
446 // ------------------------------------------------------------------------
447 // DHCP retransmission timeouts and number of tries
448 // 
449 // To work better with simulated failures (or real ones!) so that the rest
450 // of the system is tested, rather than DHCP renewal failures pulling
451 // everything down, we try a little more zealously than the RFC suggests.
452
453 static unsigned char timeout_random = 0;
454
455 struct timeout_state {
456     unsigned int secs;
457     int countdown;
458 };
459
460 static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
461 {
462     timeout_random++;
463     pstate->countdown = 4; // initial fast retries
464     pstate->secs = 3 + (timeout_random & 3);
465     ptv->tv_sec = 0;
466     ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
467 }
468
469 static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
470 {
471     if ( 0 < pstate->countdown-- )
472         return true;
473     if ( 0 == ptv->tv_sec )
474         ptv->tv_sec = pstate->secs;
475     else {
476         timeout_random++;
477         pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
478         pstate->countdown = 2; // later fast retries
479         ptv->tv_sec = 0;
480     }
481     // If longer, too many tries...
482     return pstate->secs < CYGNUM_NET_DHCP_MIN_RETRY_TIME; 
483 }
484
485 // ------------------------------------------------------------------------
486 // Lease expiry and alarms to notify it
487
488 static cyg_alarm_t alarm_function;
489
490 static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
491 {
492     struct dhcp_lease *lease = (struct dhcp_lease *)data;
493     lease->which |= lease->next;
494     if ( lease->needs_attention )
495         cyg_semaphore_post( lease->needs_attention );
496
497     // Step the lease on into its next state of being alarmed ;-)
498     if ( lease->next & DHCP_LEASE_EX ) {
499         cyg_alarm_disable( alarm );
500     }
501     else if ( lease->next & DHCP_LEASE_T2 ) {
502         lease->next = DHCP_LEASE_EX;
503         cyg_alarm_initialize( lease->alarm, lease->expiry, 0 );
504         cyg_alarm_enable( lease->alarm );
505     }
506     else if ( lease->next & DHCP_LEASE_T1 ) {
507         lease->next = DHCP_LEASE_T2;
508         cyg_alarm_initialize( lease->alarm, lease->t2, 0 );
509         cyg_alarm_enable( lease->alarm );
510     }
511 }
512
513 static inline void no_lease( struct dhcp_lease *lease )
514 {
515     if ( lease->alarm ) {
516         // Already set: delete this.
517         cyg_alarm_disable( lease->alarm );
518         cyg_alarm_delete( lease->alarm );
519         lease->alarm = 0;
520     }
521 }
522
523 static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
524 {
525     cyg_tick_count_t now = cyg_current_time();
526     cyg_tick_count_t then;
527     cyg_uint32 tag = 0;
528     cyg_uint32 expiry_then;
529     cyg_resolution_t resolution = 
530         cyg_clock_get_resolution(cyg_real_time_clock());
531     cyg_handle_t h;
532     unsigned int length;
533     
534     // Silence any jabbering from past lease on this interface
535     no_lease( lease );
536     lease->which = lease->next = 0;
537     cyg_clock_to_counter(cyg_real_time_clock(), &h);
538     cyg_alarm_create( h, alarm_function, (cyg_addrword_t)lease,
539                       &lease->alarm, &lease->alarm_obj );
540
541     // extract the lease time and scale it &c to now.
542     length = sizeof(tag);
543     if(!get_bootp_option( bootp, TAG_DHCP_LEASE_TIME, &tag ,&length))
544         tag = 0xffffffff;
545
546     if ( 0xffffffff == tag ) {
547         lease->expiry = 0xffffffff;
548         lease->t2     = 0xffffffff;
549         lease->t1     = 0xffffffff;
550         return; // it's an infinite lease, hurrah!
551     }
552
553     then = (cyg_uint64)(ntohl(tag));
554     expiry_then = then;
555
556     then *= 1000000000; // into nS - we know there is room in a tick_count_t
557     then = (then / resolution.dividend) * resolution.divisor; // into system ticks
558     lease->expiry = now + then;
559     length = sizeof(tag);
560     if (get_bootp_option( bootp, TAG_DHCP_REBIND_TIME, &tag, &length ))
561         then = (cyg_uint64)(ntohl(tag));
562     else
563         then = expiry_then - expiry_then/4;
564     then *= 1000000000; // into nS - we know there is room in a tick_count_t
565     then = (then / resolution.dividend) * resolution.divisor; // into system ticks
566     lease->t2 = now + then;
567
568     length = sizeof(tag);
569     if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag, &length ))
570         then = (cyg_uint64)(ntohl(tag));
571     else
572         then = expiry_then/2;
573     then *= 1000000000; // into nS - we know there is room in a tick_count_t
574     then = (then / resolution.dividend) * resolution.divisor; // into system ticks
575     lease->t1 = now + then;
576
577 #if 0 // for testing this mechanism
578     lease->expiry = now + 5000; // 1000 here makes for failure in the DHCP test
579     lease->t2     = now + 3500;
580     lease->t1     = now + 2500;
581 #endif
582
583 #ifdef CYGDBG_NET_DHCP_CHATTER
584     diag_printf("new_lease:\n");
585     diag_printf("  expiry = %d\n",lease->expiry);
586     diag_printf("      t1 = %d\n",lease->t1);
587     diag_printf("      t2 = %d\n",lease->t2);
588 #endif
589
590     lease->next = DHCP_LEASE_T1;
591
592     cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
593     cyg_alarm_enable( lease->alarm );
594 }
595
596 // ------------------------------------------------------------------------
597 // Set all the tags we want to use when sending a packet.
598 // This has expanded to a large, explicit set to interwork better
599 // with a variety of DHCP servers.
600
601 static void set_default_dhcp_tags( struct bootp *xmit )
602 {
603     // Explicitly request full set of params that are default for LINUX
604     // dhcp servers, but not default for others.  This is rather arbitrary,
605     // but it preserves behaviour for people using those servers.
606     // Perhaps configury of this set will be needed in future?
607     //
608     // Here's the set:
609     static cyg_uint8 req_list[]  = {
610 #ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
611         CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
612 #else
613         TAG_DHCP_SERVER_ID    ,     //     DHCP server id: 10.16.19.66
614         TAG_DHCP_LEASE_TIME   ,     //     DHCP time 51: 60
615         TAG_DHCP_RENEWAL_TIME ,     //     DHCP time 58: 30
616         TAG_DHCP_REBIND_TIME  ,     //     DHCP time 59: 52
617         TAG_SUBNET_MASK       ,     //     subnet mask: 255.255.255.0
618         TAG_GATEWAY           ,     //     gateway: 10.16.19.66
619         TAG_DOMAIN_SERVER     ,     //     domain server: 10.16.19.66
620         TAG_DOMAIN_NAME       ,     //     domain name: hmt10.cambridge.redhat.com
621         TAG_IP_BROADCAST      ,     //     IP broadcast: 10.16.19.255
622 #endif
623 #ifdef CYGNUM_NET_SNTP_UNICAST_MAXDHCP
624         TAG_NTP_SERVER        ,     //     NTP Server Addresses(es)
625 #endif
626 #ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
627         CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
628 #endif
629     };
630
631     if ( req_list[0] ) // So that one may easily turn it all off by configury
632         set_variable_tag( xmit, TAG_DHCP_PARM_REQ_LIST,
633                           &req_list[0], sizeof( req_list ) );
634
635 #ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
636 {
637     int nlen = strlen(dhcp_hostname);
638
639     if (nlen > 0)
640         set_variable_tag( xmit, TAG_HOST_NAME, dhcp_hostname, nlen + 1);
641 }
642 #endif
643 #ifdef CYGOPT_NET_DHCP_OPTION_DHCP_CLIENTID_MAC
644 {
645         cyg_uint8 id[16+1];     /* sizeof bp_chaddr[] + 1 */
646
647         id[0] = 1;  /* 1-byte hardware type: 1=ethernet. */
648     CYG_ASSERT( xmit->bp_hlen<=(sizeof(id)-1), "HW address invalid" );
649     memcpy(&id[1], &xmit->bp_chaddr, xmit->bp_hlen);
650     set_variable_tag( xmit, TAG_DHCP_CLIENTID, id, xmit->bp_hlen+1);
651 }
652 #endif
653
654     // Explicitly specify our max message size.
655     set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );
656 }
657
658 // ------------------------------------------------------------------------
659 // the DHCP state machine - this does all the work
660
661 int
662 do_dhcp(const char *intf, struct bootp *res,
663         cyg_uint8 *pstate, struct dhcp_lease *lease)
664 {
665     struct ifreq ifr;
666     struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
667     int s = -1;
668     socklen_t addrlen;
669     int one = 1;
670     unsigned char mincookie[] = {99,130,83,99,255} ;
671     struct timeval tv;
672     struct timeout_state timeout_scratch;
673     cyg_uint8 oldstate = *pstate;
674     cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
675     unsigned int length;
676     
677     cyg_uint32 xid;
678
679 #define CHECK_XID() (  /* and other details */                                  \
680     received->bp_xid   != xid            || /* not the same transaction */      \
681     received->bp_htype != xmit->bp_htype || /* not the same ESA type    */      \
682     received->bp_hlen  != xmit->bp_hlen  || /* not the same length      */      \
683     bcmp( &received->bp_chaddr, &xmit->bp_chaddr, xmit->bp_hlen )               \
684     )
685
686     // IMPORTANT: xmit is the same as res throughout this; *received is a
687     // scratch buffer for reception; its contents are always copied to res
688     // when we are happy with them.  So we always transmit from the
689     // existing state.
690     struct bootp rx_local;
691     struct bootp *received = &rx_local;
692     struct bootp *xmit = res;
693     struct bootp xmit2;
694     int xlen;
695
696     // First, get a socket on the interface in question.  But Zeroth, if
697     // needs be, bring it to the half-up broadcast only state if needs be.
698     
699     if ( DHCPSTATE_INIT      == oldstate
700          || DHCPSTATE_FAILED == oldstate
701          || 0                == oldstate ) {
702         // either explicit init state or the beginning of time or retry
703         if ( ! bring_half_up( intf, &ifr ) )
704             return false;
705
706         *pstate = DHCPSTATE_INIT;
707         lease->which = lease->next = 0;
708     }
709
710     s = socket(AF_INET, SOCK_DGRAM, 0);
711     if (s < 0) {
712         perror("socket");
713         goto out;
714     }
715
716     if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
717         perror("setsockopt");
718         goto out;
719     }
720
721     memset((char *) &cli_addr, 0, sizeof(cli_addr));
722     cli_addr.sin_family = AF_INET;
723     cli_addr.sin_len = sizeof(cli_addr);
724     cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
725     cli_addr.sin_port = htons(IPPORT_BOOTPC);
726     
727     memset((char *) &broadcast_addr, 0, sizeof(broadcast_addr));
728     broadcast_addr.sin_family = AF_INET;
729     broadcast_addr.sin_len = sizeof(broadcast_addr);
730     broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
731     broadcast_addr.sin_port = htons(IPPORT_BOOTPS);
732
733     memset((char *) &server_addr, 0, sizeof(server_addr));
734     server_addr.sin_family = AF_INET;
735     server_addr.sin_len = sizeof(server_addr);
736     server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // overwrite later
737     server_addr.sin_port = htons(IPPORT_BOOTPS);
738
739     if(bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
740         perror("bind error");
741         goto out;
742     }
743     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
744         perror("setsockopt SO_REUSEADDR");
745         goto out;
746     }
747     if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
748         perror("setsockopt SO_REUSEPORT");
749         goto out;
750     }
751     
752     // Now, we can launch into the DHCP state machine.  I think this will
753     // be the neatest way to do it; it returns from within the switch arms
754     // when all is well, or utterly failed.
755
756     reset_timeout( &tv, &timeout_scratch );
757
758     // Choose a new XID: first get the ESA as a basis:
759     strcpy(&ifr.ifr_name[0], intf);
760     if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
761         perror("SIOCGIFHWADDR 2");
762         goto out;
763     }
764
765     // Choose from scratch depending on ifr_hwaddr...[]
766     xid = ifr.ifr_hwaddr.sa_data[5];
767     xid |= (ifr.ifr_hwaddr.sa_data[4]) << 8;
768     xid |= (ifr.ifr_hwaddr.sa_data[3]) << 16;
769     xid |= (ifr.ifr_hwaddr.sa_data[2]) << 24;
770     xid ^= (cyg_arc4random() & 0xffff0000);
771
772     // Avoid adjacent ESAs colliding by increment
773 #define NEW_XID(_xid) CYG_MACRO_START (_xid)+= 0x10000; CYG_MACRO_END
774
775     while ( 1 ) {
776
777         // If we are active rather than in the process of shutting down,
778         // check for any lease expiry every time round, so that alarms
779         // *can* change the course of events even when already renewing,
780         // for example.
781         if ( DHCPSTATE_DO_RELEASE   != *pstate
782              && DHCPSTATE_NOTBOUND  != *pstate
783              && DHCPSTATE_FAILED    != *pstate ) {
784             cyg_uint8 lease_state;
785
786             cyg_scheduler_lock();
787             lease_state = lease->which;
788             lease->which = 0; // flag that we have noticed it
789             cyg_scheduler_unlock();
790
791             if ( lease_state & DHCP_LEASE_EX ) {
792                 // then the lease has expired completely!
793                 *pstate = DHCPSTATE_NOTBOUND;
794             }
795             else if ( lease_state & DHCP_LEASE_T2 ) {
796                 // Time to renew
797                 reset_timeout( &tv, &timeout_scratch ); // next conversation
798                 *pstate = DHCPSTATE_REBINDING;
799             }
800             else if ( lease_state & DHCP_LEASE_T1 ) {
801                 // Time to renew
802                 reset_timeout( &tv, &timeout_scratch ); // next conversation
803                 *pstate = DHCPSTATE_RENEWING;
804             }
805         }
806
807         switch ( *pstate ) {
808
809         case DHCPSTATE_INIT:
810
811             // Send the DHCPDISCOVER packet
812
813             // Fill in the BOOTP request - DHCPDISCOVER packet
814             bzero(xmit, sizeof(*xmit));
815             xmit->bp_op = BOOTREQUEST;
816             xmit->bp_htype = HTYPE_ETHERNET;
817             xmit->bp_hlen = IFHWADDRLEN;
818             xmit->bp_xid = xid;
819             xmit->bp_secs = cyg_current_time() / 100;
820             xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
821             bcopy(ifr.ifr_hwaddr.sa_data, &xmit->bp_chaddr, xmit->bp_hlen);
822             bcopy(mincookie, xmit->bp_vend, sizeof(mincookie));
823
824             // remove the next line to test ability to handle bootp packets.
825             set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPDISCOVER, 1 );
826             // Set all the tags we want to use when sending a packet
827             set_default_dhcp_tags( xmit );
828
829 #ifdef CYGDBG_NET_DHCP_CHATTER
830             diag_printf( "---------DHCPSTATE_INIT sending:\n" );
831             show_bootp( intf, xmit );
832 #endif            
833             if(sendto(s, xmit, dhcp_size_for_send(xmit), 0, 
834                       (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
835                 *pstate = DHCPSTATE_FAILED;
836                 break;
837             }
838
839             seen_bootp_reply = 0;
840             *pstate = DHCPSTATE_SELECTING;
841             break;
842
843         case DHCPSTATE_SELECTING:
844             // This is a separate state so that we can listen again
845             // *without* retransmitting.
846             
847             // listen for the DHCPOFFER reply
848
849             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
850
851             addrlen = sizeof(rx_addr);
852             if (recvfrom(s, received, sizeof(struct bootp), 0,
853                          (struct sockaddr *)&rx_addr, &addrlen) < 0) {
854                 // No packet arrived (this time)
855                 if ( seen_bootp_reply ) { // then already have a bootp reply
856                     // Save the good packet in *xmit
857                     bcopy( received, xmit, dhcp_size(received) );
858                     *pstate = DHCPSTATE_BOOTP_FALLBACK;
859                     NEW_XID( xid ); // Happy to advance, so new XID
860                     reset_timeout( &tv, &timeout_scratch );
861                     break;
862                 }       
863                 // go to the next larger timeout and re-send:
864                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
865                     *pstate = DHCPSTATE_FAILED;
866                     break;
867                 }
868                 *pstate = DHCPSTATE_INIT; // to retransmit
869                 break;
870             }
871             // Check for well-formed packet with correct termination (not truncated)
872             length = dhcp_size( received );
873 #ifdef CYGDBG_NET_DHCP_CHATTER
874             diag_printf( "---------DHCPSTATE_SELECTING received:\n" );
875             if ( length <= 0 )
876                 diag_printf( "WARNING! malformed or truncated packet\n" );
877             diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
878                          rx_addr.sin_family,
879                          rx_addr.sin_addr.s_addr,
880                          rx_addr.sin_port );
881             show_bootp( intf, received );
882 #endif            
883             if ( length <= 0 )
884                 break;
885             if ( CHECK_XID() )          // XID and ESA matches?
886                 break;                  // listen again...
887
888             if ( 0 == received->bp_siaddr.s_addr ) {
889                 // then fill in from the options...
890                 length = sizeof(received->bp_siaddr.s_addr);
891                 get_bootp_option( received, TAG_DHCP_SERVER_ID,
892                                   &received->bp_siaddr.s_addr,
893                                   &length);
894             }
895
896             // see if it was a DHCP reply or a bootp reply; it could be
897             // either.
898             length = sizeof(msgtype);
899             if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
900                                    &length) ) {
901                 if ( DHCPOFFER == msgtype ) { // all is well
902                     // Save the good packet in *xmit
903                     bcopy( received, xmit, dhcp_size(received) );
904                     // we like the packet, so reset the timeout for next time
905                     reset_timeout( &tv, &timeout_scratch );
906                     *pstate = DHCPSTATE_REQUESTING;
907                     NEW_XID( xid ); // Happy to advance, so new XID
908                 }
909             }
910             else // No TAG_DHCP_MESS_TYPE entry so it's a bootp reply
911                 seen_bootp_reply = 1; // (keep the bootp packet in received)
912                 
913             // If none of the above state changes occurred, we got a packet
914             // that "should not happen", OR we have a bootp reply in our
915             // hand; so listen again with the same timeout, without
916             // retrying the send, in the hope of getting a DHCP reply.
917             break;
918
919         case DHCPSTATE_REQUESTING:
920             // Just send what you got with a DHCPREQUEST in the message type.
921             // then wait for an ACK in DHCPSTATE_REQUEST_RECV.
922
923             // Fill in the BOOTP request - DHCPREQUEST packet
924             xmit->bp_xid = xid;
925             xmit->bp_op = BOOTREQUEST;
926             xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
927
928             set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
929             // Set all the tags we want to use when sending a packet
930             set_default_dhcp_tags( xmit );
931             // And this will be a new one:
932             set_fixed_tag( xmit, TAG_DHCP_REQ_IP, ntohl(xmit->bp_yiaddr.s_addr), 4 );
933             
934 #ifdef CYGDBG_NET_DHCP_CHATTER
935             diag_printf( "---------DHCPSTATE_REQUESTING sending:\n" );
936             show_bootp( intf, xmit );
937 #endif            
938             // Send back a [modified] copy.  Note that some fields are explicitly
939             // cleared, as per the RFC.  We need the copy because these fields are
940             // still useful to us (and currently stored in the 'result' structure)
941             xlen = dhcp_size_for_send( xmit );
942             bcopy( xmit, &xmit2, xlen );
943             xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
944             xmit2.bp_hops = 0;
945             if(sendto(s, &xmit2, xlen, 0, 
946                       (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
947                 *pstate = DHCPSTATE_FAILED;
948                 break;
949             }
950
951             *pstate = DHCPSTATE_REQUEST_RECV;
952             break;
953
954         case DHCPSTATE_REQUEST_RECV:
955             // wait for an ACK or a NACK - retry by going back to
956             // DHCPSTATE_REQUESTING; NACK means go back to INIT.
957
958             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
959
960             addrlen = sizeof(rx_addr);
961             if (recvfrom(s, received, sizeof(struct bootp), 0,
962                          (struct sockaddr *)&rx_addr, &addrlen) < 0) {
963                 // No packet arrived
964                 // go to the next larger timeout and re-send:
965                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
966                     *pstate = DHCPSTATE_FAILED;
967                     break;
968                 }
969                 *pstate = DHCPSTATE_REQUESTING;
970                 break;
971             }
972             // Check for well-formed packet with correct termination (not truncated)
973             length = dhcp_size( received );
974 #ifdef CYGDBG_NET_DHCP_CHATTER
975             diag_printf( "---------DHCPSTATE_REQUEST_RECV received:\n" );
976             if ( length <= 0 )
977                 diag_printf( "WARNING! malformed or truncated packet\n" );
978             diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
979                          rx_addr.sin_family,
980                          rx_addr.sin_addr.s_addr,
981                          rx_addr.sin_port );
982             show_bootp( intf, received );
983 #endif            
984             if ( length <= 0 )
985                 break;
986             if ( CHECK_XID() )          // not the same transaction;
987                 break;                  // listen again...
988
989             if ( 0 == received->bp_siaddr.s_addr ) {
990                 // then fill in from the options...
991                 length = sizeof(received->bp_siaddr.s_addr );
992                 get_bootp_option( received, TAG_DHCP_SERVER_ID,
993                                   &received->bp_siaddr.s_addr,
994                                   &length);
995             }
996
997             // check it was a DHCP reply
998             length = sizeof(msgtype);
999             if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
1000                                    &length) ) {
1001                 if ( DHCPACK == msgtype // Same offer & server?
1002                      && received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr
1003                      && received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
1004                     // we like the packet, so reset the timeout for next time
1005                     reset_timeout( &tv, &timeout_scratch );
1006                     // Record the new lease and set up timers &c
1007                     new_lease( received, lease );
1008                     *pstate = DHCPSTATE_BOUND;
1009                     break;
1010                 }
1011                 if ( DHCPNAK == msgtype // Same server?
1012                      && received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
1013                     // we're bounced!
1014                     *pstate = DHCPSTATE_INIT;  // So back the start of the rigmarole.
1015                     NEW_XID( xid ); // Unhappy to advance, so new XID
1016                     reset_timeout( &tv, &timeout_scratch );
1017                     break;
1018                 }
1019                 // otherwise it's something else, maybe another offer, or a bogus
1020                 // NAK from someone we are not asking!
1021                 // Just listen again, which implicitly discards it.
1022             }
1023             break;
1024
1025         case DHCPSTATE_BOUND:
1026
1027             // We are happy now, we have our address.
1028
1029             // All done with socket
1030             close(s);
1031             s = -1;
1032             
1033             // Re-initialize the interface with the new state
1034             if ( DHCPSTATE_BOUND != oldstate ) {
1035                 // Then need to go down and up
1036                 do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
1037                 if ( 0 != oldstate ) {
1038                     // Then not called from init_all_network_interfaces()
1039                     // so we must initialize the interface ourselves
1040                     if (!init_net(intf, res)) {
1041                         do_dhcp_down_net( intf, res, pstate, lease );
1042                         *pstate = DHCPSTATE_FAILED;
1043                         goto out;
1044                     }
1045                 }
1046             }
1047
1048             // Otherwise, nothing whatsoever to do...
1049             return true;
1050
1051         case DHCPSTATE_RENEWING:
1052             // Just send what you got with a DHCPREQUEST in the message
1053             // type UNICAST straight to the server.  Then wait for an ACK.
1054
1055             // Fill in the BOOTP request - DHCPREQUEST packet
1056             xmit->bp_xid = xid;
1057             xmit->bp_op = BOOTREQUEST;
1058             xmit->bp_flags = htons(0); // No BROADCAST FLAG
1059             // Use the *client* address here:
1060             xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
1061
1062             set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
1063             // These must not be set in this context
1064             unset_tag( xmit, TAG_DHCP_REQ_IP );
1065             unset_tag( xmit, TAG_DHCP_SERVER_ID );
1066             // Set all the tags we want to use when sending a packet
1067             set_default_dhcp_tags( xmit );
1068             
1069             // Set unicast address to *server*
1070             server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
1071
1072 #ifdef CYGDBG_NET_DHCP_CHATTER
1073             diag_printf( "---------DHCPSTATE_RENEWING sending:\n" );
1074             diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
1075                          server_addr.sin_family,
1076                          server_addr.sin_addr.s_addr,
1077                          server_addr.sin_port );
1078             show_bootp( intf, xmit );
1079 #endif            
1080             
1081             // Send back a [modified] copy.  Note that some fields are explicitly
1082             // cleared, as per the RFC.  We need the copy because these fields are
1083             // still useful to us (and currently stored in the 'result' structure)
1084             xlen = dhcp_size_for_send(xmit);
1085             bcopy( xmit, &xmit2, xlen );
1086             xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
1087             xmit2.bp_hops = 0;
1088             if(sendto(s, &xmit2, xlen, 0,
1089                        // UNICAST address of the server:
1090                       (struct sockaddr *)&server_addr,
1091                       sizeof(server_addr)) < 0) {
1092                 *pstate = DHCPSTATE_FAILED;
1093                 break;
1094             }
1095
1096             *pstate = DHCPSTATE_RENEW_RECV;
1097             break;
1098
1099         case DHCPSTATE_RENEW_RECV:
1100             // wait for an ACK or a NACK - retry by going back to
1101             // DHCPSTATE_RENEWING; NACK means go to NOTBOUND.
1102             // No answer means just wait for T2, to broadcast.
1103
1104             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1105
1106             addrlen = sizeof(rx_addr);
1107             if (recvfrom(s, received, sizeof(struct bootp), 0,
1108                          (struct sockaddr *)&rx_addr, &addrlen) < 0) {
1109                 // No packet arrived
1110                 // go to the next larger timeout and re-send:
1111                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
1112                     // If we timed out completely, just give up until T2
1113                     // expires - retain the lease meanwhile.  The normal
1114                     // lease mechanism will invoke REBINDING as and when
1115                     // necessary.
1116                     *pstate = DHCPSTATE_BOUND;
1117                     break;
1118                 }
1119                 *pstate = DHCPSTATE_RENEWING;
1120                 break;
1121             }
1122             // Check for well-formed packet with correct termination (not truncated)
1123             length = dhcp_size( received );
1124 #ifdef CYGDBG_NET_DHCP_CHATTER
1125             diag_printf( "---------DHCPSTATE_RENEW_RECV received:\n" );
1126             if ( length <= 0 )
1127                 diag_printf( "WARNING! malformed or truncated packet\n" );
1128             diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
1129                          rx_addr.sin_family,
1130                          rx_addr.sin_addr.s_addr,
1131                          rx_addr.sin_port );
1132             show_bootp( intf, received );
1133 #endif            
1134             if ( length <= 0 )
1135                 break;
1136             if ( CHECK_XID() )          // not the same transaction;
1137                 break;                  // listen again...
1138
1139             if ( 0 == received->bp_siaddr.s_addr ) {
1140                 // then fill in from the options...
1141                 length = sizeof(received->bp_siaddr.s_addr);
1142                 get_bootp_option( received, TAG_DHCP_SERVER_ID,
1143                                   &received->bp_siaddr.s_addr,
1144                                   &length);
1145             }
1146
1147             // check it was a DHCP reply
1148             length = sizeof(msgtype);
1149             if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
1150                                    &length) ) {
1151                 if ( DHCPACK == msgtype  // Same offer?
1152                      && received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
1153                     // we like the packet, so reset the timeout for next time
1154                     reset_timeout( &tv, &timeout_scratch );
1155                     // Record the new lease and set up timers &c
1156                     new_lease( received, lease );
1157                     *pstate = DHCPSTATE_BOUND;
1158                     break;
1159                 }
1160                 if ( DHCPNAK == msgtype ) { // we're bounced!
1161                     *pstate = DHCPSTATE_NOTBOUND;  // So quit out.
1162                     break;
1163                 }
1164                 // otherwise it's something else, maybe another offer.
1165                 // Just listen again, which implicitly discards it.
1166             }
1167             break;
1168
1169         case DHCPSTATE_REBINDING:
1170             // Just send what you got with a DHCPREQUEST in the message type.
1171             // Then wait for an ACK.  This one is BROADCAST.
1172
1173             // Fill in the BOOTP request - DHCPREQUEST packet
1174             xmit->bp_xid = xid;
1175             xmit->bp_op = BOOTREQUEST;
1176             xmit->bp_flags = htons(0); // no BROADCAST FLAG
1177             // Use the *client* address here:
1178             xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
1179
1180             set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPREQUEST, 1 );
1181             // These must not be set in this context
1182             unset_tag( xmit, TAG_DHCP_REQ_IP );
1183             unset_tag( xmit, TAG_DHCP_SERVER_ID );
1184             // Set all the tags we want to use when sending a packet
1185             set_default_dhcp_tags( xmit );
1186             
1187 #ifdef CYGDBG_NET_DHCP_CHATTER
1188             diag_printf( "---------DHCPSTATE_REBINDING sending:\n" );
1189             show_bootp( intf, xmit );
1190 #endif            
1191             // Send back a [modified] copy.  Note that some fields are explicitly
1192             // cleared, as per the RFC.  We need the copy because these fields are
1193             // still useful to us (and currently stored in the 'result' structure)
1194             xlen = dhcp_size_for_send( xmit );
1195             bcopy( xmit, &xmit2, xlen );
1196             xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
1197             xmit2.bp_hops = 0;
1198             if(sendto(s, &xmit2, xlen, 0, 
1199                       (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
1200                 *pstate = DHCPSTATE_FAILED;
1201                 break;
1202             }
1203
1204             *pstate = DHCPSTATE_REBIND_RECV;
1205             break;
1206
1207         case DHCPSTATE_REBIND_RECV:
1208             // wait for an ACK or a NACK - retry by going back to
1209             // DHCPSTATE_REBINDING; NACK means go to NOTBOUND.
1210             // No answer means just wait for expiry; we tried!
1211
1212             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1213
1214             addrlen = sizeof(rx_addr);
1215             if (recvfrom(s, received, sizeof(struct bootp), 0,
1216                          (struct sockaddr *)&rx_addr, &addrlen) < 0) {
1217                 // No packet arrived
1218                 // go to the next larger timeout and re-send:
1219                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
1220                     // If we timed out completely, just give up until EX
1221                     // expires - retain the lease meanwhile.  The normal
1222                     // lease mechanism will invoke NOTBOUND state as and
1223                     // when necessary.
1224                     *pstate = DHCPSTATE_BOUND;
1225                     break;
1226                 }
1227                 *pstate = DHCPSTATE_REBINDING;
1228                 break;
1229             }
1230             // Check for well-formed packet with correct termination (not truncated)
1231             length = dhcp_size( received );
1232 #ifdef CYGDBG_NET_DHCP_CHATTER
1233             diag_printf( "---------DHCPSTATE_REBIND_RECV received:\n" );
1234             if ( length <= 0 )
1235                 diag_printf( "WARNING! malformed or truncated packet\n" );
1236             diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
1237                          rx_addr.sin_family,
1238                          rx_addr.sin_addr.s_addr,
1239                          rx_addr.sin_port );
1240             show_bootp( intf, received );
1241 #endif            
1242             if ( length <= 0 )
1243                 break;
1244             if ( CHECK_XID() )          // not the same transaction;
1245                 break;                  // listen again...
1246
1247             if ( 0 == received->bp_siaddr.s_addr ) {
1248                 // then fill in from the options...
1249                 unsigned int length = sizeof(received->bp_siaddr.s_addr );
1250                 get_bootp_option( received, TAG_DHCP_SERVER_ID,
1251                                   &received->bp_siaddr.s_addr,
1252                                   &length);
1253             }
1254
1255             // check it was a DHCP reply
1256             length = sizeof(msgtype);
1257             if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
1258                                    &length) ) {
1259                 if ( DHCPACK == msgtype  // Same offer?
1260                      && received->bp_yiaddr.s_addr == xmit->bp_yiaddr.s_addr) {
1261                     // we like the packet, so reset the timeout for next time
1262                     reset_timeout( &tv, &timeout_scratch );
1263                     // Record the new lease and set up timers &c
1264                     new_lease( received, lease );
1265                     *pstate = DHCPSTATE_BOUND;
1266                     break;
1267                 }
1268                 else if ( DHCPNAK == msgtype ) { // we're bounced!
1269                     *pstate = DHCPSTATE_NOTBOUND;  // So back the start of the rigmarole.
1270                     break;
1271                 }
1272                 // otherwise it's something else, maybe another offer.
1273                 // Just listen again, which implicitly discards it.
1274             }
1275             break;
1276
1277         case DHCPSTATE_BOOTP_FALLBACK:
1278             // All done with socket
1279             close(s);
1280             s = -1;
1281             
1282             // And no lease should have become active, but JIC
1283             no_lease( lease );
1284             // Re-initialize the interface with the new state
1285             if ( DHCPSTATE_BOOTP_FALLBACK != oldstate ) {
1286                 // Then need to go down and up
1287                 do_dhcp_down_net( intf, res, &oldstate, lease ); // oldstate used
1288                 if ( 0 != oldstate ) {
1289                     // Then not called from init_all_network_interfaces()
1290                     // so we must initialize the interface ourselves
1291                     if (!init_net(intf, res)) {
1292                         do_dhcp_down_net( intf, res, pstate, lease );
1293                         *pstate = DHCPSTATE_FAILED;
1294                         goto out;
1295                     }
1296                 }
1297             }
1298
1299             // Otherwise, nothing whatsoever to do...
1300             return true;
1301
1302         case DHCPSTATE_NOTBOUND:
1303             // All done with socket
1304             close(s);
1305             // No lease active
1306             no_lease( lease );
1307             // Leave interface up so app can tidy.
1308             return true;
1309
1310         case DHCPSTATE_FAILED:
1311             // All done with socket
1312             close(s);
1313             // No lease active
1314             no_lease( lease );
1315             // Unconditionally down the interface.
1316             do_dhcp_down_net( intf, res, &oldstate, lease );
1317             return false;
1318
1319         case DHCPSTATE_DO_RELEASE:
1320             // We have been forced here by external means, to release the
1321             // lease for graceful shutdown.
1322
1323             // Just send what you got with a DHCPRELEASE in the message
1324             // type UNICAST straight to the server.  No ACK.  Then go to
1325             // NOTBOUND state.
1326             NEW_XID( xid );
1327             xmit->bp_xid = xid;
1328             xmit->bp_op = BOOTREQUEST;
1329             xmit->bp_flags = htons(0); // no BROADCAST FLAG
1330             // Use the *client* address here:
1331             xmit->bp_ciaddr.s_addr = xmit->bp_yiaddr.s_addr;
1332
1333             set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPRELEASE, 1 );
1334
1335             // Set unicast address to *server*
1336             server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
1337
1338 #ifdef CYGDBG_NET_DHCP_CHATTER
1339             diag_printf( "---------DHCPSTATE_DO_RELEASE sending:\n" );
1340             diag_printf( "UNICAST to family %d, addr %08x, port %d\n",
1341                          server_addr.sin_family,
1342                          server_addr.sin_addr.s_addr,
1343                          server_addr.sin_port );
1344             show_bootp( intf, xmit );
1345 #endif            
1346             // Send back a [modified] copy.  Note that some fields are explicitly
1347             // cleared, as per the RFC.  We need the copy because these fields are
1348             // still useful to us (and currently stored in the 'result' structure)
1349             xlen = dhcp_size_for_send( xmit );
1350             bcopy( xmit, &xmit2, xlen );
1351             xmit2.bp_yiaddr.s_addr = xmit2.bp_siaddr.s_addr = xmit2.bp_giaddr.s_addr = 0;
1352             xmit2.bp_hops = 0;
1353             if(sendto(s, &xmit2, xlen, 0, 
1354                        // UNICAST address of the server:
1355                       (struct sockaddr *)&server_addr,
1356                       sizeof(server_addr)) < 0) {
1357                 *pstate = DHCPSTATE_FAILED;
1358                 break;
1359             }
1360
1361             *pstate = DHCPSTATE_NOTBOUND;
1362             break;
1363
1364         default:
1365             no_lease( lease );
1366             close(s);
1367             return false;
1368         }
1369     }
1370 out:
1371     if (s != -1) 
1372       close (s);
1373     
1374     return false;
1375 }
1376
1377 // ------------------------------------------------------------------------
1378 // Bring an interface down, failed to initialize it or lease is expired
1379 // Also part of normal startup, bring down for proper reinitialization
1380
1381 int
1382 do_dhcp_down_net(const char *intf, struct bootp *res,
1383         cyg_uint8 *pstate, struct dhcp_lease *lease)
1384 {
1385     struct sockaddr_in *addrp;
1386     struct ifreq ifr;
1387     int s = -1;
1388     int retcode = false;
1389
1390     // Ensure clean slate
1391     cyg_route_reinit();  // Force any existing routes to be forgotten
1392
1393     s = socket(AF_INET, SOCK_DGRAM, 0);
1394     if (s < 0) {
1395         perror("socket");
1396         goto out;
1397     }
1398
1399     addrp = (struct sockaddr_in *) &ifr.ifr_addr;
1400
1401     // Remove any existing address
1402     if ( DHCPSTATE_FAILED  == *pstate
1403          || DHCPSTATE_INIT == *pstate
1404          || 0              == *pstate ) {
1405         // it was configured for broadcast only, "half-up"
1406         memset(addrp, 0, sizeof(*addrp));
1407         addrp->sin_family = AF_INET;
1408         addrp->sin_len = sizeof(*addrp);
1409         addrp->sin_port = 0;
1410         addrp->sin_addr.s_addr = INADDR_ANY;
1411     }
1412     else {
1413         // get the specific address that was used
1414         strcpy(ifr.ifr_name, intf);
1415         if (ioctl(s, SIOCGIFADDR, &ifr)) {
1416             perror("SIOCGIFADDR 1");
1417             goto out;
1418         }
1419     }
1420
1421     strcpy(ifr.ifr_name, intf);
1422     if (ioctl(s, SIOCDIFADDR, &ifr)) { /* delete IF addr */
1423         perror("SIOCDIFADDR1");
1424     }
1425
1426 #ifdef INET6
1427     {
1428       int s6;
1429       struct if_laddrreq iflr;
1430       
1431       s6 = socket(AF_INET6, SOCK_DGRAM, 0);
1432       if (s6 < 0) {
1433         perror("socket AF_INET6");
1434         close (s);
1435         return false;
1436       }
1437       // Now delete the ipv6 addr
1438       memset(&iflr,0,sizeof(iflr));
1439       strcpy(iflr.iflr_name, intf);
1440       if (!ioctl(s6, SIOCGLIFADDR, &iflr)) {
1441       
1442         strcpy(iflr.iflr_name, intf);
1443         if (ioctl(s6, SIOCDLIFADDR, &ifr)) { /* delete IF addr */
1444           perror("SIOCDIFADDR_IN61");
1445         }
1446       }
1447       close(s6);
1448     }
1449 #endif /* IP6 */
1450
1451     // Shut down interface so it can be reinitialized
1452     ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
1453     if (ioctl(s, SIOCSIFFLAGS, &ifr)) { /* set ifnet flags */
1454         perror("SIOCSIFFLAGS down");
1455         goto out;
1456     }
1457     retcode = true;
1458  
1459     if ( 0 != *pstate ) // preserve initial state
1460         *pstate = DHCPSTATE_INIT;
1461
1462     
1463  out:
1464     if (s != -1)
1465       close(s);
1466     
1467     return retcode;
1468 }
1469
1470 // ------------------------------------------------------------------------
1471 // Release (relinquish) a leased address - if we have one - and bring down
1472 // the interface.
1473 int
1474 do_dhcp_release(const char *intf, struct bootp *res,
1475         cyg_uint8 *pstate, struct dhcp_lease *lease)
1476 {
1477     if ( 0                           != *pstate
1478          && DHCPSTATE_INIT           != *pstate
1479          && DHCPSTATE_NOTBOUND       != *pstate
1480          && DHCPSTATE_FAILED         != *pstate
1481          && DHCPSTATE_BOOTP_FALLBACK != *pstate ) {
1482         *pstate = DHCPSTATE_DO_RELEASE;
1483         do_dhcp( intf, res, pstate, lease ); // to send the release packet
1484         cyg_thread_delay( 100 );             // to let it leave the building
1485     }
1486     return true;
1487 }
1488
1489 // ------------------------------------------------------------------------
1490
1491 #endif // CYGPKG_NET_DHCP
1492
1493 // EOF dhcp_prot.c