From: Marcin Siodelski Date: Thu, 3 Jul 2014 18:19:00 +0000 (+0200) Subject: [2893] Created BPF packet filtering class. X-Git-Tag: trac3482_base~81^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0b7cf4cef4c6b8b4601ef508697492402554f27c;p=thirdparty%2Fkea.git [2893] Created BPF packet filtering class. --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index d18787d06e..6d670bad34 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -50,10 +50,16 @@ libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h libkea_dhcp___la_SOURCES += pkt_filter_inet6.cc pkt_filter_inet6.h +# Utilize Linux Packet Filtering on Linux. if OS_LINUX libkea_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h endif +# Utilize Berkeley Packet Filtering on BSD. +if OS_BSD +libkea_dhcp___la_SOURCES += pkt_filter_bpf.cc pkt_filter_bpf.h +endif + libkea_dhcp___la_SOURCES += std_option_defs.h libkea_dhcp___la_SOURCES += docsis3_option_defs.h diff --git a/src/lib/dhcp/pkt_filter_bpf.cc b/src/lib/dhcp/pkt_filter_bpf.cc new file mode 100644 index 0000000000..6a2cc15eed --- /dev/null +++ b/src/lib/dhcp/pkt_filter_bpf.cc @@ -0,0 +1,331 @@ +// Copyright (C) 2014 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace isc::dhcp; + +/// @brief Maximum number of attempts to open BPF device. +const unsigned int MAX_BPF_OPEN_ATTEMPTS = 100; + +/// The following structure defines a Berkely Packet Filter program to perform +/// packet filtering. The program operates on Ethernet packets. To help with +/// interpretation of the program, for the types of Ethernet packets we are +/// interested in, the header layout is: +/// +/// 6 bytes Destination Ethernet Address +/// 6 bytes Source Ethernet Address +/// 2 bytes Ethernet packet type +/// +/// 20 bytes Fixed part of IP header +/// variable Variable part of IP header +/// +/// 2 bytes UDP Source port +/// 2 bytes UDP destination port +/// 4 bytes Rest of UDP header +/// +/// @todo We may want to extend the filter to receive packets sent +/// to the particular IP address assigned to the interface or +/// broadcast address. +struct bpf_insn dhcp_sock_filter [] = { + // Make sure this is an IP packet: check the half-word (two bytes) + // at offset 12 in the packet (the Ethernet packet type). If it + // is, advance to the next instruction. If not, advance 8 + // instructions (which takes execution to the last instruction in + // the sequence: "drop it"). + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_PACKET_TYPE_OFFSET), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), + + // Make sure it's a UDP packet. The IP protocol is at offset + // 9 in the IP header so, adding the Ethernet packet header size + // of 14 bytes gives an absolute byte offset in the packet of 23. + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHERNET_HEADER_LEN + IP_PROTO_TYPE_OFFSET), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), + + // Make sure this isn't a fragment by checking that the fragment + // offset field in the IP header is zero. This field is the + // least-significant 13 bits in the bytes at offsets 6 and 7 in + // the IP header, so the half-word at offset 20 (6 + size of + // Ethernet header) is loaded and an appropriate mask applied. + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ETHERNET_HEADER_LEN + IP_FLAGS_OFFSET), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), + + // Get the IP header length. This is achieved by the following + // (special) instruction that, given the offset of the start + // of the IP header (offset 14) loads the IP header length. + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, ETHERNET_HEADER_LEN), + + // Make sure it's to the right port. The following instruction + // adds the previously extracted IP header length to the given + // offset to locate the correct byte. The given offset of 16 + // comprises the length of the Ethernet header (14) plus the offset + // of the UDP destination port (2) within the UDP header. + BPF_STMT(BPF_LD + BPF_H + BPF_IND, ETHERNET_HEADER_LEN + UDP_DEST_PORT), + // The following instruction tests against the default DHCP server port, + // but the action port is actually set in PktFilterBPF::openSocket(). + // N.B. The code in that method assumes that this instruction is at + // offset 8 in the program. If this is changed, openSocket() must be + // updated. + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP4_SERVER_PORT, 0, 1), + + // If we passed all the tests, ask for the whole packet. + BPF_STMT(BPF_RET + BPF_K, (u_int)-1), + + // Otherwise, drop it. + BPF_STMT(BPF_RET + BPF_K, 0), +}; + +} + +using namespace isc::util; + +namespace isc { +namespace dhcp { + +SocketInfo +PktFilterBPF::openSocket(const Iface& iface, + const isc::asiolink::IOAddress& addr, + const uint16_t port, const bool, + const bool) { + + // Open fallback socket first. If it fails, it will give us an indication + // that there is another service (perhaps DHCP server) running. + // The function will throw an exception and effectivelly cease opening + // the BPF device below. + int fallback = openFallbackSocket(addr, port); + + // Fallback has opened so, let's open the BPF device that we will be + // using for receiving and sending packets. The BPF device is opened + // by opening a file /dev/bpf%d where %d is a number. There may be + // devices already open so we will try them one by one and open the + // one that is not busy. + int sock = -1; + for (unsigned int bpf_dev = 0; + bpf_dev < MAX_BPF_OPEN_ATTEMPTS && (sock < 0); + ++bpf_dev) { + std::ostringstream s; + s << "/dev/bpf" << bpf_dev; + sock = open(s.str().c_str(), O_RDWR, 0); + if (sock < 0) { + // If device busy, try another one. + if (errno == EBUSY) { + continue; + } + // All other errors are fatal, so close the fallback socket + // and throw. + close(fallback); + isc_throw(SocketConfigError, "Failed to open BPF device " << s); + } + } + + // The BPF device is now open. Now it needs to be configured. + + // Associate the device with the interface name. + struct ifreq iface_data; + memset(&iface_data, 0, sizeof(iface_data)); + std::strncpy(iface_data.ifr_name, iface.getName().c_str(), + std::min(static_cast(IFNAMSIZ), + static_cast(iface.getName().length()))); + if (ioctl(sock, BIOCSETIF, &iface_data) < 0) { + close(fallback); + close(sock); + isc_throw(SocketConfigError, "Failed to associate BPF device " + " with interface " << iface.getName()); + } + + // Get the BPF version supported by the kernel. Every application + // must check this version against the current version in use. + struct bpf_version ver; + if (ioctl(sock, BIOCVERSION, &ver) < 0) { + close(fallback); + close(sock); + isc_throw(SocketConfigError, "Failed to obtain the BPF version" + " number from the kernel"); + } + // Major BPF version must match and the minor version that the kernel + // runs must be at least the current version in use. + if ((ver.bv_major != BPF_MAJOR_VERSION) || + (ver.bv_minor < BPF_MINOR_VERSION)) { + close(fallback); + close(sock); + isc_throw(SocketConfigError, "Invalid BPF version"); + } + + // Get the size of the read buffer for this device. We will need to + // allocate the buffer of this size for packet reads. + unsigned int buf_len = 0; + if (ioctl(sock, BIOCGBLEN, &buf_len) < 0) { + close(fallback); + close(sock); + isc_throw(SocketConfigError, "Unable to obtain the required" + " buffer legth for reads from BPF device"); + } + + // Configure the BPF program to receive packets on the specified port. + dhcp_sock_filter[8].k = port; + + // Set the filter program so as we only get packets we are interested in. + struct bpf_program prog; + prog.bf_insns = dhcp_sock_filter; + prog.bf_len = sizeof(dhcp_sock_filter) / sizeof(struct bpf_insn); + if (ioctl(sock, BIOCSETF, &prog) < 0) { + close(fallback); + close(sock); + isc_throw(SocketConfigError, "Failed to install BPF filter" + " program"); + } + + // Everything is ok, return the socket (BPF device descriptor) to + // the caller. + return (SocketInfo(addr, port, sock, fallback)); +} + +Pkt4Ptr +PktFilterBPF::receive(const Iface&/* iface */, const SocketInfo& /*socket_info*/) { + return (Pkt4Ptr()); + /* 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); + + // 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)); + // If negative value is returned by read(), it indicates that an + // error occured. If returned value is 0, no data was read from the + // socket. In both cases something has gone wrong, because we expect + // that a chunk of data is there. We signal the lack of data by + // returing an empty packet. + if (data_len <= 0) { + return Pkt4Ptr(); + } + + 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 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()); + + return (pkt); */ +} + +int +PktFilterBPF::send(const Iface& /*iface*/, uint16_t /*sockfd*/, const Pkt4Ptr& /*pkt*/) { + + return 0; + + /* OutputBuffer buf(14); + + // Some interfaces may have no HW address - e.g. loopback interface. + // For these interfaces the HW address length is 0. If this is the case, + // then we will rely on the functions which construct the IP/UDP headers + // to provide a default HW addres. Otherwise, create the HW address + // object using the HW address of the interface. + if (iface.getMacLen() > 0) { + HWAddrPtr hwaddr(new HWAddr(iface.getMac(), iface.getMacLen(), + iface.getHWType())); + pkt->setLocalHWAddr(hwaddr); + } + + + // Ethernet frame header. + // Note that we don't validate whether HW addresses in 'pkt' + // are valid because they are checked by the function called. + writeEthernetHeader(pkt, buf); + + // IP and UDP header + writeIpUdpHeader(pkt, buf); + + // DHCPv4 message + buf.writeData(pkt->getBuffer().getData(), pkt->getBuffer().getLength()); + + sockaddr_ll sa; + sa.sll_family = AF_PACKET; + sa.sll_ifindex = iface.getIndex(); + sa.sll_protocol = htons(ETH_P_IP); + sa.sll_halen = 6; + + int result = sendto(sockfd, buf.getData(), buf.getLength(), 0, + reinterpret_cast(&sa), + sizeof(sockaddr_ll)); + if (result < 0) { + isc_throw(SocketWriteError, "failed to send DHCPv4 packet, errno=" + << errno << " (check errno.h)"); + } + + return (0); + */ +} + + +} // end of isc::dhcp namespace +} // end of isc namespace diff --git a/src/lib/dhcp/pkt_filter_bpf.h b/src/lib/dhcp/pkt_filter_bpf.h new file mode 100644 index 0000000000..2f2ba752fd --- /dev/null +++ b/src/lib/dhcp/pkt_filter_bpf.h @@ -0,0 +1,81 @@ +// Copyright (C) 2014 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_BPF_H +#define PKT_FILTER_BPF_H + +#include + +#include + +namespace isc { +namespace dhcp { + +/// @brief Packet handling class using Berkeley Packet Filtering +/// +/// This class provides methods to send and recive DHCPv4 messages using raw +/// sockets and Berkeley Packet Filtering. It is used by @c isc::dhcp::IfaceMgr +/// to send DHCPv4 messages to the hosts which don't have an IPv4 address +/// assigned yet. +class PktFilterBPF : public PktFilter { +public: + + /// @brief Check if packet can be sent to the host without address directly. + /// + /// This class supports direct responses to the host without address. + /// + /// @return true always. + virtual bool isDirectResponseSupported() const { + return (true); + } + + /// @brief Open primary and fallback socket. + /// + /// @param iface Interface descriptor. + /// @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 A structure describing a primary and fallback socket. + virtual SocketInfo openSocket(const Iface& iface, + const isc::asiolink::IOAddress& addr, + const uint16_t port, + const bool receive_bcast, + const bool send_bcast); + + /// @brief Receive packet over specified socket. + /// + /// @param iface interface + /// @param socket_info structure holding socket information + /// + /// @return Received packet + virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& socket_info); + + /// @brief Send packet over specified socket. + /// + /// @param iface interface to be used to send packet + /// @param sockfd socket descriptor + /// @param pkt packet to be sent + /// + /// @return result of sending a packet. It is 0 if successful. + virtual int send(const Iface& iface, uint16_t sockfd, + const Pkt4Ptr& pkt); + +}; + +} // namespace isc::dhcp +} // namespace isc + +#endif // PKT_FILTER_BPF_H diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 050e0c60cf..c4b0a5507e 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -78,10 +78,16 @@ libdhcp___unittests_SOURCES += pkt_filter6_test_stub.cc pkt_filter_test_stub.h libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc libdhcp___unittests_SOURCES += pkt_filter6_test_utils.h pkt_filter6_test_utils.cc +# Utilize Linux Packet Filtering on Linux. if OS_LINUX libdhcp___unittests_SOURCES += pkt_filter_lpf_unittest.cc endif +# Utilize Berkeley Packet Filtering on BSD. +if OS_BSD +libdhcp___unittests_SOURCES += pkt_filter_bpf_unittest.cc +endif + libdhcp___unittests_SOURCES += protocol_util_unittest.cc libdhcp___unittests_SOURCES += duid_unittest.cc diff --git a/src/lib/dhcp/tests/pkt_filter_bpf_unittest.cc b/src/lib/dhcp/tests/pkt_filter_bpf_unittest.cc new file mode 100644 index 0000000000..578092f2ac --- /dev/null +++ b/src/lib/dhcp/tests/pkt_filter_bpf_unittest.cc @@ -0,0 +1,193 @@ +// Copyright (C) 2014 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::util; + +namespace { + +/// Port number used by tests. +const uint16_t PORT = 10067; +/// Size of the buffer holding received packets. +const size_t RECV_BUF_SIZE = 2048; + +// Test fixture class inherits from the class common for all packet +// filter tests. +class PktFilterBPFTest : public isc::dhcp::test::PktFilterTest { +public: + PktFilterBPFTest() : PktFilterTest(PORT) { + } +}; + +// This test verifies that the PktFilterBPF class reports its capability +// to send packets to the host having no IP address assigned. +TEST_F(PktFilterBPFTest, isDirectResponseSupported) { + // Create object under test. + PktFilterBPF pkt_filter; + // Must support direct responses. + EXPECT_TRUE(pkt_filter.isDirectResponseSupported()); +} + +// All tests below require root privileges to execute successfully. If +// they are run as non-root user they will fail due to insufficient privileges +// to open raw network sockets. Therefore, they should remain disabled by default +// and "DISABLED_" tags should not be removed. If one is willing to run these +// tests please run "make check" as root and enable execution of disabled tests +// by setting GTEST_ALSO_RUN_DISABLED_TESTS to a value other than 0. In order +// to run tests from this particular file, set the GTEST_FILTER environmental +// variable to "PktFilterBPFTest.*" apart from GTEST_ALSO_RUN_DISABLED_TESTS +// setting. + +// This test verifies that the raw AF_PACKET family socket can +// be opened and bound to the specific interface. +TEST_F(PktFilterBPFTest, DISABLED_openSocket) { + // Create object representing loopback interface. + Iface iface(ifname_, ifindex_); + // Set loopback address. + IOAddress addr("127.0.0.1"); + + // Try to open socket. + PktFilterBPF pkt_filter; + ASSERT_NO_THROW( + sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false); + ); + + // Check that the primary socket has been opened. + ASSERT_GE(sock_info_.sockfd_, 0); + // Check that the fallback socket has been opened too. + ASSERT_GE(sock_info_.fallbackfd_, 0); + + /* // Verify that the socket belongs to AF_PACKET family. + sockaddr_ll sock_address; + socklen_t sock_address_len = sizeof(sock_address); + ASSERT_EQ(0, getsockname(sock_info_.sockfd_, + reinterpret_cast(&sock_address), + &sock_address_len)); + EXPECT_EQ(AF_PACKET, sock_address.sll_family); + + // Verify that the socket is bound to appropriate interface. + EXPECT_EQ(ifindex_, sock_address.sll_ifindex); + + // Verify that the socket has SOCK_RAW type. + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + ASSERT_EQ(0, getsockopt(sock_info_.sockfd_, SOL_SOCKET, SO_TYPE, + &sock_type, &sock_type_len)); + EXPECT_EQ(SOCK_RAW, sock_type); */ +} + +// This test verifies correctness of sending DHCP packet through the raw +// socket, whereby all IP stack headers are hand-crafted. +TEST_F(PktFilterBPFTest, DISABLED_send) { + /* // Packet will be sent over loopback interface. + Iface iface(ifname_, ifindex_); + IOAddress addr("127.0.0.1"); + + // Create an instance of the class which we are testing. + PktFilterBPF pkt_filter; + // Open socket. We don't check that the socket has appropriate + // options and family set because we have checked that in the + // openSocket test already. + + sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false); + + ASSERT_GE(sock_info_.sockfd_, 0); + + // Send the packet over the socket. + ASSERT_NO_THROW(pkt_filter.send(iface, sock_info_.sockfd_, test_message_)); + + // Read the data from socket. + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(sock_info_.sockfd_, &readfds); + + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + int result = select(sock_info_.sockfd_ + 1, &readfds, NULL, NULL, &timeout); + // We should receive some data from loopback interface. + ASSERT_GT(result, 0); + + // Get the actual data. + uint8_t rcv_buf[RECV_BUF_SIZE]; + result = recv(sock_info_.sockfd_, rcv_buf, RECV_BUF_SIZE, 0); + ASSERT_GT(result, 0); + + Pkt4Ptr dummy_pkt = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 0)); + + InputBuffer buf(rcv_buf, result); + + // Decode ethernet, ip and udp headers. + decodeEthernetHeader(buf, dummy_pkt); + decodeIpUdpHeader(buf, dummy_pkt); + + // Create the DHCPv4 packet from the received data. + std::vector dhcp_buf; + buf.readVector(dhcp_buf, buf.getLength() - buf.getPosition()); + Pkt4Ptr rcvd_pkt(new Pkt4(&dhcp_buf[0], dhcp_buf.size())); + ASSERT_TRUE(rcvd_pkt); + + // Parse the packet. + ASSERT_NO_THROW(rcvd_pkt->unpack()); + + // Check if the received message is correct. + testRcvdMessage(rcvd_pkt); */ +} + +// This test verifies correctness of reception of the DHCP packet over +// raw socket, whereby all IP stack headers are hand-crafted. +TEST_F(PktFilterBPFTest, DISABLED_receive) { + + // Packet will be received over loopback interface. + Iface iface(ifname_, ifindex_); + IOAddress addr("127.0.0.1"); + + // Create an instance of the class which we are testing. + PktFilterBPF pkt_filter; + // Open socket. We don't check that the socket has appropriate + // options and family set because we have checked that in the + // openSocket test already. + sock_info_ = pkt_filter.openSocket(iface, addr, PORT, false, false); + ASSERT_GE(sock_info_.sockfd_, 0); + + // Send DHCPv4 message to the local loopback address and server's port. + sendMessage(); + + // Receive the packet using LPF packet filter. + Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, sock_info_); + // Check that the packet has been correctly received. + ASSERT_TRUE(rcvd_pkt); + + // Parse the packet. + ASSERT_NO_THROW(rcvd_pkt->unpack()); + + // Check if the received message is correct. + testRcvdMessage(rcvd_pkt); +} + +} // anonymous namespace