// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef FD_COPY #define FD_COPY(orig, copy) \ do { \ memmove(copy, orig, sizeof(fd_set)); \ } while (0) #endif using namespace std; using namespace isc::asiolink; using namespace isc::util; using namespace isc::util::io; using namespace isc::util::io::internal; namespace ph = std::placeholders; namespace isc { namespace dhcp { IfaceMgr& IfaceMgr::instance() { return (*instancePtr()); } const IfaceMgrPtr& IfaceMgr::instancePtr() { static IfaceMgrPtr iface_mgr(new IfaceMgr()); return (iface_mgr); } Iface::Iface(const std::string& name, unsigned int ifindex) : 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), inactive4_(false), inactive6_(false) { // Sanity checks. if (name.empty()) { isc_throw(BadValue, "Interface name must not be empty"); } memset(mac_, 0, sizeof(mac_)); } void Iface::closeSockets() { // Close IPv4 sockets. closeSockets(AF_INET); // Close IPv6 sockets. closeSockets(AF_INET6); } void Iface::closeSockets(const uint16_t family) { // Check that the correct 'family' value has been specified. // The possible values are AF_INET or AF_INET6. Note that, in // the current code they are used to differentiate that the // socket is used to transmit IPv4 or IPv6 traffic. However, // the actual family types of the sockets may be different, // e.g. for LPF we are using raw sockets of AF_PACKET family. // // @todo Consider replacing the AF_INET and AF_INET6 with some // enum which will not be confused with the actual socket type. if ((family != AF_INET) && (family != AF_INET6)) { isc_throw(BadValue, "Invalid socket family " << family << " specified when requested to close all sockets" << " which belong to this family"); } // Search for the socket of the specific type. SocketCollection::iterator sock = sockets_.begin(); while (sock != sockets_.end()) { if (sock->family_ == family) { // Close and delete the socket and move to the // next one. close(sock->sockfd_); // Close fallback socket if open. if (sock->fallbackfd_ >= 0) { close(sock->fallbackfd_); } sockets_.erase(sock++); } else { // Different type of socket. Let's move // to the next one. ++sock; } } } std::string Iface::getFullName() const { ostringstream tmp; tmp << name_ << "/" << ifindex_; return (tmp.str()); } std::string Iface::getPlainMac() const { ostringstream tmp; tmp.fill('0'); tmp << hex; for (int i = 0; i < mac_len_; i++) { tmp.width(2); tmp << static_cast(mac_[i]); if (i < mac_len_-1) { tmp << ":"; } } return (tmp.str()); } void Iface::setMac(const uint8_t* mac, size_t len) { if (len > MAX_MAC_LEN) { isc_throw(OutOfRange, "Interface " << getFullName() << " was detected to have link address of length " << len << ", but maximum supported length is " << MAX_MAC_LEN); } mac_len_ = len; if (len > 0) { memcpy(mac_, mac, len); } } bool Iface::delAddress(const isc::asiolink::IOAddress& addr) { for (AddressCollection::iterator a = addrs_.begin(); a != addrs_.end(); ++a) { if (a->get() == addr) { addrs_.erase(a); return (true); } } return (false); } bool Iface::delSocket(const uint16_t sockfd) { list::iterator sock = sockets_.begin(); while (sock != sockets_.end()) { if (sock->sockfd_ == sockfd) { close(sockfd); // Close fallback socket if open. if (sock->fallbackfd_ >= 0) { close(sock->fallbackfd_); } sockets_.erase(sock); return (true); //socket found } ++sock; } return (false); // socket not found } IfaceMgr::IfaceMgr() : packet_filter_(new PktFilterInet()), packet_filter6_(new PktFilterInet6()), test_mode_(false), allow_loopback_(false) { // Ensure that PQMs have been created to guarantee we have // default packet queues in place. try { packet_queue_mgr4_.reset(new PacketQueueMgr4()); packet_queue_mgr6_.reset(new PacketQueueMgr6()); } catch (const std::exception& ex) { isc_throw(Unexpected, "Failed to create PacketQueueManagers: " << ex.what()); } detect_callback_ = std::bind(&IfaceMgr::checkDetectIfaces, this, ph::_1); try { // required for sending/receiving packets // let's keep it in front, just in case someone // wants to send anything during initialization detectIfaces(); } catch (const std::exception& ex) { isc_throw(IfaceDetectError, ex.what()); } } void Iface::addUnicast(const isc::asiolink::IOAddress& addr) { for (const Address& a : unicasts_) { if (a.get() == addr) { isc_throw(BadValue, "Address " << addr << " already defined on the " << name_ << " interface."); } } unicasts_.push_back(Optional(addr)); } bool Iface::getAddress4(isc::asiolink::IOAddress& address) const { // Iterate over existing addresses assigned to the interface. // Try to find the one that is IPv4. for (const Address& addr : getAddresses()) { // If address is IPv4, we assign it to the function argument // and return true. if (addr.get().isV4()) { address = addr.get(); return (true); } } // There is no IPv4 address assigned to this interface. return (false); } bool Iface::hasAddress(const isc::asiolink::IOAddress& address) const { for (const Address& addr : getAddresses()) { if (address == addr.get()) { return (true); } } return (false); } void Iface::addAddress(const isc::asiolink::IOAddress& addr) { if (!hasAddress(addr)) { addrs_.push_back(Address(addr)); } } void Iface::setActive(const IOAddress& address, const bool active) { for (auto& addr : addrs_) { if (address == addr.get()) { addr.unspecified(!active); return; } } isc_throw(BadValue, "specified address " << address << " was not" " found on the interface " << getName()); } void Iface::setActive(const bool active) { for (auto& addr : addrs_) { addr.unspecified(!active); } } unsigned int Iface::countActive4() const { uint16_t count = 0; for (const Address& addr : addrs_) { if (!addr.unspecified() && addr.get().isV4()) { ++count; } } return (count); } void IfaceMgr::closeSockets() { // Clears bound addresses. clearBoundAddresses(); // Stops the receiver thread if there is one. stopDHCPReceiver(); for (const IfacePtr& iface : ifaces_) { iface->closeSockets(); } } void IfaceMgr::stopDHCPReceiver() { if (isDHCPReceiverRunning()) { dhcp_receiver_->stop(); } dhcp_receiver_.reset(); if (getPacketQueue4()) { getPacketQueue4()->clear(); } if (getPacketQueue6()) { getPacketQueue6()->clear(); } } IfaceMgr::~IfaceMgr() { closeSockets(); } bool IfaceMgr::isDirectResponseSupported() const { return (packet_filter_->isDirectResponseSupported()); } bool IfaceMgr::isSocketReceivedTimeSupported() const { return (packet_filter_->isSocketReceivedTimeSupported()); } void IfaceMgr::addExternalSocket(int socketfd, SocketCallback callback) { if (socketfd < 0) { isc_throw(BadValue, "Attempted to install callback for invalid socket " << socketfd); } std::lock_guard lock(callbacks_mutex_); for (SocketCallbackInfo& s : callbacks_) { // There's such a socket description there already. // Update the callback and we're done if (s.socket_ == socketfd) { s.callback_ = callback; return; } } // Add a new entry to the callbacks vector SocketCallbackInfo x; x.socket_ = socketfd; x.callback_ = callback; callbacks_.push_back(x); } void IfaceMgr::deleteExternalSocket(int socketfd) { std::lock_guard lock(callbacks_mutex_); deleteExternalSocketInternal(socketfd); } void IfaceMgr::deleteExternalSocketInternal(int socketfd) { for (SocketCallbackInfoContainer::iterator s = callbacks_.begin(); s != callbacks_.end(); ++s) { if (s->socket_ == socketfd) { callbacks_.erase(s); return; } } } bool IfaceMgr::isExternalSocket(int fd) { std::lock_guard lock(callbacks_mutex_); for (const SocketCallbackInfo& s : callbacks_) { if (s.socket_ == fd) { return (true); } } return (false); } int IfaceMgr::purgeBadSockets() { std::lock_guard lock(callbacks_mutex_); std::vector bad_fds; for (const SocketCallbackInfo& s : callbacks_) { errno = 0; if (fcntl(s.socket_, F_GETFD) < 0 && (errno == EBADF)) { bad_fds.push_back(s.socket_); } } for (auto bad_fd : bad_fds) { deleteExternalSocketInternal(bad_fd); } return (bad_fds.size()); } void IfaceMgr::deleteAllExternalSockets() { std::lock_guard lock(callbacks_mutex_); callbacks_.clear(); } void IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) { // Do not allow null pointer. if (!packet_filter) { isc_throw(InvalidPacketFilter, "NULL packet filter object specified for" " DHCPv4"); } // Different packet filters use different socket types. It does not make // sense to allow the change of packet filter when there are IPv4 sockets // open because they can't be used by the receive/send functions of the // new packet filter. Below, we check that there are no open IPv4 sockets. // If we find at least one, we have to fail. However, caller still has a // chance to replace the packet filter if he closes sockets explicitly. if (hasOpenSocket(AF_INET)) { // There is at least one socket open, so we have to fail. isc_throw(PacketFilterChangeDenied, "it is not allowed to set new packet" << " filter when there are open IPv4 sockets - need" << " to close them first"); } // Everything is fine, so replace packet filter. packet_filter_ = packet_filter; } void IfaceMgr::setPacketFilter(const PktFilter6Ptr& packet_filter) { if (!packet_filter) { isc_throw(InvalidPacketFilter, "NULL packet filter object specified for" " DHCPv6"); } if (hasOpenSocket(AF_INET6)) { // There is at least one socket open, so we have to fail. isc_throw(PacketFilterChangeDenied, "it is not allowed to set new packet" << " filter when there are open IPv6 sockets - need" << " to close them first"); } packet_filter6_ = packet_filter; } bool IfaceMgr::hasOpenSocket(const uint16_t family) const { // Iterate over all interfaces and search for open sockets. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& sock : iface->getSockets()) { // Check if the socket matches specified family. if (sock.family_ == family) { // There is at least one socket open, so return. return (true); } } } // There are no open sockets found for the specified family. return (false); } bool IfaceMgr::hasOpenSocket(const IOAddress& addr) const { // Fast track for IPv4 using bound addresses. if (addr.isV4() && !bound_address_.empty()) { return (bound_address_.count(addr.toUint32()) != 0); } // Iterate over all interfaces and search for open sockets. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& sock : iface->getSockets()) { // Check if the socket address matches the specified address or // if address is unspecified (in6addr_any). if (sock.addr_ == addr) { return (true); } else if (sock.addr_.isV6Zero()) { // Handle the case that the address is unspecified (any). // This happens only with IPv6 so we do not check IPv4. // In this case, we should check if the specified address // belongs to any of the interfaces. for (const IfacePtr& it : ifaces_) { for (const Iface::Address& a : it->getAddresses()) { if (addr == a.get()) { return (true); } } } // The address does not belongs to any interface. return (false); } } } // There are no open sockets found for the specified family. return (false); } bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, IfaceMgrErrorMsgCallback error_handler, const bool skip_opened) { int count = 0; int bcast_num = 0; for (const IfacePtr& iface : ifaces_) { // Clear any errors from previous socket opening. iface->clearErrors(); // If the interface is inactive, there is nothing to do. Simply // proceed to the next detected interface. if (iface->inactive4_) { continue; } // If the interface has been specified in the configuration that // it should be used to listen the DHCP traffic we have to check // that the interface configuration is valid and that the interface // is not a loopback interface. In both cases, we want to report // that the socket will not be opened. // Relax the check when the loopback interface was explicitly // allowed if (iface->flag_loopback_ && !allow_loopback_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "must not open socket on the loopback" " interface " << iface->getName()); continue; } if (!iface->flag_up_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "the interface " << iface->getName() << " is down"); continue; } if (!iface->flag_running_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "the interface " << iface->getName() << " is not running"); continue; } IOAddress out_address("0.0.0.0"); if (!iface->getAddress4(out_address)) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "the interface " << iface->getName() << " has no usable IPv4 addresses configured"); continue; } for (const Iface::Address& addr : iface->getAddresses()) { // Skip non-IPv4 addresses and those that weren't selected.. if (addr.unspecified() || !addr.get().isV4()) { continue; } // If selected interface is broadcast capable set appropriate // options on the socket so as it can receive and send broadcast // messages. bool is_open_as_broadcast = iface->flag_broadcast_ && use_bcast; // The DHCP server must have means to determine which interface // the broadcast packets are coming from. This is achieved by // binding a socket to the device (interface) and specialized // packet filters (e.g. BPF and LPF) implement this mechanism. // If the PktFilterInet (generic one) is used, the socket is // bound to INADDR_ANY which effectively binds the socket to // all addresses on all interfaces. So, only one of those can // be opened. Currently, the direct response support is // provided by the PktFilterLPF and PktFilterBPF, so by checking // the support for direct response we actually determine that // one of those objects is in use. For all other objects we // assume that binding to the device is not supported and we // cease opening sockets and display the appropriate message. if (is_open_as_broadcast && !isDirectResponseSupported() && bcast_num > 0) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "Binding socket to an interface is not" " supported on this OS; therefore only" " one socket listening to broadcast traffic" " can be opened. Sockets will not be opened" " on remaining interfaces"); continue; } // Skip the address that already has a bound socket. It allows // for preventing bind errors or re-opening sockets. if (!skip_opened || !IfaceMgr::hasOpenSocket(addr.get())) { try { // We haven't open any broadcast sockets yet, so we can // open at least one more or // not broadcast capable, do not set broadcast flags. IfaceMgr::openSocket(iface->getName(), addr.get(), port, is_open_as_broadcast, is_open_as_broadcast); } catch (const Exception& ex) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "Failed to open socket on interface " << iface->getName() << ", reason: " << ex.what()); continue; } } if (is_open_as_broadcast) { // Binding socket to an interface is not supported so we // can't open any more broadcast sockets. Increase the // number of open broadcast sockets. ++bcast_num; } ++count; } } // If we have open sockets, start the receiver. if (count > 0) { // Collects bound addresses. collectBoundAddresses(); // Starts the receiver thread (if queueing is enabled). startDHCPReceiver(AF_INET); } return (count > 0); } bool IfaceMgr::openSockets6(const uint16_t port, IfaceMgrErrorMsgCallback error_handler, const bool skip_opened) { int count = 0; for (const IfacePtr& iface : ifaces_) { // Clear any errors from previous socket opening. iface->clearErrors(); // If the interface is inactive, there is nothing to do. Simply // proceed to the next detected interface. if (iface->inactive6_) { continue; } // If the interface has been specified in the configuration that // it should be used to listen the DHCP traffic we have to check // that the interface configuration is valid and that the interface // is not a loopback interface. In both cases, we want to report // that the socket will not be opened. // Relax the check when the loopback interface was explicitly // allowed if (iface->flag_loopback_ && !allow_loopback_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "must not open socket on the loopback" " interface " << iface->getName()); continue; } else if (!iface->flag_up_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "the interface " << iface->getName() << " is down"); continue; } else if (!iface->flag_running_) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "the interface " << iface->getName() << " is not running"); continue; } // Open unicast sockets if there are any unicast addresses defined for (const Iface::Address& addr : iface->getUnicasts()) { // Skip the address that already has a bound socket. It allows // for preventing bind errors or re-opening sockets. // The @ref IfaceMgr::hasOpenSocket(addr) does match the "::" // address on BSD and Solaris on any interface, so we make sure that // that interface actually has opened sockets by checking the number // of sockets to be non zero. if (!skip_opened || !IfaceMgr::hasOpenSocket(addr) || !iface->getSockets().size()) { try { IfaceMgr::openSocket(iface->getName(), addr, port, false, false); } catch (const Exception& ex) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "Failed to open unicast socket on interface " << iface->getName() << ", reason: " << ex.what()); continue; } } count++; } for (const Iface::Address& addr : iface->getAddresses()) { // Skip all but V6 addresses. if (!addr.get().isV6()) { continue; } // Bind link-local addresses only. Otherwise we bind several sockets // on interfaces that have several global addresses. For examples // with interface with 2 global addresses, we would bind 3 sockets // (one for link-local and two for global). That would result in // getting each message 3 times. if (!addr.get().isV6LinkLocal()){ continue; } // Run OS-specific function to open a socket capable of receiving // packets sent to All_DHCP_Relay_Agents_and_Servers multicast // address. // Skip the address that already has a bound socket. It allows // for preventing bind errors or re-opening sockets. // The @ref IfaceMgr::hasOpenSocket(addr) does match the "::" // address on BSD and Solaris on any interface, so we make sure that // the interface actually has opened sockets by checking the number // of sockets to be non zero. if (!skip_opened || !IfaceMgr::hasOpenSocket(addr) || !iface->getSockets().size()) { try { // Pass a null pointer as an error handler to avoid // suppressing an exception in a system-specific function. IfaceMgr::openMulticastSocket(*iface, addr, port); } catch (const Exception& ex) { IFACEMGR_ERROR(SocketConfigError, error_handler, iface, "Failed to open multicast socket on interface " << iface->getName() << ", reason: " << ex.what()); continue; } } ++count; } } // If we have open sockets, start the receiver. if (count > 0) { // starts the receiver thread (if queueing is enabled). startDHCPReceiver(AF_INET6); } return (count > 0); } void IfaceMgr::startDHCPReceiver(const uint16_t family) { if (isDHCPReceiverRunning()) { isc_throw(InvalidOperation, "a receiver thread already exists"); } switch (family) { case AF_INET: // If the queue doesn't exist, packet queing has been configured // as disabled. If there is no queue, we do not create a receiver. if(!getPacketQueue4()) { return; } dhcp_receiver_.reset(new WatchedThread()); dhcp_receiver_->start(std::bind(&IfaceMgr::receiveDHCP4Packets, this)); break; case AF_INET6: // If the queue doesn't exist, packet queing has been configured // as disabled. If there is no queue, we do not create a receiver. if(!getPacketQueue6()) { return; } dhcp_receiver_.reset(new WatchedThread()); dhcp_receiver_->start(std::bind(&IfaceMgr::receiveDHCP6Packets, this)); break; default: isc_throw (BadValue, "startDHCPReceiver: invalid family: " << family); break; } } void IfaceMgr::addInterface(const IfacePtr& iface) { for (const IfacePtr& existing : ifaces_) { if ((existing->getName() == iface->getName()) || (existing->getIndex() == iface->getIndex())) { isc_throw(Unexpected, "Can't add " << iface->getFullName() << " when " << existing->getFullName() << " already exists."); } } ifaces_.push_back(iface); } void IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) { for (const IfacePtr& iface : ifaces_) { const Iface::AddressCollection& addrs = iface->getAddresses(); out << "Detected interface " << iface->getFullName() << ", hwtype=" << iface->getHWType() << ", mac=" << iface->getPlainMac(); out << ", flags=" << hex << iface->flags_ << dec << "(" << (iface->flag_loopback_?"LOOPBACK ":"") << (iface->flag_up_?"UP ":"") << (iface->flag_running_?"RUNNING ":"") << (iface->flag_multicast_?"MULTICAST ":"") << (iface->flag_broadcast_?"BROADCAST ":"") << ")" << endl; out << " " << addrs.size() << " addr(s):"; for (const Iface::Address& addr : addrs) { out << " " << addr.get().toText(); } out << endl; } } IfacePtr IfaceCollection::getIface(const unsigned int ifindex) { return (getIfaceInternal(ifindex, MultiThreadingMgr::instance().getMode())); } IfacePtr IfaceCollection::getIface(const std::string& ifname) { return (getIfaceInternal(ifname, MultiThreadingMgr::instance().getMode())); } IfacePtr IfaceCollection::getIfaceInternal(const unsigned int ifindex, const bool need_lock) { if (ifindex == UNSET_IFINDEX) { isc_throw(BadValue, "interface index was not set"); } if (need_lock) { lock_guard lock(mutex_); if (cache_ && (cache_->getIndex() == ifindex)) { return (cache_); } } else { if (cache_ && (cache_->getIndex() == ifindex)) { return (cache_); } } auto const& idx = ifaces_container_.get<1>(); auto it = idx.find(ifindex); if (it == idx.end()) { return (IfacePtr()); // not found } if (need_lock) { lock_guard lock(mutex_); cache_ = *it; return (cache_); } else { cache_ = *it; return (cache_); } } IfacePtr IfaceCollection::getIfaceInternal(const std::string& ifname, const bool need_lock) { if (need_lock) { lock_guard lock(mutex_); if (cache_ && (cache_->getName() == ifname)) { return (cache_); } } else { if (cache_ && (cache_->getName() == ifname)) { return (cache_); } } auto const& idx = ifaces_container_.get<2>(); auto it = idx.find(ifname); if (it == idx.end()) { return (IfacePtr()); // not found } if (need_lock) { lock_guard lock(mutex_); cache_ = *it; return (cache_); } else { cache_ = *it; return (cache_); } } IfacePtr IfaceMgr::getIface(const unsigned int ifindex) { return (ifaces_.getIface(ifindex)); } IfacePtr IfaceMgr::getIface(const std::string& ifname) { if (ifname.empty()) { return (IfacePtr()); // empty } return (ifaces_.getIface(ifname)); } IfacePtr IfaceMgr::getIface(const PktPtr& pkt) { if (pkt->indexSet()) { return (getIface(pkt->getIndex())); } else { return (getIface(pkt->getIface())); } } void IfaceMgr::clearIfaces() { ifaces_.clear(); } void IfaceMgr::clearBoundAddresses() { bound_address_.clear(); } void IfaceMgr::collectBoundAddresses() { for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& sock : iface->getSockets()) { const IOAddress& addr = sock.addr_; if (!addr.isV4()) { continue; } if (bound_address_.count(addr.toUint32()) == 0) { bound_address_.insert(addr); } } } } void IfaceMgr::clearUnicasts() { for (const IfacePtr& iface : ifaces_) { iface->clearUnicasts(); } } int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr, const uint16_t port, const bool receive_bcast, const bool send_bcast) { IfacePtr iface = getIface(ifname); if (!iface) { isc_throw(BadValue, "There is no " << ifname << " interface present."); } if (addr.isV4()) { return openSocket4(*iface, addr, port, receive_bcast, send_bcast); } else if (addr.isV6()) { return openSocket6(*iface, addr, port, receive_bcast); } else { isc_throw(BadValue, "Failed to detect family of address: " << addr); } } int IfaceMgr::openSocketFromIface(const std::string& ifname, const uint16_t port, const uint8_t family) { // Search for specified interface among detected interfaces. for (const IfacePtr& iface : ifaces_) { if ((iface->getFullName() != ifname) && (iface->getName() != ifname)) { continue; } // Interface is now detected. Search for address on interface // that matches address family (v6 or v4). Iface::AddressCollection addrs = iface->getAddresses(); Iface::AddressCollection::iterator addr_it = addrs.begin(); while (addr_it != addrs.end()) { if (addr_it->get().getFamily() == family) { // We have interface and address so let's open socket. // This may cause isc::Unexpected exception. return (openSocket(iface->getName(), *addr_it, port, false)); } ++addr_it; } // If we are at the end of address collection it means that we found // interface but there is no address for family specified. if (addr_it == addrs.end()) { // Stringify the family value to append it to exception string. std::string family_name("AF_INET"); if (family == AF_INET6) { family_name = "AF_INET6"; } // We did not find address on the interface. isc_throw(SocketConfigError, "There is no address for interface: " << ifname << ", port: " << port << ", address " " family: " << family_name); } } // If we got here it means that we had not found the specified interface. // Otherwise we would have returned from previous exist points. isc_throw(BadValue, "There is no " << ifname << " interface present."); } int IfaceMgr::openSocketFromAddress(const IOAddress& addr, const uint16_t port) { // Search through detected interfaces and addresses to match // local address we got. for (const IfacePtr& iface : ifaces_) { for (const Iface::Address& a : iface->getAddresses()) { // Local address must match one of the addresses // on detected interfaces. If it does, we have // address and interface detected so we can open // socket. if (a.get() == addr) { // Open socket using local interface, address and port. // This may cause isc::Unexpected exception. return (openSocket(iface->getName(), a, port, false)); } } } // If we got here it means that we did not find specified address // on any available interface. isc_throw(BadValue, "There is no such address " << addr); } int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr, const uint16_t port) { try { // Get local address to be used to connect to remote location. IOAddress local_address(getLocalAddress(remote_addr, port)); return openSocketFromAddress(local_address, port); } catch (const Exception& e) { isc_throw(SocketConfigError, e.what()); } } isc::asiolink::IOAddress IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) { // Create remote endpoint, we will be connecting to it. boost::scoped_ptr remote_endpoint(static_cast (UDPEndpoint::create(IPPROTO_UDP, remote_addr, port))); if (!remote_endpoint) { isc_throw(Unexpected, "Unable to create remote endpoint"); } // Create socket that will be used to connect to remote endpoint. boost::asio::io_service io_service; boost::asio::ip::udp::socket sock(io_service); boost::system::error_code err_code; // If remote address is broadcast address we have to // allow this on the socket. if (remote_addr.isV4() && (remote_addr == IOAddress(DHCP_IPV4_BROADCAST_ADDRESS))) { // Socket has to be open prior to setting the broadcast // option. Otherwise set_option will complain about // bad file descriptor. // @todo: We don't specify interface in any way here. 255.255.255.255 // We can very easily end up with a socket working on a different // interface. // zero out the errno to be safe errno = 0; sock.open(boost::asio::ip::udp::v4(), err_code); if (err_code) { const char* errstr = strerror(errno); isc_throw(Unexpected, "failed to open UDPv4 socket, reason:" << errstr); } sock.set_option(boost::asio::socket_base::broadcast(true), err_code); if (err_code) { sock.close(); isc_throw(Unexpected, "failed to enable broadcast on the socket"); } } // Try to connect to remote endpoint and check if attempt is successful. sock.connect(remote_endpoint->getASIOEndpoint(), err_code); if (err_code) { sock.close(); isc_throw(Unexpected, "failed to connect to remote endpoint."); } // Once we are connected socket object holds local endpoint. boost::asio::ip::udp::socket::endpoint_type local_endpoint = sock.local_endpoint(); boost::asio::ip::address local_address(local_endpoint.address()); // Close the socket. sock.close(); // Return address of local endpoint. return IOAddress(local_address); } int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, const uint16_t port, const bool receive_bcast, const bool send_bcast) { // Assuming that packet filter is not null, because its modifier checks it. SocketInfo info = packet_filter_->openSocket(iface, addr, port, receive_bcast, send_bcast); iface.addSocket(info); return (info.sockfd_); } bool IfaceMgr::send(const Pkt6Ptr& pkt) { IfacePtr iface = getIface(pkt); if (!iface) { isc_throw(BadValue, "Unable to send DHCPv6 message. Invalid interface (" << pkt->getIface() << ") specified."); } // Assuming that packet filter is not null, because its modifier checks it. // The packet filter returns an int but in fact it either returns 0 or throws. return (packet_filter6_->send(*iface, getSocket(pkt), pkt) == 0); } bool IfaceMgr::send(const Pkt4Ptr& pkt) { IfacePtr iface = getIface(pkt); if (!iface) { isc_throw(BadValue, "Unable to send DHCPv4 message. Invalid interface (" << pkt->getIface() << ") specified."); } // Assuming that packet filter is not null, because its modifier checks it. // The packet filter returns an int but in fact it either returns 0 or throws. return (packet_filter_->send(*iface, getSocket(pkt).sockfd_, pkt) == 0); } Pkt4Ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { if (isDHCPReceiverRunning()) { return (receive4Indirect(timeout_sec, timeout_usec)); } return (receive4Direct(timeout_sec, timeout_usec)); } Pkt4Ptr IfaceMgr::receive4Indirect(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" " one million microseconds"); } fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); // if there are any callbacks for external sockets registered... { std::lock_guard lock(callbacks_mutex_); if (!callbacks_.empty()) { for (const SocketCallbackInfo& s : callbacks_) { // Add this socket to listening set addFDtoSet(s.socket_, maxfd, &sockets); } } } // Add Receiver ready watch socket addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::READY), maxfd, &sockets); // Add Receiver error watch socket addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::ERROR), maxfd, &sockets); // Set timeout for our next select() call. If there are // no DHCP packets to read, then we'll wait for a finite // amount of time for an IO event. Otherwise, we'll // poll (timeout = 0 secs). We need to poll, even if // DHCP packets are waiting so we don't starve external // sockets under heavy DHCP load. struct timeval select_timeout; if (getPacketQueue4()->empty()) { select_timeout.tv_sec = timeout_sec; select_timeout.tv_usec = timeout_usec; } else { select_timeout.tv_sec = 0; select_timeout.tv_usec = 0; } // zero out the errno to be safe errno = 0; int result = select(maxfd + 1, &sockets, 0, 0, &select_timeout); if ((result == 0) && getPacketQueue4()->empty()) { // nothing received and timeout has been reached return (Pkt4Ptr()); } else if (result < 0) { // In most cases we would like to know whether select() returned // an error because of a signal being received or for some other // reason. This is because DHCP servers use signals to trigger // certain actions, like reconfiguration or graceful shutdown. // By catching a dedicated exception the caller will know if the // error returned by the function is due to the reception of the // signal or for some other reason. if (errno == EINTR) { isc_throw(SignalInterruptOnSelect, strerror(errno)); } else if (errno == EBADF) { int cnt = purgeBadSockets(); isc_throw(SocketReadError, "SELECT interrupted by one invalid sockets, purged " << cnt << " socket descriptors"); } else { isc_throw(SocketReadError, strerror(errno)); } } // We only check external sockets if select detected an event. if (result > 0) { // Check for receiver thread read errors. if (dhcp_receiver_->isReady(WatchedThread::ERROR)) { string msg = dhcp_receiver_->getLastError(); dhcp_receiver_->clearReady(WatchedThread::ERROR); isc_throw(SocketReadError, msg); } // Let's find out which external socket has the data SocketCallbackInfo ex_sock; bool found = false; { std::lock_guard lock(callbacks_mutex_); for (const SocketCallbackInfo& s : callbacks_) { if (!FD_ISSET(s.socket_, &sockets)) { continue; } found = true; // something received over external socket if (s.callback_) { // Note the external socket to call its callback without // the lock taken so it can be deleted. ex_sock = s; break; } } } if (ex_sock.callback_) { // Calling the external socket's callback provides its service // layer access without integrating any specific features // in IfaceMgr ex_sock.callback_(ex_sock.socket_); } if (found) { return (Pkt4Ptr()); } } // If we're here it should only be because there are DHCP packets waiting. Pkt4Ptr pkt = getPacketQueue4()->dequeuePacket(); if (!pkt) { dhcp_receiver_->clearReady(WatchedThread::READY); } return (pkt); } Pkt4Ptr IfaceMgr::receive4Direct(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" " one million microseconds"); } boost::scoped_ptr candidate; fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); /// @todo: marginal performance optimization. We could create the set once /// and then use its copy for select(). Please note that select() modifies /// provided set to indicated which sockets have something to read. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { // Only deal with IPv4 addresses. if (s.addr_.isV4()) { // Add this socket to listening set addFDtoSet(s.sockfd_, maxfd, &sockets); } } } // if there are any callbacks for external sockets registered... { std::lock_guard lock(callbacks_mutex_); if (!callbacks_.empty()) { for (const SocketCallbackInfo& s : callbacks_) { // Add this socket to listening set addFDtoSet(s.socket_, maxfd, &sockets); } } } struct timeval select_timeout; select_timeout.tv_sec = timeout_sec; select_timeout.tv_usec = timeout_usec; // zero out the errno to be safe errno = 0; int result = select(maxfd + 1, &sockets, 0, 0, &select_timeout); if (result == 0) { // nothing received and timeout has been reached return (Pkt4Ptr()); // null } else if (result < 0) { // In most cases we would like to know whether select() returned // an error because of a signal being received or for some other // reason. This is because DHCP servers use signals to trigger // certain actions, like reconfiguration or graceful shutdown. // By catching a dedicated exception the caller will know if the // error returned by the function is due to the reception of the // signal or for some other reason. if (errno == EINTR) { isc_throw(SignalInterruptOnSelect, strerror(errno)); } else if (errno == EBADF) { int cnt = purgeBadSockets(); isc_throw(SocketReadError, "SELECT interrupted by one invalid sockets, purged " << cnt << " socket descriptors"); } else { isc_throw(SocketReadError, strerror(errno)); } } // Let's find out which socket has the data SocketCallbackInfo ex_sock; bool found = false; { std::lock_guard lock(callbacks_mutex_); for (const SocketCallbackInfo& s : callbacks_) { if (!FD_ISSET(s.socket_, &sockets)) { continue; } found = true; // something received over external socket if (s.callback_) { // Note the external socket to call its callback without // the lock taken so it can be deleted. ex_sock = s; break; } } } if (ex_sock.callback_) { // Calling the external socket's callback provides its service // layer access without integrating any specific features // in IfaceMgr ex_sock.callback_(ex_sock.socket_); } if (found) { return (Pkt4Ptr()); } // Let's find out which interface/socket has the data IfacePtr recv_if; for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { candidate.reset(new SocketInfo(s)); break; } } if (candidate) { recv_if = iface; break; } } if (!candidate || !recv_if) { isc_throw(SocketReadError, "received data over unknown socket"); } // Now we have a socket, let's get some data from it! // Assuming that packet filter is not null, because its modifier checks it. return (packet_filter_->receive(*recv_if, *candidate)); } Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { if (isDHCPReceiverRunning()) { return (receive6Indirect(timeout_sec, timeout_usec)); } return (receive6Direct(timeout_sec, timeout_usec)); } void IfaceMgr::addFDtoSet(int fd, int& maxfd, fd_set* sockets) { if (!sockets) { isc_throw(BadValue, "addFDtoSet: sockets can't be null"); } FD_SET(fd, sockets); if (maxfd < fd) { maxfd = fd; } } Pkt6Ptr IfaceMgr::receive6Direct(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" " one million microseconds"); } boost::scoped_ptr candidate; fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); /// @todo: marginal performance optimization. We could create the set once /// and then use its copy for select(). Please note that select() modifies /// provided set to indicated which sockets have something to read. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { // Only deal with IPv6 addresses. if (s.addr_.isV6()) { // Add this socket to listening set addFDtoSet(s.sockfd_, maxfd, &sockets); } } } // if there are any callbacks for external sockets registered... { std::lock_guard lock(callbacks_mutex_); if (!callbacks_.empty()) { for (const SocketCallbackInfo& s : callbacks_) { // Add this socket to listening set addFDtoSet(s.socket_, maxfd, &sockets); } } } struct timeval select_timeout; select_timeout.tv_sec = timeout_sec; select_timeout.tv_usec = timeout_usec; // zero out the errno to be safe errno = 0; int result = select(maxfd + 1, &sockets, 0, 0, &select_timeout); if (result == 0) { // nothing received and timeout has been reached return (Pkt6Ptr()); // null } else if (result < 0) { // In most cases we would like to know whether select() returned // an error because of a signal being received or for some other // reason. This is because DHCP servers use signals to trigger // certain actions, like reconfiguration or graceful shutdown. // By catching a dedicated exception the caller will know if the // error returned by the function is due to the reception of the // signal or for some other reason. if (errno == EINTR) { isc_throw(SignalInterruptOnSelect, strerror(errno)); } else if (errno == EBADF) { int cnt = purgeBadSockets(); isc_throw(SocketReadError, "SELECT interrupted by one invalid sockets, purged " << cnt << " socket descriptors"); } else { isc_throw(SocketReadError, strerror(errno)); } } // Let's find out which socket has the data SocketCallbackInfo ex_sock; bool found = false; { std::lock_guard lock(callbacks_mutex_); for (const SocketCallbackInfo& s : callbacks_) { if (!FD_ISSET(s.socket_, &sockets)) { continue; } found = true; // something received over external socket if (s.callback_) { // Note the external socket to call its callback without // the lock taken so it can be deleted. ex_sock = s; break; } } } if (ex_sock.callback_) { // Calling the external socket's callback provides its service // layer access without integrating any specific features // in IfaceMgr ex_sock.callback_(ex_sock.socket_); } if (found) { return (Pkt6Ptr()); } // Let's find out which interface/socket has the data for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { candidate.reset(new SocketInfo(s)); break; } } if (candidate) { break; } } if (!candidate) { isc_throw(SocketReadError, "received data over unknown socket"); } // Assuming that packet filter is not null, because its modifier checks it. return (packet_filter6_->receive(*candidate)); } Pkt6Ptr IfaceMgr::receive6Indirect(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" " one million microseconds"); } fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); // if there are any callbacks for external sockets registered... { std::lock_guard lock(callbacks_mutex_); if (!callbacks_.empty()) { for (const SocketCallbackInfo& s : callbacks_) { // Add this socket to listening set addFDtoSet(s.socket_, maxfd, &sockets); } } } // Add Receiver ready watch socket addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::READY), maxfd, &sockets); // Add Receiver error watch socket addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::ERROR), maxfd, &sockets); // Set timeout for our next select() call. If there are // no DHCP packets to read, then we'll wait for a finite // amount of time for an IO event. Otherwise, we'll // poll (timeout = 0 secs). We need to poll, even if // DHCP packets are waiting so we don't starve external // sockets under heavy DHCP load. struct timeval select_timeout; if (getPacketQueue6()->empty()) { select_timeout.tv_sec = timeout_sec; select_timeout.tv_usec = timeout_usec; } else { select_timeout.tv_sec = 0; select_timeout.tv_usec = 0; } // zero out the errno to be safe errno = 0; int result = select(maxfd + 1, &sockets, 0, 0, &select_timeout); if ((result == 0) && getPacketQueue6()->empty()) { // nothing received and timeout has been reached return (Pkt6Ptr()); } else if (result < 0) { // In most cases we would like to know whether select() returned // an error because of a signal being received or for some other // reason. This is because DHCP servers use signals to trigger // certain actions, like reconfiguration or graceful shutdown. // By catching a dedicated exception the caller will know if the // error returned by the function is due to the reception of the // signal or for some other reason. if (errno == EINTR) { isc_throw(SignalInterruptOnSelect, strerror(errno)); } else if (errno == EBADF) { int cnt = purgeBadSockets(); isc_throw(SocketReadError, "SELECT interrupted by one invalid sockets, purged " << cnt << " socket descriptors"); } else { isc_throw(SocketReadError, strerror(errno)); } } // We only check external sockets if select detected an event. if (result > 0) { // Check for receiver thread read errors. if (dhcp_receiver_->isReady(WatchedThread::ERROR)) { string msg = dhcp_receiver_->getLastError(); dhcp_receiver_->clearReady(WatchedThread::ERROR); isc_throw(SocketReadError, msg); } // Let's find out which external socket has the data SocketCallbackInfo ex_sock; bool found = false; { std::lock_guard lock(callbacks_mutex_); for (const SocketCallbackInfo& s : callbacks_) { if (!FD_ISSET(s.socket_, &sockets)) { continue; } found = true; // something received over external socket if (s.callback_) { // Note the external socket to call its callback without // the lock taken so it can be deleted. ex_sock = s; break; } } } if (ex_sock.callback_) { // Calling the external socket's callback provides its service // layer access without integrating any specific features // in IfaceMgr ex_sock.callback_(ex_sock.socket_); } if (found) { return (Pkt6Ptr()); } } // If we're here it should only be because there are DHCP packets waiting. Pkt6Ptr pkt = getPacketQueue6()->dequeuePacket(); if (!pkt) { dhcp_receiver_->clearReady(WatchedThread::READY); } return (pkt); } void IfaceMgr::receiveDHCP4Packets() { fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); // Add terminate watch socket. addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::TERMINATE), maxfd, &sockets); // Add Interface sockets. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { // Only deal with IPv4 addresses. if (s.addr_.isV4()) { // Add this socket to listening set. addFDtoSet(s.sockfd_, maxfd, &sockets); } } } for (;;) { // Check the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } fd_set rd_set; FD_COPY(&sockets, &rd_set); // zero out the errno to be safe. errno = 0; // Select with null timeouts to wait indefinitely an event int result = select(maxfd + 1, &rd_set, 0, 0, 0); // Re-check the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } if (result == 0) { // nothing received? continue; } else if (result < 0) { // This thread should not get signals? if (errno != EINTR) { // Signal the error to receive4. dhcp_receiver_->setError(strerror(errno)); // We need to sleep in case of the error condition to // prevent the thread from tight looping when result // gets negative. sleep(1); } continue; } // Let's find out which interface/socket has data. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { receiveDHCP4Packet(*iface, s); // Can take time so check one more time the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } } } } } } void IfaceMgr::receiveDHCP6Packets() { fd_set sockets; int maxfd = 0; FD_ZERO(&sockets); // Add terminate watch socket. addFDtoSet(dhcp_receiver_->getWatchFd(WatchedThread::TERMINATE), maxfd, &sockets); // Add Interface sockets. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { // Only deal with IPv6 addresses. if (s.addr_.isV6()) { // Add this socket to listening set. addFDtoSet(s.sockfd_ , maxfd, &sockets); } } } for (;;) { // Check the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } fd_set rd_set; FD_COPY(&sockets, &rd_set); // zero out the errno to be safe. errno = 0; // Note we wait until something happen. int result = select(maxfd + 1, &rd_set, 0, 0, 0); // Re-check the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } if (result == 0) { // nothing received? continue; } else if (result < 0) { // This thread should not get signals? if (errno != EINTR) { // Signal the error to receive6. dhcp_receiver_->setError(strerror(errno)); // We need to sleep in case of the error condition to // prevent the thread from tight looping when result // gets negative. sleep(1); } continue; } // Let's find out which interface/socket has data. for (const IfacePtr& iface : ifaces_) { for (const SocketInfo& s : iface->getSockets()) { if (FD_ISSET(s.sockfd_, &sockets)) { receiveDHCP6Packet(s); // Can take time so check one more time the watch socket. if (dhcp_receiver_->shouldTerminate()) { return; } } } } } } void IfaceMgr::receiveDHCP4Packet(Iface& iface, const SocketInfo& socket_info) { int len; int result = ioctl(socket_info.sockfd_, FIONREAD, &len); if (result < 0) { // Signal the error to receive4. dhcp_receiver_->setError(strerror(errno)); return; } if (len == 0) { // Nothing to read. return; } Pkt4Ptr pkt; try { pkt = packet_filter_->receive(iface, socket_info); } catch (const std::exception& ex) { dhcp_receiver_->setError(strerror(errno)); } catch (...) { dhcp_receiver_->setError("packet filter receive() failed"); } if (pkt) { getPacketQueue4()->enqueuePacket(pkt, socket_info); dhcp_receiver_->markReady(WatchedThread::READY); } } void IfaceMgr::receiveDHCP6Packet(const SocketInfo& socket_info) { int len; int result = ioctl(socket_info.sockfd_, FIONREAD, &len); if (result < 0) { // Signal the error to receive6. dhcp_receiver_->setError(strerror(errno)); return; } if (len == 0) { // Nothing to read. return; } Pkt6Ptr pkt; try { pkt = packet_filter6_->receive(socket_info); } catch (const std::exception& ex) { dhcp_receiver_->setError(ex.what()); } catch (...) { dhcp_receiver_->setError("packet filter receive() failed"); } if (pkt) { getPacketQueue6()->enqueuePacket(pkt, socket_info); dhcp_receiver_->markReady(WatchedThread::READY); } } uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6Ptr& pkt) { IfacePtr iface = getIface(pkt); if (!iface) { isc_throw(IfaceNotFound, "Tried to find socket for non-existent interface"); } const Iface::SocketCollection& socket_collection = iface->getSockets(); Iface::SocketCollection::const_iterator candidate = socket_collection.end(); Iface::SocketCollection::const_iterator s; for (s = socket_collection.begin(); s != socket_collection.end(); ++s) { // We should not merge those conditions for debugging reasons. // V4 sockets are useless for sending v6 packets. if (s->family_ != AF_INET6) { continue; } // Sockets bound to multicast address are useless for sending anything. if (s->addr_.isV6Multicast()) { continue; } if (s->addr_ == pkt->getLocalAddr()) { // This socket is bound to the source address. This is perfect // match, no need to look any further. return (s->sockfd_); } // If we don't have any other candidate, this one will do if (candidate == socket_collection.end()) { candidate = s; } else { // If we want to send something to link-local and the socket is // bound to link-local or we want to send to global and the socket // is bound to global, then use it as candidate if ( (pkt->getRemoteAddr().isV6LinkLocal() && s->addr_.isV6LinkLocal()) || (!pkt->getRemoteAddr().isV6LinkLocal() && !s->addr_.isV6LinkLocal()) ) { candidate = s; } } } if (candidate != socket_collection.end()) { return (candidate->sockfd_); } isc_throw(SocketNotFound, "Interface " << iface->getFullName() << " does not have any suitable IPv6 sockets open."); } SocketInfo IfaceMgr::getSocket(const isc::dhcp::Pkt4Ptr& pkt) { IfacePtr iface = getIface(pkt); if (!iface) { isc_throw(IfaceNotFound, "Tried to find socket for non-existent interface"); } const Iface::SocketCollection& socket_collection = iface->getSockets(); // A candidate being an end of the iterator marks that it is a beginning of // the socket search and that the candidate needs to be set to the first // socket found. Iface::SocketCollection::const_iterator candidate = socket_collection.end(); Iface::SocketCollection::const_iterator s; for (s = socket_collection.begin(); s != socket_collection.end(); ++s) { if (s->family_ == AF_INET) { if (s->addr_ == pkt->getLocalAddr()) { return (*s); } if (candidate == socket_collection.end()) { candidate = s; } } } if (candidate == socket_collection.end()) { isc_throw(SocketNotFound, "Interface " << iface->getFullName() << " does not have any suitable IPv4 sockets open."); } return (*candidate); } bool IfaceMgr::configureDHCPPacketQueue(uint16_t family, data::ConstElementPtr queue_control) { if (isDHCPReceiverRunning()) { isc_throw(InvalidOperation, "Cannot reconfigure queueing" " while DHCP receiver thread is running"); } bool enable_queue = false; if (queue_control) { try { enable_queue = data::SimpleParser::getBoolean(queue_control, "enable-queue"); } catch (...) { // @todo - for now swallow not found errors. // if not present we assume default } } if (enable_queue) { // Try to create the queue as configured. if (family == AF_INET) { packet_queue_mgr4_->createPacketQueue(queue_control); } else { packet_queue_mgr6_->createPacketQueue(queue_control); } } else { // Destroy the current queue (if one), this inherently disables threading. if (family == AF_INET) { packet_queue_mgr4_->destroyPacketQueue(); } else { packet_queue_mgr6_->destroyPacketQueue(); } } return (enable_queue); } void Iface::addError(std::string const& message) { errors_.push_back(message); } void Iface::clearErrors() { errors_.clear(); } Iface::ErrorBuffer const& Iface::getErrors() const { return errors_; } bool IfaceMgr::checkDetectIfaces(bool update_only) { if (test_mode_ && update_only) { return (false); } return (true); } } // end of namespace isc::dhcp } // end of namespace isc