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

[991] Abstracted socket handling methods to separate class.

parent 3c941835
......@@ -35,6 +35,8 @@ libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
libb10_dhcp___la_SOURCES += option_space.cc option_space.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_SOURCES += pkt_filter.h
libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
libb10_dhcp___la_SOURCES += std_option_defs.h
libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
......
......@@ -22,6 +22,7 @@
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt_filter_inet.h>
#include <exceptions/exceptions.h>
#include <util/io/pktinfo_utilities.h>
......@@ -125,7 +126,7 @@ IfaceMgr::IfaceMgr()
:control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_]),
session_socket_(INVALID_SOCKET), session_callback_(NULL),
socket_handler_(new SocketHandler())
packet_filter_(new PktFilterInet())
{
try {
......@@ -531,7 +532,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
// TODO: use sockcreator once it becomes available
// make a socket
int sock = socket_handler_->openSocket(AF_INET6, SOCK_DGRAM, 0);
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
......
......@@ -20,6 +20,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/pkt_filter.h>
#include <boost/noncopyable.hpp>
#include <boost/scoped_array.hpp>
......@@ -38,10 +39,10 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief IfaceMgr exception thrown when invalid socket handler supplied.
class InvalidSocketHandler : public Exception {
/// @brief IfaceMgr exception thrown when invalid packet filter object specified.
class InvalidPacketFilter : public Exception {
public:
InvalidSocketHandler(const char* file, size_t line, const char* what) :
InvalidPacketFilter(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
......@@ -69,13 +70,6 @@ public:
isc::Exception(file, line, what) { };
};
class SocketHandler {
public:
virtual int openSocket(int socket_family, int socket_type, int protocol) {
return socket(socket_family, socket_type, protocol);
}
};
/// @brief handles network interfaces, transmission and reception
///
/// IfaceMgr is an interface manager class that detects available network
......@@ -558,11 +552,11 @@ public:
session_callback_ = callback;
}
void setSocketHandler(const boost::shared_ptr<SocketHandler>& handler) {
if (!handler) {
isc_throw(InvalidSocketHandler, "NULL socket handler spcified");
void setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
if (!packet_filter) {
isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
}
socket_handler_ = handler;
packet_filter_ = packet_filter;
}
/// A value of socket descriptor representing "not specified" state.
......@@ -713,7 +707,8 @@ private:
getLocalAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
boost::shared_ptr<SocketHandler> socket_handler_;
/// Low level packet handler used to open socket and send/receive packets.
boost::shared_ptr<PktFilter> packet_filter_;
};
}; // namespace isc::dhcp
......
......@@ -502,55 +502,8 @@ IfaceMgr::isDirectResponseSupported() {
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port,
bool receive_bcast, bool send_bcast) {
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(sockaddr));
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
// If we are to receive broadcast messages we have to bind
// to "ANY" address.
addr4.sin_addr.s_addr = receive_bcast ? INADDR_ANY : htonl(addr);
int sock = socket_handler_->openSocket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
if (receive_bcast) {
// Bind to device so as we receive traffic on a specific interface.
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(),
iface.getName().length() + 1) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to set SO_BINDTODEVICE option"
<< "on socket " << sock);
}
}
if (send_bcast) {
// Enable sending to broadcast address.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to set SO_BINDTODEVICE option"
<< "on socket " << sock);
}
}
if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
<< "/port=" << port);
}
// if there is no support for IP_PKTINFO, we are really out of luck
// it will be difficult to undersand, where this packet came from
#if defined(IP_PKTINFO)
int flag = 1;
if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
close(sock);
isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
}
#endif
int sock = packet_filter_->openSocket(iface.getName(), addr, port,
receive_bcast, send_bcast);
SocketInfo info(sock, addr, port);
iface.addSocket(info);
......
// Copyright (C) 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef PKT_FILTER_H
#define PKT_FILTER_H
#include <asiolink/io_address.h>
namespace isc {
namespace dhcp {
/// @brief Abstract packet handling class
///
/// This class represents low level method to send and receive DHCP packet.
/// Different methods, represented by classes derived from this class, use
/// different socket families and socket types. Also, various packet filtering
/// methods can be implemented by derived classes, e.g. Linux Packet
/// Filtering (LPF) or Berkeley Packet Filtering (BPF).
///
/// Low-level code operating on sockets may require special privileges to execute.
/// For example: opening raw socket or opening socket on low port number requires
/// root privileges. This makes it impossible or very hard to unit test the IfaceMgr.
/// In order to overcome this problem, it is recommended to create mock object derived
/// from this class that mimics the behavior of the real packet handling class making
/// IfaceMgr testable.
class PktFilter {
public:
/// @brief Open socket.
///
/// @param interface name
/// @param addr address on the interface to be used to send packets.
/// @param port port number.
/// @param receive_bcast configure socket to receive broadcast messages
/// @param send_bcast configure socket to send broadcast messages.
///
/// @return created socket's descriptor
virtual int openSocket(const std::string& ifname,
const isc::asiolink::IOAddress& addr,
const uint16_t port,
const bool receive_bcast,
const bool send_bcast) = 0;
/// @brief Receive packet over specified socket.
///
/// @param sockfd descriptor of a socket to be used for packet reception
/// @param timeout_sec integral part of a timeout.
/// @param timeout_usec fractional part of a timeout (in microseconds).
///
/// @return Received packet
Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec,
uint32_t timeout_usec = 0);
// bool send(const Pkt4Ptr& pkt) = 0;
};
} // namespace isc::dhcp
} // namespace isc
#endif // PKT_FILTER_H
// Copyright (C) 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt_filter_inet.h>
namespace isc {
namespace dhcp {
int
PktFilterInet::openSocket(const std::string& ifname, const isc::asiolink::IOAddress& addr,
const uint16_t port, const bool receive_bcast,
const bool send_bcast) {
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(sockaddr));
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
// If we are to receive broadcast messages we have to bind
// to "ANY" address.
addr4.sin_addr.s_addr = receive_bcast ? INADDR_ANY : htonl(addr);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
if (receive_bcast) {
// Bind to device so as we receive traffic on a specific interface.
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname.c_str(),
ifname.length() + 1) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to set SO_BINDTODEVICE option"
<< "on socket " << sock);
}
}
if (send_bcast) {
// Enable sending to broadcast address.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to set SO_BINDTODEVICE option"
<< "on socket " << sock);
}
}
if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
<< "/port=" << port);
}
// if there is no support for IP_PKTINFO, we are really out of luck
// it will be difficult to undersand, where this packet came from
#if defined(IP_PKTINFO)
int flag = 1;
if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
close(sock);
isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
}
#endif
return sock;
}
} // end of isc::dhcp namespace
} // end of isc namespace
// Copyright (C) 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef PKT_FILTER_INET_H
#define PKT_FILTER_INET_H
#include <dhcp/pkt_filter.h>
namespace isc {
namespace dhcp {
/// @brief Packet handling class using AF_INET socket family
///
/// This class provides methods to send and recive packet via socket using
/// AF_INET family and SOCK_DGRAM type.
class PktFilterInet : public PktFilter {
public:
/// @brief Open socket.
///
/// @param interface name
/// @param addr address on the interface to be used to send packets.
/// @param port port number.
/// @param receive_bcast configure socket to receive broadcast messages
/// @param send_bcast configure socket to send broadcast messages.
///
/// @return created socket's descriptor
virtual int openSocket(const std::string& ifname,
const isc::asiolink::IOAddress& addr,
const uint16_t port,
const bool receive_bcast,
const bool send_bcast);
/// @brief Receive packet over specified socket.
///
/// @param sockfd descriptor of a socket to be used for packet reception
/// @param timeout_sec integral part of a timeout.
/// @param timeout_usec fractional part of a timeout (in microseconds).
///
/// @return Received packet
Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec,
uint32_t timeout_usec = 0);
// bool send(const Pkt4Ptr& pkt) = 0;
};
} // namespace isc::dhcp
} // namespace isc
#endif // PKT_FILTER_INET_H
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