Browse Source

[3390] Implemented DHCPINFORM message processing by the server.

Marcin Siodelski 10 years ago
parent
commit
e3ada09615
2 changed files with 80 additions and 12 deletions
  1. 77 10
      src/bin/dhcp4/dhcp4_srv.cc
  2. 3 2
      src/bin/dhcp4/dhcp4_srv.h

+ 77 - 10
src/bin/dhcp4/dhcp4_srv.cc

@@ -299,7 +299,7 @@ Dhcpv4Srv::run() {
                 break;
 
             case DHCPINFORM:
-                processInform(query);
+                rsp = processInform(query);
                 break;
 
             default:
@@ -1133,8 +1133,41 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, const Pkt4Ptr& response) {
     static const IOAddress zero_addr("0.0.0.0");
     static const IOAddress bcast_addr("255.255.255.255");
 
+    // The DHCPINFORM is slightly different than other messages in a sense
+    // that the server should always unicast the response to the ciaddr.
+    // It appears however that some clients don't set the ciaddr. We still
+    // want to provision these clients and we do what we can't to send the
+    // packet to the address where client can receive it.
+    if (question->getType() == DHCPINFORM) {
+        // If client adheres to RFC2131 it will set the ciaddr and in this
+        // case we always unicast our response to this address.
+        if (question->getCiaddr() != zero_addr) {
+            response->setRemoteAddr(question->getCiaddr());
+
+        // If we received DHCPINFOM via relay and the ciaddr is not set we
+        // will try to send the response via relay. The caveat is that the
+        // relay will not have any idea where to forward the packet because
+        // the yiaddr is likely not set. So, the broadcast flag is set so
+        // as the response may be broadcast.
+        } else if (question->isRelayed()) {
+            response->setRemoteAddr(question->getGiaddr());
+            response->setFlags(response->getFlags() | BOOTP_BROADCAST);
+
+        // If there is no ciaddr and no giaddr the only thing we can do is
+        // to use the source address of the packet.
+        } else {
+            response->setRemoteAddr(question->getRemoteAddr());
+        }
+        // Remote addres is now set so return.
+        return;
+    }
+
     // If received relayed message, server responds to the relay address.
     if (question->isRelayed()) {
+        if ((question->getType() == DHCPINFORM) &&
+            (question->getCiaddr() == zero_addr)) {
+            response->setFlags(BOOTP_BROADCAST);
+        }
         response->setRemoteAddr(question->getGiaddr());
 
     // If giaddr is 0 but client set ciaddr, server should unicast the
@@ -1385,9 +1418,26 @@ Dhcpv4Srv::processDecline(Pkt4Ptr& /* decline */) {
 
 Pkt4Ptr
 Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
-
-    /// @todo Implement this for real. (also see ticket #3116)
-    return (inform);
+    Pkt4Ptr ack = Pkt4Ptr(new Pkt4(DHCPACK, inform->getTransid()));
+    copyDefaultFields(inform, ack);
+    appendRequestedOptions(inform, ack);
+    appendRequestedVendorOptions(inform, ack);
+    adjustIfaceData(inform, ack);
+
+    // There are cases for the DHCPINFORM that the server receives it via
+    // relay but will send the response to the client's unicast address
+    // carried in the ciaddr. In this case, the giaddr and hops field should
+    // be cleared (these fields were copied by the copyDefaultFields function).
+    // Also Relay Agent Options should be removed if present.
+    if (ack->getRemoteAddr() != inform->getGiaddr()) {
+        ack->setHops(0);
+        ack->setGiaddr(IOAddress("0.0.0.0"));
+        ack->delOption(DHO_DHCP_AGENT_OPTIONS);
+    }
+
+    // The DHCPACK must contain server id.
+    appendServerID(ack);
+    return (ack);
 }
 
 const char*
@@ -1493,6 +1543,12 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
 
 bool
 Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
+    // Check that the message type is accepted by the server. We rely on the
+    // function called to log a message if needed.
+    if (!acceptMessageType(query)) {
+        return (false);
+    }
+
     // Check if the message from directly connected client (if directly
     // connected) should be dropped or processed.
     if (!acceptDirectRequest(query)) {
@@ -1511,12 +1567,6 @@ Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
         return (false);
     }
 
-    // Check that the message type is accepted by the server. We rely on the
-    // function called to log a message if needed.
-    if (!acceptMessageType(query)) {
-        return (false);
-    }
-
     return (true);
 }
 
@@ -1529,6 +1579,23 @@ Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
     } catch (const Exception& ex) {
         return (false);
     }
+    // The source address must not be zero for the DHCPINFORM message from
+    // the directly connected client because the server will not know where
+    // to respond if the ciaddr was not present.
+    static const IOAddress zero_addr("0.0.0.0");
+    try {
+        if (pkt->getType() == DHCPINFORM) {
+            if ((pkt->getRemoteAddr() == zero_addr) &&
+                (pkt->getCiaddr() == zero_addr)) {
+                return (false);
+            }
+        }
+    } catch (...) {
+        // If we got here, it is probably because the message type hasn't
+        // been set. But, this should not really happen assuming that
+        // we validate the message type prior to calling this function.
+        return (false);
+    }
     static const IOAddress bcast("255.255.255.255");
     return ((pkt->getLocalAddr() != bcast || selectSubnet(pkt)));
 }

+ 3 - 2
src/bin/dhcp4/dhcp4_srv.h

@@ -239,8 +239,9 @@ protected:
     /// This function accepts the following messages:
     /// - all valid relayed messages,
     /// - all unicast messages,
-    /// - all broadcast messages received on the interface for which the
-    /// suitable subnet exists (is configured).
+    /// - all broadcast messages except DHCPINFORM received on the interface
+    /// for which the suitable subnet exists (is configured).
+    /// - all DHCPINFORM messages with source address or ciaddr set.
     ///
     /// @param query Message sent by a client.
     ///