Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright (C) 2014 Intel Corporation. All rights reserved.
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 <netinet/ether.h>
23 : #include <linux/if.h>
24 :
25 : #include "networkd-link.h"
26 : #include "network-internal.h"
27 :
28 : #include "sd-icmp6-nd.h"
29 : #include "sd-dhcp6-client.h"
30 :
31 : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
32 :
33 0 : static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
34 : Link *link) {
35 0 : return 0;
36 : }
37 :
38 0 : static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
39 : void *userdata) {
40 0 : _cleanup_link_unref_ Link *link = userdata;
41 : int r;
42 :
43 0 : assert(link);
44 :
45 0 : r = sd_netlink_message_get_errno(m);
46 0 : if (r < 0 && r != -EEXIST) {
47 0 : if (link->rtnl_extended_attrs) {
48 0 : log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
49 :
50 0 : link->rtnl_extended_attrs = false;
51 0 : dhcp6_lease_address_acquired(link->dhcp6_client, link);
52 :
53 0 : return 1;
54 : }
55 :
56 0 : log_link_error(link, "Could not set DHCPv6 address: %s",
57 : strerror(-r));
58 :
59 0 : link_enter_failed(link);
60 :
61 0 : } else if (r >= 0)
62 0 : link_rtnl_process_address(rtnl, m, link->manager);
63 :
64 0 : return 1;
65 : }
66 :
67 0 : static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
68 : uint8_t prefixlen, uint32_t lifetime_preferred,
69 : uint32_t lifetime_valid) {
70 : int r;
71 0 : _cleanup_address_free_ Address *addr = NULL;
72 :
73 0 : r = address_new_dynamic(&addr);
74 0 : if (r < 0)
75 0 : return r;
76 :
77 0 : addr->family = AF_INET6;
78 0 : memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
79 :
80 0 : addr->flags = IFA_F_NOPREFIXROUTE;
81 0 : addr->prefixlen = prefixlen;
82 :
83 0 : addr->cinfo.ifa_prefered = lifetime_preferred;
84 0 : addr->cinfo.ifa_valid = lifetime_valid;
85 :
86 0 : log_link_info(link,
87 : "DHCPv6 address "SD_ICMP6_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
88 : SD_ICMP6_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
89 : addr->prefixlen, lifetime_preferred, lifetime_valid);
90 :
91 0 : r = address_update(addr, link, dhcp6_address_handler);
92 0 : if (r < 0)
93 0 : log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
94 :
95 0 : return r;
96 : }
97 :
98 0 : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
99 : int r;
100 : sd_dhcp6_lease *lease;
101 : struct in6_addr ip6_addr;
102 : uint32_t lifetime_preferred, lifetime_valid;
103 : uint8_t prefixlen;
104 :
105 0 : r = sd_dhcp6_client_get_lease(client, &lease);
106 0 : if (r < 0)
107 0 : return r;
108 :
109 0 : sd_dhcp6_lease_reset_address_iter(lease);
110 :
111 0 : while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
112 : &lifetime_preferred,
113 : &lifetime_valid) >= 0) {
114 :
115 0 : r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery,
116 : &ip6_addr, &prefixlen);
117 0 : if (r < 0 && r != -EADDRNOTAVAIL) {
118 0 : log_link_warning(link, "Could not get prefix information: %s",
119 : strerror(-r));
120 0 : return r;
121 : }
122 :
123 0 : if (r == -EADDRNOTAVAIL)
124 0 : prefixlen = 128;
125 :
126 0 : r = dhcp6_address_update(link, &ip6_addr, prefixlen,
127 : lifetime_preferred, lifetime_valid);
128 0 : if (r < 0)
129 0 : return r;
130 : }
131 :
132 0 : return 0;
133 : }
134 :
135 0 : static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
136 : int r;
137 0 : Link *link = userdata;
138 :
139 0 : assert(link);
140 0 : assert(link->network);
141 0 : assert(link->manager);
142 :
143 0 : if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
144 0 : return;
145 :
146 0 : switch(event) {
147 : case DHCP6_EVENT_STOP:
148 : case DHCP6_EVENT_RESEND_EXPIRE:
149 : case DHCP6_EVENT_RETRANS_MAX:
150 0 : log_link_debug(link, "DHCPv6 event %d", event);
151 0 : break;
152 :
153 : case DHCP6_EVENT_IP_ACQUIRE:
154 0 : r = dhcp6_lease_address_acquired(client, link);
155 0 : if (r < 0) {
156 0 : link_enter_failed(link);
157 0 : return;
158 : }
159 :
160 : /* fall through */
161 : case DHCP6_EVENT_INFORMATION_REQUEST:
162 0 : r = dhcp6_lease_information_acquired(client, link);
163 0 : if (r < 0) {
164 0 : link_enter_failed(link);
165 0 : return;
166 : }
167 :
168 0 : break;
169 :
170 : default:
171 0 : if (event < 0)
172 0 : log_link_warning(link, "DHCPv6 error: %s",
173 : strerror(-event));
174 : else
175 0 : log_link_warning(link, "DHCPv6 unknown event: %d",
176 : event);
177 0 : return;
178 : }
179 : }
180 :
181 0 : static int dhcp6_configure(Link *link, int event) {
182 : int r;
183 : bool information_request;
184 :
185 0 : assert_return(link, -EINVAL);
186 :
187 0 : if (link->dhcp6_client) {
188 0 : if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED)
189 0 : return 0;
190 :
191 0 : r = sd_dhcp6_client_get_information_request(link->dhcp6_client,
192 : &information_request);
193 0 : if (r < 0) {
194 0 : log_link_warning(link, "Could not get DHCPv6 Information request setting: %s",
195 : strerror(-r));
196 0 : link->dhcp6_client =
197 0 : sd_dhcp6_client_unref(link->dhcp6_client);
198 0 : return r;
199 : }
200 :
201 0 : if (!information_request)
202 0 : return r;
203 :
204 0 : r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
205 : false);
206 0 : if (r < 0) {
207 0 : log_link_warning(link, "Could not unset DHCPv6 Information request: %s",
208 : strerror(-r));
209 0 : link->dhcp6_client =
210 0 : sd_dhcp6_client_unref(link->dhcp6_client);
211 0 : return r;
212 : }
213 :
214 0 : r = sd_dhcp6_client_start(link->dhcp6_client);
215 0 : if (r < 0) {
216 0 : log_link_warning(link, "Could not restart DHCPv6 after enabling Information request: %s",
217 : strerror(-r));
218 0 : link->dhcp6_client =
219 0 : sd_dhcp6_client_unref(link->dhcp6_client);
220 0 : return r;
221 : }
222 :
223 0 : return r;
224 : }
225 :
226 0 : r = sd_dhcp6_client_new(&link->dhcp6_client);
227 0 : if (r < 0)
228 0 : return r;
229 :
230 0 : r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
231 0 : if (r < 0) {
232 0 : link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
233 0 : return r;
234 : }
235 :
236 0 : r = sd_dhcp6_client_set_mac(link->dhcp6_client,
237 0 : (const uint8_t *) &link->mac,
238 : sizeof (link->mac), ARPHRD_ETHER);
239 0 : if (r < 0) {
240 0 : link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
241 0 : return r;
242 : }
243 :
244 0 : r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
245 0 : if (r < 0) {
246 0 : link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
247 0 : return r;
248 : }
249 :
250 0 : r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
251 : link);
252 0 : if (r < 0) {
253 0 : link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
254 0 : return r;
255 : }
256 :
257 0 : if (event == ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER) {
258 0 : r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
259 : true);
260 0 : if (r < 0) {
261 0 : link->dhcp6_client =
262 0 : sd_dhcp6_client_unref(link->dhcp6_client);
263 0 : return r;
264 : }
265 : }
266 :
267 0 : r = sd_dhcp6_client_start(link->dhcp6_client);
268 0 : if (r < 0)
269 0 : link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
270 :
271 0 : return r;
272 : }
273 :
274 0 : static int dhcp6_prefix_expired(Link *link) {
275 : int r;
276 : sd_dhcp6_lease *lease;
277 : struct in6_addr *expired_prefix, ip6_addr;
278 : uint8_t expired_prefixlen;
279 : uint32_t lifetime_preferred, lifetime_valid;
280 :
281 0 : r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
282 : &expired_prefix, &expired_prefixlen);
283 0 : if (r < 0)
284 0 : return r;
285 :
286 0 : r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
287 0 : if (r < 0)
288 0 : return r;
289 :
290 0 : log_link_info(link, "IPv6 prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d expired",
291 : SD_ICMP6_ADDRESS_FORMAT_VAL(*expired_prefix),
292 : expired_prefixlen);
293 :
294 0 : sd_dhcp6_lease_reset_address_iter(lease);
295 :
296 0 : while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
297 : &lifetime_preferred,
298 : &lifetime_valid) >= 0) {
299 :
300 0 : r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
301 : &ip6_addr);
302 0 : if (r < 0)
303 0 : continue;
304 :
305 0 : log_link_info(link, "IPv6 prefix length updated "SD_ICMP6_ADDRESS_FORMAT_STR"/%d", SD_ICMP6_ADDRESS_FORMAT_VAL(ip6_addr), 128);
306 :
307 0 : dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid);
308 : }
309 :
310 0 : return 0;
311 : }
312 :
313 0 : static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
314 0 : Link *link = userdata;
315 :
316 0 : assert(link);
317 0 : assert(link->network);
318 0 : assert(link->manager);
319 :
320 0 : if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
321 0 : return;
322 :
323 0 : switch(event) {
324 : case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE:
325 0 : return;
326 :
327 : case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
328 : case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER:
329 : case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
330 0 : dhcp6_configure(link, event);
331 :
332 0 : break;
333 :
334 : case ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
335 0 : if (!link->rtnl_extended_attrs)
336 0 : dhcp6_prefix_expired(link);
337 :
338 0 : break;
339 :
340 : default:
341 0 : if (event < 0)
342 0 : log_link_warning(link, "ICMPv6 error: %s",
343 : strerror(-event));
344 : else
345 0 : log_link_warning(link, "ICMPv6 unknown event: %d",
346 : event);
347 :
348 0 : break;
349 : }
350 :
351 : }
352 :
353 0 : int icmp6_configure(Link *link) {
354 : int r;
355 :
356 0 : assert_return(link, -EINVAL);
357 :
358 0 : r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
359 0 : if (r < 0)
360 0 : return r;
361 :
362 0 : r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0);
363 0 : if (r < 0)
364 0 : return r;
365 :
366 0 : r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac);
367 0 : if (r < 0)
368 0 : return r;
369 :
370 0 : r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex);
371 0 : if (r < 0)
372 0 : return r;
373 :
374 0 : r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
375 : icmp6_router_handler, link);
376 :
377 0 : return r;
378 : }
|