]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: split all port exposure code into nspawn-expose-port.[ch]
authorLennart Poettering <lennart@poettering.net>
Mon, 7 Sep 2015 14:52:24 +0000 (16:52 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 7 Sep 2015 16:44:30 +0000 (18:44 +0200)
Makefile.am
src/nspawn/nspawn-expose-ports.c [new file with mode: 0644]
src/nspawn/nspawn-expose-ports.h [new file with mode: 0644]
src/nspawn/nspawn-gperf.gperf
src/nspawn/nspawn-settings.h
src/nspawn/nspawn.c
src/nspawn/nspawn.h

index ece328846764966d21939513decfa6e039fdf241..8c371fa1e7f1e4a3fe5f3b45ec5134572e33ba11 100644 (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 \
diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c
new file mode 100644 (file)
index 0000000..a6b8175
--- /dev/null
@@ -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 (file)
index 0000000..39cec28
--- /dev/null
@@ -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);
index 8b68dfa9f5d3d4d1b3a4325f7ebf71e1d83ddb29..e0f05547f34e2439850ecba62080ce0a6c0c2dc7 100644 (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
index 24adbe2d5db7487e1b3452f33083b4847be53002..2e84be6889cd1952852d87c6e597f3b065c2b5b6 100644 (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,
index c249017c431c46469a0f22daf27ac2435e039aba..92c14143c1d2848137821f69bc6a0235f25e4fae 100644 (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"
 #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);
index 1e2c26b65ca09110e404be8614b1db6a421373c3..33eaa7e9d11ef16ff87e8db49ffee6d826a490cf 100644 (file)
   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);