]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/socket-netlink.c
core: reduce scope of variants
[thirdparty/systemd.git] / src / shared / socket-netlink.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <arpa/inet.h>
4 #include <errno.h>
5 #include <net/if.h>
6 #include <string.h>
7
8 #include "alloc-util.h"
9 #include "errno-util.h"
10 #include "extract-word.h"
11 #include "log.h"
12 #include "memory-util.h"
13 #include "netlink-util.h"
14 #include "parse-util.h"
15 #include "socket-netlink.h"
16 #include "socket-util.h"
17 #include "string-util.h"
18
19 int resolve_ifname(sd_netlink **rtnl, const char *name) {
20 int r;
21
22 /* Like if_nametoindex, but resolves "alternative names" too. */
23
24 assert(name);
25
26 r = if_nametoindex(name);
27 if (r > 0)
28 return r;
29
30 return rtnl_resolve_link_alternative_name(rtnl, name);
31 }
32
33 int resolve_interface(sd_netlink **rtnl, const char *name) {
34 int r;
35
36 /* Like resolve_ifname, but resolves interface numbers too. */
37
38 assert(name);
39
40 r = parse_ifindex(name);
41 if (r > 0)
42 return r;
43 assert(r < 0);
44
45 return resolve_ifname(rtnl, name);
46 }
47
48 int resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
49 int r;
50
51 r = resolve_interface(rtnl, name);
52 if (r < 0)
53 return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
54 return r;
55 }
56
57 int socket_address_parse(SocketAddress *a, const char *s) {
58 _cleanup_free_ char *n = NULL;
59 char *e;
60 int r;
61
62 assert(a);
63 assert(s);
64
65 if (IN_SET(*s, '/', '@')) {
66 /* AF_UNIX socket */
67 struct sockaddr_un un;
68
69 r = sockaddr_un_set_path(&un, s);
70 if (r < 0)
71 return r;
72
73 *a = (SocketAddress) {
74 .sockaddr.un = un,
75 .size = r,
76 };
77
78 } else if (startswith(s, "vsock:")) {
79 /* AF_VSOCK socket in vsock:cid:port notation */
80 const char *cid_start = s + STRLEN("vsock:");
81 unsigned port, cid;
82
83 e = strchr(cid_start, ':');
84 if (!e)
85 return -EINVAL;
86
87 r = safe_atou(e+1, &port);
88 if (r < 0)
89 return r;
90
91 n = strndup(cid_start, e - cid_start);
92 if (!n)
93 return -ENOMEM;
94
95 if (isempty(n))
96 cid = VMADDR_CID_ANY;
97 else {
98 r = safe_atou(n, &cid);
99 if (r < 0)
100 return r;
101 }
102
103 *a = (SocketAddress) {
104 .sockaddr.vm = {
105 .svm_cid = cid,
106 .svm_family = AF_VSOCK,
107 .svm_port = port,
108 },
109 .size = sizeof(struct sockaddr_vm),
110 };
111
112 } else {
113 uint16_t port;
114
115 r = parse_ip_port(s, &port);
116 if (r == -ERANGE)
117 return r; /* Valid port syntax, but the numerical value is wrong for a port. */
118 if (r >= 0) {
119 /* Just a port */
120 if (socket_ipv6_is_supported())
121 *a = (SocketAddress) {
122 .sockaddr.in6 = {
123 .sin6_family = AF_INET6,
124 .sin6_port = htobe16(port),
125 .sin6_addr = in6addr_any,
126 },
127 .size = sizeof(struct sockaddr_in6),
128 };
129 else
130 *a = (SocketAddress) {
131 .sockaddr.in = {
132 .sin_family = AF_INET,
133 .sin_port = htobe16(port),
134 .sin_addr.s_addr = INADDR_ANY,
135 },
136 .size = sizeof(struct sockaddr_in),
137 };
138
139 } else {
140 union in_addr_union address;
141 int family, ifindex;
142
143 r = in_addr_port_ifindex_name_from_string_auto(s, &family, &address, &port, &ifindex, NULL);
144 if (r < 0)
145 return r;
146
147 if (port == 0) /* No port, no go. */
148 return -EINVAL;
149
150 if (family == AF_INET)
151 *a = (SocketAddress) {
152 .sockaddr.in = {
153 .sin_family = AF_INET,
154 .sin_addr = address.in,
155 .sin_port = htobe16(port),
156 },
157 .size = sizeof(struct sockaddr_in),
158 };
159 else if (family == AF_INET6)
160 *a = (SocketAddress) {
161 .sockaddr.in6 = {
162 .sin6_family = AF_INET6,
163 .sin6_addr = address.in6,
164 .sin6_port = htobe16(port),
165 .sin6_scope_id = ifindex,
166 },
167 .size = sizeof(struct sockaddr_in6),
168 };
169 else
170 assert_not_reached("Family quarrel");
171 }
172 }
173
174 return 0;
175 }
176
177 int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
178 SocketAddress b;
179 int r;
180
181 /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
182
183 r = socket_address_parse(&b, s);
184 if (r < 0)
185 return r;
186
187 if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
188 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
189 return -EAFNOSUPPORT;
190 }
191
192 *a = b;
193 return 0;
194 }
195
196 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
197 _cleanup_free_ char *word = NULL;
198 unsigned group = 0;
199 int family, r;
200
201 assert(a);
202 assert(s);
203
204 r = extract_first_word(&s, &word, NULL, 0);
205 if (r < 0)
206 return r;
207 if (r == 0)
208 return -EINVAL;
209
210 family = netlink_family_from_string(word);
211 if (family < 0)
212 return -EINVAL;
213
214 if (!isempty(s)) {
215 r = safe_atou(s, &group);
216 if (r < 0)
217 return r;
218 }
219
220 *a = (SocketAddress) {
221 .type = SOCK_RAW,
222 .sockaddr.nl.nl_family = AF_NETLINK,
223 .sockaddr.nl.nl_groups = group,
224 .protocol = family,
225 .size = sizeof(struct sockaddr_nl),
226 };
227
228 return 0;
229 }
230
231 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
232 struct SocketAddress b;
233
234 assert(a);
235 assert(s);
236
237 if (socket_address_parse(&b, s) < 0)
238 return false;
239
240 b.type = type;
241
242 return socket_address_equal(a, &b);
243 }
244
245 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
246 struct SocketAddress b;
247
248 assert(a);
249 assert(s);
250
251 if (socket_address_parse_netlink(&b, s) < 0)
252 return false;
253
254 return socket_address_equal(a, &b);
255 }
256
257 int make_socket_fd(int log_level, const char* address, int type, int flags) {
258 SocketAddress a;
259 int fd, r;
260
261 r = socket_address_parse(&a, address);
262 if (r < 0)
263 return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
264
265 a.type = type;
266
267 fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
268 NULL, false, false, false, 0755, 0644, NULL);
269 if (fd < 0 || log_get_max_level() >= log_level) {
270 _cleanup_free_ char *p = NULL;
271
272 r = socket_address_print(&a, &p);
273 if (r < 0)
274 return log_error_errno(r, "socket_address_print(): %m");
275
276 if (fd < 0)
277 log_error_errno(fd, "Failed to listen on %s: %m", p);
278 else
279 log_full(log_level, "Listening on %s", p);
280 }
281
282 return fd;
283 }
284
285 int in_addr_port_ifindex_name_from_string_auto(
286 const char *s,
287 int *ret_family,
288 union in_addr_union *ret_address,
289 uint16_t *ret_port,
290 int *ret_ifindex,
291 char **ret_server_name) {
292
293 _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL;
294 int family, ifindex = 0, r;
295 union in_addr_union a;
296 uint16_t port = 0;
297 const char *m;
298
299 assert(s);
300
301 /* This accepts the following:
302 * 192.168.0.1:53#example.com
303 * [2001:4860:4860::8888]:53%eth0#example.com
304 *
305 * If ret_port is NULL, then the port cannot be specified.
306 * If ret_ifindex is NULL, then the interface index cannot be specified.
307 * If ret_server_name is NULL, then server_name cannot be specified.
308 *
309 * ret_family is always AF_INET or AF_INET6.
310 */
311
312 m = strchr(s, '#');
313 if (m) {
314 if (!ret_server_name)
315 return -EINVAL;
316
317 if (isempty(m + 1))
318 return -EINVAL;
319
320 name = strdup(m + 1);
321 if (!name)
322 return -ENOMEM;
323
324 s = buf1 = strndup(s, m - s);
325 if (!buf1)
326 return -ENOMEM;
327 }
328
329 m = strchr(s, '%');
330 if (m) {
331 if (!ret_ifindex)
332 return -EINVAL;
333
334 if (isempty(m + 1))
335 return -EINVAL;
336
337 if (!ifname_valid_full(m + 1, IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC))
338 return -EINVAL; /* We want to return -EINVAL for syntactically invalid names,
339 * and -ENODEV for valid but nonexistent interfaces. */
340
341 ifindex = resolve_interface(NULL, m + 1);
342 if (ifindex < 0)
343 return ifindex;
344
345 s = buf2 = strndup(s, m - s);
346 if (!buf2)
347 return -ENOMEM;
348 }
349
350 m = strrchr(s, ':');
351 if (m) {
352 if (*s == '[') {
353 _cleanup_free_ char *ip_str = NULL;
354
355 if (!ret_port)
356 return -EINVAL;
357
358 if (*(m - 1) != ']')
359 return -EINVAL;
360
361 family = AF_INET6;
362
363 r = parse_ip_port(m + 1, &port);
364 if (r < 0)
365 return r;
366
367 ip_str = strndup(s + 1, m - s - 2);
368 if (!ip_str)
369 return -ENOMEM;
370
371 r = in_addr_from_string(family, ip_str, &a);
372 if (r < 0)
373 return r;
374 } else {
375 /* First try to parse the string as IPv6 address without port number */
376 r = in_addr_from_string(AF_INET6, s, &a);
377 if (r < 0) {
378 /* Then the input should be IPv4 address with port number */
379 _cleanup_free_ char *ip_str = NULL;
380
381 if (!ret_port)
382 return -EINVAL;
383
384 family = AF_INET;
385
386 ip_str = strndup(s, m - s);
387 if (!ip_str)
388 return -ENOMEM;
389
390 r = in_addr_from_string(family, ip_str, &a);
391 if (r < 0)
392 return r;
393
394 r = parse_ip_port(m + 1, &port);
395 if (r < 0)
396 return r;
397 } else
398 family = AF_INET6;
399 }
400 } else {
401 family = AF_INET;
402 r = in_addr_from_string(family, s, &a);
403 if (r < 0)
404 return r;
405 }
406
407 if (ret_family)
408 *ret_family = family;
409 if (ret_address)
410 *ret_address = a;
411 if (ret_port)
412 *ret_port = port;
413 if (ret_ifindex)
414 *ret_ifindex = ifindex;
415 if (ret_server_name)
416 *ret_server_name = TAKE_PTR(name);
417
418 return r;
419 }
420
421 struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
422 if (!a)
423 return NULL;
424
425 free(a->server_name);
426 free(a->cached_server_string);
427 return mfree(a);
428 }
429
430 int in_addr_full_new(
431 int family,
432 const union in_addr_union *a,
433 uint16_t port,
434 int ifindex,
435 const char *server_name,
436 struct in_addr_full **ret) {
437
438 _cleanup_free_ char *name = NULL;
439 struct in_addr_full *x;
440
441 assert(ret);
442
443 if (!isempty(server_name)) {
444 name = strdup(server_name);
445 if (!name)
446 return -ENOMEM;
447 }
448
449 x = new(struct in_addr_full, 1);
450 if (!x)
451 return -ENOMEM;
452
453 *x = (struct in_addr_full) {
454 .family = family,
455 .address = *a,
456 .port = port,
457 .ifindex = ifindex,
458 .server_name = TAKE_PTR(name),
459 };
460
461 *ret = x;
462 return 0;
463 }
464
465 int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) {
466 _cleanup_free_ char *server_name = NULL;
467 int family, ifindex, r;
468 union in_addr_union a;
469 uint16_t port;
470
471 assert(s);
472
473 r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name);
474 if (r < 0)
475 return r;
476
477 return in_addr_full_new(family, &a, port, ifindex, server_name, ret);
478 }
479
480 const char *in_addr_full_to_string(struct in_addr_full *a) {
481 assert(a);
482
483 if (!a->cached_server_string)
484 (void) in_addr_port_ifindex_name_to_string(
485 a->family,
486 &a->address,
487 a->port,
488 a->ifindex,
489 a->server_name,
490 &a->cached_server_string);
491
492 return a->cached_server_string;
493 }