]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7a8f6325
LP
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
b5efdb8a 23#include "alloc-util.h"
3ffd4af2 24#include "fd-util.h"
7a8f6325 25#include "firewall-util.h"
07630cea 26#include "in-addr-util.h"
7a8f6325
LP
27#include "local-addresses.h"
28#include "netlink-util.h"
3ffd4af2 29#include "nspawn-expose-ports.h"
6bedfcbb 30#include "parse-util.h"
2583fbea 31#include "socket-util.h"
07630cea
LP
32#include "string-util.h"
33#include "util.h"
7a8f6325
LP
34
35int 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
10452f7c
SS
62 r = parse_ip_port(v, &host_port);
63 if (r < 0)
7a8f6325
LP
64 return -EINVAL;
65
10452f7c 66 r = parse_ip_port(split + 1, &container_port);
7a8f6325 67 } else {
10452f7c 68 r = parse_ip_port(e, &container_port);
7a8f6325
LP
69 host_port = container_port;
70 }
71
10452f7c 72 if (r < 0)
7a8f6325
LP
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
92void 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
101int 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
134int 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
188int expose_port_send_rtnl(int send_fd) {
7a8f6325 189 _cleanup_close_ int fd = -1;
d9603714 190 int r;
7a8f6325
LP
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
7a8f6325
LP
198 /* Store away the fd in the socket, so that it stays open as
199 * long as we run the child */
3ee897d6 200 r = send_one_fd(send_fd, fd, 0);
d9603714
DH
201 if (r < 0)
202 return log_error_errno(r, "Failed to send netlink fd: %m");
7a8f6325
LP
203
204 return 0;
205}
206
207int 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) {
4afd3348 213 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
7a8f6325 214 int fd, r;
7a8f6325
LP
215
216 assert(event);
217 assert(recv_fd >= 0);
218 assert(ret);
219
3ee897d6 220 fd = receive_one_fd(recv_fd, 0);
d9603714
DH
221 if (fd < 0)
222 return log_error_errno(fd, "Failed to recv netlink fd: %m");
7a8f6325
LP
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}