The attached patch fixes the DHCP option parsing to deal with DHCP servers that don't terminate their options with an END tag. The eCos implementation relies heavily on this. RFC 2132 doesn't specify if this END tag is required or not. Up until now, I have never seen a DHCP server do this, but have run into now.

There are other ways to implement this, but I chose this approach as the least invasive.

Jay
Index: ecos/packages/net/common/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/common/current/ChangeLog,v
retrieving revision 1.86
diff -u -5 -p -r1.86 ChangeLog
--- ecos/packages/net/common/current/ChangeLog  3 Jul 2009 12:40:25 -0000       
1.86
+++ ecos/packages/net/common/current/ChangeLog  17 Sep 2009 23:00:48 -0000
@@ -1,5 +1,10 @@
+2009-09-17     Jay Foster <[email protected]>
+
+       * src/dhcp_prot.c (do_dhcp): Fix packet parsing for DHCP servers
+       that do not terminate the options with an END tag.
+
 2009-06-23  Rene Schipp von Branitz Nielsen <[email protected]>
 
        * src/ifaddrs.c (getifaddrs): If socket() call for IPv6 fails,
        a stray pointer was freed.
 
Index: ecos/packages/net/common/current/src/dhcp_prot.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/common/current/src/dhcp_prot.c,v
retrieving revision 1.21
diff -u -5 -p -r1.21 dhcp_prot.c
--- ecos/packages/net/common/current/src/dhcp_prot.c    29 Jan 2009 17:49:57 
-0000      1.21
+++ ecos/packages/net/common/current/src/dhcp_prot.c    17 Sep 2009 23:00:49 
-0000
@@ -669,11 +669,11 @@ do_dhcp(const char *intf, struct bootp *
     struct timeval tv;
     struct timeout_state timeout_scratch;
     cyg_uint8 oldstate = *pstate;
     cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
     unsigned int length;
-    
+    int pktlen;
     cyg_uint32 xid;
 
 #define CHECK_XID() (  /* and other details */                                 
 \
     received->bp_xid   != xid            || /* not the same transaction */     
 \
     received->bp_htype != xmit->bp_htype || /* not the same ESA type    */     
 \
@@ -845,12 +845,13 @@ do_dhcp(const char *intf, struct bootp *
             // listen for the DHCPOFFER reply
 
             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
             addrlen = sizeof(rx_addr);
-            if (recvfrom(s, received, sizeof(struct bootp), 0,
-                         (struct sockaddr *)&rx_addr, &addrlen) < 0) {
+            pktlen = recvfrom(s, received, sizeof(struct bootp), 0,
+                         (struct sockaddr *)&rx_addr, &addrlen);
+            if (pktlen < 0) {
                 // No packet arrived (this time)
                 if ( seen_bootp_reply ) { // then already have a bootp reply
                     // Save the good packet in *xmit
                     bcopy( received, xmit, dhcp_size(received) );
                     *pstate = DHCPSTATE_BOOTP_FALLBACK;
@@ -864,10 +865,17 @@ do_dhcp(const char *intf, struct bootp *
                     break;
                 }
                 *pstate = DHCPSTATE_INIT; // to retransmit
                 break;
             }
+            /* Some DHCP servers don't terminate the options list with
+             * an END tag.  Append one if we can.
+             */
+            if (pktlen < sizeof(struct bootp))
+            {
+                ((unsigned char *)received)[pktlen] = TAG_END;
+            }
             // Check for well-formed packet with correct termination (not 
truncated)
             length = dhcp_size( received );
 #ifdef CYGDBG_NET_DHCP_CHATTER
             diag_printf( "---------DHCPSTATE_SELECTING received:\n" );
             if ( length <= 0 )
@@ -954,21 +962,29 @@ do_dhcp(const char *intf, struct bootp *
             // DHCPSTATE_REQUESTING; NACK means go back to INIT.
 
             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
             addrlen = sizeof(rx_addr);
-            if (recvfrom(s, received, sizeof(struct bootp), 0,
-                         (struct sockaddr *)&rx_addr, &addrlen) < 0) {
+            pktlen = recvfrom(s, received, sizeof(struct bootp), 0,
+                         (struct sockaddr *)&rx_addr, &addrlen);
+            if (pktlen < 0) {
                 // No packet arrived
                 // go to the next larger timeout and re-send:
                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
                     *pstate = DHCPSTATE_FAILED;
                     break;
                 }
                 *pstate = DHCPSTATE_REQUESTING;
                 break;
             }
+            /* Some DHCP servers don't terminate the options list with
+             * an END tag.  Append one if we can.
+             */
+            if (pktlen < sizeof(struct bootp))
+            {
+                ((unsigned char *)received)[pktlen] = TAG_END;
+            }
             // Check for well-formed packet with correct termination (not 
truncated)
             length = dhcp_size( received );
 #ifdef CYGDBG_NET_DHCP_CHATTER
             diag_printf( "---------DHCPSTATE_REQUEST_RECV received:\n" );
             if ( length <= 0 )
@@ -1100,12 +1116,13 @@ do_dhcp(const char *intf, struct bootp *
             // No answer means just wait for T2, to broadcast.
 
             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
             addrlen = sizeof(rx_addr);
-            if (recvfrom(s, received, sizeof(struct bootp), 0,
-                         (struct sockaddr *)&rx_addr, &addrlen) < 0) {
+            pktlen = recvfrom(s, received, sizeof(struct bootp), 0,
+                         (struct sockaddr *)&rx_addr, &addrlen);
+            if (pktlen < 0) {
                 // No packet arrived
                 // go to the next larger timeout and re-send:
                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
                     // If we timed out completely, just give up until T2
                     // expires - retain the lease meanwhile.  The normal
@@ -1115,10 +1132,17 @@ do_dhcp(const char *intf, struct bootp *
                     break;
                 }
                 *pstate = DHCPSTATE_RENEWING;
                 break;
             }
+            /* Some DHCP servers don't terminate the options list with
+             * an END tag.  Append one if we can.
+             */
+            if (pktlen < sizeof(struct bootp))
+            {
+                ((unsigned char *)received)[pktlen] = TAG_END;
+            }
             // Check for well-formed packet with correct termination (not 
truncated)
             length = dhcp_size( received );
 #ifdef CYGDBG_NET_DHCP_CHATTER
             diag_printf( "---------DHCPSTATE_RENEW_RECV received:\n" );
             if ( length <= 0 )
@@ -1208,12 +1232,13 @@ do_dhcp(const char *intf, struct bootp *
             // No answer means just wait for expiry; we tried!
 
             setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
             addrlen = sizeof(rx_addr);
-            if (recvfrom(s, received, sizeof(struct bootp), 0,
-                         (struct sockaddr *)&rx_addr, &addrlen) < 0) {
+            pktlen = recvfrom(s, received, sizeof(struct bootp), 0,
+                         (struct sockaddr *)&rx_addr, &addrlen);
+            if (pktlen < 0) {
                 // No packet arrived
                 // go to the next larger timeout and re-send:
                 if ( ! next_timeout( &tv, &timeout_scratch ) ) {
                     // If we timed out completely, just give up until EX
                     // expires - retain the lease meanwhile.  The normal
@@ -1223,10 +1248,17 @@ do_dhcp(const char *intf, struct bootp *
                     break;
                 }
                 *pstate = DHCPSTATE_REBINDING;
                 break;
             }
+            /* Some DHCP servers don't terminate the options list with
+             * an END tag.  Append one if we can.
+             */
+            if (pktlen < sizeof(struct bootp))
+            {
+                ((unsigned char *)received)[pktlen] = TAG_END;
+            }
             // Check for well-formed packet with correct termination (not 
truncated)
             length = dhcp_size( received );
 #ifdef CYGDBG_NET_DHCP_CHATTER
             diag_printf( "---------DHCPSTATE_REBIND_RECV received:\n" );
             if ( length <= 0 )

Reply via email to