1 /*==========================================================================
5 // DHCP protocol implementation for DHCP client
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
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.
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
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.
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.
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.
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####
45 // Contributors: gthomas, andrew.lunn@ascom.ch
47 // Purpose: DHCP support
50 //####DESCRIPTIONEND####
52 //========================================================================*/
54 #include <pkgconf/system.h>
55 #include <pkgconf/net.h>
57 #ifdef CYGPKG_NET_DHCP
59 #ifdef CYGPKG_NET_SNTP
60 #include <pkgconf/net_sntp.h>
61 #endif /* CYGPKG_NET_SNTP */
64 #define perror( txt ) // nothing
71 #include <cyg/infra/cyg_ass.h>
74 #include <net/if_var.h>
75 #include <netinet6/in6_var.h>
78 extern int cyg_arc4random(void);
80 #ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
81 static char dhcp_hostname[CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN+1];
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)
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);
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
98 static unsigned char *
99 scan_dhcp_size( struct bootp *ppkt )
103 op = &ppkt->bp_vend[0];
104 // First check for the cookie!
109 CYG_FAIL( "Bad DHCP cookie" );
114 // This will only scan the options field.
115 while (*op != TAG_END) {
116 if ( *op == TAG_PAD ) {
121 if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
122 CYG_FAIL( "Oversize DHCP packet in dhcp_size" );
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
133 // ------------------------------------------------------------------------
134 // Get the actual packet size of an initialized buffer
137 dhcp_size( struct bootp *ppkt )
141 op = scan_dhcp_size( ppkt );
143 return (op - (unsigned char *)ppkt);
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.
153 dhcp_size_for_send( struct bootp *ppkt )
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++ )
162 return (op - (unsigned char *)ppkt);
165 // ------------------------------------------------------------------------
166 // Insert/set an option value in an initialized buffer
169 set_fixed_tag( struct bootp *ppkt,
176 // Initially this will only scan the options field.
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" );
184 if (*op == tag) // Found it...
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
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" );
202 *(op + len + 2) = TAG_END;
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);
213 // Note that this does not permit changing the size of an extant tag.
215 set_variable_tag( struct bootp *ppkt,
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" );
229 if (*op == tag) // Found it...
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
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" );
247 *(op + len + 2) = TAG_END;
249 // and insert the value. No order is implied.
250 op += 2; // point to start of value
251 while ( len-- > 0 ) {
258 unset_tag( struct bootp *ppkt,
261 unsigned char *op, *nextp = 0, *killp = 0;
263 // Initially this will only scan the options field.
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" );
271 if (*op == tag) { // Found it...
272 killp = op; // item to kill
273 nextp = op + *(op+1)+2; // next item address
275 op += *(op+1)+2; // scan to the end
281 // Obliterate the found op by copying down: *op is the end.
282 while( nextp <= op ) // <= to copy the TAG_END too.
288 // ------------------------------------------------------------------------
289 // Bring up an interface enough to broadcast, before we know who we are
292 bring_half_up(const char *intf, struct ifreq *ifrp )
297 struct sockaddr_in *addrp;
298 struct ecos_rtentry route;
301 // Ensure clean slate
302 cyg_route_reinit(); // Force any existing routes to be forgotten
304 s = socket(AF_INET, SOCK_DGRAM, 0);
310 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
311 perror("setsockopt");
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);
320 addrp->sin_addr.s_addr = INADDR_ANY;
322 strcpy(ifrp->ifr_name, intf);
323 if (ioctl(s, SIOCSIFADDR, ifrp)) { /* set ifnet address */
324 perror("SIOCSIFADDR");
328 if (ioctl(s, SIOCSIFNETMASK, ifrp)) { /* set net addr mask */
329 perror("SIOCSIFNETMASK");
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");
340 ifrp->ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
341 if (ioctl(s, SIOCSIFFLAGS, ifrp)) { /* set ifnet flags */
342 perror("SIOCSIFFLAGS up");
346 if (ioctl(s, SIOCGIFHWADDR, ifrp) < 0) { /* get MAC address */
347 perror("SIOCGIFHWADDR 1");
352 addrp->sin_family = AF_INET;
354 addrp->sin_len = sizeof(*addrp); // Size of address
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));
361 addrp->sin_addr.s_addr = INADDR_ANY;
362 memcpy(&route.rt_dst, addrp, sizeof(*addrp));
363 memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
365 route.rt_dev = ifrp->ifr_name;
366 route.rt_flags = RTF_UP|RTF_GATEWAY;
369 if (ioctl(s, SIOCADDRT, &route)) { /* add route */
370 if (errno != EEXIST) {
371 perror("SIOCADDRT 3");
384 // ------------------------------------------------------------------------
385 // DHCP retransmission timeouts and number of tries
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.
391 static unsigned char timeout_random = 0;
393 struct timeout_state {
398 static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
401 pstate->countdown = 4; // initial fast retries
402 pstate->secs = 3 + (timeout_random & 3);
404 ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
407 static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
409 if ( 0 < pstate->countdown-- )
411 if ( 0 == ptv->tv_sec )
412 ptv->tv_sec = pstate->secs;
415 pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
416 pstate->countdown = 2; // later fast retries
419 // If longer, too many tries...
420 return pstate->secs < CYGNUM_NET_DHCP_MIN_RETRY_TIME;
423 // ------------------------------------------------------------------------
424 // Lease expiry and alarms to notify it
426 static cyg_alarm_t alarm_function;
428 static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
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 );
435 // Step the lease on into its next state of being alarmed ;-)
436 if ( lease->next & DHCP_LEASE_EX ) {
437 cyg_alarm_disable( alarm );
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 );
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 );
451 static inline void no_lease( struct dhcp_lease *lease )
453 if ( lease->alarm ) {
454 // Already set: delete this.
455 cyg_alarm_disable( lease->alarm );
456 cyg_alarm_delete( lease->alarm );
461 static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
463 cyg_tick_count_t now = cyg_current_time();
464 cyg_tick_count_t then;
466 cyg_uint32 expiry_then;
467 cyg_resolution_t resolution =
468 cyg_clock_get_resolution(cyg_real_time_clock());
472 // Silence any jabbering from past lease on this interface
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 );
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))
484 if ( 0xffffffff == tag ) {
485 lease->expiry = 0xffffffff;
486 lease->t2 = 0xffffffff;
487 lease->t1 = 0xffffffff;
488 return; // it's an infinite lease, hurrah!
491 then = (cyg_uint64)(ntohl(tag));
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));
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;
506 length = sizeof(tag);
507 if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag, &length ))
508 then = (cyg_uint64)(ntohl(tag));
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;
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;
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);
528 lease->next = DHCP_LEASE_T1;
530 cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
531 cyg_alarm_enable( lease->alarm );
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.
539 static void set_default_dhcp_tags( struct bootp *xmit )
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?
547 static cyg_uint8 req_list[] = {
548 #ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
549 CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
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
561 #ifdef CYGNUM_NET_SNTP_UNICAST_MAXDHCP
562 TAG_NTP_SERVER , // NTP Server Addresses(es)
564 #ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
565 CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
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 ) );
573 #ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
575 int nlen = strlen(dhcp_hostname);
578 set_variable_tag( xmit, TAG_HOST_NAME, dhcp_hostname, nlen + 1);
581 #ifdef CYGOPT_NET_DHCP_OPTION_DHCP_CLIENTID_MAC
583 cyg_uint8 id[16+1]; /* sizeof bp_chaddr[] + 1 */
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);
592 // Explicitly specify our max message size.
593 set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );
596 // ------------------------------------------------------------------------
597 // the DHCP state machine - this does all the work
600 do_dhcp(const char *intf, struct bootp *res,
601 cyg_uint8 *pstate, struct dhcp_lease *lease)
604 struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
608 unsigned char mincookie[] = {99,130,83,99,255} ;
610 struct timeout_state timeout_scratch;
611 cyg_uint8 oldstate = *pstate;
612 cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
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 ) \
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
628 struct bootp rx_local;
629 struct bootp *received = &rx_local;
630 struct bootp *xmit = res;
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.
637 if ( DHCPSTATE_INIT == oldstate
638 || DHCPSTATE_FAILED == oldstate
640 // either explicit init state or the beginning of time or retry
641 if ( ! bring_half_up( intf, &ifr ) )
644 *pstate = DHCPSTATE_INIT;
645 lease->which = lease->next = 0;
648 s = socket(AF_INET, SOCK_DGRAM, 0);
654 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
655 perror("setsockopt");
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);
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);
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);
677 if(bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
678 perror("bind error");
681 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
682 perror("setsockopt SO_REUSEADDR");
685 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
686 perror("setsockopt SO_REUSEPORT");
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.
694 reset_timeout( &tv, &timeout_scratch );
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");
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);
710 // Avoid adjacent ESAs colliding by increment
711 #define NEW_XID(_xid) CYG_MACRO_START (_xid)+= 0x10000; CYG_MACRO_END
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,
719 if ( DHCPSTATE_DO_RELEASE != *pstate
720 && DHCPSTATE_NOTBOUND != *pstate
721 && DHCPSTATE_FAILED != *pstate ) {
722 cyg_uint8 lease_state;
724 cyg_scheduler_lock();
725 lease_state = lease->which;
726 lease->which = 0; // flag that we have noticed it
727 cyg_scheduler_unlock();
729 if ( lease_state & DHCP_LEASE_EX ) {
730 // then the lease has expired completely!
731 *pstate = DHCPSTATE_NOTBOUND;
733 else if ( lease_state & DHCP_LEASE_T2 ) {
735 reset_timeout( &tv, &timeout_scratch ); // next conversation
736 *pstate = DHCPSTATE_REBINDING;
738 else if ( lease_state & DHCP_LEASE_T1 ) {
740 reset_timeout( &tv, &timeout_scratch ); // next conversation
741 *pstate = DHCPSTATE_RENEWING;
749 // Send the DHCPDISCOVER packet
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;
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));
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 );
767 #ifdef CYGDBG_NET_DHCP_CHATTER
768 diag_printf( "---------DHCPSTATE_INIT sending:\n" );
769 show_bootp( intf, xmit );
771 if(sendto(s, xmit, dhcp_size_for_send(xmit), 0,
772 (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
773 *pstate = DHCPSTATE_FAILED;
777 seen_bootp_reply = 0;
778 *pstate = DHCPSTATE_SELECTING;
781 case DHCPSTATE_SELECTING:
782 // This is a separate state so that we can listen again
783 // *without* retransmitting.
785 // listen for the DHCPOFFER reply
787 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
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 );
801 // go to the next larger timeout and re-send:
802 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
803 *pstate = DHCPSTATE_FAILED;
806 *pstate = DHCPSTATE_INIT; // to retransmit
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" );
814 diag_printf( "WARNING! malformed or truncated packet\n" );
815 diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
817 rx_addr.sin_addr.s_addr,
819 show_bootp( intf, received );
823 if ( CHECK_XID() ) // XID and ESA matches?
824 break; // listen again...
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,
834 // see if it was a DHCP reply or a bootp reply; it could be
836 length = sizeof(msgtype);
837 if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
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
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)
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.
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.
861 // Fill in the BOOTP request - DHCPREQUEST packet
863 xmit->bp_op = BOOTREQUEST;
864 xmit->bp_flags = htons(0x8000); // BROADCAST FLAG
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 );
872 #ifdef CYGDBG_NET_DHCP_CHATTER
873 diag_printf( "---------DHCPSTATE_REQUESTING sending:\n" );
874 show_bootp( intf, xmit );
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;
883 if(sendto(s, &xmit2, xlen, 0,
884 (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
885 *pstate = DHCPSTATE_FAILED;
889 *pstate = DHCPSTATE_REQUEST_RECV;
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.
896 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
898 addrlen = sizeof(rx_addr);
899 if (recvfrom(s, received, sizeof(struct bootp), 0,
900 (struct sockaddr *)&rx_addr, &addrlen) < 0) {
902 // go to the next larger timeout and re-send:
903 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
904 *pstate = DHCPSTATE_FAILED;
907 *pstate = DHCPSTATE_REQUESTING;
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" );
915 diag_printf( "WARNING! malformed or truncated packet\n" );
916 diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
918 rx_addr.sin_addr.s_addr,
920 show_bootp( intf, received );
924 if ( CHECK_XID() ) // not the same transaction;
925 break; // listen again...
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,
935 // check it was a DHCP reply
936 length = sizeof(msgtype);
937 if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
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;
949 if ( DHCPNAK == msgtype // Same server?
950 && received->bp_siaddr.s_addr == xmit->bp_siaddr.s_addr) {
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 );
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.
963 case DHCPSTATE_BOUND:
965 // We are happy now, we have our address.
967 // All done with socket
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;
986 // Otherwise, nothing whatsoever to do...
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.
993 // Fill in the BOOTP request - DHCPREQUEST packet
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;
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 );
1007 // Set unicast address to *server*
1008 server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
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 );
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;
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;
1034 *pstate = DHCPSTATE_RENEW_RECV;
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.
1042 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
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
1054 *pstate = DHCPSTATE_BOUND;
1057 *pstate = DHCPSTATE_RENEWING;
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" );
1065 diag_printf( "WARNING! malformed or truncated packet\n" );
1066 diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
1068 rx_addr.sin_addr.s_addr,
1070 show_bootp( intf, received );
1074 if ( CHECK_XID() ) // not the same transaction;
1075 break; // listen again...
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,
1085 // check it was a DHCP reply
1086 length = sizeof(msgtype);
1087 if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
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;
1098 if ( DHCPNAK == msgtype ) { // we're bounced!
1099 *pstate = DHCPSTATE_NOTBOUND; // So quit out.
1102 // otherwise it's something else, maybe another offer.
1103 // Just listen again, which implicitly discards it.
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.
1111 // Fill in the BOOTP request - DHCPREQUEST packet
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;
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 );
1125 #ifdef CYGDBG_NET_DHCP_CHATTER
1126 diag_printf( "---------DHCPSTATE_REBINDING sending:\n" );
1127 show_bootp( intf, xmit );
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;
1136 if(sendto(s, &xmit2, xlen, 0,
1137 (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) < 0) {
1138 *pstate = DHCPSTATE_FAILED;
1142 *pstate = DHCPSTATE_REBIND_RECV;
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!
1150 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
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
1162 *pstate = DHCPSTATE_BOUND;
1165 *pstate = DHCPSTATE_REBINDING;
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" );
1173 diag_printf( "WARNING! malformed or truncated packet\n" );
1174 diag_printf( "...rx_addr is family %d, addr %08x, port %d\n",
1176 rx_addr.sin_addr.s_addr,
1178 show_bootp( intf, received );
1182 if ( CHECK_XID() ) // not the same transaction;
1183 break; // listen again...
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,
1193 // check it was a DHCP reply
1194 length = sizeof(msgtype);
1195 if ( get_bootp_option( received, TAG_DHCP_MESS_TYPE, &msgtype,
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;
1206 else if ( DHCPNAK == msgtype ) { // we're bounced!
1207 *pstate = DHCPSTATE_NOTBOUND; // So back the start of the rigmarole.
1210 // otherwise it's something else, maybe another offer.
1211 // Just listen again, which implicitly discards it.
1215 case DHCPSTATE_BOOTP_FALLBACK:
1216 // All done with socket
1220 // And no lease should have become active, but JIC
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;
1237 // Otherwise, nothing whatsoever to do...
1240 case DHCPSTATE_NOTBOUND:
1241 // All done with socket
1245 // Leave interface up so app can tidy.
1248 case DHCPSTATE_FAILED:
1249 // All done with socket
1253 // Unconditionally down the interface.
1254 do_dhcp_down_net( intf, res, &oldstate, lease );
1257 case DHCPSTATE_DO_RELEASE:
1258 // We have been forced here by external means, to release the
1259 // lease for graceful shutdown.
1261 // Just send what you got with a DHCPRELEASE in the message
1262 // type UNICAST straight to the server. No ACK. Then go to
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;
1271 set_fixed_tag( xmit, TAG_DHCP_MESS_TYPE, DHCPRELEASE, 1 );
1273 // Set unicast address to *server*
1274 server_addr.sin_addr.s_addr = res->bp_siaddr.s_addr;
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 );
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;
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;
1299 *pstate = DHCPSTATE_NOTBOUND;
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
1320 do_dhcp_down_net(const char *intf, struct bootp *res,
1321 cyg_uint8 *pstate, struct dhcp_lease *lease)
1323 struct sockaddr_in *addrp;
1326 int retcode = false;
1328 // Ensure clean slate
1329 cyg_route_reinit(); // Force any existing routes to be forgotten
1331 s = socket(AF_INET, SOCK_DGRAM, 0);
1337 addrp = (struct sockaddr_in *) &ifr.ifr_addr;
1339 // Remove any existing address
1340 if ( DHCPSTATE_FAILED == *pstate
1341 || DHCPSTATE_INIT == *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;
1351 // get the specific address that was used
1352 strcpy(ifr.ifr_name, intf);
1353 if (ioctl(s, SIOCGIFADDR, &ifr)) {
1354 perror("SIOCGIFADDR 1");
1359 strcpy(ifr.ifr_name, intf);
1360 if (ioctl(s, SIOCDIFADDR, &ifr)) { /* delete IF addr */
1361 perror("SIOCDIFADDR1");
1367 struct if_laddrreq iflr;
1369 s6 = socket(AF_INET6, SOCK_DGRAM, 0);
1371 perror("socket AF_INET6");
1375 // Now delete the ipv6 addr
1376 memset(&iflr,0,sizeof(iflr));
1377 strcpy(iflr.iflr_name, intf);
1378 if (!ioctl(s6, SIOCGLIFADDR, &iflr)) {
1380 strcpy(iflr.iflr_name, intf);
1381 if (ioctl(s6, SIOCDLIFADDR, &ifr)) { /* delete IF addr */
1382 perror("SIOCDIFADDR_IN61");
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");
1397 if ( 0 != *pstate ) // preserve initial state
1398 *pstate = DHCPSTATE_INIT;
1408 // ------------------------------------------------------------------------
1409 // Release (relinquish) a leased address - if we have one - and bring down
1412 do_dhcp_release(const char *intf, struct bootp *res,
1413 cyg_uint8 *pstate, struct dhcp_lease *lease)
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
1427 // ------------------------------------------------------------------------
1429 #endif // CYGPKG_NET_DHCP