Commit b5c50e2a authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3688'

Conflicts:
	src/bin/dhcp4/dhcp4_srv.h
parents ef19bd40 a397cb9b
......@@ -2124,10 +2124,65 @@ temporarily override a list of interface names and listen on all interfaces.
<section id="reservation4-hostname">
<title>Reserving a hostname</title>
<!-- @todo: replace this with the actual text once #3688 is implemented -->
<para>Reserving a hostname is currently not supported. It is possible
to specify that information in the configuration file, but that data
is not used by the server engine yet.</para>
<para>When the reservation for the client includes the <command>hostname
</command>, the server will assign this hostname to the client and send
it back in the Client FQDN or Hostname option, depending on which of them
the client has sent to the server. The reserved hostname always takes
precedence over the hostname supplied by the client or the autogenerated
(from the IPv4 address) hostname.</para>
<para>The server qualifies the reserved hostname with the value
of the <command>qualifying-suffix</command> parameter. For example, the
following subnet configuration:
<screen>
{
"subnet4": [ {
"subnet": "10.0.0.0/24",
"pools": [ { "pool": "10.0.0.10-10.0.0.100" } ],
"reservations": [
{
"hw-address": "aa:bb:cc:dd:ee:ff",
"hostname": "alice-laptop"
}
]
}],
"dhcp-ddns": {
"enable-updates": true,
"qualifying-suffix": "example.isc.org."
}
}
</screen>
will result in assigning the "alice-laptop.example.isc.org." hostname to the
client using the MAC address "aa:bb:cc:dd:ee:ff". If the <command>qualifying-suffix
</command> is not specified, the default (empty) value will be used, and
in this case the value specified as a <command>hostname</command> will
be treated as fully qualified name. Thus, by leaving the
<command>qualifying-suffix</command> empty it is possible to qualify
hostnames for the different clients with different domain names:
<screen>
{
"subnet4": [ {
"subnet": "10.0.0.0/24",
"pools": [ { "pool": "10.0.0.10-10.0.0.100" } ],
"reservations": [
{
"hw-address": "aa:bb:cc:dd:ee:ff",
"hostname": "alice-laptop.isc.org."
},
{
"hw-address": "12:34:56:78:99:AA",
"hostname": "mark-desktop.example.org."
}
]
}],
"dhcp-ddns": {
"enable-updates": true,
}
}
</screen>
</para>
</section>
<section id="reservation4-options">
......
......@@ -31,10 +31,6 @@ to establish a session with the Kea control channel.
This debug message informs that incoming packet has been assigned to specified
class or classes. This is a normal behavior and indicates successful operation.
% DHCP4_CLASS_PROCESSING_FAILED client class specific processing failed
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_CLIENT_NAME_PROC_FAIL failed to process the fqdn or hostname sent by a client: %1
This debug message is issued when the DHCP server was unable to process the
FQDN or Hostname option sent by a client. This is likely because the client's
......@@ -78,6 +74,10 @@ change is committed by the administrator.
A debug message indicating that the DHCPv4 server has received an
updated configuration from the Kea configuration system.
% DHCP4_DISCOVER_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPDISCOVER
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_DDNS_REQUEST_SEND_FAILED failed sending a request to kea-dhcp-ddns, error: %1, ncr: %2
This error message indicates that DHCP4 server attempted to send a DDNS
update request to the DHCP-DDNS server. This is most likely a configuration or
......@@ -148,6 +148,10 @@ point, the setting of the flag instructs the server not to choose a
subnet, an action that severely limits further processing; the server
will be only able to offer global options - no addresses will be assigned.
% DHCP4_INFORM_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPINFORM
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_INIT_FAIL failed to initialize Kea server: %1
The server has failed to initialize. This may be because the configuration
was not successful, or it encountered any other critical error on startup.
......@@ -349,6 +353,10 @@ a different hardware address. One possible reason for using different
hardware address is that a cloned virtual machine was not updated and
both clones use the same client-id.
% DHCP4_REQUEST_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPREQUEST
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
A debug message listing the data returned to the client.
......
This diff is collapsed.
......@@ -43,6 +43,99 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief DHCPv4 message exchange.
///
/// This class represents the DHCPv4 message exchange. The message exchange
/// consists of the single client message, server response to this message
/// and the mechanisms to generate the server's response. The server creates
/// the instance of the @c Dhcpv4Exchange for each inbound message that it
/// accepts for processing.
///
/// The use of the @c Dhcpv4Exchange object as a central repository of
/// information about the message exchange simplifies the API of the
/// @c Dhcpv4Srv class.
///
/// Another benefit of using this class is that different methods of the
/// @c Dhcpv4Srv may share information. For example, the constructor of this
/// class selects the subnet and multiple methods of @c Dhcpv4Srv use this
/// subnet, without the need to select it again.
///
/// @todo This is the initial version of this class. In the future a lot of
/// code from the @c Dhcpv4Srv class will be migrated here.
class Dhcpv4Exchange {
public:
/// @brief Constructor.
///
/// The constructor selects the subnet for the query and checks for the
/// static host reservations for the client which has sent the message.
/// The information about the reservations is stored in the
/// @c AllocEngine::ClientContext4 object, which can be obtained by
/// calling the @c getContext.
///
/// @param alloc_engine Pointer to the instance of the Allocation Engine
/// used by the server.
/// @param query Pointer to the client message.
/// @param subnet Pointer to the subnet to which the client belongs.
Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query,
const Subnet4Ptr& subnet);
/// @brief Initializes the instance of the response message.
///
/// The type of the response depends on the type of the query message.
/// For the DHCPDISCOVER the DHCPOFFER is created. For the DHCPREQUEST
/// and DHCPINFORM the DHCPACK is created. For the DHCPRELEASE the
/// response is not initialized.
void initResponse();
/// @brief Returns the pointer to the query from the client.
Pkt4Ptr getQuery() const {
return (query_);
}
/// @brief Returns the pointer to the server's response.
///
/// The returned pointer is NULL if the query type is DHCPRELEASE or DHCPDECLINE.
Pkt4Ptr getResponse() const {
return (resp_);
}
/// @brief Removes the response message by resetting the pointer to NULL.
void deleteResponse() {
resp_.reset();
}
/// @brief Returns the copy of the context for the Allocation engine.
AllocEngine::ClientContext4Ptr getContext() const {
return (context_);
}
private:
/// @brief Copies default parameters from client's to server's message
///
/// Some fields are copied from client's message into server's response,
/// e.g. client HW address, number of hops, transaction-id etc.
///
/// @warning This message is called internally by @c initResponse and
/// thus it doesn't check if the resp_ value has been initialized. The
/// calling method is responsible for making sure that @c resp_ is
/// not NULL.
void copyDefaultFields();
/// @brief Pointer to the allocation engine used by the server.
AllocEnginePtr alloc_engine_;
/// @brief Pointer to the DHCPv4 message sent by the client.
Pkt4Ptr query_;
/// @brief Pointer to the DHCPv4 message to be sent to the client.
Pkt4Ptr resp_;
/// @brief Context for use with allocation engine.
AllocEngine::ClientContext4Ptr context_;
};
/// @brief Type representing the pointer to the @c Dhcpv4Exchange.
typedef boost::shared_ptr<Dhcpv4Exchange> Dhcpv4ExchangePtr;
/// @brief DHCPv4 server service.
///
/// This singleton class represents DHCPv4 server. It contains all
......@@ -269,15 +362,15 @@ protected:
bool acceptServerId(const Pkt4Ptr& pkt) const;
//@}
/// @brief verifies if specified packet meets RFC requirements
/// @brief Verifies if specified packet meets RFC requirements
///
/// Checks if mandatory option is really there, that forbidden option
/// is not there, and that client-id or server-id appears only once.
///
/// @param pkt packet to be checked
/// @param query Pointer to the client's message.
/// @param serverid expectation regarding server-id option
/// @throw RFCViolation if any issues are detected
static void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
static void sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid);
/// @brief Processes incoming DISCOVER and returns response.
///
......@@ -322,23 +415,14 @@ protected:
/// @param inform message received from client
Pkt4Ptr processInform(Pkt4Ptr& inform);
/// @brief Copies default parameters from client's to server's message
///
/// Some fields are copied from client's message into server's response,
/// e.g. client HW address, number of hops, transaction-id etc.
///
/// @param question any message sent by client
/// @param answer any message server is going to send as response
void copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer);
/// @brief Appends options requested by client.
///
/// This method assigns options that were requested by client
/// (sent in PRL) or are enforced by server.
///
/// @param question DISCOVER or REQUEST message from a client.
/// @param msg outgoing message (options will be added here)
void appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
/// @param ex The exchange holding both the client's message and the
/// server's response.
void appendRequestedOptions(Dhcpv4Exchange& ex);
/// @brief Appends requested vendor options as requested by client.
///
......@@ -348,9 +432,9 @@ protected:
/// options, each with unique vendor-id). Vendor options are requested
/// using separate options within their respective vendor-option spaces.
///
/// @param question DISCOVER or REQUEST message from a client.
/// @param answer outgoing message (options will be added here)
void appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer);
/// @param ex The exchange holding both the client's message and the
/// server's response.
void appendRequestedVendorOptions(Dhcpv4Exchange& ex);
/// @brief Assigns a lease and appends corresponding options
///
......@@ -358,28 +442,27 @@ protected:
/// client and assigning it. Options corresponding to the lease
/// are added to specific message.
///
/// @param question DISCOVER or REQUEST message from client
/// @param answer OFFER or ACK/NAK message (lease options will be
/// added here)
///
/// This method may reset the @c answer shared pointer to indicate
/// that the response should not be sent to the client. The caller
/// must check if the @c answer is null after calling this method.
void assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer);
/// This method may reset the pointer to the response in the @c ex object
/// to indicate that the response should not be sent to the client.
/// The caller must check if the response is is null after calling
/// this method.
///
/// The response type in the @c ex object may be set to DHCPACK or DHCPNAK.
///
/// @param ex DHCPv4 exchange holding the client's message to be checked.
void assignLease(Dhcpv4Exchange& ex);
/// @brief Append basic options if they are not present.
///
/// This function adds the following basic options if they
/// are not yet added to the message:
/// are not yet added to the response message:
/// - Subnet Mask,
/// - Router,
/// - Name Server,
/// - Domain Name.
///
/// @param question DISCOVER or REQUEST message from a client.
/// @param msg the message to add options to.
void appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg);
/// @param ex DHCPv4 exchange holding the client's message to be checked.
void appendBasicOptions(Dhcpv4Exchange& ex);
/// @brief Processes Client FQDN and Hostname Options sent by a client.
///
......@@ -416,9 +499,9 @@ protected:
/// This function does not throw. It simply logs the debug message if the
/// processing of the FQDN or Hostname failed.
///
/// @param query A DISCOVER or REQUEST message from a client.
/// @param [out] answer A response message to be sent to a client.
void processClientName(const Pkt4Ptr& query, Pkt4Ptr& answer);
/// @param ex The exchange holding both the client's message and the
/// server's response.
void processClientName(Dhcpv4Exchange& ex);
/// @brief this is a prefix added to the contend of vendor-class option
///
......@@ -437,10 +520,9 @@ private:
/// the FQDN option to be sent back to the client in the server's
/// response.
///
/// @param fqdn An DHCPv4 Client FQDN %Option sent by a client.
/// @param [out] answer A response message to be sent to a client.
void processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
Pkt4Ptr& answer);
/// @param ex The exchange holding both the client's message and the
/// server's response.
void processClientFqdnOption(Dhcpv4Exchange& ex);
/// @brief Process Hostname %Option sent by a client.
///
......@@ -450,11 +532,9 @@ private:
/// prepare the Hostname option to be sent back to the client in the
/// server's response.
///
/// @param opt_hostname An @c OptionString object encapsulating the Hostname
/// %Option.
/// @param [out] answer A response message to be sent to a client.
void processHostnameOption(const OptionStringPtr& opt_hostname,
Pkt4Ptr& answer);
/// @param ex The exchange holding both the client's message and the
/// server's response.
void processHostnameOption(Dhcpv4Exchange& ex);
protected:
......@@ -499,17 +579,6 @@ protected:
/// @param reply server's response (ACK or NAK)
void renewLease(const Pkt4Ptr& renew, Pkt4Ptr& reply);
/// @brief Appends default options to a message
///
/// Currently it is only a Message Type option. This function does not add
/// the Server Identifier option as this option must be added using
/// @c Dhcpv4Srv::appendServerID.
///
///
/// @param msg message object (options will be added to it)
/// @param msg_type specifies message type
void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
/// @brief Adds server identifier option to the server's response.
///
/// This method adds a server identifier to the DHCPv4 message. It expects
......@@ -527,9 +596,9 @@ protected:
/// @note This method is static because it is not dependent on the class
/// state.
///
/// @param [out] response DHCPv4 message to which the server identifier
/// option should be added.
static void appendServerID(const Pkt4Ptr& response);
/// @param ex The exchange holding both the client's message and the
/// server's response.
static void appendServerID(Dhcpv4Exchange& ex);
/// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
///
......@@ -561,7 +630,10 @@ protected:
///
/// @note This method is static because it is not dependent on the class
/// state.
static void adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response);
///
/// @param ex The exchange holding both the client's message and the
/// server's response.
static void adjustIfaceData(Dhcpv4Exchange& ex);
/// @brief Sets remote addresses for outgoing packet.
///
......@@ -579,11 +651,9 @@ protected:
/// @note This method is static because it is not dependent on the class
/// state.
///
/// @param question instance of a packet received by a server.
/// @param [out] response response packet which addresses are to be
/// adjusted.
static void adjustRemoteAddr(const Pkt4Ptr& question,
const Pkt4Ptr& response);
/// @param ex The exchange holding both the client's message and the
/// server's response.
static void adjustRemoteAddr(Dhcpv4Exchange& ex);
/// @brief converts server-id to text
/// Converts content of server-id option to a text representation, e.g.
......@@ -617,9 +687,9 @@ protected:
/// @brief Selects a subnet for a given client's packet.
///
/// @param question client's message
/// @param query client's message
/// @return selected subnet (or NULL if no suitable subnet was found)
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question) const;
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query) const;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
......@@ -661,12 +731,21 @@ protected:
/// @brief Performs packet processing specific to a class
///
/// This processing is a likely candidate to be pushed into hooks.
/// If the selected subnet, query or response in the @c ex object is NULL
/// this method returns immediately and returns true.
///
/// @param query incoming client's packet
/// @param rsp server's response
/// @note This processing is a likely candidate to be pushed into hooks.
///
/// @param ex The exchange holding both the client's message and the
/// server's response.
/// @return true if successful, false otherwise (will prevent sending response)
bool classSpecificProcessing(const Pkt4Ptr& query, const Pkt4Ptr& rsp);
bool classSpecificProcessing(const Dhcpv4Exchange& ex);
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
/// during normal operation (e.g. to use different allocators)
boost::shared_ptr<AllocEngine> alloc_engine_;
private:
......@@ -676,11 +755,6 @@ private:
/// @return Option that contains netmask information
static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
/// during normal operation (e.g. to use different allocators)
boost::shared_ptr<AllocEngine> alloc_engine_;
uint16_t port_; ///< UDP port number on which server listens.
bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
......
......@@ -64,6 +64,7 @@ Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
ciaddr_(IOAddress("0.0.0.0")),
curr_transid_(0),
dest_addr_("255.255.255.255"),
fqdn_(),
hwaddr_(generateHWAddr()),
iface_name_("eth0"),
relay_addr_("192.0.2.2"),
......@@ -150,6 +151,8 @@ Dhcp4Client::doDiscover(const boost::shared_ptr<IOAddress>& requested_addr) {
context_.query_ = createMsg(DHCPDISCOVER);
// Request options if any.
includePRL();
// Include FQDN or Hostname.
includeName();
if (requested_addr) {
addRequestedAddress(*requested_addr);
}
......@@ -239,6 +242,8 @@ Dhcp4Client::doRequest() {
// Request options if any.
includePRL();
// Include FQDN or Hostname.
includeName();
// Send the message to the server.
sendMsg(context_.query_);
// Expect response.
......@@ -249,6 +254,33 @@ Dhcp4Client::doRequest() {
}
}
void
Dhcp4Client::includeFQDN(const uint8_t flags, const std::string& fqdn_name,
Option4ClientFqdn::DomainNameType fqdn_type) {
fqdn_.reset(new Option4ClientFqdn(flags, Option4ClientFqdn::RCODE_CLIENT(),
fqdn_name, fqdn_type));
}
void
Dhcp4Client::includeHostname(const std::string& name) {
hostname_.reset(new OptionString(Option::V4, DHO_HOST_NAME, name));
}
void
Dhcp4Client::includeName() {
if (!context_.query_) {
isc_throw(Dhcp4ClientError, "pointer to the query must not be NULL"
" when adding FQDN or Hostname option");
}
if (fqdn_) {
context_.query_->addOption(fqdn_);
} else if (hostname_) {
context_.query_->addOption(hostname_);
}
}
void
Dhcp4Client::includePRL() {
if (!context_.query_) {
......
......@@ -217,6 +217,21 @@ public:
return (srv_);
}
/// @brief Creates an instance of the Client FQDN option to be included
/// in the client's message.
///
/// @param flags Flags.
/// @param fqdn_name Name in the textual format.
/// @param fqdn_type Type of the name (fully qualified or partial).
void includeFQDN(const uint8_t flags, const std::string& fqdn_name,
Option4ClientFqdn::DomainNameType fqdn_type);
/// @brief Creates an instance of the Hostname option to be included
/// in the client's message.
///
/// @param name Name to be stored in the option.
void includeHostname(const std::string& name);
/// @brief Modifies the client's HW address (adds one to it).
///
/// The HW address should be modified to test negative scenarios when the
......@@ -345,6 +360,13 @@ private:
/// @return An instance of the message created.
Pkt4Ptr createMsg(const uint8_t msg_type);
/// @brief Includes FQDN or Hostname option in the client's message.
///
/// This method checks if @c fqdn_ or @c hostname_ is specified and
/// includes it in the client's message. If both are specified, the
/// @c fqdn_ will be used.
void includeName();
/// @brief Include PRL Option in the query message.
///
/// This function creates the instance of the PRL (Parameter Request List)
......@@ -376,6 +398,12 @@ private:
/// @brief Currently used destination address.
asiolink::IOAddress dest_addr_;
/// @brief FQDN requested by the client.
Option4ClientFqdnPtr fqdn_;
/// @brief Hostname requested by the client.
OptionStringPtr hostname_;
/// @brief Current hardware address of the client.
HWAddrPtr hwaddr_;
......@@ -406,4 +434,4 @@ private:
} // end of namespace isc::dhcp
} // end of namespace isc
#endif // DHCP4_CLIENT
#endif // DHCP4_CLIENT_H
......@@ -86,10 +86,10 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
req->setIface("eth1");
req->setIndex(1);
// Create a response packet. Assume that the new lease have
// been created and new address allocated. This address is
// stored in yiaddr field.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
// Create the exchange using the req.
Dhcpv4Exchange ex = createExchange(req);
Pkt4Ptr resp = ex.getResponse();
resp->setYiaddr(IOAddress("192.0.1.100"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
......@@ -97,7 +97,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
resp->setHops(req->getHops());
// This function never throws.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
// Now the destination address should be relay's address.
EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
......@@ -123,7 +123,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
// Clear remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
// Response should be sent back to the relay address.
EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
......@@ -163,8 +163,10 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
req->setIface("eth1");
req->setIndex(1);
// Create a response.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
// Create the exchange using the req.
Dhcpv4Exchange ex = createExchange(req);
Pkt4Ptr resp = ex.getResponse();
// Let's extend the lease for the client in such a way that
// it will actually get different address. The response
// should not be sent to this address but rather to ciaddr
......@@ -175,7 +177,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
// Copy hops value from the query.
resp->setHops(req->getHops());
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
// Check that server responds to ciaddr
EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
......@@ -225,8 +227,9 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
req->setIface("eth1");
req->setIndex(1);
// Create a response.