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