]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-address.c
Merge pull request #1510 from mirco/fix-nspawn
[thirdparty/systemd.git] / src / network / networkd-address.c
CommitLineData
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
f579559b
TG
24#include "utf8.h"
25#include "util.h"
26#include "conf-parser.h"
12c2884c 27#include "firewall-util.h"
fc2f9534
LP
28#include "netlink-util.h"
29
5a8bcb67 30#include "networkd.h"
fc2f9534 31#include "networkd-address.h"
f579559b 32
aba496a5
UTL
33static void address_init(Address *address) {
34 assert(address);
35
36 address->family = AF_UNSPEC;
37 address->scope = RT_SCOPE_UNIVERSE;
38 address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
39 address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
40}
41
f048a16b 42int address_new_static(Network *network, unsigned section, Address **ret) {
f579559b
TG
43 _cleanup_address_free_ Address *address = NULL;
44
6ae115c1 45 if (section) {
16aa63a0 46 address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
6ae115c1
TG
47 if (address) {
48 *ret = address;
49 address = NULL;
50
51 return 0;
52 }
53 }
54
f579559b
TG
55 address = new0(Address, 1);
56 if (!address)
57 return -ENOMEM;
58
aba496a5 59 address_init(address);
801bd9e8 60
f579559b
TG
61 address->network = network;
62
1e39ff92 63 LIST_APPEND(addresses, network->static_addresses, address);
f579559b 64
6ae115c1
TG
65 if (section) {
66 address->section = section;
16aa63a0
TG
67 hashmap_put(network->addresses_by_section,
68 UINT_TO_PTR(address->section), address);
6ae115c1
TG
69 }
70
f579559b
TG
71 *ret = address;
72 address = NULL;
73
74 return 0;
75}
76
f048a16b
TG
77int address_new_dynamic(Address **ret) {
78 _cleanup_address_free_ Address *address = NULL;
79
80 address = new0(Address, 1);
81 if (!address)
82 return -ENOMEM;
83
aba496a5 84 address_init(address);
801bd9e8 85
f048a16b
TG
86 *ret = address;
87 address = NULL;
88
89 return 0;
90}
91
f579559b
TG
92void address_free(Address *address) {
93 if (!address)
94 return;
95
f048a16b 96 if (address->network) {
3d3d4255 97 LIST_REMOVE(addresses, address->network->static_addresses, address);
f579559b 98
f048a16b
TG
99 if (address->section)
100 hashmap_remove(address->network->addresses_by_section,
16aa63a0 101 UINT_TO_PTR(address->section));
f048a16b 102 }
6ae115c1 103
f579559b
TG
104 free(address);
105}
106
5a8bcb67
LP
107int address_establish(Address *address, Link *link) {
108 bool masq;
109 int r;
110
111 assert(address);
112 assert(link);
113
114 masq = link->network &&
115 link->network->ip_masquerade &&
116 address->family == AF_INET &&
117 address->scope < RT_SCOPE_LINK;
118
119 /* Add firewall entry if this is requested */
fd6d906c 120 if (address->ip_masquerade_done != masq) {
5a8bcb67
LP
121 union in_addr_union masked = address->in_addr;
122 in_addr_mask(address->family, &masked, address->prefixlen);
123
124 r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
125 if (r < 0)
126 log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
127
fd6d906c 128 address->ip_masquerade_done = masq;
5a8bcb67
LP
129 }
130
131 return 0;
132}
133
134int address_release(Address *address, Link *link) {
135 int r;
136
137 assert(address);
138 assert(link);
139
140 /* Remove masquerading firewall entry if it was added */
fd6d906c 141 if (address->ip_masquerade_done) {
5a8bcb67
LP
142 union in_addr_union masked = address->in_addr;
143 in_addr_mask(address->family, &masked, address->prefixlen);
144
145 r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
146 if (r < 0)
147 log_link_warning_errno(link, r, "Failed to disable IP masquerading: %m");
148
fd6d906c 149 address->ip_masquerade_done = false;
5a8bcb67
LP
150 }
151
152 return 0;
153}
154
407fe036 155int address_drop(Address *address, Link *link,
1c4baffc
TG
156 sd_netlink_message_handler_t callback) {
157 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
407fe036
TG
158 int r;
159
160 assert(address);
161 assert(address->family == AF_INET || address->family == AF_INET6);
162 assert(link);
163 assert(link->ifindex > 0);
164 assert(link->manager);
165 assert(link->manager->rtnl);
166
5a8bcb67
LP
167 address_release(address, link);
168
151b9b96
LP
169 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
170 link->ifindex, address->family);
eb56eb9b
MS
171 if (r < 0)
172 return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
407fe036 173
5a723174 174 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
eb56eb9b
MS
175 if (r < 0)
176 return log_error_errno(r, "Could not set prefixlen: %m");
5a723174 177
407fe036 178 if (address->family == AF_INET)
1c4baffc 179 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
407fe036 180 else if (address->family == AF_INET6)
1c4baffc 181 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
eb56eb9b
MS
182 if (r < 0)
183 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
407fe036 184
1c4baffc 185 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
eb56eb9b
MS
186 if (r < 0)
187 return log_error_errno(r, "Could not send rtnetlink message: %m");
407fe036 188
563c69c6
TG
189 link_ref(link);
190
407fe036
TG
191 return 0;
192}
193
aba496a5 194int address_update(Address *address, Link *link,
1c4baffc
TG
195 sd_netlink_message_handler_t callback) {
196 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
aba496a5
UTL
197 int r;
198
199 assert(address);
200 assert(address->family == AF_INET || address->family == AF_INET6);
201 assert(link->ifindex > 0);
202 assert(link->manager);
203 assert(link->manager->rtnl);
204
205 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
206 link->ifindex, address->family);
eb56eb9b
MS
207 if (r < 0)
208 return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
aba496a5
UTL
209
210 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
eb56eb9b
MS
211 if (r < 0)
212 return log_error_errno(r, "Could not set prefixlen: %m");
aba496a5 213
851c9f82
PF
214 address->flags |= IFA_F_PERMANENT;
215
216 r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff);
eb56eb9b
MS
217 if (r < 0)
218 return log_error_errno(r, "Could not set flags: %m");
aba496a5 219
be3a09b7 220 if (address->flags & ~0xff && link->rtnl_extended_attrs) {
1c4baffc 221 r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
851c9f82
PF
222 if (r < 0)
223 return log_error_errno(r, "Could not set extended flags: %m");
224 }
225
aba496a5 226 r = sd_rtnl_message_addr_set_scope(req, address->scope);
eb56eb9b
MS
227 if (r < 0)
228 return log_error_errno(r, "Could not set scope: %m");
aba496a5
UTL
229
230 if (address->family == AF_INET)
1c4baffc 231 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
aba496a5 232 else if (address->family == AF_INET6)
1c4baffc 233 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
eb56eb9b
MS
234 if (r < 0)
235 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
aba496a5
UTL
236
237 if (address->family == AF_INET) {
1c4baffc 238 r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
eb56eb9b
MS
239 if (r < 0)
240 return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
aba496a5
UTL
241 }
242
243 if (address->label) {
1c4baffc 244 r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
eb56eb9b
MS
245 if (r < 0)
246 return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
aba496a5
UTL
247 }
248
1c4baffc 249 r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo);
eb56eb9b
MS
250 if (r < 0)
251 return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
aba496a5 252
1c4baffc 253 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
eb56eb9b
MS
254 if (r < 0)
255 return log_error_errno(r, "Could not send rtnetlink message: %m");
aba496a5 256
563c69c6
TG
257 link_ref(link);
258
aba496a5
UTL
259 return 0;
260}
261
11bf3cce
LP
262static int address_acquire(Link *link, Address *original, Address **ret) {
263 union in_addr_union in_addr = {};
264 struct in_addr broadcast = {};
0099bc15 265 _cleanup_address_free_ Address *na = NULL;
11bf3cce
LP
266 int r;
267
268 assert(link);
269 assert(original);
270 assert(ret);
271
272 /* Something useful was configured? just use it */
af93291c 273 if (in_addr_is_null(original->family, &original->in_addr) <= 0)
11bf3cce
LP
274 return 0;
275
276 /* The address is configured to be 0.0.0.0 or [::] by the user?
277 * Then let's acquire something more useful from the pool. */
278 r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
6a7a4e4d
LP
279 if (r < 0)
280 return log_link_error_errno(link, r, "Failed to acquire address from pool: %m");
11bf3cce 281 if (r == 0) {
79008bdd 282 log_link_error(link, "Couldn't find free address for interface, all taken.");
11bf3cce
LP
283 return -EBUSY;
284 }
285
286 if (original->family == AF_INET) {
d076c6f9 287 /* Pick first address in range for ourselves ... */
11bf3cce
LP
288 in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
289
290 /* .. and use last as broadcast address */
291 broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
292 } else if (original->family == AF_INET6)
293 in_addr.in6.s6_addr[15] |= 1;
294
295 r = address_new_dynamic(&na);
296 if (r < 0)
297 return r;
298
299 na->family = original->family;
300 na->prefixlen = original->prefixlen;
301 na->scope = original->scope;
302 na->cinfo = original->cinfo;
303
304 if (original->label) {
305 na->label = strdup(original->label);
0099bc15 306 if (!na->label)
11bf3cce 307 return -ENOMEM;
11bf3cce
LP
308 }
309
310 na->broadcast = broadcast;
311 na->in_addr = in_addr;
312
313 LIST_PREPEND(addresses, link->pool_addresses, na);
314
315 *ret = na;
0099bc15
SS
316 na = NULL;
317
11bf3cce
LP
318 return 0;
319}
320
f882c247 321int address_configure(Address *address, Link *link,
1c4baffc
TG
322 sd_netlink_message_handler_t callback) {
323 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
f579559b
TG
324 int r;
325
c166a070
TG
326 assert(address);
327 assert(address->family == AF_INET || address->family == AF_INET6);
328 assert(link);
329 assert(link->ifindex > 0);
f882c247 330 assert(link->manager);
c166a070 331 assert(link->manager->rtnl);
f882c247 332
11bf3cce
LP
333 r = address_acquire(link, address, &address);
334 if (r < 0)
335 return r;
336
151b9b96
LP
337 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
338 link->ifindex, address->family);
eb56eb9b
MS
339 if (r < 0)
340 return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
f579559b 341
5a723174 342 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
eb56eb9b
MS
343 if (r < 0)
344 return log_error_errno(r, "Could not set prefixlen: %m");
5a723174 345
851c9f82
PF
346 address->flags |= IFA_F_PERMANENT;
347
348 r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
eb56eb9b
MS
349 if (r < 0)
350 return log_error_errno(r, "Could not set flags: %m");
5a723174 351
851c9f82 352 if (address->flags & ~0xff) {
1c4baffc 353 r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
851c9f82
PF
354 if (r < 0)
355 return log_error_errno(r, "Could not set extended flags: %m");
356 }
357
5c1d3fc9 358 r = sd_rtnl_message_addr_set_scope(req, address->scope);
eb56eb9b
MS
359 if (r < 0)
360 return log_error_errno(r, "Could not set scope: %m");
5a723174 361
0a0dc69b 362 if (address->family == AF_INET)
1c4baffc 363 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
0a0dc69b 364 else if (address->family == AF_INET6)
1c4baffc 365 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
eb56eb9b
MS
366 if (r < 0)
367 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
f579559b 368
af93291c 369 if (!in_addr_is_null(address->family, &address->in_addr_peer)) {
c081882f 370 if (address->family == AF_INET)
1c4baffc 371 r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in);
c081882f 372 else if (address->family == AF_INET6)
1c4baffc 373 r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6);
eb56eb9b
MS
374 if (r < 0)
375 return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m");
c081882f
SS
376 } else {
377 if (address->family == AF_INET) {
1c4baffc 378 r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
eb56eb9b
MS
379 if (r < 0)
380 return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
c081882f 381 }
f579559b
TG
382 }
383
384 if (address->label) {
1c4baffc 385 r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
eb56eb9b
MS
386 if (r < 0)
387 return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
f579559b
TG
388 }
389
1c4baffc 390 r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO,
68ceb9df 391 &address->cinfo);
eb56eb9b
MS
392 if (r < 0)
393 return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
68ceb9df 394
1c4baffc 395 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
eb56eb9b
MS
396 if (r < 0)
397 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 398
563c69c6
TG
399 link_ref(link);
400
5a8bcb67
LP
401 address_establish(address, link);
402
f579559b
TG
403 return 0;
404}
405
44e7b949
LP
406int config_parse_broadcast(
407 const char *unit,
eb0ea358
TG
408 const char *filename,
409 unsigned line,
410 const char *section,
411 unsigned section_line,
412 const char *lvalue,
413 int ltype,
414 const char *rvalue,
415 void *data,
416 void *userdata) {
44e7b949 417
eb0ea358
TG
418 Network *network = userdata;
419 _cleanup_address_free_ Address *n = NULL;
eb0ea358
TG
420 int r;
421
422 assert(filename);
423 assert(section);
424 assert(lvalue);
425 assert(rvalue);
426 assert(data);
427
428 r = address_new_static(network, section_line, &n);
429 if (r < 0)
430 return r;
431
482e2ac1 432 if (n->family == AF_INET6) {
12ca818f 433 log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
482e2ac1
TG
434 return 0;
435 }
436
44e7b949 437 r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
eb0ea358 438 if (r < 0) {
12ca818f 439 log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue);
eb0ea358
TG
440 return 0;
441 }
442
44e7b949 443 n->family = AF_INET;
eb0ea358
TG
444 n = NULL;
445
446 return 0;
447}
448
f579559b
TG
449int config_parse_address(const char *unit,
450 const char *filename,
451 unsigned line,
452 const char *section,
71a61510 453 unsigned section_line,
f579559b
TG
454 const char *lvalue,
455 int ltype,
456 const char *rvalue,
457 void *data,
458 void *userdata) {
44e7b949 459
6ae115c1 460 Network *network = userdata;
f579559b 461 _cleanup_address_free_ Address *n = NULL;
44e7b949
LP
462 const char *address, *e;
463 union in_addr_union buffer;
464 int r, f;
f579559b
TG
465
466 assert(filename);
6ae115c1 467 assert(section);
f579559b
TG
468 assert(lvalue);
469 assert(rvalue);
470 assert(data);
471
92fe133a
TG
472 if (streq(section, "Network")) {
473 /* we are not in an Address section, so treat
474 * this as the special '0' section */
475 section_line = 0;
476 }
477
f048a16b 478 r = address_new_static(network, section_line, &n);
f579559b
TG
479 if (r < 0)
480 return r;
481
482 /* Address=address/prefixlen */
483
484 /* prefixlen */
485 e = strchr(rvalue, '/');
486 if (e) {
487 unsigned i;
12ca818f 488
f579559b
TG
489 r = safe_atou(e + 1, &i);
490 if (r < 0) {
12ca818f 491 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
f579559b
TG
492 return 0;
493 }
494
495 n->prefixlen = (unsigned char) i;
8cd11a0f 496
44e7b949
LP
497 address = strndupa(rvalue, e - rvalue);
498 } else
499 address = rvalue;
f579559b 500
44e7b949 501 r = in_addr_from_string_auto(address, &f, &buffer);
f579559b 502 if (r < 0) {
12ca818f 503 log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
f579559b
TG
504 return 0;
505 }
506
a2a85a22
TG
507 if (!e && f == AF_INET) {
508 r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
509 if (r < 0) {
12ca818f 510 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
a2a85a22
TG
511 return 0;
512 }
513 }
514
44e7b949 515 if (n->family != AF_UNSPEC && f != n->family) {
12ca818f 516 log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
44e7b949
LP
517 return 0;
518 }
519
520 n->family = f;
521
522 if (streq(lvalue, "Address"))
523 n->in_addr = buffer;
524 else
525 n->in_addr_peer = buffer;
526
527 if (n->family == AF_INET && n->broadcast.s_addr == 0)
528 n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen);
eb0ea358 529
f579559b
TG
530 n = NULL;
531
532 return 0;
533}
6ae115c1
TG
534
535int config_parse_label(const char *unit,
536 const char *filename,
537 unsigned line,
538 const char *section,
539 unsigned section_line,
540 const char *lvalue,
541 int ltype,
542 const char *rvalue,
543 void *data,
544 void *userdata) {
545 Network *network = userdata;
546 _cleanup_address_free_ Address *n = NULL;
6ae115c1
TG
547 char *label;
548 int r;
549
550 assert(filename);
551 assert(section);
552 assert(lvalue);
553 assert(rvalue);
554 assert(data);
555
f048a16b 556 r = address_new_static(network, section_line, &n);
6ae115c1
TG
557 if (r < 0)
558 return r;
559
560 label = strdup(rvalue);
561 if (!label)
562 return log_oom();
563
564 if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
12ca818f 565 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
6ae115c1
TG
566 free(label);
567 return 0;
568 }
569
570 free(n->label);
571 if (*label)
572 n->label = label;
573 else {
574 free(label);
575 n->label = NULL;
576 }
577
578 n = NULL;
579
580 return 0;
581}
9505d3c6
TG
582
583bool address_equal(Address *a1, Address *a2) {
584 /* same object */
585 if (a1 == a2)
586 return true;
587
588 /* one, but not both, is NULL */
589 if (!a1 || !a2)
590 return false;
591
592 if (a1->family != a2->family)
593 return false;
594
595 switch (a1->family) {
596 /* use the same notion of equality as the kernel does */
597 case AF_UNSPEC:
598 return true;
599
600 case AF_INET:
601 if (a1->prefixlen != a2->prefixlen)
602 return false;
6cb8e687
ZJS
603 else if (a1->prefixlen == 0)
604 /* make sure we don't try to shift by 32.
605 * See ISO/IEC 9899:TC3 § 6.5.7.3. */
606 return true;
9505d3c6
TG
607 else {
608 uint32_t b1, b2;
609
610 b1 = be32toh(a1->in_addr.in.s_addr);
611 b2 = be32toh(a2->in_addr.in.s_addr);
612
613 return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
614 }
615
5a8bcb67 616 case AF_INET6: {
9505d3c6
TG
617 uint64_t *b1, *b2;
618
619 b1 = (uint64_t*)&a1->in_addr.in6;
620 b2 = (uint64_t*)&a2->in_addr.in6;
621
622 return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
623 }
5a8bcb67 624
9505d3c6
TG
625 default:
626 assert_not_reached("Invalid address family");
627 }
628}