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 "in-addr-util.h"
26 #include "firewall-util.h"
27 #include "local-addresses.h"
28 #include "netlink-util.h"
30 #include "nspawn-expose-ports.h"
32 int expose_port_parse(ExposePort
**l
, const char *s
) {
34 const char *split
, *e
;
35 uint16_t container_port
, host_port
;
43 if ((e
= startswith(s
, "tcp:")))
44 protocol
= IPPROTO_TCP
;
45 else if ((e
= startswith(s
, "udp:")))
46 protocol
= IPPROTO_UDP
;
49 protocol
= IPPROTO_TCP
;
52 split
= strchr(e
, ':');
54 char v
[split
- e
+ 1];
56 memcpy(v
, e
, split
- e
);
59 r
= safe_atou16(v
, &host_port
);
60 if (r
< 0 || host_port
<= 0)
63 r
= safe_atou16(split
+ 1, &container_port
);
65 r
= safe_atou16(e
, &container_port
);
66 host_port
= container_port
;
69 if (r
< 0 || container_port
<= 0)
72 LIST_FOREACH(ports
, p
, *l
)
73 if (p
->protocol
== protocol
&& p
->host_port
== host_port
)
76 p
= new(ExposePort
, 1);
80 p
->protocol
= protocol
;
81 p
->host_port
= host_port
;
82 p
->container_port
= container_port
;
84 LIST_PREPEND(ports
, *l
, p
);
89 void expose_port_free_all(ExposePort
*p
) {
93 LIST_REMOVE(ports
, p
, q
);
98 int expose_port_flush(ExposePort
* l
, union in_addr_union
*exposed
) {
107 if (in_addr_is_null(af
, exposed
))
110 log_debug("Lost IP address.");
112 LIST_FOREACH(ports
, p
, l
) {
113 r
= fw_add_local_dnat(false,
124 log_warning_errno(r
, "Failed to modify firewall: %m");
127 *exposed
= IN_ADDR_NULL
;
131 int expose_port_execute(sd_netlink
*rtnl
, ExposePort
*l
, union in_addr_union
*exposed
) {
132 _cleanup_free_
struct local_address
*addresses
= NULL
;
133 _cleanup_free_
char *pretty
= NULL
;
134 union in_addr_union new_exposed
;
141 /* Invoked each time an address is added or removed inside the
147 r
= local_addresses(rtnl
, 0, af
, &addresses
);
149 return log_error_errno(r
, "Failed to enumerate local addresses: %m");
152 addresses
[0].family
== af
&&
153 addresses
[0].scope
< RT_SCOPE_LINK
;
156 return expose_port_flush(l
, exposed
);
158 new_exposed
= addresses
[0].address
;
159 if (in_addr_equal(af
, exposed
, &new_exposed
))
162 in_addr_to_string(af
, &new_exposed
, &pretty
);
163 log_debug("New container IP is %s.", strna(pretty
));
165 LIST_FOREACH(ports
, p
, l
) {
167 r
= fw_add_local_dnat(true,
176 in_addr_is_null(af
, exposed
) ? NULL
: exposed
);
178 log_warning_errno(r
, "Failed to modify firewall: %m");
181 *exposed
= new_exposed
;
185 int expose_port_send_rtnl(int send_fd
) {
187 struct cmsghdr cmsghdr
;
188 uint8_t buf
[CMSG_SPACE(sizeof(int))];
191 .msg_control
= &control
,
192 .msg_controllen
= sizeof(control
),
194 struct cmsghdr
*cmsg
;
195 _cleanup_close_
int fd
= -1;
198 assert(send_fd
>= 0);
200 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_ROUTE
);
202 return log_error_errno(errno
, "Failed to allocate container netlink: %m");
204 cmsg
= CMSG_FIRSTHDR(&mh
);
205 cmsg
->cmsg_level
= SOL_SOCKET
;
206 cmsg
->cmsg_type
= SCM_RIGHTS
;
207 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
208 memcpy(CMSG_DATA(cmsg
), &fd
, sizeof(int));
210 mh
.msg_controllen
= cmsg
->cmsg_len
;
212 /* Store away the fd in the socket, so that it stays open as
213 * long as we run the child */
214 k
= sendmsg(send_fd
, &mh
, MSG_NOSIGNAL
);
216 return log_error_errno(errno
, "Failed to send netlink fd: %m");
221 int expose_port_watch_rtnl(
224 sd_netlink_message_handler_t handler
,
225 union in_addr_union
*exposed
,
229 struct cmsghdr cmsghdr
;
230 uint8_t buf
[CMSG_SPACE(sizeof(int))];
233 .msg_control
= &control
,
234 .msg_controllen
= sizeof(control
),
236 struct cmsghdr
*cmsg
;
237 _cleanup_netlink_unref_ sd_netlink
*rtnl
= NULL
;
242 assert(recv_fd
>= 0);
245 k
= recvmsg(recv_fd
, &mh
, MSG_NOSIGNAL
);
247 return log_error_errno(errno
, "Failed to recv netlink fd: %m");
249 cmsg
= CMSG_FIRSTHDR(&mh
);
250 assert(cmsg
->cmsg_level
== SOL_SOCKET
);
251 assert(cmsg
->cmsg_type
== SCM_RIGHTS
);
252 assert(cmsg
->cmsg_len
== CMSG_LEN(sizeof(int)));
253 memcpy(&fd
, CMSG_DATA(cmsg
), sizeof(int));
255 r
= sd_netlink_open_fd(&rtnl
, fd
);
258 return log_error_errno(r
, "Failed to create rtnl object: %m");
261 r
= sd_netlink_add_match(rtnl
, RTM_NEWADDR
, handler
, exposed
);
263 return log_error_errno(r
, "Failed to subscribe to RTM_NEWADDR messages: %m");
265 r
= sd_netlink_add_match(rtnl
, RTM_DELADDR
, handler
, exposed
);
267 return log_error_errno(r
, "Failed to subscribe to RTM_DELADDR messages: %m");
269 r
= sd_netlink_attach_event(rtnl
, event
, 0);
271 return log_error_errno(r
, "Failed to add to even loop: %m");