Commit 84076d91 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent b59d780f
......@@ -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_);
......
......@@ -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) {
......
// 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 */) {
......
......@@ -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
......
// 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*/,
......
// 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,