// Copyright (C) 2011-2012 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 #include #include using namespace std; using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; 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: NakedIfaceMgr() { } IfaceCollection & getIfacesLst() { return ifaces_; } }; // dummy class for now, but this will be expanded when needed class IfaceMgrTest : public ::testing::Test { public: // these are empty for now, but let's keep them around IfaceMgrTest() { } ~IfaceMgrTest() { } }; // We need some known interface to work reliably. Loopback interface // is named lo on Linux and lo0 on BSD boxes. We need to find out // which is available. This is not a real test, but rather a workaround // that will go away when interface detection is implemented. // NOTE: At this stage of development, write access to current directory // during running tests is required. TEST_F(IfaceMgrTest, loDetect) { // poor man's interface detection // it will go away as soon as proper interface detection // is implemented if (if_nametoindex("lo") > 0) { cout << "This is Linux, using lo as loopback." << endl; 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"); } else { cout << "Failed to detect loopback interface. Neither " << "lo nor lo0 worked. I give up." << endl; FAIL(); } } // 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 // // TODO: this potentially should be moved to a separate tool // #if 0 TEST_F(IfaceMgrTest, dhcp6Sniffer) { // testing socket operation in a portable way is tricky // without interface detection implemented unlink("interfaces.txt"); ofstream interfaces("interfaces.txt", ios::ate); interfaces << "eth0 fe80::21e:8cff:fe9b:7349"; interfaces.close(); NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); Pkt6* pkt = NULL; int cnt = 0; cout << "---8X-----------------------------------------" << endl; while (true) { pkt = ifacemgr->receive(); cout << "// this code is autogenerated. Do NOT edit." << endl; cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl; cout << "Pkt6 *capture" << cnt++ << "() {" << endl; cout << " Pkt6* pkt;" << endl; cout << " pkt = new Pkt6(" << pkt->data_len_ << ");" << endl; cout << " pkt->remote_port_ = " << pkt-> remote_port_ << ";" << endl; cout << " pkt->remote_addr_ = IOAddress(\"" << pkt->remote_addr_.toText() << "\");" << endl; cout << " pkt->local_port_ = " << pkt-> local_port_ << ";" << endl; cout << " pkt->local_addr_ = IOAddress(\"" << pkt->local_addr_.toText() << "\");" << endl; cout << " pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl; cout << " pkt->iface_ = \"" << pkt->iface_ << "\";" << endl; // TODO it is better to declare statically initialize the array // and then memcpy it to packet. for (int i=0; i< pkt->data_len_; i++) { cout << " pkt->data_[" << i << "]=" << (int)(unsigned char)pkt->data_[i] << "; "; if (!(i%4)) cout << endl; } cout << endl; cout << " return (pkt);" << endl; cout << "}" << endl << endl; delete pkt; } cout << "---8X-----------------------------------------" << endl; // never happens. Infinite loop is infinite delete pkt; delete ifacemgr; } #endif TEST_F(IfaceMgrTest, basic) { // checks that IfaceManager can be instantiated IfaceMgr & ifacemgr = IfaceMgr::instance(); ASSERT_TRUE(&ifacemgr != 0); } TEST_F(IfaceMgrTest, ifaceClass) { // basic tests for Iface inner class IfaceMgr::Iface* iface = new IfaceMgr::Iface("eth5", 7); EXPECT_STREQ("eth5/7", iface->getFullName().c_str()); delete iface; } // TODO: Implement getPlainMac() test as soon as interface detection // is implemented. TEST_F(IfaceMgrTest, getIface) { cout << "Interface checks. Please ignore socket binding errors." << endl; NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); // interface name, ifindex IfaceMgr::Iface iface1("lo1", 100); IfaceMgr::Iface iface2("eth9", 101); IfaceMgr::Iface iface3("en3", 102); IfaceMgr::Iface iface4("e1000g4", 103); cout << "This test assumes that there are less than 100 network interfaces" << " in the tested system and there are no lo1, eth9, en3, e1000g4" << " or wifi15 interfaces present." << endl; // note: real interfaces may be detected as well ifacemgr->getIfacesLst().push_back(iface1); ifacemgr->getIfacesLst().push_back(iface2); ifacemgr->getIfacesLst().push_back(iface3); ifacemgr->getIfacesLst().push_back(iface4); cout << "There are " << ifacemgr->getIfacesLst().size() << " interfaces." << endl; for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin(); iface != ifacemgr->getIfacesLst().end(); ++iface) { cout << " " << iface->getFullName() << endl; } // check that interface can be retrieved by ifindex IfaceMgr::Iface* tmp = ifacemgr->getIface(102); ASSERT_TRUE(tmp != NULL); EXPECT_EQ("en3", tmp->getName()); EXPECT_EQ(102, tmp->getIndex()); // check that interface can be retrieved by name tmp = ifacemgr->getIface("lo1"); ASSERT_TRUE(tmp != NULL); EXPECT_EQ("lo1", tmp->getName()); EXPECT_EQ(100, tmp->getIndex()); // check that non-existing interfaces are not returned EXPECT_EQ(static_cast(NULL), ifacemgr->getIface("wifi15") ); delete ifacemgr; } TEST_F(IfaceMgrTest, sockets6) { // testing socket operation in a portable way is tricky // without interface detection implemented NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); IOAddress loAddr("::1"); Pkt6 pkt6(DHCPV6_SOLICIT, 123); pkt6.setIface(LOOPBACK); // bind multicast socket to port 10547 int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547); EXPECT_GT(socket1, 0); // socket > 0 EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6)); // bind unicast socket to port 10548 int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548); EXPECT_GT(socket2, 0); // removed code for binding socket twice to the same address/port // as it caused problems on some platforms (e.g. Mac OS X) close(socket1); close(socket2); delete ifacemgr; } TEST_F(IfaceMgrTest, socketsFromIface) { boost::scoped_ptr 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 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 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) { // testing socket operation in a portable way is tricky // without interface detection implemented NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); IOAddress loAddr("::1"); IOAddress mcastAddr("ff02::1:2"); // bind multicast socket to port 10547 int socket1 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547); EXPECT_GT(socket1, 0); // socket > 0 // expect success. This address/port is already bound, but // we are using SO_REUSEADDR, so we can bind it twice int socket2 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547); EXPECT_GT(socket2, 0); // there's no good way to test negative case here. // we would need non-multicast interface. We will be able // to iterate thru available interfaces and check if there // are interfaces without multicast-capable flag. close(socket1); close(socket2); delete ifacemgr; } TEST_F(IfaceMgrTest, sendReceive6) { // testing socket operation in a portable way is tricky // without interface detection implemented NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); // let's assume that every supported OS have lo interface IOAddress loAddr("::1"); int socket1 = 0, socket2 = 0; EXPECT_NO_THROW( socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547); socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546); ); EXPECT_GT(socket1, 0); EXPECT_GT(socket2, 0); // prepare dummy payload uint8_t data[128]; for (int i = 0; i < 128; i++) { data[i] = i; } Pkt6Ptr sendPkt = Pkt6Ptr(new Pkt6(data, 128)); sendPkt->repack(); sendPkt->setRemotePort(10547); sendPkt->setRemoteAddr(IOAddress("::1")); sendPkt->setIndex(1); sendPkt->setIface(LOOPBACK); Pkt6Ptr rcvPkt; EXPECT_EQ(true, ifacemgr->send(sendPkt)); rcvPkt = ifacemgr->receive6(); ASSERT_TRUE(rcvPkt); // received our own packet // let's check that we received what was sent ASSERT_EQ(sendPkt->getData().size(), rcvPkt->getData().size()); EXPECT_EQ(0, memcmp(&sendPkt->getData()[0], &rcvPkt->getData()[0], rcvPkt->getData().size())); EXPECT_EQ(sendPkt->getRemoteAddr().toText(), rcvPkt->getRemoteAddr().toText()); // since we opened 2 sockets on the same interface and none of them is multicast, // none is preferred over the other for sending data, so we really should not // assume the one or the other will always be choosen for sending data. Therefore // we should accept both values as source ports. EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547)); delete ifacemgr; } TEST_F(IfaceMgrTest, sendReceive4) { // testing socket operation in a portable way is tricky // without interface detection implemented NakedIfaceMgr* ifacemgr = new NakedIfaceMgr(); // let's assume that every supported OS have lo interface IOAddress loAddr("127.0.0.1"); int socket1 = 0, socket2 = 0; EXPECT_NO_THROW( socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000); socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000 + 1); ); EXPECT_GE(socket1, 0); EXPECT_GE(socket2, 0); boost::shared_ptr sendPkt(new Pkt4(DHCPDISCOVER, 1234) ); sendPkt->setLocalAddr(IOAddress("127.0.0.1")); sendPkt->setLocalPort(DHCP4_SERVER_PORT + 10000 + 1); sendPkt->setRemotePort(DHCP4_SERVER_PORT + 10000); sendPkt->setRemoteAddr(IOAddress("127.0.0.1")); sendPkt->setIndex(1); sendPkt->setIface(string(LOOPBACK)); sendPkt->setHops(6); sendPkt->setSecs(42); sendPkt->setCiaddr(IOAddress("192.0.2.1")); sendPkt->setSiaddr(IOAddress("192.0.2.2")); sendPkt->setYiaddr(IOAddress("192.0.2.3")); sendPkt->setGiaddr(IOAddress("192.0.2.4")); // unpack() now checks if mandatory DHCP_MESSAGE_TYPE is present boost::shared_ptr