1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2015 Lennart Poettering
6 #include "sd-netlink.h"
8 #include "alloc-util.h"
10 #include "firewall-util.h"
11 #include "in-addr-util.h"
12 #include "local-addresses.h"
13 #include "netlink-util.h"
14 #include "nspawn-expose-ports.h"
15 #include "parse-util.h"
16 #include "socket-util.h"
17 #include "string-util.h"
20 int expose_port_parse(ExposePort
**l
, const char *s
) {
22 const char *split
, *e
;
23 uint16_t container_port
, host_port
;
31 if ((e
= startswith(s
, "tcp:")))
32 protocol
= IPPROTO_TCP
;
33 else if ((e
= startswith(s
, "udp:")))
34 protocol
= IPPROTO_UDP
;
37 protocol
= IPPROTO_TCP
;
40 split
= strchr(e
, ':');
42 char v
[split
- e
+ 1];
44 memcpy(v
, e
, split
- e
);
47 r
= parse_ip_port(v
, &host_port
);
51 r
= parse_ip_port(split
+ 1, &container_port
);
53 r
= parse_ip_port(e
, &container_port
);
54 host_port
= container_port
;
60 LIST_FOREACH(ports
, p
, *l
)
61 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
64 p
= new(ExposePort
, 1);
68 p
->protocol
= protocol
;
69 p
->host_port
= host_port
;
70 p
->container_port
= container_port
;
72 LIST_PREPEND(ports
, *l
, p
);
77 void expose_port_free_all(ExposePort
*p
) {
81 LIST_REMOVE(ports
, p
, q
);
86 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
95 if (in_addr_is_null(af
, exposed
))
98 log_debug("Lost IP address.");
100 LIST_FOREACH(ports
, p
, l
) {
101 r
= fw_add_local_dnat(false,
112 log_warning_errno(r
, "Failed to modify firewall: %m");
115 *exposed
= IN_ADDR_NULL
;
119 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
120 _cleanup_free_
struct local_address
*addresses
= NULL
;
121 _cleanup_free_
char *pretty
= NULL
;
122 union in_addr_union new_exposed
;
129 /* Invoked each time an address is added or removed inside the
135 r
= local_addresses(rtnl
, 0, af
, &addresses
);
137 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
140 addresses
[0].family
== af
&&
141 addresses
[0].scope
< RT_SCOPE_LINK
;
144 return expose_port_flush(l
, exposed
);
146 new_exposed
= addresses
[0].address
;
147 if (in_addr_equal(af
, exposed
, &new_exposed
))
150 in_addr_to_string(af
, &new_exposed
, &pretty
);
151 log_debug("New container IP is %s.", strna(pretty
));
153 LIST_FOREACH(ports
, p
, l
) {
155 r
= fw_add_local_dnat(true,
164 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
166 log_warning_errno(r
, "Failed to modify firewall: %m");
169 *exposed
= new_exposed
;
173 int expose_port_send_rtnl(int send_fd
) {
174 _cleanup_close_
int fd
= -1;
177 assert(send_fd
>= 0);
179 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
181 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
183 /* Store away the fd in the socket, so that it stays open as
184 * long as we run the child */
185 r
= send_one_fd(send_fd
, fd
, 0);
187 return log_error_errno(r
, "Failed to send netlink fd: %m");
192 int expose_port_watch_rtnl(
195 sd_netlink_message_handler_t handler
,
196 union in_addr_union
*exposed
,
198 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
202 assert(recv_fd
>= 0);
205 fd
= receive_one_fd(recv_fd
, 0);
207 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
209 r
= sd_netlink_open_fd(&rtnl
, fd
);
212 return log_error_errno(r
, "Failed to create rtnl object: %m");
215 r
= sd_netlink_add_match(rtnl
, RTM_NEWADDR
, handler
, exposed
);
217 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
219 r
= sd_netlink_add_match(rtnl
, RTM_DELADDR
, handler
, exposed
);
221 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
223 r
= sd_netlink_attach_event(rtnl
, event
, 0);
225 return log_error_errno(r
, "Failed to add to even loop: %m");
227 *ret
= TAKE_PTR(rtnl
);