summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/transport/nrinterfaceprioritizer.cpp')
-rw-r--r--dom/media/webrtc/transport/nrinterfaceprioritizer.cpp258
1 files changed, 258 insertions, 0 deletions
diff --git a/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp
new file mode 100644
index 0000000000..f022f8c29a
--- /dev/null
+++ b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp
@@ -0,0 +1,258 @@
+/* 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 "nr_api.h"
+#include "nrinterfaceprioritizer.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 + 47];
+ 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;
+ }
+ if (type & NR_INTERFACE_TYPE_TEREDO) {
+ // Teredo gets penalty because it's IP relayed
+ return 5;
+ }
+ 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 (const auto& local_addr : local_addrs_) {
+ if (tmp_pref == 0) {
+ return R_FAILED;
+ }
+ preference_map_.insert(make_pair(local_addr.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 = nullptr;
+ 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