Branch data 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 : : };
|