From: Marcin Siodelski Date: Tue, 15 Jul 2014 10:06:40 +0000 (+0200) Subject: [3390] Implemented DHCPINFORM message processing by the server. X-Git-Tag: trac3482_base~79^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e3ada09615d46e5bd4f1e772952ff307eaf20680;p=thirdparty%2Fkea.git [3390] Implemented DHCPINFORM message processing by the server. --- diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index ae278201af..b23130b362 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/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))); } diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 15fab541dd..280165ab27 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/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. ///