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);
65 p
->protocol
= protocol
;
66 p
->host_port
= host_port
;
67 p
->container_port
= container_port
;
69 LIST_PREPEND(ports
, *l
, p
);
74 void expose_port_free_all(ExposePort
*p
) {
78 LIST_REMOVE(ports
, p
, q
);
83 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
92 if (in_addr_is_null(af
, exposed
))
95 log_debug("Lost IP address.");
97 LIST_FOREACH(ports
, p
, l
) {
98 r
= fw_add_local_dnat(false,
109 log_warning_errno(r
, "Failed to modify firewall: %m");
112 *exposed
= IN_ADDR_NULL
;
116 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
117 _cleanup_free_
struct local_address
*addresses
= NULL
;
118 _cleanup_free_
char *pretty
= NULL
;
119 union in_addr_union new_exposed
;
126 /* Invoked each time an address is added or removed inside the
132 r
= local_addresses(rtnl
, 0, af
, &addresses
);
134 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
137 addresses
[0].family
== af
&&
138 addresses
[0].scope
< RT_SCOPE_LINK
;
141 return expose_port_flush(l
, exposed
);
143 new_exposed
= addresses
[0].address
;
144 if (in_addr_equal(af
, exposed
, &new_exposed
))
147 in_addr_to_string(af
, &new_exposed
, &pretty
);
148 log_debug("New container IP is %s.", strna(pretty
));
150 LIST_FOREACH(ports
, p
, l
) {
152 r
= fw_add_local_dnat(true,
161 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
163 log_warning_errno(r
, "Failed to modify firewall: %m");
166 *exposed
= new_exposed
;
170 int expose_port_send_rtnl(int send_fd
) {
171 _cleanup_close_
int fd
= -1;
174 assert(send_fd
>= 0);
176 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
178 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
180 /* Store away the fd in the socket, so that it stays open as
181 * long as we run the child */
182 r
= send_one_fd(send_fd
, fd
, 0);
184 return log_error_errno(r
, "Failed to send netlink fd: %m");
189 int expose_port_watch_rtnl(
192 sd_netlink_message_handler_t handler
,
193 union in_addr_union
*exposed
,
195 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
199 assert(recv_fd
>= 0);
202 fd
= receive_one_fd(recv_fd
, 0);
204 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
206 r
= sd_netlink_open_fd(&rtnl
, fd
);
209 return log_error_errno(r
, "Failed to create rtnl object: %m");
212 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_NEWADDR
, handler
, NULL
, exposed
, "nspawn-NEWADDR");
214 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
216 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_DELADDR
, handler
, NULL
, exposed
, "nspawn-DELADDR");
218 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
220 r
= sd_netlink_attach_event(rtnl
, event
, 0);
222 return log_error_errno(r
, "Failed to add to event loop: %m");
224 *ret
= TAKE_PTR(rtnl
);