iface_mgr_unittest.cc 15.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// Copyright (C) 2011  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 <iostream>
#include <fstream>
#include <sstream>

#include <arpa/inet.h>
#include <gtest/gtest.h>

23 24 25
#include <asiolink/io_address.h>
#include <dhcp/pkt6.h>
#include <dhcp/iface_mgr.h>
26
#include <dhcp/dhcp4.h>
27 28 29

using namespace std;
using namespace isc;
30
using namespace isc::asiolink;
31
using namespace isc::dhcp;
32

33
// name of loopback interface detection
34 35
const size_t buf_size = 32;
char LOOPBACK[buf_size] = "lo";
36

37
namespace {
38 39
const char* const INTERFACE_FILE = TEST_DATA_BUILDDIR "/interfaces.txt";

40
class NakedIfaceMgr: public IfaceMgr {
41
    // "naked" Interface Manager, exposes internal fields
42 43
public:
    NakedIfaceMgr() { }
44
    IfaceCollection & getIfacesLst() { return ifaces_; }
45 46 47 48 49 50 51
};

// dummy class for now, but this will be expanded when needed
class IfaceMgrTest : public ::testing::Test {
public:
    IfaceMgrTest() {
    }
52 53 54 55 56 57 58

    void createLoInterfacesTxt() {
        unlink(INTERFACE_FILE);
        fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
        fakeifaces << LOOPBACK << " ::1";
        fakeifaces.close();
    }
59 60
};

61 62 63 64
// 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.
65 66 67

// NOTE: At this stage of development, write access to current directory
// during running tests is required.
68 69
TEST_F(IfaceMgrTest, loDetect) {

70
    // poor man's interface detection
71 72
    // it will go away as soon as proper interface detection
    // is implemented
73
    if (if_nametoindex("lo") > 0) {
74
        cout << "This is Linux, using lo as loopback." << endl;
75 76
        snprintf(LOOPBACK, buf_size - 1, "lo");
    } else if (if_nametoindex("lo0") > 0) {
77
        cout << "This is BSD, using lo0 as loopback." << endl;
78
        snprintf(LOOPBACK, buf_size - 1, "lo0");
79
    } else {
80
        cout << "Failed to detect loopback interface. Neither "
81 82
             << "lo nor lo0 worked. I give up." << endl;
        FAIL();
83 84 85
    }
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
// 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();

104
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
105

106
    Pkt6* pkt = NULL;
107 108 109 110
    int cnt = 0;
    cout << "---8X-----------------------------------------" << endl;
    while (true) {
        pkt = ifacemgr->receive();
111

112
        cout << "// this code is autogenerated. Do NOT edit." << endl;
113 114 115 116 117
        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;
118 119
        cout << "    pkt->remote_addr_ = IOAddress(\""
             << pkt->remote_addr_.toText() << "\");" << endl;
120
        cout << "    pkt->local_port_ = " << pkt-> local_port_ << ";" << endl;
121 122
        cout << "    pkt->local_addr_ = IOAddress(\""
             << pkt->local_addr_.toText() << "\");" << endl;
123 124
        cout << "    pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
        cout << "    pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
125 126 127

        // TODO it is better to declare an array and then memcpy it to
        // packet.
128
        for (int i=0; i< pkt->data_len_; i++) {
129 130
            cout << "    pkt->data_[" << i << "]="
                 << (int)(unsigned char)pkt->data_[i] << "; ";
131 132 133 134 135 136 137 138 139 140
            if (!(i%4))
                cout << endl;
        }
        cout << endl;
        cout << "    return (pkt);" << endl;
        cout << "}" << endl << endl;

        delete pkt;
    }
    cout << "---8X-----------------------------------------" << endl;
141

142 143 144 145 146 147
    // never happens. Infinite loop is infinite
    delete pkt;
    delete ifacemgr;
}
#endif

148 149 150 151 152 153 154 155 156 157
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

158
    IfaceMgr::Iface* iface = new IfaceMgr::Iface("eth5", 7);
159 160 161 162 163 164 165

    EXPECT_STREQ("eth5/7", iface->getFullName().c_str());

    delete iface;

}

166 167
// TODO: Implement getPlainMac() test as soon as interface detection
// is implemented.
168 169 170
TEST_F(IfaceMgrTest, getIface) {

    cout << "Interface checks. Please ignore socket binding errors." << endl;
171
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
172 173

    // interface name, ifindex
174
    IfaceMgr::Iface iface1("lo1", 1);
175 176 177 178
    IfaceMgr::Iface iface2("eth5", 2);
    IfaceMgr::Iface iface3("en3", 5);
    IfaceMgr::Iface iface4("e1000g0", 3);

179
    // note: real interfaces may be detected as well
180 181 182 183 184
    ifacemgr->getIfacesLst().push_back(iface1);
    ifacemgr->getIfacesLst().push_back(iface2);
    ifacemgr->getIfacesLst().push_back(iface3);
    ifacemgr->getIfacesLst().push_back(iface4);

185 186
    cout << "There are " << ifacemgr->getIfacesLst().size()
         << " interfaces." << endl;
187
    for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin();
188 189
         iface != ifacemgr->getIfacesLst().end();
         ++iface) {
190
        cout << "  " << iface->getFullName() << endl;
191 192 193
    }


194
    // check that interface can be retrieved by ifindex
195
    IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
196 197 198
    // ASSERT_NE(NULL, tmp); is not supported. hmmmm.
    ASSERT_TRUE( tmp != NULL );

199 200
    EXPECT_EQ( "en3", tmp->getName() );
    EXPECT_EQ(5, tmp->getIndex());
201 202

    // check that interface can be retrieved by name
203
    tmp = ifacemgr->getIface("lo1");
204 205
    ASSERT_TRUE( tmp != NULL );

206 207
    EXPECT_EQ( "lo1", tmp->getName() );
    EXPECT_EQ(1, tmp->getIndex());
208 209

    // check that non-existing interfaces are not returned
210
    EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
211 212 213 214 215 216

    delete ifacemgr;
}

TEST_F(IfaceMgrTest, detectIfaces) {

217
    // test detects that interfaces can be detected
218 219
    // there is no code for that now, but interfaces are
    // read from file
220
    fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
221 222
    fakeifaces << "eth0 fe80::1234";
    fakeifaces.close();
223

224 225 226
    // this is not usable on systems that don't have eth0
    // interfaces. Nevertheless, this fake interface should
    // be on list, but if_nametoindex() will fail.
227

228
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
229

230
    ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
231

232
    IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
233

234
    // there should be one address
235 236
    IfaceMgr::AddressCollection addrs = eth0->getAddresses();
    ASSERT_EQ(1, addrs.size());
237

238
    IOAddress addr = *addrs.begin();
239

240
    EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
241 242

    delete ifacemgr;
243
    unlink(INTERFACE_FILE);
244 245
}

246
TEST_F(IfaceMgrTest, sockets6) {
247 248 249
    // testing socket operation in a portable way is tricky
    // without interface detection implemented

250 251
    createLoInterfacesTxt();

252
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
253

254
    IOAddress loAddr("::1");
255

256 257 258
    Pkt6 pkt6(128);
    pkt6.iface_ = LOOPBACK;

259
    // bind multicast socket to port 10547
260
    int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
261 262
    EXPECT_GT(socket1, 0); // socket > 0

263 264
    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6));

265
    // bind unicast socket to port 10548
266
    int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548);
267 268
    EXPECT_GT(socket2, 0);

269 270
    // removed code for binding socket twice to the same address/port
    // as it caused problems on some platforms (e.g. Mac OS X)
271 272 273 274 275

    close(socket1);
    close(socket2);

    delete ifacemgr;
276
    unlink(INTERFACE_FILE);
277 278
}

279 280
// TODO: disabled due to other naming on various systems
// (lo in Linux, lo0 in BSD systems)
281
TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
282 283 284
    // testing socket operation in a portable way is tricky
    // without interface detection implemented

285
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
286 287 288 289 290

    IOAddress loAddr("::1");
    IOAddress mcastAddr("ff02::1:2");

    // bind multicast socket to port 10547
291
    int socket1 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
292 293 294 295
    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
296
    int socket2 = ifacemgr->openSocket(LOOPBACK, mcastAddr, 10547);
297 298
    EXPECT_GT(socket2, 0);

299 300 301 302
    // 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.
303 304 305

    close(socket1);
    close(socket2);
306 307

    delete ifacemgr;
308 309
}

310
TEST_F(IfaceMgrTest, sendReceive6) {
311

312 313
    // testing socket operation in a portable way is tricky
    // without interface detection implemented
314
    createLoInterfacesTxt();
315

316
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
317 318

    // let's assume that every supported OS have lo interface
319
    IOAddress loAddr("::1");
320 321 322 323 324
    int socket1 = 0, socket2 = 0;
    EXPECT_NO_THROW(
        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
    );
325

326 327
    EXPECT_GT(socket1, 0);
    EXPECT_GT(socket2, 0);
328

329
    boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
330 331 332

    // prepare dummy payload
    for (int i=0;i<128; i++) {
333
        sendPkt->data_[i] = i;
334 335
    }

336 337 338
    sendPkt->remote_port_ = 10547;
    sendPkt->remote_addr_ = IOAddress("::1");
    sendPkt->ifindex_ = 1;
339
    sendPkt->iface_ = LOOPBACK;
340

341
    boost::shared_ptr<Pkt6> rcvPkt;
342 343 344

    EXPECT_EQ(true, ifacemgr->send(sendPkt));

345
    rcvPkt = ifacemgr->receive6();
346

347
    ASSERT_TRUE( rcvPkt ); // received our own packet
348 349

    // let's check that we received what was sent
350 351
    EXPECT_EQ(sendPkt->data_len_, rcvPkt->data_len_);
    EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
352
                        rcvPkt->data_len_) );
353

354
    EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
355 356 357 358 359 360

    // 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->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
361 362

    delete ifacemgr;
363
    unlink(INTERFACE_FILE);
364 365
}

366 367 368 369 370
TEST_F(IfaceMgrTest, socket4) {

    createLoInterfacesTxt();
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();

371
    // Let's assume that every supported OS have lo interface.
372
    IOAddress loAddr("127.0.0.1");
373
    // Use unprivileged port (it's convenient for running tests as non-root).
374 375 376 377 378 379 380 381
    int socket1 = 0;

    EXPECT_NO_THROW(
        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
    );

    EXPECT_GT(socket1, 0);

382 383 384
    Pkt4 pkt(DHCPDISCOVER, 1234);
    pkt.setIface(LOOPBACK);

385
    // Expect that we get the socket that we just opened.
386 387
    EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));

388 389 390
    close(socket1);

    delete ifacemgr;
391
    unlink(INTERFACE_FILE);
392 393
}

394 395
// Test the Iface structure itself
TEST_F(IfaceMgrTest, iface) {
396
    IfaceMgr::Iface* iface = NULL;
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    EXPECT_NO_THROW(
        iface = new IfaceMgr::Iface("eth0",1);
    );

    EXPECT_EQ("eth0", iface->getName());
    EXPECT_EQ(1, iface->getIndex());
    EXPECT_EQ("eth0/1", iface->getFullName());

    // Let's make a copy of this address collection.
    IfaceMgr::AddressCollection addrs = iface->getAddresses();

    EXPECT_EQ(0, addrs.size());

    IOAddress addr1("192.0.2.6");
    iface->addAddress(addr1);

    addrs = iface->getAddresses();
    ASSERT_EQ(1, addrs.size());
    EXPECT_EQ("192.0.2.6", addrs.at(0).toText());

    // No such address, should return false.
    EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9")));

    // This address is present, delete it!
    EXPECT_TRUE(iface->delAddress(IOAddress("192.0.2.6")));

    // Not really necessary, previous reference still points to the same
    // collection. Let's do it anyway, as test code may serve as example
    // usage code as well.
    addrs = iface->getAddresses();

    EXPECT_EQ(0, addrs.size());

    EXPECT_NO_THROW(
        delete iface;
    );
}

TEST_F(IfaceMgrTest, socketInfo) {

    // check that socketinfo for IPv4 socket is functional
    IfaceMgr::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
    IfaceMgr::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_);
    EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock2.port_);

    // now let's test if IfaceMgr handles socket info properly
    createLoInterfacesTxt();
453
    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
    IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
    ASSERT_TRUE(loopback);
    loopback->addSocket(sock1);
    loopback->addSocket(sock2);

    Pkt6 pkt6(100);

    // pkt6 dos not have interface set yet
    EXPECT_THROW(
        ifacemgr->getSocket(pkt6),
        BadValue
    );

    // try to send over non-existing interface
    pkt6.iface_ = "nosuchinterface45";
    EXPECT_THROW(
        ifacemgr->getSocket(pkt6),
        BadValue
    );

    // this will work
    pkt6.iface_ = LOOPBACK;
    EXPECT_EQ(9, ifacemgr->getSocket(pkt6));

    bool deleted = false;
    EXPECT_NO_THROW(
        deleted = ifacemgr->getIface(LOOPBACK)->delSocket(9);
    );
    EXPECT_EQ(true, deleted);

    // it should throw again, there's no usable socket anymore
    EXPECT_THROW(
        ifacemgr->getSocket(pkt6),
        Unexpected
    );

    // repeat for pkt4
    Pkt4 pkt4(DHCPDISCOVER, 1);

    // pkt4 does not have interface set yet.
    EXPECT_THROW(
        ifacemgr->getSocket(pkt4),
        BadValue
    );

    // Try to send over non-existing interface.
    pkt4.setIface("nosuchinterface45");
    EXPECT_THROW(
        ifacemgr->getSocket(pkt4),
        BadValue
    );

    // Socket info is set, packet has well defined interface. It should work.
    pkt4.setIface(LOOPBACK);
    EXPECT_EQ(7, ifacemgr->getSocket(pkt4));

    EXPECT_NO_THROW(
        ifacemgr->getIface(LOOPBACK)->delSocket(7);
    );

    // It should throw again, there's no usable socket anymore.
    EXPECT_THROW(
        ifacemgr->getSocket(pkt4),
        Unexpected
    );
519 520

    delete ifacemgr;
521
    unlink(INTERFACE_FILE);
522 523
}

524
}