michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include michael@0: #include michael@0: #include michael@0: #include "logging.h" michael@0: #include "nrinterfaceprioritizer.h" michael@0: #include "nsCOMPtr.h" michael@0: michael@0: MOZ_MTLOG_MODULE("mtransport") michael@0: michael@0: namespace { michael@0: michael@0: class LocalAddress { michael@0: public: michael@0: LocalAddress() michael@0: : key_(), michael@0: is_vpn_(-1), michael@0: estimated_speed_(-1), michael@0: type_preference_(-1) {} michael@0: michael@0: bool Init(const nr_local_addr& local_addr) { michael@0: char buf[MAXIFNAME + 41]; michael@0: int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, sizeof(buf)); michael@0: if (r) { michael@0: MOZ_MTLOG(PR_LOG_ERROR, "Error formatting interface address string."); michael@0: return false; michael@0: } michael@0: key_ = buf; michael@0: is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0; michael@0: estimated_speed_ = local_addr.interface.estimated_speed; michael@0: type_preference_ = GetNetworkTypePreference(local_addr.interface.type); michael@0: return true; michael@0: } michael@0: michael@0: bool operator<(const LocalAddress& rhs) const { michael@0: // Interface that is "less" here is preferred. michael@0: // If type preferences are different, we should simply sort by michael@0: // |type_preference_|. michael@0: if (type_preference_ != rhs.type_preference_) { michael@0: return type_preference_ < rhs.type_preference_; michael@0: } michael@0: michael@0: // If type preferences are the same, the next thing we use to sort is vpn. michael@0: // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is michael@0: // not in vpn gets priority. michael@0: if (is_vpn_ != rhs.is_vpn_) { michael@0: return is_vpn_ < rhs.is_vpn_; michael@0: } michael@0: michael@0: // Compare estimated speed. michael@0: if (estimated_speed_ != rhs.estimated_speed_) { michael@0: return estimated_speed_ > rhs.estimated_speed_; michael@0: } michael@0: michael@0: // All things above are the same, we can at least sort with key. michael@0: return key_ < rhs.key_; michael@0: } michael@0: michael@0: const std::string& GetKey() const { michael@0: return key_; michael@0: } michael@0: michael@0: private: michael@0: // Getting the preference corresponding to a type. Getting lower number here michael@0: // means the type of network is preferred. michael@0: static inline int GetNetworkTypePreference(int type) { michael@0: if (type & NR_INTERFACE_TYPE_WIRED) { michael@0: return 1; michael@0: } michael@0: if (type & NR_INTERFACE_TYPE_WIFI) { michael@0: return 2; michael@0: } michael@0: if (type & NR_INTERFACE_TYPE_MOBILE) { michael@0: return 3; michael@0: } michael@0: return 4; michael@0: } michael@0: michael@0: std::string key_; michael@0: int is_vpn_; michael@0: int estimated_speed_; michael@0: int type_preference_; michael@0: }; michael@0: michael@0: class InterfacePrioritizer { michael@0: public: michael@0: InterfacePrioritizer() michael@0: : local_addrs_(), michael@0: preference_map_(), michael@0: sorted_(false) {} michael@0: michael@0: int add(const nr_local_addr *iface) { michael@0: LocalAddress addr; michael@0: if (!addr.Init(*iface)) { michael@0: return R_FAILED; michael@0: } michael@0: std::pair::iterator, bool> r = michael@0: local_addrs_.insert(addr); michael@0: if (!r.second) { michael@0: return R_ALREADY; // This address is already in the set. michael@0: } michael@0: sorted_ = false; michael@0: return 0; michael@0: } michael@0: michael@0: int sort() { michael@0: UCHAR tmp_pref = 127; michael@0: preference_map_.clear(); michael@0: for (std::set::iterator i = local_addrs_.begin(); michael@0: i != local_addrs_.end(); ++i) { michael@0: if (tmp_pref == 0) { michael@0: return R_FAILED; michael@0: } michael@0: preference_map_.insert(make_pair(i->GetKey(), tmp_pref--)); michael@0: } michael@0: sorted_ = true; michael@0: return 0; michael@0: } michael@0: michael@0: int getPreference(const char *key, UCHAR *pref) { michael@0: if (!sorted_) { michael@0: return R_FAILED; michael@0: } michael@0: std::map::iterator i = preference_map_.find(key); michael@0: if (i == preference_map_.end()) { michael@0: return R_NOT_FOUND; michael@0: } michael@0: *pref = i->second; michael@0: return 0; michael@0: } michael@0: michael@0: private: michael@0: std::set local_addrs_; michael@0: std::map preference_map_; michael@0: bool sorted_; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: static int add_interface(void *obj, nr_local_addr *iface) { michael@0: InterfacePrioritizer *ip = static_cast(obj); michael@0: return ip->add(iface); michael@0: } michael@0: michael@0: static int get_priority(void *obj, const char *key, UCHAR *pref) { michael@0: InterfacePrioritizer *ip = static_cast(obj); michael@0: return ip->getPreference(key, pref); michael@0: } michael@0: michael@0: static int sort_preference(void *obj) { michael@0: InterfacePrioritizer *ip = static_cast(obj); michael@0: return ip->sort(); michael@0: } michael@0: michael@0: static int destroy(void **objp) { michael@0: if (!objp || !*objp) { michael@0: return 0; michael@0: } michael@0: michael@0: InterfacePrioritizer *ip = static_cast(*objp); michael@0: *objp = 0; michael@0: delete ip; michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static nr_interface_prioritizer_vtbl priorizer_vtbl = { michael@0: add_interface, michael@0: get_priority, michael@0: sort_preference, michael@0: destroy michael@0: }; michael@0: michael@0: namespace mozilla { michael@0: michael@0: nr_interface_prioritizer* CreateInterfacePrioritizer() { michael@0: nr_interface_prioritizer *ip; michael@0: int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(), michael@0: &priorizer_vtbl, michael@0: &ip); michael@0: if (r != 0) { michael@0: return nullptr; michael@0: } michael@0: return ip; michael@0: } michael@0: michael@0: } // namespace mozilla