]>
Commit | Line | Data |
---|---|---|
f579559b TG |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Tom Gundersen <teg@jklm.no> | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <net/if.h> | |
23 | ||
24 | #include "networkd.h" | |
25 | ||
26 | #include "utf8.h" | |
27 | #include "util.h" | |
28 | #include "conf-parser.h" | |
c6f7c917 | 29 | #include "network-internal.h" |
f579559b | 30 | |
aba496a5 UTL |
31 | static void address_init(Address *address) { |
32 | assert(address); | |
33 | ||
34 | address->family = AF_UNSPEC; | |
35 | address->scope = RT_SCOPE_UNIVERSE; | |
36 | address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; | |
37 | address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME; | |
38 | } | |
39 | ||
f048a16b | 40 | int address_new_static(Network *network, unsigned section, Address **ret) { |
f579559b TG |
41 | _cleanup_address_free_ Address *address = NULL; |
42 | ||
6ae115c1 TG |
43 | if (section) { |
44 | uint64_t key = section; | |
45 | address = hashmap_get(network->addresses_by_section, &key); | |
46 | if (address) { | |
47 | *ret = address; | |
48 | address = NULL; | |
49 | ||
50 | return 0; | |
51 | } | |
52 | } | |
53 | ||
f579559b TG |
54 | address = new0(Address, 1); |
55 | if (!address) | |
56 | return -ENOMEM; | |
57 | ||
aba496a5 | 58 | address_init(address); |
801bd9e8 | 59 | |
f579559b TG |
60 | address->network = network; |
61 | ||
3d3d4255 | 62 | LIST_PREPEND(addresses, network->static_addresses, address); |
f579559b | 63 | |
6ae115c1 TG |
64 | if (section) { |
65 | address->section = section; | |
66 | hashmap_put(network->addresses_by_section, &address->section, address); | |
67 | } | |
68 | ||
f579559b TG |
69 | *ret = address; |
70 | address = NULL; | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
f048a16b TG |
75 | int address_new_dynamic(Address **ret) { |
76 | _cleanup_address_free_ Address *address = NULL; | |
77 | ||
78 | address = new0(Address, 1); | |
79 | if (!address) | |
80 | return -ENOMEM; | |
81 | ||
aba496a5 | 82 | address_init(address); |
801bd9e8 | 83 | |
f048a16b TG |
84 | *ret = address; |
85 | address = NULL; | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
f579559b TG |
90 | void address_free(Address *address) { |
91 | if (!address) | |
92 | return; | |
93 | ||
f048a16b | 94 | if (address->network) { |
3d3d4255 | 95 | LIST_REMOVE(addresses, address->network->static_addresses, address); |
f579559b | 96 | |
f048a16b TG |
97 | if (address->section) |
98 | hashmap_remove(address->network->addresses_by_section, | |
99 | &address->section); | |
100 | } | |
6ae115c1 | 101 | |
f579559b TG |
102 | free(address); |
103 | } | |
104 | ||
407fe036 TG |
105 | int address_drop(Address *address, Link *link, |
106 | sd_rtnl_message_handler_t callback) { | |
cf6a8911 | 107 | _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; |
407fe036 TG |
108 | int r; |
109 | ||
110 | assert(address); | |
111 | assert(address->family == AF_INET || address->family == AF_INET6); | |
112 | assert(link); | |
113 | assert(link->ifindex > 0); | |
114 | assert(link->manager); | |
115 | assert(link->manager->rtnl); | |
116 | ||
151b9b96 LP |
117 | r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, |
118 | link->ifindex, address->family); | |
407fe036 TG |
119 | if (r < 0) { |
120 | log_error("Could not allocate RTM_DELADDR message: %s", | |
121 | strerror(-r)); | |
122 | return r; | |
123 | } | |
124 | ||
5a723174 TG |
125 | r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); |
126 | if (r < 0) { | |
127 | log_error("Could not set prefixlen: %s", strerror(-r)); | |
128 | return r; | |
129 | } | |
130 | ||
407fe036 TG |
131 | if (address->family == AF_INET) |
132 | r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); | |
133 | else if (address->family == AF_INET6) | |
134 | r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); | |
135 | if (r < 0) { | |
136 | log_error("Could not append IFA_LOCAL attribute: %s", | |
137 | strerror(-r)); | |
138 | return r; | |
139 | } | |
140 | ||
141 | r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); | |
142 | if (r < 0) { | |
143 | log_error("Could not send rtnetlink message: %s", strerror(-r)); | |
144 | return r; | |
145 | } | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
aba496a5 UTL |
150 | int address_update(Address *address, Link *link, |
151 | sd_rtnl_message_handler_t callback) { | |
152 | _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; | |
153 | int r; | |
154 | ||
155 | assert(address); | |
156 | assert(address->family == AF_INET || address->family == AF_INET6); | |
157 | assert(link->ifindex > 0); | |
158 | assert(link->manager); | |
159 | assert(link->manager->rtnl); | |
160 | ||
161 | r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, | |
162 | link->ifindex, address->family); | |
163 | if (r < 0) { | |
164 | log_error("Could not allocate RTM_NEWADDR message: %s", | |
165 | strerror(-r)); | |
166 | return r; | |
167 | } | |
168 | ||
169 | r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); | |
170 | if (r < 0) { | |
171 | log_error("Could not set prefixlen: %s", strerror(-r)); | |
172 | return r; | |
173 | } | |
174 | ||
175 | r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); | |
176 | if (r < 0) { | |
177 | log_error("Could not set flags: %s", strerror(-r)); | |
178 | return r; | |
179 | } | |
180 | ||
181 | r = sd_rtnl_message_addr_set_scope(req, address->scope); | |
182 | if (r < 0) { | |
183 | log_error("Could not set scope: %s", strerror(-r)); | |
184 | return r; | |
185 | } | |
186 | ||
187 | if (address->family == AF_INET) | |
188 | r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); | |
189 | else if (address->family == AF_INET6) | |
190 | r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); | |
191 | if (r < 0) { | |
192 | log_error("Could not append IFA_LOCAL attribute: %s", | |
193 | strerror(-r)); | |
194 | return r; | |
195 | } | |
196 | ||
197 | if (address->family == AF_INET) { | |
198 | r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); | |
199 | if (r < 0) { | |
200 | log_error("Could not append IFA_BROADCAST attribute: %s", | |
201 | strerror(-r)); | |
202 | return r; | |
203 | } | |
204 | } | |
205 | ||
206 | if (address->label) { | |
207 | r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label); | |
208 | if (r < 0) { | |
209 | log_error("Could not append IFA_LABEL attribute: %s", | |
210 | strerror(-r)); | |
211 | return r; | |
212 | } | |
213 | } | |
214 | ||
215 | r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo); | |
216 | if (r < 0) { | |
217 | log_error("Could not append IFA_CACHEINFO attribute: %s", | |
218 | strerror(-r)); | |
219 | return r; | |
220 | } | |
221 | ||
222 | r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); | |
223 | if (r < 0) { | |
224 | log_error("Could not send rtnetlink message: %s", strerror(-r)); | |
225 | return r; | |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
11bf3cce LP |
231 | static int address_acquire(Link *link, Address *original, Address **ret) { |
232 | union in_addr_union in_addr = {}; | |
233 | struct in_addr broadcast = {}; | |
234 | Address *na = NULL; | |
235 | int r; | |
236 | ||
237 | assert(link); | |
238 | assert(original); | |
239 | assert(ret); | |
240 | ||
241 | /* Something useful was configured? just use it */ | |
242 | if (in_addr_null(original->family, &original->in_addr) <= 0) | |
243 | return 0; | |
244 | ||
245 | /* The address is configured to be 0.0.0.0 or [::] by the user? | |
246 | * Then let's acquire something more useful from the pool. */ | |
247 | r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); | |
248 | if (r < 0) { | |
249 | log_error_link(link, "Failed to acquire address from pool: %s", strerror(-r)); | |
250 | return r; | |
251 | } | |
252 | if (r == 0) { | |
253 | log_error_link(link, "Couldn't find free address for interface, all taken."); | |
254 | return -EBUSY; | |
255 | } | |
256 | ||
257 | if (original->family == AF_INET) { | |
258 | /* Pick first address in range for ourselves ...*/ | |
259 | in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1); | |
260 | ||
261 | /* .. and use last as broadcast address */ | |
262 | broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen); | |
263 | } else if (original->family == AF_INET6) | |
264 | in_addr.in6.s6_addr[15] |= 1; | |
265 | ||
266 | r = address_new_dynamic(&na); | |
267 | if (r < 0) | |
268 | return r; | |
269 | ||
270 | na->family = original->family; | |
271 | na->prefixlen = original->prefixlen; | |
272 | na->scope = original->scope; | |
273 | na->cinfo = original->cinfo; | |
274 | ||
275 | if (original->label) { | |
276 | na->label = strdup(original->label); | |
277 | ||
278 | if (!na->label) { | |
279 | free(na); | |
280 | return -ENOMEM; | |
281 | } | |
282 | } | |
283 | ||
284 | na->broadcast = broadcast; | |
285 | na->in_addr = in_addr; | |
286 | ||
287 | LIST_PREPEND(addresses, link->pool_addresses, na); | |
288 | ||
289 | *ret = na; | |
290 | return 0; | |
291 | } | |
292 | ||
f882c247 TG |
293 | int address_configure(Address *address, Link *link, |
294 | sd_rtnl_message_handler_t callback) { | |
cf6a8911 | 295 | _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; |
f579559b TG |
296 | int r; |
297 | ||
c166a070 TG |
298 | assert(address); |
299 | assert(address->family == AF_INET || address->family == AF_INET6); | |
300 | assert(link); | |
301 | assert(link->ifindex > 0); | |
f882c247 | 302 | assert(link->manager); |
c166a070 | 303 | assert(link->manager->rtnl); |
f882c247 | 304 | |
11bf3cce LP |
305 | r = address_acquire(link, address, &address); |
306 | if (r < 0) | |
307 | return r; | |
308 | ||
151b9b96 LP |
309 | r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR, |
310 | link->ifindex, address->family); | |
f579559b TG |
311 | if (r < 0) { |
312 | log_error("Could not allocate RTM_NEWADDR message: %s", | |
313 | strerror(-r)); | |
314 | return r; | |
315 | } | |
316 | ||
5a723174 TG |
317 | r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); |
318 | if (r < 0) { | |
319 | log_error("Could not set prefixlen: %s", strerror(-r)); | |
320 | return r; | |
321 | } | |
322 | ||
323 | r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); | |
324 | if (r < 0) { | |
325 | log_error("Could not set flags: %s", strerror(-r)); | |
326 | return r; | |
327 | } | |
328 | ||
5c1d3fc9 | 329 | r = sd_rtnl_message_addr_set_scope(req, address->scope); |
5a723174 TG |
330 | if (r < 0) { |
331 | log_error("Could not set scope: %s", strerror(-r)); | |
332 | return r; | |
333 | } | |
334 | ||
0a0dc69b TG |
335 | if (address->family == AF_INET) |
336 | r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); | |
337 | else if (address->family == AF_INET6) | |
338 | r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); | |
f579559b TG |
339 | if (r < 0) { |
340 | log_error("Could not append IFA_LOCAL attribute: %s", | |
341 | strerror(-r)); | |
342 | return r; | |
343 | } | |
344 | ||
345 | if (address->family == AF_INET) { | |
eb0ea358 | 346 | r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); |
f579559b TG |
347 | if (r < 0) { |
348 | log_error("Could not append IFA_BROADCAST attribute: %s", | |
349 | strerror(-r)); | |
350 | return r; | |
351 | } | |
352 | } | |
353 | ||
354 | if (address->label) { | |
0a0dc69b | 355 | r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label); |
f579559b TG |
356 | if (r < 0) { |
357 | log_error("Could not append IFA_LABEL attribute: %s", | |
358 | strerror(-r)); | |
359 | return r; | |
360 | } | |
361 | } | |
362 | ||
68ceb9df PF |
363 | r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO, |
364 | &address->cinfo); | |
365 | if (r < 0) { | |
366 | log_error("Could not append IFA_CACHEINFO attribute: %s", | |
367 | strerror(-r)); | |
368 | return r; | |
369 | } | |
370 | ||
f882c247 | 371 | r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); |
f579559b | 372 | if (r < 0) { |
f882c247 TG |
373 | log_error("Could not send rtnetlink message: %s", strerror(-r)); |
374 | return r; | |
f579559b TG |
375 | } |
376 | ||
f579559b TG |
377 | return 0; |
378 | } | |
379 | ||
3bef724f TG |
380 | int config_parse_dns(const char *unit, |
381 | const char *filename, | |
382 | unsigned line, | |
383 | const char *section, | |
384 | unsigned section_line, | |
385 | const char *lvalue, | |
386 | int ltype, | |
387 | const char *rvalue, | |
388 | void *data, | |
389 | void *userdata) { | |
d4920165 TG |
390 | Network *network = userdata; |
391 | Address *tail; | |
3bef724f TG |
392 | _cleanup_address_free_ Address *n = NULL; |
393 | int r; | |
394 | ||
395 | assert(filename); | |
396 | assert(section); | |
397 | assert(lvalue); | |
398 | assert(rvalue); | |
d4920165 | 399 | assert(network); |
3bef724f TG |
400 | |
401 | r = address_new_dynamic(&n); | |
402 | if (r < 0) | |
403 | return r; | |
404 | ||
405 | r = net_parse_inaddr(rvalue, &n->family, &n->in_addr); | |
406 | if (r < 0) { | |
407 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
408 | "DNS address is invalid, ignoring assignment: %s", rvalue); | |
409 | return 0; | |
410 | } | |
411 | ||
bcb7a07e TG |
412 | if (streq(lvalue, "DNS")) { |
413 | LIST_FIND_TAIL(addresses, network->dns, tail); | |
414 | LIST_INSERT_AFTER(addresses, network->dns, tail, n); | |
415 | } else if (streq(lvalue, "NTP")) { | |
416 | LIST_FIND_TAIL(addresses, network->ntp, tail); | |
417 | LIST_INSERT_AFTER(addresses, network->ntp, tail, n); | |
418 | } else { | |
419 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
420 | "Key is invalid, ignoring assignment: %s=%s", lvalue, rvalue); | |
421 | return 0; | |
422 | } | |
423 | ||
3bef724f TG |
424 | n = NULL; |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
eb0ea358 TG |
429 | int config_parse_broadcast(const char *unit, |
430 | const char *filename, | |
431 | unsigned line, | |
432 | const char *section, | |
433 | unsigned section_line, | |
434 | const char *lvalue, | |
435 | int ltype, | |
436 | const char *rvalue, | |
437 | void *data, | |
438 | void *userdata) { | |
439 | Network *network = userdata; | |
440 | _cleanup_address_free_ Address *n = NULL; | |
441 | _cleanup_free_ char *address = NULL; | |
442 | int r; | |
443 | ||
444 | assert(filename); | |
445 | assert(section); | |
446 | assert(lvalue); | |
447 | assert(rvalue); | |
448 | assert(data); | |
449 | ||
450 | r = address_new_static(network, section_line, &n); | |
451 | if (r < 0) | |
452 | return r; | |
453 | ||
482e2ac1 TG |
454 | if (n->family == AF_INET6) { |
455 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
456 | "Broadcast is not valid for IPv6 addresses, " | |
457 | "ignoring assignment: %s", address); | |
458 | return 0; | |
459 | } | |
460 | ||
eb0ea358 TG |
461 | r = net_parse_inaddr(address, &n->family, &n->broadcast); |
462 | if (r < 0) { | |
463 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
464 | "Broadcast is invalid, ignoring assignment: %s", address); | |
465 | return 0; | |
466 | } | |
467 | ||
468 | n = NULL; | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
f579559b TG |
473 | int config_parse_address(const char *unit, |
474 | const char *filename, | |
475 | unsigned line, | |
476 | const char *section, | |
71a61510 | 477 | unsigned section_line, |
f579559b TG |
478 | const char *lvalue, |
479 | int ltype, | |
480 | const char *rvalue, | |
481 | void *data, | |
482 | void *userdata) { | |
6ae115c1 | 483 | Network *network = userdata; |
f579559b TG |
484 | _cleanup_address_free_ Address *n = NULL; |
485 | _cleanup_free_ char *address = NULL; | |
486 | const char *e; | |
487 | int r; | |
488 | ||
489 | assert(filename); | |
6ae115c1 | 490 | assert(section); |
f579559b TG |
491 | assert(lvalue); |
492 | assert(rvalue); | |
493 | assert(data); | |
494 | ||
92fe133a TG |
495 | if (streq(section, "Network")) { |
496 | /* we are not in an Address section, so treat | |
497 | * this as the special '0' section */ | |
498 | section_line = 0; | |
499 | } | |
500 | ||
f048a16b | 501 | r = address_new_static(network, section_line, &n); |
f579559b TG |
502 | if (r < 0) |
503 | return r; | |
504 | ||
505 | /* Address=address/prefixlen */ | |
506 | ||
507 | /* prefixlen */ | |
508 | e = strchr(rvalue, '/'); | |
509 | if (e) { | |
510 | unsigned i; | |
511 | r = safe_atou(e + 1, &i); | |
512 | if (r < 0) { | |
513 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
514 | "Interface prefix length is invalid, " | |
515 | "ignoring assignment: %s", e + 1); | |
516 | return 0; | |
517 | } | |
518 | ||
519 | n->prefixlen = (unsigned char) i; | |
8cd11a0f | 520 | |
f579559b TG |
521 | address = strndup(rvalue, e - rvalue); |
522 | if (!address) | |
523 | return log_oom(); | |
524 | } else { | |
525 | address = strdup(rvalue); | |
526 | if (!address) | |
527 | return log_oom(); | |
528 | } | |
529 | ||
530 | r = net_parse_inaddr(address, &n->family, &n->in_addr); | |
531 | if (r < 0) { | |
532 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
533 | "Address is invalid, ignoring assignment: %s", address); | |
534 | return 0; | |
535 | } | |
536 | ||
eb0ea358 TG |
537 | if (n->family == AF_INET && !n->broadcast.s_addr) |
538 | n->broadcast.s_addr = n->in_addr.in.s_addr | | |
539 | htonl(0xfffffffflu >> n->prefixlen); | |
540 | ||
f579559b TG |
541 | n = NULL; |
542 | ||
543 | return 0; | |
544 | } | |
6ae115c1 TG |
545 | |
546 | int config_parse_label(const char *unit, | |
547 | const char *filename, | |
548 | unsigned line, | |
549 | const char *section, | |
550 | unsigned section_line, | |
551 | const char *lvalue, | |
552 | int ltype, | |
553 | const char *rvalue, | |
554 | void *data, | |
555 | void *userdata) { | |
556 | Network *network = userdata; | |
557 | _cleanup_address_free_ Address *n = NULL; | |
6ae115c1 TG |
558 | char *label; |
559 | int r; | |
560 | ||
561 | assert(filename); | |
562 | assert(section); | |
563 | assert(lvalue); | |
564 | assert(rvalue); | |
565 | assert(data); | |
566 | ||
f048a16b | 567 | r = address_new_static(network, section_line, &n); |
6ae115c1 TG |
568 | if (r < 0) |
569 | return r; | |
570 | ||
571 | label = strdup(rvalue); | |
572 | if (!label) | |
573 | return log_oom(); | |
574 | ||
575 | if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { | |
576 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
577 | "Interface label is not ASCII clean or is too" | |
578 | " long, ignoring assignment: %s", rvalue); | |
579 | free(label); | |
580 | return 0; | |
581 | } | |
582 | ||
583 | free(n->label); | |
584 | if (*label) | |
585 | n->label = label; | |
586 | else { | |
587 | free(label); | |
588 | n->label = NULL; | |
589 | } | |
590 | ||
591 | n = NULL; | |
592 | ||
593 | return 0; | |
594 | } | |
9505d3c6 TG |
595 | |
596 | bool address_equal(Address *a1, Address *a2) { | |
597 | /* same object */ | |
598 | if (a1 == a2) | |
599 | return true; | |
600 | ||
601 | /* one, but not both, is NULL */ | |
602 | if (!a1 || !a2) | |
603 | return false; | |
604 | ||
605 | if (a1->family != a2->family) | |
606 | return false; | |
607 | ||
608 | switch (a1->family) { | |
609 | /* use the same notion of equality as the kernel does */ | |
610 | case AF_UNSPEC: | |
611 | return true; | |
612 | ||
613 | case AF_INET: | |
614 | if (a1->prefixlen != a2->prefixlen) | |
615 | return false; | |
616 | else { | |
617 | uint32_t b1, b2; | |
618 | ||
619 | b1 = be32toh(a1->in_addr.in.s_addr); | |
620 | b2 = be32toh(a2->in_addr.in.s_addr); | |
621 | ||
622 | return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen)); | |
623 | } | |
624 | ||
625 | case AF_INET6: | |
626 | { | |
627 | uint64_t *b1, *b2; | |
628 | ||
629 | b1 = (uint64_t*)&a1->in_addr.in6; | |
630 | b2 = (uint64_t*)&a2->in_addr.in6; | |
631 | ||
632 | return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL); | |
633 | } | |
634 | default: | |
635 | assert_not_reached("Invalid address family"); | |
636 | } | |
637 | } |