iface_mgr.cc 33.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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.

15
#include <config.h>
16 17 18 19 20
#include <sstream>
#include <fstream>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
21
#include <sys/select.h>
22

23
#include <dhcp/dhcp4.h>
24 25 26
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
27
#include <util/io/pktinfo_utilities.h>
28 29

using namespace std;
30
using namespace isc::asiolink;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
31
using namespace isc::util::io::internal;
32

33
namespace isc {
34
namespace dhcp {
35

36
/// IfaceMgr is a singleton implementation
37
IfaceMgr* IfaceMgr::instance_ = 0;
38

39 40
void
IfaceMgr::instanceCreate() {
41
    if (instance_) {
42
        // no need to do anything. Instance is already created.
43
        // Who called it again anyway? Uh oh. Had to be us, as
44
        // this is private method.
45 46 47 48 49
        return;
    }
    instance_ = new IfaceMgr();
}

50 51
IfaceMgr&
IfaceMgr::instance() {
52
    if (instance_ == 0) {
53
        instanceCreate();
54
    }
55
    return (*instance_);
56 57
}

58
IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
59 60 61
    :name_(name), ifindex_(ifindex), mac_len_(0), hardware_type_(0),
     flag_loopback_(false), flag_up_(false), flag_running_(false),
     flag_multicast_(false), flag_broadcast_(false), flags_(0)
62
{
63
    memset(mac_, 0, sizeof(mac_));
64 65
}

66 67
std::string
IfaceMgr::Iface::getFullName() const {
68 69
    ostringstream tmp;
    tmp << name_ << "/" << ifindex_;
70
    return (tmp.str());
71 72
}

73 74
std::string
IfaceMgr::Iface::getPlainMac() const {
75
    ostringstream tmp;
76 77
    tmp.fill('0');
    tmp << hex;
78
    for (int i = 0; i < mac_len_; i++) {
79
        tmp.width(2);
80
        tmp <<  static_cast<int>(mac_[i]);
81
        if (i < mac_len_-1) {
82 83 84
            tmp << ":";
        }
    }
85
    return (tmp.str());
86 87
}

88 89 90 91 92 93 94 95 96 97 98
void IfaceMgr::Iface::setMac(const uint8_t* mac, size_t len) {
    if (len > IfaceMgr::MAX_MAC_LEN) {
        isc_throw(OutOfRange, "Interface " << getFullName()
                  << " was detected to have link address of length "
                  << len << ", but maximum supported length is "
                  << IfaceMgr::MAX_MAC_LEN);
    }
    mac_len_ = len;
    memcpy(mac_, mac, len);
}

99
bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
100 101 102 103 104 105 106 107
    for (AddressCollection::iterator a = addrs_.begin();
         a!=addrs_.end(); ++a) {
        if (*a==addr) {
            addrs_.erase(a);
            return (true);
        }
    }
    return (false);
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121 122
bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
    list<SocketInfo>::iterator sock = sockets_.begin();
    while (sock!=sockets_.end()) {
        if (sock->sockfd_ == sockfd) {
            close(sockfd);
            sockets_.erase(sock);
            return (true); //socket found
        }
        ++sock;
    }
    return (false); // socket not found
}

123 124
IfaceMgr::IfaceMgr()
    :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
125 126
     control_buf_(new char[control_buf_len_]),
     session_socket_(0), session_callback_(NULL)
127
{
128

129
    cout << "IfaceMgr initialization." << endl;
130

131
    try {
132 133 134
        // required for sending/receiving packets
        // let's keep it in front, just in case someone
        // wants to send anything during initialization
135 136

        // control_buf_ = boost::scoped_array<char>();
137

138
        detectIfaces();
139

140
    } catch (const std::exception& ex) {
141 142 143 144 145 146
        cout << "IfaceMgr creation failed:" << ex.what() << endl;

        // TODO Uncomment this (or call LOG_FATAL) once
        // interface detection is implemented. Otherwise
        // it is not possible to run tests in a portable
        // way (see detectIfaces() method).
147
        throw;
148
    }
149 150
}

151 152 153 154 155 156 157 158 159 160 161 162 163 164
void IfaceMgr::closeSockets() {
    for (IfaceCollection::iterator iface = ifaces_.begin();
         iface != ifaces_.end(); ++iface) {

        for (SocketCollection::iterator sock = iface->sockets_.begin();
             sock != iface->sockets_.end(); ++sock) {
            cout << "Closing socket " << sock->sockfd_ << endl;
            close(sock->sockfd_);
        }
        iface->sockets_.clear();
    }

}

165
IfaceMgr::~IfaceMgr() {
166
    // control_buf_ is deleted automatically (scoped_ptr)
167
    control_buf_len_ = 0;
168 169

    closeSockets();
170 171
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
172
void IfaceMgr::stubDetectIfaces() {
173 174
    string ifaceName, linkLocal;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
175 176 177
    // This is a stub implementation for interface detection. Actual detection
    // is faked by reading a text file. It will eventually be removed once
    // we have actual implementations for all supported systems.
178 179 180 181 182

    cout << "Interface detection is not implemented yet. "
         << "Reading interfaces.txt file instead." << endl;
    cout << "Please use format: interface-name link-local-address" << endl;

183 184
    try {
        ifstream interfaces("interfaces.txt");
185

186
        if (!interfaces.good()) {
187 188
            cout << "interfaces.txt file is not available. Stub interface detection skipped." << endl;
            return;
189 190 191 192 193 194
        }
        interfaces >> ifaceName;
        interfaces >> linkLocal;

        cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
195
        Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
196 197 198 199 200
        iface.flag_up_ = true;
        iface.flag_running_ = true;
        iface.flag_loopback_ = false;
        iface.flag_multicast_ = true;
        iface.flag_broadcast_ = true;
201
        iface.setHWType(HWTYPE_ETHERNET);
202
        IOAddress addr(linkLocal);
203 204
        iface.addAddress(addr);
        addInterface(iface);
205
        interfaces.close();
206
    } catch (const std::exception& ex) {
207 208 209 210 211
        // TODO: deallocate whatever memory we used
        // not that important, since this function is going to be
        // thrown away as soon as we get proper interface detection
        // implemented

212 213
        // TODO Do LOG_FATAL here
        std::cerr << "Interface detection failed." << std::endl;
214
        throw;
215
    }
216 217
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
218 219 220
/// @todo: Remove this once we have OS-specific interface detection
/// routines (or at least OS-specific files, like iface_mgr_solaris.cc)
/// for all OSes.
221 222 223 224 225 226
#if !defined(OS_LINUX) && !defined(OS_BSD)
void IfaceMgr::detectIfaces() {
    stubDetectIfaces();
}
#endif

Tomek Mrugalski's avatar
Tomek Mrugalski committed
227
bool IfaceMgr::openSockets4(const uint16_t port) {
228 229 230 231 232 233 234
    int sock;
    int count = 0;

    for (IfaceCollection::iterator iface=ifaces_.begin();
         iface!=ifaces_.end();
         ++iface) {

235
        cout << "Trying opening socket on interface " << iface->getFullName() << endl;
236 237 238 239

        if (iface->flag_loopback_ ||
            !iface->flag_up_ ||
            !iface->flag_running_) {
240 241
            cout << "Interface " << iface->getFullName()
                 << " not suitable: is loopback, is down or not running" << endl;
242 243 244 245 246 247 248 249 250
            continue;
        }

        AddressCollection addrs = iface->getAddresses();

        for (AddressCollection::iterator addr= addrs.begin();
             addr != addrs.end();
             ++addr) {

Tomek Mrugalski's avatar
Tomek Mrugalski committed
251
            // skip IPv6 addresses
252 253 254 255
            if (addr->getFamily() != AF_INET) {
                continue;
            }

256
            sock = openSocket(iface->getName(), *addr, port);
257 258 259 260 261 262 263 264 265 266 267 268
            if (sock<0) {
                cout << "Failed to open unicast socket." << endl;
                return (false);
            }

            count++;
        }
    }
    return (count > 0);

}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
269
bool IfaceMgr::openSockets6(const uint16_t port) {
270
    int sock;
271
    int count = 0;
272

273 274 275
    for (IfaceCollection::iterator iface=ifaces_.begin();
         iface!=ifaces_.end();
         ++iface) {
276

277 278 279 280 281 282
        if (iface->flag_loopback_ ||
            !iface->flag_up_ ||
            !iface->flag_running_) {
            continue;
        }

283
        AddressCollection addrs = iface->getAddresses();
284

285
        for (AddressCollection::iterator addr= addrs.begin();
286
             addr != addrs.end();
287 288
             ++addr) {

289 290 291
            // skip IPv4 addresses
            if (addr->getFamily() != AF_INET6) {
                continue;
292 293
            }

294
            sock = openSocket(iface->getName(), *addr, port);
295 296 297 298 299
            if (sock<0) {
                cout << "Failed to open unicast socket." << endl;
                return (false);
            }

300 301 302
            // Binding socket to unicast address and then joining multicast group
            // works well on Mac OS (and possibly other BSDs), but does not work
            // on Linux.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
303 304
            if ( !joinMulticast(sock, iface->getName(),
                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
305
                close(sock);
306 307
                isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                          << " multicast group.");
308
            }
309

310
            count++;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
311 312

            /// @todo: Remove this ifdef once we start supporting BSD systems.
313
#if defined(OS_LINUX)
314 315
            // To receive multicast traffic, Linux requires binding socket to
            // a multicast group. That in turn doesn't work on NetBSD.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
316

317 318 319
            int sock2 = openSocket(iface->getName(),
                                   IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                   port);
320
            if (sock2<0) {
321 322
                isc_throw(Unexpected, "Failed to open multicast socket on "
                          << " interface " << iface->getFullName());
323
                iface->delSocket(sock); // delete previously opened socket
324
            }
325
#endif
326 327
        }
    }
328
    return (count > 0);
329 330
}

331
void
332
IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
333 334 335 336
    for (IfaceCollection::const_iterator iface=ifaces_.begin();
         iface!=ifaces_.end();
         ++iface) {

337 338
        const AddressCollection& addrs = iface->getAddresses();

339
        out << "Detected interface " << iface->getFullName()
340 341
            << ", hwtype=" << iface->getHWType()
            << ", mac=" << iface->getPlainMac();
342
        out << ", flags=" << hex << iface->flags_ << dec << "("
343 344 345 346 347 348
            << (iface->flag_loopback_?"LOOPBACK ":"")
            << (iface->flag_up_?"UP ":"")
            << (iface->flag_running_?"RUNNING ":"")
            << (iface->flag_multicast_?"MULTICAST ":"")
            << (iface->flag_broadcast_?"BROADCAST ":"")
            << ")" << endl;
349
        out << "  " << addrs.size() << " addr(s):";
350 351

        for (AddressCollection::const_iterator addr = addrs.begin();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
352
             addr != addrs.end(); ++addr) {
353
            out << "  " << addr->toText();
354
        }
355
        out << endl;
356 357 358
    }
}

359 360
IfaceMgr::Iface*
IfaceMgr::getIface(int ifindex) {
361 362 363 364
    for (IfaceCollection::iterator iface=ifaces_.begin();
         iface!=ifaces_.end();
         ++iface) {
        if (iface->getIndex() == ifindex)
365
            return (&(*iface));
366 367
    }

368
    return (NULL); // not found
369 370
}

371
IfaceMgr::Iface*
372
IfaceMgr::getIface(const std::string& ifname) {
373 374 375 376
    for (IfaceCollection::iterator iface=ifaces_.begin();
         iface!=ifaces_.end();
         ++iface) {
        if (iface->getName() == ifname)
377
            return (&(*iface));
378 379
    }

380
    return (NULL); // not found
381 382
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
383 384
int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
                         const uint16_t port) {
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
    Iface* iface = getIface(ifname);
    if (!iface) {
        isc_throw(BadValue, "There is no " << ifname << " interface present.");
    }
    switch (addr.getFamily()) {
    case AF_INET:
        return openSocket4(*iface, addr, port);
    case AF_INET6:
        return openSocket6(*iface, addr, port);
    default:
        isc_throw(BadValue, "Failed to detect family of address: "
                  << addr.toText());
    }
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
400
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
401 402 403 404 405 406 407 408

    cout << "Creating UDP4 socket on " << iface.getFullName()
         << " " << addr.toText() << "/port=" << port << endl;

    struct sockaddr_in addr4;
    memset(&addr4, 0, sizeof(sockaddr));
    addr4.sin_family = AF_INET;
    addr4.sin_port = htons(port);
409 410 411 412

    addr4.sin_addr.s_addr = htonl(addr);
    //addr4.sin_addr.s_addr = 0; // anyaddr: this will receive 0.0.0.0 => 255.255.255.255 traffic
    // addr4.sin_addr.s_addr = 0xffffffffu; // broadcast address. This will receive 0.0.0.0 => 255.255.255.255 as well
413 414 415 416 417 418 419 420 421 422 423 424

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        isc_throw(Unexpected, "Failed to create UDP6 socket.");
    }

    if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
        close(sock);
        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
                  << "/port=" << port);
    }

425 426
    // if there is no support for IP_PKTINFO, we are really out of luck
    // it will be difficult to undersand, where this packet came from
427
#if defined(IP_PKTINFO)
428
    int flag = 1;
429
    if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
430 431 432 433 434 435 436
        close(sock);
        isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
    }
#endif

    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
        addr.toText() << "/port=" << port << endl;
437

438 439
    SocketInfo info(sock, addr, port);
    iface.addSocket(info);
440

441
    return (sock);
442 443
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
444
int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
445

446 447
    cout << "Creating UDP6 socket on " << iface.getFullName()
         << " " << addr.toText() << "/port=" << port << endl;
448

449
    struct sockaddr_in6 addr6;
450 451 452
    memset(&addr6, 0, sizeof(addr6));
    addr6.sin6_family = AF_INET6;
    addr6.sin6_port = htons(port);
453 454
    if (addr.toText() != "::1")
      addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
455

456
    memcpy(&addr6.sin6_addr,
457
           addr.getAddress().to_v6().to_bytes().data(),
458
           sizeof(addr6.sin6_addr));
459
#ifdef HAVE_SA_LEN
460
    addr6.sin6_len = sizeof(addr6);
461 462
#endif

463
    // TODO: use sockcreator once it becomes available
464 465 466 467

    // make a socket
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0) {
468
        isc_throw(Unexpected, "Failed to create UDP6 socket.");
469 470
    }

471 472
    // Set the REUSEADDR option so that we don't fail to start if
    // we're being restarted.
473 474 475
    int flag = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                   (char *)&flag, sizeof(flag)) < 0) {
476
        close(sock);
477
        isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
478 479
    }

480
    if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
481
        close(sock);
482 483
        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
                  << "/port=" << port);
484 485
    }
#ifdef IPV6_RECVPKTINFO
486
    // RFC3542 - a new way
487 488
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                   &flag, sizeof(flag)) != 0) {
489
        close(sock);
490
        isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
491 492
    }
#else
493
    // RFC2292 - an old way
494 495
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
                   &flag, sizeof(flag)) != 0) {
496
        close(sock);
497
        isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
498 499 500 501
    }
#endif

    // multicast stuff
502
    if (addr.getAddress().to_v6().is_multicast()) {
503
        // both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
504 505
        // are link and site-scoped, so there is no sense to join those groups
        // with global addresses.
506

507
        if ( !joinMulticast( sock, iface.getName(),
508 509
                         string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
            close(sock);
510 511
            isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                      << " multicast group.");
512 513 514
        }
    }

515
    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
516
        addr.toText() << "/port=" << port << endl;
517

518 519
    SocketInfo info(sock, addr, port);
    iface.addSocket(info);
520

521
    return (sock);
522 523
}

524
bool
525
IfaceMgr::joinMulticast(int sock, const std::string& ifname,
526
const std::string & mcast) {
527 528 529

    struct ipv6_mreq mreq;

530 531 532 533 534
    if (inet_pton(AF_INET6, mcast.c_str(),
                  &mreq.ipv6mr_multiaddr) <= 0) {
        cout << "Failed to convert " << ifname
             << " to IPv6 multicast address." << endl;
        return (false);
535 536
    }

537 538 539 540 541
    mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                   &mreq, sizeof(mreq)) < 0) {
        cout << "Failed to join " << mcast << " multicast group." << endl;
        return (false);
542 543
    }

544
    cout << "Joined multicast " << mcast << " group." << endl;
545

546 547
    return (true);
}
548

549
bool
Tomek Mrugalski's avatar
Tomek Mrugalski committed
550
IfaceMgr::send(const Pkt6Ptr& pkt) {
551
    int result;
552

553
    Iface* iface = getIface(pkt->getIface());
554 555
    if (!iface) {
        isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
556
                  << pkt->getIface() << ") specified.");
557 558
    }

559
    memset(&control_buf_[0], 0, control_buf_len_);
560 561


562
    // Set the target address we're sending to.
563 564 565
    sockaddr_in6 to;
    memset(&to, 0, sizeof(to));
    to.sin6_family = AF_INET6;
566
    to.sin6_port = htons(pkt->getRemotePort());
567
    memcpy(&to.sin6_addr,
568
           pkt->getRemoteAddr().getAddress().to_v6().to_bytes().data(),
569
           16);
570
    to.sin6_scope_id = pkt->getIndex();
571

Tomek Mrugalski's avatar
Tomek Mrugalski committed
572 573 574
    // Initialize our message header structure.
    struct msghdr m;
    memset(&m, 0, sizeof(m));
575 576 577
    m.msg_name = &to;
    m.msg_namelen = sizeof(to);

578 579 580
    // 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.)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
581 582 583 584 585 586 587 588

    // As v structure is a C-style is used for both sending and
    // receiving data, it is shared between sending and receiving
    // (sendmsg and recvmsg). It is also defined in system headers,
    // so we have no control over its definition. To set iov_base
    // (defined as void*) we must use const cast from void *.
    // Otherwise C++ compiler would complain that we are trying
    // to assign const void* to void*.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
589 590
    struct iovec v;
    memset(&v, 0, sizeof(v));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
591
    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
592
    v.iov_len = pkt->getBuffer().getLength();
593 594 595
    m.msg_iov = &v;
    m.msg_iovlen = 1;

596 597 598 599 600 601
    // Setting the interface is a bit more involved.
    //
    // We have to create a "control message", and set that to
    // define the IPv6 packet information. We could set the
    // source address if we wanted, but we can safely let the
    // kernel decide what that should be.
602
    m.msg_control = &control_buf_[0];
603
    m.msg_controllen = control_buf_len_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
604
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
605 606
    cmsg->cmsg_level = IPPROTO_IPV6;
    cmsg->cmsg_type = IPV6_PKTINFO;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
607 608
    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
    struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
609
    memset(pktinfo, 0, sizeof(struct in6_pktinfo));
610
    pktinfo->ipi6_ifindex = pkt->getIndex();
611 612
    m.msg_controllen = cmsg->cmsg_len;

613
    result = sendmsg(getSocket(*pkt), &m, 0);
614
    if (result < 0) {
615
        isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
616
    }
617
    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
618
         << " on " << iface->getFullName() << " interface: "
619 620
         << " dst=[" << pkt->getRemoteAddr().toText() << "]:" << pkt->getRemotePort()
         << ", src=" << pkt->getLocalAddr().toText() << "]:" << pkt->getLocalPort()
621
         << endl;
622

623 624
    return (result);
}
625

626
bool
Tomek Mrugalski's avatar
Tomek Mrugalski committed
627
IfaceMgr::send(const Pkt4Ptr& pkt)
628
{
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

    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());

Tomek Mrugalski's avatar
Tomek Mrugalski committed
646 647 648
    struct msghdr m;
    // Initialize our message header structure.
    memset(&m, 0, sizeof(m));
649 650 651
    m.msg_name = &to;
    m.msg_namelen = sizeof(to);

652 653 654
    // 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.)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
655 656 657 658 659
    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());
660 661 662 663
    v.iov_len = pkt->getBuffer().getLength();
    m.msg_iov = &v;
    m.msg_iovlen = 1;

664 665
    // call OS-specific routines (like setting interface index)
    os_send4(m, control_buf_, control_buf_len_, pkt);
666

667 668 669 670 671
    cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
         << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
         << " over socket " << getSocket(*pkt) << " on interface "
         << getIface(pkt->getIface())->getFullName() << endl;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
672
    int result = sendmsg(getSocket(*pkt), &m, 0);
673 674 675 676 677 678 679 680 681 682 683
    if (result < 0) {
        isc_throw(Unexpected, "Pkt4 send failed.");
    }

    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
         << " on " << iface->getFullName() << " interface: "
         << " dst=" << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
         << ", src=" << pkt->getLocalAddr().toText() << ":" << pkt->getLocalPort()
         << endl;

    return (result);
684 685 686
}


687
boost::shared_ptr<Pkt4>
688
IfaceMgr::receive4(unsigned int timeout) {
689

690 691
    const SocketInfo* candidate = 0;
    IfaceCollection::const_iterator iface;
692 693 694 695 696 697 698

    fd_set sockets;
    FD_ZERO(&sockets);
    int maxfd = 0;

    stringstream names;

699 700
    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {

Tomek Mrugalski's avatar
Tomek Mrugalski committed
701 702
        for (SocketCollection::const_iterator s = iface->sockets_.begin();
             s != iface->sockets_.end(); ++s) {
703 704 705 706 707 708

            // We don't want IPv6 addresses here.
            if (s->addr_.getFamily() != AF_INET) {
                continue;
            }

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
            names << s->sockfd_ << "(" << iface->getName() << ") ";

            // add this socket to listening set
            FD_SET(s->sockfd_, &sockets);
            if (maxfd < s->sockfd_)
                maxfd = s->sockfd_;
        }
    }

    // if there is session socket registered...
    if (session_socket_) {
        // at it to the set as well
        FD_SET(session_socket_, &sockets);
        if (maxfd < session_socket_)
            maxfd = session_socket_;
        names << session_socket_ << "(session)";
    }

    /// @todo: implement sub-second precision one day
    struct timeval select_timeout;
    select_timeout.tv_sec = timeout;
    select_timeout.tv_usec = 0;

    cout << "Trying to receive data on sockets: " << names.str()
         << ". Timeout is " << timeout << " seconds." << endl;
    int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
    cout << "select returned " << result << endl;

    if (result == 0) {
        // nothing received and timeout has been reached
        return (Pkt4Ptr()); // NULL
    }
    if (result < 0) {
        char buf[512];
        strncpy(buf, strerror(errno), 512);
        cout << "Socket read error: " << buf << endl;

        /// @todo: perhaps throw here?
        return (Pkt4Ptr()); // NULL
    }

    // Let's find out which socket has the data

    if (session_socket_ && (FD_ISSET(session_socket_, &sockets))) {
        // something received over session socket
        cout << "BIND10 command or config available over session socket." << endl;

        if (session_callback_) {
            // in theory we could call io_service.run_one() here, instead of
            // implementing callback mechanism, but that would introduce
            // asiolink dependency to libdhcp++ and that is something we want
            // to avoid (see CPE market and out long term plans for minimalistic
            // implementations.
            session_callback_();
        }

        return (Pkt4Ptr()); // NULL
    }

    // Let's find out which interface/socket has the data
    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
        for (SocketCollection::const_iterator s = iface->sockets_.begin();
             s != iface->sockets_.end(); ++s) {
            if (FD_ISSET(s->sockfd_, &sockets)) {
773 774 775 776 777 778 779 780 781 782
                candidate = &(*s);
                break;
            }
        }
        if (candidate) {
            break;
        }
    }

    if (!candidate) {
783 784
        cout << "Received data over unknown socket." << endl;
        return (Pkt4Ptr()); // NULL
785 786 787 788 789 790 791 792
    }

    cout << "Trying to receive over UDP4 socket " << candidate->sockfd_ << " bound to "
         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
         << iface->getFullName() << endl;

    // Now we have a socket, let's get some data from it!
    struct sockaddr_in from_addr;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
793
    uint8_t buf[RCVBUFSIZE];
794 795 796 797 798

    memset(&control_buf_[0], 0, control_buf_len_);
    memset(&from_addr, 0, sizeof(from_addr));

    // Initialize our message header structure.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
799
    struct msghdr m;
800 801 802 803 804 805
    memset(&m, 0, sizeof(m));

    // Point so we can get the from address.
    m.msg_name = &from_addr;
    m.msg_namelen = sizeof(from_addr);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
806 807
    struct iovec v;
    v.iov_base = static_cast<void*>(buf);
808 809 810 811
    v.iov_len = RCVBUFSIZE;
    m.msg_iov = &v;
    m.msg_iovlen = 1;

812 813 814 815 816 817
    // 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.
818 819 820
    m.msg_control = &control_buf_[0];
    m.msg_controllen = control_buf_len_;

821
    result = recvmsg(candidate->sockfd_, &m, 0);
822 823
    if (result < 0) {
        cout << "Failed to receive UDP4 data." << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
824
        return (Pkt4Ptr()); // NULL
825 826
    }

827 828
    // We have all data let's create Pkt4 object.
    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
829

830
    unsigned int ifindex = iface->getIndex();
831 832 833 834

    IOAddress from(htonl(from_addr.sin_addr.s_addr));
    uint16_t from_port = htons(from_addr.sin_port);

835 836 837
    // 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.
838
    pkt->setIndex(ifindex);
839
    pkt->setIface(iface->getName());
840
    pkt->setRemoteAddr(from);
841 842 843
    pkt->setRemotePort(from_port);
    pkt->setLocalPort(candidate->port_);

844
    if (!os_receive4(m, pkt)) {
845 846 847 848 849 850
        cout << "Unable to find pktinfo" << endl;
        return (boost::shared_ptr<Pkt4>()); // NULL
    }

    cout << "Received " << result << " bytes from " << from.toText()
         << "/port=" << from_port
851
         << " sent to " << pkt->getLocalAddr().toText() << " over interface "
852 853 854
         << iface->getFullName() << endl;

    return (pkt);
855 856
}

857
Pkt6Ptr IfaceMgr::receive6() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
858
    uint8_t buf[RCVBUFSIZE];
859

860
    memset(&control_buf_[0], 0, control_buf_len_);
861
    struct sockaddr_in6 from;
862 863
    memset(&from, 0, sizeof(from));

864
    // Initialize our message header structure.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
865
    struct msghdr m;
866 867
    memset(&m, 0, sizeof(m));

868
    // Point so we can get the from address.
869 870 871
    m.msg_name = &from;
    m.msg_namelen = sizeof(from);

872 873 874
    // Set the data buffer we're receiving. (Using this wacky
    // "scatter-gather" stuff... but we that doesn't really make
    // sense for us, so we use a single vector entry.)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
875 876 877
    struct iovec v;
    memset(&v, 0, sizeof(v));
    v.iov_base = static_cast<void*>(buf);
878
    v.iov_len = RCVBUFSIZE;
879 880 881
    m.msg_iov = &v;
    m.msg_iovlen = 1;

882 883 884 885 886 887
    // 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.
888
    m.msg_control = &control_buf_[0];
889 890
    m.msg_controllen = control_buf_len_;

891 892 893 894 895
    /// TODO: Need to move to select() and pool over
    /// all available sockets. For now, we just take the
    /// first interface and use first socket from it.
    IfaceCollection::const_iterator iface = ifaces_.begin();
    const SocketInfo* candidate = 0;
896
    while (iface != ifaces_.end()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
897 898
        for (SocketCollection::const_iterator s = iface->sockets_.begin();
             s != iface->sockets_.end(); ++s) {
899 900 901 902 903 904 905 906 907 908
            if (s->addr_.getFamily() != AF_INET6) {
                continue;
            }
            if (s->addr_.getAddress().to_v6().is_multicast()) {
                candidate = &(*s);
                break;
            }
            if (!candidate) {
                candidate = &(*s); // it's not multicast, but it's better than nothing
            }
909
        }
910
        if (candidate) {