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