]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: link - introduce LINGER state and link_drop()
[thirdparty/systemd.git] / src / network / networkd-netdev.c
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"
23 #include "network-internal.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "list.h"
28
29 #define VLANID_MAX 4094
30
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",
36 };
37
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");
40
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",
46 };
47
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");
50
51 static void netdev_cancel_callbacks(NetDev *netdev) {
52 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
53 netdev_enslave_callback *callback;
54
55 if (!netdev)
56 return;
57
58 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
59
60 while ((callback = netdev->callbacks)) {
61 if (m) {
62 assert(callback->link);
63 assert(callback->callback);
64 assert(netdev->manager);
65 assert(netdev->manager->rtnl);
66
67 callback->callback(netdev->manager->rtnl, m, link);
68 }
69
70 LIST_REMOVE(callbacks, netdev->callbacks, callback);
71 free(callback);
72 }
73 }
74
75 static void netdev_free(NetDev *netdev) {
76 if (!netdev)
77 return;
78
79 netdev_cancel_callbacks(netdev);
80
81 if (netdev->name)
82 hashmap_remove(netdev->manager->netdevs, netdev->name);
83
84 free(netdev->filename);
85
86 free(netdev->description);
87 free(netdev->name);
88
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);
93
94 free(netdev);
95 }
96
97 NetDev *netdev_unref(NetDev *netdev) {
98 if (netdev && (-- netdev->n_ref <= 0))
99 netdev_free(netdev);
100
101 return NULL;
102 }
103
104 NetDev *netdev_ref(NetDev *netdev) {
105 if (netdev)
106 assert_se(++ netdev->n_ref >= 2);
107
108 return netdev;
109 }
110
111 void netdev_drop(NetDev *netdev) {
112 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
113 return;
114
115 netdev->state = NETDEV_STATE_LINGER;
116
117 log_debug_netdev(netdev, "dropped");
118
119 netdev_cancel_callbacks(netdev);
120
121 netdev_unref(netdev);
122
123 return;
124 }
125
126 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
127 NetDev *netdev;
128
129 assert(manager);
130 assert(name);
131 assert(ret);
132
133 netdev = hashmap_get(manager->netdevs, name);
134 if (!netdev) {
135 *ret = NULL;
136 return -ENOENT;
137 }
138
139 *ret = netdev;
140
141 return 0;
142 }
143
144 static int netdev_enter_failed(NetDev *netdev) {
145 netdev->state = NETDEV_STATE_FAILED;
146
147 return 0;
148 }
149
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;
152 int r;
153
154 assert(netdev);
155 assert(netdev->state == NETDEV_STATE_READY);
156 assert(netdev->manager);
157 assert(netdev->manager->rtnl);
158 assert(link);
159 assert(callback);
160
161 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
162 RTM_SETLINK, link->ifindex);
163 if (r < 0) {
164 log_error_netdev(netdev,
165 "Could not allocate RTM_SETLINK message: %s",
166 strerror(-r));
167 return r;
168 }
169
170 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
171 if (r < 0) {
172 log_error_netdev(netdev,
173 "Could not append IFLA_MASTER attribute: %s",
174 strerror(-r));
175 return r;
176 }
177
178 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
179 if (r < 0) {
180 log_error_netdev(netdev,
181 "Could not send rtnetlink message: %s",
182 strerror(-r));
183 return r;
184 }
185
186 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
187
188 return 0;
189 }
190
191 static int netdev_enter_ready(NetDev *netdev) {
192 netdev_enslave_callback *callback;
193
194 assert(netdev);
195 assert(netdev->name);
196
197 if (netdev->state != NETDEV_STATE_CREATING)
198 return 0;
199
200 netdev->state = NETDEV_STATE_READY;
201
202 log_info_netdev(netdev, "netdev ready");
203
204 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
205 /* enslave the links that were attempted to be enslaved before the
206 * link was ready */
207 netdev_enslave_ready(netdev, callback->link, callback->callback);
208 }
209
210 return 0;
211 }
212 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
213 NetDev *netdev = userdata;
214 int r;
215
216 assert(netdev->state != _NETDEV_STATE_INVALID);
217
218 r = sd_rtnl_message_get_errno(m);
219 if (r == -EEXIST)
220 log_debug_netdev(netdev, "netdev exists, using existing");
221 else if (r < 0) {
222 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
223 netdev_enter_failed(netdev);
224
225 return 1;
226 }
227
228 return 1;
229 }
230
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;
233 const char *kind;
234 int r;
235
236 assert(netdev);
237 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
238 (link && callback));
239 assert(netdev->name);
240 assert(netdev->manager);
241 assert(netdev->manager->rtnl);
242
243 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
244 if (r < 0) {
245 log_error_netdev(netdev,
246 "Could not allocate RTM_NEWLINK message: %s",
247 strerror(-r));
248 return r;
249 }
250
251 if (link) {
252 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
253 if (r < 0) {
254 log_error_netdev(netdev,
255 "Could not append IFLA_LINK attribute: %s",
256 strerror(-r));
257 return r;
258 }
259 }
260
261 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
262 if (r < 0) {
263 log_error_netdev(netdev,
264 "Could not append IFLA_IFNAME attribute: %s",
265 strerror(-r));
266 return r;
267 }
268
269 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
270 if (r < 0) {
271 log_error_netdev(netdev,
272 "Could not open IFLA_LINKINFO container: %s",
273 strerror(-r));
274 return r;
275 }
276
277 kind = netdev_kind_to_string(netdev->kind);
278 if (!kind) {
279 log_error_netdev(netdev, "Invalid kind");
280 return -EINVAL;
281 }
282
283 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
284 if (r < 0) {
285 log_error_netdev(netdev,
286 "Could not open IFLA_INFO_DATA container: %s",
287 strerror(-r));
288 return r;
289 }
290
291 if (netdev->vlanid <= VLANID_MAX) {
292 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
293 if (r < 0) {
294 log_error_netdev(netdev,
295 "Could not append IFLA_VLAN_ID attribute: %s",
296 strerror(-r));
297 return r;
298 }
299 }
300
301 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
302 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
303 if (r < 0) {
304 log_error_netdev(netdev,
305 "Could not append IFLA_MACVLAN_MODE attribute: %s",
306 strerror(-r));
307 return r;
308 }
309 }
310
311 r = sd_rtnl_message_close_container(req);
312 if (r < 0) {
313 log_error_netdev(netdev,
314 "Could not close IFLA_INFO_DATA container %s",
315 strerror(-r));
316 return r;
317 }
318
319 r = sd_rtnl_message_close_container(req);
320 if (r < 0) {
321 log_error_netdev(netdev,
322 "Could not close IFLA_LINKINFO container %s",
323 strerror(-r));
324 return r;
325 }
326
327 if (link)
328 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
329 else
330 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
331 if (r < 0) {
332 log_error_netdev(netdev,
333 "Could not send rtnetlink message: %s", strerror(-r));
334 return r;
335 }
336
337 log_debug_netdev(netdev, "creating netdev");
338
339 netdev->state = NETDEV_STATE_CREATING;
340
341 return 0;
342 }
343
344 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
345 int r;
346
347 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
348 return netdev_create(netdev, link, callback);
349
350 if (netdev->state == NETDEV_STATE_READY) {
351 r = netdev_enslave_ready(netdev, link, callback);
352 if (r < 0)
353 return r;
354 } else {
355 /* the netdev is not yet read, save this request for when it is*/
356 netdev_enslave_callback *cb;
357
358 cb = new0(netdev_enslave_callback, 1);
359 if (!cb)
360 return log_oom();
361
362 cb->callback = callback;
363 cb->link = link;
364
365 LIST_PREPEND(callbacks, netdev->callbacks, cb);
366 }
367
368 return 0;
369 }
370
371 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
372 uint16_t type;
373 const char *kind;
374 char *received_kind;
375 char *received_name;
376 int r, ifindex;
377
378 assert(netdev);
379 assert(message);
380
381 r = sd_rtnl_message_get_type(message, &type);
382 if (r < 0) {
383 log_error_netdev(netdev, "Could not get rtnl message type");
384 return r;
385 }
386
387 if (type != RTM_NEWLINK) {
388 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
389 return -EINVAL;
390 }
391
392 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
393 if (r < 0) {
394 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
395 netdev_enter_failed(netdev);
396 return r;
397 } else if (ifindex <= 0) {
398 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
399 netdev_enter_failed(netdev);
400 return r;
401 }
402
403
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);
409 return -EEXIST;
410 } else
411 /* ifindex already set to the same for this netdev */
412 return 0;
413 }
414
415 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
416 if (r < 0) {
417 log_error_netdev(netdev, "Could not get IFNAME");
418 return r;
419 }
420
421 if (!streq(netdev->name, received_name)) {
422 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
423 received_name);
424 netdev_enter_failed(netdev);
425 return r;
426 }
427
428 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
429 if (r < 0) {
430 log_error_netdev(netdev, "Could not get LINKINFO");
431 return r;
432 }
433
434 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
435 if (r < 0) {
436 log_error_netdev(netdev, "Could not get KIND");
437 return r;
438 }
439
440 r = sd_rtnl_message_exit_container(message);
441 if (r < 0) {
442 log_error_netdev(netdev, "Could not exit container");
443 return r;
444 }
445
446 kind = netdev_kind_to_string(netdev->kind);
447 if (!kind) {
448 log_error_netdev(netdev, "Could not get kind");
449 netdev_enter_failed(netdev);
450 return -EINVAL;
451 }
452
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);
457 return r;
458 }
459
460 netdev->ifindex = ifindex;
461
462 netdev_enter_ready(netdev);
463
464 return 0;
465 }
466
467 static int netdev_load_one(Manager *manager, const char *filename) {
468 _cleanup_netdev_unref_ NetDev *netdev = NULL;
469 _cleanup_fclose_ FILE *file = NULL;
470 int r;
471
472 assert(manager);
473 assert(filename);
474
475 if (null_or_empty_path(filename)) {
476 log_debug("skipping empty file: %s", filename);
477 return 0;
478 }
479
480 file = fopen(filename, "re");
481 if (!file) {
482 if (errno == ENOENT)
483 return 0;
484 else
485 return -errno;
486 }
487
488 netdev = new0(NetDev, 1);
489 if (!netdev)
490 return log_oom();
491
492 netdev->n_ref = 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;
498
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);
502 if (r < 0) {
503 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
504 return r;
505 }
506
507 if (netdev->kind == _NETDEV_KIND_INVALID) {
508 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
509 return 0;
510 }
511
512 if (!netdev->name) {
513 log_warning("NetDev without Name configured in %s. Ignoring", filename);
514 return 0;
515 }
516
517 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
518 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
519 return 0;
520 }
521
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);
525 return 0;
526 }
527
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);
532 return 0;
533 }
534
535 netdev->filename = strdup(filename);
536 if (!netdev->filename)
537 return log_oom();
538
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)
543 return 0;
544
545 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
546 if (r < 0)
547 return r;
548
549 LIST_HEAD_INIT(netdev->callbacks);
550
551 if (netdev->kind != NETDEV_KIND_VLAN &&
552 netdev->kind != NETDEV_KIND_MACVLAN) {
553 r = netdev_create(netdev, NULL, NULL);
554 if (r < 0)
555 return r;
556 }
557
558 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
559
560 netdev = NULL;
561
562 return 0;
563 }
564
565 int netdev_load(Manager *manager) {
566 NetDev *netdev;
567 char **files, **f;
568 int r;
569
570 assert(manager);
571
572 while ((netdev = hashmap_first(manager->netdevs)))
573 netdev_unref(netdev);
574
575 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
576 if (r < 0) {
577 log_error("Failed to enumerate netdev files: %s", strerror(-r));
578 return r;
579 }
580
581 STRV_FOREACH_BACKWARDS(f, files) {
582 r = netdev_load_one(manager, *f);
583 if (r < 0)
584 return r;
585 }
586
587 strv_free(files);
588
589 return 0;
590 }