]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: log ifindices when links and netdevs are added
[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 [NETDEV_KIND_IPIP] = "ipip",
37 [NETDEV_KIND_GRE] = "gre",
38 [NETDEV_KIND_SIT] = "sit",
39 };
40
41 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
42 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
43
44 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
45 [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
46 [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
47 [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
48 [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
49 };
50
51 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
52 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
53
54 static void netdev_cancel_callbacks(NetDev *netdev) {
55 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
56 netdev_enslave_callback *callback;
57
58 if (!netdev)
59 return;
60
61 rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
62
63 while ((callback = netdev->callbacks)) {
64 if (m) {
65 assert(callback->link);
66 assert(callback->callback);
67 assert(netdev->manager);
68 assert(netdev->manager->rtnl);
69
70 callback->callback(netdev->manager->rtnl, m, link);
71 }
72
73 LIST_REMOVE(callbacks, netdev->callbacks, callback);
74 free(callback);
75 }
76 }
77
78 static void netdev_free(NetDev *netdev) {
79 if (!netdev)
80 return;
81
82 netdev_cancel_callbacks(netdev);
83
84 if (netdev->ifname)
85 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
86
87 free(netdev->filename);
88
89 free(netdev->description);
90 free(netdev->ifname);
91
92 condition_free_list(netdev->match_host);
93 condition_free_list(netdev->match_virt);
94 condition_free_list(netdev->match_kernel);
95 condition_free_list(netdev->match_arch);
96
97 free(netdev);
98 }
99
100 NetDev *netdev_unref(NetDev *netdev) {
101 if (netdev && (-- netdev->n_ref <= 0))
102 netdev_free(netdev);
103
104 return NULL;
105 }
106
107 NetDev *netdev_ref(NetDev *netdev) {
108 if (netdev)
109 assert_se(++ netdev->n_ref >= 2);
110
111 return netdev;
112 }
113
114 void netdev_drop(NetDev *netdev) {
115 if (!netdev || netdev->state == NETDEV_STATE_LINGER)
116 return;
117
118 netdev->state = NETDEV_STATE_LINGER;
119
120 log_debug_netdev(netdev, "netdev removed");
121
122 netdev_cancel_callbacks(netdev);
123
124 netdev_unref(netdev);
125
126 return;
127 }
128
129 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
130 NetDev *netdev;
131
132 assert(manager);
133 assert(name);
134 assert(ret);
135
136 netdev = hashmap_get(manager->netdevs, name);
137 if (!netdev) {
138 *ret = NULL;
139 return -ENOENT;
140 }
141
142 *ret = netdev;
143
144 return 0;
145 }
146
147 static int netdev_enter_failed(NetDev *netdev) {
148 netdev->state = NETDEV_STATE_FAILED;
149
150 return 0;
151 }
152
153 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
154 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
155 int r;
156
157 assert(netdev);
158 assert(netdev->state == NETDEV_STATE_READY);
159 assert(netdev->manager);
160 assert(netdev->manager->rtnl);
161 assert(link);
162 assert(callback);
163
164 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
165 RTM_SETLINK, link->ifindex);
166 if (r < 0) {
167 log_error_netdev(netdev,
168 "Could not allocate RTM_SETLINK message: %s",
169 strerror(-r));
170 return r;
171 }
172
173 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
174 if (r < 0) {
175 log_error_netdev(netdev,
176 "Could not append IFLA_MASTER attribute: %s",
177 strerror(-r));
178 return r;
179 }
180
181 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
182 if (r < 0) {
183 log_error_netdev(netdev,
184 "Could not send rtnetlink message: %s",
185 strerror(-r));
186 return r;
187 }
188
189 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
190
191 return 0;
192 }
193
194 static int netdev_enter_ready(NetDev *netdev) {
195 netdev_enslave_callback *callback;
196
197 assert(netdev);
198 assert(netdev->ifname);
199
200 if (netdev->state != NETDEV_STATE_CREATING)
201 return 0;
202
203 netdev->state = NETDEV_STATE_READY;
204
205 log_info_netdev(netdev, "netdev ready");
206
207 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
208 /* enslave the links that were attempted to be enslaved before the
209 * link was ready */
210 netdev_enslave_ready(netdev, callback->link, callback->callback);
211 }
212
213 return 0;
214 }
215 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
216 NetDev *netdev = userdata;
217 int r;
218
219 assert(netdev->state != _NETDEV_STATE_INVALID);
220
221 r = sd_rtnl_message_get_errno(m);
222 if (r == -EEXIST)
223 log_debug_netdev(netdev, "netdev exists, using existing");
224 else if (r < 0) {
225 log_warning_netdev(netdev, "netdev could not be greated: %s", strerror(-r));
226 netdev_drop(netdev);
227
228 return 1;
229 }
230
231 return 1;
232 }
233
234 int config_parse_tunnel_address(const char *unit,
235 const char *filename,
236 unsigned line,
237 const char *section,
238 unsigned section_line,
239 const char *lvalue,
240 int ltype,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
244 NetDev *n = data;
245 unsigned char family = AF_INET;
246 int r;
247
248 assert(filename);
249 assert(lvalue);
250 assert(rvalue);
251 assert(data);
252
253 r = net_parse_inaddr(rvalue, &family, n);
254 if (r < 0) {
255 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
256 "Tunnel address is invalid, ignoring assignment: %s", rvalue);
257 return 0;
258 }
259 return 0;
260 }
261
262 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
263 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
264 const char *kind;
265 int r;
266
267 assert(netdev);
268 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
269 (link && callback));
270 assert(netdev->ifname);
271 assert(netdev->manager);
272 assert(netdev->manager->rtnl);
273
274 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
275 if (r < 0) {
276 log_error_netdev(netdev,
277 "Could not allocate RTM_NEWLINK message: %s",
278 strerror(-r));
279 return r;
280 }
281
282 if (link) {
283 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
284 if (r < 0) {
285 log_error_netdev(netdev,
286 "Could not append IFLA_LINK attribute: %s",
287 strerror(-r));
288 return r;
289 }
290 }
291
292 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->ifname);
293 if (r < 0) {
294 log_error_netdev(netdev,
295 "Could not append IFLA_IFNAME attribute: %s",
296 strerror(-r));
297 return r;
298 }
299
300 if(netdev->mtu) {
301 r = sd_rtnl_message_append_u32(req, IFLA_MTU, netdev->mtu);
302 if (r < 0) {
303 log_error_netdev(netdev,
304 "Could not append IFLA_MTU attribute: %s",
305 strerror(-r));
306 return r;
307 }
308 }
309
310 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
311 if (r < 0) {
312 log_error_netdev(netdev,
313 "Could not open IFLA_LINKINFO container: %s",
314 strerror(-r));
315 return r;
316 }
317
318 kind = netdev_kind_to_string(netdev->kind);
319 if (!kind) {
320 log_error_netdev(netdev, "Invalid kind");
321 return -EINVAL;
322 }
323
324 r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
325 if (r < 0) {
326 log_error_netdev(netdev,
327 "Could not open IFLA_INFO_DATA container: %s",
328 strerror(-r));
329 return r;
330 }
331
332 if (netdev->vlanid <= VLANID_MAX) {
333 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
334 if (r < 0) {
335 log_error_netdev(netdev,
336 "Could not append IFLA_VLAN_ID attribute: %s",
337 strerror(-r));
338 return r;
339 }
340 }
341
342 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
343 r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
344 if (r < 0) {
345 log_error_netdev(netdev,
346 "Could not append IFLA_MACVLAN_MODE attribute: %s",
347 strerror(-r));
348 return r;
349 }
350 }
351
352 r = sd_rtnl_message_close_container(req);
353 if (r < 0) {
354 log_error_netdev(netdev,
355 "Could not close IFLA_INFO_DATA container %s",
356 strerror(-r));
357 return r;
358 }
359
360 r = sd_rtnl_message_close_container(req);
361 if (r < 0) {
362 log_error_netdev(netdev,
363 "Could not close IFLA_LINKINFO container %s",
364 strerror(-r));
365 return r;
366 }
367
368 if (link)
369 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
370 else
371 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
372 if (r < 0) {
373 log_error_netdev(netdev,
374 "Could not send rtnetlink message: %s", strerror(-r));
375 return r;
376 }
377
378 log_debug_netdev(netdev, "creating netdev");
379
380 netdev->state = NETDEV_STATE_CREATING;
381
382 return 0;
383 }
384
385 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
386 int r;
387
388 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
389 return netdev_create(netdev, link, callback);
390
391 if(netdev->kind == NETDEV_KIND_IPIP ||
392 netdev->kind == NETDEV_KIND_GRE ||
393 netdev->kind == NETDEV_KIND_SIT)
394 return netdev_create_tunnel(link, netdev_create_handler);
395
396 if (netdev->state == NETDEV_STATE_READY) {
397 r = netdev_enslave_ready(netdev, link, callback);
398 if (r < 0)
399 return r;
400 } else {
401 /* the netdev is not yet read, save this request for when it is*/
402 netdev_enslave_callback *cb;
403
404 cb = new0(netdev_enslave_callback, 1);
405 if (!cb)
406 return log_oom();
407
408 cb->callback = callback;
409 cb->link = link;
410
411 LIST_PREPEND(callbacks, netdev->callbacks, cb);
412 }
413
414 return 0;
415 }
416
417 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
418 uint16_t type;
419 const char *kind;
420 char *received_kind;
421 char *received_name;
422 int r, ifindex;
423
424 assert(netdev);
425 assert(message);
426
427 r = sd_rtnl_message_get_type(message, &type);
428 if (r < 0) {
429 log_error_netdev(netdev, "Could not get rtnl message type");
430 return r;
431 }
432
433 if (type != RTM_NEWLINK) {
434 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
435 return -EINVAL;
436 }
437
438 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
439 if (r < 0) {
440 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
441 netdev_enter_failed(netdev);
442 return r;
443 } else if (ifindex <= 0) {
444 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
445 netdev_enter_failed(netdev);
446 return r;
447 }
448
449
450 if (netdev->ifindex > 0) {
451 if (netdev->ifindex != ifindex) {
452 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
453 ifindex, netdev->ifindex);
454 netdev_enter_failed(netdev);
455 return -EEXIST;
456 } else
457 /* ifindex already set to the same for this netdev */
458 return 0;
459 }
460
461 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
462 if (r < 0) {
463 log_error_netdev(netdev, "Could not get IFNAME");
464 return r;
465 }
466
467 if (!streq(netdev->ifname, received_name)) {
468 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
469 received_name);
470 netdev_enter_failed(netdev);
471 return r;
472 }
473
474 r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
475 if (r < 0) {
476 log_error_netdev(netdev, "Could not get LINKINFO");
477 return r;
478 }
479
480 r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
481 if (r < 0) {
482 log_error_netdev(netdev, "Could not get KIND");
483 return r;
484 }
485
486 r = sd_rtnl_message_exit_container(message);
487 if (r < 0) {
488 log_error_netdev(netdev, "Could not exit container");
489 return r;
490 }
491
492 kind = netdev_kind_to_string(netdev->kind);
493 if (!kind) {
494 log_error_netdev(netdev, "Could not get kind");
495 netdev_enter_failed(netdev);
496 return -EINVAL;
497 }
498
499 if (!streq(kind, received_kind)) {
500 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
501 "expected %s", received_kind, kind);
502 netdev_enter_failed(netdev);
503 return r;
504 }
505
506 netdev->ifindex = ifindex;
507
508 log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
509
510 netdev_enter_ready(netdev);
511
512 return 0;
513 }
514
515 static int netdev_load_one(Manager *manager, const char *filename) {
516 _cleanup_netdev_unref_ NetDev *netdev = NULL;
517 _cleanup_fclose_ FILE *file = NULL;
518 int r;
519
520 assert(manager);
521 assert(filename);
522
523 if (null_or_empty_path(filename)) {
524 log_debug("skipping empty file: %s", filename);
525 return 0;
526 }
527
528 file = fopen(filename, "re");
529 if (!file) {
530 if (errno == ENOENT)
531 return 0;
532 else
533 return -errno;
534 }
535
536 netdev = new0(NetDev, 1);
537 if (!netdev)
538 return log_oom();
539
540 netdev->n_ref = 1;
541 netdev->manager = manager;
542 netdev->state = _NETDEV_STATE_INVALID;
543 netdev->kind = _NETDEV_KIND_INVALID;
544 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
545 netdev->vlanid = VLANID_MAX + 1;
546
547 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0Tunnel\0",
548 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
549 false, false, netdev);
550 if (r < 0) {
551 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
552 return r;
553 }
554
555 if (netdev->kind == _NETDEV_KIND_INVALID) {
556 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
557 return 0;
558 }
559
560 if (!netdev->ifname) {
561 log_warning("NetDev without Name configured in %s. Ignoring", filename);
562 return 0;
563 }
564
565 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
566 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
567 return 0;
568 }
569
570 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
571 log_warning("VLAN Id configured for a %s in %s. Ignoring",
572 netdev_kind_to_string(netdev->kind), filename);
573 return 0;
574 }
575
576 if (netdev->kind != NETDEV_KIND_MACVLAN &&
577 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
578 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
579 netdev_kind_to_string(netdev->kind), filename);
580 return 0;
581 }
582
583 netdev->filename = strdup(filename);
584 if (!netdev->filename)
585 return log_oom();
586
587 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
588 netdev->match_host, netdev->match_virt,
589 netdev->match_kernel, netdev->match_arch,
590 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
591 return 0;
592
593 r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
594 if (r < 0)
595 return r;
596
597 LIST_HEAD_INIT(netdev->callbacks);
598
599 if (netdev->kind != NETDEV_KIND_VLAN &&
600 netdev->kind != NETDEV_KIND_MACVLAN &&
601 netdev->kind != NETDEV_KIND_IPIP &&
602 netdev->kind != NETDEV_KIND_GRE &&
603 netdev->kind != NETDEV_KIND_SIT) {
604 r = netdev_create(netdev, NULL, NULL);
605 if (r < 0)
606 return r;
607 }
608
609 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
610
611 netdev = NULL;
612
613 return 0;
614 }
615
616 int netdev_load(Manager *manager) {
617 NetDev *netdev;
618 char **files, **f;
619 int r;
620
621 assert(manager);
622
623 while ((netdev = hashmap_first(manager->netdevs)))
624 netdev_unref(netdev);
625
626 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
627 if (r < 0) {
628 log_error("Failed to enumerate netdev files: %s", strerror(-r));
629 return r;
630 }
631
632 STRV_FOREACH_BACKWARDS(f, files) {
633 r = netdev_load_one(manager, *f);
634 if (r < 0)
635 return r;
636 }
637
638 strv_free(files);
639
640 return 0;
641 }