]>
Commit | Line | Data |
---|---|---|
5c79bd79 PF |
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 | ||
3ad0c5d8 | 28 | #include "sd-ndisc.h" |
5c79bd79 PF |
29 | #include "sd-dhcp6-client.h" |
30 | ||
be3a09b7 PF |
31 | static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); |
32 | ||
c62c4628 PF |
33 | static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, |
34 | Link *link) { | |
35 | return 0; | |
36 | } | |
37 | ||
1c4baffc | 38 | static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, |
c62c4628 PF |
39 | void *userdata) { |
40 | _cleanup_link_unref_ Link *link = userdata; | |
41 | int r; | |
42 | ||
43 | assert(link); | |
44 | ||
1c4baffc | 45 | r = sd_netlink_message_get_errno(m); |
c62c4628 | 46 | if (r < 0 && r != -EEXIST) { |
be3a09b7 PF |
47 | if (link->rtnl_extended_attrs) { |
48 | log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism"); | |
49 | ||
50 | link->rtnl_extended_attrs = false; | |
51 | dhcp6_lease_address_acquired(link->dhcp6_client, link); | |
52 | ||
53 | return 1; | |
54 | } | |
55 | ||
e53fc357 | 56 | log_link_error_errno(link, r, "Could not set DHCPv6 address: %m"); |
c62c4628 PF |
57 | |
58 | link_enter_failed(link); | |
59 | ||
60 | } else if (r >= 0) | |
200a0868 | 61 | manager_rtnl_process_address(rtnl, m, link->manager); |
c62c4628 PF |
62 | |
63 | return 1; | |
64 | } | |
65 | ||
36c32f61 | 66 | static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr, |
c62c4628 PF |
67 | uint8_t prefixlen, uint32_t lifetime_preferred, |
68 | uint32_t lifetime_valid) { | |
69 | int r; | |
70 | _cleanup_address_free_ Address *addr = NULL; | |
71 | ||
f0213e37 | 72 | r = address_new(&addr); |
c62c4628 PF |
73 | if (r < 0) |
74 | return r; | |
75 | ||
76 | addr->family = AF_INET6; | |
77 | memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr)); | |
851c9f82 PF |
78 | |
79 | addr->flags = IFA_F_NOPREFIXROUTE; | |
be3a09b7 | 80 | addr->prefixlen = prefixlen; |
c62c4628 PF |
81 | |
82 | addr->cinfo.ifa_prefered = lifetime_preferred; | |
83 | addr->cinfo.ifa_valid = lifetime_valid; | |
84 | ||
f2341e0a | 85 | log_link_info(link, |
4d7b83da TG |
86 | "DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d", |
87 | SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6), | |
f2341e0a | 88 | addr->prefixlen, lifetime_preferred, lifetime_valid); |
c62c4628 | 89 | |
66669078 | 90 | r = address_configure(addr, link, dhcp6_address_handler, true); |
c62c4628 | 91 | if (r < 0) |
f2341e0a | 92 | log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); |
c62c4628 PF |
93 | |
94 | return r; | |
95 | } | |
96 | ||
c62c4628 PF |
97 | static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { |
98 | int r; | |
99 | sd_dhcp6_lease *lease; | |
100 | struct in6_addr ip6_addr; | |
101 | uint32_t lifetime_preferred, lifetime_valid; | |
102 | uint8_t prefixlen; | |
103 | ||
104 | r = sd_dhcp6_client_get_lease(client, &lease); | |
105 | if (r < 0) | |
106 | return r; | |
107 | ||
108 | sd_dhcp6_lease_reset_address_iter(lease); | |
109 | ||
110 | while (sd_dhcp6_lease_get_address(lease, &ip6_addr, | |
111 | &lifetime_preferred, | |
112 | &lifetime_valid) >= 0) { | |
113 | ||
de1e9928 | 114 | r = sd_ndisc_get_prefixlen(link->ndisc_router_discovery, |
c62c4628 PF |
115 | &ip6_addr, &prefixlen); |
116 | if (r < 0 && r != -EADDRNOTAVAIL) { | |
e53fc357 | 117 | log_link_warning_errno(link, r, "Could not get prefix information: %m"); |
c62c4628 PF |
118 | return r; |
119 | } | |
120 | ||
121 | if (r == -EADDRNOTAVAIL) | |
122 | prefixlen = 128; | |
123 | ||
36c32f61 | 124 | r = dhcp6_address_change(link, &ip6_addr, prefixlen, |
c62c4628 PF |
125 | lifetime_preferred, lifetime_valid); |
126 | if (r < 0) | |
127 | return r; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
5c79bd79 | 133 | static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { |
c62c4628 | 134 | int r; |
5c79bd79 PF |
135 | Link *link = userdata; |
136 | ||
137 | assert(link); | |
138 | assert(link->network); | |
139 | assert(link->manager); | |
140 | ||
141 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
142 | return; | |
143 | ||
144 | switch(event) { | |
10c9ce61 DH |
145 | case SD_DHCP6_CLIENT_EVENT_STOP: |
146 | case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: | |
147 | case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: | |
18d29550 PF |
148 | log_link_warning(link, "DHCPv6 lease lost"); |
149 | ||
150 | link->dhcp6_configured = false; | |
c62c4628 PF |
151 | break; |
152 | ||
10c9ce61 | 153 | case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: |
c62c4628 PF |
154 | r = dhcp6_lease_address_acquired(client, link); |
155 | if (r < 0) { | |
156 | link_enter_failed(link); | |
157 | return; | |
158 | } | |
159 | ||
160 | /* fall through */ | |
10c9ce61 | 161 | case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: |
c62c4628 PF |
162 | r = dhcp6_lease_information_acquired(client, link); |
163 | if (r < 0) { | |
164 | link_enter_failed(link); | |
165 | return; | |
166 | } | |
5c79bd79 | 167 | |
18d29550 | 168 | link->dhcp6_configured = true; |
5c79bd79 PF |
169 | break; |
170 | ||
171 | default: | |
172 | if (event < 0) | |
e53fc357 | 173 | log_link_warning_errno(link, event, "DHCPv6 error: %m"); |
5c79bd79 | 174 | else |
e53fc357 | 175 | log_link_warning(link, "DHCPv6 unknown event: %d", event); |
5c79bd79 PF |
176 | return; |
177 | } | |
18d29550 | 178 | |
8012cd39 | 179 | link_check_ready(link); |
5c79bd79 PF |
180 | } |
181 | ||
85bd849f | 182 | static int dhcp6_configure(Link *link, int event) { |
5c79bd79 | 183 | int r; |
85bd849f | 184 | bool information_request; |
5c79bd79 PF |
185 | |
186 | assert_return(link, -EINVAL); | |
4d7b83da TG |
187 | assert_return(IN_SET(event, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT, |
188 | SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER, | |
189 | SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED), -EINVAL); | |
5c79bd79 | 190 | |
18d29550 PF |
191 | link->dhcp6_configured = false; |
192 | ||
85bd849f | 193 | if (link->dhcp6_client) { |
85bd849f PF |
194 | r = sd_dhcp6_client_get_information_request(link->dhcp6_client, |
195 | &information_request); | |
196 | if (r < 0) { | |
e53fc357 | 197 | log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m"); |
e6604041 | 198 | goto error; |
85bd849f PF |
199 | } |
200 | ||
4d7b83da | 201 | if (information_request && event != SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER) { |
e6604041 PF |
202 | r = sd_dhcp6_client_stop(link->dhcp6_client); |
203 | if (r < 0) { | |
e53fc357 | 204 | log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m"); |
e6604041 PF |
205 | goto error; |
206 | } | |
207 | ||
208 | r = sd_dhcp6_client_set_information_request(link->dhcp6_client, | |
209 | false); | |
210 | if (r < 0) { | |
e53fc357 | 211 | log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m"); |
e6604041 PF |
212 | goto error; |
213 | } | |
85bd849f | 214 | |
85bd849f PF |
215 | } |
216 | ||
217 | r = sd_dhcp6_client_start(link->dhcp6_client); | |
e6604041 | 218 | if (r < 0 && r != -EALREADY) { |
e53fc357 | 219 | log_link_warning_errno(link, r, "Could not restart DHCPv6: %m"); |
e6604041 | 220 | goto error; |
85bd849f PF |
221 | } |
222 | ||
18d29550 PF |
223 | if (r == -EALREADY) |
224 | link->dhcp6_configured = true; | |
225 | ||
85bd849f PF |
226 | return r; |
227 | } | |
5c79bd79 PF |
228 | |
229 | r = sd_dhcp6_client_new(&link->dhcp6_client); | |
230 | if (r < 0) | |
e6604041 | 231 | goto error; |
5c79bd79 PF |
232 | |
233 | r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); | |
e6604041 PF |
234 | if (r < 0) |
235 | goto error; | |
5c79bd79 PF |
236 | |
237 | r = sd_dhcp6_client_set_mac(link->dhcp6_client, | |
238 | (const uint8_t *) &link->mac, | |
239 | sizeof (link->mac), ARPHRD_ETHER); | |
e6604041 PF |
240 | if (r < 0) |
241 | goto error; | |
5c79bd79 PF |
242 | |
243 | r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); | |
e6604041 PF |
244 | if (r < 0) |
245 | goto error; | |
5c79bd79 PF |
246 | |
247 | r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, | |
248 | link); | |
e6604041 PF |
249 | if (r < 0) |
250 | goto error; | |
5c79bd79 | 251 | |
4d7b83da | 252 | if (event == SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER) { |
85bd849f PF |
253 | r = sd_dhcp6_client_set_information_request(link->dhcp6_client, |
254 | true); | |
e6604041 PF |
255 | if (r < 0) |
256 | goto error; | |
85bd849f PF |
257 | } |
258 | ||
5c79bd79 PF |
259 | r = sd_dhcp6_client_start(link->dhcp6_client); |
260 | if (r < 0) | |
e6604041 PF |
261 | goto error; |
262 | ||
263 | return r; | |
5c79bd79 | 264 | |
e6604041 PF |
265 | error: |
266 | link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); | |
5c79bd79 PF |
267 | return r; |
268 | } | |
269 | ||
be3a09b7 PF |
270 | static int dhcp6_prefix_expired(Link *link) { |
271 | int r; | |
272 | sd_dhcp6_lease *lease; | |
273 | struct in6_addr *expired_prefix, ip6_addr; | |
274 | uint8_t expired_prefixlen; | |
275 | uint32_t lifetime_preferred, lifetime_valid; | |
276 | ||
de1e9928 | 277 | r = sd_ndisc_get_expired_prefix(link->ndisc_router_discovery, |
be3a09b7 PF |
278 | &expired_prefix, &expired_prefixlen); |
279 | if (r < 0) | |
280 | return r; | |
281 | ||
282 | r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease); | |
283 | if (r < 0) | |
284 | return r; | |
285 | ||
4d7b83da TG |
286 | log_link_info(link, "IPv6 prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d expired", |
287 | SD_NDISC_ADDRESS_FORMAT_VAL(*expired_prefix), | |
f2341e0a | 288 | expired_prefixlen); |
be3a09b7 PF |
289 | |
290 | sd_dhcp6_lease_reset_address_iter(lease); | |
291 | ||
292 | while (sd_dhcp6_lease_get_address(lease, &ip6_addr, | |
293 | &lifetime_preferred, | |
294 | &lifetime_valid) >= 0) { | |
295 | ||
4d7b83da | 296 | r = sd_ndisc_prefix_match(expired_prefix, expired_prefixlen, |
be3a09b7 PF |
297 | &ip6_addr); |
298 | if (r < 0) | |
299 | continue; | |
300 | ||
4d7b83da | 301 | log_link_info(link, "IPv6 prefix length updated "SD_NDISC_ADDRESS_FORMAT_STR"/%d", SD_NDISC_ADDRESS_FORMAT_VAL(ip6_addr), 128); |
be3a09b7 | 302 | |
36c32f61 | 303 | dhcp6_address_change(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid); |
be3a09b7 PF |
304 | } |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
de1e9928 | 309 | static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { |
5c79bd79 PF |
310 | Link *link = userdata; |
311 | ||
312 | assert(link); | |
313 | assert(link->network); | |
314 | assert(link->manager); | |
315 | ||
316 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
317 | return; | |
318 | ||
319 | switch(event) { | |
4d7b83da | 320 | case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: |
5c79bd79 PF |
321 | return; |
322 | ||
4d7b83da TG |
323 | case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: |
324 | case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER: | |
325 | case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED: | |
c62c4628 PF |
326 | dhcp6_configure(link, event); |
327 | ||
328 | break; | |
329 | ||
4d7b83da | 330 | case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED: |
be3a09b7 PF |
331 | if (!link->rtnl_extended_attrs) |
332 | dhcp6_prefix_expired(link); | |
333 | ||
334 | break; | |
335 | ||
5c79bd79 PF |
336 | default: |
337 | if (event < 0) | |
e53fc357 | 338 | log_link_warning_errno(link, event, "ICMPv6 error: %m"); |
5c79bd79 | 339 | else |
e53fc357 | 340 | log_link_warning(link, "ICMPv6 unknown event: %d", event); |
5c79bd79 | 341 | |
c62c4628 | 342 | break; |
5c79bd79 PF |
343 | } |
344 | ||
5c79bd79 PF |
345 | } |
346 | ||
de1e9928 | 347 | int ndisc_configure(Link *link) { |
5c79bd79 PF |
348 | int r; |
349 | ||
350 | assert_return(link, -EINVAL); | |
351 | ||
de1e9928 | 352 | r = sd_ndisc_new(&link->ndisc_router_discovery); |
5c79bd79 PF |
353 | if (r < 0) |
354 | return r; | |
355 | ||
de1e9928 | 356 | r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0); |
5c79bd79 PF |
357 | if (r < 0) |
358 | return r; | |
359 | ||
de1e9928 | 360 | r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac); |
5c79bd79 PF |
361 | if (r < 0) |
362 | return r; | |
363 | ||
de1e9928 | 364 | r = sd_ndisc_set_index(link->ndisc_router_discovery, link->ifindex); |
5c79bd79 PF |
365 | if (r < 0) |
366 | return r; | |
367 | ||
de1e9928 TG |
368 | r = sd_ndisc_set_callback(link->ndisc_router_discovery, |
369 | ndisc_router_handler, link); | |
5c79bd79 PF |
370 | |
371 | return r; | |
372 | } |