1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

nspawn: split all port exposure code into nspawn-expose-port.[ch]

This commit is contained in:
Lennart Poettering 2015-09-07 16:52:24 +02:00
parent e83bebeff7
commit 7a8f63251d
7 changed files with 339 additions and 271 deletions

View File

@ -2783,6 +2783,8 @@ systemd_nspawn_SOURCES = \
src/nspawn/nspawn-settings.h \
src/nspawn/nspawn-mount.c \
src/nspawn/nspawn-mount.h \
src/nspawn/nspawn-expose-ports.c \
src/nspawn/nspawn-expose-ports.h \
src/core/mount-setup.c \
src/core/mount-setup.h \
src/core/loopback-setup.c \

View File

@ -0,0 +1,278 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "sd-netlink.h"
#include "util.h"
#include "in-addr-util.h"
#include "firewall-util.h"
#include "local-addresses.h"
#include "netlink-util.h"
#include "nspawn.h"
#include "nspawn-expose-ports.h"
int expose_port_parse(ExposePort **l, const char *s) {
const char *split, *e;
uint16_t container_port, host_port;
int protocol;
ExposePort *p;
int r;
assert(l);
assert(s);
if ((e = startswith(s, "tcp:")))
protocol = IPPROTO_TCP;
else if ((e = startswith(s, "udp:")))
protocol = IPPROTO_UDP;
else {
e = s;
protocol = IPPROTO_TCP;
}
split = strchr(e, ':');
if (split) {
char v[split - e + 1];
memcpy(v, e, split - e);
v[split - e] = 0;
r = safe_atou16(v, &host_port);
if (r < 0 || host_port <= 0)
return -EINVAL;
r = safe_atou16(split + 1, &container_port);
} else {
r = safe_atou16(e, &container_port);
host_port = container_port;
}
if (r < 0 || container_port <= 0)
return -EINVAL;
LIST_FOREACH(ports, p, *l)
if (p->protocol == protocol && p->host_port == host_port)
return -EEXIST;
p = new(ExposePort, 1);
if (!p)
return -ENOMEM;
p->protocol = protocol;
p->host_port = host_port;
p->container_port = container_port;
LIST_PREPEND(ports, *l, p);
return 0;
}
void expose_port_free_all(ExposePort *p) {
while (p) {
ExposePort *q = p;
LIST_REMOVE(ports, p, q);
free(q);
}
}
int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
ExposePort *p;
int r, af = AF_INET;
assert(exposed);
if (!l)
return 0;
if (in_addr_is_null(af, exposed))
return 0;
log_debug("Lost IP address.");
LIST_FOREACH(ports, p, l) {
r = fw_add_local_dnat(false,
af,
p->protocol,
NULL,
NULL, 0,
NULL, 0,
p->host_port,
exposed,
p->container_port,
NULL);
if (r < 0)
log_warning_errno(r, "Failed to modify firewall: %m");
}
*exposed = IN_ADDR_NULL;
return 0;
}
int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
_cleanup_free_ struct local_address *addresses = NULL;
_cleanup_free_ char *pretty = NULL;
union in_addr_union new_exposed;
ExposePort *p;
bool add;
int af = AF_INET, r;
assert(exposed);
/* Invoked each time an address is added or removed inside the
* container */
if (!l)
return 0;
r = local_addresses(rtnl, 0, af, &addresses);
if (r < 0)
return log_error_errno(r, "Failed to enumerate local addresses: %m");
add = r > 0 &&
addresses[0].family == af &&
addresses[0].scope < RT_SCOPE_LINK;
if (!add)
return expose_port_flush(l, exposed);
new_exposed = addresses[0].address;
if (in_addr_equal(af, exposed, &new_exposed))
return 0;
in_addr_to_string(af, &new_exposed, &pretty);
log_debug("New container IP is %s.", strna(pretty));
LIST_FOREACH(ports, p, l) {
r = fw_add_local_dnat(true,
af,
p->protocol,
NULL,
NULL, 0,
NULL, 0,
p->host_port,
&new_exposed,
p->container_port,
in_addr_is_null(af, exposed) ? NULL : exposed);
if (r < 0)
log_warning_errno(r, "Failed to modify firewall: %m");
}
*exposed = new_exposed;
return 0;
}
int expose_port_send_rtnl(int send_fd) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
_cleanup_close_ int fd = -1;
ssize_t k;
assert(send_fd >= 0);
fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate container netlink: %m");
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
k = sendmsg(send_fd, &mh, MSG_NOSIGNAL);
if (k < 0)
return log_error_errno(errno, "Failed to send netlink fd: %m");
return 0;
}
int expose_port_watch_rtnl(
sd_event *event,
int recv_fd,
sd_netlink_message_handler_t handler,
union in_addr_union *exposed,
sd_netlink **ret) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
_cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
int fd, r;
ssize_t k;
assert(event);
assert(recv_fd >= 0);
assert(ret);
k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
if (k < 0)
return log_error_errno(errno, "Failed to recv netlink fd: %m");
cmsg = CMSG_FIRSTHDR(&mh);
assert(cmsg->cmsg_level == SOL_SOCKET);
assert(cmsg->cmsg_type == SCM_RIGHTS);
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
r = sd_netlink_open_fd(&rtnl, fd);
if (r < 0) {
safe_close(fd);
return log_error_errno(r, "Failed to create rtnl object: %m");
}
r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
r = sd_netlink_attach_event(rtnl, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to add to even loop: %m");
*ret = rtnl;
rtnl = NULL;
return 0;
}

View File

@ -0,0 +1,45 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include "sd-event.h"
#include "sd-netlink.h"
#include "list.h"
#include "in-addr-util.h"
typedef struct ExposePort {
int protocol;
uint16_t host_port;
uint16_t container_port;
LIST_FIELDS(struct ExposePort, ports);
} ExposePort;
void expose_port_free_all(ExposePort *p);
int expose_port_parse(ExposePort **l, const char *s);
int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret);
int expose_port_send_rtnl(int send_fd);
int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed);
int expose_port_flush(ExposePort* l, union in_addr_union *exposed);

View File

@ -3,6 +3,7 @@
#include "conf-parser.h"
#include "nspawn.h"
#include "nspawn-settings.h"
#include "nspawn-expose-ports.h"
%}
struct ConfigPerfItem;
%null_strings

View File

@ -26,6 +26,7 @@
#include "macro.h"
#include "nspawn.h"
#include "nspawn-mount.h"
#include "nspawn-expose-ports.h"
typedef enum SettingsMask {
SETTING_BOOT = 1 << 0,

View File

@ -55,7 +55,6 @@
#include "sd-daemon.h"
#include "sd-bus.h"
#include "sd-id128.h"
#include "sd-netlink.h"
#include "random-util.h"
#include "log.h"
#include "util.h"
@ -90,8 +89,6 @@
#include "machine-image.h"
#include "list.h"
#include "in-addr-util.h"
#include "firewall-util.h"
#include "local-addresses.h"
#include "formats-util.h"
#include "process-util.h"
#include "terminal-util.h"
@ -105,6 +102,7 @@
#include "nspawn.h"
#include "nspawn-settings.h"
#include "nspawn-mount.h"
#include "nspawn-expose-ports.h"
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
@ -336,60 +334,6 @@ static int detect_unified_cgroup_hierarchy(void) {
return 0;
}
int expose_port_parse(ExposePort **l, const char *s) {
const char *split, *e;
uint16_t container_port, host_port;
int protocol;
ExposePort *p;
int r;
if ((e = startswith(s, "tcp:")))
protocol = IPPROTO_TCP;
else if ((e = startswith(s, "udp:")))
protocol = IPPROTO_UDP;
else {
e = s;
protocol = IPPROTO_TCP;
}
split = strchr(e, ':');
if (split) {
char v[split - e + 1];
memcpy(v, e, split - e);
v[split - e] = 0;
r = safe_atou16(v, &host_port);
if (r < 0 || host_port <= 0)
return -EINVAL;
r = safe_atou16(split + 1, &container_port);
} else {
r = safe_atou16(e, &container_port);
host_port = container_port;
}
if (r < 0 || container_port <= 0)
return -EINVAL;
LIST_FOREACH(ports, p, arg_expose_ports)
if (p->protocol == protocol && p->host_port == host_port)
return -EEXIST;
p = new(ExposePort, 1);
if (!p)
return -ENOMEM;
p->protocol = protocol;
p->host_port = host_port;
p->container_port = container_port;
LIST_PREPEND(ports, *l, p);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
@ -1388,141 +1332,6 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
return 0;
}
static int send_rtnl(int send_fd) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
_cleanup_close_ int fd = -1;
ssize_t k;
assert(send_fd >= 0);
if (!arg_expose_ports)
return 0;
fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate container netlink: %m");
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
k = sendmsg(send_fd, &mh, MSG_NOSIGNAL);
if (k < 0)
return log_error_errno(errno, "Failed to send netlink fd: %m");
return 0;
}
static int flush_ports(union in_addr_union *exposed) {
ExposePort *p;
int r, af = AF_INET;
assert(exposed);
if (!arg_expose_ports)
return 0;
if (in_addr_is_null(af, exposed))
return 0;
log_debug("Lost IP address.");
LIST_FOREACH(ports, p, arg_expose_ports) {
r = fw_add_local_dnat(false,
af,
p->protocol,
NULL,
NULL, 0,
NULL, 0,
p->host_port,
exposed,
p->container_port,
NULL);
if (r < 0)
log_warning_errno(r, "Failed to modify firewall: %m");
}
*exposed = IN_ADDR_NULL;
return 0;
}
static int expose_ports(sd_netlink *rtnl, union in_addr_union *exposed) {
_cleanup_free_ struct local_address *addresses = NULL;
_cleanup_free_ char *pretty = NULL;
union in_addr_union new_exposed;
ExposePort *p;
bool add;
int af = AF_INET, r;
assert(exposed);
/* Invoked each time an address is added or removed inside the
* container */
if (!arg_expose_ports)
return 0;
r = local_addresses(rtnl, 0, af, &addresses);
if (r < 0)
return log_error_errno(r, "Failed to enumerate local addresses: %m");
add = r > 0 &&
addresses[0].family == af &&
addresses[0].scope < RT_SCOPE_LINK;
if (!add)
return flush_ports(exposed);
new_exposed = addresses[0].address;
if (in_addr_equal(af, exposed, &new_exposed))
return 0;
in_addr_to_string(af, &new_exposed, &pretty);
log_debug("New container IP is %s.", strna(pretty));
LIST_FOREACH(ports, p, arg_expose_ports) {
r = fw_add_local_dnat(true,
af,
p->protocol,
NULL,
NULL, 0,
NULL, 0,
p->host_port,
&new_exposed,
p->container_port,
in_addr_is_null(af, exposed) ? NULL : exposed);
if (r < 0)
log_warning_errno(r, "Failed to modify firewall: %m");
}
*exposed = new_exposed;
return 0;
}
void expose_port_free_all(ExposePort *p) {
while (p) {
ExposePort *q = p;
LIST_REMOVE(ports, p, q);
free(q);
}
}
static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
union in_addr_union *exposed = userdata;
@ -1530,62 +1339,7 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user
assert(m);
assert(exposed);
expose_ports(rtnl, exposed);
return 0;
}
static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed, sd_netlink **ret) {
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
_cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
int fd, r;
ssize_t k;
assert(event);
assert(recv_fd >= 0);
assert(ret);
if (!arg_expose_ports)
return 0;
k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
if (k < 0)
return log_error_errno(errno, "Failed to recv netlink fd: %m");
cmsg = CMSG_FIRSTHDR(&mh);
assert(cmsg->cmsg_level == SOL_SOCKET);
assert(cmsg->cmsg_type == SCM_RIGHTS);
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
r = sd_netlink_open_fd(&rtnl, fd);
if (r < 0) {
safe_close(fd);
return log_error_errno(r, "Failed to create rtnl object: %m");
}
r = sd_netlink_add_match(rtnl, RTM_NEWADDR, on_address_change, exposed);
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
r = sd_netlink_add_match(rtnl, RTM_DELADDR, on_address_change, exposed);
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
r = sd_netlink_attach_event(rtnl, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to add to even loop: %m");
*ret = rtnl;
rtnl = NULL;
expose_port_execute(rtnl, arg_expose_ports, exposed);
return 0;
}
@ -3642,10 +3396,12 @@ static int inner_child(
if (arg_private_network)
loopback_setup();
r = send_rtnl(rtnl_socket);
if (r < 0)
return r;
rtnl_socket = safe_close(rtnl_socket);
if (arg_expose_ports) {
r = expose_port_send_rtnl(rtnl_socket);
if (r < 0)
return r;
rtnl_socket = safe_close(rtnl_socket);
}
if (drop_capabilities() < 0)
return log_error_errno(errno, "drop_capabilities() failed: %m");
@ -4759,11 +4515,11 @@ int main(int argc, char *argv[]) {
sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
if (arg_expose_ports) {
r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl);
if (r < 0)
goto finish;
(void) expose_ports(rtnl, &exposed);
(void) expose_port_execute(rtnl, arg_expose_ports, &exposed);
}
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
@ -4826,7 +4582,7 @@ int main(int argc, char *argv[]) {
break;
}
flush_ports(&exposed);
expose_port_flush(arg_expose_ports, &exposed);
}
finish:
@ -4858,7 +4614,7 @@ finish:
(void) rm_rf(p, REMOVE_ROOT);
}
flush_ports(&exposed);
expose_port_flush(arg_expose_ports, &exposed);
free(arg_directory);
free(arg_template);

View File

@ -20,18 +20,3 @@
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <stdbool.h>
#include "list.h"
typedef struct ExposePort {
int protocol;
uint16_t host_port;
uint16_t container_port;
LIST_FIELDS(struct ExposePort, ports);
} ExposePort;
void expose_port_free_all(ExposePort *p);
int expose_port_parse(ExposePort **l, const char *s);