Parcourir la source

Merge branch 'trac1230'

Conflicts:
	ChangeLog
	src/lib/dhcp/iface_mgr.h
Tomek Mrugalski il y a 13 ans
Parent
commit
be309bf010

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+363.	[func]		tomek
+	dhcp4: Support for DISCOVER and OFFER implemented. b10-dhcp4 is
+	now able to offer hardcoded leases to DHCPv4 clients.
+	dhcp6: Code refactored to use the same approach as dhcp4.
+	(Trac #1230, git aac05f566c49daad4d3de35550cfaff31c124513)
+
 362.	[func]		tomek
 	libdhcp++: Interface detection in Linux implemented. libdhcp++
 	if now able to detect available network interfaces, its link-layer

+ 123 - 16
src/bin/dhcp4/dhcp4_srv.cc

@@ -17,13 +17,22 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <asiolink/io_address.h>
+#include <dhcp/option4_addrlst.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
-// #define ECHO_SERVER
+// These are hardcoded parameters. Currently this is a skeleton server that only
+// grants those options and a single, fixed, hardcoded lease.
+const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
+const std::string HARDCODED_NETMASK = "255.255.255.0";
+const uint32_t    HARDCODED_LEASE_TIME = 60; // in seconds
+const std::string HARDCODED_GATEWAY = "192.0.2.1";
+const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
+const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
+const std::string HARDCODED_SERVER_ID = "192.0.2.1";
 
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
     cout << "Initialization: opening sockets on port " << port << endl;
@@ -55,12 +64,6 @@ Dhcpv4Srv::run() {
 
         query = IfaceMgr::instance().receive4();
 
-#if defined(ECHO_SERVER)
-        query->repack();
-        IfaceMgr::instance().send(query);
-        continue;
-#endif
-
         if (query) {
             try {
                 query->unpack();
@@ -98,9 +101,16 @@ Dhcpv4Srv::run() {
             cout << query->toText();
 
             if (rsp) {
-                rsp->setRemoteAddr(query->getRemoteAddr());
+                if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
+                    rsp->setRemoteAddr(query->getRemoteAddr());
+                }
+                if (!rsp->getHops()) {
+                    rsp->setRemotePort(DHCP4_CLIENT_PORT);
+                } else {
+                    rsp->setRemotePort(DHCP4_SERVER_PORT);
+                }
+
                 rsp->setLocalAddr(query->getLocalAddr());
-                rsp->setRemotePort(DHCP4_CLIENT_PORT);
                 rsp->setLocalPort(DHCP4_SERVER_PORT);
                 rsp->setIface(query->getIface());
                 rsp->setIndex(query->getIndex());
@@ -112,10 +122,7 @@ Dhcpv4Srv::run() {
                 if (rsp->pack()) {
                     cout << "Packet assembled correctly." << endl;
                 }
-#if 1
-                // uncomment this once ticket 1240 is merged.
                 IfaceMgr::instance().send(rsp);
-#endif
             }
         }
 
@@ -139,16 +146,116 @@ Dhcpv4Srv::setServerID() {
 #endif
 }
 
+
+void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
+                                  boost::shared_ptr<Pkt4>& answer) {
+    answer->setIface(question->getIface());
+    answer->setIndex(question->getIndex());
+    answer->setCiaddr(question->getCiaddr());
+
+    answer->setSiaddr(IOAddress("0.0.0.0")); // explictly set this to 0
+    answer->setHops(question->getHops());
+
+    // copy MAC address
+    vector<uint8_t> mac(question->getChaddr(),
+                        question->getChaddr() + Pkt4::MAX_CHADDR_LEN);
+    answer->setHWAddr(question->getHtype(), question->getHlen(), mac);
+
+    // relay address
+    answer->setGiaddr(question->getGiaddr());
+
+    if (question->getGiaddr().toText() != "0.0.0.0") {
+        // relayed traffic
+        answer->setRemoteAddr(question->getGiaddr());
+    } else {
+        // direct traffic
+        answer->setRemoteAddr(question->getRemoteAddr());
+    }
+
+}
+
+void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type) {
+    boost::shared_ptr<Option> opt;
+
+    // add Message Type Option (type 53)
+    std::vector<uint8_t> tmp;
+    tmp.push_back(static_cast<uint8_t>(msg_type));
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
+    msg->addOption(opt);
+
+    // DHCP Server Identifier (type 54)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
+    msg->addOption(opt);
+
+    // more options will be added here later
+}
+
+
+void Dhcpv4Srv::appendRequestedOptions(boost::shared_ptr<Pkt4>& msg) {
+    boost::shared_ptr<Option> opt;
+
+    // Domain name (type 15)
+    vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
+    msg->addOption(opt);
+    // TODO: Add Option_String class
+
+    // DNS servers (type 6)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
+    msg->addOption(opt);
+}
+
+void Dhcpv4Srv::tryAssignLease(boost::shared_ptr<Pkt4>& msg) {
+    boost::shared_ptr<Option> opt;
+
+    // TODO: Implement actual lease assignment here
+    msg->setYiaddr(IOAddress(HARDCODED_LEASE));
+
+    // IP Address Lease time (type 51)
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
+    opt->setUint32(HARDCODED_LEASE_TIME);
+    msg->addOption(opt);
+    // TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
+
+    // Subnet mask (type 1)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
+    msg->addOption(opt);
+
+    // Router (type 3)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
+    msg->addOption(opt);
+}
+
 boost::shared_ptr<Pkt4>
 Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
-    /// TODO: Currently implemented echo mode. Implement this for real
-    return (discover);
+    boost::shared_ptr<Pkt4> offer = boost::shared_ptr<Pkt4>
+        (new Pkt4(DHCPOFFER, discover->getTransid()));
+
+    copyDefaultFields(discover, offer);
+    appendDefaultOptions(offer, DHCPOFFER);
+    appendRequestedOptions(offer);
+
+    tryAssignLease(offer);
+
+    return (offer);
 }
 
 boost::shared_ptr<Pkt4>
 Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
-    /// TODO: Currently implemented echo mode. Implement this for real
-    return (request);
+    boost::shared_ptr<Pkt4> ack = boost::shared_ptr<Pkt4>
+        (new Pkt4(DHCPACK, request->getTransid()));
+
+    copyDefaultFields(request, ack);
+    appendDefaultOptions(ack, DHCPACK);
+    appendRequestedOptions(ack);
+
+    tryAssignLease(ack);
+
+    return (ack);
 }
 
 void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {

+ 39 - 0
src/bin/dhcp4/dhcp4_srv.h

@@ -106,6 +106,45 @@ protected:
     /// @param infRequest message received from client
     boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& 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 boost::shared_ptr<Pkt4>& question,
+                           boost::shared_ptr<Pkt4>& 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 msg outgoing message (options will be added here)
+    void appendRequestedOptions(boost::shared_ptr<Pkt4>& msg);
+
+
+    /// @brief Assigns a lease and appends corresponding options
+    ///
+    /// This method chooses the most appropriate lease for reqesting
+    /// client and assigning it. Options corresponding to the lease
+    /// are added to specific message.
+    ///
+    /// Note: Lease manager is not implemented yet, so this method
+    /// used fixed, hardcoded lease.
+    ///
+    /// @param msg OFFER or ACK message (lease options will be added here)
+    void tryAssignLease(boost::shared_ptr<Pkt4>& msg);
+
+
+    /// @brief Appends default options to a message
+    ///
+    /// @param msg message object (options will be added to it)
+    /// @param msg_type specifies message type
+    void appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type);
+
     /// @brief Returns server-intentifier option
     ///
     /// @return server-id option

+ 111 - 9
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -23,10 +23,12 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp/option.h>
+#include <asiolink/io_address.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::asiolink;
 
 namespace {
 const char* const INTERFACE_FILE = "interfaces.txt";
@@ -66,6 +68,30 @@ public:
         fakeifaces.close();
     }
 
+    void MessageCheck(const boost::shared_ptr<Pkt4>& q,
+                      const boost::shared_ptr<Pkt4>& a) {
+        ASSERT_TRUE(q);
+        ASSERT_TRUE(a);
+
+        EXPECT_EQ(q->getHops(),   a->getHops());
+        EXPECT_EQ(q->getIface(),  a->getIface());
+        EXPECT_EQ(q->getIndex(),  a->getIndex());
+        EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
+
+        // check that bare minimum of required options are there
+        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
+        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
+        EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
+        EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
+        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
+        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
+        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
+        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));
+
+        // check that something is offered
+        EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
+    }
+
     ~Dhcpv4SrvTest() {
         unlink(INTERFACE_FILE);
     };
@@ -85,37 +111,113 @@ TEST_F(Dhcpv4SrvTest, basic) {
 
 TEST_F(Dhcpv4SrvTest, processDiscover) {
     NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+    vector<uint8_t> mac(6);
+    for (int i = 0; i < 6; i++) {
+        mac[i] = 255 - i;
+    }
 
     boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
+    boost::shared_ptr<Pkt4> offer;
+
+    pkt->setIface("eth0");
+    pkt->setIndex(17);
+    pkt->setHWAddr(1, 6, mac);
+    pkt->setRemoteAddr(IOAddress("192.0.2.56"));
+    pkt->setGiaddr(IOAddress("192.0.2.67"));
+
+    // let's make it a relayed message
+    pkt->setHops(3);
+    pkt->setRemotePort(DHCP4_SERVER_PORT);
 
     // should not throw
     EXPECT_NO_THROW(
-        srv->processDiscover(pkt);
+        offer = srv->processDiscover(pkt);
     );
 
     // should return something
-    EXPECT_TRUE(srv->processDiscover(pkt));
+    ASSERT_TRUE(offer);
+
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    // this is relayed message. It should be sent back to relay address.
+    EXPECT_EQ(pkt->getGiaddr(), offer->getRemoteAddr());
+
+    MessageCheck(pkt, offer);
+
+    // now repeat the test for directly sent message
+    pkt->setHops(0);
+    pkt->setGiaddr(IOAddress("0.0.0.0"));
+    pkt->setRemotePort(DHCP4_CLIENT_PORT);
+
+    EXPECT_NO_THROW(
+        offer = srv->processDiscover(pkt);
+    );
+
+    // should return something
+    ASSERT_TRUE(offer);
+
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    // this is direct message. It should be sent back to origin, not
+    // to relay.
+    EXPECT_EQ(pkt->getRemoteAddr(), offer->getRemoteAddr());
+
+    MessageCheck(pkt, offer);
 
-    // TODO: Implement more reasonable tests before starting
-    // work on processSomething() method.
     delete srv;
 }
 
 TEST_F(Dhcpv4SrvTest, processRequest) {
     NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+    vector<uint8_t> mac(6);
+    for (int i = 0; i < 6; i++) {
+        mac[i] = i*10;
+    }
 
-    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPREQUEST, 1234));
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPREQUEST, 1234));
+    boost::shared_ptr<Pkt4> ack;
+
+    req->setIface("eth0");
+    req->setIndex(17);
+    req->setHWAddr(1, 6, mac);
+    req->setRemoteAddr(IOAddress("192.0.2.56"));
+    req->setGiaddr(IOAddress("192.0.2.67"));
 
     // should not throw
+    ASSERT_NO_THROW(
+        ack = srv->processRequest(req);
+    );
+
+    // should return something
+    ASSERT_TRUE(ack);
+
+    EXPECT_EQ(DHCPACK, ack->getType());
+
+    // this is relayed message. It should be sent back to relay address.
+    EXPECT_EQ(req->getGiaddr(), ack->getRemoteAddr());
+
+    MessageCheck(req, ack);
+
+    // now repeat the test for directly sent message
+    req->setHops(0);
+    req->setGiaddr(IOAddress("0.0.0.0"));
+    req->setRemotePort(DHCP4_CLIENT_PORT);
+
     EXPECT_NO_THROW(
-        srv->processRequest(pkt);
+        ack = srv->processDiscover(req);
     );
 
     // should return something
-    EXPECT_TRUE(srv->processRequest(pkt));
+    ASSERT_TRUE(ack);
+
+    EXPECT_EQ(DHCPOFFER, ack->getType());
+
+    // this is direct message. It should be sent back to origin, not
+    // to relay.
+    EXPECT_EQ(ack->getRemoteAddr(), req->getRemoteAddr());
+
+    MessageCheck(req, ack);
 
-    // TODO: Implement more reasonable tests before starting
-    // work on processSomething() method.
     delete srv;
 }
 

+ 86 - 37
src/bin/dhcp6/dhcp6_srv.cc

@@ -18,6 +18,7 @@
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_addrlst.h>
 #include <asiolink/io_address.h>
 #include <exceptions/exceptions.h>
 
@@ -26,21 +27,28 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
+const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
+const uint32_t HARDCODED_T1 = 1500; // in seconds
+const uint32_t HARDCODED_T2 = 2600; // in seconds
+const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
+const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
+const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
+
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
     cout << "Initialization" << endl;
 
     // first call to instance() will create IfaceMgr (it's a singleton)
     // it may throw something if things go wrong
     try {
-	IfaceMgr::instance();
+        IfaceMgr::instance();
     } catch (const std::exception &e) {
-	cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
-	shutdown = true;
+        cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
+        shutdown = true;
     }
 
     if (IfaceMgr::instance().countIfaces() == 0) {
-	cout << "Failed to detect any network interfaces. Aborting." << endl;
-	shutdown = true;
+        cout << "Failed to detect any network interfaces. Aborting." << endl;
+        shutdown = true;
     }
 
     // Now try to open IPv6 sockets on detected interfaces.
@@ -115,8 +123,9 @@ Dhcpv6Srv::run() {
                 cout << "Replying with:" << rsp->getType() << endl;
                 cout << rsp->toText();
                 cout << "----" << endl;
-                if (rsp->pack()) {
-                    cout << "#### pack successful." << endl;
+                if (!rsp->pack()) {
+                    cout << "Failed to assemble response packet." << endl;
+                    continue;
                 }
                 IfaceMgr::instance().send(rsp);
             }
@@ -149,18 +158,45 @@ Dhcpv6Srv::setServerID() {
                                                      0, 14));
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processSolicit(boost::shared_ptr<Pkt6> solicit) {
+void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                                   boost::shared_ptr<Pkt6>& answer) {
+    // add client-id
+    boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
+    if (clientid) {
+        answer->addOption(clientid);
+    }
 
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_ADVERTISE,
-                                           solicit->getTransid(),
-                                           Pkt6::UDP));
+    // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
+}
+
+void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+                                   boost::shared_ptr<Pkt6>& answer) {
+    // TODO: question is currently unused, but we need it at least to know
+    // message type we are answering
 
+    // add server-id
+    answer->addOption(getServerID());
+}
+
+
+void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+                                       boost::shared_ptr<Pkt6>& answer) {
+    // TODO: question is currently unused, but we need to extract ORO from it
+    // and act on its content. Now we just send DNS-SERVERS option.
+
+    // add dns-servers option
+    boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
+                                         IOAddress(HARDCODED_DNS_SERVER)));
+    answer->addOption(dnsservers);
+}
+
+void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
+                             boost::shared_ptr<Pkt6>& answer) {
     /// TODO Rewrite this once LeaseManager is implemented.
 
     // answer client's IA (this is mostly a dummy,
     // so let's answer only first IA and hope there is only one)
-    boost::shared_ptr<Option> ia_opt = solicit->getOption(D6O_IA_NA);
+    boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
     if (ia_opt) {
         // found IA
         Option* tmp = ia_opt.get();
@@ -168,38 +204,51 @@ Dhcpv6Srv::processSolicit(boost::shared_ptr<Pkt6> solicit) {
         if (ia_req) {
             boost::shared_ptr<Option6IA>
                 ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
-            ia_rsp->setT1(1500);
-            ia_rsp->setT2(2600);
+            ia_rsp->setT1(HARDCODED_T1);
+            ia_rsp->setT2(HARDCODED_T2);
             boost::shared_ptr<Option6IAAddr>
                 addr(new Option6IAAddr(D6O_IAADDR,
-                                       IOAddress("2001:db8:1234:5678::abcd"),
-                                       5000, 7000));
+                                       IOAddress(HARDCODED_LEASE),
+                                       HARDCODED_PREFERRED_LIFETIME,
+                                       HARDCODED_VALID_LIFETIME));
             ia_rsp->addOption(addr);
-            reply->addOption(ia_rsp);
+            answer->addOption(ia_rsp);
         }
     }
+}
 
-    // add client-id
-    boost::shared_ptr<Option> clientid = solicit->getOption(D6O_CLIENTID);
-    if (clientid) {
-        reply->addOption(clientid);
-    }
+boost::shared_ptr<Pkt6>
+Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
 
-    // add server-id
-    reply->addOption(getServerID());
-    return reply;
+    boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE,
+                                               solicit->getTransid()));
+
+    copyDefaultOptions(solicit, advertise);
+    appendDefaultOptions(solicit, advertise);
+    appendRequestedOptions(solicit, advertise);
+
+    assignLeases(solicit, advertise);
+
+    return (advertise);
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRequest(boost::shared_ptr<Pkt6> request) {
-    /// TODO: Implement processRequest() for real
-    boost::shared_ptr<Pkt6> reply = processSolicit(request);
-    reply->setType(DHCPV6_REPLY);
-    return reply;
+Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
+
+    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
+                                           request->getTransid()));
+
+    copyDefaultOptions(request, reply);
+    appendDefaultOptions(request, reply);
+    appendRequestedOptions(request, reply);
+
+    assignLeases(request, reply);
+
+    return (reply);
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRenew(boost::shared_ptr<Pkt6> renew) {
+Dhcpv6Srv::processRenew(const boost::shared_ptr<Pkt6>& renew) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            renew->getTransid(),
                                            Pkt6::UDP));
@@ -207,7 +256,7 @@ Dhcpv6Srv::processRenew(boost::shared_ptr<Pkt6> renew) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRebind(boost::shared_ptr<Pkt6> rebind) {
+Dhcpv6Srv::processRebind(const boost::shared_ptr<Pkt6>& rebind) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            rebind->getTransid(),
                                            Pkt6::UDP));
@@ -215,7 +264,7 @@ Dhcpv6Srv::processRebind(boost::shared_ptr<Pkt6> rebind) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processConfirm(boost::shared_ptr<Pkt6> confirm) {
+Dhcpv6Srv::processConfirm(const boost::shared_ptr<Pkt6>& confirm) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            confirm->getTransid(),
                                            Pkt6::UDP));
@@ -223,7 +272,7 @@ Dhcpv6Srv::processConfirm(boost::shared_ptr<Pkt6> confirm) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRelease(boost::shared_ptr<Pkt6> release) {
+Dhcpv6Srv::processRelease(const boost::shared_ptr<Pkt6>& release) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            release->getTransid(),
                                            Pkt6::UDP));
@@ -231,7 +280,7 @@ Dhcpv6Srv::processRelease(boost::shared_ptr<Pkt6> release) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processDecline(boost::shared_ptr<Pkt6> decline) {
+Dhcpv6Srv::processDecline(const boost::shared_ptr<Pkt6>& decline) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            decline->getTransid(),
                                            Pkt6::UDP));
@@ -239,7 +288,7 @@ Dhcpv6Srv::processDecline(boost::shared_ptr<Pkt6> decline) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processInfRequest(boost::shared_ptr<Pkt6> infRequest) {
+Dhcpv6Srv::processInfRequest(const boost::shared_ptr<Pkt6>& infRequest) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            infRequest->getTransid(),
                                            Pkt6::UDP));

+ 52 - 8
src/bin/dhcp6/dhcp6_srv.h

@@ -81,7 +81,7 @@ protected:
     ///
     /// @return ADVERTISE, REPLY message or NULL
     boost::shared_ptr<Pkt6>
-    processSolicit(boost::shared_ptr<Pkt6> solicit);
+    processSolicit(const boost::shared_ptr<Pkt6>& solicit);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
     ///
@@ -95,43 +95,87 @@ protected:
     ///
     /// @return REPLY message or NULL
     boost::shared_ptr<Pkt6>
-    processRequest(boost::shared_ptr<Pkt6> request);
+    processRequest(const boost::shared_ptr<Pkt6>& request);
 
     /// @brief Stub function that will handle incoming RENEW messages.
     ///
     /// @param renew message received from client
     boost::shared_ptr<Pkt6>
-    processRenew(boost::shared_ptr<Pkt6> renew);
+    processRenew(const boost::shared_ptr<Pkt6>& renew);
 
     /// @brief Stub function that will handle incoming REBIND messages.
     ///
     /// @param rebind message received from client
     boost::shared_ptr<Pkt6>
-    processRebind(boost::shared_ptr<Pkt6> rebind);
+    processRebind(const boost::shared_ptr<Pkt6>& rebind);
 
     /// @brief Stub function that will handle incoming CONFIRM messages.
     ///
     /// @param confirm message received from client
     boost::shared_ptr<Pkt6>
-    processConfirm(boost::shared_ptr<Pkt6> confirm);
+    processConfirm(const boost::shared_ptr<Pkt6>& confirm);
 
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
     /// @param release message received from client
     boost::shared_ptr<Pkt6>
-    processRelease(boost::shared_ptr<Pkt6> release);
+    processRelease(const boost::shared_ptr<Pkt6>& release);
 
     /// @brief Stub function that will handle incoming DECLINE messages.
     ///
     /// @param decline message received from client
     boost::shared_ptr<Pkt6>
-    processDecline(boost::shared_ptr<Pkt6> decline);
+    processDecline(const boost::shared_ptr<Pkt6>& decline);
 
     /// @brief Stub function that will handle incoming INF-REQUEST messages.
     ///
     /// @param infRequest message received from client
     boost::shared_ptr<Pkt6>
-    processInfRequest(boost::shared_ptr<Pkt6> infRequest);
+    processInfRequest(const boost::shared_ptr<Pkt6>& infRequest);
+
+    /// @brief Copies required options from client message to server answer
+    ///
+    /// Copies options that must appear in any server response (ADVERTISE, REPLY)
+    /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
+    /// One notable example is client-id. Other options may be copied as required.
+    ///
+    /// @param question client's message (options will be copied from here)
+    /// @param answer server's message (options will be copied here)
+    void copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                            boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Appends default options to server's answer.
+    ///
+    /// Adds required options to server's answer. In particular, server-id
+    /// is added. Possibly other mandatory options will be added, depending
+    /// on type (or content) of client message.
+    ///
+    /// @param question client's message
+    /// @param answer server's message (options will be added here)
+    void appendDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                              boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Appends requested options to server's answer.
+    ///
+    /// Appends options requested by client to the server's answer.
+    /// TODO: This method is currently a stub. It just appends DNS-SERVERS
+    /// option.
+    ///
+    /// @param question client's message
+    /// @param answer server's message (options will be added here)
+    void appendRequestedOptions(const boost::shared_ptr<Pkt6>& question,
+                                boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Assigns leases.
+    ///
+    /// TODO: This method is currently a stub. It just appends one
+    /// hardcoded lease. It supports addresses (IA_NA) only. It does NOT
+    /// support temporary addresses (IA_TA) nor prefixes (IA_PD).
+    ///
+    /// @param question client's message (with requested IA_NA)
+    /// @param answer server's message (IA_NA options will be added here)
+    void assignLeases(const boost::shared_ptr<Pkt6>& question,
+                      boost::shared_ptr<Pkt6>& answer);
 
     /// @brief Sets server-identifier.
     ///

+ 32 - 21
src/lib/dhcp/iface_mgr.cc

@@ -211,13 +211,11 @@ bool IfaceMgr::openSockets4(uint16_t port) {
 
         cout << "Trying interface " << iface->getFullName() << endl;
 
-#if 0
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
             !iface->flag_running_) {
             continue;
         }
-#endif
 
         AddressCollection addrs = iface->getAddresses();
 
@@ -225,7 +223,7 @@ bool IfaceMgr::openSockets4(uint16_t port) {
              addr != addrs.end();
              ++addr) {
 
-            // skip IPv4 addresses
+            // skip IPv6 addresses
             if (addr->getFamily() != AF_INET) {
                 continue;
             }
@@ -251,6 +249,12 @@ bool IfaceMgr::openSockets6(uint16_t port) {
          iface!=ifaces_.end();
          ++iface) {
 
+        if (iface->flag_loopback_ ||
+            !iface->flag_up_ ||
+            !iface->flag_running_) {
+            continue;
+        }
+
         AddressCollection addrs = iface->getAddresses();
 
         for (AddressCollection::iterator addr= addrs.begin();
@@ -276,15 +280,15 @@ bool IfaceMgr::openSockets6(uint16_t port) {
             }
 
             count++;
-#if 0
+#if defined(OS_LINUX)
             // this doesn't work too well on NetBSD
-            sock2 = openSocket(iface->getName(),
-                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
-                               DHCP6_SERVER_PORT);
+            int sock2 = openSocket(iface->getName(),
+                                   IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+                                   port);
             if (sock2<0) {
                 isc_throw(Unexpected, "Failed to open multicast socket on "
                           << " interface " << iface->getFullName());
-                iface->delSocket(sock1); // delete previously opened socket
+                iface->delSocket(sock); // delete previously opened socket
             }
 #endif
         }
@@ -876,25 +880,32 @@ IfaceMgr::receive6() {
     /// all available sockets. For now, we just take the
     /// first interface and use first socket from it.
     IfaceCollection::const_iterator iface = ifaces_.begin();
-    if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
-    }
-    SocketCollection::const_iterator s = iface->sockets_.begin();
     const SocketInfo* candidate = 0;
-    while (s != iface->sockets_.end()) {
-        if (s->addr_.getFamily() != AF_INET6) {
+    while (iface != ifaces_.end()) {
+        SocketCollection::const_iterator s = iface->sockets_.begin();
+        while (s != iface->sockets_.end()) {
+            if (s->addr_.getFamily() != AF_INET6) {
+                ++s;
+                continue;
+            }
+            if (s->addr_.getAddress().to_v6().is_multicast()) {
+                candidate = &(*s);
+                break;
+            }
+            if (!candidate) {
+                candidate = &(*s); // it's not multicast, but it's better than nothing
+            }
             ++s;
-            continue;
         }
-        if (s->addr_.getAddress().to_v6().is_multicast()) {
-            candidate = &(*s);
+        if (candidate) {
             break;
         }
-        if (!candidate) {
-            candidate = &(*s); // it's not multicast, but it's better than nothing
-        }
-        ++s;
+        ++iface;
     }
+    if (iface == ifaces_.end()) {
+        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+    }
+
     if (!candidate) {
         isc_throw(Unexpected, "Interface " << iface->getFullName()
                   << " does not have any sockets open.");

+ 1 - 1
src/lib/dhcp/iface_mgr.h

@@ -343,7 +343,7 @@ public:
     /// Opens IPv4 sockets on detected interfaces.
     /// Will throw exception if socket creation fails.
     ///
-    /// @param port specifies port number (usually DHCP6_SERVER_PORT)
+    /// @param port specifies port number (usually DHCP4_SERVER_PORT)
     ///
     /// @return true if any sockets were open
     bool openSockets4(uint16_t port = DHCP4_SERVER_PORT);

+ 15 - 0
src/lib/dhcp/option.cc

@@ -326,6 +326,21 @@ uint32_t Option::getUint32() {
     return ( readUint32(&data_[0]) );
 }
 
+void Option::setUint8(uint8_t value) {
+  data_.resize(1);
+  data_[0] = value;
+}
+
+void Option::setUint16(uint16_t value) {
+  data_.resize(2);
+  writeUint16(value, &data_[0]);
+}
+
+void Option::setUint32(uint32_t value) {
+  data_.resize(4);
+  writeUint32(value, &data_[0]);
+}
+
 Option::~Option() {
 
 }

+ 21 - 0
src/lib/dhcp/option.h

@@ -257,6 +257,27 @@ public:
     /// @return uint32_t value stored on first four bytes
     uint32_t getUint32();
 
+    /// @brief Sets content of this option to singe uint8 value.
+    ///
+    /// Option it resized appropriately (to length of 1 octet).
+    ///
+    /// @param value value to be set
+    void setUint8(uint8_t value);
+
+    /// @brief Sets content of this option to singe uint16 value.
+    ///
+    /// Option it resized appropriately (to length of 2 octets).
+    ///
+    /// @param value value to be set
+    void setUint16(uint16_t value);
+
+    /// @brief Sets content of this option to singe uint32 value.
+    ///
+    /// Option it resized appropriately (to length of 4 octets).
+    ///
+    /// @param value value to be set
+    void setUint32(uint32_t value);
+
     /// just to force that every option has virtual dtor
     virtual ~Option();
 

+ 7 - 4
src/lib/dhcp/pkt4.cc

@@ -125,7 +125,8 @@ Pkt4::pack() {
 
     return (true);
 }
-bool
+
+void
 Pkt4::unpack() {
 
     // input buffer (used during message reception)
@@ -156,7 +157,7 @@ Pkt4::unpack() {
         // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
         // particular, it does not have magic cookie, a 4 byte sequence that
         // differentiates between DHCP and BOOTP packets.
-        return (true);
+        isc_throw(InvalidOperation, "Recevied BOOTP packet. BOOTP is not supported.");
     }
 
     if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
@@ -172,11 +173,13 @@ Pkt4::unpack() {
     size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
     vector<uint8_t> optsBuffer;
 
-    // fist use of readVector
+    // First use of readVector.
     bufferIn.readVector(optsBuffer, opts_len);
     LibDHCP::unpackOptions4(optsBuffer, options_);
 
-    return (true);
+    // TODO: check will need to be called separately, so hooks can be called after
+    // packet is parsed, but before its content is verified
+    check();
 }
 
 void Pkt4::check() {

+ 4 - 3
src/lib/dhcp/pkt4.h

@@ -74,9 +74,8 @@ public:
     /// Will create a collection of option objects that will
     /// be stored in options_ container.
     ///
-    /// @return true, if parsing was successful
-    bool
-    unpack();
+    /// Method with throw exception if packet parsing fails.
+    void unpack();
 
     /// @brief performs sanity check on a packet.
     ///
@@ -86,6 +85,8 @@ public:
     /// reasonable value. This method is expected to grow significantly.
     /// It makes sense to separate unpack() and check() for testing purposes.
     ///
+    /// TODO: It is called from unpack() directly. It should be separated.
+    ///
     /// Method will throw exception if anomaly is found.
     void check();
 

+ 7 - 1
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -406,6 +406,12 @@ TEST_F(IfaceMgrTest, sendReceive4) {
     sendPkt->setYiaddr(IOAddress("192.0.2.3"));
     sendPkt->setGiaddr(IOAddress("192.0.2.4"));
 
+    // unpack() now checks if mandatory DHCP_MESSAGE_TYPE is present
+    boost::shared_ptr<Option> msgType(new Option(Option::V4,
+           static_cast<uint16_t>(DHO_DHCP_MESSAGE_TYPE)));
+    msgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
+    sendPkt->addOption(msgType);
+
     uint8_t sname[] = "That's just a string that will act as SNAME";
     sendPkt->setSname(sname, strlen((const char*)sname));
     uint8_t file[] = "/another/string/that/acts/as/a/file_name.txt";
@@ -761,7 +767,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
 // (check that each interface is reported, i.e. no missing or extra interfaces) and
 // address completeness is verified.
 //
-// Things that are not tested: 
+// Things that are not tested:
 // - ifindex (ifconfig does not print it out)
 // - address scopes and lifetimes (we don't need it, so it is not implemented in IfaceMgr)
 TEST_F(IfaceMgrTest, detectIfaces_linux) {

+ 44 - 1
src/lib/dhcp/tests/option_unittest.cc

@@ -33,8 +33,14 @@ using namespace isc::util;
 namespace {
 class OptionTest : public ::testing::Test {
 public:
-    OptionTest() {
+    OptionTest(): outBuffer_(255) {
+        buf_ = boost::shared_array<uint8_t>(new uint8_t[255]);
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
     }
+    boost::shared_array<uint8_t> buf_;
+    OutputBuffer outBuffer_;
 };
 
 // v4 is not really implemented yet. A simple test will do for now
@@ -421,6 +427,8 @@ TEST_F(OptionTest, v6_toText) {
 }
 
 TEST_F(OptionTest, getUintX) {
+
+    // TODO: Update this test to use buf_ instead of buf
     boost::shared_array<uint8_t> buf(new uint8_t[5]);
     buf[0] = 0x5;
     buf[1] = 0x4;
@@ -457,3 +465,38 @@ TEST_F(OptionTest, getUintX) {
     EXPECT_EQ(0x05040302, opt5->getUint32());
 
 }
+
+TEST_F(OptionTest, setUintX) {
+    boost::shared_ptr<Option> opt1(new Option(Option::V4, 125));
+    boost::shared_ptr<Option> opt2(new Option(Option::V4, 125));
+    boost::shared_ptr<Option> opt4(new Option(Option::V4, 125));
+
+    // verify setUint8
+    opt1->setUint8(255);
+    EXPECT_EQ(255, opt1->getUint8());
+    opt1->pack4(outBuffer_);
+    EXPECT_EQ(3, opt1->len());
+    EXPECT_EQ(3, outBuffer_.getLength());
+    uint8_t exp1[] = {125, 1, 255};
+    EXPECT_TRUE(0 == memcmp(exp1, outBuffer_.getData(), 3));
+
+    // verify getUint16
+    outBuffer_.clear();
+    opt2->setUint16(12345);
+    opt2->pack4(outBuffer_);
+    EXPECT_EQ(12345, opt2->getUint16());
+    EXPECT_EQ(4, opt2->len());
+    EXPECT_EQ(4, outBuffer_.getLength());
+    uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
+    EXPECT_TRUE(0 == memcmp(exp2, outBuffer_.getData(), 4));
+
+    // verity getUint32
+    outBuffer_.clear();
+    opt4->setUint32(0x12345678);
+    opt4->pack4(outBuffer_);
+    EXPECT_EQ(0x12345678, opt4->getUint32());
+    EXPECT_EQ(6, opt4->len());
+    EXPECT_EQ(6, outBuffer_.getLength());
+    uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
+    EXPECT_TRUE(0 == memcmp(exp4, outBuffer_.getData(), 6));
+}

+ 18 - 4
src/lib/dhcp/tests/pkt4_unittest.cc

@@ -244,8 +244,18 @@ TEST(Pkt4Test, fixedFieldsPack) {
 TEST(Pkt4Test, fixedFieldsUnpack) {
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
+    expectedFormat.push_back(0x63); // magic cookie
+    expectedFormat.push_back(0x82);
+    expectedFormat.push_back(0x53);
+    expectedFormat.push_back(0x63);
+
+    expectedFormat.push_back(0x35); // message-type
+    expectedFormat.push_back(0x1);
+    expectedFormat.push_back(0x1);
+
     boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
-                                Pkt4::DHCPV4_PKT_HDR_LEN));
+                                         expectedFormat.size()));;
+
 
     EXPECT_NO_THROW(
         pkt->unpack()
@@ -441,8 +451,9 @@ static uint8_t v4Opts[] = {
     12,  3, 0,   1,  2,
     13,  3, 10, 11, 12,
     14,  3, 20, 21, 22,
+    53, 1, 1, // DHCP_MESSAGE_TYPE (required to not throw exception during unpack)
     128, 3, 30, 31, 32,
-    254, 3, 40, 41, 42
+    254, 3, 40, 41, 42,
 };
 
 TEST(Pkt4Test, options) {
@@ -458,14 +469,17 @@ TEST(Pkt4Test, options) {
     boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
     boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
     boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
+    boost::shared_ptr<Option> optMsgType(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE));
     boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[3]));
     boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[4]));
+    optMsgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
 
     pkt->addOption(opt1);
     pkt->addOption(opt2);
     pkt->addOption(opt3);
     pkt->addOption(opt4);
     pkt->addOption(opt5);
+    pkt->addOption(optMsgType);
 
     EXPECT_TRUE(pkt->getOption(12));
     EXPECT_TRUE(pkt->getOption(13));
@@ -556,14 +570,14 @@ TEST(Pkt4Test, unpackOptions) {
     EXPECT_EQ(128, x->getType());  // this should be option 254
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+17, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+20, 3)); // data len=3
 
     x = pkt->getOption(254);
     ASSERT_TRUE(x); // option 3 should exist
     EXPECT_EQ(254, x->getType());  // this should be option 254
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+22, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+25, 3)); // data len=3
 }
 
 // This test verifies methods that are used for manipulating meta fields