]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
networkd: link - clean up state files
[thirdparty/systemd.git] / src / network / networkd-netdev.c
CommitLineData
02b59d57
TG
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 "networkd.h"
c6f7c917 23#include "network-internal.h"
02b59d57
TG
24#include "path-util.h"
25#include "conf-files.h"
26#include "conf-parser.h"
27#include "list.h"
28
672682a6
TG
29#define VLANID_MAX 4094
30
2c5859af 31static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
52433f6b 32 [NETDEV_KIND_BRIDGE] = "bridge",
54abf461
TG
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
fe6b2d55 35 [NETDEV_KIND_MACVLAN] = "macvlan",
52433f6b 36};
02b59d57 37
1a436809
TG
38DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
52433f6b 40
2c5859af 41static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
fe6b2d55
TG
42 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
46};
47
48DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
50
1a436809 51void netdev_free(NetDev *netdev) {
52433f6b
TG
52 netdev_enslave_callback *callback;
53
54 if (!netdev)
02b59d57
TG
55 return;
56
52433f6b
TG
57 while ((callback = netdev->callbacks)) {
58 LIST_REMOVE(callbacks, netdev->callbacks, callback);
02b59d57
TG
59 free(callback);
60 }
61
52433f6b
TG
62 if (netdev->name)
63 hashmap_remove(netdev->manager->netdevs, netdev->name);
02b59d57 64
52433f6b 65 free(netdev->filename);
02b59d57 66
52433f6b
TG
67 free(netdev->description);
68 free(netdev->name);
02b59d57 69
79e16ce3
LP
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);
74
52433f6b 75 free(netdev);
02b59d57
TG
76}
77
1a436809
TG
78int netdev_get(Manager *manager, const char *name, NetDev **ret) {
79 NetDev *netdev;
02b59d57
TG
80
81 assert(manager);
82 assert(name);
83 assert(ret);
84
52433f6b
TG
85 netdev = hashmap_get(manager->netdevs, name);
86 if (!netdev) {
02b59d57
TG
87 *ret = NULL;
88 return -ENOENT;
89 }
90
52433f6b 91 *ret = netdev;
02b59d57
TG
92
93 return 0;
94}
95
1a436809 96static int netdev_enter_failed(NetDev *netdev) {
52433f6b 97 netdev->state = NETDEV_STATE_FAILED;
02b59d57
TG
98
99 return 0;
100}
101
1a436809 102static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
cf6a8911 103 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
02b59d57
TG
104 int r;
105
52433f6b
TG
106 assert(netdev);
107 assert(netdev->state == NETDEV_STATE_READY);
4fb7242c
TG
108 assert(netdev->manager);
109 assert(netdev->manager->rtnl);
02b59d57
TG
110 assert(link);
111 assert(callback);
112
151b9b96
LP
113 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
114 RTM_SETLINK, link->ifindex);
02b59d57 115 if (r < 0) {
52433f6b 116 log_error_netdev(netdev,
3333d748
ZJS
117 "Could not allocate RTM_SETLINK message: %s",
118 strerror(-r));
02b59d57
TG
119 return r;
120 }
121
50add290 122 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
02b59d57 123 if (r < 0) {
52433f6b 124 log_error_netdev(netdev,
3333d748
ZJS
125 "Could not append IFLA_MASTER attribute: %s",
126 strerror(-r));
02b59d57
TG
127 return r;
128 }
129
52433f6b 130 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 131 if (r < 0) {
52433f6b 132 log_error_netdev(netdev,
3333d748
ZJS
133 "Could not send rtnetlink message: %s",
134 strerror(-r));
02b59d57
TG
135 return r;
136 }
137
52433f6b 138 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 139
02b59d57
TG
140 return 0;
141}
142
1a436809 143static int netdev_enter_ready(NetDev *netdev) {
52433f6b 144 netdev_enslave_callback *callback;
02b59d57 145
52433f6b
TG
146 assert(netdev);
147 assert(netdev->name);
924fe430 148
ba5596ec
TG
149 if (netdev->state != NETDEV_STATE_CREATING)
150 return 0;
151
52433f6b 152 netdev->state = NETDEV_STATE_READY;
02b59d57 153
52433f6b 154 log_info_netdev(netdev, "netdev ready");
02b59d57 155
52433f6b
TG
156 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
157 /* enslave the links that were attempted to be enslaved befor the
02b59d57 158 * link was ready */
52433f6b 159 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
160 }
161
162 return 0;
163}
52433f6b 164static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
1a436809 165 NetDev *netdev = userdata;
172f6635 166 int r;
02b59d57 167
52433f6b 168 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
169
170 r = sd_rtnl_message_get_errno(m);
e09826dc 171 if (r == -EEXIST)
505f8da7
TG
172 log_debug_netdev(netdev, "netdev exists, using existing");
173 else if (r < 0) {
52433f6b
TG
174 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
175 netdev_enter_failed(netdev);
dd3efc09
TG
176
177 return 1;
02b59d57
TG
178 }
179
dd3efc09 180 return 1;
02b59d57
TG
181}
182
1a436809 183static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
cf6a8911 184 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 185 const char *kind;
02b59d57
TG
186 int r;
187
52433f6b 188 assert(netdev);
fe6b2d55
TG
189 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
190 (link && callback));
52433f6b
TG
191 assert(netdev->name);
192 assert(netdev->manager);
193 assert(netdev->manager->rtnl);
02b59d57 194
151b9b96 195 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
02b59d57 196 if (r < 0) {
52433f6b 197 log_error_netdev(netdev,
3333d748
ZJS
198 "Could not allocate RTM_NEWLINK message: %s",
199 strerror(-r));
02b59d57
TG
200 return r;
201 }
202
54abf461
TG
203 if (link) {
204 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
205 if (r < 0) {
206 log_error_netdev(netdev,
207 "Could not append IFLA_LINK attribute: %s",
208 strerror(-r));
209 return r;
210 }
211 }
212
52433f6b 213 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 214 if (r < 0) {
52433f6b 215 log_error_netdev(netdev,
3333d748
ZJS
216 "Could not append IFLA_IFNAME attribute: %s",
217 strerror(-r));
02b59d57
TG
218 return r;
219 }
220
ee3a6a51 221 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
02b59d57 222 if (r < 0) {
52433f6b 223 log_error_netdev(netdev,
3333d748
ZJS
224 "Could not open IFLA_LINKINFO container: %s",
225 strerror(-r));
02b59d57
TG
226 return r;
227 }
228
52433f6b
TG
229 kind = netdev_kind_to_string(netdev->kind);
230 if (!kind) {
231 log_error_netdev(netdev, "Invalid kind");
232 return -EINVAL;
233 }
234
d8e538ec 235 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
02b59d57 236 if (r < 0) {
52433f6b 237 log_error_netdev(netdev,
d8e538ec
TG
238 "Could not open IFLA_INFO_DATA container: %s",
239 strerror(-r));
02b59d57
TG
240 return r;
241 }
242
d8e538ec
TG
243 if (netdev->vlanid <= VLANID_MAX) {
244 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
54abf461
TG
245 if (r < 0) {
246 log_error_netdev(netdev,
d8e538ec 247 "Could not append IFLA_VLAN_ID attribute: %s",
54abf461
TG
248 strerror(-r));
249 return r;
250 }
d8e538ec 251 }
54abf461 252
d8e538ec
TG
253 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
254 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
255 if (r < 0) {
256 log_error_netdev(netdev,
257 "Could not append IFLA_MACVLAN_MODE attribute: %s",
258 strerror(-r));
54abf461
TG
259 return r;
260 }
261 }
262
d8e538ec
TG
263 r = sd_rtnl_message_close_container(req);
264 if (r < 0) {
265 log_error_netdev(netdev,
266 "Could not close IFLA_INFO_DATA container %s",
267 strerror(-r));
268 return r;
269 }
270
02b59d57
TG
271 r = sd_rtnl_message_close_container(req);
272 if (r < 0) {
52433f6b 273 log_error_netdev(netdev,
3333d748
ZJS
274 "Could not close IFLA_LINKINFO container %s",
275 strerror(-r));
02b59d57
TG
276 return r;
277 }
278
54abf461
TG
279 if (link)
280 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
281 else
282 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 283 if (r < 0) {
52433f6b 284 log_error_netdev(netdev,
3333d748 285 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
286 return r;
287 }
288
52433f6b 289 log_debug_netdev(netdev, "creating netdev");
02b59d57 290
52433f6b 291 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
292
293 return 0;
294}
295
1a436809 296int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
fe6b2d55 297 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
54abf461
TG
298 return netdev_create(netdev, link, callback);
299
52433f6b
TG
300 if (netdev->state == NETDEV_STATE_READY) {
301 netdev_enslave_ready(netdev, link, callback);
02b59d57 302 } else {
52433f6b
TG
303 /* the netdev is not yet read, save this request for when it is*/
304 netdev_enslave_callback *cb;
02b59d57 305
52433f6b 306 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
307 if (!cb)
308 return log_oom();
309
310 cb->callback = callback;
311 cb->link = link;
312
52433f6b 313 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
314 }
315
316 return 0;
317}
318
d39edfc7 319int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
c3ebdce3 320 uint16_t type;
d39edfc7
TG
321 const char *kind;
322 char *received_kind;
c6315a7a 323 char *received_name;
d39edfc7
TG
324 int r, ifindex;
325
50add290 326 assert(netdev);
c3ebdce3 327 assert(message);
02b59d57 328
c3ebdce3 329 r = sd_rtnl_message_get_type(message, &type);
ba5596ec
TG
330 if (r < 0) {
331 log_error_netdev(netdev, "Could not get rtnl message type");
c3ebdce3 332 return r;
ba5596ec 333 }
c3ebdce3 334
ba5596ec
TG
335 if (type != RTM_NEWLINK) {
336 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
c3ebdce3 337 return -EINVAL;
ba5596ec 338 }
d39edfc7 339
a21df104
TG
340 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
341 if (r < 0) {
342 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
343 netdev_enter_failed(netdev);
344 return r;
345 } else if (ifindex <= 0) {
346 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
347 netdev_enter_failed(netdev);
348 return r;
349 }
350
351
352 if (netdev->ifindex > 0) {
353 if (netdev->ifindex != ifindex) {
354 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
355 ifindex, netdev->ifindex);
356 netdev_enter_failed(netdev);
357 return -EEXIST;
358 } else
359 /* ifindex already set to the same for this netdev */
360 return 0;
361 }
362
c6315a7a
TG
363 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
364 if (r < 0) {
365 log_error_netdev(netdev, "Could not get IFNAME");
366 return r;
367 }
368
369 if (!streq(netdev->name, received_name)) {
370 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
371 received_name);
372 netdev_enter_failed(netdev);
373 return r;
374 }
375
d39edfc7
TG
376 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
377 if (r < 0) {
378 log_error_netdev(netdev, "Could not get LINKINFO");
379 return r;
380 }
381
382 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
383 if (r < 0) {
384 log_error_netdev(netdev, "Could not get KIND");
385 return r;
386 }
387
505f8da7
TG
388 r = sd_rtnl_message_exit_container(message);
389 if (r < 0) {
390 log_error_netdev(netdev, "Could not exit container");
391 return r;
392 }
393
c3ebdce3
TG
394 kind = netdev_kind_to_string(netdev->kind);
395 if (!kind) {
396 log_error_netdev(netdev, "Could not get kind");
397 netdev_enter_failed(netdev);
398 return -EINVAL;
399 }
400
d39edfc7 401 if (!streq(kind, received_kind)) {
c6315a7a
TG
402 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
403 "expected %s", received_kind, kind);
d39edfc7
TG
404 netdev_enter_failed(netdev);
405 return r;
406 }
407
50add290 408 netdev->ifindex = ifindex;
52433f6b
TG
409
410 netdev_enter_ready(netdev);
02b59d57
TG
411
412 return 0;
413}
414
52433f6b 415static int netdev_load_one(Manager *manager, const char *filename) {
1a436809 416 _cleanup_netdev_free_ NetDev *netdev = NULL;
02b59d57
TG
417 _cleanup_fclose_ FILE *file = NULL;
418 int r;
419
bf1bc670
TA
420 assert(manager);
421 assert(filename);
422
6916ec29
TG
423 if (null_or_empty_path(filename)) {
424 log_debug("skipping empty file: %s", filename);
425 return 0;
426 }
427
02b59d57
TG
428 file = fopen(filename, "re");
429 if (!file) {
430 if (errno == ENOENT)
431 return 0;
432 else
ecb08ec6 433 return -errno;
02b59d57
TG
434 }
435
1a436809 436 netdev = new0(NetDev, 1);
52433f6b 437 if (!netdev)
02b59d57
TG
438 return log_oom();
439
52433f6b
TG
440 netdev->manager = manager;
441 netdev->state = _NETDEV_STATE_INVALID;
442 netdev->kind = _NETDEV_KIND_INVALID;
fe6b2d55 443 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
672682a6 444 netdev->vlanid = VLANID_MAX + 1;
02b59d57 445
fe6b2d55
TG
446 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
447 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
448 false, false, netdev);
02b59d57
TG
449 if (r < 0) {
450 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
451 return r;
449f7554 452 }
02b59d57 453
52433f6b 454 if (netdev->kind == _NETDEV_KIND_INVALID) {
1a436809 455 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
52433f6b
TG
456 return 0;
457 }
458
459 if (!netdev->name) {
1a436809 460 log_warning("NetDev without Name configured in %s. Ignoring", filename);
02b59d57
TG
461 return 0;
462 }
463
672682a6
TG
464 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
465 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
54abf461
TG
466 return 0;
467 }
468
fe6b2d55
TG
469 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
470 log_warning("VLAN Id configured for a %s in %s. Ignoring",
471 netdev_kind_to_string(netdev->kind), filename);
472 return 0;
473 }
474
475 if (netdev->kind != NETDEV_KIND_MACVLAN &&
476 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
477 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
478 netdev_kind_to_string(netdev->kind), filename);
479 return 0;
480 }
481
52433f6b
TG
482 netdev->filename = strdup(filename);
483 if (!netdev->filename)
02b59d57
TG
484 return log_oom();
485
c0dda186 486 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
edbb03e9
TG
487 netdev->match_host, netdev->match_virt,
488 netdev->match_kernel, netdev->match_arch,
bf175aaf 489 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
c0dda186
TG
490 return 0;
491
52433f6b 492 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
493 if (r < 0)
494 return r;
495
52433f6b 496 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 497
fe6b2d55
TG
498 if (netdev->kind != NETDEV_KIND_VLAN &&
499 netdev->kind != NETDEV_KIND_MACVLAN) {
54abf461
TG
500 r = netdev_create(netdev, NULL, NULL);
501 if (r < 0)
502 return r;
503 }
02b59d57 504
69ceb044
TG
505 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
506
52433f6b 507 netdev = NULL;
02b59d57
TG
508
509 return 0;
510}
511
52433f6b 512int netdev_load(Manager *manager) {
1a436809 513 NetDev *netdev;
02b59d57
TG
514 char **files, **f;
515 int r;
516
517 assert(manager);
518
52433f6b
TG
519 while ((netdev = hashmap_first(manager->netdevs)))
520 netdev_free(netdev);
02b59d57 521
2ad8416d 522 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
523 if (r < 0) {
524 log_error("Failed to enumerate netdev files: %s", strerror(-r));
525 return r;
526 }
527
528 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 529 r = netdev_load_one(manager, *f);
02b59d57
TG
530 if (r < 0)
531 return r;
532 }
533
534 strv_free(files);
535
536 return 0;
537}