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