summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-09-07 16:52:24 +0200
committerLennart Poettering <lennart@poettering.net>2015-09-07 18:44:30 +0200
commit7a8f63251df2202e59a76e537a688fe7500cb1ad (patch)
tree03d8862040b6db029825b25603762244fa98d089
parente83bebeff7d9d734e17c3e38ac13daabc09518e1 (diff)
nspawn: split all port exposure code into nspawn-expose-port.[ch]
-rw-r--r--Makefile.am2
-rw-r--r--src/nspawn/nspawn-expose-ports.c278
-rw-r--r--src/nspawn/nspawn-expose-ports.h45
-rw-r--r--src/nspawn/nspawn-gperf.gperf1
-rw-r--r--src/nspawn/nspawn-settings.h1
-rw-r--r--src/nspawn/nspawn.c268
-rw-r--r--src/nspawn/nspawn.h15
7 files changed, 339 insertions, 271 deletions
diff --git a/Makefile.am b/Makefile.am
index ece3288467..8c371fa1e7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
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 <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;
+}
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 <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);
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 <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);