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/>.
23 #include "network-internal.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
28 #include "siphash24.h"
30 #define VLANID_MAX 4094
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",
39 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
40 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
42 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
43 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
44 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
45 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
46 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
49 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
50 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
52 void netdev_free(NetDev *netdev) {
53 netdev_enslave_callback *callback;
58 while ((callback = netdev->callbacks)) {
59 LIST_REMOVE(callbacks, netdev->callbacks, callback);
64 hashmap_remove(netdev->manager->netdevs, netdev->name);
66 free(netdev->filename);
68 free(netdev->description);
71 condition_free_list(netdev->match_host);
72 condition_free_list(netdev->match_virt);
73 condition_free_list(netdev->match_kernel);
74 condition_free_list(netdev->match_arch);
79 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
86 netdev = hashmap_get(manager->netdevs, name);
97 static int netdev_enter_failed(NetDev *netdev) {
98 netdev->state = NETDEV_STATE_FAILED;
103 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
104 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
108 assert(netdev->state == NETDEV_STATE_READY);
109 assert(netdev->manager);
110 assert(netdev->manager->rtnl);
114 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
115 RTM_SETLINK, link->ifindex);
117 log_error_netdev(netdev,
118 "Could not allocate RTM_SETLINK message: %s",
123 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
125 log_error_netdev(netdev,
126 "Could not append IFLA_MASTER attribute: %s",
131 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
133 log_error_netdev(netdev,
134 "Could not send rtnetlink message: %s",
139 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
144 static int netdev_enter_ready(NetDev *netdev) {
145 netdev_enslave_callback *callback;
148 assert(netdev->name);
150 if (netdev->state != NETDEV_STATE_CREATING)
153 netdev->state = NETDEV_STATE_READY;
155 log_info_netdev(netdev, "netdev ready");
157 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
158 /* enslave the links that were attempted to be enslaved befor the
160 netdev_enslave_ready(netdev, callback->link, callback->callback);
165 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
166 NetDev *netdev = userdata;
169 assert(netdev->state != _NETDEV_STATE_INVALID);
171 r = sd_rtnl_message_get_errno(m);
173 log_debug_netdev(netdev, "netdev exists, using existing");
175 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
176 netdev_enter_failed(netdev);
184 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
186 static int netdev_get_mac(NetDev *netdev, struct ether_addr *mac) {
193 assert(netdev->name);
196 l = strlen(netdev->name);
197 sz = sizeof(sd_id128_t) + l;
200 /* fetch some persistent data unique to the machine */
201 r = sd_id128_get_machine((sd_id128_t*) v);
205 /* combine with some data unique (on this machine) to this
207 memcpy(v + sizeof(sd_id128_t), netdev->name, l);
209 /* Let's hash the host machine ID plus the container name. We
210 * use a fixed, but originally randomly created hash key here. */
211 siphash24(result, v, sz, HASH_KEY.bytes);
213 assert_cc(ETH_ALEN <= sizeof(result));
214 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
216 /* see eth_random_addr in the kernel */
217 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
218 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
223 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
224 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
225 struct ether_addr mac;
230 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
232 assert(netdev->name);
233 assert(netdev->manager);
234 assert(netdev->manager->rtnl);
236 r = netdev_get_mac(netdev, &mac);
238 log_error("Failed to generate predictable MAC address for %s", netdev->name);
242 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
244 log_error_netdev(netdev,
245 "Could not allocate RTM_NEWLINK message: %s",
251 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
253 log_error_netdev(netdev,
254 "Could not append IFLA_LINK attribute: %s",
260 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
262 log_error_netdev(netdev,
263 "Could not append IFLA_IFNAME attribute: %s",
268 r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, &mac);
270 log_error_netdev(netdev,
271 "Colud not append IFLA_ADDRESS attribute: %s",
276 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
278 log_error_netdev(netdev,
279 "Could not open IFLA_LINKINFO container: %s",
284 kind = netdev_kind_to_string(netdev->kind);
286 log_error_netdev(netdev, "Invalid kind");
290 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
292 log_error_netdev(netdev,
293 "Could not open IFLA_INFO_DATA container: %s",
298 if (netdev->vlanid <= VLANID_MAX) {
299 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
301 log_error_netdev(netdev,
302 "Could not append IFLA_VLAN_ID attribute: %s",
308 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
309 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
311 log_error_netdev(netdev,
312 "Could not append IFLA_MACVLAN_MODE attribute: %s",
318 r = sd_rtnl_message_close_container(req);
320 log_error_netdev(netdev,
321 "Could not close IFLA_INFO_DATA container %s",
326 r = sd_rtnl_message_close_container(req);
328 log_error_netdev(netdev,
329 "Could not close IFLA_LINKINFO container %s",
335 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
337 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
339 log_error_netdev(netdev,
340 "Could not send rtnetlink message: %s", strerror(-r));
344 log_debug_netdev(netdev, "creating netdev");
346 netdev->state = NETDEV_STATE_CREATING;
351 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
352 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
353 return netdev_create(netdev, link, callback);
355 if (netdev->state == NETDEV_STATE_READY) {
356 netdev_enslave_ready(netdev, link, callback);
358 /* the netdev is not yet read, save this request for when it is*/
359 netdev_enslave_callback *cb;
361 cb = new0(netdev_enslave_callback, 1);
365 cb->callback = callback;
368 LIST_PREPEND(callbacks, netdev->callbacks, cb);
374 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
384 r = sd_rtnl_message_get_type(message, &type);
386 log_error_netdev(netdev, "Could not get rtnl message type");
390 if (type != RTM_NEWLINK) {
391 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
395 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
397 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
398 netdev_enter_failed(netdev);
400 } else if (ifindex <= 0) {
401 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
402 netdev_enter_failed(netdev);
407 if (netdev->ifindex > 0) {
408 if (netdev->ifindex != ifindex) {
409 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
410 ifindex, netdev->ifindex);
411 netdev_enter_failed(netdev);
414 /* ifindex already set to the same for this netdev */
418 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
420 log_error_netdev(netdev, "Could not get IFNAME");
424 if (!streq(netdev->name, received_name)) {
425 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
427 netdev_enter_failed(netdev);
431 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
433 log_error_netdev(netdev, "Could not get LINKINFO");
437 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
439 log_error_netdev(netdev, "Could not get KIND");
443 r = sd_rtnl_message_exit_container(message);
445 log_error_netdev(netdev, "Could not exit container");
449 kind = netdev_kind_to_string(netdev->kind);
451 log_error_netdev(netdev, "Could not get kind");
452 netdev_enter_failed(netdev);
456 if (!streq(kind, received_kind)) {
457 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
458 "expected %s", received_kind, kind);
459 netdev_enter_failed(netdev);
463 netdev->ifindex = ifindex;
465 netdev_enter_ready(netdev);
470 static int netdev_load_one(Manager *manager, const char *filename) {
471 _cleanup_netdev_free_ NetDev *netdev = NULL;
472 _cleanup_fclose_ FILE *file = NULL;
478 if (null_or_empty_path(filename)) {
479 log_debug("skipping empty file: %s", filename);
483 file = fopen(filename, "re");
491 netdev = new0(NetDev, 1);
495 netdev->manager = manager;
496 netdev->state = _NETDEV_STATE_INVALID;
497 netdev->kind = _NETDEV_KIND_INVALID;
498 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
499 netdev->vlanid = VLANID_MAX + 1;
501 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
502 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
503 false, false, netdev);
505 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
509 if (netdev->kind == _NETDEV_KIND_INVALID) {
510 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
515 log_warning("NetDev without Name configured in %s. Ignoring", filename);
519 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
520 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
524 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
525 log_warning("VLAN Id configured for a %s in %s. Ignoring",
526 netdev_kind_to_string(netdev->kind), filename);
530 if (netdev->kind != NETDEV_KIND_MACVLAN &&
531 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
532 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
533 netdev_kind_to_string(netdev->kind), filename);
537 netdev->filename = strdup(filename);
538 if (!netdev->filename)
541 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
542 netdev->match_host, netdev->match_virt,
543 netdev->match_kernel, netdev->match_arch,
544 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
547 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
551 LIST_HEAD_INIT(netdev->callbacks);
553 if (netdev->kind != NETDEV_KIND_VLAN &&
554 netdev->kind != NETDEV_KIND_MACVLAN) {
555 r = netdev_create(netdev, NULL, NULL);
565 int netdev_load(Manager *manager) {
572 while ((netdev = hashmap_first(manager->netdevs)))
575 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
577 log_error("Failed to enumerate netdev files: %s", strerror(-r));
581 STRV_FOREACH_BACKWARDS(f, files) {
582 r = netdev_load_one(manager, *f);