Commit 43fa9db5 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[991] Moved send/receive implementation to class derived from PktFilter.

parent d919490c
......@@ -581,7 +581,19 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
}
}
Iface::SocketInfo info(sock, addr, port);
SocketInfo info(sock, addr, port);
iface.addSocket(info);
return (sock);
}
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port,
bool receive_bcast, bool send_bcast) {
int sock = packet_filter_->openSocket(iface, addr, port,
receive_bcast, send_bcast);
SocketInfo info(sock, addr, port);
iface.addSocket(info);
return (sock);
......@@ -688,6 +700,18 @@ 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.");
}
return (packet_filter_->send(getSocket(*pkt), pkt));
}
boost::shared_ptr<Pkt4>
IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
......@@ -696,7 +720,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
isc_throw(BadValue, "fractional timeout must be shorter than"
" one million microseconds");
}
const Iface::SocketInfo* candidate = 0;
const SocketInfo* candidate = 0;
IfaceCollection::const_iterator iface;
fd_set sockets;
int maxfd = 0;
......@@ -783,64 +807,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
}
// Now we have a socket, let's get some data from it!
struct sockaddr_in from_addr;
uint8_t buf[RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
memset(&from_addr, 0, sizeof(from_addr));
// Initialize our message header structure.
struct msghdr m;
memset(&m, 0, sizeof(m));
// Point so we can get the from address.
m.msg_name = &from_addr;
m.msg_namelen = sizeof(from_addr);
struct iovec v;
v.iov_base = static_cast<void*>(buf);
v.iov_len = RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
// Getting the interface is a bit more involved.
//
// We set up some space for a "control message". We have
// previously asked the kernel to give us packet
// information (when we initialized the interface), so we
// should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
result = recvmsg(candidate->sockfd_, &m, 0);
if (result < 0) {
isc_throw(SocketReadError, "failed to receive UDP4 data");
}
// We have all data let's create Pkt4 object.
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
pkt->updateTimestamp();
unsigned int ifindex = iface->getIndex();
IOAddress from(htonl(from_addr.sin_addr.s_addr));
uint16_t from_port = htons(from_addr.sin_port);
// Set receiving interface based on information, which socket was used to
// receive data. OS-specific info (see os_receive4()) may be more reliable,
// so this value may be overwritten.
pkt->setIndex(ifindex);
pkt->setIface(iface->getName());
pkt->setRemoteAddr(from);
pkt->setRemotePort(from_port);
pkt->setLocalPort(candidate->port_);
if (!os_receive4(m, pkt)) {
isc_throw(SocketReadError, "unable to find pktinfo");
}
return (pkt);
return (packet_filter_->receive(*iface, *candidate));
}
Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) {
......@@ -850,7 +817,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
" one million microseconds");
}
const Iface::SocketInfo* candidate = 0;
const SocketInfo* candidate = 0;
fd_set sockets;
int maxfd = 0;
stringstream names;
......
......@@ -70,6 +70,24 @@ public:
isc::Exception(file, line, what) { };
};
/// Holds information about socket.
struct SocketInfo {
uint16_t sockfd_; /// socket descriptor
isc::asiolink::IOAddress addr_; /// bound address
uint16_t port_; /// socket port
uint16_t family_; /// IPv4 or IPv6
/// @brief SocketInfo constructor.
///
/// @param sockfd socket descriptor
/// @param addr an address the socket is bound to
/// @param port a port the socket is bound to
SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr,
uint16_t port)
:sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { }
};
/// @brief represents a single network interface
///
/// Iface structure represents network interface with all useful
......@@ -84,23 +102,6 @@ public:
/// type that defines list of addresses
typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
/// Holds information about socket.
struct SocketInfo {
uint16_t sockfd_; /// socket descriptor
isc::asiolink::IOAddress addr_; /// bound address
uint16_t port_; /// socket port
uint16_t family_; /// IPv4 or IPv6
/// @brief SocketInfo constructor.
///
/// @param sockfd socket descriptor
/// @param addr an address the socket is bound to
/// @param port a port the socket is bound to
SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr,
uint16_t port)
:sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { }
};
/// type that holds a list of socket informations
/// @todo: Add SocketCollectionConstIter type
typedef std::list<SocketInfo> SocketCollection;
......
......@@ -499,18 +499,6 @@ IfaceMgr::isDirectResponseSupported() {
return (false);
}
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port,
bool receive_bcast, bool send_bcast) {
int sock = packet_filter_->openSocket(iface, addr, port,
receive_bcast, send_bcast);
Iface::SocketInfo info(sock, addr, port);
iface.addSocket(info);
return (sock);
}
/// @brief sets flag_*_ fields.
///
/// This implementation is OS-specific as bits have different meaning
......@@ -527,105 +515,15 @@ void 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&, boost::scoped_array<char>&,
size_t, const Pkt4Ptr&) {
return;
void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
size_t control_buf_len, const Pkt4Ptr& pkt) {
// Setting the interface is a bit more involved.
//
// We have to create a "control message", and set that to
// define the IPv4 packet information. We could set the
// source address if we wanted, but we can safely let the
// kernel decide what that should be.
m.msg_control = &control_buf[0];
m.msg_controllen = control_buf_len;
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(struct in_pktinfo));
pktinfo->ipi_ifindex = pkt->getIndex();
m.msg_controllen = cmsg->cmsg_len;
}
bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
struct cmsghdr* cmsg;
struct in_pktinfo* pktinfo;
struct in_addr to_addr;
memset(&to_addr, 0, sizeof(to_addr));
cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IP) &&
(cmsg->cmsg_type == IP_PKTINFO)) {
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
pkt->setIndex(pktinfo->ipi_ifindex);
pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
return (true);
// This field is useful, when we are bound to unicast
// address e.g. 192.0.2.1 and the packet was sent to
// broadcast. This will return broadcast address, not
// the address we are bound to.
// XXX: Perhaps we should uncomment this:
// to_addr = pktinfo->ipi_spec_dst;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
}
return (false);
bool IfaceMgr::os_receive4(struct msghdr&, Pkt4Ptr&) {
return (true);
}
} // end of isc::dhcp namespace
......
......@@ -20,6 +20,8 @@
namespace isc {
namespace dhcp {
struct SocketInfo;
/// Forward declaration to the class representing interface
class Iface;
......@@ -57,15 +59,20 @@ public:
/// @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).
/// @param iface interface
/// @param socket_info structure holding socket information
///
/// @return Received packet
Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec,
uint32_t timeout_usec = 0);
virtual Pkt4Ptr receive(const Iface& iface,
const SocketInfo& socket_info) = 0;
// bool send(const Pkt4Ptr& pkt) = 0;
/// @brief Send packet over specified socket.
///
/// @param sockfd socket descriptor
/// @param pkt packet to be sent
///
/// @return result of sending the packet. It is 0 if successful.
virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt) = 0;
};
} // namespace isc::dhcp
......
......@@ -17,9 +17,17 @@
#include <dhcp/pkt4.h>
#include <dhcp/pkt_filter_inet.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
PktFilterInet::PktFilterInet()
: control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_])
{
}
int
PktFilterInet::openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
......@@ -77,10 +85,152 @@ PktFilterInet::openSocket(const Iface& iface,
}
#endif
return sock;
return (sock);
}
Pkt4Ptr
PktFilterInet::receive(const Iface& iface, const SocketInfo& socket_info) {
struct sockaddr_in from_addr;
uint8_t buf[IfaceMgr::RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
memset(&from_addr, 0, sizeof(from_addr));
// Initialize our message header structure.
struct msghdr m;
memset(&m, 0, sizeof(m));
// Point so we can get the from address.
m.msg_name = &from_addr;
m.msg_namelen = sizeof(from_addr);
struct iovec v;
v.iov_base = static_cast<void*>(buf);
v.iov_len = IfaceMgr::RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
// Getting the interface is a bit more involved.
//
// We set up some space for a "control message". We have
// previously asked the kernel to give us packet
// information (when we initialized the interface), so we
// should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
int result = recvmsg(socket_info.sockfd_, &m, 0);
if (result < 0) {
isc_throw(SocketReadError, "failed to receive UDP4 data");
}
// We have all data let's create Pkt4 object.
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
pkt->updateTimestamp();
unsigned int ifindex = iface.getIndex();
IOAddress from(htonl(from_addr.sin_addr.s_addr));
uint16_t from_port = htons(from_addr.sin_port);
// Set receiving interface based on information, which socket was used to
// receive data. OS-specific info (see os_receive4()) may be more reliable,
// so this value may be overwritten.
pkt->setIndex(ifindex);
pkt->setIface(iface.getName());
pkt->setRemoteAddr(from);
pkt->setRemotePort(from_port);
pkt->setLocalPort(socket_info.port_);
struct cmsghdr* cmsg;
struct in_pktinfo* pktinfo;
struct in_addr to_addr;
memset(&to_addr, 0, sizeof(to_addr));
cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IP) &&
(cmsg->cmsg_type == IP_PKTINFO)) {
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
pkt->setIndex(pktinfo->ipi_ifindex);
pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
break;
// This field is useful, when we are bound to unicast
// address e.g. 192.0.2.1 and the packet was sent to
// broadcast. This will return broadcast address, not
// the address we are bound to.
// XXX: Perhaps we should uncomment this:
// to_addr = pktinfo->ipi_spec_dst;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
}
return (pkt);
}
int
PktFilterInet::send(uint16_t sockfd, const Pkt4Ptr& pkt) {
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;
// Setting the interface is a bit more involved.
//
// We have to create a "control message", and set that to
// define the IPv4 packet information. We could set the
// source address if we wanted, but we can safely let the
// kernel decide what that should be.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(struct in_pktinfo));
pktinfo->ipi_ifindex = pkt->getIndex();
m.msg_controllen = cmsg->cmsg_len;
pkt->updateTimestamp();
int result = sendmsg(sockfd, &m, 0);
if (result < 0) {
isc_throw(SocketWriteError, "pkt4 send failed");
}
return (result);
}
} // end of isc::dhcp namespace
} // end of isc namespace
......@@ -27,6 +27,11 @@ namespace dhcp {
class PktFilterInet : public PktFilter {
public:
/// @brief Constructor
///
/// Allocates control buffer.
PktFilterInet();
/// @brief Open socket.
///
/// @param iface interface descriptor
......@@ -44,15 +49,25 @@ public:
/// @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).
/// @param iface interface
/// @param socket_info structure holding socket information
///
/// @return Received packet
Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec,
uint32_t timeout_usec = 0);
virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& socket_info);
/// @brief Send packet over specified socket.
///
/// @param sockfd socket descriptor
/// @param pkt packet to be sent
///
/// @return result of sending a packet. It is 0 if successful.
virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt);
// bool send(const Pkt4Ptr& pkt) = 0;
private:
/// Length of the control_buf_ array.
size_t control_buf_len_;
/// Control buffer, used in transmission and reception.
boost::scoped_array<char> control_buf_;
};
} // namespace isc::dhcp
......
......@@ -863,14 +863,14 @@ TEST_F(IfaceMgrTest, iface_methods) {
TEST_F(IfaceMgrTest, socketInfo) {
// check that socketinfo for IPv4 socket is functional
Iface::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
EXPECT_EQ(7, sock1.sockfd_);
EXPECT_EQ("192.0.2.56", sock1.addr_.toText());
EXPECT_EQ(AF_INET, sock1.family_);
EXPECT_EQ(DHCP4_SERVER_PORT + 7, sock1.port_);
// check that socketinfo for IPv6 socket is functional
Iface::SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9);
SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9);
EXPECT_EQ(9, sock2.sockfd_);
EXPECT_EQ("2001:db8:1::56", sock2.addr_.toText());
EXPECT_EQ(AF_INET6, sock2.family_);
......
Supports Markdown
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