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