1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "sd-netlink.h"
5 #include "alloc-util.h"
7 #include "firewall-util.h"
8 #include "in-addr-util.h"
9 #include "local-addresses.h"
10 #include "netlink-util.h"
11 #include "nspawn-expose-ports.h"
12 #include "parse-util.h"
13 #include "socket-util.h"
14 #include "string-util.h"
17 int expose_port_parse(ExposePort
**l
, const char *s
) {
19 const char *split
, *e
;
20 uint16_t container_port
, host_port
;
28 if ((e
= startswith(s
, "tcp:")))
29 protocol
= IPPROTO_TCP
;
30 else if ((e
= startswith(s
, "udp:")))
31 protocol
= IPPROTO_UDP
;
34 protocol
= IPPROTO_TCP
;
37 split
= strchr(e
, ':');
39 char v
[split
- e
+ 1];
41 memcpy(v
, e
, split
- e
);
44 r
= parse_ip_port(v
, &host_port
);
48 r
= parse_ip_port(split
+ 1, &container_port
);
50 r
= parse_ip_port(e
, &container_port
);
51 host_port
= container_port
;
57 LIST_FOREACH(ports
, p
, *l
)
58 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
61 p
= new(ExposePort
, 1);
67 .host_port
= host_port
,
68 .container_port
= container_port
,
71 LIST_PREPEND(ports
, *l
, p
);
76 void expose_port_free_all(ExposePort
*p
) {
80 LIST_REMOVE(ports
, p
, q
);
85 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
94 if (in_addr_is_null(af
, exposed
))
97 log_debug("Lost IP address.");
99 LIST_FOREACH(ports
, p
, l
) {
100 r
= fw_add_local_dnat(false,
111 log_warning_errno(r
, "Failed to modify firewall: %m");
114 *exposed
= IN_ADDR_NULL
;
118 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
119 _cleanup_free_
struct local_address
*addresses
= NULL
;
120 union in_addr_union new_exposed
;
127 /* Invoked each time an address is added or removed inside the
133 r
= local_addresses(rtnl
, 0, af
, &addresses
);
135 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
138 addresses
[0].family
== af
&&
139 addresses
[0].scope
< RT_SCOPE_LINK
;
142 return expose_port_flush(l
, exposed
);
144 new_exposed
= addresses
[0].address
;
145 if (in_addr_equal(af
, exposed
, &new_exposed
))
149 _cleanup_free_
char *pretty
= NULL
;
150 in_addr_to_string(af
, &new_exposed
, &pretty
);
151 log_debug("New container IP is %s.", strna(pretty
));
154 LIST_FOREACH(ports
, p
, l
) {
156 r
= fw_add_local_dnat(true,
165 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
167 log_warning_errno(r
, "Failed to modify firewall: %m");
170 *exposed
= new_exposed
;
174 int expose_port_send_rtnl(int send_fd
) {
175 _cleanup_close_
int fd
= -1;
178 assert(send_fd
>= 0);
180 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
182 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
184 /* Store away the fd in the socket, so that it stays open as
185 * long as we run the child */
186 r
= send_one_fd(send_fd
, fd
, 0);
188 return log_error_errno(r
, "Failed to send netlink fd: %m");
193 int expose_port_watch_rtnl(
196 sd_netlink_message_handler_t handler
,
197 union in_addr_union
*exposed
,
199 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
203 assert(recv_fd
>= 0);
206 fd
= receive_one_fd(recv_fd
, 0);
208 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
210 r
= sd_netlink_open_fd(&rtnl
, fd
);
213 return log_error_errno(r
, "Failed to create rtnl object: %m");
216 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_NEWADDR
, handler
, NULL
, exposed
, "nspawn-NEWADDR");
218 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
220 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_DELADDR
, handler
, NULL
, exposed
, "nspawn-DELADDR");
222 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
224 r
= sd_netlink_attach_event(rtnl
, event
, 0);
226 return log_error_errno(r
, "Failed to add to event loop: %m");
228 *ret
= TAKE_PTR(rtnl
);