]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
53cb501a | 2 | |
edda10f2 | 3 | /* Make sure the net/if.h header is included before any linux/ one */ |
53cb501a | 4 | #include <net/if.h> |
edda10f2 | 5 | #include <linux/fou.h> |
9aa5d8ba | 6 | #include <netinet/in.h> |
53cb501a SS |
7 | #include <linux/ip.h> |
8 | ||
9 | #include "conf-parser.h" | |
737f1405 | 10 | #include "fou-tunnel.h" |
7df5c6ba | 11 | #include "ip-protocol-list.h" |
881c74bd | 12 | #include "netlink-util.h" |
53cb501a SS |
13 | #include "networkd-manager.h" |
14 | #include "parse-util.h" | |
53cb501a SS |
15 | #include "string-table.h" |
16 | #include "string-util.h" | |
53cb501a SS |
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); | |
e3bb989b ZJS |
24 | DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType, |
25 | "Failed to parse Encapsulation="); | |
53cb501a | 26 | |
2f64865b | 27 | static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message *m) { |
117843fe | 28 | FouTunnel *t = FOU(netdev); |
3abf950f | 29 | uint8_t encap_type; |
53cb501a SS |
30 | int r; |
31 | ||
53cb501a SS |
32 | r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port)); |
33 | if (r < 0) | |
2f64865b | 34 | return r; |
53cb501a | 35 | |
8f02c9b0 SS |
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) | |
2f64865b | 39 | return r; |
8f02c9b0 SS |
40 | } |
41 | ||
3abf950f YW |
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: | |
04499a70 | 50 | assert_not_reached(); |
3abf950f YW |
51 | } |
52 | ||
53 | r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type); | |
53cb501a | 54 | if (r < 0) |
2f64865b | 55 | return r; |
53cb501a SS |
56 | |
57 | r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET); | |
58 | if (r < 0) | |
2f64865b | 59 | return r; |
53cb501a SS |
60 | |
61 | r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol); | |
62 | if (r < 0) | |
2f64865b | 63 | return r; |
53cb501a | 64 | |
4502a61c SS |
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) | |
2f64865b | 68 | return r; |
8f02c9b0 | 69 | } else if (t->local_family == AF_INET6) { |
4502a61c SS |
70 | r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_LOCAL_V6, &t->local.in6); |
71 | if (r < 0) | |
2f64865b | 72 | return r; |
4502a61c SS |
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) | |
2f64865b | 78 | return r; |
8f02c9b0 | 79 | } else if (t->peer_family == AF_INET6){ |
4502a61c SS |
80 | r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_PEER_V6, &t->peer.in6); |
81 | if (r < 0) | |
2f64865b | 82 | return r; |
4502a61c SS |
83 | } |
84 | ||
2f64865b ZJS |
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 | ||
3abf950f | 102 | *ret = TAKE_PTR(m); |
53cb501a SS |
103 | return 0; |
104 | } | |
105 | ||
881c74bd YW |
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"); | |
8f65304c | 117 | netdev_enter_failed(netdev); |
881c74bd YW |
118 | |
119 | return 1; | |
120 | } | |
121 | ||
122 | log_netdev_debug(netdev, "FooOverUDP tunnel is created"); | |
123 | return 1; | |
124 | } | |
125 | ||
53cb501a | 126 | static int netdev_fou_tunnel_create(NetDev *netdev) { |
7dd05e94 | 127 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
53cb501a SS |
128 | int r; |
129 | ||
10c353e1 | 130 | assert(FOU(netdev)); |
53cb501a | 131 | |
2f64865b | 132 | r = netdev_create_fou_tunnel_message(netdev, &m); |
53cb501a SS |
133 | if (r < 0) |
134 | return r; | |
135 | ||
881c74bd YW |
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"); | |
53cb501a | 140 | |
881c74bd | 141 | netdev_ref(netdev); |
53cb501a SS |
142 | return 0; |
143 | } | |
144 | ||
7df5c6ba YW |
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 | ||
7df5c6ba YW |
157 | assert(filename); |
158 | assert(section); | |
159 | assert(lvalue); | |
160 | assert(rvalue); | |
7df5c6ba | 161 | |
a893c121 ZJS |
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); | |
7df5c6ba YW |
170 | return 0; |
171 | } | |
172 | ||
a893c121 ZJS |
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); | |
3d58d732 ZJS |
179 | return 0; |
180 | } | |
181 | ||
a893c121 | 182 | *proto = r; |
7df5c6ba YW |
183 | return 0; |
184 | } | |
185 | ||
4502a61c SS |
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 | ||
99534007 | 198 | union in_addr_union *addr = ASSERT_PTR(data); |
4502a61c SS |
199 | FouTunnel *t = userdata; |
200 | int r, *f; | |
201 | ||
202 | assert(filename); | |
203 | assert(lvalue); | |
204 | assert(rvalue); | |
4502a61c SS |
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) | |
d96edb2c | 213 | log_syntax(unit, LOG_WARNING, filename, line, r, |
fac21663 | 214 | "FooOverUDP tunnel '%s' address is invalid, ignoring assignment: %s", |
4502a61c SS |
215 | lvalue, rvalue); |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
53cb501a | 220 | static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) { |
53cb501a SS |
221 | assert(filename); |
222 | ||
117843fe | 223 | FouTunnel *t = FOU(netdev); |
53cb501a | 224 | |
3abf950f YW |
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: | |
04499a70 | 239 | assert_not_reached(); |
53cb501a SS |
240 | } |
241 | ||
8f02c9b0 SS |
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); | |
53cb501a SS |
250 | return 0; |
251 | } | |
252 | ||
253 | static void fou_tunnel_init(NetDev *netdev) { | |
117843fe | 254 | FouTunnel *t = FOU(netdev); |
53cb501a SS |
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, | |
130b812f | 262 | .sections = NETDEV_COMMON_SECTIONS "FooOverUDP\0", |
53cb501a SS |
263 | .create = netdev_fou_tunnel_create, |
264 | .create_type = NETDEV_CREATE_INDEPENDENT, | |
265 | .config_verify = netdev_fou_tunnel_verify, | |
266 | }; |