]>
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 if (IN_SET(*s
, '/', '@')) {
67 struct sockaddr_un un
;
69 r
= sockaddr_un_set_path(&un
, s
);
73 *a
= (SocketAddress
) {
78 } else if (startswith(s
, "vsock:")) {
79 /* AF_VSOCK socket in vsock:cid:port notation */
80 const char *cid_start
= s
+ STRLEN("vsock:");
83 e
= strchr(cid_start
, ':');
87 r
= safe_atou(e
+1, &port
);
91 n
= strndup(cid_start
, e
- cid_start
);
98 r
= safe_atou(n
, &cid
);
103 *a
= (SocketAddress
) {
106 .svm_family
= AF_VSOCK
,
109 .size
= sizeof(struct sockaddr_vm
),
115 r
= parse_ip_port(s
, &port
);
117 return r
; /* Valid port syntax, but the numerical value is wrong for a port. */
120 if (socket_ipv6_is_supported())
121 *a
= (SocketAddress
) {
123 .sin6_family
= AF_INET6
,
124 .sin6_port
= htobe16(port
),
125 .sin6_addr
= in6addr_any
,
127 .size
= sizeof(struct sockaddr_in6
),
130 *a
= (SocketAddress
) {
132 .sin_family
= AF_INET
,
133 .sin_port
= htobe16(port
),
134 .sin_addr
.s_addr
= INADDR_ANY
,
136 .size
= sizeof(struct sockaddr_in
),
140 union in_addr_union address
;
143 r
= in_addr_port_ifindex_name_from_string_auto(s
, &family
, &address
, &port
, &ifindex
, NULL
);
147 if (port
== 0) /* No port, no go. */
150 if (family
== AF_INET
)
151 *a
= (SocketAddress
) {
153 .sin_family
= AF_INET
,
154 .sin_addr
= address
.in
,
155 .sin_port
= htobe16(port
),
157 .size
= sizeof(struct sockaddr_in
),
159 else if (family
== AF_INET6
)
160 *a
= (SocketAddress
) {
162 .sin6_family
= AF_INET6
,
163 .sin6_addr
= address
.in6
,
164 .sin6_port
= htobe16(port
),
165 .sin6_scope_id
= ifindex
,
167 .size
= sizeof(struct sockaddr_in6
),
170 assert_not_reached("Family quarrel");
177 int socket_address_parse_and_warn(SocketAddress
*a
, const char *s
) {
181 /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
183 r
= socket_address_parse(&b
, s
);
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
;
196 int socket_address_parse_netlink(SocketAddress
*a
, const char *s
) {
197 _cleanup_free_
char *word
= NULL
;
204 r
= extract_first_word(&s
, &word
, NULL
, 0);
210 family
= netlink_family_from_string(word
);
215 r
= safe_atou(s
, &group
);
220 *a
= (SocketAddress
) {
222 .sockaddr
.nl
.nl_family
= AF_NETLINK
,
223 .sockaddr
.nl
.nl_groups
= group
,
225 .size
= sizeof(struct sockaddr_nl
),
231 bool socket_address_is(const SocketAddress
*a
, const char *s
, int type
) {
232 struct SocketAddress b
;
237 if (socket_address_parse(&b
, s
) < 0)
242 return socket_address_equal(a
, &b
);
245 bool socket_address_is_netlink(const SocketAddress
*a
, const char *s
) {
246 struct SocketAddress b
;
251 if (socket_address_parse_netlink(&b
, s
) < 0)
254 return socket_address_equal(a
, &b
);
257 int make_socket_fd(int log_level
, const char* address
, int type
, int flags
) {
261 r
= socket_address_parse(&a
, address
);
263 return log_error_errno(r
, "Failed to parse socket address \"%s\": %m", address
);
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
;
272 r
= socket_address_print(&a
, &p
);
274 return log_error_errno(r
, "socket_address_print(): %m");
277 log_error_errno(fd
, "Failed to listen on %s: %m", p
);
279 log_full(log_level
, "Listening on %s", p
);
285 int in_addr_port_ifindex_name_from_string_auto(
288 union in_addr_union
*ret_address
,
291 char **ret_server_name
) {
293 _cleanup_free_
char *buf1
= NULL
, *buf2
= NULL
, *name
= NULL
;
294 int family
, ifindex
= 0, r
;
295 union in_addr_union a
;
301 /* This accepts the following:
302 * 192.168.0.1:53#example.com
303 * [2001:4860:4860::8888]:53%eth0#example.com
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.
309 * ret_family is always AF_INET or AF_INET6.
314 if (!ret_server_name
)
320 name
= strdup(m
+ 1);
324 s
= buf1
= strndup(s
, m
- s
);
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. */
341 ifindex
= resolve_interface(NULL
, m
+ 1);
345 s
= buf2
= strndup(s
, m
- s
);
353 _cleanup_free_
char *ip_str
= NULL
;
363 r
= parse_ip_port(m
+ 1, &port
);
367 ip_str
= strndup(s
+ 1, m
- s
- 2);
371 r
= in_addr_from_string(family
, ip_str
, &a
);
375 /* First try to parse the string as IPv6 address without port number */
376 r
= in_addr_from_string(AF_INET6
, s
, &a
);
378 /* Then the input should be IPv4 address with port number */
379 _cleanup_free_
char *ip_str
= NULL
;
386 ip_str
= strndup(s
, m
- s
);
390 r
= in_addr_from_string(family
, ip_str
, &a
);
394 r
= parse_ip_port(m
+ 1, &port
);
402 r
= in_addr_from_string(family
, s
, &a
);
408 *ret_family
= family
;
414 *ret_ifindex
= ifindex
;
416 *ret_server_name
= TAKE_PTR(name
);
421 struct in_addr_full
*in_addr_full_free(struct in_addr_full
*a
) {
425 free(a
->server_name
);
426 free(a
->cached_server_string
);
430 int in_addr_full_new(
432 const union in_addr_union
*a
,
435 const char *server_name
,
436 struct in_addr_full
**ret
) {
438 _cleanup_free_
char *name
= NULL
;
439 struct in_addr_full
*x
;
443 if (!isempty(server_name
)) {
444 name
= strdup(server_name
);
449 x
= new(struct in_addr_full
, 1);
453 *x
= (struct in_addr_full
) {
458 .server_name
= TAKE_PTR(name
),
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
;
473 r
= in_addr_port_ifindex_name_from_string_auto(s
, &family
, &a
, &port
, &ifindex
, &server_name
);
477 return in_addr_full_new(family
, &a
, port
, ifindex
, server_name
, ret
);
480 const char *in_addr_full_to_string(struct in_addr_full
*a
) {
483 if (!a
->cached_server_string
)
484 (void) in_addr_port_ifindex_name_to_string(
490 &a
->cached_server_string
);
492 return a
->cached_server_string
;