]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-expose-ports.c
shared/microhttp-util: silence gcc warning
[thirdparty/systemd.git] / src / nspawn / nspawn-expose-ports.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
7a8f6325
LP
2
3#include "sd-netlink.h"
4
deff68e7 5#include "af-list.h"
b5efdb8a 6#include "alloc-util.h"
3ffd4af2 7#include "fd-util.h"
7a8f6325 8#include "firewall-util.h"
07630cea 9#include "in-addr-util.h"
7a8f6325
LP
10#include "local-addresses.h"
11#include "netlink-util.h"
3ffd4af2 12#include "nspawn-expose-ports.h"
6bedfcbb 13#include "parse-util.h"
2583fbea 14#include "socket-util.h"
07630cea
LP
15#include "string-util.h"
16#include "util.h"
7a8f6325
LP
17
18int expose_port_parse(ExposePort **l, const char *s) {
7a8f6325
LP
19 const char *split, *e;
20 uint16_t container_port, host_port;
03677889 21 ExposePort *port;
7a8f6325 22 int protocol;
7a8f6325
LP
23 int r;
24
25 assert(l);
26 assert(s);
27
28 if ((e = startswith(s, "tcp:")))
29 protocol = IPPROTO_TCP;
30 else if ((e = startswith(s, "udp:")))
31 protocol = IPPROTO_UDP;
32 else {
33 e = s;
34 protocol = IPPROTO_TCP;
35 }
36
37 split = strchr(e, ':');
38 if (split) {
39 char v[split - e + 1];
40
41 memcpy(v, e, split - e);
42 v[split - e] = 0;
43
10452f7c
SS
44 r = parse_ip_port(v, &host_port);
45 if (r < 0)
7a8f6325
LP
46 return -EINVAL;
47
10452f7c 48 r = parse_ip_port(split + 1, &container_port);
7a8f6325 49 } else {
10452f7c 50 r = parse_ip_port(e, &container_port);
7a8f6325
LP
51 host_port = container_port;
52 }
53
10452f7c 54 if (r < 0)
81d2fe53 55 return r;
7a8f6325
LP
56
57 LIST_FOREACH(ports, p, *l)
58 if (p->protocol == protocol && p->host_port == host_port)
59 return -EEXIST;
60
03677889
YW
61 port = new(ExposePort, 1);
62 if (!port)
7a8f6325
LP
63 return -ENOMEM;
64
03677889 65 *port = (ExposePort) {
81d2fe53
LP
66 .protocol = protocol,
67 .host_port = host_port,
68 .container_port = container_port,
69 };
7a8f6325 70
03677889 71 LIST_PREPEND(ports, *l, port);
7a8f6325
LP
72
73 return 0;
74}
75
76void expose_port_free_all(ExposePort *p) {
77
78 while (p) {
79 ExposePort *q = p;
80 LIST_REMOVE(ports, p, q);
81 free(q);
82 }
83}
84
deff68e7 85int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, int af, union in_addr_union *exposed) {
deff68e7 86 int r;
7a8f6325
LP
87
88 assert(exposed);
89
90 if (!l)
91 return 0;
92
94876904 93 if (!in_addr_is_set(af, exposed))
7a8f6325
LP
94 return 0;
95
96 log_debug("Lost IP address.");
97
98 LIST_FOREACH(ports, p, l) {
761cf19d
FW
99 r = fw_add_local_dnat(fw_ctx,
100 false,
7a8f6325
LP
101 af,
102 p->protocol,
7a8f6325
LP
103 p->host_port,
104 exposed,
105 p->container_port,
106 NULL);
107 if (r < 0)
deff68e7 108 log_warning_errno(r, "Failed to modify %s firewall: %m", af_to_name(af));
7a8f6325
LP
109 }
110
111 *exposed = IN_ADDR_NULL;
112 return 0;
113}
114
deff68e7 115int expose_port_execute(sd_netlink *rtnl, FirewallContext **fw_ctx, ExposePort *l, int af, union in_addr_union *exposed) {
7a8f6325 116 _cleanup_free_ struct local_address *addresses = NULL;
7a8f6325 117 union in_addr_union new_exposed;
7a8f6325 118 bool add;
deff68e7 119 int r;
7a8f6325
LP
120
121 assert(exposed);
122
123 /* Invoked each time an address is added or removed inside the
124 * container */
125
126 if (!l)
127 return 0;
128
129 r = local_addresses(rtnl, 0, af, &addresses);
130 if (r < 0)
131 return log_error_errno(r, "Failed to enumerate local addresses: %m");
132
133 add = r > 0 &&
134 addresses[0].family == af &&
135 addresses[0].scope < RT_SCOPE_LINK;
136
137 if (!add)
deff68e7 138 return expose_port_flush(fw_ctx, l, af, exposed);
7a8f6325
LP
139
140 new_exposed = addresses[0].address;
141 if (in_addr_equal(af, exposed, &new_exposed))
142 return 0;
143
81d2fe53
LP
144 if (DEBUG_LOGGING) {
145 _cleanup_free_ char *pretty = NULL;
146 in_addr_to_string(af, &new_exposed, &pretty);
147 log_debug("New container IP is %s.", strna(pretty));
148 }
7a8f6325
LP
149
150 LIST_FOREACH(ports, p, l) {
151
761cf19d
FW
152 r = fw_add_local_dnat(fw_ctx,
153 true,
7a8f6325
LP
154 af,
155 p->protocol,
7a8f6325
LP
156 p->host_port,
157 &new_exposed,
158 p->container_port,
94876904 159 in_addr_is_set(af, exposed) ? exposed : NULL);
7a8f6325 160 if (r < 0)
deff68e7 161 log_warning_errno(r, "Failed to modify %s firewall: %m", af_to_name(af));
7a8f6325
LP
162 }
163
164 *exposed = new_exposed;
165 return 0;
166}
167
168int expose_port_send_rtnl(int send_fd) {
7a8f6325 169 _cleanup_close_ int fd = -1;
d9603714 170 int r;
7a8f6325
LP
171
172 assert(send_fd >= 0);
173
0522729e 174 fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
7a8f6325
LP
175 if (fd < 0)
176 return log_error_errno(errno, "Failed to allocate container netlink: %m");
177
7a8f6325
LP
178 /* Store away the fd in the socket, so that it stays open as
179 * long as we run the child */
3ee897d6 180 r = send_one_fd(send_fd, fd, 0);
d9603714
DH
181 if (r < 0)
182 return log_error_errno(r, "Failed to send netlink fd: %m");
7a8f6325
LP
183
184 return 0;
185}
186
187int expose_port_watch_rtnl(
188 sd_event *event,
189 int recv_fd,
190 sd_netlink_message_handler_t handler,
f51343d0 191 void *userdata,
7a8f6325 192 sd_netlink **ret) {
4afd3348 193 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
7a8f6325 194 int fd, r;
7a8f6325
LP
195
196 assert(event);
197 assert(recv_fd >= 0);
198 assert(ret);
199
3ee897d6 200 fd = receive_one_fd(recv_fd, 0);
d9603714
DH
201 if (fd < 0)
202 return log_error_errno(fd, "Failed to recv netlink fd: %m");
7a8f6325
LP
203
204 r = sd_netlink_open_fd(&rtnl, fd);
205 if (r < 0) {
206 safe_close(fd);
207 return log_error_errno(r, "Failed to create rtnl object: %m");
208 }
209
f51343d0 210 r = sd_netlink_add_match(rtnl, NULL, RTM_NEWADDR, handler, NULL, userdata, "nspawn-NEWADDR");
7a8f6325
LP
211 if (r < 0)
212 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
213
f51343d0 214 r = sd_netlink_add_match(rtnl, NULL, RTM_DELADDR, handler, NULL, userdata, "nspawn-DELADDR");
7a8f6325
LP
215 if (r < 0)
216 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
217
218 r = sd_netlink_attach_event(rtnl, event, 0);
219 if (r < 0)
8f8dfb95 220 return log_error_errno(r, "Failed to add to event loop: %m");
7a8f6325 221
1cc6c93a 222 *ret = TAKE_PTR(rtnl);
7a8f6325
LP
223
224 return 0;
225}