]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp6.c
network: beef up ipv6 RA support considerably
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
CommitLineData
5c79bd79
PF
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <netinet/ether.h>
21#include <linux/if.h>
22
5c79bd79
PF
23#include "sd-dhcp6-client.h"
24
07630cea 25#include "network-internal.h"
634f0f98 26#include "networkd.h"
07630cea 27
be3a09b7
PF
28static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
29
c62c4628
PF
30static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
31 Link *link) {
32 return 0;
33}
34
1c4baffc 35static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
c62c4628
PF
36 void *userdata) {
37 _cleanup_link_unref_ Link *link = userdata;
38 int r;
39
40 assert(link);
41
1c4baffc 42 r = sd_netlink_message_get_errno(m);
c62c4628 43 if (r < 0 && r != -EEXIST) {
be3a09b7
PF
44 if (link->rtnl_extended_attrs) {
45 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
46
47 link->rtnl_extended_attrs = false;
48 dhcp6_lease_address_acquired(link->dhcp6_client, link);
49
50 return 1;
51 }
52
e53fc357 53 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
c62c4628
PF
54
55 link_enter_failed(link);
56
57 } else if (r >= 0)
200a0868 58 manager_rtnl_process_address(rtnl, m, link->manager);
c62c4628
PF
59
60 return 1;
61}
62
1e7a0e21
LP
63static int dhcp6_address_change(
64 Link *link,
65 struct in6_addr *ip6_addr,
66 uint32_t lifetime_preferred,
67 uint32_t lifetime_valid) {
68
c62c4628 69 _cleanup_address_free_ Address *addr = NULL;
1e7a0e21
LP
70 char buffer[INET6_ADDRSTRLEN];
71 int r;
c62c4628 72
f0213e37 73 r = address_new(&addr);
c62c4628
PF
74 if (r < 0)
75 return r;
76
77 addr->family = AF_INET6;
78 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
851c9f82
PF
79
80 addr->flags = IFA_F_NOPREFIXROUTE;
6d8f6b0b 81 addr->prefixlen = 128;
c62c4628
PF
82
83 addr->cinfo.ifa_prefered = lifetime_preferred;
84 addr->cinfo.ifa_valid = lifetime_valid;
85
f2341e0a 86 log_link_info(link,
1e7a0e21
LP
87 "DHCPv6 address %s/%d timeout preferred %d valid %d",
88 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
f2341e0a 89 addr->prefixlen, lifetime_preferred, lifetime_valid);
c62c4628 90
66669078 91 r = address_configure(addr, link, dhcp6_address_handler, true);
c62c4628 92 if (r < 0)
f2341e0a 93 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
c62c4628
PF
94
95 return r;
96}
97
c62c4628
PF
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;
c62c4628
PF
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,
483d099e
ZJS
111 &lifetime_preferred,
112 &lifetime_valid) >= 0) {
c62c4628 113
6d8f6b0b 114 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
c62c4628
PF
115 if (r < 0)
116 return r;
117 }
118
119 return 0;
120}
121
5c79bd79 122static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
c62c4628 123 int r;
5c79bd79
PF
124 Link *link = userdata;
125
126 assert(link);
127 assert(link->network);
128 assert(link->manager);
129
130 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
131 return;
132
133 switch(event) {
10c9ce61
DH
134 case SD_DHCP6_CLIENT_EVENT_STOP:
135 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
136 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
3098562c
TG
137 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
138 log_link_warning(link, "DHCPv6 lease lost");
18d29550
PF
139
140 link->dhcp6_configured = false;
c62c4628
PF
141 break;
142
10c9ce61 143 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
c62c4628
PF
144 r = dhcp6_lease_address_acquired(client, link);
145 if (r < 0) {
146 link_enter_failed(link);
147 return;
148 }
149
150 /* fall through */
10c9ce61 151 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
c62c4628
PF
152 r = dhcp6_lease_information_acquired(client, link);
153 if (r < 0) {
154 link_enter_failed(link);
155 return;
156 }
5c79bd79 157
18d29550 158 link->dhcp6_configured = true;
5c79bd79
PF
159 break;
160
161 default:
162 if (event < 0)
e53fc357 163 log_link_warning_errno(link, event, "DHCPv6 error: %m");
5c79bd79 164 else
e53fc357 165 log_link_warning(link, "DHCPv6 unknown event: %d", event);
5c79bd79
PF
166 return;
167 }
18d29550 168
8012cd39 169 link_check_ready(link);
5c79bd79
PF
170}
171
720bec40 172int dhcp6_request_address(Link *link, int ir) {
7a695d8e
TG
173 int r, inf_req;
174 bool running;
5c79bd79 175
7a695d8e
TG
176 assert(link);
177 assert(link->dhcp6_client);
720bec40 178 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
85bd849f 179
7a695d8e
TG
180 r = sd_dhcp6_client_is_running(link->dhcp6_client);
181 if (r < 0)
182 return r;
183 else
184 running = !!r;
e6604041 185
7a695d8e 186 if (running) {
720bec40
TY
187 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
188 if (r < 0)
189 return r;
190
191 if (inf_req == ir)
192 return 0;
193
7a695d8e
TG
194 r = sd_dhcp6_client_stop(link->dhcp6_client);
195 if (r < 0)
196 return r;
720bec40
TY
197 } else {
198 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
199 if (r < 0)
200 return r;
7a695d8e 201 }
85bd849f 202
720bec40
TY
203 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
204 if (r < 0)
205 return r;
206
207 r = sd_dhcp6_client_start(link->dhcp6_client);
7a695d8e
TG
208 if (r < 0)
209 return r;
85bd849f 210
7a695d8e
TG
211 return 0;
212}
18d29550 213
7a695d8e
TG
214int dhcp6_configure(Link *link) {
215 sd_dhcp6_client *client = NULL;
216 int r;
8341a5c3 217 const DUID *duid;
7a695d8e
TG
218
219 assert(link);
220
62379e88
TG
221 if (link->dhcp6_client)
222 return 0;
223
7a695d8e
TG
224 r = sd_dhcp6_client_new(&client);
225 if (r < 0)
85bd849f 226 return r;
5c79bd79 227
7a695d8e 228 r = sd_dhcp6_client_attach_event(client, NULL, 0);
5c79bd79 229 if (r < 0)
e6604041 230 goto error;
5c79bd79 231
7a695d8e 232 r = sd_dhcp6_client_set_mac(client,
5c79bd79
PF
233 (const uint8_t *) &link->mac,
234 sizeof (link->mac), ARPHRD_ETHER);
e6604041
PF
235 if (r < 0)
236 goto error;
5c79bd79 237
413708d1
VK
238 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
239 if (r < 0)
240 goto error;
241
8341a5c3
ZJS
242 duid = link_duid(link);
243 r = sd_dhcp6_client_set_duid(client,
244 duid->type,
245 duid->raw_data_len > 0 ? duid->raw_data : NULL,
246 duid->raw_data_len);
413708d1
VK
247 if (r < 0)
248 goto error;
249
2f8e7633 250 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
e6604041
PF
251 if (r < 0)
252 goto error;
5c79bd79 253
7a695d8e 254 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
e6604041
PF
255 if (r < 0)
256 goto error;
5c79bd79 257
7a695d8e 258 link->dhcp6_client = client;
e6604041 259
7a695d8e 260 return 0;
5c79bd79 261
7a695d8e
TG
262error:
263 sd_dhcp6_client_unref(client);
5c79bd79
PF
264 return r;
265}