From 7a8f63251df2202e59a76e537a688fe7500cb1ad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 7 Sep 2015 16:52:24 +0200 Subject: nspawn: split all port exposure code into nspawn-expose-port.[ch] --- src/nspawn/nspawn-expose-ports.c | 278 +++++++++++++++++++++++++++++++++++++++ src/nspawn/nspawn-expose-ports.h | 45 +++++++ src/nspawn/nspawn-gperf.gperf | 1 + src/nspawn/nspawn-settings.h | 1 + src/nspawn/nspawn.c | 268 ++----------------------------------- src/nspawn/nspawn.h | 15 --- 6 files changed, 337 insertions(+), 271 deletions(-) create mode 100644 src/nspawn/nspawn-expose-ports.c create mode 100644 src/nspawn/nspawn-expose-ports.h (limited to 'src') diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c new file mode 100644 index 0000000000..a6b8175e84 --- /dev/null +++ b/src/nspawn/nspawn-expose-ports.c @@ -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 . +***/ + +#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; +} diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h new file mode 100644 index 0000000000..39cec28695 --- /dev/null +++ b/src/nspawn/nspawn-expose-ports.h @@ -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 . +***/ + +#include + +#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); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 8b68dfa9f5..e0f05547f3 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -3,6 +3,7 @@ #include "conf-parser.h" #include "nspawn.h" #include "nspawn-settings.h" +#include "nspawn-expose-ports.h" %} struct ConfigPerfItem; %null_strings diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 24adbe2d5d..2e84be6889 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -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, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c249017c43..92c14143c1 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -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); diff --git a/src/nspawn/nspawn.h b/src/nspawn/nspawn.h index 1e2c26b65c..33eaa7e9d1 100644 --- a/src/nspawn/nspawn.h +++ b/src/nspawn/nspawn.h @@ -20,18 +20,3 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ - -#include -#include - -#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); -- cgit v1.2.3-54-g00ecf