return (packet_filter_->isDirectResponseSupported());
}
+bool
+IfaceMgr::isSocketReceivedTimeSupported() const {
+ return (packet_filter_->isSocketReceivedTimeSupported());
+}
+
void
IfaceMgr::addExternalSocket(int socketfd, SocketCallback callback) {
if (socketfd < 0) {
/// @return true if direct response is supported.
bool isDirectResponseSupported() const;
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then received packets will include a SOCKET_RECEIVED
+ /// event in their event stack.
+ ///
+ /// @return True if it is supported.
+ virtual bool isSocketReceivedTimeSupported() const;
+
/// @brief Returns interface specified interface index
///
/// @param ifindex index of searched interface
return (sock);
}
-
} // end of isc::dhcp namespace
} // end of isc namespace
/// @return true of the direct response is supported.
virtual bool isDirectResponseSupported() const = 0;
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack. Other than direct
+ /// clients using BPF for which this is always true, this function
+ /// is true provided SO_TIMESTAMP is defined.
+ ///
+ /// @return True if it is supported.
+ virtual bool isSocketReceivedTimeSupported() const = 0;
+
/// @brief Open primary and fallback socket.
///
/// A method implementation in the derived class may open one or two
return (true);
}
-
} // end of isc::dhcp namespace
} // end of isc namespace
return (true);
}
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// @return true always.
+ virtual bool isSocketReceivedTimeSupported() const {
+ return (true);
+ }
+
/// @brief Open primary and fallback socket.
///
/// This method opens the BPF device and applies the following
return (false);
}
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack.
+ ///
+ /// @return True if SO_TIMESTAMP is defined.
+ virtual bool isSocketReceivedTimeSupported() const {
+#ifdef SO_TIMESTAMP
+ return (true);
+#else
+ return (false);
+#endif
+ }
+
/// @brief Open primary and fallback socket.
///
/// @param iface Interface descriptor.
/// class to be used by @c IfaceMgr to access IPv6 sockets.
class PktFilterInet6 : public PktFilter6 {
public:
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack.
+ ///
+ /// @return True if SO_TIMESTAMP is defined.
+ virtual bool isSocketReceivedTimeSupported() const {
+#ifdef SO_TIMESTAMP
+ return (true);
+#else
+ return (false);
+#endif
+ }
/// @brief Opens a socket.
///
#include <linux/if_ether.h>
#include <linux/if_packet.h>
-#define WITH_CMSG
-
namespace {
using namespace isc::dhcp;
}
-#ifndef WITH_CMSG
Pkt4Ptr
PktFilterLPF::receive(Iface& iface, const SocketInfo& socket_info) {
uint8_t raw_buf[IfaceMgr::RCVBUFSIZE];
datalen = recv(socket_info.fallbackfd_, raw_buf, sizeof(raw_buf), 0);
} while (datalen > 0);
+#ifndef SO_TIMESTAMP
// Now that we finished getting data from the fallback socket, we
// have to get the data from the raw socket too.
int data_len = read(socket_info.sockfd_, raw_buf, sizeof(raw_buf));
}
InputBuffer buf(raw_buf, data_len);
-
- // @todo: This is awkward way to solve the chicken and egg problem
- // whereby we don't know the offset where DHCP data start in the
- // received buffer when we create the packet object. In general case,
- // the IP header has variable length. The information about its length
- // is stored in one of its fields. Therefore, we have to decode the
- // packet to get the offset of the DHCP data. The dummy object is
- // created so as we can pass it to the functions which decode IP stack
- // and find actual offset of the DHCP data.
- // Once we find the offset we can create another Pkt4 object from
- // the reminder of the input buffer and set the IP addresses and
- // ports from the dummy packet. We should consider doing it
- // in some more elegant way.
- Pkt4Ptr dummy_pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 0));
-
- // Decode ethernet, ip and udp headers.
- decodeEthernetHeader(buf, dummy_pkt);
- decodeIpUdpHeader(buf, dummy_pkt);
-
- // Read the DHCP data.
- std::vector<uint8_t> dhcp_buf;
- buf.readVector(dhcp_buf, buf.getLength() - buf.getPosition());
-
- // Decode DHCP data into the Pkt4 object.
- Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(&dhcp_buf[0], dhcp_buf.size()));
-
- // Set the appropriate packet members using data collected from
- // the decoded headers.
- pkt->setIndex(iface.getIndex());
- pkt->setIface(iface.getName());
- pkt->setLocalAddr(dummy_pkt->getLocalAddr());
- pkt->setRemoteAddr(dummy_pkt->getRemoteAddr());
- pkt->setLocalPort(dummy_pkt->getLocalPort());
- pkt->setRemotePort(dummy_pkt->getRemotePort());
- pkt->setLocalHWAddr(dummy_pkt->getLocalHWAddr());
- pkt->setRemoteHWAddr(dummy_pkt->getRemoteHWAddr());
-
- // Set time packet was read from the buffer.
- pkt->addPktEvent(PktEvent::BUFFER_READ);
-
- return (pkt);
-}
#else
-Pkt4Ptr
-PktFilterLPF::receive(Iface& iface, const SocketInfo& socket_info) {
- uint8_t raw_buf[IfaceMgr::RCVBUFSIZE];
- // First let's get some data from the fallback socket. The data will be
- // discarded but we don't want the socket buffer to bloat. We get the
- // packets from the socket in loop but most of the time the loop will
- // end after receiving one packet. The call to recv returns immediately
- // when there is no data left on the socket because the socket is
- // non-blocking.
- // @todo In the normal conditions, both the primary socket and the fallback
- // socket are in sync as they are set to receive packets on the same
- // address and port. The reception of packets on the fallback socket
- // shouldn't cause significant lags in packet reception. If we find in the
- // future that it does, the sort of threshold could be set for the maximum
- // bytes received on the fallback socket in a single round. Further
- // optimizations would include an asynchronous read from the fallback socket
- // when the DHCP server is idle.
- int datalen;
- do {
- datalen = recv(socket_info.fallbackfd_, raw_buf, sizeof(raw_buf), 0);
- } while (datalen > 0);
-
const size_t CONTROL_BUF_LEN = 512;
-
uint8_t msg_buf[IfaceMgr::RCVBUFSIZE];
uint8_t control_buf[CONTROL_BUF_LEN];
}
InputBuffer buf(msg_buf, result);
+#endif
// @todo: This is awkward way to solve the chicken and egg problem
// whereby we don't know the offset where DHCP data start in the
pkt->setLocalHWAddr(dummy_pkt->getLocalHWAddr());
pkt->setRemoteHWAddr(dummy_pkt->getRemoteHWAddr());
+#ifdef SO_TIMESTAMP
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == SOL_SOCKET) &&
cmsg = CMSG_NXTHDR(&m, cmsg);
}
+#endif
// Set time packet was read from the buffer.
pkt->addPktEvent(PktEvent::BUFFER_READ);
return (pkt);
}
-#endif
int
PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
}
-
} // end of isc::dhcp namespace
} // end of isc namespace
return (true);
}
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack.
+ ///
+ /// @return True if SO_TIMESTAMP is defined.
+ virtual bool isSocketReceivedTimeSupported() const {
+#ifdef SO_TIMESTAMP
+ return (true);
+#else
+ return (false);
+#endif
+ }
+
/// @brief Open primary and fallback socket.
///
/// @param iface Interface descriptor.
return (false);
}
+ virtual bool isSocketReceivedTimeSupported() const {
+ return (false);
+ }
+
/// @brief Pretend to open a socket.
///
/// This function doesn't open a real socket. It always returns the
EXPECT_EQ(test_message_->getTransid(), rcvd_msg->getTransid());
}
+void
+PktFilter6Test::testReceivedPktEvents(const PktPtr& msg,
+ bool so_time_supported) const {
+ std::list<std::string> expected_events;
+ if (so_time_supported) {
+ expected_events.push_back(PktEvent::SOCKET_RECEIVED);
+ }
+
+ expected_events.push_back(PktEvent::BUFFER_READ);
+ testPktEvents(msg, start_time_, expected_events);
+}
+
void
PktFilter6Test::testPktEvents(const PktPtr& msg, ptime start_time,
std::list<std::string> expected_events) const {
for (auto const& event : events) {
ASSERT_EQ(event.label_, *expected_event);
EXPECT_GE(event.timestamp_, prev_time);
+ prev_time = event.timestamp_;
++expected_event;
}
}
/// @param rcvd_msg An instance of the message to be tested.
void testRcvdMessage(const Pkt6Ptr& rcvd_msg) const;
+ /// @brief Checks that a received message has the appropriate events
+ /// in it's event stack.
+ ///
+ /// @param rcvd_msg An instance of the message to be tested.
+ /// @param so_time_supported If true the event stack should have with
+ /// a SOCKET_RECEIVED event followed by a BUFFER_READ event, if false
+ /// it should have only the latter.
+ void testReceivedPktEvents(const PktPtr& msg, bool so_time_supported) const;
+
/// @brief Checks the contents of a packet's event stack agains a list
/// of expected events.
///
testRcvdMessage(rcvd_pkt);
testRcvdMessageAddressPort(rcvd_pkt);
- // Verify that the packet event stack has SOCKET_RECEIVED and BUFFER_READ events.
- testPktEvents(rcvd_pkt, start_time_,
- std::list<std::string>{PktEvent::SOCKET_RECEIVED,
- PktEvent::BUFFER_READ});
+ // Verify that the packet event stack is as expected.
+ testReceivedPktEvents (rcvd_pkt, pkt_filter.isSocketReceivedTimeSupported());
}
// This test verifies that if the packet is received over the raw
// Check if the received message is correct.
testRcvdMessage(rcvd_pkt);
- // Verify that the packet event stack has SOCKET_RECEIVED and BUFFER_READ events.
- testPktEvents(rcvd_pkt, start_time_,
- std::list<std::string>{PktEvent::SOCKET_RECEIVED,
- PktEvent::BUFFER_READ});
+ // Verify that the packet event stack is as expected.
+ testReceivedPktEvents (rcvd_pkt, pkt_filter.isSocketReceivedTimeSupported());
}
} // anonymous namespace
testRcvdMessage(rcvd_pkt);
testRcvdMessageAddressPort(rcvd_pkt);
- // Verify that the packet event stack has SOCKET_RECEIVED and BUFFER_READ events.
- testPktEvents(rcvd_pkt, start_time_,
- std::list<std::string>{PktEvent::SOCKET_RECEIVED,
- PktEvent::BUFFER_READ});
+ // Verify that the packet event stack is as expected.
+ testReceivedPktEvents (rcvd_pkt, pkt_filter.isSocketReceivedTimeSupported());
}
} // anonymous namespace
testRcvdMessage(rcvd_pkt);
testRcvdMessageAddressPort(rcvd_pkt);
- // Verify that the packet event stack has SOCKET_RECEIVED and BUFFER_READ events.
- testPktEvents(rcvd_pkt, start_time_,
- std::list<std::string>{PktEvent::SOCKET_RECEIVED,
- PktEvent::BUFFER_READ});
+ // Verify that the packet event stack is as expected.
+ testReceivedPktEvents (rcvd_pkt, pkt_filter.isSocketReceivedTimeSupported());
}
// This test verifies that if the packet is received over the raw
EXPECT_EQ(test_message_->getLocalPort(), rcvd_msg->getRemotePort());
}
+void
+PktFilterTest::testReceivedPktEvents(const PktPtr& msg,
+ bool so_time_supported) const {
+ std::list<std::string> expected_events;
+ if (so_time_supported) {
+ expected_events.push_back(PktEvent::SOCKET_RECEIVED);
+ }
+
+ expected_events.push_back(PktEvent::BUFFER_READ);
+ testPktEvents(msg, start_time_, expected_events);
+}
+
void
PktFilterTest::testPktEvents(const PktPtr& msg, ptime start_time,
std::list<std::string> expected_events) const {
for (auto const& event : events) {
ASSERT_EQ(event.label_, *expected_event);
EXPECT_GE(event.timestamp_, prev_time);
+ prev_time = event.timestamp_;
++expected_event;
}
}
return (true);
}
+bool
+PktFilterStub::isSocketReceivedTimeSupported() const {
+ return (true);
+}
+
SocketInfo
PktFilterStub::openSocket(Iface&,
const isc::asiolink::IOAddress& addr,
/// @param rcvd_msg An instance of the message to be tested.
void testRcvdMessageAddressPort(const Pkt4Ptr& rcvd_msg) const;
+ /// @brief Checks that a received message has the appropriate events
+ /// in it's event stack.
+ ///
+ /// @param rcvd_msg An instance of the message to be tested.
+ /// @param so_time_supported If true the event stack should have with
+ /// a SOCKET_RECEIVED event followed by a BUFFER_READ event, if false
+ /// it should have only the latter.
+ void testReceivedPktEvents(const PktPtr& msg, bool so_time_supported) const;
+
/// @brief Checks the contents of a packet's event stack agains a list
/// of expected events.
///
/// @return always true.
virtual bool isDirectResponseSupported() const;
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack.
+ ///
+ /// @return always true.
+ virtual bool isSocketReceivedTimeSupported() const;
+
/// @brief Simulate opening of the socket.
///
/// This function simulates opening a primary socket. In reality, it doesn't
return (direct_response_supported_);
}
+bool
+PktFilterTestStub::isSocketReceivedTimeSupported() const {
+#ifdef SO_TIMESTAMP
+ return(true);
+#else
+ return(false);
+#endif
+}
+
SocketInfo
PktFilterTestStub::openSocket(Iface&,
const isc::asiolink::IOAddress& addr,
/// @return always true.
virtual bool isDirectResponseSupported() const;
+ /// @brief Check if the socket received time is supported.
+ ///
+ /// If true, then packets received through this filter will include
+ /// a SOCKET_RECEIVED event in its event stack. Other than direct
+ /// clients using BPF for which this is always true, this function
+ /// is true provided SO_TIMESTAMP is defined.
+ ///
+ /// @return True if it is supported.
+ virtual bool isSocketReceivedTimeSupported() const;
+
/// @brief Simulate opening of the socket.
///
/// This function simulates opening a primary socket. Rather than open