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