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