]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-expose-ports.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / nspawn / nspawn-expose-ports.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include "sd-netlink.h"
23
24 #include "firewall-util.h"
25 #include "in-addr-util.h"
26 #include "local-addresses.h"
27 #include "netlink-util.h"
28 #include "string-util.h"
29 #include "util.h"
30 #include "nspawn-expose-ports.h"
31
32 int expose_port_parse(ExposePort **l, const char *s) {
33
34 const char *split, *e;
35 uint16_t container_port, host_port;
36 int protocol;
37 ExposePort *p;
38 int r;
39
40 assert(l);
41 assert(s);
42
43 if ((e = startswith(s, "tcp:")))
44 protocol = IPPROTO_TCP;
45 else if ((e = startswith(s, "udp:")))
46 protocol = IPPROTO_UDP;
47 else {
48 e = s;
49 protocol = IPPROTO_TCP;
50 }
51
52 split = strchr(e, ':');
53 if (split) {
54 char v[split - e + 1];
55
56 memcpy(v, e, split - e);
57 v[split - e] = 0;
58
59 r = safe_atou16(v, &host_port);
60 if (r < 0 || host_port <= 0)
61 return -EINVAL;
62
63 r = safe_atou16(split + 1, &container_port);
64 } else {
65 r = safe_atou16(e, &container_port);
66 host_port = container_port;
67 }
68
69 if (r < 0 || container_port <= 0)
70 return -EINVAL;
71
72 LIST_FOREACH(ports, p, *l)
73 if (p->protocol == protocol && p->host_port == host_port)
74 return -EEXIST;
75
76 p = new(ExposePort, 1);
77 if (!p)
78 return -ENOMEM;
79
80 p->protocol = protocol;
81 p->host_port = host_port;
82 p->container_port = container_port;
83
84 LIST_PREPEND(ports, *l, p);
85
86 return 0;
87 }
88
89 void expose_port_free_all(ExposePort *p) {
90
91 while (p) {
92 ExposePort *q = p;
93 LIST_REMOVE(ports, p, q);
94 free(q);
95 }
96 }
97
98 int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
99 ExposePort *p;
100 int r, af = AF_INET;
101
102 assert(exposed);
103
104 if (!l)
105 return 0;
106
107 if (in_addr_is_null(af, exposed))
108 return 0;
109
110 log_debug("Lost IP address.");
111
112 LIST_FOREACH(ports, p, l) {
113 r = fw_add_local_dnat(false,
114 af,
115 p->protocol,
116 NULL,
117 NULL, 0,
118 NULL, 0,
119 p->host_port,
120 exposed,
121 p->container_port,
122 NULL);
123 if (r < 0)
124 log_warning_errno(r, "Failed to modify firewall: %m");
125 }
126
127 *exposed = IN_ADDR_NULL;
128 return 0;
129 }
130
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;
135 ExposePort *p;
136 bool add;
137 int af = AF_INET, r;
138
139 assert(exposed);
140
141 /* Invoked each time an address is added or removed inside the
142 * container */
143
144 if (!l)
145 return 0;
146
147 r = local_addresses(rtnl, 0, af, &addresses);
148 if (r < 0)
149 return log_error_errno(r, "Failed to enumerate local addresses: %m");
150
151 add = r > 0 &&
152 addresses[0].family == af &&
153 addresses[0].scope < RT_SCOPE_LINK;
154
155 if (!add)
156 return expose_port_flush(l, exposed);
157
158 new_exposed = addresses[0].address;
159 if (in_addr_equal(af, exposed, &new_exposed))
160 return 0;
161
162 in_addr_to_string(af, &new_exposed, &pretty);
163 log_debug("New container IP is %s.", strna(pretty));
164
165 LIST_FOREACH(ports, p, l) {
166
167 r = fw_add_local_dnat(true,
168 af,
169 p->protocol,
170 NULL,
171 NULL, 0,
172 NULL, 0,
173 p->host_port,
174 &new_exposed,
175 p->container_port,
176 in_addr_is_null(af, exposed) ? NULL : exposed);
177 if (r < 0)
178 log_warning_errno(r, "Failed to modify firewall: %m");
179 }
180
181 *exposed = new_exposed;
182 return 0;
183 }
184
185 int expose_port_send_rtnl(int send_fd) {
186 _cleanup_close_ int fd = -1;
187 int r;
188
189 assert(send_fd >= 0);
190
191 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
192 if (fd < 0)
193 return log_error_errno(errno, "Failed to allocate container netlink: %m");
194
195 /* Store away the fd in the socket, so that it stays open as
196 * long as we run the child */
197 r = send_one_fd(send_fd, fd, 0);
198 if (r < 0)
199 return log_error_errno(r, "Failed to send netlink fd: %m");
200
201 return 0;
202 }
203
204 int expose_port_watch_rtnl(
205 sd_event *event,
206 int recv_fd,
207 sd_netlink_message_handler_t handler,
208 union in_addr_union *exposed,
209 sd_netlink **ret) {
210 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
211 int fd, r;
212
213 assert(event);
214 assert(recv_fd >= 0);
215 assert(ret);
216
217 fd = receive_one_fd(recv_fd, 0);
218 if (fd < 0)
219 return log_error_errno(fd, "Failed to recv netlink fd: %m");
220
221 r = sd_netlink_open_fd(&rtnl, fd);
222 if (r < 0) {
223 safe_close(fd);
224 return log_error_errno(r, "Failed to create rtnl object: %m");
225 }
226
227 r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
228 if (r < 0)
229 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
230
231 r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
232 if (r < 0)
233 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
234
235 r = sd_netlink_attach_event(rtnl, event, 0);
236 if (r < 0)
237 return log_error_errno(r, "Failed to add to even loop: %m");
238
239 *ret = rtnl;
240 rtnl = NULL;
241
242 return 0;
243 }