OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/dns/address_sorter.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <netinet/in.h> | |
9 | |
10 #include <algorithm> | |
11 #include <map> | |
12 #include <vector> | |
13 | |
14 #include "base/eintr_wrapper.h" | |
15 #include "base/logging.h" | |
16 #include "base/memory/scoped_vector.h" | |
17 #include "net/base/address_list.h" | |
18 #include "net/base/network_change_notifier.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/base/net_util.h" | |
21 #include "net/socket/client_socket_factory.h" | |
22 #include "net/udp/datagram_client_socket.h" | |
23 | |
24 namespace net { | |
25 | |
26 namespace { | |
27 | |
28 // TODO(szym): Document general approach. | |
29 // http://tools.ietf.org/html/draft-ietf-6man-rfc3484bis-03 | |
30 | |
31 // Generic policy entry. | |
32 struct PolicyEntry { | |
33 // IPv4 addresses must be mapped to IPv6. | |
34 unsigned char prefix[kIPv6AddressSize]; | |
35 unsigned prefix_length; | |
36 unsigned value; | |
37 }; | |
38 | |
39 typedef std::vector<PolicyEntry> PolicyTable; | |
40 | |
41 // Sort table by decreasing prefix size to allow longest prefix matching. | |
42 bool ComparePolicy(const PolicyEntry& a, const PolicyEntry& b) { | |
43 return a.prefix_length > b.prefix_length; | |
44 } | |
45 | |
46 // Creates sorted PolicyTable from |table| with |size| entries. | |
47 PolicyTable LoadPolicy(PolicyEntry* table, size_t size) { | |
48 PolicyTable result(table, table + size); | |
49 std::sort(result.begin(), result.end(), ComparePolicy); | |
50 return result; | |
51 } | |
52 | |
53 // Search |table| for matching prefix of |address|. |table| must be sorted by | |
54 // descending prefix (prefix of another prefix must be later in table). | |
55 unsigned GetPolicyValue(const PolicyTable& table, | |
56 const IPAddressNumber& address) { | |
57 if (address.size() == kIPv4AddressSize) | |
58 return GetPolicyValue(table, ConvertIPv4NumberToIPv6Number(address)); | |
59 for (unsigned i = 0; i < table.size(); ++i) { | |
60 const PolicyEntry& entry = table[i]; | |
61 IPAddressNumber prefix(entry.prefix, entry.prefix + kIPv6AddressSize); | |
62 if (IPNumberMatchesPrefix(address, prefix, entry.prefix_length)) | |
63 return entry.value; | |
64 } | |
65 NOTREACHED(); | |
66 // The last entry is the least restrictive, so assume it's default. | |
67 return table.back().value; | |
68 } | |
69 | |
70 enum AddressScope { | |
71 SCOPE_NODELOCAL = 1, | |
72 SCOPE_LINKLOCAL = 2, | |
73 SCOPE_SITELOCAL = 5, | |
74 SCOPE_ORGLOCAL = 8, | |
75 SCOPE_GLOBAL = 14, | |
76 }; | |
77 | |
78 bool IsMulticast(const IPAddressNumber& address) { | |
79 return address[0] == 0xFF; | |
80 } | |
81 | |
82 AddressScope GetMulticastScope(const IPAddressNumber& address) { | |
83 return static_cast<AddressScope>(address[1] & 0x0F); | |
84 } | |
85 | |
86 bool IsIPv6Loopback(const IPAddressNumber& address) { | |
87 // IN6_IS_ADDR_LOOPBACK | |
88 unsigned char kLoopback[kIPv6AddressSize] = { | |
89 0, 0, 0, 0, 0, 0, 0, 0, | |
90 0, 0, 0, 0, 0, 0, 0, 1, | |
91 }; | |
92 return address == IPAddressNumber(kLoopback, kLoopback + kIPv6AddressSize); | |
93 } | |
94 | |
95 bool IsLinkLocal(const IPAddressNumber& address) { | |
96 // IN6_IS_ADDR_LINKLOCAL | |
97 return (address[0] == 0xFE) && ((address[1] & 0xC0) == 0x80); | |
98 } | |
99 | |
100 bool IsSiteLocal(const IPAddressNumber& address) { | |
101 // IN6_IS_ADDR_SITELOCAL | |
102 return (address[0] == 0xFE) && ((address[1] & 0xC0) == 0xC0); | |
103 } | |
104 | |
105 AddressScope GetScope(const PolicyTable& table, | |
106 const IPAddressNumber& address) { | |
107 if (address.size() == kIPv6AddressSize) { | |
108 if (IsMulticast(address)) { | |
109 return GetMulticastScope(address); | |
110 } else if (IsIPv6Loopback(address) || IsLinkLocal(address)) { | |
111 return SCOPE_LINKLOCAL; | |
112 } else if (IsSiteLocal(address)) { | |
113 return SCOPE_SITELOCAL; | |
114 } else { | |
115 return SCOPE_GLOBAL; | |
116 } | |
117 } else if (address.size() == kIPv4AddressSize) { | |
118 return static_cast<AddressScope>(GetPolicyValue(table, address)); | |
119 } else { | |
120 NOTREACHED(); | |
121 return SCOPE_NODELOCAL; | |
122 } | |
123 } | |
124 | |
125 // Default policy table. RFC 3484, Section 2.1. | |
126 // Updated for glibc: http://www.akkadia.org/drepper/linux-rfc3484.html | |
127 // Precedence and label are separate to support /etc/gai.conf. | |
128 PolicyEntry kDefaultPrecedenceTable[] = { | |
129 // ::1/128 -- loopback | |
130 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 128, 60 }, | |
131 // fc00::/7 -- multicast | |
132 { { 0xFC }, 7, 50 }, | |
133 // ::/0 -- any | |
134 { { }, 0, 40 }, | |
135 // ::ffff:0:0/96 -- IPv4 mapped | |
136 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }, 96, 30 }, | |
137 // 2002::/16 -- 6to4 | |
138 { { 0x20, 0x02, }, 17, 20 }, | |
139 // 2001::/32 -- Teredo | |
140 { { 0x20, 0x01, 0, 0 }, 32, 10 }, | |
141 // ::/96 -- IPv4 compatible | |
142 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 96, 1 }, | |
143 // fec0::/16 -- unique local address | |
144 { { 0xFE, 0xC0 }, 16, 1 }, | |
145 // 3ffe::/16 -- 6bone | |
146 { { 0x3F, 0xFE }, 16, 1 }, | |
147 }; | |
148 | |
149 PolicyEntry kDefaultLabelTable[] = { | |
150 // ::1/128 -- loopback | |
151 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 128, 0 }, | |
152 // fc00::/7 -- multicast | |
153 { { 0xFC }, 7, 1 }, | |
154 // ::/0 -- any | |
155 { { }, 0, 2 }, | |
156 // ::ffff:0:0/96 -- IPv4 mapped | |
157 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }, 96, 3 }, | |
158 // 2002::/16 -- 6to4 | |
159 { { 0x20, 0x02, }, 17, 4 }, | |
160 // 2001::/32 -- Teredo | |
161 { { 0x20, 0x01, 0, 0 }, 32, 5 }, | |
162 // ::/96 -- IPv4 compatible | |
163 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 96, 10 }, | |
164 // fec0::/16 -- unique local address | |
165 { { 0xFE, 0xC0 }, 16, 11 }, | |
166 // 3ffe::/16 -- 6bone | |
167 { { 0x3F, 0xFE }, 16, 12 }, | |
168 }; | |
169 | |
170 // Default mapping of IPv4 addresses to scope. | |
171 PolicyEntry kDefaultScopeTable[] = { | |
172 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F }, 104, | |
173 SCOPE_LINKLOCAL }, | |
mmenke
2012/06/07 18:32:34
nit: Think this and the next one both fit on one
| |
174 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xA9, 0xFE }, 112, | |
175 SCOPE_LINKLOCAL }, | |
176 { { }, 0, SCOPE_GLOBAL }, | |
177 }; | |
178 | |
179 // Returns number of matching initial bits between the addresses |a1| and |a2|. | |
180 unsigned CommonPrefixLength(const IPAddressNumber& a1, | |
181 const IPAddressNumber& a2) { | |
182 DCHECK_EQ(a1.size(), a2.size()); | |
183 for (size_t i = 0; i < a1.size(); ++i) { | |
184 unsigned diff = a1[i] ^ a2[i]; | |
185 if (!diff) | |
186 continue; | |
187 for (unsigned j = 0; j < CHAR_BIT; ++j) { | |
188 if (diff & (1 << (CHAR_BIT - 1))) | |
189 return i * CHAR_BIT + j; | |
190 diff <<= 1; | |
191 } | |
192 } | |
193 return a1.size() * CHAR_BIT; | |
194 } | |
195 | |
196 struct SourceAddressInfo { | |
197 AddressScope scope; | |
198 unsigned label; | |
199 // Only needed if more than one source address in the list. | |
200 unsigned prefix_length; | |
201 bool deprecated; // vs. preferred RFC4862 | |
202 bool home; // vs. care-of RFC6275 | |
203 bool native; | |
204 }; | |
205 | |
206 typedef std::map<IPAddressNumber, SourceAddressInfo> SourceAddressMap; | |
207 | |
208 struct SortElement { | |
209 IPAddressNumber address; | |
210 AddressScope scope; | |
211 unsigned precedence; | |
212 unsigned label; | |
213 const SourceAddressInfo* src; | |
214 unsigned common_prefix_length; | |
215 }; | |
216 | |
217 // RFC 3484, section 6. | |
218 bool CompareRFC3484(const SortElement* a1, const SortElement* a2) { | |
219 // Rule 1: Avoid unusable destinations. | |
220 // Unusable destinations are already filtered out. | |
221 | |
222 // Rule 2: Prefer matching scope. | |
223 bool scope_match1 = (a1->src->scope == a1->scope); | |
224 bool scope_match2 = (a2->src->scope == a2->scope); | |
225 if (scope_match1 != scope_match2) | |
226 return scope_match1; | |
227 | |
228 // Rule 3: Avoid deprecated addresses. | |
229 if (a1->src->deprecated != a2->src->deprecated) | |
230 return !a1->src->deprecated; | |
231 | |
232 // Rule 4: Prefer home addresses. | |
233 if (a1->src->home != a2->src->home) | |
234 return !a1->src->home; | |
235 | |
236 // Rule 5: Prefer matching label. | |
237 bool label_match1 = (a1->src->label == a1->label); | |
238 bool label_match2 = (a2->src->label == a2->label); | |
239 if (label_match1 != label_match2) | |
240 return label_match1; | |
241 | |
242 // Rule 6: Prefer higher precedence. | |
243 if (a1->precedence != a2->precedence) | |
244 return a1->precedence > a2->precedence; | |
245 | |
246 // Rule 7: Prefer native transport. | |
247 if (a1->src->native != a2->src->native) | |
248 return !a1->src->native; | |
249 | |
250 // Rule 8: Prefer smaller scope. | |
251 if (a1->scope != a2->scope) | |
252 return a1->scope < a2->scope; | |
253 | |
254 // Rule 9: Use longest matching prefix. | |
255 if (a1->address.size() == a2->address.size()) { | |
256 if (a1->common_prefix_length != a1->common_prefix_length) | |
257 return a1->common_prefix_length > a1->common_prefix_length; | |
258 } | |
259 | |
260 // Rule 10: Leave the order unchanged. | |
261 // stable_sort takes care of that. | |
262 return false; | |
263 } | |
264 | |
265 #if defined(OS_LINUX) | |
266 class SourceAddressTracker : public NetworkChangeNotifier::IPAddressObserver { | |
267 public: | |
268 typedef base::Callback<void(const SourceAddressMap&)> Callback; | |
269 SourceAddressTracker(Callback callback) : callback_(callback) { | |
270 // TODO: ask for Netlink updates on NEWLINK | |
271 } | |
272 virtual ~SourceAddressTracker() {} | |
273 private: | |
274 // NetworkChangeNotifier::IPAddressObserver: | |
275 virtual void OnIPAddressChanged() OVERRIDE { | |
276 // TODO: ask NetworkChangeNotifier for detailed source info | |
277 } | |
278 | |
279 // TODO: Handle NEWLINK updates. | |
280 | |
281 SourceAddressMap source_info_; | |
282 Callback callback_; | |
283 }; | |
284 | |
285 #endif // defined(OS_LINUX) | |
286 | |
287 class AddressSorterPosix : public AddressSorter { | |
288 public: | |
289 explicit AddressSorterPosix(ClientSocketFactory* socket_factory); | |
290 ~AddressSorterPosix() {} | |
291 | |
292 virtual void Sort(AddressList* list) const OVERRIDE; | |
293 | |
294 private: | |
295 #if defined(OS_LINUX) | |
296 void OnSourceAddressUpdate(const SourceAddressMap& source_info); | |
297 | |
298 SourceAddressTracker source_tracker_; | |
299 SourceAddressMap source_info_; | |
300 // TODO(szym): remove. | |
301 SourceAddressInfo mock_source_info_; | |
302 #endif | |
303 | |
304 ClientSocketFactory* socket_factory_; | |
305 PolicyTable precedence_table_; | |
306 PolicyTable label_table_; | |
307 PolicyTable scope_table_; | |
308 | |
309 DISALLOW_COPY_AND_ASSIGN(AddressSorterPosix); | |
310 }; | |
311 | |
312 AddressSorterPosix::AddressSorterPosix(ClientSocketFactory* socket_factory) | |
313 : | |
314 #if defined(OS_LINUX) | |
315 source_tracker_(base::Bind(&AddressSorterPosix::OnSourceAddressUpdate, | |
316 base::Unretained(this))), | |
317 #endif | |
318 socket_factory_(socket_factory), | |
319 precedence_table_(LoadPolicy(kDefaultPrecedenceTable, | |
320 arraysize(kDefaultPrecedenceTable))), | |
321 label_table_(LoadPolicy(kDefaultLabelTable, | |
322 arraysize(kDefaultLabelTable))), | |
323 scope_table_(LoadPolicy(kDefaultScopeTable, | |
324 arraysize(kDefaultScopeTable))) { | |
325 #if defined(OS_LINUX) | |
326 mock_source_info_.scope = SCOPE_GLOBAL; | |
327 mock_source_info_.deprecated = false; | |
328 mock_source_info_.home = false; | |
329 mock_source_info_.native = false; | |
330 mock_source_info_.label = static_cast<unsigned>(-1); | |
331 mock_source_info_.prefix_length = 0; | |
332 #endif | |
333 } | |
334 | |
335 bool AddressSorterPosix::Sort(AddressList* list) const { | |
336 ScopedVector<SortElement> sort_list; | |
337 | |
338 for (size_t i = 0; i < list->size(); ++i) { | |
339 scoped_ptr<SortElement> el(new SortElement()); | |
340 el->address = (*list)[i].address(); | |
341 el->scope = GetScope(scope_table_, el->address); | |
342 el->precedence = GetPolicyValue(precedence_table_, el->address); | |
343 el->label = GetPolicyValue(label_table_, el->address); | |
344 | |
345 scoped_ptr<DatagramClientSocket> socket( | |
346 socket_factory_->CreateDatagramClientSocket( | |
347 DatagramSocket::DEFAULT_BIND, | |
348 RandIntCallback(), | |
349 NULL /* NetLog */, | |
350 NetLog::Source())); | |
351 | |
352 if (socket->Connect(IPEndPoint(el->address, 0 /* port */)) != OK) | |
353 continue; | |
354 IPEndPoint endpoint; | |
355 // Filter out unsable destinations. | |
mmenke
2012/06/07 18:32:34
nit: unusable.
mmenke
2012/06/07 18:32:34
Also, doesn't this comment also apply to the line
| |
356 if (socket->GetLocalAddress(&endpoint) != OK); | |
357 continue; | |
358 #if defined(OS_LINUX) | |
359 SourceAddressMap::const_iterator it = source_info_.find(endpoint.address()); | |
360 if (it != source_info_.end()) { | |
361 el->src = &(it->second); | |
362 } else { | |
363 // TODO(szym): This should not be possible. | |
mmenke
2012/06/07 18:32:34
Are you sure about this? Adapters can be removed/
| |
364 el->src = &(mock_source_info_); | |
365 } | |
366 #elif defined(OS_MACOSX) || defined(OS_BSD) | |
367 NOTIMPLEMENTED(); | |
368 // TODO(szym): ioctl SIOCGIFFLAGS to get deprecated | |
369 #endif | |
370 | |
371 if (el->address.size() == endpoint.address().size()) { | |
372 el->common_prefix_length = std::min( | |
373 CommonPrefixLength(el->address, endpoint.address()), | |
374 el->src->prefix_length); | |
375 } | |
376 sort_list.push_back(el.release()); | |
377 } | |
378 | |
379 std::stable_sort(sort_list.begin(), sort_list.end(), CompareRFC3484); | |
380 | |
381 list->clear(); | |
382 for (size_t i = 0; i < sort_list.size(); ++i) { | |
383 list->push_back(IPEndPoint(sort_list[i]->address, 0 /* port */)); | |
384 } | |
385 return true; | |
386 } | |
387 | |
388 #if defined(OS_LINUX) | |
389 void AddressSorterPosix::OnSourceAddressUpdate( | |
390 const SourceAddressMap& source_info) { | |
391 source_info_ = source_info; | |
392 } | |
393 #endif | |
394 | |
395 } // namespace | |
396 | |
397 // static | |
398 scoped_ptr<AddressSorter> AddressSorter::CreateAddressSorter() { | |
399 return scoped_ptr<AddressSorter>( | |
400 new AddressSorterPosix(ClientSocketFactory::GetDefaultFactory())); | |
401 } | |
402 | |
403 } // namespace net | |
404 | |
OLD | NEW |