]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-expose-ports.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / nspawn / nspawn-expose-ports.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "sd-netlink.h"
22
23 #include "alloc-util.h"
24 #include "fd-util.h"
25 #include "firewall-util.h"
26 #include "in-addr-util.h"
27 #include "local-addresses.h"
28 #include "netlink-util.h"
29 #include "nspawn-expose-ports.h"
30 #include "parse-util.h"
31 #include "socket-util.h"
32 #include "string-util.h"
33 #include "util.h"
34
35 int expose_port_parse(ExposePort **l, const char *s) {
36
37 const char *split, *e;
38 uint16_t container_port, host_port;
39 int protocol;
40 ExposePort *p;
41 int r;
42
43 assert(l);
44 assert(s);
45
46 if ((e = startswith(s, "tcp:")))
47 protocol = IPPROTO_TCP;
48 else if ((e = startswith(s, "udp:")))
49 protocol = IPPROTO_UDP;
50 else {
51 e = s;
52 protocol = IPPROTO_TCP;
53 }
54
55 split = strchr(e, ':');
56 if (split) {
57 char v[split - e + 1];
58
59 memcpy(v, e, split - e);
60 v[split - e] = 0;
61
62 r = parse_ip_port(v, &host_port);
63 if (r < 0)
64 return -EINVAL;
65
66 r = parse_ip_port(split + 1, &container_port);
67 } else {
68 r = parse_ip_port(e, &container_port);
69 host_port = container_port;
70 }
71
72 if (r < 0)
73 return -EINVAL;
74
75 LIST_FOREACH(ports, p, *l)
76 if (p->protocol == protocol && p->host_port == host_port)
77 return -EEXIST;
78
79 p = new(ExposePort, 1);
80 if (!p)
81 return -ENOMEM;
82
83 p->protocol = protocol;
84 p->host_port = host_port;
85 p->container_port = container_port;
86
87 LIST_PREPEND(ports, *l, p);
88
89 return 0;
90 }
91
92 void expose_port_free_all(ExposePort *p) {
93
94 while (p) {
95 ExposePort *q = p;
96 LIST_REMOVE(ports, p, q);
97 free(q);
98 }
99 }
100
101 int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
102 ExposePort *p;
103 int r, af = AF_INET;
104
105 assert(exposed);
106
107 if (!l)
108 return 0;
109
110 if (in_addr_is_null(af, exposed))
111 return 0;
112
113 log_debug("Lost IP address.");
114
115 LIST_FOREACH(ports, p, l) {
116 r = fw_add_local_dnat(false,
117 af,
118 p->protocol,
119 NULL,
120 NULL, 0,
121 NULL, 0,
122 p->host_port,
123 exposed,
124 p->container_port,
125 NULL);
126 if (r < 0)
127 log_warning_errno(r, "Failed to modify firewall: %m");
128 }
129
130 *exposed = IN_ADDR_NULL;
131 return 0;
132 }
133
134 int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
135 _cleanup_free_ struct local_address *addresses = NULL;
136 _cleanup_free_ char *pretty = NULL;
137 union in_addr_union new_exposed;
138 ExposePort *p;
139 bool add;
140 int af = AF_INET, r;
141
142 assert(exposed);
143
144 /* Invoked each time an address is added or removed inside the
145 * container */
146
147 if (!l)
148 return 0;
149
150 r = local_addresses(rtnl, 0, af, &addresses);
151 if (r < 0)
152 return log_error_errno(r, "Failed to enumerate local addresses: %m");
153
154 add = r > 0 &&
155 addresses[0].family == af &&
156 addresses[0].scope < RT_SCOPE_LINK;
157
158 if (!add)
159 return expose_port_flush(l, exposed);
160
161 new_exposed = addresses[0].address;
162 if (in_addr_equal(af, exposed, &new_exposed))
163 return 0;
164
165 in_addr_to_string(af, &new_exposed, &pretty);
166 log_debug("New container IP is %s.", strna(pretty));
167
168 LIST_FOREACH(ports, p, l) {
169
170 r = fw_add_local_dnat(true,
171 af,
172 p->protocol,
173 NULL,
174 NULL, 0,
175 NULL, 0,
176 p->host_port,
177 &new_exposed,
178 p->container_port,
179 in_addr_is_null(af, exposed) ? NULL : exposed);
180 if (r < 0)
181 log_warning_errno(r, "Failed to modify firewall: %m");
182 }
183
184 *exposed = new_exposed;
185 return 0;
186 }
187
188 int expose_port_send_rtnl(int send_fd) {
189 _cleanup_close_ int fd = -1;
190 int r;
191
192 assert(send_fd >= 0);
193
194 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
195 if (fd < 0)
196 return log_error_errno(errno, "Failed to allocate container netlink: %m");
197
198 /* Store away the fd in the socket, so that it stays open as
199 * long as we run the child */
200 r = send_one_fd(send_fd, fd, 0);
201 if (r < 0)
202 return log_error_errno(r, "Failed to send netlink fd: %m");
203
204 return 0;
205 }
206
207 int expose_port_watch_rtnl(
208 sd_event *event,
209 int recv_fd,
210 sd_netlink_message_handler_t handler,
211 union in_addr_union *exposed,
212 sd_netlink **ret) {
213 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
214 int fd, r;
215
216 assert(event);
217 assert(recv_fd >= 0);
218 assert(ret);
219
220 fd = receive_one_fd(recv_fd, 0);
221 if (fd < 0)
222 return log_error_errno(fd, "Failed to recv netlink fd: %m");
223
224 r = sd_netlink_open_fd(&rtnl, fd);
225 if (r < 0) {
226 safe_close(fd);
227 return log_error_errno(r, "Failed to create rtnl object: %m");
228 }
229
230 r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
231 if (r < 0)
232 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
233
234 r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
235 if (r < 0)
236 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
237
238 r = sd_netlink_attach_event(rtnl, event, 0);
239 if (r < 0)
240 return log_error_errno(r, "Failed to add to even loop: %m");
241
242 *ret = rtnl;
243 rtnl = NULL;
244
245 return 0;
246 }