]> git.proxmox.com Git - systemd.git/blame - src/nspawn/nspawn-expose-ports.c
New upstream version 236
[systemd.git] / src / nspawn / nspawn-expose-ports.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
d9dfd233
MP
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
db2df898
MP
23#include "alloc-util.h"
24#include "fd-util.h"
d9dfd233 25#include "firewall-util.h"
db2df898 26#include "in-addr-util.h"
d9dfd233
MP
27#include "local-addresses.h"
28#include "netlink-util.h"
d9dfd233 29#include "nspawn-expose-ports.h"
db2df898
MP
30#include "parse-util.h"
31#include "socket-util.h"
32#include "string-util.h"
33#include "util.h"
d9dfd233
MP
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
2897b343
MP
62 r = parse_ip_port(v, &host_port);
63 if (r < 0)
d9dfd233
MP
64 return -EINVAL;
65
2897b343 66 r = parse_ip_port(split + 1, &container_port);
d9dfd233 67 } else {
2897b343 68 r = parse_ip_port(e, &container_port);
d9dfd233
MP
69 host_port = container_port;
70 }
71
2897b343 72 if (r < 0)
d9dfd233
MP
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) {
d9dfd233 189 _cleanup_close_ int fd = -1;
6300502b 190 int r;
d9dfd233
MP
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
d9dfd233
MP
198 /* Store away the fd in the socket, so that it stays open as
199 * long as we run the child */
6300502b
MP
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");
d9dfd233
MP
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) {
4c89c718 213 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
d9dfd233 214 int fd, r;
d9dfd233
MP
215
216 assert(event);
217 assert(recv_fd >= 0);
218 assert(ret);
219
6300502b
MP
220 fd = receive_one_fd(recv_fd, 0);
221 if (fd < 0)
222 return log_error_errno(fd, "Failed to recv netlink fd: %m");
d9dfd233
MP
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}