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