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