Branch data 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 : 16 : void expose_port_free_all(ExposePort *p) {
75 : :
76 [ - + ]: 16 : while (p) {
77 : 0 : ExposePort *q = p;
78 [ # # # # : 0 : LIST_REMOVE(ports, p, q);
# # # # ]
79 : 0 : free(q);
80 : : }
81 : 16 : }
82 : :
83 : 16 : int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
84 : : ExposePort *p;
85 : 16 : int r, af = AF_INET;
86 : :
87 [ - + ]: 16 : assert(exposed);
88 : :
89 [ + - ]: 16 : if (!l)
90 : 16 : 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 : : }
|