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