]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/socket-netlink.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "errno-util.h"
10 #include "extract-word.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"
19 int resolve_ifname(sd_netlink
**rtnl
, const char *name
) {
22 /* Like if_nametoindex, but resolves "alternative names" too. */
26 r
= if_nametoindex(name
);
30 return rtnl_resolve_link_alternative_name(rtnl
, name
);
33 int resolve_interface(sd_netlink
**rtnl
, const char *name
) {
36 /* Like resolve_ifname, but resolves interface numbers too. */
40 r
= parse_ifindex(name
);
45 return resolve_ifname(rtnl
, name
);
48 int resolve_interface_or_warn(sd_netlink
**rtnl
, const char *name
) {
51 r
= resolve_interface(rtnl
, name
);
53 return log_error_errno(r
, "Failed to resolve interface \"%s\": %m", name
);
57 int socket_address_parse(SocketAddress
*a
, const char *s
) {
58 _cleanup_free_
char *n
= NULL
;
65 *a
= (SocketAddress
) {
72 /* IPv6 in [x:.....:z]:p notation */
78 n
= strndup(s
+1, e
-s
-1);
83 if (inet_pton(AF_INET6
, n
, &a
->sockaddr
.in6
.sin6_addr
) <= 0)
84 return errno_or_else(EINVAL
);
91 r
= parse_ip_port(e
, &port
);
95 a
->sockaddr
.in6
.sin6_family
= AF_INET6
;
96 a
->sockaddr
.in6
.sin6_port
= htobe16(port
);
97 a
->size
= sizeof(struct sockaddr_in6
);
99 } else if (*s
== '/') {
105 if (l
>= sizeof(a
->sockaddr
.un
.sun_path
)) /* Note that we refuse non-NUL-terminated sockets when
106 * parsing (the kernel itself is less strict here in what it
110 a
->sockaddr
.un
.sun_family
= AF_UNIX
;
111 memcpy(a
->sockaddr
.un
.sun_path
, s
, l
);
112 a
->size
= offsetof(struct sockaddr_un
, sun_path
) + l
+ 1;
114 } else if (*s
== '@') {
115 /* Abstract AF_UNIX socket */
119 if (l
>= sizeof(a
->sockaddr
.un
.sun_path
) - 1) /* Note that we refuse non-NUL-terminated sockets here
120 * when parsing, even though abstract namespace sockets
121 * explicitly allow embedded NUL bytes and don't consider
122 * them special. But it's simply annoying to debug such
126 a
->sockaddr
.un
.sun_family
= AF_UNIX
;
127 memcpy(a
->sockaddr
.un
.sun_path
+1, s
+1, l
);
128 a
->size
= offsetof(struct sockaddr_un
, sun_path
) + 1 + l
;
130 } else if (startswith(s
, "vsock:")) {
131 /* AF_VSOCK socket in vsock:cid:port notation */
132 const char *cid_start
= s
+ STRLEN("vsock:");
135 e
= strchr(cid_start
, ':');
139 r
= safe_atou(e
+1, &port
);
143 n
= strndup(cid_start
, e
- cid_start
);
148 r
= safe_atou(n
, &a
->sockaddr
.vm
.svm_cid
);
152 a
->sockaddr
.vm
.svm_cid
= VMADDR_CID_ANY
;
154 a
->sockaddr
.vm
.svm_family
= AF_VSOCK
;
155 a
->sockaddr
.vm
.svm_port
= port
;
156 a
->size
= sizeof(struct sockaddr_vm
);
163 r
= parse_ip_port(e
+ 1, &port
);
171 /* IPv4 in w.x.y.z:p notation? */
172 r
= inet_pton(AF_INET
, n
, &a
->sockaddr
.in
.sin_addr
);
177 /* Gotcha, it's a traditional IPv4 address */
178 a
->sockaddr
.in
.sin_family
= AF_INET
;
179 a
->sockaddr
.in
.sin_port
= htobe16(port
);
180 a
->size
= sizeof(struct sockaddr_in
);
184 /* Uh, our last resort, an interface name */
185 idx
= resolve_ifname(NULL
, n
);
189 a
->sockaddr
.in6
.sin6_family
= AF_INET6
;
190 a
->sockaddr
.in6
.sin6_port
= htobe16(port
);
191 a
->sockaddr
.in6
.sin6_scope_id
= idx
;
192 a
->sockaddr
.in6
.sin6_addr
= in6addr_any
;
193 a
->size
= sizeof(struct sockaddr_in6
);
198 r
= parse_ip_port(s
, &port
);
202 if (socket_ipv6_is_supported()) {
203 a
->sockaddr
.in6
.sin6_family
= AF_INET6
;
204 a
->sockaddr
.in6
.sin6_port
= htobe16(port
);
205 a
->sockaddr
.in6
.sin6_addr
= in6addr_any
;
206 a
->size
= sizeof(struct sockaddr_in6
);
208 a
->sockaddr
.in
.sin_family
= AF_INET
;
209 a
->sockaddr
.in
.sin_port
= htobe16(port
);
210 a
->sockaddr
.in
.sin_addr
.s_addr
= INADDR_ANY
;
211 a
->size
= sizeof(struct sockaddr_in
);
219 int socket_address_parse_and_warn(SocketAddress
*a
, const char *s
) {
223 /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
225 r
= socket_address_parse(&b
, s
);
229 if (!socket_ipv6_is_supported() && b
.sockaddr
.sa
.sa_family
== AF_INET6
) {
230 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
231 return -EAFNOSUPPORT
;
238 int socket_address_parse_netlink(SocketAddress
*a
, const char *s
) {
239 _cleanup_free_
char *word
= NULL
;
246 *a
= (SocketAddress
) {
250 r
= extract_first_word(&s
, &word
, NULL
, 0);
256 family
= netlink_family_from_string(word
);
261 r
= safe_atou(s
, &group
);
266 a
->sockaddr
.nl
.nl_family
= AF_NETLINK
;
267 a
->sockaddr
.nl
.nl_groups
= group
;
270 a
->size
= sizeof(struct sockaddr_nl
);
271 a
->protocol
= family
;
276 bool socket_address_is(const SocketAddress
*a
, const char *s
, int type
) {
277 struct SocketAddress b
;
282 if (socket_address_parse(&b
, s
) < 0)
287 return socket_address_equal(a
, &b
);
290 bool socket_address_is_netlink(const SocketAddress
*a
, const char *s
) {
291 struct SocketAddress b
;
296 if (socket_address_parse_netlink(&b
, s
) < 0)
299 return socket_address_equal(a
, &b
);
302 int make_socket_fd(int log_level
, const char* address
, int type
, int flags
) {
306 r
= socket_address_parse(&a
, address
);
308 return log_error_errno(r
, "Failed to parse socket address \"%s\": %m", address
);
312 fd
= socket_address_listen(&a
, type
| flags
, SOMAXCONN
, SOCKET_ADDRESS_DEFAULT
,
313 NULL
, false, false, false, 0755, 0644, NULL
);
314 if (fd
< 0 || log_get_max_level() >= log_level
) {
315 _cleanup_free_
char *p
= NULL
;
317 r
= socket_address_print(&a
, &p
);
319 return log_error_errno(r
, "socket_address_print(): %m");
322 log_error_errno(fd
, "Failed to listen on %s: %m", p
);
324 log_full(log_level
, "Listening on %s", p
);
330 int in_addr_port_ifindex_name_from_string_auto(
333 union in_addr_union
*ret_address
,
336 char **ret_server_name
) {
338 _cleanup_free_
char *buf1
= NULL
, *buf2
= NULL
, *name
= NULL
;
339 int family
, ifindex
= 0, r
;
340 union in_addr_union a
;
346 /* This accepts the following:
347 * 192.168.0.1:53#example.com
348 * [2001:4860:4860::8888]:53%eth0#example.com */
350 /* if ret_port is NULL, then strings with port cannot be specified.
351 * Also, if ret_server_name is NULL, then server_name cannot be specified. */
355 if (!ret_server_name
)
361 name
= strdup(m
+ 1);
365 s
= buf1
= strndup(s
, m
- s
);
376 /* If we shall return the interface index, try to parse it */
377 ifindex
= resolve_interface(NULL
, m
+ 1);
382 s
= buf2
= strndup(s
, m
- s
);
390 _cleanup_free_
char *ip_str
= NULL
;
400 r
= parse_ip_port(m
+ 1, &port
);
404 ip_str
= strndup(s
+ 1, m
- s
- 2);
408 r
= in_addr_from_string(family
, ip_str
, &a
);
412 /* First try to parse the string as IPv6 address without port number */
413 r
= in_addr_from_string(AF_INET6
, s
, &a
);
415 /* Then the input should be IPv4 address with port number */
416 _cleanup_free_
char *ip_str
= NULL
;
423 ip_str
= strndup(s
, m
- s
);
427 r
= in_addr_from_string(family
, ip_str
, &a
);
431 r
= parse_ip_port(m
+ 1, &port
);
439 r
= in_addr_from_string(family
, s
, &a
);
445 *ret_family
= family
;
451 *ret_ifindex
= ifindex
;
453 *ret_server_name
= TAKE_PTR(name
);
458 struct in_addr_full
*in_addr_full_free(struct in_addr_full
*a
) {
462 free(a
->server_name
);
463 free(a
->cached_server_string
);
467 int in_addr_full_new(
469 const union in_addr_union
*a
,
472 const char *server_name
,
473 struct in_addr_full
**ret
) {
475 _cleanup_free_
char *name
= NULL
;
476 struct in_addr_full
*x
;
480 if (!isempty(server_name
)) {
481 name
= strdup(server_name
);
486 x
= new(struct in_addr_full
, 1);
490 *x
= (struct in_addr_full
) {
495 .server_name
= TAKE_PTR(name
),
502 int in_addr_full_new_from_string(const char *s
, struct in_addr_full
**ret
) {
503 _cleanup_free_
char *server_name
= NULL
;
504 int family
, ifindex
, r
;
505 union in_addr_union a
;
510 r
= in_addr_port_ifindex_name_from_string_auto(s
, &family
, &a
, &port
, &ifindex
, &server_name
);
514 return in_addr_full_new(family
, &a
, port
, ifindex
, server_name
, ret
);
517 const char *in_addr_full_to_string(struct in_addr_full
*a
) {
520 if (!a
->cached_server_string
)
521 (void) in_addr_port_ifindex_name_to_string(
527 &a
->cached_server_string
);
529 return a
->cached_server_string
;