1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
25 #include "network-internal.h"
26 #include "path-util.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
30 #include "siphash24.h"
32 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
33 [NETDEV_KIND_BRIDGE] = "bridge",
34 [NETDEV_KIND_BOND] = "bond",
35 [NETDEV_KIND_VLAN] = "vlan",
36 [NETDEV_KIND_MACVLAN] = "macvlan",
37 [NETDEV_KIND_VXLAN] = "vxlan",
38 [NETDEV_KIND_IPIP] = "ipip",
39 [NETDEV_KIND_GRE] = "gre",
40 [NETDEV_KIND_SIT] = "sit",
41 [NETDEV_KIND_VETH] = "veth",
42 [NETDEV_KIND_VTI] = "vti",
43 [NETDEV_KIND_DUMMY] = "dummy",
46 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
47 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
49 static void netdev_cancel_callbacks(NetDev *netdev) {
50 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
51 netdev_enslave_callback *callback;
56 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
58 while ((callback = netdev->callbacks)) {
60 assert(callback->link);
61 assert(callback->callback);
62 assert(netdev->manager);
63 assert(netdev->manager->rtnl);
65 callback->callback(netdev->manager->rtnl, m, link);
68 LIST_REMOVE(callbacks, netdev->callbacks, callback);
73 static void netdev_free(NetDev *netdev) {
77 netdev_cancel_callbacks(netdev);
80 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
82 free(netdev->filename);
84 free(netdev->description);
86 free(netdev->ifname_peer);
88 free(netdev->mac_peer);
90 condition_free_list(netdev->match_host);
91 condition_free_list(netdev->match_virt);
92 condition_free_list(netdev->match_kernel);
93 condition_free_list(netdev->match_arch);
98 NetDev *netdev_unref(NetDev *netdev) {
99 if (netdev && (-- netdev->n_ref <= 0))
105 NetDev *netdev_ref(NetDev *netdev) {
107 assert_se(++ netdev->n_ref >= 2);
112 void netdev_drop(NetDev *netdev) {
113 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
116 netdev->state = NETDEV_STATE_LINGER;
118 log_debug_netdev(netdev, "netdev removed");
120 netdev_cancel_callbacks(netdev);
122 netdev_unref(netdev);
127 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
134 netdev = hashmap_get(manager->netdevs, name);
145 static int netdev_enter_failed(NetDev *netdev) {
146 netdev->state = NETDEV_STATE_FAILED;
151 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
152 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
156 assert(netdev->state == NETDEV_STATE_READY);
157 assert(netdev->manager);
158 assert(netdev->manager->rtnl);
162 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
163 RTM_SETLINK, link->ifindex);
165 log_error_netdev(netdev,
166 "Could not allocate RTM_SETLINK message: %s",
171 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
173 log_error_netdev(netdev,
174 "Could not append IFLA_MASTER attribute: %s",
179 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
181 log_error_netdev(netdev,
182 "Could not send rtnetlink message: %s",
189 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
194 static int netdev_enter_ready(NetDev *netdev) {
195 netdev_enslave_callback *callback, *callback_next;
199 assert(netdev->ifname);
201 if (netdev->state != NETDEV_STATE_CREATING)
204 netdev->state = NETDEV_STATE_READY;
206 log_info_netdev(netdev, "netdev ready");
208 LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
209 /* enslave the links that were attempted to be enslaved before the
211 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
215 LIST_REMOVE(callbacks, netdev->callbacks, callback);
216 link_unref(callback->link);
223 /* callback for netdev's created without a backing Link */
224 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
225 _cleanup_netdev_unref_ NetDev *netdev = userdata;
228 assert(netdev->state != _NETDEV_STATE_INVALID);
230 r = sd_rtnl_message_get_errno(m);
232 log_debug_netdev(netdev, "netdev exists, using existing");
234 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
243 int config_parse_tunnel_address(const char *unit,
244 const char *filename,
247 unsigned section_line,
254 unsigned char family = AF_INET;
262 r = net_parse_inaddr(rvalue, &family, n);
264 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
265 "Tunnel address is invalid, ignoring assignment: %s", rvalue);
271 static int netdev_create(NetDev *netdev) {
272 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
277 assert(netdev->ifname);
278 assert(netdev->manager);
279 assert(netdev->manager->rtnl);
281 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
283 log_error_netdev(netdev,
284 "Could not allocate RTM_NEWLINK message: %s",
289 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->ifname);
291 log_error_netdev(netdev,
292 "Could not append IFLA_IFNAME attribute: %s",
298 r = sd_rtnl_message_append_u32(req, IFLA_MTU, netdev->mtu);
300 log_error_netdev(netdev,
301 "Could not append IFLA_MTU attribute: %s",
308 r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, netdev->mac);
310 log_error_netdev(netdev,
311 "Colud not append IFLA_ADDRESS attribute: %s",
317 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
319 log_error_netdev(netdev,
320 "Could not open IFLA_LINKINFO container: %s",
325 kind = netdev_kind_to_string(netdev->kind);
327 log_error_netdev(netdev, "Invalid kind");
331 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
333 log_error_netdev(netdev,
334 "Could not open IFLA_INFO_DATA container: %s",
339 r = sd_rtnl_message_close_container(req);
341 log_error_netdev(netdev,
342 "Could not close IFLA_INFO_DATA container %s",
347 r = sd_rtnl_message_close_container(req);
349 log_error_netdev(netdev,
350 "Could not close IFLA_LINKINFO container %s",
355 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
357 log_error_netdev(netdev,
358 "Could not send rtnetlink message: %s", strerror(-r));
364 log_debug_netdev(netdev, "creating netdev");
366 netdev->state = NETDEV_STATE_CREATING;
371 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
372 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
375 switch(netdev->kind) {
376 case NETDEV_KIND_VLAN:
377 return netdev_create_vlan(netdev, link, callback);
378 case NETDEV_KIND_MACVLAN:
379 return netdev_create_macvlan(netdev, link, callback);
380 case NETDEV_KIND_VXLAN:
381 return netdev_create_vxlan(netdev, link, callback);
382 case NETDEV_KIND_IPIP:
383 case NETDEV_KIND_GRE:
384 case NETDEV_KIND_SIT:
385 case NETDEV_KIND_VTI:
386 return netdev_create_tunnel(netdev, link, callback);
391 if (netdev->state == NETDEV_STATE_READY) {
392 r = netdev_enslave_ready(netdev, link, callback);
396 /* the netdev is not yet read, save this request for when it is*/
397 netdev_enslave_callback *cb;
399 cb = new0(netdev_enslave_callback, 1);
403 cb->callback = callback;
407 LIST_PREPEND(callbacks, netdev->callbacks, cb);
413 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
423 r = sd_rtnl_message_get_type(message, &type);
425 log_error_netdev(netdev, "Could not get rtnl message type");
429 if (type != RTM_NEWLINK) {
430 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
434 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
436 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
437 netdev_enter_failed(netdev);
439 } else if (ifindex <= 0) {
440 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
441 netdev_enter_failed(netdev);
445 if (netdev->ifindex > 0) {
446 if (netdev->ifindex != ifindex) {
447 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
448 ifindex, netdev->ifindex);
449 netdev_enter_failed(netdev);
452 /* ifindex already set to the same for this netdev */
456 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
458 log_error_netdev(netdev, "Could not get IFNAME");
462 if (!streq(netdev->ifname, received_name)) {
463 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
465 netdev_enter_failed(netdev);
469 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
471 log_error_netdev(netdev, "Could not get LINKINFO");
475 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
477 log_error_netdev(netdev, "Could not get KIND");
481 r = sd_rtnl_message_exit_container(message);
483 log_error_netdev(netdev, "Could not exit container");
487 kind = netdev_kind_to_string(netdev->kind);
489 log_error_netdev(netdev, "Could not get kind");
490 netdev_enter_failed(netdev);
494 if (!streq(kind, received_kind)) {
495 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
496 "expected %s", received_kind, kind);
497 netdev_enter_failed(netdev);
501 netdev->ifindex = ifindex;
503 log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
505 netdev_enter_ready(netdev);
510 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
512 static int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
513 _cleanup_free_ struct ether_addr *mac = NULL;
522 mac = new0(struct ether_addr, 1);
527 sz = sizeof(sd_id128_t) + l;
530 /* fetch some persistent data unique to the machine */
531 r = sd_id128_get_machine((sd_id128_t*) v);
535 /* combine with some data unique (on this machine) to this
537 memcpy(v + sizeof(sd_id128_t), ifname, l);
539 /* Let's hash the host machine ID plus the container name. We
540 * use a fixed, but originally randomly created hash key here. */
541 siphash24(result, v, sz, HASH_KEY.bytes);
543 assert_cc(ETH_ALEN <= sizeof(result));
544 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
546 /* see eth_random_addr in the kernel */
547 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
548 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
556 static int netdev_load_one(Manager *manager, const char *filename) {
557 _cleanup_netdev_unref_ NetDev *netdev = NULL;
558 _cleanup_fclose_ FILE *file = NULL;
564 if (null_or_empty_path(filename)) {
565 log_debug("skipping empty file: %s", filename);
569 file = fopen(filename, "re");
577 netdev = new0(NetDev, 1);
582 netdev->manager = manager;
583 netdev->state = _NETDEV_STATE_INVALID;
584 netdev->kind = _NETDEV_KIND_INVALID;
585 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
586 netdev->vlanid = VLANID_MAX + 1;
587 netdev->vxlanid = VXLAN_VID_MAX + 1;
588 netdev->tunnel_pmtudisc = true;
589 netdev->learning = true;
591 r = config_parse(NULL, filename, file,
592 "Match\0NetDev\0VLAN\0MACVLAN\0VXLAN\0Tunnel\0Peer\0",
593 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
594 false, false, netdev);
596 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
600 if (netdev->kind == _NETDEV_KIND_INVALID) {
601 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
605 if (!netdev->ifname) {
606 log_warning("NetDev without Name configured in %s. Ignoring", filename);
610 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
611 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
615 if (netdev->kind == NETDEV_KIND_VXLAN && netdev->vxlanid > VXLAN_VID_MAX) {
616 log_warning("VXLAN without valid Id configured in %s. Ignoring", filename);
620 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
621 log_warning("VLAN Id configured for a %s in %s. Ignoring",
622 netdev_kind_to_string(netdev->kind), filename);
626 if (netdev->kind != NETDEV_KIND_VXLAN && netdev->vxlanid <= VXLAN_VID_MAX) {
627 log_warning("VXLAN Id configured for a %s in %s. Ignoring",
628 netdev_kind_to_string(netdev->kind), filename);
632 if (netdev->kind != NETDEV_KIND_MACVLAN &&
633 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
634 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
635 netdev_kind_to_string(netdev->kind), filename);
639 netdev->filename = strdup(filename);
640 if (!netdev->filename)
643 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
644 netdev->match_host, netdev->match_virt,
645 netdev->match_kernel, netdev->match_arch,
646 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
650 r = netdev_get_mac(netdev->ifname, &netdev->mac);
652 log_error("Failed to generate predictable MAC address for %s",
658 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
662 LIST_HEAD_INIT(netdev->callbacks);
664 switch (netdev->kind) {
665 case NETDEV_KIND_VETH:
666 if (!netdev->ifname_peer) {
667 log_warning("Veth NetDev without peer name configured "
668 "in %s. Ignoring", filename);
673 r = netdev_get_mac(netdev->ifname_peer, &netdev->mac_peer);
675 log_error("Failed to generate predictable MAC address for %s",
676 netdev->ifname_peer);
681 r = netdev_create_veth(netdev, netdev_create_handler);
686 case NETDEV_KIND_DUMMY:
687 r = netdev_create_dummy(netdev, netdev_create_handler);
692 case NETDEV_KIND_BRIDGE:
693 case NETDEV_KIND_BOND:
694 r = netdev_create(netdev);
702 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
709 int netdev_load(Manager *manager) {
716 while ((netdev = hashmap_first(manager->netdevs)))
717 netdev_unref(netdev);
719 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
721 log_error("Failed to enumerate netdev files: %s", strerror(-r));
725 STRV_FOREACH_BACKWARDS(f, files) {
726 r = netdev_load_one(manager, *f);