diff --git a/src/Context.cc b/src/Context.cc index 204e0fc8..6a3aac83 100644 --- a/src/Context.cc +++ b/src/Context.cc @@ -229,6 +229,11 @@ Context::Context(bool standalone, std::string iface = op->get(PREF_INTERFACE); SocketCore::bindAddress(iface); } + // Bind multiple interfaces + if(!op->get(PREF_MULTIPLE_INTERFACE).empty() && op->get(PREF_INTERFACE).empty()) { + std::string ifaces = op->get(PREF_MULTIPLE_INTERFACE); + SocketCore::bindAllAddress(ifaces); + } std::vector > requestGroups; std::shared_ptr uriListParser; #ifdef ENABLE_BITTORRENT diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 8542900c..fb5e3493 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -470,6 +470,16 @@ std::vector OptionHandlerFactory::createOptionHandlers() op->addTag(TAG_ADVANCED); handlers.push_back(op); } + { + OptionHandler* op(new DefaultOptionHandler + (PREF_MULTIPLE_INTERFACE, + TEXT_MULTIPLE_INTERFACE, + NO_DEFAULT_VALUE, + "interface, IP address, hostname", + OptionHandler::REQ_ARG)); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } { OptionHandler* op(new DefaultOptionHandler (PREF_LOG, diff --git a/src/SocketCore.cc b/src/SocketCore.cc index 0106a042..b42c7ecd 100644 --- a/src/SocketCore.cc +++ b/src/SocketCore.cc @@ -134,6 +134,10 @@ int SocketCore::ipDscp_ = 0; std::vector > SocketCore::bindAddrs_; +std::vector > > +SocketCore::bindAddrsList_; +std::vector > >::iterator +SocketCore::bindAddrsListIt_; #ifdef ENABLE_SSL std::shared_ptr SocketCore::clTlsContext_; @@ -448,6 +452,13 @@ void SocketCore::establishConnection(const std::string& host, uint16_t port, continue; } } + if(!bindAddrsList_.empty()) { + ++bindAddrsListIt_; + if (bindAddrsListIt_ == bindAddrsList_.end()) { + bindAddrsListIt_ = bindAddrsList_.begin(); + } + bindAddrs_ = *bindAddrsListIt_; + } sockfd_ = fd; // make socket non-blocking mode @@ -1059,6 +1070,37 @@ void SocketCore::bindAddress(const std::string& iface) } } +void SocketCore::bindAllAddress(const std::string& ifaces) +{ + std::vector > > bindAddrsList; + std::vector ifaceList; + util::split(ifaces.begin(), ifaces.end(), std::back_inserter(ifaceList), ',', true); + if (ifaceList.empty()) { + throw DL_ABORT_EX("List of interfaces is empty, one or more interfaces is required"); + } + for (auto& iface: ifaceList) { + std::vector > bindAddrs; + getInterfaceAddress(bindAddrs, iface, protocolFamily_); + if(bindAddrs.empty()) { + throw DL_ABORT_EX(fmt(MSG_INTERFACE_NOT_FOUND, iface.c_str(), + "not available")); + } + bindAddrsList.push_back(bindAddrs); + for (const auto& a: bindAddrs) { + char host[NI_MAXHOST]; + int s; + s = getnameinfo(&a.first.sa, a.second, host, NI_MAXHOST, nullptr, 0, + NI_NUMERICHOST); + if(s == 0) { + A2_LOG_DEBUG(fmt("Sockets will bind to %s", host)); + } + } + } + bindAddrsList_.swap(bindAddrsList); + bindAddrsListIt_ = bindAddrsList_.begin(); + bindAddrs_ = *bindAddrsListIt_; +} + void getInterfaceAddress (std::vector >& ifAddrs, const std::string& iface, int family, int aiFlags) diff --git a/src/SocketCore.h b/src/SocketCore.h index b3c6ca91..35f0f83f 100644 --- a/src/SocketCore.h +++ b/src/SocketCore.h @@ -69,6 +69,8 @@ private: static int ipDscp_; static std::vector > bindAddrs_; + static std::vector > > bindAddrsList_; + static std::vector > >::iterator bindAddrsListIt_; bool blocking_; int secure_; @@ -334,6 +336,7 @@ public: // We cannot use interface as an argument because it is a reserved // keyword in MSVC. static void bindAddress(const std::string& iface); + static void bindAllAddress(const std::string& ifaces); friend void getInterfaceAddress (std::vector >& ifAddrs, diff --git a/src/prefs.cc b/src/prefs.cc index 502cfe1d..903e9d5f 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -308,6 +308,8 @@ PrefPtr PREF_ON_DOWNLOAD_COMPLETE = makePref("on-download-complete"); PrefPtr PREF_ON_DOWNLOAD_ERROR = makePref("on-download-error"); // value: string PrefPtr PREF_INTERFACE = makePref("interface"); +// value: string +PrefPtr PREF_MULTIPLE_INTERFACE = makePref("multiple-interface"); // value: true | false PrefPtr PREF_DISABLE_IPV6 = makePref("disable-ipv6"); // value: true | false diff --git a/src/prefs.h b/src/prefs.h index 3bcafe23..2b3ade6d 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -244,6 +244,8 @@ extern PrefPtr PREF_ON_DOWNLOAD_COMPLETE; extern PrefPtr PREF_ON_DOWNLOAD_ERROR; // value: string extern PrefPtr PREF_INTERFACE; +// value: string +extern PrefPtr PREF_MULTIPLE_INTERFACE; // value: true | false extern PrefPtr PREF_DISABLE_IPV6; // value: true | false diff --git a/src/usage_text.h b/src/usage_text.h index f556acca..ba2645f8 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -599,6 +599,11 @@ #define TEXT_INTERFACE \ _(" --interface=INTERFACE Bind sockets to given interface. You can specify\n" \ " interface name, IP address and hostname.") +#define TEXT_MULTIPLE_INTERFACE \ + _(" --multiple-interface=INTERFACES Comma separated list of interfaces to bind\n" \ + " sockets to. Requests will be splited among the\n" \ + " interfaces to achieve link aggregation. You can\n" \ + " specify interface name, IP address and hostname.") #define TEXT_DISABLE_IPV6 \ _(" --disable-ipv6[=true|false] Disable IPv6.") #define TEXT_BT_SAVE_METADATA \