// Copyright (C) 2012-2020 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 using namespace isc::dhcp; using namespace isc::asiolink; namespace isc { namespace perfdhcp { PerfSocket::PerfSocket(CommandOptions& options) { sockfd_ = openSocket(options); initSocketData(); } int PerfSocket::openSocket(CommandOptions& options) const { std::string localname = options.getLocalName(); std::string servername = options.getServerName(); uint16_t port = options.getLocalPort(); int sock = 0; uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET; IOAddress remoteaddr(servername); // Check for mismatch between IP option and server address if (family != remoteaddr.getFamily()) { isc_throw(InvalidParameter, "Values for IP version: " << static_cast(options.getIpVersion()) << " and server address: " << servername << " are mismatched."); } if (port == 0) { if (family == AF_INET6) { // need server port (547) because the server is acting as a relay agent port = DHCP6_CLIENT_PORT; // if acting as a relay agent change port. if (options.isUseRelayedV6()) { port = DHCP6_SERVER_PORT; } } else if (options.getIpVersion() == 4) { port = 67; /// @todo: find out why port 68 is wrong here. } } // Local name is specified along with '-l' option. // It may point to interface name or local address. if (!localname.empty()) { // CommandOptions should be already aware whether local name // is interface name or address because it uses IfaceMgr to // scan interfaces and get's their names. if (options.isInterface()) { sock = IfaceMgr::instance().openSocketFromIface(localname, port, family); } else { IOAddress localaddr(localname); sock = IfaceMgr::instance().openSocketFromAddress(localaddr, port); } } else if (!servername.empty()) { // If only server name is given we will need to try to resolve // the local address to bind socket to based on remote address. sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr, port); } if (sock <= 0) { isc_throw(BadValue, "unable to open socket to communicate with " "DHCP server"); } // IfaceMgr does not set broadcast option on the socket. We rely // on CommandOptions object to find out if socket has to have // broadcast enabled. if ((options.getIpVersion() == 4) && options.isBroadcast()) { int broadcast_enable = 1; int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast_enable, sizeof(broadcast_enable)); if (ret < 0) { isc_throw(InvalidOperation, "unable to set broadcast option on the socket"); } } else if (options.getIpVersion() == 6) { // If remote address is multicast we need to enable it on // the socket that has been created. if (remoteaddr.isV6Multicast()) { int hops = 1; int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); // If user specified interface name with '-l' the // IPV6_MULTICAST_IF has to be set. if ((ret >= 0) && options.isInterface()) { IfacePtr iface = IfaceMgr::instance().getIface(options.getLocalName()); if (iface == NULL) { isc_throw(Unexpected, "unknown interface " << options.getLocalName()); } int idx = iface->getIndex(); ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx)); } if (ret < 0) { isc_throw(InvalidOperation, "unable to enable multicast on socket " << sock << ". errno = " << errno); } } } return (sock); } PerfSocket::~PerfSocket() { IfacePtr iface = IfaceMgr::instance().getIface(ifindex_); if (iface) { iface->delSocket(sockfd_); } } void PerfSocket::initSocketData() { for (IfacePtr iface : IfaceMgr::instance().getIfaces()) { for (SocketInfo s : iface->getSockets()) { if (s.sockfd_ == sockfd_) { ifindex_ = iface->getIndex(); addr_ = s.addr_; return; } } } isc_throw(BadValue, "interface for specified socket descriptor not found"); } Pkt4Ptr PerfSocket::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { Pkt4Ptr pkt = IfaceMgr::instance().receive4(timeout_sec, timeout_usec); if (pkt) { try { pkt->unpack(); } catch (const std::exception &e) { ExchangeStats::malformed_pkts_++; std::cout << "Incorrect DHCP packet received" << e.what() << std::endl; } } return (pkt); } Pkt6Ptr PerfSocket::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { Pkt6Ptr pkt = IfaceMgr::instance().receive6(timeout_sec, timeout_usec); if (pkt) { try { pkt->unpack(); } catch (const std::exception &e) { ExchangeStats::malformed_pkts_++; std::cout << "Incorrect DHCP packet received" << e.what() << std::endl; } } return (pkt); } bool PerfSocket::send(const Pkt4Ptr& pkt) { return IfaceMgr::instance().send(pkt); } bool PerfSocket::send(const Pkt6Ptr& pkt) { return IfaceMgr::instance().send(pkt); } IfacePtr PerfSocket::getIface() { return (IfaceMgr::instance().getIface(ifindex_)); } } }