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"
29 #define VLANID_MAX 4094
31 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
32 [NETDEV_KIND_BRIDGE] = "bridge",
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
35 [NETDEV_KIND_MACVLAN] = "macvlan",
38 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
41 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
42 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
48 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
51 static void netdev_free(NetDev *netdev) {
52 netdev_enslave_callback *callback;
57 while ((callback = netdev->callbacks)) {
58 LIST_REMOVE(callbacks, netdev->callbacks, callback);
63 hashmap_remove(netdev->manager->netdevs, netdev->name);
65 free(netdev->filename);
67 free(netdev->description);
70 condition_free_list(netdev->match_host);
71 condition_free_list(netdev->match_virt);
72 condition_free_list(netdev->match_kernel);
73 condition_free_list(netdev->match_arch);
78 NetDev *netdev_unref(NetDev *netdev) {
79 if (netdev && (-- netdev->n_ref <= 0))
85 NetDev *netdev_ref(NetDev *netdev) {
87 assert_se(++ netdev->n_ref >= 2);
92 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
99 netdev = hashmap_get(manager->netdevs, name);
110 static int netdev_enter_failed(NetDev *netdev) {
111 netdev->state = NETDEV_STATE_FAILED;
116 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
117 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
121 assert(netdev->state == NETDEV_STATE_READY);
122 assert(netdev->manager);
123 assert(netdev->manager->rtnl);
127 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
128 RTM_SETLINK, link->ifindex);
130 log_error_netdev(netdev,
131 "Could not allocate RTM_SETLINK message: %s",
136 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
138 log_error_netdev(netdev,
139 "Could not append IFLA_MASTER attribute: %s",
144 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
146 log_error_netdev(netdev,
147 "Could not send rtnetlink message: %s",
152 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
157 static int netdev_enter_ready(NetDev *netdev) {
158 netdev_enslave_callback *callback;
161 assert(netdev->name);
163 if (netdev->state != NETDEV_STATE_CREATING)
166 netdev->state = NETDEV_STATE_READY;
168 log_info_netdev(netdev, "netdev ready");
170 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
171 /* enslave the links that were attempted to be enslaved before the
173 netdev_enslave_ready(netdev, callback->link, callback->callback);
178 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
179 NetDev *netdev = userdata;
182 assert(netdev->state != _NETDEV_STATE_INVALID);
184 r = sd_rtnl_message_get_errno(m);
186 log_debug_netdev(netdev, "netdev exists, using existing");
188 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
189 netdev_enter_failed(netdev);
197 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
198 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
203 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
205 assert(netdev->name);
206 assert(netdev->manager);
207 assert(netdev->manager->rtnl);
209 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
211 log_error_netdev(netdev,
212 "Could not allocate RTM_NEWLINK message: %s",
218 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
220 log_error_netdev(netdev,
221 "Could not append IFLA_LINK attribute: %s",
227 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
229 log_error_netdev(netdev,
230 "Could not append IFLA_IFNAME attribute: %s",
235 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
237 log_error_netdev(netdev,
238 "Could not open IFLA_LINKINFO container: %s",
243 kind = netdev_kind_to_string(netdev->kind);
245 log_error_netdev(netdev, "Invalid kind");
249 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
251 log_error_netdev(netdev,
252 "Could not open IFLA_INFO_DATA container: %s",
257 if (netdev->vlanid <= VLANID_MAX) {
258 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
260 log_error_netdev(netdev,
261 "Could not append IFLA_VLAN_ID attribute: %s",
267 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
268 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
270 log_error_netdev(netdev,
271 "Could not append IFLA_MACVLAN_MODE attribute: %s",
277 r = sd_rtnl_message_close_container(req);
279 log_error_netdev(netdev,
280 "Could not close IFLA_INFO_DATA container %s",
285 r = sd_rtnl_message_close_container(req);
287 log_error_netdev(netdev,
288 "Could not close IFLA_LINKINFO container %s",
294 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
296 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
298 log_error_netdev(netdev,
299 "Could not send rtnetlink message: %s", strerror(-r));
303 log_debug_netdev(netdev, "creating netdev");
305 netdev->state = NETDEV_STATE_CREATING;
310 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
313 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
314 return netdev_create(netdev, link, callback);
316 if (netdev->state == NETDEV_STATE_READY) {
317 r = netdev_enslave_ready(netdev, link, callback);
321 /* the netdev is not yet read, save this request for when it is*/
322 netdev_enslave_callback *cb;
324 cb = new0(netdev_enslave_callback, 1);
328 cb->callback = callback;
331 LIST_PREPEND(callbacks, netdev->callbacks, cb);
337 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
347 r = sd_rtnl_message_get_type(message, &type);
349 log_error_netdev(netdev, "Could not get rtnl message type");
353 if (type != RTM_NEWLINK) {
354 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
358 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
360 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
361 netdev_enter_failed(netdev);
363 } else if (ifindex <= 0) {
364 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
365 netdev_enter_failed(netdev);
370 if (netdev->ifindex > 0) {
371 if (netdev->ifindex != ifindex) {
372 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
373 ifindex, netdev->ifindex);
374 netdev_enter_failed(netdev);
377 /* ifindex already set to the same for this netdev */
381 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
383 log_error_netdev(netdev, "Could not get IFNAME");
387 if (!streq(netdev->name, received_name)) {
388 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
390 netdev_enter_failed(netdev);
394 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
396 log_error_netdev(netdev, "Could not get LINKINFO");
400 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
402 log_error_netdev(netdev, "Could not get KIND");
406 r = sd_rtnl_message_exit_container(message);
408 log_error_netdev(netdev, "Could not exit container");
412 kind = netdev_kind_to_string(netdev->kind);
414 log_error_netdev(netdev, "Could not get kind");
415 netdev_enter_failed(netdev);
419 if (!streq(kind, received_kind)) {
420 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
421 "expected %s", received_kind, kind);
422 netdev_enter_failed(netdev);
426 netdev->ifindex = ifindex;
428 netdev_enter_ready(netdev);
433 static int netdev_load_one(Manager *manager, const char *filename) {
434 _cleanup_netdev_unref_ NetDev *netdev = NULL;
435 _cleanup_fclose_ FILE *file = NULL;
441 if (null_or_empty_path(filename)) {
442 log_debug("skipping empty file: %s", filename);
446 file = fopen(filename, "re");
454 netdev = new0(NetDev, 1);
459 netdev->manager = manager;
460 netdev->state = _NETDEV_STATE_INVALID;
461 netdev->kind = _NETDEV_KIND_INVALID;
462 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
463 netdev->vlanid = VLANID_MAX + 1;
465 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
466 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
467 false, false, netdev);
469 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
473 if (netdev->kind == _NETDEV_KIND_INVALID) {
474 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
479 log_warning("NetDev without Name configured in %s. Ignoring", filename);
483 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
484 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
488 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
489 log_warning("VLAN Id configured for a %s in %s. Ignoring",
490 netdev_kind_to_string(netdev->kind), filename);
494 if (netdev->kind != NETDEV_KIND_MACVLAN &&
495 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
496 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
497 netdev_kind_to_string(netdev->kind), filename);
501 netdev->filename = strdup(filename);
502 if (!netdev->filename)
505 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
506 netdev->match_host, netdev->match_virt,
507 netdev->match_kernel, netdev->match_arch,
508 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
511 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
515 LIST_HEAD_INIT(netdev->callbacks);
517 if (netdev->kind != NETDEV_KIND_VLAN &&
518 netdev->kind != NETDEV_KIND_MACVLAN) {
519 r = netdev_create(netdev, NULL, NULL);
524 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
531 int netdev_load(Manager *manager) {
538 while ((netdev = hashmap_first(manager->netdevs)))
539 netdev_unref(netdev);
541 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
543 log_error("Failed to enumerate netdev files: %s", strerror(-r));
547 STRV_FOREACH_BACKWARDS(f, files) {
548 r = netdev_load_one(manager, *f);