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