summaryrefslogtreecommitdiffstats
path: root/src/bin/perfdhcp/perf_socket.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/perfdhcp/perf_socket.cc')
-rw-r--r--src/bin/perfdhcp/perf_socket.cc195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/perf_socket.cc b/src/bin/perfdhcp/perf_socket.cc
new file mode 100644
index 0000000..aa67796
--- /dev/null
+++ b/src/bin/perfdhcp/perf_socket.cc
@@ -0,0 +1,195 @@
+// 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 <config.h>
+
+#include <perfdhcp/perf_socket.h>
+#include <perfdhcp/command_options.h>
+#include <perfdhcp/stats_mgr.h>
+
+#include <dhcp/iface_mgr.h>
+#include <asiolink/io_address.h>
+
+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<unsigned int>(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_));
+}
+
+}
+}