1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "sd-netlink.h"
25 #include "firewall-util.h"
26 #include "in-addr-util.h"
27 #include "local-addresses.h"
28 #include "netlink-util.h"
29 #include "nspawn-expose-ports.h"
30 #include "parse-util.h"
31 #include "socket-util.h"
32 #include "string-util.h"
35 int expose_port_parse(ExposePort
**l
, const char *s
) {
37 const char *split
, *e
;
38 uint16_t container_port
, host_port
;
46 if ((e
= startswith(s
, "tcp:")))
47 protocol
= IPPROTO_TCP
;
48 else if ((e
= startswith(s
, "udp:")))
49 protocol
= IPPROTO_UDP
;
52 protocol
= IPPROTO_TCP
;
55 split
= strchr(e
, ':');
57 char v
[split
- e
+ 1];
59 memcpy(v
, e
, split
- e
);
62 r
= safe_atou16(v
, &host_port
);
63 if (r
< 0 || host_port
<= 0)
66 r
= safe_atou16(split
+ 1, &container_port
);
68 r
= safe_atou16(e
, &container_port
);
69 host_port
= container_port
;
72 if (r
< 0 || container_port
<= 0)
75 LIST_FOREACH(ports
, p
, *l
)
76 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
79 p
= new(ExposePort
, 1);
83 p
->protocol
= protocol
;
84 p
->host_port
= host_port
;
85 p
->container_port
= container_port
;
87 LIST_PREPEND(ports
, *l
, p
);
92 void expose_port_free_all(ExposePort
*p
) {
96 LIST_REMOVE(ports
, p
, q
);
101 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
110 if (in_addr_is_null(af
, exposed
))
113 log_debug("Lost IP address.");
115 LIST_FOREACH(ports
, p
, l
) {
116 r
= fw_add_local_dnat(false,
127 log_warning_errno(r
, "Failed to modify firewall: %m");
130 *exposed
= IN_ADDR_NULL
;
134 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
135 _cleanup_free_
struct local_address
*addresses
= NULL
;
136 _cleanup_free_
char *pretty
= NULL
;
137 union in_addr_union new_exposed
;
144 /* Invoked each time an address is added or removed inside the
150 r
= local_addresses(rtnl
, 0, af
, &addresses
);
152 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
155 addresses
[0].family
== af
&&
156 addresses
[0].scope
< RT_SCOPE_LINK
;
159 return expose_port_flush(l
, exposed
);
161 new_exposed
= addresses
[0].address
;
162 if (in_addr_equal(af
, exposed
, &new_exposed
))
165 in_addr_to_string(af
, &new_exposed
, &pretty
);
166 log_debug("New container IP is %s.", strna(pretty
));
168 LIST_FOREACH(ports
, p
, l
) {
170 r
= fw_add_local_dnat(true,
179 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
181 log_warning_errno(r
, "Failed to modify firewall: %m");
184 *exposed
= new_exposed
;
188 int expose_port_send_rtnl(int send_fd
) {
189 _cleanup_close_
int fd
= -1;
192 assert(send_fd
>= 0);
194 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
196 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
198 /* Store away the fd in the socket, so that it stays open as
199 * long as we run the child */
200 r
= send_one_fd(send_fd
, fd
, 0);
202 return log_error_errno(r
, "Failed to send netlink fd: %m");
207 int expose_port_watch_rtnl(
210 sd_netlink_message_handler_t handler
,
211 union in_addr_union
*exposed
,
213 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
217 assert(recv_fd
>= 0);
220 fd
= receive_one_fd(recv_fd
, 0);
222 return log_error_errno(fd
, "Failed to recv netlink fd: %m");
224 r
= sd_netlink_open_fd(&rtnl
, fd
);
227 return log_error_errno(r
, "Failed to create rtnl object: %m");
230 r
= sd_netlink_add_match(rtnl
, RTM_NEWADDR
, handler
, exposed
);
232 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
234 r
= sd_netlink_add_match(rtnl
, RTM_DELADDR
, handler
, exposed
);
236 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
238 r
= sd_netlink_attach_event(rtnl
, event
, 0);
240 return log_error_errno(r
, "Failed to add to even loop: %m");