nrinterfaceprioritizer.cpp (7049B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 #include "nrinterfaceprioritizer.h" 5 6 #include <algorithm> 7 #include <map> 8 #include <set> 9 #include <string> 10 #include <vector> 11 12 #include "logging.h" 13 #include "nr_api.h" 14 15 MOZ_MTLOG_MODULE("mtransport") 16 17 namespace { 18 19 class LocalAddress { 20 public: 21 LocalAddress() 22 : is_vpn_(-1), 23 estimated_speed_(-1), 24 type_preference_(-1), 25 ip_version_(-1) {} 26 27 bool Init(const nr_local_addr& local_addr) { 28 ifname_ = local_addr.addr.ifname; 29 30 char buf[MAXIFNAME + 47]; 31 int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, 32 sizeof(buf)); 33 if (r) { 34 MOZ_MTLOG(ML_ERROR, "Error formatting interface key."); 35 return false; 36 } 37 key_ = buf; 38 39 r = nr_transport_addr_get_addrstring(&local_addr.addr, buf, sizeof(buf)); 40 if (r) { 41 MOZ_MTLOG(ML_ERROR, "Error formatting address string."); 42 return false; 43 } 44 addr_ = buf; 45 46 is_vpn_ = (local_addr.iface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0; 47 estimated_speed_ = local_addr.iface.estimated_speed; 48 type_preference_ = GetNetworkTypePreference(local_addr.iface.type); 49 ip_version_ = local_addr.addr.ip_version; 50 return true; 51 } 52 53 bool operator<(const LocalAddress& rhs) const { 54 // Interface that is "less" here is preferred. 55 // If type preferences are different, we should simply sort by 56 // |type_preference_|. 57 if (type_preference_ != rhs.type_preference_) { 58 return type_preference_ < rhs.type_preference_; 59 } 60 61 // If type preferences are the same, the next thing we use to sort is vpn. 62 // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is 63 // not in vpn gets priority. 64 if (is_vpn_ != rhs.is_vpn_) { 65 return is_vpn_ < rhs.is_vpn_; 66 } 67 68 // Compare estimated speed. 69 if (estimated_speed_ != rhs.estimated_speed_) { 70 return estimated_speed_ > rhs.estimated_speed_; 71 } 72 73 // See if our hard-coded pref list helps us. 74 auto thisindex = std::find(interface_preference_list().begin(), 75 interface_preference_list().end(), ifname_); 76 auto rhsindex = std::find(interface_preference_list().begin(), 77 interface_preference_list().end(), rhs.ifname_); 78 if (thisindex != rhsindex) { 79 return thisindex < rhsindex; 80 } 81 82 // Prefer IPV6 over IPV4 83 if (ip_version_ != rhs.ip_version_) { 84 return ip_version_ > rhs.ip_version_; 85 } 86 87 // Now we start getting into arbitrary stuff 88 if (ifname_ != rhs.ifname_) { 89 return ifname_ < rhs.ifname_; 90 } 91 92 return addr_ < rhs.addr_; 93 } 94 95 const std::string& GetKey() const { return key_; } 96 97 private: 98 // Getting the preference corresponding to a type. Getting lower number here 99 // means the type of network is preferred. 100 static inline int GetNetworkTypePreference(int type) { 101 if (type & NR_INTERFACE_TYPE_WIRED) { 102 return 1; 103 } 104 if (type & NR_INTERFACE_TYPE_WIFI) { 105 return 2; 106 } 107 if (type & NR_INTERFACE_TYPE_MOBILE) { 108 return 3; 109 } 110 if (type & NR_INTERFACE_TYPE_TEREDO) { 111 // Teredo gets penalty because it's IP relayed 112 return 5; 113 } 114 return 4; 115 } 116 117 // TODO(bug 895790): Once we can get useful interface properties on Darwin, 118 // we should remove this stuff. 119 static const std::vector<std::string>& interface_preference_list() { 120 static std::vector<std::string> list(build_interface_preference_list()); 121 return list; 122 } 123 124 static std::vector<std::string> build_interface_preference_list() { 125 std::vector<std::string> result; 126 result.push_back("rl0"); 127 result.push_back("wi0"); 128 result.push_back("en0"); 129 result.push_back("enp2s0"); 130 result.push_back("enp3s0"); 131 result.push_back("en1"); 132 result.push_back("en2"); 133 result.push_back("en3"); 134 result.push_back("eth0"); 135 result.push_back("eth1"); 136 result.push_back("eth2"); 137 result.push_back("em1"); 138 result.push_back("em0"); 139 result.push_back("ppp"); 140 result.push_back("ppp0"); 141 result.push_back("vmnet1"); 142 result.push_back("vmnet0"); 143 result.push_back("vmnet3"); 144 result.push_back("vmnet4"); 145 result.push_back("vmnet5"); 146 result.push_back("vmnet6"); 147 result.push_back("vmnet7"); 148 result.push_back("vmnet8"); 149 result.push_back("virbr0"); 150 result.push_back("wlan0"); 151 result.push_back("lo0"); 152 return result; 153 } 154 155 std::string ifname_; 156 std::string addr_; 157 std::string key_; 158 int is_vpn_; 159 int estimated_speed_; 160 int type_preference_; 161 int ip_version_; 162 }; 163 164 class InterfacePrioritizer { 165 public: 166 InterfacePrioritizer() : sorted_(false) {} 167 168 int add(const nr_local_addr* iface) { 169 LocalAddress addr; 170 if (!addr.Init(*iface)) { 171 return R_FAILED; 172 } 173 std::pair<std::set<LocalAddress>::iterator, bool> r = 174 local_addrs_.insert(addr); 175 if (!r.second) { 176 return R_ALREADY; // This address is already in the set. 177 } 178 sorted_ = false; 179 return 0; 180 } 181 182 int sort() { 183 UCHAR tmp_pref = 127; 184 preference_map_.clear(); 185 for (const auto& local_addr : local_addrs_) { 186 if (tmp_pref == 0) { 187 return R_FAILED; 188 } 189 preference_map_.insert(make_pair(local_addr.GetKey(), tmp_pref--)); 190 } 191 sorted_ = true; 192 return 0; 193 } 194 195 int getPreference(const char* key, UCHAR* pref) { 196 if (!sorted_) { 197 return R_FAILED; 198 } 199 std::map<std::string, UCHAR>::iterator i = preference_map_.find(key); 200 if (i == preference_map_.end()) { 201 return R_NOT_FOUND; 202 } 203 *pref = i->second; 204 return 0; 205 } 206 207 private: 208 std::set<LocalAddress> local_addrs_; 209 std::map<std::string, UCHAR> preference_map_; 210 bool sorted_; 211 }; 212 213 } // anonymous namespace 214 215 static int add_interface(void* obj, nr_local_addr* iface) { 216 InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); 217 return ip->add(iface); 218 } 219 220 static int get_priority(void* obj, const char* key, UCHAR* pref) { 221 InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); 222 return ip->getPreference(key, pref); 223 } 224 225 static int sort_preference(void* obj) { 226 InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); 227 return ip->sort(); 228 } 229 230 static int destroy(void** objp) { 231 if (!objp || !*objp) { 232 return 0; 233 } 234 235 InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(*objp); 236 *objp = nullptr; 237 delete ip; 238 239 return 0; 240 } 241 242 static nr_interface_prioritizer_vtbl priorizer_vtbl = { 243 add_interface, get_priority, sort_preference, destroy}; 244 245 namespace mozilla { 246 247 nr_interface_prioritizer* CreateInterfacePrioritizer() { 248 nr_interface_prioritizer* ip; 249 int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(), 250 &priorizer_vtbl, &ip); 251 if (r != 0) { 252 return nullptr; 253 } 254 return ip; 255 } 256 257 } // namespace mozilla