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_cancel_callbacks(NetDev
*netdev
) {
52 _cleanup_rtnl_message_unref_ sd_rtnl_message
*m
= NULL
;
53 netdev_enslave_callback
*callback
;
58 rtnl_message_new_synthetic_error(-ENODEV
, 0, &m
);
60 while ((callback
= netdev
->callbacks
)) {
62 assert(callback
->link
);
63 assert(callback
->callback
);
64 assert(netdev
->manager
);
65 assert(netdev
->manager
->rtnl
);
67 callback
->callback(netdev
->manager
->rtnl
, m
, link
);
70 LIST_REMOVE(callbacks
, netdev
->callbacks
, callback
);
75 static void netdev_free(NetDev
*netdev
) {
79 netdev_cancel_callbacks(netdev
);
82 hashmap_remove(netdev
->manager
->netdevs
, netdev
->name
);
84 free(netdev
->filename
);
86 free(netdev
->description
);
89 condition_free_list(netdev
->match_host
);
90 condition_free_list(netdev
->match_virt
);
91 condition_free_list(netdev
->match_kernel
);
92 condition_free_list(netdev
->match_arch
);
97 NetDev
*netdev_unref(NetDev
*netdev
) {
98 if (netdev
&& (-- netdev
->n_ref
<= 0))
104 NetDev
*netdev_ref(NetDev
*netdev
) {
106 assert_se(++ netdev
->n_ref
>= 2);
111 void netdev_drop(NetDev
*netdev
) {
112 if (!netdev
|| netdev
->state
== NETDEV_STATE_LINGER
)
115 netdev
->state
= NETDEV_STATE_LINGER
;
117 log_debug_netdev(netdev
, "dropped");
119 netdev_cancel_callbacks(netdev
);
121 netdev_unref(netdev
);
126 int netdev_get(Manager
*manager
, const char *name
, NetDev
**ret
) {
133 netdev
= hashmap_get(manager
->netdevs
, name
);
144 static int netdev_enter_failed(NetDev
*netdev
) {
145 netdev
->state
= NETDEV_STATE_FAILED
;
150 static int netdev_enslave_ready(NetDev
*netdev
, Link
* link
, sd_rtnl_message_handler_t callback
) {
151 _cleanup_rtnl_message_unref_ sd_rtnl_message
*req
= NULL
;
155 assert(netdev
->state
== NETDEV_STATE_READY
);
156 assert(netdev
->manager
);
157 assert(netdev
->manager
->rtnl
);
161 r
= sd_rtnl_message_new_link(netdev
->manager
->rtnl
, &req
,
162 RTM_SETLINK
, link
->ifindex
);
164 log_error_netdev(netdev
,
165 "Could not allocate RTM_SETLINK message: %s",
170 r
= sd_rtnl_message_append_u32(req
, IFLA_MASTER
, netdev
->ifindex
);
172 log_error_netdev(netdev
,
173 "Could not append IFLA_MASTER attribute: %s",
178 r
= sd_rtnl_call_async(netdev
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
180 log_error_netdev(netdev
,
181 "Could not send rtnetlink message: %s",
186 log_debug_netdev(netdev
, "enslaving link '%s'", link
->ifname
);
191 static int netdev_enter_ready(NetDev
*netdev
) {
192 netdev_enslave_callback
*callback
;
195 assert(netdev
->name
);
197 if (netdev
->state
!= NETDEV_STATE_CREATING
)
200 netdev
->state
= NETDEV_STATE_READY
;
202 log_info_netdev(netdev
, "netdev ready");
204 LIST_FOREACH(callbacks
, callback
, netdev
->callbacks
) {
205 /* enslave the links that were attempted to be enslaved before the
207 netdev_enslave_ready(netdev
, callback
->link
, callback
->callback
);
212 static int netdev_create_handler(sd_rtnl
*rtnl
, sd_rtnl_message
*m
, void *userdata
) {
213 NetDev
*netdev
= userdata
;
216 assert(netdev
->state
!= _NETDEV_STATE_INVALID
);
218 r
= sd_rtnl_message_get_errno(m
);
220 log_debug_netdev(netdev
, "netdev exists, using existing");
222 log_warning_netdev(netdev
, "netdev failed: %s", strerror(-r
));
223 netdev_enter_failed(netdev
);
231 static int netdev_create(NetDev
*netdev
, Link
*link
, sd_rtnl_message_handler_t callback
) {
232 _cleanup_rtnl_message_unref_ sd_rtnl_message
*req
= NULL
;
237 assert(!(netdev
->kind
== NETDEV_KIND_VLAN
|| netdev
->kind
== NETDEV_KIND_MACVLAN
) ||
239 assert(netdev
->name
);
240 assert(netdev
->manager
);
241 assert(netdev
->manager
->rtnl
);
243 r
= sd_rtnl_message_new_link(netdev
->manager
->rtnl
, &req
, RTM_NEWLINK
, 0);
245 log_error_netdev(netdev
,
246 "Could not allocate RTM_NEWLINK message: %s",
252 r
= sd_rtnl_message_append_u32(req
, IFLA_LINK
, link
->ifindex
);
254 log_error_netdev(netdev
,
255 "Could not append IFLA_LINK attribute: %s",
261 r
= sd_rtnl_message_append_string(req
, IFLA_IFNAME
, netdev
->name
);
263 log_error_netdev(netdev
,
264 "Could not append IFLA_IFNAME attribute: %s",
269 r
= sd_rtnl_message_open_container(req
, IFLA_LINKINFO
);
271 log_error_netdev(netdev
,
272 "Could not open IFLA_LINKINFO container: %s",
277 kind
= netdev_kind_to_string(netdev
->kind
);
279 log_error_netdev(netdev
, "Invalid kind");
283 r
= sd_rtnl_message_open_container_union(req
, IFLA_INFO_DATA
, kind
);
285 log_error_netdev(netdev
,
286 "Could not open IFLA_INFO_DATA container: %s",
291 if (netdev
->vlanid
<= VLANID_MAX
) {
292 r
= sd_rtnl_message_append_u16(req
, IFLA_VLAN_ID
, netdev
->vlanid
);
294 log_error_netdev(netdev
,
295 "Could not append IFLA_VLAN_ID attribute: %s",
301 if (netdev
->macvlan_mode
!= _NETDEV_MACVLAN_MODE_INVALID
) {
302 r
= sd_rtnl_message_append_u32(req
, IFLA_MACVLAN_MODE
, netdev
->macvlan_mode
);
304 log_error_netdev(netdev
,
305 "Could not append IFLA_MACVLAN_MODE attribute: %s",
311 r
= sd_rtnl_message_close_container(req
);
313 log_error_netdev(netdev
,
314 "Could not close IFLA_INFO_DATA container %s",
319 r
= sd_rtnl_message_close_container(req
);
321 log_error_netdev(netdev
,
322 "Could not close IFLA_LINKINFO container %s",
328 r
= sd_rtnl_call_async(netdev
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
330 r
= sd_rtnl_call_async(netdev
->manager
->rtnl
, req
, &netdev_create_handler
, netdev
, 0, NULL
);
332 log_error_netdev(netdev
,
333 "Could not send rtnetlink message: %s", strerror(-r
));
337 log_debug_netdev(netdev
, "creating netdev");
339 netdev
->state
= NETDEV_STATE_CREATING
;
344 int netdev_enslave(NetDev
*netdev
, Link
*link
, sd_rtnl_message_handler_t callback
) {
347 if (netdev
->kind
== NETDEV_KIND_VLAN
|| netdev
->kind
== NETDEV_KIND_MACVLAN
)
348 return netdev_create(netdev
, link
, callback
);
350 if (netdev
->state
== NETDEV_STATE_READY
) {
351 r
= netdev_enslave_ready(netdev
, link
, callback
);
355 /* the netdev is not yet read, save this request for when it is*/
356 netdev_enslave_callback
*cb
;
358 cb
= new0(netdev_enslave_callback
, 1);
362 cb
->callback
= callback
;
365 LIST_PREPEND(callbacks
, netdev
->callbacks
, cb
);
371 int netdev_set_ifindex(NetDev
*netdev
, sd_rtnl_message
*message
) {
381 r
= sd_rtnl_message_get_type(message
, &type
);
383 log_error_netdev(netdev
, "Could not get rtnl message type");
387 if (type
!= RTM_NEWLINK
) {
388 log_error_netdev(netdev
, "Can not set ifindex from unexpected rtnl message type");
392 r
= sd_rtnl_message_link_get_ifindex(message
, &ifindex
);
394 log_error_netdev(netdev
, "Could not get ifindex: %s", strerror(-r
));
395 netdev_enter_failed(netdev
);
397 } else if (ifindex
<= 0) {
398 log_error_netdev(netdev
, "Got invalid ifindex: %d", ifindex
);
399 netdev_enter_failed(netdev
);
404 if (netdev
->ifindex
> 0) {
405 if (netdev
->ifindex
!= ifindex
) {
406 log_error_netdev(netdev
, "Could not set ifindex to %d, already set to %d",
407 ifindex
, netdev
->ifindex
);
408 netdev_enter_failed(netdev
);
411 /* ifindex already set to the same for this netdev */
415 r
= sd_rtnl_message_read_string(message
, IFLA_IFNAME
, &received_name
);
417 log_error_netdev(netdev
, "Could not get IFNAME");
421 if (!streq(netdev
->name
, received_name
)) {
422 log_error_netdev(netdev
, "Received newlink with wrong IFNAME %s",
424 netdev_enter_failed(netdev
);
428 r
= sd_rtnl_message_enter_container(message
, IFLA_LINKINFO
);
430 log_error_netdev(netdev
, "Could not get LINKINFO");
434 r
= sd_rtnl_message_read_string(message
, IFLA_INFO_KIND
, &received_kind
);
436 log_error_netdev(netdev
, "Could not get KIND");
440 r
= sd_rtnl_message_exit_container(message
);
442 log_error_netdev(netdev
, "Could not exit container");
446 kind
= netdev_kind_to_string(netdev
->kind
);
448 log_error_netdev(netdev
, "Could not get kind");
449 netdev_enter_failed(netdev
);
453 if (!streq(kind
, received_kind
)) {
454 log_error_netdev(netdev
, "Received newlink with wrong KIND %s, "
455 "expected %s", received_kind
, kind
);
456 netdev_enter_failed(netdev
);
460 netdev
->ifindex
= ifindex
;
462 netdev_enter_ready(netdev
);
467 static int netdev_load_one(Manager
*manager
, const char *filename
) {
468 _cleanup_netdev_unref_ NetDev
*netdev
= NULL
;
469 _cleanup_fclose_
FILE *file
= NULL
;
475 if (null_or_empty_path(filename
)) {
476 log_debug("skipping empty file: %s", filename
);
480 file
= fopen(filename
, "re");
488 netdev
= new0(NetDev
, 1);
493 netdev
->manager
= manager
;
494 netdev
->state
= _NETDEV_STATE_INVALID
;
495 netdev
->kind
= _NETDEV_KIND_INVALID
;
496 netdev
->macvlan_mode
= _NETDEV_MACVLAN_MODE_INVALID
;
497 netdev
->vlanid
= VLANID_MAX
+ 1;
499 r
= config_parse(NULL
, filename
, file
, "Match\0NetDev\0VLAN\0MACVLAN\0",
500 config_item_perf_lookup
, (void*) network_netdev_gperf_lookup
,
501 false, false, netdev
);
503 log_warning("Could not parse config file %s: %s", filename
, strerror(-r
));
507 if (netdev
->kind
== _NETDEV_KIND_INVALID
) {
508 log_warning("NetDev without Kind configured in %s. Ignoring", filename
);
513 log_warning("NetDev without Name configured in %s. Ignoring", filename
);
517 if (netdev
->kind
== NETDEV_KIND_VLAN
&& netdev
->vlanid
> VLANID_MAX
) {
518 log_warning("VLAN without valid Id configured in %s. Ignoring", filename
);
522 if (netdev
->kind
!= NETDEV_KIND_VLAN
&& netdev
->vlanid
<= VLANID_MAX
) {
523 log_warning("VLAN Id configured for a %s in %s. Ignoring",
524 netdev_kind_to_string(netdev
->kind
), filename
);
528 if (netdev
->kind
!= NETDEV_KIND_MACVLAN
&&
529 netdev
->macvlan_mode
!= _NETDEV_MACVLAN_MODE_INVALID
) {
530 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
531 netdev_kind_to_string(netdev
->kind
), filename
);
535 netdev
->filename
= strdup(filename
);
536 if (!netdev
->filename
)
539 if (net_match_config(NULL
, NULL
, NULL
, NULL
, NULL
,
540 netdev
->match_host
, netdev
->match_virt
,
541 netdev
->match_kernel
, netdev
->match_arch
,
542 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
) <= 0)
545 r
= hashmap_put(netdev
->manager
->netdevs
, netdev
->name
, netdev
);
549 LIST_HEAD_INIT(netdev
->callbacks
);
551 if (netdev
->kind
!= NETDEV_KIND_VLAN
&&
552 netdev
->kind
!= NETDEV_KIND_MACVLAN
) {
553 r
= netdev_create(netdev
, NULL
, NULL
);
558 log_debug_netdev(netdev
, "loaded %s", netdev_kind_to_string(netdev
->kind
));
565 int netdev_load(Manager
*manager
) {
572 while ((netdev
= hashmap_first(manager
->netdevs
)))
573 netdev_unref(netdev
);
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
);