chiark / gitweb /
networkd: add support for Uplink Failure Detection
[elogind.git] / src / network / networkd-netdev-tunnel.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4     This file is part of systemd.
5
6     Copyright 2014 Susant Sahani
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 <arpa/inet.h>
23 #include <net/if.h>
24 #include <linux/ip.h>
25 #include <linux/if_tunnel.h>
26 #include <linux/ip6_tunnel.h>
27
28 #include "sd-rtnl.h"
29 #include "networkd-netdev-tunnel.h"
30 #include "networkd-link.h"
31 #include "util.h"
32 #include "missing.h"
33 #include "conf-parser.h"
34
35 #define DEFAULT_TNL_HOP_LIMIT   64
36
37 static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = {
38         [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6",
39         [NETDEV_IP6_TNL_MODE_IPIP6] = "ipip6",
40         [NETDEV_IP6_TNL_MODE_ANYIP6] = "any",
41 };
42
43 DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode);
44 DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode, "Failed to parse ip6 tunnel Mode");
45
46 static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
47         Tunnel *t = IPIP(netdev);
48         int r;
49
50         assert(netdev);
51         assert(link);
52         assert(m);
53         assert(t);
54         assert(t->family == AF_INET);
55
56         r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
57         if (r < 0) {
58                 log_netdev_error(netdev,
59                                  "Could not append IFLA_IPTUN_LINK attribute: %s",
60                                  strerror(-r));
61                 return r;
62         }
63
64         r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
65         if (r < 0) {
66                 log_netdev_error(netdev,
67                                  "Could not append IFLA_IPTUN_LOCAL attribute: %s",
68                                  strerror(-r));
69                 return r;
70         }
71
72         r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in);
73         if (r < 0) {
74                 log_netdev_error(netdev,
75                                  "Could not append IFLA_IPTUN_REMOTE attribute: %s",
76                                  strerror(-r));
77                 return r;
78         }
79
80         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
81         if (r < 0) {
82                 log_netdev_error(netdev,
83                                  "Could not append IFLA_IPTUN_TTL  attribute: %s",
84                                  strerror(-r));
85                 return r;
86         }
87
88         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc);
89         if (r < 0) {
90                 log_netdev_error(netdev,
91                                  "Could not append IFLA_IPTUN_PMTUDISC attribute: %s",
92                                  strerror(-r));
93                 return r;
94         }
95
96         return r;
97 }
98
99 static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
100         Tunnel *t = SIT(netdev);
101         int r;
102
103         assert(netdev);
104         assert(link);
105         assert(m);
106         assert(t);
107         assert(t->family == AF_INET);
108
109         r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
110         if (r < 0) {
111                 log_netdev_error(netdev,
112                                  "Could not append IFLA_IPTUN_LINK attribute: %s",
113                                  strerror(-r));
114                 return r;
115         }
116
117         r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
118         if (r < 0) {
119                 log_netdev_error(netdev,
120                                  "Could not append IFLA_IPTUN_LOCAL attribute: %s",
121                                  strerror(-r));
122                 return r;
123         }
124
125         r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in);
126         if (r < 0) {
127                 log_netdev_error(netdev,
128                                  "Could not append IFLA_IPTUN_REMOTE attribute: %s",
129                                  strerror(-r));
130                 return r;
131         }
132
133         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
134         if (r < 0) {
135                 log_netdev_error(netdev,
136                                  "Could not append IFLA_IPTUN_TTL attribute: %s",
137                                  strerror(-r));
138                 return r;
139         }
140
141         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc);
142         if (r < 0) {
143                 log_netdev_error(netdev,
144                                  "Could not append IFLA_IPTUN_PMTUDISC attribute: %s",
145                                  strerror(-r));
146                 return r;
147         }
148
149         return r;
150 }
151
152 static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
153         Tunnel *t;
154         int r;
155
156         assert(netdev);
157
158         if (netdev->kind == NETDEV_KIND_GRE)
159                  t = GRE(netdev);
160         else
161                  t = GRETAP(netdev);
162
163         assert(t);
164         assert(t->family == AF_INET);
165         assert(link);
166         assert(m);
167
168         r = sd_rtnl_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
169         if (r < 0) {
170                 log_netdev_error(netdev,
171                                  "Could not append IFLA_GRE_LINK attribute: %s",
172                                  strerror(-r));
173                 return r;
174         }
175
176         r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in);
177         if (r < 0) {
178                 log_netdev_error(netdev,
179                                  "Could not append IFLA_GRE_LOCAL attribute: %s",
180                                  strerror(-r));
181                 return r;
182         }
183
184         r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in);
185         if (r < 0) {
186                 log_netdev_error(netdev,
187                                  "Could not append IFLA_GRE_REMOTE attribute: %s",
188                                  strerror(-r));
189                 return r;
190         }
191
192         r = sd_rtnl_message_append_u8(m, IFLA_GRE_TTL, t->ttl);
193         if (r < 0) {
194                 log_netdev_error(netdev,
195                                  "Could not append IFLA_GRE_TTL attribute: %s",
196                                  strerror(-r));
197                 return r;
198         }
199
200         r = sd_rtnl_message_append_u8(m, IFLA_GRE_TOS, t->tos);
201         if (r < 0) {
202                 log_netdev_error(netdev,
203                                  "Could not append IFLA_GRE_TOS attribute: %s",
204                                  strerror(-r));
205                 return r;
206         }
207
208         r = sd_rtnl_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc);
209         if (r < 0) {
210                 log_netdev_error(netdev,
211                                  "Could not append IFLA_GRE_PMTUDISC attribute: %s",
212                                  strerror(-r));
213                 return r;
214         }
215
216         return r;
217 }
218
219 static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
220         Tunnel *t;
221         int r;
222
223         assert(netdev);
224
225         if (netdev->kind == NETDEV_KIND_IP6GRE)
226                  t = IP6GRE(netdev);
227         else
228                  t = IP6GRETAP(netdev);
229
230         assert(t);
231         assert(t->family == AF_INET6);
232         assert(link);
233         assert(m);
234
235         r = sd_rtnl_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
236         if (r < 0) {
237                 log_netdev_error(netdev,
238                                  "Could not append IFLA_GRE_LINK attribute: %s",
239                                  strerror(-r));
240                 return r;
241         }
242
243         r = sd_rtnl_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6);
244         if (r < 0) {
245                 log_netdev_error(netdev,
246                                  "Could not append IFLA_GRE_LOCAL attribute: %s",
247                                  strerror(-r));
248                 return r;
249         }
250
251         r = sd_rtnl_message_append_in6_addr(m, IFLA_GRE_REMOTE, &t->remote.in6);
252         if (r < 0) {
253                 log_netdev_error(netdev,
254                                  "Could not append IFLA_GRE_REMOTE attribute: %s",
255                                  strerror(-r));
256                 return r;
257         }
258
259         r = sd_rtnl_message_append_u8(m, IFLA_GRE_TTL, t->ttl);
260         if (r < 0) {
261                 log_netdev_error(netdev,
262                                  "Could not append IFLA_GRE_TTL attribute: %s",
263                                  strerror(-r));
264                 return r;
265         }
266
267         return r;
268 }
269
270 static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
271         Tunnel *t = VTI(netdev);
272         int r;
273
274         assert(netdev);
275         assert(link);
276         assert(m);
277         assert(t);
278         assert(t->family == AF_INET);
279
280         r = sd_rtnl_message_append_u32(m, IFLA_VTI_LINK, link->ifindex);
281         if (r < 0) {
282                 log_netdev_error(netdev,
283                                  "Could not append IFLA_IPTUN_LINK attribute: %s",
284                                  strerror(-r));
285                 return r;
286         }
287
288         r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in);
289         if (r < 0) {
290                 log_netdev_error(netdev,
291                                  "Could not append IFLA_IPTUN_LOCAL attribute: %s",
292                                  strerror(-r));
293                 return r;
294         }
295
296         r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in);
297         if (r < 0) {
298                 log_netdev_error(netdev,
299                                  "Could not append IFLA_IPTUN_REMOTE attribute: %s",
300                                  strerror(-r));
301                 return r;
302         }
303
304         return r;
305 }
306
307 static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) {
308         Tunnel *t = IP6TNL(netdev);
309         uint8_t proto;
310         int r;
311
312         assert(netdev);
313         assert(link);
314         assert(m);
315         assert(t);
316         assert(t->family == AF_INET6);
317
318         r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
319         if (r < 0) {
320                 log_netdev_error(netdev,
321                                  "Could not append IFLA_IPTUN_LINK attribute: %s",
322                                  strerror(-r));
323                 return r;
324         }
325
326         r = sd_rtnl_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6);
327         if (r < 0) {
328                 log_netdev_error(netdev,
329                                  "Could not append IFLA_IPTUN_LOCAL attribute: %s",
330                                  strerror(-r));
331                 return r;
332         }
333
334         r = sd_rtnl_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6);
335         if (r < 0) {
336                 log_netdev_error(netdev,
337                                  "Could not append IFLA_IPTUN_REMOTE attribute: %s",
338                                  strerror(-r));
339                 return r;
340         }
341
342         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl);
343         if (r < 0) {
344                 log_netdev_error(netdev,
345                                  "Could not append IFLA_IPTUN_TTL attribute: %s",
346                                  strerror(-r));
347                 return r;
348         }
349
350         switch (t->ip6tnl_mode) {
351         case NETDEV_IP6_TNL_MODE_IP6IP6:
352                 proto = IPPROTO_IPV6;
353                 break;
354         case NETDEV_IP6_TNL_MODE_IPIP6:
355                 proto = IPPROTO_IPIP;
356                 break;
357         case NETDEV_IP6_TNL_MODE_ANYIP6:
358         default:
359                 proto = 0;
360                 break;
361         }
362
363         r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PROTO, proto);
364         if (r < 0) {
365                 log_netdev_error(netdev,
366                                  "Could not append IFLA_IPTUN_MODE attribute: %s",
367                                  strerror(-r));
368                 return r;
369         }
370
371         return r;
372 }
373
374 static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
375         Tunnel *t = NULL;
376
377         assert(netdev);
378         assert(filename);
379
380         switch (netdev->kind) {
381         case NETDEV_KIND_IPIP:
382                 t = IPIP(netdev);
383                 break;
384         case NETDEV_KIND_SIT:
385                 t = SIT(netdev);
386                 break;
387         case NETDEV_KIND_GRE:
388                 t = GRE(netdev);
389                 break;
390         case NETDEV_KIND_GRETAP:
391                 t = GRETAP(netdev);
392                 break;
393         case NETDEV_KIND_IP6GRE:
394                 t = IP6GRE(netdev);
395                 break;
396         case NETDEV_KIND_IP6GRETAP:
397                 t = IP6GRETAP(netdev);
398                 break;
399         case NETDEV_KIND_VTI:
400                 t = VTI(netdev);
401                 break;
402         case NETDEV_KIND_IP6TNL:
403                 t = IP6TNL(netdev);
404                 break;
405         default:
406                 assert_not_reached("Invalid tunnel kind");
407         }
408
409         assert(t);
410
411         if (t->remote.in.s_addr == INADDR_ANY) {
412                log_warning("Tunnel without remote address configured in %s. Ignoring", filename);
413                return -EINVAL;
414         }
415
416         if (t->family != AF_INET && t->family != AF_INET6) {
417               log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
418               return -EINVAL;
419         }
420
421         if (netdev->kind == NETDEV_KIND_IP6TNL) {
422                 if (t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) {
423                         log_warning("IP6 Tunnel without mode configured in %s. Ignoring", filename);
424                         return -EINVAL;
425                 }
426         }
427
428         return 0;
429 }
430
431 int config_parse_tunnel_address(const char *unit,
432                                 const char *filename,
433                                 unsigned line,
434                                 const char *section,
435                                 unsigned section_line,
436                                 const char *lvalue,
437                                 int ltype,
438                                 const char *rvalue,
439                                 void *data,
440                                 void *userdata) {
441         Tunnel *t = userdata;
442         union in_addr_union *addr = data, buffer;
443         int r, f;
444
445         assert(filename);
446         assert(lvalue);
447         assert(rvalue);
448         assert(data);
449
450         r = in_addr_from_string_auto(rvalue, &f, &buffer);
451         if (r < 0) {
452                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
453                 return 0;
454         }
455
456         if (t->family != AF_UNSPEC && t->family != f) {
457                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
458                 return 0;
459         }
460
461         t->family = f;
462         *addr = buffer;
463
464         return 0;
465 }
466
467 static void ipip_init(NetDev *n) {
468         Tunnel *t = IPIP(n);
469
470         assert(n);
471         assert(t);
472
473         t->pmtudisc = true;
474 }
475
476 static void sit_init(NetDev *n) {
477         Tunnel *t = SIT(n);
478
479         assert(n);
480         assert(t);
481
482         t->pmtudisc = true;
483 }
484
485 static void vti_init(NetDev *n) {
486         Tunnel *t = VTI(n);
487
488         assert(n);
489         assert(t);
490
491         t->pmtudisc = true;
492 }
493
494 static void gre_init(NetDev *n) {
495         Tunnel *t;
496
497         assert(n);
498
499         if (n->kind == NETDEV_KIND_GRE)
500                 t = GRE(n);
501         else
502                 t = GRETAP(n);
503
504         assert(t);
505
506         t->pmtudisc = true;
507 }
508
509 static void ip6gre_init(NetDev *n) {
510         Tunnel *t;
511
512         assert(n);
513
514         if (n->kind == NETDEV_KIND_IP6GRE)
515                 t = IP6GRE(n);
516         else
517                 t = IP6GRETAP(n);
518
519         assert(t);
520
521         t->ttl = DEFAULT_TNL_HOP_LIMIT;
522 }
523
524 static void ip6tnl_init(NetDev *n) {
525         Tunnel *t = IP6TNL(n);
526
527         assert(n);
528         assert(t);
529
530         t->ttl = DEFAULT_TNL_HOP_LIMIT;
531         t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
532         t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID;
533 }
534
535 const NetDevVTable ipip_vtable = {
536         .object_size = sizeof(Tunnel),
537         .init = ipip_init,
538         .sections = "Match\0NetDev\0Tunnel\0",
539         .fill_message_create = netdev_ipip_fill_message_create,
540         .create_type = NETDEV_CREATE_STACKED,
541         .config_verify = netdev_tunnel_verify,
542 };
543
544 const NetDevVTable sit_vtable = {
545         .object_size = sizeof(Tunnel),
546         .init = sit_init,
547         .sections = "Match\0NetDev\0Tunnel\0",
548         .fill_message_create = netdev_sit_fill_message_create,
549         .create_type = NETDEV_CREATE_STACKED,
550         .config_verify = netdev_tunnel_verify,
551 };
552
553 const NetDevVTable vti_vtable = {
554         .object_size = sizeof(Tunnel),
555         .init = vti_init,
556         .sections = "Match\0NetDev\0Tunnel\0",
557         .fill_message_create = netdev_vti_fill_message_create,
558         .create_type = NETDEV_CREATE_STACKED,
559         .config_verify = netdev_tunnel_verify,
560 };
561
562 const NetDevVTable gre_vtable = {
563         .object_size = sizeof(Tunnel),
564         .init = gre_init,
565         .sections = "Match\0NetDev\0Tunnel\0",
566         .fill_message_create = netdev_gre_fill_message_create,
567         .create_type = NETDEV_CREATE_STACKED,
568         .config_verify = netdev_tunnel_verify,
569 };
570
571 const NetDevVTable gretap_vtable = {
572         .object_size = sizeof(Tunnel),
573         .init = gre_init,
574         .sections = "Match\0NetDev\0Tunnel\0",
575         .fill_message_create = netdev_gre_fill_message_create,
576         .create_type = NETDEV_CREATE_STACKED,
577         .config_verify = netdev_tunnel_verify,
578 };
579
580 const NetDevVTable ip6gre_vtable = {
581         .object_size = sizeof(Tunnel),
582         .init = ip6gre_init,
583         .sections = "Match\0NetDev\0Tunnel\0",
584         .fill_message_create = netdev_ip6gre_fill_message_create,
585         .create_type = NETDEV_CREATE_STACKED,
586         .config_verify = netdev_tunnel_verify,
587 };
588
589 const NetDevVTable ip6gretap_vtable = {
590         .object_size = sizeof(Tunnel),
591         .init = ip6gre_init,
592         .sections = "Match\0NetDev\0Tunnel\0",
593         .fill_message_create = netdev_ip6gre_fill_message_create,
594         .create_type = NETDEV_CREATE_STACKED,
595         .config_verify = netdev_tunnel_verify,
596 };
597
598 const NetDevVTable ip6tnl_vtable = {
599         .object_size = sizeof(Tunnel),
600         .init = ip6tnl_init,
601         .sections = "Match\0NetDev\0Tunnel\0",
602         .fill_message_create = netdev_ip6tnl_fill_message_create,
603         .create_type = NETDEV_CREATE_STACKED,
604         .config_verify = netdev_tunnel_verify,
605 };