1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "sd-netlink.h"
6 #include "alloc-util.h"
8 #include "firewall-util.h"
9 #include "in-addr-util.h"
10 #include "local-addresses.h"
11 #include "netlink-util.h"
12 #include "nspawn-expose-ports.h"
13 #include "parse-util.h"
14 #include "socket-util.h"
15 #include "string-util.h"
17 int expose_port_parse(ExposePort
**l
, const char *s
) {
18 const char *split
, *e
;
19 uint16_t container_port
, host_port
;
27 if ((e
= startswith(s
, "tcp:")))
28 protocol
= IPPROTO_TCP
;
29 else if ((e
= startswith(s
, "udp:")))
30 protocol
= IPPROTO_UDP
;
33 protocol
= IPPROTO_TCP
;
36 split
= strchr(e
, ':');
38 char v
[split
- e
+ 1];
40 memcpy(v
, e
, split
- e
);
43 r
= parse_ip_port(v
, &host_port
);
47 r
= parse_ip_port(split
+ 1, &container_port
);
49 r
= parse_ip_port(e
, &container_port
);
50 host_port
= container_port
;
56 LIST_FOREACH(ports
, p
, *l
)
57 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
60 port
= new(ExposePort
, 1);
64 *port
= (ExposePort
) {
66 .host_port
= host_port
,
67 .container_port
= container_port
,
70 LIST_PREPEND(ports
, *l
, port
);
75 void expose_port_free_all(ExposePort
*p
) {
79 LIST_REMOVE(ports
, p
, q
);
84 int expose_port_flush(FirewallContext
**fw_ctx
, ExposePort
* l
, int af
, union in_addr_union
*exposed
) {
92 if (!in_addr_is_set(af
, exposed
))
95 log_debug("Lost IP address.");
97 LIST_FOREACH(ports
, p
, l
) {
98 r
= fw_add_local_dnat(fw_ctx
,
107 log_warning_errno(r
, "Failed to modify %s firewall: %m", af_to_name(af
));
110 *exposed
= IN_ADDR_NULL
;
114 int expose_port_execute(sd_netlink
*rtnl
, FirewallContext
**fw_ctx
, ExposePort
*l
, int af
, union in_addr_union
*exposed
) {
115 _cleanup_free_
struct local_address
*addresses
= NULL
;
116 union in_addr_union new_exposed
;
122 /* Invoked each time an address is added or removed inside the
128 r
= local_addresses(rtnl
, 0, af
, &addresses
);
130 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
133 addresses
[0].family
== af
&&
134 addresses
[0].scope
< RT_SCOPE_LINK
;
137 return expose_port_flush(fw_ctx
, l
, af
, exposed
);
139 new_exposed
= addresses
[0].address
;
140 if (in_addr_equal(af
, exposed
, &new_exposed
))
143 log_debug("New container IP is %s.", IN_ADDR_TO_STRING(af
, &new_exposed
));
145 LIST_FOREACH(ports
, p
, l
) {
146 r
= fw_add_local_dnat(fw_ctx
,
153 in_addr_is_set(af
, exposed
) ? exposed
: NULL
);
155 log_warning_errno(r
, "Failed to modify %s firewall: %m", af_to_name(af
));
158 *exposed
= new_exposed
;
162 int expose_port_send_rtnl(int send_fd
) {
163 _cleanup_close_
int fd
= -EBADF
;
166 assert(send_fd
>= 0);
168 fd
= socket(AF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
170 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
172 /* Store away the fd in the socket, so that it stays open as
173 * long as we run the child */
174 r
= send_one_fd(send_fd
, fd
, 0);
176 return log_error_errno(r
, "Failed to send netlink fd: %m");
181 int expose_port_watch_rtnl(
184 sd_netlink_message_handler_t handler
,
187 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
191 assert(recv_fd
>= 0);
194 fd
= receive_one_fd(recv_fd
, 0);
196 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
198 r
= sd_netlink_open_fd(&rtnl
, fd
);
201 return log_error_errno(r
, "Failed to create rtnl object: %m");
204 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_NEWADDR
, handler
, NULL
, userdata
, "nspawn-NEWADDR");
206 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
208 r
= sd_netlink_add_match(rtnl
, NULL
, RTM_DELADDR
, handler
, NULL
, userdata
, "nspawn-DELADDR");
210 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
212 r
= sd_netlink_attach_event(rtnl
, event
, 0);
214 return log_error_errno(r
, "Failed to add to event loop: %m");
216 *ret
= TAKE_PTR(rtnl
);