]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
networkd: introduce ipip tunnel
[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->name)
85 hashmap_remove(netdev->manager->netdevs, netdev->name);
86
87 free(netdev->filename);
88
89 free(netdev->description);
90 free(netdev->name);
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->name);
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->name);
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->name);
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->name, 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 netdev_enter_ready(netdev);
509
510 return 0;
511 }
512
513 static int netdev_load_one(Manager *manager, const char *filename) {
514 _cleanup_netdev_unref_ NetDev *netdev = NULL;
515 _cleanup_fclose_ FILE *file = NULL;
516 int r;
517
518 assert(manager);
519 assert(filename);
520
521 if (null_or_empty_path(filename)) {
522 log_debug("skipping empty file: %s", filename);
523 return 0;
524 }
525
526 file = fopen(filename, "re");
527 if (!file) {
528 if (errno == ENOENT)
529 return 0;
530 else
531 return -errno;
532 }
533
534 netdev = new0(NetDev, 1);
535 if (!netdev)
536 return log_oom();
537
538 netdev->n_ref = 1;
539 netdev->manager = manager;
540 netdev->state = _NETDEV_STATE_INVALID;
541 netdev->kind = _NETDEV_KIND_INVALID;
542 netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
543 netdev->vlanid = VLANID_MAX + 1;
544
545 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0Tunnel\0",
546 config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
547 false, false, netdev);
548 if (r < 0) {
549 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
550 return r;
551 }
552
553 if (netdev->kind == _NETDEV_KIND_INVALID) {
554 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
555 return 0;
556 }
557
558 if (!netdev->name) {
559 log_warning("NetDev without Name configured in %s. Ignoring", filename);
560 return 0;
561 }
562
563 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
564 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
565 return 0;
566 }
567
568 if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
569 log_warning("VLAN Id configured for a %s in %s. Ignoring",
570 netdev_kind_to_string(netdev->kind), filename);
571 return 0;
572 }
573
574 if (netdev->kind != NETDEV_KIND_MACVLAN &&
575 netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
576 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
577 netdev_kind_to_string(netdev->kind), filename);
578 return 0;
579 }
580
581 netdev->filename = strdup(filename);
582 if (!netdev->filename)
583 return log_oom();
584
585 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
586 netdev->match_host, netdev->match_virt,
587 netdev->match_kernel, netdev->match_arch,
588 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
589 return 0;
590
591 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
592 if (r < 0)
593 return r;
594
595 LIST_HEAD_INIT(netdev->callbacks);
596
597 if (netdev->kind != NETDEV_KIND_VLAN &&
598 netdev->kind != NETDEV_KIND_MACVLAN &&
599 netdev->kind != NETDEV_KIND_IPIP &&
600 netdev->kind != NETDEV_KIND_GRE &&
601 netdev->kind != NETDEV_KIND_SIT) {
602 r = netdev_create(netdev, NULL, NULL);
603 if (r < 0)
604 return r;
605 }
606
607 log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
608
609 netdev = NULL;
610
611 return 0;
612 }
613
614 int netdev_load(Manager *manager) {
615 NetDev *netdev;
616 char **files, **f;
617 int r;
618
619 assert(manager);
620
621 while ((netdev = hashmap_first(manager->netdevs)))
622 netdev_unref(netdev);
623
624 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
625 if (r < 0) {
626 log_error("Failed to enumerate netdev files: %s", strerror(-r));
627 return r;
628 }
629
630 STRV_FOREACH_BACKWARDS(f, files) {
631 r = netdev_load_one(manager, *f);
632 if (r < 0)
633 return r;
634 }
635
636 strv_free(files);
637
638 return 0;
639 }