Browse Source

[991] Server responds with broadcast if direct response not supported.

Marcin Siodelski 12 years ago
parent
commit
84076d913d

+ 17 - 1
src/bin/dhcp4/dhcp4_srv.cc

@@ -317,7 +317,7 @@ Dhcpv4Srv::writeServerID(const std::string& file_name) {
     return (true);
 }
 
-string 
+string
 Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
     if (!srvid) {
         isc_throw(BadValue, "NULL pointer passed to srvidToString()");
@@ -517,6 +517,22 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
 
         answer->setYiaddr(lease->addr_);
 
+        // If remote address is not set, we are dealing with a directly
+        // connected client requesting new lease. We scan end response to
+        // the address assigned in the lease, but first we have to make sure
+        // that IfaceMgr supports responding directly to the client when
+        // client doesn't have address assigned to its interface yet.
+        if (answer->getRemoteAddr().toText() == "0.0.0.0") {
+            if (IfaceMgr::instance().isDirectResponseSupported()) {
+                answer->setRemoteAddr(lease->addr_);
+            } else {
+                // Since IfaceMgr does not support direct responses to
+                // clients not having IP address, we have to send response
+                // to broadcast.
+                answer->setRemoteAddr(IOAddress("255.255.255.255"));
+            }
+        }
+
         // IP Address Lease time (type 51)
         opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
         opt->setUint32(lease->valid_lft_);

+ 137 - 188
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -17,6 +17,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp4.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp/option.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_custom.h>
@@ -345,6 +346,113 @@ public:
         EXPECT_TRUE(expected_clientid->getData() == opt->getData());
     }
 
+    /// @brief Tests if Discover or Request message is processed correctly
+    ///
+    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
+    /// @param client_addr client address
+    /// @param relay_addr relay address
+    void testDiscoverRequest(const uint8_t msg_type,
+                             const IOAddress& client_addr,
+                             const IOAddress& relay_addr) {
+
+        NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(0);
+        vector<uint8_t> mac(6);
+        for (int i = 0; i < 6; i++) {
+            mac[i] = i*10;
+        }
+
+        boost::shared_ptr<Pkt4> req(new Pkt4(msg_type, 1234));
+        boost::shared_ptr<Pkt4> rsp;
+
+        req->setIface("eth0");
+        req->setIndex(17);
+        req->setHWAddr(1, 6, mac);
+        req->setRemoteAddr(IOAddress(client_addr));
+        req->setGiaddr(relay_addr);
+
+        // We are going to test that certain options are returned
+        // in the response message when requested using 'Parameter
+        // Request List' option. Let's configure those options that
+        // are returned when requested.
+        configureRequestedOptions();
+
+        if (msg_type == DHCPDISCOVER) {
+            ASSERT_NO_THROW(
+                rsp = srv->processDiscover(req);
+            );
+
+            // Should return OFFER
+            ASSERT_TRUE(rsp);
+            EXPECT_EQ(DHCPOFFER, rsp->getType());
+
+        } else {
+            ASSERT_NO_THROW(
+                rsp = srv->processRequest(req);
+            );
+
+            // Should return ACK
+            ASSERT_TRUE(rsp);
+            EXPECT_EQ(DHCPACK, rsp->getType());
+
+        }
+
+        if (relay_addr.toText() != "0.0.0.0") {
+            // This is relayed message. It should be sent brsp to relay address.
+            EXPECT_EQ(req->getGiaddr().toText(),
+                      rsp->getRemoteAddr().toText());
+
+        } else if (client_addr.toText() != "0.0.0.0") {
+            // This is a message from a client having an IP address.
+            EXPECT_EQ(req->getRemoteAddr().toText(),
+                      rsp->getRemoteAddr().toText());
+
+        } else {
+            // This is a message from a client having no IP address yet.
+            // If IfaceMgr supports direct traffic the response should
+            // be sent to the new address assigned to the client.
+            if (IfaceMgr::instance().isDirectResponseSupported()) {
+                EXPECT_EQ(rsp->getYiaddr(),
+                          rsp->getRemoteAddr().toText());
+
+            // If direct response to the client having no IP address is
+            // not supported, response should go to broadcast.
+            } else {
+                EXPECT_EQ("255.255.255.255", rsp->getRemoteAddr().toText());
+
+            }
+
+        }
+
+        messageCheck(req, rsp);
+
+        // There are some options that are always present in the
+        // message, even if not requested.
+        EXPECT_TRUE(rsp->getOption(DHO_DOMAIN_NAME));
+        EXPECT_TRUE(rsp->getOption(DHO_DOMAIN_NAME_SERVERS));
+
+        // We did not request any options so these should not be present
+        // in the RSP.
+        EXPECT_FALSE(rsp->getOption(DHO_LOG_SERVERS));
+        EXPECT_FALSE(rsp->getOption(DHO_COOKIE_SERVERS));
+        EXPECT_FALSE(rsp->getOption(DHO_LPR_SERVERS));
+
+        // Add 'Parameter Request List' option.
+        addPrlOption(req);
+
+        // Repeat the test but request some options.
+        ASSERT_NO_THROW(
+            rsp = srv->processRequest(req);
+        );
+
+        // Should return something
+        ASSERT_TRUE(rsp);
+
+        EXPECT_EQ(DHCPACK, rsp->getType());
+
+        // Check that the requested options are returned.
+        optionsCheck(rsp);
+    }
+
     ~Dhcpv4SrvTest() {
         CfgMgr::instance().deleteSubnets4();
 
@@ -389,7 +497,7 @@ TEST_F(Dhcpv4SrvTest, basic) {
     delete naked_srv;
 }
 
-// Verifies that received DISCOVER can be processed correctly,
+// Verifies that DISCOVER received via relay can be processed correctly,
 // that the OFFER message generated in response is valid and
 // contains necessary options.
 //
@@ -397,203 +505,44 @@ TEST_F(Dhcpv4SrvTest, basic) {
 // are other tests that verify correctness of the allocation
 // engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
 // and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvTest, processDiscover) {
-    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(0);
-    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);
-
-    // We are going to test that certain options are returned
-    // (or not returned) in the OFFER message when requested
-    // using 'Parameter Request List' option. Let's configure
-    // those options that are returned when requested.
-    configureRequestedOptions();
-
-    // Should not throw
-    EXPECT_NO_THROW(
-        offer = srv->processDiscover(pkt);
-    );
-
-    // Should return something
-    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);
-
-    // There are some options that are always present in the
-    // message, even if not requested.
-    EXPECT_TRUE(offer->getOption(DHO_DOMAIN_NAME));
-    EXPECT_TRUE(offer->getOption(DHO_DOMAIN_NAME_SERVERS));
-
-    // We did not request any options so they should not be present
-    // in the OFFER.
-    EXPECT_FALSE(offer->getOption(DHO_LOG_SERVERS));
-    EXPECT_FALSE(offer->getOption(DHO_COOKIE_SERVERS));
-    EXPECT_FALSE(offer->getOption(DHO_LPR_SERVERS));
-
-    // Add 'Parameter Request List' option.
-    addPrlOption(pkt);
-
-    // Now repeat the test but request some options.
-    EXPECT_NO_THROW(
-        offer = srv->processDiscover(pkt);
-    );
-
-    // Should return something
-    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);
-
-    // Check that the requested options are returned.
-    optionsCheck(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);
+TEST_F(Dhcpv4SrvTest, processDiscoverRelay) {
+    testDiscoverRequest(DHCPDISCOVER, IOAddress("192.0.2.56"), IOAddress("192.0.2.67"));
+}
 
-    // Check that the requested options are returned.
-    optionsCheck(offer);
+// Verifies that the non-relayed DISCOVER is processed correctly when
+// client source address is specified.
+TEST_F(Dhcpv4SrvTest, processDiscoverNoRelay) {
+    testDiscoverRequest(DHCPDISCOVER, IOAddress("0.0.0.0"), IOAddress("192.0.2.67"));
+}
 
-    delete srv;
+// Verified that the non-relayed DISCOVER is processed correctly when
+// client source address is not specified.
+TEST_F(Dhcpv4SrvTest, processDiscoverNoClientAddr) {
+    testDiscoverRequest(DHCPDISCOVER, IOAddress("0.0.0.0"), IOAddress("0.0.0.0"));
 }
 
-// Verifies that received REQUEST can be processed correctly,
-// that the ACK message generated in response is valid and
+// Verifies that REQUEST received via relay can be processed correctly,
+// that the OFFER message generated in response is valid and
 // contains necessary options.
 //
 // Note: this test focuses on the packet correctness. There
 // are other tests that verify correctness of the allocation
-// engine. See RequestBasic.
-TEST_F(Dhcpv4SrvTest, processRequest) {
-    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(0);
-    vector<uint8_t> mac(6);
-    for (int i = 0; i < 6; i++) {
-        mac[i] = i*10;
-    }
-
-    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"));
-
-    // We are going to test that certain options are returned
-    // in the ACK message when requested using 'Parameter
-    // Request List' option. Let's configure those options that
-    // are returned when requested.
-    configureRequestedOptions();
-
-    // 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);
-
-    // There are some options that are always present in the
-    // message, even if not requested.
-    EXPECT_TRUE(ack->getOption(DHO_DOMAIN_NAME));
-    EXPECT_TRUE(ack->getOption(DHO_DOMAIN_NAME_SERVERS));
-
-    // We did not request any options so these should not be present
-    // in the ACK.
-    EXPECT_FALSE(ack->getOption(DHO_LOG_SERVERS));
-    EXPECT_FALSE(ack->getOption(DHO_COOKIE_SERVERS));
-    EXPECT_FALSE(ack->getOption(DHO_LPR_SERVERS));
-
-    // Add 'Parameter Request List' option.
-    addPrlOption(req);
-
-    // Repeat the test but request some options.
-    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());
-
-    // Check that the requested options are returned.
-    optionsCheck(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(
-        ack = srv->processDiscover(req);
-    );
-
-    // Should return something
-    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);
+// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
+// and DiscoverInvalidHint.
+TEST_F(Dhcpv4SrvTest, processRequestRelay) {
+    testDiscoverRequest(DHCPREQUEST, IOAddress("192.0.2.56"), IOAddress("192.0.2.67"));
+}
 
-    // Check that the requested options are returned.
-    optionsCheck(ack);
+// Verifies that the non-relayed REQUEST is processed correctly when
+// client source address is specified.
+TEST_F(Dhcpv4SrvTest, processRequestNoRelay) {
+    testDiscoverRequest(DHCPREQUEST, IOAddress("0.0.0.0"), IOAddress("192.0.2.67"));
+}
 
-    delete srv;
+// Verified that the non-relayed REQUEST is processed correctly when
+// client source address is not specified.
+TEST_F(Dhcpv4SrvTest, processRequestNoClientAddr) {
+    testDiscoverRequest(DHCPREQUEST, IOAddress("0.0.0.0"), IOAddress("0.0.0.0"));
 }
 
 TEST_F(Dhcpv4SrvTest, processRelease) {

+ 1 - 51
src/lib/dhcp/iface_mgr.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -721,56 +721,6 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
     return (result);
 }
 
-bool
-IfaceMgr::send(const Pkt4Ptr& pkt)
-{
-    Iface* iface = getIface(pkt->getIface());
-    if (!iface) {
-        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
-                  << pkt->getIface() << ") specified.");
-    }
-
-    memset(&control_buf_[0], 0, control_buf_len_);
-
-
-    // Set the target address we're sending to.
-    sockaddr_in to;
-    memset(&to, 0, sizeof(to));
-    to.sin_family = AF_INET;
-    to.sin_port = htons(pkt->getRemotePort());
-    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
-
-    struct msghdr m;
-    // Initialize our message header structure.
-    memset(&m, 0, sizeof(m));
-    m.msg_name = &to;
-    m.msg_namelen = sizeof(to);
-
-    // Set the data buffer we're sending. (Using this wacky
-    // "scatter-gather" stuff... we only have a single chunk
-    // of data to send, so we declare a single vector entry.)
-    struct iovec v;
-    memset(&v, 0, sizeof(v));
-    // iov_base field is of void * type. We use it for packet
-    // transmission, so this buffer will not be modified.
-    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
-    v.iov_len = pkt->getBuffer().getLength();
-    m.msg_iov = &v;
-    m.msg_iovlen = 1;
-
-    // call OS-specific routines (like setting interface index)
-    os_send4(m, control_buf_, control_buf_len_, pkt);
-
-    pkt->updateTimestamp();
-
-    int result = sendmsg(getSocket(*pkt), &m, 0);
-    if (result < 0) {
-        isc_throw(SocketWriteError, "pkt4 send failed");
-    }
-
-    return (result);
-}
-
 
 boost::shared_ptr<Pkt4>
 IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {

+ 10 - 0
src/lib/dhcp/iface_mgr.h

@@ -306,6 +306,16 @@ public:
     /// @return the only existing instance of interface manager
     static IfaceMgr& instance();
 
+    /// @brief Can be packet can be send directly to the client without address.
+    ///
+    /// Checks if IfaceMgr can send DHCPv4 packet to the client
+    /// who hasn't got address assigned. If this is not supported
+    /// broadcast address should be used to send response to
+    /// the client.
+    ///
+    /// @return true if direct response is supported.
+    bool isDirectResponseSupported();
+
     /// @brief Returns interface with specified interface index
     ///
     /// @param ifindex index of searched interface

+ 55 - 1
src/lib/dhcp/iface_mgr_bsd.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -34,6 +34,60 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
+bool
+IfaceMgr::isDirectResponseSupported() {
+    return (false);
+}
+
+bool
+IfaceMgr::send(const Pkt4Ptr& pkt)
+{
+    Iface* iface = getIface(pkt->getIface());
+    if (!iface) {
+        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+                  << pkt->getIface() << ") specified.");
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Set the target address we're sending to.
+    sockaddr_in to;
+    memset(&to, 0, sizeof(to));
+    to.sin_family = AF_INET;
+    to.sin_port = htons(pkt->getRemotePort());
+    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+
+    struct msghdr m;
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    // iov_base field is of void * type. We use it for packet
+    // transmission, so this buffer will not be modified.
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+    v.iov_len = pkt->getBuffer().getLength();
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // call OS-specific routines (like setting interface index)
+    os_send4(m, control_buf_, control_buf_len_, pkt);
+
+    pkt->updateTimestamp();
+
+    int result = sendmsg(getSocket(*pkt), &m, 0);
+    if (result < 0) {
+        isc_throw(SocketWriteError, "pkt4 send failed");
+    }
+
+    return (result);
+}
+
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,

+ 55 - 1
src/lib/dhcp/iface_mgr_linux.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -494,6 +494,11 @@ void IfaceMgr::detectIfaces() {
     nl.release_list(addr_info);
 }
 
+bool
+IfaceMgr::isDirectResponseSupported() {
+    return (false);
+}
+
 /// @brief sets flag_*_ fields.
 ///
 /// This implementation is OS-specific as bits have different meaning
@@ -510,6 +515,55 @@ void IfaceMgr::Iface::setFlags(uint32_t flags) {
     flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
+bool
+IfaceMgr::send(const Pkt4Ptr& pkt)
+{
+    Iface* iface = getIface(pkt->getIface());
+    if (!iface) {
+        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+                  << pkt->getIface() << ") specified.");
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Set the target address we're sending to.
+    sockaddr_in to;
+    memset(&to, 0, sizeof(to));
+    to.sin_family = AF_INET;
+    to.sin_port = htons(pkt->getRemotePort());
+    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+
+    struct msghdr m;
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    // iov_base field is of void * type. We use it for packet
+    // transmission, so this buffer will not be modified.
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+    v.iov_len = pkt->getBuffer().getLength();
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // call OS-specific routines (like setting interface index)
+    os_send4(m, control_buf_, control_buf_len_, pkt);
+
+    pkt->updateTimestamp();
+
+    int result = sendmsg(getSocket(*pkt), &m, 0);
+    if (result < 0) {
+        isc_throw(SocketWriteError, "pkt4 send failed");
+    }
+
+    return (result);
+}
+
 void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
                         size_t control_buf_len, const Pkt4Ptr& pkt) {
 

+ 55 - 1
src/lib/dhcp/iface_mgr_sun.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -34,6 +34,60 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
+bool
+IfaceMgr::isDirectResposeSupported() {
+    return (false);
+}
+
+bool
+IfaceMgr::send(const Pkt4Ptr& pkt)
+{
+    Iface* iface = getIface(pkt->getIface());
+    if (!iface) {
+        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+                  << pkt->getIface() << ") specified.");
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Set the target address we're sending to.
+    sockaddr_in to;
+    memset(&to, 0, sizeof(to));
+    to.sin_family = AF_INET;
+    to.sin_port = htons(pkt->getRemotePort());
+    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+
+    struct msghdr m;
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    // iov_base field is of void * type. We use it for packet
+    // transmission, so this buffer will not be modified.
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+    v.iov_len = pkt->getBuffer().getLength();
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // call OS-specific routines (like setting interface index)
+    os_send4(m, control_buf_, control_buf_len_, pkt);
+
+    pkt->updateTimestamp();
+
+    int result = sendmsg(getSocket(*pkt), &m, 0);
+    if (result < 0) {
+        isc_throw(SocketWriteError, "pkt4 send failed");
+    }
+
+    return (result);
+}
+
 void IfaceMgr::os_send4(struct msghdr& /*m*/,
                         boost::scoped_array<char>& /*control_buf*/,
                         size_t /*control_buf_len*/,

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

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above