2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "sd-netlink.h"
22 #include "alloc-util.h"
24 #include "firewall-util.h"
25 #include "in-addr-util.h"
26 #include "local-addresses.h"
27 #include "netlink-util.h"
28 #include "nspawn-expose-ports.h"
29 #include "parse-util.h"
30 #include "socket-util.h"
31 #include "string-util.h"
34 int expose_port_parse(ExposePort
**l
, const char *s
) {
36 const char *split
, *e
;
37 uint16_t container_port
, host_port
;
45 if ((e
= startswith(s
, "tcp:")))
46 protocol
= IPPROTO_TCP
;
47 else if ((e
= startswith(s
, "udp:")))
48 protocol
= IPPROTO_UDP
;
51 protocol
= IPPROTO_TCP
;
54 split
= strchr(e
, ':');
56 char v
[split
- e
+ 1];
58 memcpy(v
, e
, split
- e
);
61 r
= parse_ip_port(v
, &host_port
);
65 r
= parse_ip_port(split
+ 1, &container_port
);
67 r
= parse_ip_port(e
, &container_port
);
68 host_port
= container_port
;
74 LIST_FOREACH(ports
, p
, *l
)
75 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
78 p
= new(ExposePort
, 1);
82 p
->protocol
= protocol
;
83 p
->host_port
= host_port
;
84 p
->container_port
= container_port
;
86 LIST_PREPEND(ports
, *l
, p
);
91 void expose_port_free_all(ExposePort
*p
) {
95 LIST_REMOVE(ports
, p
, q
);
100 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
109 if (in_addr_is_null(af
, exposed
))
112 log_debug("Lost IP address.");
114 LIST_FOREACH(ports
, p
, l
) {
115 r
= fw_add_local_dnat(false,
126 log_warning_errno(r
, "Failed to modify firewall: %m");
129 *exposed
= IN_ADDR_NULL
;
133 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
134 _cleanup_free_
struct local_address
*addresses
= NULL
;
135 _cleanup_free_
char *pretty
= NULL
;
136 union in_addr_union new_exposed
;
143 /* Invoked each time an address is added or removed inside the
149 r
= local_addresses(rtnl
, 0, af
, &addresses
);
151 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
154 addresses
[0].family
== af
&&
155 addresses
[0].scope
< RT_SCOPE_LINK
;
158 return expose_port_flush(l
, exposed
);
160 new_exposed
= addresses
[0].address
;
161 if (in_addr_equal(af
, exposed
, &new_exposed
))
164 in_addr_to_string(af
, &new_exposed
, &pretty
);
165 log_debug("New container IP is %s.", strna(pretty
));
167 LIST_FOREACH(ports
, p
, l
) {
169 r
= fw_add_local_dnat(true,
178 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
180 log_warning_errno(r
, "Failed to modify firewall: %m");
183 *exposed
= new_exposed
;
187 int expose_port_send_rtnl(int send_fd
) {
188 _cleanup_close_
int fd
= -1;
191 assert(send_fd
>= 0);
193 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
195 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
197 /* Store away the fd in the socket, so that it stays open as
198 * long as we run the child */
199 r
= send_one_fd(send_fd
, fd
, 0);
201 return log_error_errno(r
, "Failed to send netlink fd: %m");
206 int expose_port_watch_rtnl(
209 sd_netlink_message_handler_t handler
,
210 union in_addr_union
*exposed
,
212 _cleanup_(sd_netlink_unrefp
) sd_netlink
*rtnl
= NULL
;
216 assert(recv_fd
>= 0);
219 fd
= receive_one_fd(recv_fd
, 0);
221 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
223 r
= sd_netlink_open_fd(&rtnl
, fd
);
226 return log_error_errno(r
, "Failed to create rtnl object: %m");
229 r
= sd_netlink_add_match(rtnl
, RTM_NEWADDR
, handler
, exposed
);
231 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
233 r
= sd_netlink_add_match(rtnl
, RTM_DELADDR
, handler
, exposed
);
235 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
237 r
= sd_netlink_attach_event(rtnl
, event
, 0);
239 return log_error_errno(r
, "Failed to add to even loop: %m");