diff options
Diffstat (limited to 'media/mtransport/nrinterfaceprioritizer.cpp')
-rw-r--r-- | media/mtransport/nrinterfaceprioritizer.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/media/mtransport/nrinterfaceprioritizer.cpp b/media/mtransport/nrinterfaceprioritizer.cpp new file mode 100644 index 000000000..90ab1bc60 --- /dev/null +++ b/media/mtransport/nrinterfaceprioritizer.cpp @@ -0,0 +1,268 @@ +/* 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 <algorithm> +#include <map> +#include <set> +#include <string> +#include <vector> +#include "logging.h" +#include "nrinterfaceprioritizer.h" +#include "nsCOMPtr.h" + +MOZ_MTLOG_MODULE("mtransport") + +namespace { + +class LocalAddress { +public: + LocalAddress() + : ifname_(), + addr_(), + key_(), + is_vpn_(-1), + estimated_speed_(-1), + type_preference_(-1), + ip_version_(-1) {} + + bool Init(const nr_local_addr& local_addr) { + ifname_ = local_addr.addr.ifname; + + char buf[MAXIFNAME + 41]; + int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, sizeof(buf)); + if (r) { + MOZ_MTLOG(ML_ERROR, "Error formatting interface key."); + return false; + } + key_ = buf; + + r = nr_transport_addr_get_addrstring(&local_addr.addr, buf, sizeof(buf)); + if (r) { + MOZ_MTLOG(ML_ERROR, "Error formatting address string."); + return false; + } + addr_ = buf; + + is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0; + estimated_speed_ = local_addr.interface.estimated_speed; + type_preference_ = GetNetworkTypePreference(local_addr.interface.type); + ip_version_ = local_addr.addr.ip_version; + return true; + } + + bool operator<(const LocalAddress& rhs) const { + // Interface that is "less" here is preferred. + // If type preferences are different, we should simply sort by + // |type_preference_|. + if (type_preference_ != rhs.type_preference_) { + return type_preference_ < rhs.type_preference_; + } + + // If type preferences are the same, the next thing we use to sort is vpn. + // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is + // not in vpn gets priority. + if (is_vpn_ != rhs.is_vpn_) { + return is_vpn_ < rhs.is_vpn_; + } + + // Compare estimated speed. + if (estimated_speed_ != rhs.estimated_speed_) { + return estimated_speed_ > rhs.estimated_speed_; + } + + // See if our hard-coded pref list helps us. + auto thisindex = std::find(interface_preference_list().begin(), + interface_preference_list().end(), + ifname_); + auto rhsindex = std::find(interface_preference_list().begin(), + interface_preference_list().end(), + rhs.ifname_); + if (thisindex != rhsindex) { + return thisindex < rhsindex; + } + + // Prefer IPV6 over IPV4 + if (ip_version_ != rhs.ip_version_) { + return ip_version_ > rhs.ip_version_; + } + + // Now we start getting into arbitrary stuff + if (ifname_ != rhs.ifname_) { + return ifname_ < rhs.ifname_; + } + + return addr_ < rhs.addr_; + } + + const std::string& GetKey() const { + return key_; + } + +private: + // Getting the preference corresponding to a type. Getting lower number here + // means the type of network is preferred. + static inline int GetNetworkTypePreference(int type) { + if (type & NR_INTERFACE_TYPE_WIRED) { + return 1; + } + if (type & NR_INTERFACE_TYPE_WIFI) { + return 2; + } + if (type & NR_INTERFACE_TYPE_MOBILE) { + return 3; + } + return 4; + } + + // TODO(bug 895790): Once we can get useful interface properties on Darwin, + // we should remove this stuff. + static const std::vector<std::string>& interface_preference_list() + { + static std::vector<std::string> list(build_interface_preference_list()); + return list; + } + + static std::vector<std::string> build_interface_preference_list() + { + std::vector<std::string> result; + result.push_back("rl0"); + result.push_back("wi0"); + result.push_back("en0"); + result.push_back("enp2s0"); + result.push_back("enp3s0"); + result.push_back("en1"); + result.push_back("en2"); + result.push_back("en3"); + result.push_back("eth0"); + result.push_back("eth1"); + result.push_back("eth2"); + result.push_back("em1"); + result.push_back("em0"); + result.push_back("ppp"); + result.push_back("ppp0"); + result.push_back("vmnet1"); + result.push_back("vmnet0"); + result.push_back("vmnet3"); + result.push_back("vmnet4"); + result.push_back("vmnet5"); + result.push_back("vmnet6"); + result.push_back("vmnet7"); + result.push_back("vmnet8"); + result.push_back("virbr0"); + result.push_back("wlan0"); + result.push_back("lo0"); + return result; + } + + std::string ifname_; + std::string addr_; + std::string key_; + int is_vpn_; + int estimated_speed_; + int type_preference_; + int ip_version_; +}; + +class InterfacePrioritizer { +public: + InterfacePrioritizer() + : local_addrs_(), + preference_map_(), + sorted_(false) {} + + int add(const nr_local_addr *iface) { + LocalAddress addr; + if (!addr.Init(*iface)) { + return R_FAILED; + } + std::pair<std::set<LocalAddress>::iterator, bool> r = + local_addrs_.insert(addr); + if (!r.second) { + return R_ALREADY; // This address is already in the set. + } + sorted_ = false; + return 0; + } + + int sort() { + UCHAR tmp_pref = 127; + preference_map_.clear(); + for (std::set<LocalAddress>::iterator i = local_addrs_.begin(); + i != local_addrs_.end(); ++i) { + if (tmp_pref == 0) { + return R_FAILED; + } + preference_map_.insert(make_pair(i->GetKey(), tmp_pref--)); + } + sorted_ = true; + return 0; + } + + int getPreference(const char *key, UCHAR *pref) { + if (!sorted_) { + return R_FAILED; + } + std::map<std::string, UCHAR>::iterator i = preference_map_.find(key); + if (i == preference_map_.end()) { + return R_NOT_FOUND; + } + *pref = i->second; + return 0; + } + +private: + std::set<LocalAddress> local_addrs_; + std::map<std::string, UCHAR> preference_map_; + bool sorted_; +}; + +} // anonymous namespace + +static int add_interface(void *obj, nr_local_addr *iface) { + InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj); + return ip->add(iface); +} + +static int get_priority(void *obj, const char *key, UCHAR *pref) { + InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj); + return ip->getPreference(key, pref); +} + +static int sort_preference(void *obj) { + InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj); + return ip->sort(); +} + +static int destroy(void **objp) { + if (!objp || !*objp) { + return 0; + } + + InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(*objp); + *objp = 0; + delete ip; + + return 0; +} + +static nr_interface_prioritizer_vtbl priorizer_vtbl = { + add_interface, + get_priority, + sort_preference, + destroy +}; + +namespace mozilla { + +nr_interface_prioritizer* CreateInterfacePrioritizer() { + nr_interface_prioritizer *ip; + int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(), + &priorizer_vtbl, + &ip); + if (r != 0) { + return nullptr; + } + return ip; +} + +} // namespace mozilla |