]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: netdev - support joining already existing netdevs
[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 "net-util.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, 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
194 static 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
229 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
230 NetDev *netdev = userdata;
231 int r;
232
233 assert(netdev->state != _NETDEV_STATE_INVALID);
234
235 r = sd_rtnl_message_get_errno(m);
236 if (r == -EEXIST)
237 r = netdev_getlink(netdev);
238
239 if (r < 0) {
240 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
241 netdev_enter_failed(netdev);
242
243 return 1;
244 }
245
246 return 1;
247 }
248
249 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
250 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
251 const char *kind;
252 int r;
253
254 assert(netdev);
255 assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
256 (link && callback));
257 assert(netdev->name);
258 assert(netdev->manager);
259 assert(netdev->manager->rtnl);
260
261 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
262 if (r < 0) {
263 log_error_netdev(netdev,
264 "Could not allocate RTM_NEWLINK message: %s",
265 strerror(-r));
266 return r;
267 }
268
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
279 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
280 if (r < 0) {
281 log_error_netdev(netdev,
282 "Could not append IFLA_IFNAME attribute: %s",
283 strerror(-r));
284 return r;
285 }
286
287 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
288 if (r < 0) {
289 log_error_netdev(netdev,
290 "Could not open IFLA_LINKINFO container: %s",
291 strerror(-r));
292 return r;
293 }
294
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);
302 if (r < 0) {
303 log_error_netdev(netdev,
304 "Could not append IFLA_INFO_KIND attribute: %s",
305 strerror(-r));
306 return r;
307 }
308
309 if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
310 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
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
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 }
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
347 r = sd_rtnl_message_close_container(req);
348 if (r < 0) {
349 log_error_netdev(netdev,
350 "Could not close IFLA_LINKINFO container %s",
351 strerror(-r));
352 return r;
353 }
354
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);
359 if (r < 0) {
360 log_error_netdev(netdev,
361 "Could not send rtnetlink message: %s", strerror(-r));
362 return r;
363 }
364
365 log_debug_netdev(netdev, "creating netdev");
366
367 netdev->state = NETDEV_STATE_CREATING;
368
369 return 0;
370 }
371
372 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
373 if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
374 return netdev_create(netdev, link, callback);
375
376 if (netdev->state == NETDEV_STATE_READY) {
377 netdev_enslave_ready(netdev, link, callback);
378 } else {
379 /* the netdev is not yet read, save this request for when it is*/
380 netdev_enslave_callback *cb;
381
382 cb = new0(netdev_enslave_callback, 1);
383 if (!cb)
384 return log_oom();
385
386 cb->callback = callback;
387 cb->link = link;
388
389 LIST_PREPEND(callbacks, netdev->callbacks, cb);
390 }
391
392 return 0;
393 }
394
395 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
396 assert(netdev);
397 assert(ifindex > 0);
398
399 if (netdev->ifindex > 0) {
400 if (netdev->ifindex == ifindex)
401 return 0;
402 else
403 return -EEXIST;
404 }
405
406 netdev->ifindex = ifindex;
407
408 netdev_enter_ready(netdev);
409
410 return 0;
411 }
412
413 static int netdev_load_one(Manager *manager, const char *filename) {
414 _cleanup_netdev_free_ NetDev *netdev = NULL;
415 _cleanup_fclose_ FILE *file = NULL;
416 int r;
417
418 assert(manager);
419 assert(filename);
420
421 file = fopen(filename, "re");
422 if (!file) {
423 if (errno == ENOENT)
424 return 0;
425 else
426 return errno;
427 }
428
429 netdev = new0(NetDev, 1);
430 if (!netdev)
431 return log_oom();
432
433 netdev->manager = manager;
434 netdev->state = _NETDEV_STATE_INVALID;
435 netdev->kind = _NETDEV_KIND_INVALID;
436 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
437 netdev->vlanid = VLANID_MAX + 1;
438
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);
442 if (r < 0) {
443 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
444 return r;
445 }
446
447 if (netdev->kind == _NETDEV_KIND_INVALID) {
448 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
449 return 0;
450 }
451
452 if (!netdev->name) {
453 log_warning("NetDev without Name configured in %s. Ignoring", filename);
454 return 0;
455 }
456
457 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
458 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
459 return 0;
460 }
461
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
475 netdev->filename = strdup(filename);
476 if (!netdev->filename)
477 return log_oom();
478
479 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
480 netdev->match_host, netdev->match_virt,
481 netdev->match_kernel, netdev->match_arch,
482 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
483 return 0;
484
485 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
486 if (r < 0)
487 return r;
488
489 LIST_HEAD_INIT(netdev->callbacks);
490
491 if (netdev->kind != NETDEV_KIND_VLAN &&
492 netdev->kind != NETDEV_KIND_MACVLAN) {
493 r = netdev_create(netdev, NULL, NULL);
494 if (r < 0)
495 return r;
496 }
497
498 netdev = NULL;
499
500 return 0;
501 }
502
503 int netdev_load(Manager *manager) {
504 NetDev *netdev;
505 char **files, **f;
506 int r;
507
508 assert(manager);
509
510 while ((netdev = hashmap_first(manager->netdevs)))
511 netdev_free(netdev);
512
513 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
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) {
520 r = netdev_load_one(manager, *f);
521 if (r < 0)
522 return r;
523 }
524
525 strv_free(files);
526
527 return 0;
528 }