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