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