]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-expose-ports.c
Merge pull request #1880 from fsateler/sysctl-doc
[thirdparty/systemd.git] / src / nspawn / nspawn-expose-ports.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "sd-netlink.h"
23
24 #include "alloc-util.h"
25 #include "fd-util.h"
26 #include "firewall-util.h"
27 #include "in-addr-util.h"
28 #include "local-addresses.h"
29 #include "netlink-util.h"
30 #include "nspawn-expose-ports.h"
31 #include "parse-util.h"
32 #include "socket-util.h"
33 #include "string-util.h"
34 #include "util.h"
35
36 int expose_port_parse(ExposePort **l, const char *s) {
37
38 const char *split, *e;
39 uint16_t container_port, host_port;
40 int protocol;
41 ExposePort *p;
42 int r;
43
44 assert(l);
45 assert(s);
46
47 if ((e = startswith(s, "tcp:")))
48 protocol = IPPROTO_TCP;
49 else if ((e = startswith(s, "udp:")))
50 protocol = IPPROTO_UDP;
51 else {
52 e = s;
53 protocol = IPPROTO_TCP;
54 }
55
56 split = strchr(e, ':');
57 if (split) {
58 char v[split - e + 1];
59
60 memcpy(v, e, split - e);
61 v[split - e] = 0;
62
63 r = safe_atou16(v, &host_port);
64 if (r < 0 || host_port <= 0)
65 return -EINVAL;
66
67 r = safe_atou16(split + 1, &container_port);
68 } else {
69 r = safe_atou16(e, &container_port);
70 host_port = container_port;
71 }
72
73 if (r < 0 || container_port <= 0)
74 return -EINVAL;
75
76 LIST_FOREACH(ports, p, *l)
77 if (p->protocol == protocol && p->host_port == host_port)
78 return -EEXIST;
79
80 p = new(ExposePort, 1);
81 if (!p)
82 return -ENOMEM;
83
84 p->protocol = protocol;
85 p->host_port = host_port;
86 p->container_port = container_port;
87
88 LIST_PREPEND(ports, *l, p);
89
90 return 0;
91 }
92
93 void expose_port_free_all(ExposePort *p) {
94
95 while (p) {
96 ExposePort *q = p;
97 LIST_REMOVE(ports, p, q);
98 free(q);
99 }
100 }
101
102 int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
103 ExposePort *p;
104 int r, af = AF_INET;
105
106 assert(exposed);
107
108 if (!l)
109 return 0;
110
111 if (in_addr_is_null(af, exposed))
112 return 0;
113
114 log_debug("Lost IP address.");
115
116 LIST_FOREACH(ports, p, l) {
117 r = fw_add_local_dnat(false,
118 af,
119 p->protocol,
120 NULL,
121 NULL, 0,
122 NULL, 0,
123 p->host_port,
124 exposed,
125 p->container_port,
126 NULL);
127 if (r < 0)
128 log_warning_errno(r, "Failed to modify firewall: %m");
129 }
130
131 *exposed = IN_ADDR_NULL;
132 return 0;
133 }
134
135 int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
136 _cleanup_free_ struct local_address *addresses = NULL;
137 _cleanup_free_ char *pretty = NULL;
138 union in_addr_union new_exposed;
139 ExposePort *p;
140 bool add;
141 int af = AF_INET, r;
142
143 assert(exposed);
144
145 /* Invoked each time an address is added or removed inside the
146 * container */
147
148 if (!l)
149 return 0;
150
151 r = local_addresses(rtnl, 0, af, &addresses);
152 if (r < 0)
153 return log_error_errno(r, "Failed to enumerate local addresses: %m");
154
155 add = r > 0 &&
156 addresses[0].family == af &&
157 addresses[0].scope < RT_SCOPE_LINK;
158
159 if (!add)
160 return expose_port_flush(l, exposed);
161
162 new_exposed = addresses[0].address;
163 if (in_addr_equal(af, exposed, &new_exposed))
164 return 0;
165
166 in_addr_to_string(af, &new_exposed, &pretty);
167 log_debug("New container IP is %s.", strna(pretty));
168
169 LIST_FOREACH(ports, p, l) {
170
171 r = fw_add_local_dnat(true,
172 af,
173 p->protocol,
174 NULL,
175 NULL, 0,
176 NULL, 0,
177 p->host_port,
178 &new_exposed,
179 p->container_port,
180 in_addr_is_null(af, exposed) ? NULL : exposed);
181 if (r < 0)
182 log_warning_errno(r, "Failed to modify firewall: %m");
183 }
184
185 *exposed = new_exposed;
186 return 0;
187 }
188
189 int expose_port_send_rtnl(int send_fd) {
190 _cleanup_close_ int fd = -1;
191 int r;
192
193 assert(send_fd >= 0);
194
195 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
196 if (fd < 0)
197 return log_error_errno(errno, "Failed to allocate container netlink: %m");
198
199 /* Store away the fd in the socket, so that it stays open as
200 * long as we run the child */
201 r = send_one_fd(send_fd, fd, 0);
202 if (r < 0)
203 return log_error_errno(r, "Failed to send netlink fd: %m");
204
205 return 0;
206 }
207
208 int expose_port_watch_rtnl(
209 sd_event *event,
210 int recv_fd,
211 sd_netlink_message_handler_t handler,
212 union in_addr_union *exposed,
213 sd_netlink **ret) {
214 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
215 int fd, r;
216
217 assert(event);
218 assert(recv_fd >= 0);
219 assert(ret);
220
221 fd = receive_one_fd(recv_fd, 0);
222 if (fd < 0)
223 return log_error_errno(fd, "Failed to recv netlink fd: %m");
224
225 r = sd_netlink_open_fd(&rtnl, fd);
226 if (r < 0) {
227 safe_close(fd);
228 return log_error_errno(r, "Failed to create rtnl object: %m");
229 }
230
231 r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
232 if (r < 0)
233 return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
234
235 r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
236 if (r < 0)
237 return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
238
239 r = sd_netlink_attach_event(rtnl, event, 0);
240 if (r < 0)
241 return log_error_errno(r, "Failed to add to even loop: %m");
242
243 *ret = rtnl;
244 rtnl = NULL;
245
246 return 0;
247 }