Commit 7fca8171 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac1957'

Conflicts:
	src/lib/dhcp/iface_mgr.cc
parents 3fac7d55 1793d893
......@@ -19,11 +19,14 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <asio.hpp>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/io_error.h>
#include <util/io/pktinfo_utilities.h>
using namespace std;
......@@ -197,7 +200,7 @@ void IfaceMgr::stubDetectIfaces() {
iface.flag_up_ = true;
iface.flag_running_ = true;
// note that we claim that this is not a loopback. iface_mgr tries to open a
// Note that we claim that this is not a loopback. iface_mgr tries to open a
// socket on all interaces that are up, running and not loopback. As this is
// the only interface we were able to detect, let's pretend this is a normal
// interface.
......@@ -228,8 +231,8 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
int sock;
int count = 0;
for (IfaceCollection::iterator iface=ifaces_.begin();
iface!=ifaces_.end();
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end();
++iface) {
cout << "Trying opening socket on interface " << iface->getFullName() << endl;
......@@ -243,18 +246,17 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
}
AddressCollection addrs = iface->getAddresses();
for (AddressCollection::iterator addr= addrs.begin();
for (AddressCollection::iterator addr = addrs.begin();
addr != addrs.end();
++addr) {
// skip IPv6 addresses
// Skip IPv6 addresses
if (addr->getFamily() != AF_INET) {
continue;
}
sock = openSocket(iface->getName(), *addr, port);
if (sock<0) {
if (sock < 0) {
cout << "Failed to open unicast socket." << endl;
return (false);
}
......@@ -270,8 +272,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
int sock;
int count = 0;
for (IfaceCollection::iterator iface=ifaces_.begin();
iface!=ifaces_.end();
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end();
++iface) {
if (iface->flag_loopback_ ||
......@@ -281,8 +283,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
}
AddressCollection addrs = iface->getAddresses();
for (AddressCollection::iterator addr= addrs.begin();
for (AddressCollection::iterator addr = addrs.begin();
addr != addrs.end();
++addr) {
......@@ -292,7 +293,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
}
sock = openSocket(iface->getName(), *addr, port);
if (sock<0) {
if (sock < 0) {
cout << "Failed to open unicast socket." << endl;
return (false);
}
......@@ -301,7 +302,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
// works well on Mac OS (and possibly other BSDs), but does not work
// on Linux.
if ( !joinMulticast(sock, iface->getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
close(sock);
isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
<< " multicast group.");
......@@ -317,7 +318,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
int sock2 = openSocket(iface->getName(),
IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
port);
if (sock2<0) {
if (sock2 < 0) {
isc_throw(Unexpected, "Failed to open multicast socket on "
<< " interface " << iface->getFullName());
iface->delSocket(sock); // delete previously opened socket
......@@ -397,6 +398,117 @@ int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
}
}
int IfaceMgr::openSocketFromIface(const std::string& ifname,
const uint16_t port,
const uint8_t family) {
// Search for specified interface among detected interfaces.
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end();
++iface) {
if ((iface->getFullName() != ifname) &&
(iface->getName() != ifname)) {
continue;
}
// Interface is now detected. Search for address on interface
// that matches address family (v6 or v4).
AddressCollection addrs = iface->getAddresses();
AddressCollection::iterator addr_it = addrs.begin();
while (addr_it != addrs.end()) {
if (addr_it->getFamily() == family) {
// We have interface and address so let's open socket.
// This may cause isc::Unexpected exception.
return (openSocket(iface->getName(), *addr_it, port));
}
++addr_it;
}
// If we are at the end of address collection it means that we found
// interface but there is no address for family specified.
if (addr_it == addrs.end()) {
// Stringify the family value to append it to exception string.
std::string family_name("AF_INET");
if (family == AF_INET6) {
family_name = "AF_INET6";
}
// We did not find address on the interface.
isc_throw(BadValue, "There is no address for interface: "
<< ifname << ", port: " << port << ", address "
" family: " << family_name);
}
}
// If we got here it means that we had not found the specified interface.
// Otherwise we would have returned from previous exist points.
isc_throw(BadValue, "There is no " << ifname << " interface present.");
}
int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
const uint16_t port) {
// Search through detected interfaces and addresses to match
// local address we got.
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end();
++iface) {
AddressCollection addrs = iface->getAddresses();
for (AddressCollection::iterator addr_it = addrs.begin();
addr_it != addrs.end();
++addr_it) {
// Local address must match one of the addresses
// on detected interfaces. If it does, we have
// address and interface detected so we can open
// socket.
if (*addr_it == addr) {
// Open socket using local interface, address and port.
// This may cause isc::Unexpected exception.
return (openSocket(iface->getName(), *addr_it, port));
}
}
}
// If we got here it means that we did not find specified address
// on any available interface.
isc_throw(BadValue, "There is no such address " << addr.toText());
}
int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
const uint16_t port) {
// Get local address to be used to connect to remote location.
IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
return openSocketFromAddress(local_address, port);
}
isc::asiolink::IOAddress
IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
// Create remote endpoint, we will be connecting to it.
boost::scoped_ptr<const UDPEndpoint>
remote_endpoint(static_cast<const UDPEndpoint*>
(UDPEndpoint::create(IPPROTO_UDP, remote_addr, port)));
if (!remote_endpoint) {
isc_throw(Unexpected, "Unable to create remote endpoint");
}
// Create socket that will be used to connect to remote endpoint.
asio::io_service io_service;
asio::ip::udp::socket sock(io_service);
// Try to connect to remote endpoint and check if attempt is successful.
asio::error_code err_code;
sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
if (err_code) {
isc_throw(Unexpected,"Failed to connect to remote endpoint.");
}
// Once we are connected socket object holds local endpoint.
asio::ip::udp::socket::endpoint_type local_endpoint =
sock.local_endpoint();
asio::ip::address local_address(local_endpoint.address());
// Return address of local endpoint.
return IOAddress(local_address);
}
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
cout << "Creating UDP4 socket on " << iface.getFullName()
......
......@@ -374,7 +374,58 @@ public:
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
int openSocket(const std::string& ifname,
const isc::asiolink::IOAddress& addr, const uint16_t port);
const isc::asiolink::IOAddress& addr,
const uint16_t port);
/// @brief Opens UDP/IP socket and binds it to interface specified.
///
/// This method differs from \ref openSocket in that it does not require
/// the specification of a local address to which socket will be bound.
/// Instead, the method searches through the addresses on the specified
/// interface and selects one that matches the address family.
///
/// @param ifname name of the interface
/// @param port UDP port
/// @param family address family (AF_INET or AF_INET6)
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @throw isc::Unexpected if failed to create and bind socket.
/// @throw isc::BadValue if there is no address on specified interface
/// that belongs to given family.
int openSocketFromIface(const std::string& ifname,
const uint16_t port,
const uint8_t family);
/// @brief Opens UDP/IP socket and binds to address specified
///
/// This methods differs from \ref openSocket in that it does not require
/// the specification of the interface to which the socket will be bound.
///
/// @param addr address to be bound
/// @param port UDP port
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @throw isc::Unexpected if failed to create and bind socket
/// @throw isc::BadValue if specified address is not available on
/// any interface
int openSocketFromAddress(const isc::asiolink::IOAddress& addr,
const uint16_t port);
/// @brief Opens UDP/IP socket to be used to connect to remote address
///
/// This method identifies the local address to be used to connect to the
/// remote address specified as argument. Once the local address is
/// identified, \ref openSocket is called to open a socket and bind it to
/// the interface, address and port.
///
/// @param remote_addr remote address to connect to
/// @param port UDP port
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @throw isc::Unexpected if failed to create and bind socket
int openSocketFromRemoteAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
/// Opens IPv6 sockets on detected interfaces.
///
......@@ -548,6 +599,23 @@ private:
joinMulticast(int sock, const std::string& ifname,
const std::string& mcast);
/// @brief Identifies local network address to be used to
/// connect to remote address.
///
/// This method identifies local network address that can be used
/// to connect to remote address specified.
/// It first creates socket and makes attempt to connect
/// to remote location via this socket. If connection
/// is established successfully, the local address to which
/// socket is bound is returned.
///
/// @param remote_addr remote address to connect to
/// @param port port to be used
/// @return local address to be used to connect to remote address
/// @throw isc::Unexpected if unable to indentify local address
isc::asiolink::IOAddress
getLocalAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
};
}; // namespace isc::dhcp
......
......@@ -19,6 +19,7 @@
#include <unistd.h>
#include <arpa/inet.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <asiolink/io_address.h>
......@@ -31,12 +32,17 @@ using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
// name of loopback interface detection
const size_t buf_size = 32;
char LOOPBACK[buf_size] = "lo";
namespace {
// Name of loopback interface detection
const size_t BUF_SIZE = 32;
char LOOPBACK[BUF_SIZE] = "lo";
// Ports used during testing
const uint16_t PORT1 = 10547; // V6 socket
const uint16_t PORT2 = 10548; // V4 socket
class NakedIfaceMgr: public IfaceMgr {
// "naked" Interface Manager, exposes internal fields
public:
......@@ -69,10 +75,10 @@ TEST_F(IfaceMgrTest, loDetect) {
// is implemented
if (if_nametoindex("lo") > 0) {
cout << "This is Linux, using lo as loopback." << endl;
snprintf(LOOPBACK, buf_size - 1, "lo");
snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
} else if (if_nametoindex("lo0") > 0) {
cout << "This is BSD, using lo0 as loopback." << endl;
snprintf(LOOPBACK, buf_size - 1, "lo0");
snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
} else {
cout << "Failed to detect loopback interface. Neither "
<< "lo nor lo0 worked. I give up." << endl;
......@@ -80,7 +86,7 @@ TEST_F(IfaceMgrTest, loDetect) {
}
}
// uncomment this test to create packet writer. It will
// Uncomment this test to create packet writer. It will
// write incoming DHCPv6 packets as C arrays. That is useful
// for generating test sequences based on actual traffic
//
......@@ -241,6 +247,78 @@ TEST_F(IfaceMgrTest, sockets6) {
delete ifacemgr;
}
TEST_F(IfaceMgrTest, socketsFromIface) {
boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
// Open v6 socket on loopback interface and bind to port
int socket1 = 0;
EXPECT_NO_THROW(
socket1 = ifacemgr->openSocketFromIface(LOOPBACK, PORT1, AF_INET6);
);
// Socket descriptor must be positive integer
EXPECT_GT(socket1, 0);
close(socket1);
// Open v4 socket on loopback interface and bind to different port
int socket2 = 0;
EXPECT_NO_THROW(
socket2 = ifacemgr->openSocketFromIface(LOOPBACK, PORT2, AF_INET);
);
// socket descriptor must be positive integer
EXPECT_GT(socket2, 0);
close(socket2);
}
TEST_F(IfaceMgrTest, socketsFromAddress) {
boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
// Open v6 socket on loopback interface and bind to port
int socket1 = 0;
IOAddress loAddr6("::1");
EXPECT_NO_THROW(
socket1 = ifacemgr->openSocketFromAddress(loAddr6, PORT1);
);
// socket descriptor must be positive integer
EXPECT_GT(socket1, 0);
close(socket1);
// Open v4 socket on loopback interface and bind to different port
int socket2 = 0;
IOAddress loAddr("127.0.0.1");
EXPECT_NO_THROW(
socket2 = ifacemgr->openSocketFromAddress(loAddr, PORT2);
);
// socket descriptor must be positive integer
EXPECT_GT(socket2, 0);
close(socket2);
}
TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
// Open v6 socket to connect to remote address.
// Loopback address is the only one that we know
// so let's treat it as remote address.
int socket1 = 0;
IOAddress loAddr6("::1");
EXPECT_NO_THROW(
socket1 = ifacemgr->openSocketFromRemoteAddress(loAddr6, PORT1);
);
EXPECT_GT(socket1, 0);
close(socket1);
// Open v4 socket to connect to remote address.
int socket2 = 0;
IOAddress loAddr("127.0.0.1");
EXPECT_NO_THROW(
socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
);
EXPECT_GT(socket2, 0);
close(socket2);
}
// TODO: disabled due to other naming on various systems
// (lo in Linux, lo0 in BSD systems)
TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment