]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: netdev - split NetDev struct into per-kind structs
[thirdparty/systemd.git] / src / network / networkd-netdev.c
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-netdev.h"
25 #include "network-internal.h"
26 #include "path-util.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
29 #include "list.h"
30 #include "siphash24.h"
31
32 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
33 [NETDEV_KIND_BRIDGE] = &bridge_vtable,
34 [NETDEV_KIND_BOND] = &bond_vtable,
35 [NETDEV_KIND_VLAN] = &vlan_vtable,
36 [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
37 [NETDEV_KIND_VXLAN] = &vxlan_vtable,
38 [NETDEV_KIND_IPIP] = &ipip_vtable,
39 [NETDEV_KIND_GRE] = &gre_vtable,
40 [NETDEV_KIND_SIT] = &sit_vtable,
41 [NETDEV_KIND_VTI] = &vti_vtable,
42 [NETDEV_KIND_VETH] = &veth_vtable,
43 [NETDEV_KIND_DUMMY] = &dummy_vtable,
44 [NETDEV_KIND_TUN] = &tun_vtable,
45 [NETDEV_KIND_TAP] = &tap_vtable,
46 };
47
48 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
49 [NETDEV_KIND_BRIDGE] = "bridge",
50 [NETDEV_KIND_BOND] = "bond",
51 [NETDEV_KIND_VLAN] = "vlan",
52 [NETDEV_KIND_MACVLAN] = "macvlan",
53 [NETDEV_KIND_VXLAN] = "vxlan",
54 [NETDEV_KIND_IPIP] = "ipip",
55 [NETDEV_KIND_GRE] = "gre",
56 [NETDEV_KIND_SIT] = "sit",
57 [NETDEV_KIND_VETH] = "veth",
58 [NETDEV_KIND_VTI] = "vti",
59 [NETDEV_KIND_DUMMY] = "dummy",
60 [NETDEV_KIND_TUN] = "tun",
61 [NETDEV_KIND_TAP] = "tap",
62 };
63
64 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
65 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
66
67
68 static void netdev_cancel_callbacks(NetDev *netdev) {
69 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
70 netdev_join_callback *callback;
71
72 if (!netdev)
73 return;
74
75 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
76
77 while ((callback = netdev->callbacks)) {
78 if (m) {
79 assert(callback->link);
80 assert(callback->callback);
81 assert(netdev->manager);
82 assert(netdev->manager->rtnl);
83
84 callback->callback(netdev->manager->rtnl, m, link);
85 }
86
87 LIST_REMOVE(callbacks, netdev->callbacks, callback);
88 free(callback);
89 }
90 }
91
92 static void netdev_free(NetDev *netdev) {
93 if (!netdev)
94 return;
95
96 netdev_cancel_callbacks(netdev);
97
98 if (netdev->ifname)
99 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
100
101 free(netdev->filename);
102
103 free(netdev->description);
104 free(netdev->ifname);
105 free(netdev->mac);
106
107 condition_free_list(netdev->match_host);
108 condition_free_list(netdev->match_virt);
109 condition_free_list(netdev->match_kernel);
110 condition_free_list(netdev->match_arch);
111
112 if (NETDEV_VTABLE(netdev) &&
113 NETDEV_VTABLE(netdev)->done)
114 NETDEV_VTABLE(netdev)->done(netdev);
115
116 free(netdev);
117 }
118
119 NetDev *netdev_unref(NetDev *netdev) {
120 if (netdev && (-- netdev->n_ref <= 0))
121 netdev_free(netdev);
122
123 return NULL;
124 }
125
126 NetDev *netdev_ref(NetDev *netdev) {
127 if (netdev)
128 assert_se(++ netdev->n_ref >= 2);
129
130 return netdev;
131 }
132
133 void netdev_drop(NetDev *netdev) {
134 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
135 return;
136
137 netdev->state = NETDEV_STATE_LINGER;
138
139 log_debug_netdev(netdev, "netdev removed");
140
141 netdev_cancel_callbacks(netdev);
142
143 netdev_unref(netdev);
144
145 return;
146 }
147
148 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
149 NetDev *netdev;
150
151 assert(manager);
152 assert(name);
153 assert(ret);
154
155 netdev = hashmap_get(manager->netdevs, name);
156 if (!netdev) {
157 *ret = NULL;
158 return -ENOENT;
159 }
160
161 *ret = netdev;
162
163 return 0;
164 }
165
166 static int netdev_enter_failed(NetDev *netdev) {
167 netdev->state = NETDEV_STATE_FAILED;
168
169 return 0;
170 }
171
172 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
173 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
174 int r;
175
176 assert(netdev);
177 assert(netdev->state == NETDEV_STATE_READY);
178 assert(netdev->manager);
179 assert(netdev->manager->rtnl);
180 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
181 assert(link);
182 assert(callback);
183
184 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
185 RTM_SETLINK, link->ifindex);
186 if (r < 0) {
187 log_error_netdev(netdev,
188 "Could not allocate RTM_SETLINK message: %s",
189 strerror(-r));
190 return r;
191 }
192
193 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
194 if (r < 0) {
195 log_error_netdev(netdev,
196 "Could not append IFLA_MASTER attribute: %s",
197 strerror(-r));
198 return r;
199 }
200
201 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
202 if (r < 0) {
203 log_error_netdev(netdev,
204 "Could not send rtnetlink message: %s",
205 strerror(-r));
206 return r;
207 }
208
209 link_ref(link);
210
211 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
212
213 return 0;
214 }
215
216 static int netdev_enter_ready(NetDev *netdev) {
217 netdev_join_callback *callback, *callback_next;
218 int r;
219
220 assert(netdev);
221 assert(netdev->ifname);
222
223 if (netdev->state != NETDEV_STATE_CREATING)
224 return 0;
225
226 netdev->state = NETDEV_STATE_READY;
227
228 log_info_netdev(netdev, "netdev ready");
229
230 LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
231 /* enslave the links that were attempted to be enslaved before the
232 * link was ready */
233 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
234 if (r < 0)
235 return r;
236
237 LIST_REMOVE(callbacks, netdev->callbacks, callback);
238 link_unref(callback->link);
239 free(callback);
240 }
241
242 return 0;
243 }
244
245 /* callback for netdev's created without a backing Link */
246 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
247 _cleanup_netdev_unref_ NetDev *netdev = userdata;
248 int r;
249
250 assert(netdev->state != _NETDEV_STATE_INVALID);
251
252 r = sd_rtnl_message_get_errno(m);
253 if (r == -EEXIST)
254 log_debug_netdev(netdev, "netdev exists, using existing");
255 else if (r < 0) {
256 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
257 netdev_drop(netdev);
258
259 return 1;
260 }
261
262 log_debug_netdev(netdev, "created");
263
264 return 1;
265 }
266
267 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
268 int r;
269
270 assert(netdev);
271 assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
272
273 if (netdev->state == NETDEV_STATE_READY) {
274 r = netdev_enslave_ready(netdev, link, callback);
275 if (r < 0)
276 return r;
277 } else {
278 /* the netdev is not yet read, save this request for when it is*/
279 netdev_join_callback *cb;
280
281 cb = new0(netdev_join_callback, 1);
282 if (!cb)
283 return log_oom();
284
285 cb->callback = callback;
286 cb->link = link;
287 link_ref(link);
288
289 LIST_PREPEND(callbacks, netdev->callbacks, cb);
290
291 log_debug_netdev(netdev, "will enslave '%s', when reday",
292 link->ifname);
293 }
294
295 return 0;
296 }
297
298 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
299 uint16_t type;
300 const char *kind;
301 const char *received_kind;
302 const char *received_name;
303 int r, ifindex;
304
305 assert(netdev);
306 assert(message);
307
308 r = sd_rtnl_message_get_type(message, &type);
309 if (r < 0) {
310 log_error_netdev(netdev, "Could not get rtnl message type");
311 return r;
312 }
313
314 if (type != RTM_NEWLINK) {
315 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
316 return -EINVAL;
317 }
318
319 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
320 if (r < 0) {
321 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
322 netdev_enter_failed(netdev);
323 return r;
324 } else if (ifindex <= 0) {
325 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
326 netdev_enter_failed(netdev);
327 return r;
328 }
329
330 if (netdev->ifindex > 0) {
331 if (netdev->ifindex != ifindex) {
332 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
333 ifindex, netdev->ifindex);
334 netdev_enter_failed(netdev);
335 return -EEXIST;
336 } else
337 /* ifindex already set to the same for this netdev */
338 return 0;
339 }
340
341 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
342 if (r < 0) {
343 log_error_netdev(netdev, "Could not get IFNAME");
344 return r;
345 }
346
347 if (!streq(netdev->ifname, received_name)) {
348 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
349 received_name);
350 netdev_enter_failed(netdev);
351 return r;
352 }
353
354 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
355 if (r < 0) {
356 log_error_netdev(netdev, "Could not get LINKINFO");
357 return r;
358 }
359
360 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
361 if (r < 0) {
362 log_error_netdev(netdev, "Could not get KIND");
363 return r;
364 }
365
366 r = sd_rtnl_message_exit_container(message);
367 if (r < 0) {
368 log_error_netdev(netdev, "Could not exit container");
369 return r;
370 }
371
372 if (netdev->kind == NETDEV_KIND_TAP)
373 /* the kernel does not distinguish between tun and tap */
374 kind = "tun";
375 else {
376 kind = netdev_kind_to_string(netdev->kind);
377 if (!kind) {
378 log_error_netdev(netdev, "Could not get kind");
379 netdev_enter_failed(netdev);
380 return -EINVAL;
381 }
382 }
383
384 if (!streq(kind, received_kind)) {
385 log_error_netdev(netdev,
386 "Received newlink with wrong KIND %s, "
387 "expected %s", received_kind, kind);
388 netdev_enter_failed(netdev);
389 return r;
390 }
391
392 netdev->ifindex = ifindex;
393
394 log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
395
396 netdev_enter_ready(netdev);
397
398 return 0;
399 }
400
401 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
402
403 int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
404 _cleanup_free_ struct ether_addr *mac = NULL;
405 uint8_t result[8];
406 size_t l, sz;
407 uint8_t *v;
408 int r;
409
410 assert(ifname);
411 assert(ret);
412
413 mac = new0(struct ether_addr, 1);
414 if (!mac)
415 return -ENOMEM;
416
417 l = strlen(ifname);
418 sz = sizeof(sd_id128_t) + l;
419 v = alloca(sz);
420
421 /* fetch some persistent data unique to the machine */
422 r = sd_id128_get_machine((sd_id128_t*) v);
423 if (r < 0)
424 return r;
425
426 /* combine with some data unique (on this machine) to this
427 * netdev */
428 memcpy(v + sizeof(sd_id128_t), ifname, l);
429
430 /* Let's hash the host machine ID plus the container name. We
431 * use a fixed, but originally randomly created hash key here. */
432 siphash24(result, v, sz, HASH_KEY.bytes);
433
434 assert_cc(ETH_ALEN <= sizeof(result));
435 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
436
437 /* see eth_random_addr in the kernel */
438 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
439 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
440
441 *ret = mac;
442 mac = NULL;
443
444 return 0;
445 }
446
447 static int netdev_create(NetDev *netdev, Link *link) {
448 int r;
449
450 assert(netdev);
451
452 /* create netdev */
453 if (NETDEV_VTABLE(netdev)->create) {
454 r = NETDEV_VTABLE(netdev)->create(netdev);
455 if (r < 0)
456 return r;
457
458 log_debug_netdev(netdev, "created");
459 } else {
460 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
461
462 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
463 if (r < 0) {
464 log_error_netdev(netdev,
465 "Could not allocate RTM_NEWLINK message: %s",
466 strerror(-r));
467 return r;
468 }
469
470 r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname);
471 if (r < 0) {
472 log_error_netdev(netdev,
473 "Could not append IFLA_IFNAME, attribute: %s",
474 strerror(-r));
475 return r;
476 }
477
478 if (netdev->mac) {
479 r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
480 if (r < 0) {
481 log_error_netdev(netdev,
482 "Could not append IFLA_ADDRESS attribute: %s",
483 strerror(-r));
484 return r;
485 }
486 }
487
488 if (netdev->mtu) {
489 r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu);
490 if (r < 0) {
491 log_error_netdev(netdev,
492 "Could not append IFLA_MTU attribute: %s",
493 strerror(-r));
494 return r;
495 }
496 }
497
498 if (link) {
499 r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex);
500 if (r < 0) {
501 log_error_netdev(netdev,
502 "Colud not append IFLA_LINK attribute: %s",
503 strerror(-r));
504 return r;
505 }
506 }
507
508 r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
509 if (r < 0) {
510 log_error_netdev(netdev,
511 "Could not append IFLA_LINKINFO attribute: %s",
512 strerror(-r));
513 return r;
514 }
515
516 r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA,
517 netdev_kind_to_string(netdev->kind));
518 if (r < 0) {
519 log_error_netdev(netdev,
520 "Could not append IFLA_INFO_DATA attribute: %s",
521 strerror(-r));
522 return r;
523 }
524
525 if (NETDEV_VTABLE(netdev)->fill_message_create) {
526 r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
527 if (r < 0)
528 return r;
529 }
530
531 r = sd_rtnl_message_close_container(m);
532 if (r < 0) {
533 log_error_netdev(netdev,
534 "Could not append IFLA_LINKINFO attribute: %s",
535 strerror(-r));
536 return r;
537 }
538
539 r = sd_rtnl_message_close_container(m);
540 if (r < 0) {
541 log_error_netdev(netdev,
542 "Could not append IFLA_LINKINFO attribute: %s",
543 strerror(-r));
544 return r;
545 }
546
547
548 r = sd_rtnl_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
549 if (r < 0) {
550 log_error_netdev(netdev,
551 "Could not send rtnetlink message: %s", strerror(-r));
552 return r;
553 }
554
555 netdev_ref(netdev);
556
557 netdev->state = NETDEV_STATE_CREATING;
558
559 log_debug_netdev(netdev, "creating");
560 }
561
562 return 0;
563 }
564
565 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
566 int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
567 int r;
568
569 assert(netdev);
570 assert(netdev->manager);
571 assert(netdev->manager->rtnl);
572 assert(NETDEV_VTABLE(netdev));
573
574 switch (NETDEV_VTABLE(netdev)->create_type) {
575 case NETDEV_CREATE_MASTER:
576 r = netdev_enslave(netdev, link, callback);
577 if (r < 0)
578 return r;
579
580 break;
581 case NETDEV_CREATE_STACKED:
582 r = netdev_create(netdev, link);
583 if (r < 0)
584 return r;
585
586 break;
587 default:
588 assert_not_reached("Can not join independent netdev");
589 }
590
591 return 0;
592 }
593
594 static int netdev_load_one(Manager *manager, const char *filename) {
595 _cleanup_netdev_unref_ NetDev *netdev = NULL;
596 _cleanup_free_ NetDev *netdev_raw = NULL;
597 _cleanup_fclose_ FILE *file = NULL;
598 int r;
599
600 assert(manager);
601 assert(filename);
602
603 file = fopen(filename, "re");
604 if (!file) {
605 if (errno == ENOENT)
606 return 0;
607 else
608 return -errno;
609 }
610
611 if (null_or_empty_fd(fileno(file))) {
612 log_debug("Skipping empty file: %s", filename);
613 return 0;
614 }
615
616 netdev_raw = new0(NetDev, 1);
617 if (!netdev_raw)
618 return log_oom();
619
620 netdev_raw->kind = _NETDEV_KIND_INVALID;
621
622 r = config_parse(NULL, filename, file,
623 "Match\0NetDev\0",
624 config_item_perf_lookup, network_netdev_gperf_lookup,
625 true, false, true, netdev_raw);
626 if (r < 0)
627 return r;
628
629 r = fseek(file, 0, SEEK_SET);
630 if (r < 0)
631 return -errno;
632
633 /* skip out early if configuration does not match the environment */
634 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
635 netdev_raw->match_host, netdev_raw->match_virt,
636 netdev_raw->match_kernel, netdev_raw->match_arch,
637 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
638 return 0;
639
640 if (!NETDEV_VTABLE(netdev_raw)) {
641 log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
642 return 0;
643 }
644
645 if (!netdev_raw->ifname) {
646 log_warning("NetDev without Name configured in %s. Ignoring", filename);
647 return 0;
648 }
649
650 netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
651 if (!netdev)
652 return log_oom();
653
654 netdev->n_ref = 1;
655 netdev->manager = manager;
656 netdev->state = _NETDEV_STATE_INVALID;
657 netdev->kind = netdev_raw->kind;
658 netdev->ifname = netdev_raw->ifname;
659
660 if (NETDEV_VTABLE(netdev)->init)
661 NETDEV_VTABLE(netdev)->init(netdev);
662
663 r = config_parse(NULL, filename, file,
664 NETDEV_VTABLE(netdev)->sections,
665 config_item_perf_lookup, network_netdev_gperf_lookup,
666 false, false, false, netdev);
667 if (r < 0)
668 return r;
669
670 /* verify configuration */
671 if (NETDEV_VTABLE(netdev)->config_verify) {
672 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
673 if (r < 0)
674 return 0;
675 }
676
677 netdev->filename = strdup(filename);
678 if (!netdev->filename)
679 return log_oom();
680
681 if (!netdev->mac) {
682 r = netdev_get_mac(netdev->ifname, &netdev->mac);
683 if (r < 0) {
684 log_error("Failed to generate predictable MAC address for %s",
685 netdev->ifname);
686 return r;
687 }
688 }
689
690 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
691 if (r < 0)
692 return r;
693
694 LIST_HEAD_INIT(netdev->callbacks);
695
696 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
697
698 switch (NETDEV_VTABLE(netdev)->create_type) {
699 case NETDEV_CREATE_MASTER:
700 case NETDEV_CREATE_INDEPENDENT:
701 r = netdev_create(netdev, NULL);
702 if (r < 0)
703 return r;
704
705 break;
706 default:
707 break;
708 }
709
710 netdev = NULL;
711
712 return 0;
713 }
714
715 int netdev_load(Manager *manager) {
716 NetDev *netdev;
717 char **files, **f;
718 int r;
719
720 assert(manager);
721
722 while ((netdev = hashmap_first(manager->netdevs)))
723 netdev_unref(netdev);
724
725 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
726 if (r < 0) {
727 log_error("Failed to enumerate netdev files: %s", strerror(-r));
728 return r;
729 }
730
731 STRV_FOREACH_BACKWARDS(f, files) {
732 r = netdev_load_one(manager, *f);
733 if (r < 0)
734 return r;
735 }
736
737 strv_free(files);
738
739 return 0;
740 }