]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/fou-tunnel.c
login: respect install_sysconfdir_samples in meson file
[thirdparty/systemd.git] / src / network / netdev / fou-tunnel.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/fou.h>
4 #include <net/if.h>
5 #include <netinet/in.h>
6 #include <linux/ip.h>
7
8 #include "conf-parser.h"
9 #include "fou-tunnel.h"
10 #include "ip-protocol-list.h"
11 #include "netlink-util.h"
12 #include "networkd-manager.h"
13 #include "parse-util.h"
14 #include "string-table.h"
15 #include "string-util.h"
16 #include "util.h"
17
18 static const char* const fou_encap_type_table[_NETDEV_FOO_OVER_UDP_ENCAP_MAX] = {
19 [NETDEV_FOO_OVER_UDP_ENCAP_DIRECT] = "FooOverUDP",
20 [NETDEV_FOO_OVER_UDP_ENCAP_GUE] = "GenericUDPEncapsulation",
21 };
22
23 DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType);
24 DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType,
25 "Failed to parse Encapsulation=");
26
27 static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) {
28 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
29 FouTunnel *t;
30 uint8_t encap_type;
31 int r;
32
33 assert(netdev);
34
35 t = FOU(netdev);
36
37 assert(t);
38
39 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
40 if (r < 0)
41 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
42
43 r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port));
44 if (r < 0)
45 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m");
46
47 if (IN_SET(t->peer_family, AF_INET, AF_INET6)) {
48 r = sd_netlink_message_append_u16(m, FOU_ATTR_PEER_PORT, htobe16(t->peer_port));
49 if (r < 0)
50 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_PORT attribute: %m");
51 }
52
53 switch (t->fou_encap_type) {
54 case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT:
55 encap_type = FOU_ENCAP_DIRECT;
56 break;
57 case NETDEV_FOO_OVER_UDP_ENCAP_GUE:
58 encap_type = FOU_ENCAP_GUE;
59 break;
60 default:
61 assert_not_reached("invalid encap type");
62 }
63
64 r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type);
65 if (r < 0)
66 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m");
67
68 r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET);
69 if (r < 0)
70 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m");
71
72 r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol);
73 if (r < 0)
74 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m");
75
76 if (t->local_family == AF_INET) {
77 r = sd_netlink_message_append_in_addr(m, FOU_ATTR_LOCAL_V4, &t->local.in);
78 if (r < 0)
79 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V4 attribute: %m");
80 } else if (t->local_family == AF_INET6) {
81 r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_LOCAL_V6, &t->local.in6);
82 if (r < 0)
83 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V6 attribute: %m");
84 }
85
86 if (t->peer_family == AF_INET) {
87 r = sd_netlink_message_append_in_addr(m, FOU_ATTR_PEER_V4, &t->peer.in);
88 if (r < 0)
89 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V4 attribute: %m");
90 } else if (t->peer_family == AF_INET6){
91 r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_PEER_V6, &t->peer.in6);
92 if (r < 0)
93 return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V6 attribute: %m");
94 }
95
96 *ret = TAKE_PTR(m);
97 return 0;
98 }
99
100 static int fou_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
101 int r;
102
103 assert(netdev);
104 assert(netdev->state != _NETDEV_STATE_INVALID);
105
106 r = sd_netlink_message_get_errno(m);
107 if (r == -EEXIST)
108 log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
109 else if (r < 0) {
110 log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
111 netdev_drop(netdev);
112
113 return 1;
114 }
115
116 log_netdev_debug(netdev, "FooOverUDP tunnel is created");
117 return 1;
118 }
119
120 static int netdev_fou_tunnel_create(NetDev *netdev) {
121 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
122 int r;
123
124 assert(netdev);
125 assert(FOU(netdev));
126
127 r = netdev_fill_fou_tunnel_message(netdev, &m);
128 if (r < 0)
129 return r;
130
131 r = netlink_call_async(netdev->manager->genl, NULL, m, fou_tunnel_create_handler,
132 netdev_destroy_callback, netdev);
133 if (r < 0)
134 return log_netdev_error_errno(netdev, r, "Failed to create FooOverUDP tunnel: %m");
135
136 netdev_ref(netdev);
137 return 0;
138 }
139
140 int config_parse_ip_protocol(
141 const char *unit,
142 const char *filename,
143 unsigned line,
144 const char *section,
145 unsigned section_line,
146 const char *lvalue,
147 int ltype,
148 const char *rvalue,
149 void *data,
150 void *userdata) {
151
152 uint8_t *ret = data;
153 unsigned protocol;
154 /* linux/fou.h defines the netlink field as one byte, so we need to reject protocols numbers that
155 * don't fit in one byte. */
156 int r;
157
158 assert(filename);
159 assert(section);
160 assert(lvalue);
161 assert(rvalue);
162 assert(data);
163
164 r = parse_ip_protocol(rvalue);
165 if (r >= 0)
166 protocol = r;
167 else {
168 r = safe_atou(rvalue, &protocol);
169 if (r < 0)
170 log_syntax(unit, LOG_WARNING, filename, line, r,
171 "Failed to parse IP protocol '%s' for FooOverUDP tunnel, "
172 "ignoring assignment: %m", rvalue);
173 return 0;
174 }
175
176 if (protocol > UINT8_MAX) {
177 log_syntax(unit, LOG_WARNING, filename, line, 0,
178 "IP protocol '%s' for FooOverUDP tunnel out of range, "
179 "ignoring assignment: %m", rvalue);
180 return 0;
181 }
182
183 *ret = protocol;
184 return 0;
185 }
186
187 int config_parse_fou_tunnel_address(
188 const char *unit,
189 const char *filename,
190 unsigned line,
191 const char *section,
192 unsigned section_line,
193 const char *lvalue,
194 int ltype,
195 const char *rvalue,
196 void *data,
197 void *userdata) {
198
199 union in_addr_union *addr = data;
200 FouTunnel *t = userdata;
201 int r, *f;
202
203 assert(filename);
204 assert(lvalue);
205 assert(rvalue);
206 assert(data);
207
208 if (streq(lvalue, "Local"))
209 f = &t->local_family;
210 else
211 f = &t->peer_family;
212
213 r = in_addr_from_string_auto(rvalue, f, addr);
214 if (r < 0)
215 log_syntax(unit, LOG_WARNING, filename, line, r,
216 "FooOverUDP tunnel '%s' address is invalid, ignoring assignment: %s",
217 lvalue, rvalue);
218
219 return 0;
220 }
221
222 static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) {
223 FouTunnel *t;
224
225 assert(netdev);
226 assert(filename);
227
228 t = FOU(netdev);
229
230 assert(t);
231
232 switch (t->fou_encap_type) {
233 case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT:
234 if (t->fou_protocol <= 0)
235 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
236 "FooOverUDP protocol not configured in %s. Rejecting configuration.",
237 filename);
238 break;
239 case NETDEV_FOO_OVER_UDP_ENCAP_GUE:
240 if (t->fou_protocol > 0)
241 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
242 "FooOverUDP GUE can't be set with protocol configured in %s. Rejecting configuration.",
243 filename);
244 break;
245 default:
246 assert_not_reached("Invalid fou encap type");
247 }
248
249 if (t->peer_family == AF_UNSPEC && t->peer_port > 0)
250 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
251 "FooOverUDP peer port is set but peer address not configured in %s. Rejecting configuration.",
252 filename);
253 else if (t->peer_family != AF_UNSPEC && t->peer_port == 0)
254 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
255 "FooOverUDP peer port not set but peer address is configured in %s. Rejecting configuration.",
256 filename);
257 return 0;
258 }
259
260 static void fou_tunnel_init(NetDev *netdev) {
261 FouTunnel *t;
262
263 assert(netdev);
264
265 t = FOU(netdev);
266
267 assert(t);
268
269 t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
270 }
271
272 const NetDevVTable foutnl_vtable = {
273 .object_size = sizeof(FouTunnel),
274 .init = fou_tunnel_init,
275 .sections = NETDEV_COMMON_SECTIONS "FooOverUDP\0",
276 .create = netdev_fou_tunnel_create,
277 .create_type = NETDEV_CREATE_INDEPENDENT,
278 .config_verify = netdev_fou_tunnel_verify,
279 };