]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-expose-ports.c
Merge pull request #15265 from fbuihuu/mount-fixes
[thirdparty/systemd.git] / src / nspawn / nspawn-expose-ports.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "sd-netlink.h"
4
5 #include "alloc-util.h"
6 #include "fd-util.h"
7 #include "firewall-util.h"
8 #include "in-addr-util.h"
9 #include "local-addresses.h"
10 #include "netlink-util.h"
11 #include "nspawn-expose-ports.h"
12 #include "parse-util.h"
13 #include "socket-util.h"
14 #include "string-util.h"
15 #include "util.h"
16
17 int expose_port_parse(ExposePort **l, const char *s) {
18
19 const char *split, *e;
20 uint16_t container_port, host_port;
21 int protocol;
22 ExposePort *p;
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
44 r = parse_ip_port(v, &host_port);
45 if (r < 0)
46 return -EINVAL;
47
48 r = parse_ip_port(split + 1, &container_port);
49 } else {
50 r = parse_ip_port(e, &container_port);
51 host_port = container_port;
52 }
53
54 if (r < 0)
55 return r;
56
57 LIST_FOREACH(ports, p, *l)
58 if (p->protocol == protocol && p->host_port == host_port)
59 return -EEXIST;
60
61 p = new(ExposePort, 1);
62 if (!p)
63 return -ENOMEM;
64
65 *p = (ExposePort) {
66 .protocol = protocol,
67 .host_port = host_port,
68 .container_port = container_port,
69 };
70
71 LIST_PREPEND(ports, *l, p);
72
73 return 0;
74 }
75
76 void 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
85 int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
86 ExposePort *p;
87 int r, af = AF_INET;
88
89 assert(exposed);
90
91 if (!l)
92 return 0;
93
94 if (in_addr_is_null(af, exposed))
95 return 0;
96
97 log_debug("Lost IP address.");
98
99 LIST_FOREACH(ports, p, l) {
100 r = fw_add_local_dnat(false,
101 af,
102 p->protocol,
103 NULL,
104 NULL, 0,
105 NULL, 0,
106 p->host_port,
107 exposed,
108 p->container_port,
109 NULL);
110 if (r < 0)
111 log_warning_errno(r, "Failed to modify firewall: %m");
112 }
113
114 *exposed = IN_ADDR_NULL;
115 return 0;
116 }
117
118 int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
119 _cleanup_free_ struct local_address *addresses = NULL;
120 union in_addr_union new_exposed;
121 ExposePort *p;
122 bool add;
123 int af = AF_INET, r;
124
125 assert(exposed);
126
127 /* Invoked each time an address is added or removed inside the
128 * container */
129
130 if (!l)
131 return 0;
132
133 r = local_addresses(rtnl, 0, af, &addresses);
134 if (r < 0)
135 return log_error_errno(r, "Failed to enumerate local addresses: %m");
136
137 add = r > 0 &&
138 addresses[0].family == af &&
139 addresses[0].scope < RT_SCOPE_LINK;
140
141 if (!add)
142 return expose_port_flush(l, exposed);
143
144 new_exposed = addresses[0].address;
145 if (in_addr_equal(af, exposed, &new_exposed))
146 return 0;
147
148 if (DEBUG_LOGGING) {
149 _cleanup_free_ char *pretty = NULL;
150 in_addr_to_string(af, &new_exposed, &pretty);
151 log_debug("New container IP is %s.", strna(pretty));
152 }
153
154 LIST_FOREACH(ports, p, l) {
155
156 r = fw_add_local_dnat(true,
157 af,
158 p->protocol,
159 NULL,
160 NULL, 0,
161 NULL, 0,
162 p->host_port,
163 &new_exposed,
164 p->container_port,
165 in_addr_is_null(af, exposed) ? NULL : exposed);
166 if (r < 0)
167 log_warning_errno(r, "Failed to modify firewall: %m");
168 }
169
170 *exposed = new_exposed;
171 return 0;
172 }
173
174 int expose_port_send_rtnl(int send_fd) {
175 _cleanup_close_ int fd = -1;
176 int r;
177
178 assert(send_fd >= 0);
179
180 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
181 if (fd < 0)
182 return log_error_errno(errno, "Failed to allocate container netlink: %m");
183
184 /* Store away the fd in the socket, so that it stays open as
185 * long as we run the child */
186 r = send_one_fd(send_fd, fd, 0);
187 if (r < 0)
188 return log_error_errno(r, "Failed to send netlink fd: %m");
189
190 return 0;
191 }
192
193 int expose_port_watch_rtnl(
194 sd_event *event,
195 int recv_fd,
196 sd_netlink_message_handler_t handler,
197 union in_addr_union *exposed,
198 sd_netlink **ret) {
199 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
200 int fd, r;
201
202 assert(event);
203 assert(recv_fd >= 0);
204 assert(ret);
205
206 fd = receive_one_fd(recv_fd, 0);
207 if (fd < 0)
208 return log_error_errno(fd, "Failed to recv netlink fd: %m");
209
210 r = sd_netlink_open_fd(&rtnl, fd);
211 if (r < 0) {
212 safe_close(fd);
213 return log_error_errno(r, "Failed to create rtnl object: %m");
214 }
215
216 r = sd_netlink_add_match(rtnl, NULL, RTM_NEWADDR, handler, NULL, exposed, "nspawn-NEWADDR");
217 if (r < 0)
218 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
219
220 r = sd_netlink_add_match(rtnl, NULL, RTM_DELADDR, handler, NULL, exposed, "nspawn-DELADDR");
221 if (r < 0)
222 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
223
224 r = sd_netlink_attach_event(rtnl, event, 0);
225 if (r < 0)
226 return log_error_errno(r, "Failed to add to event loop: %m");
227
228 *ret = TAKE_PTR(rtnl);
229
230 return 0;
231 }