]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #7376 from keszybz/simplify-root-options
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netinet/ether.h>
22 #include <linux/if.h>
23
24 #include "sd-dhcp6-client.h"
25
26 #include "hostname-util.h"
27 #include "network-internal.h"
28 #include "networkd-link.h"
29 #include "networkd-manager.h"
30
31 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
32
33 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
34 Link *link) {
35 return 0;
36 }
37
38 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
39 void *userdata) {
40 _cleanup_link_unref_ Link *link = userdata;
41 int r;
42
43 assert(link);
44
45 r = sd_netlink_message_get_errno(m);
46 if (r < 0 && r != -EEXIST) {
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
56 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
57
58 link_enter_failed(link);
59
60 } else if (r >= 0)
61 manager_rtnl_process_address(rtnl, m, link->manager);
62
63 return 1;
64 }
65
66 static int dhcp6_address_change(
67 Link *link,
68 struct in6_addr *ip6_addr,
69 uint32_t lifetime_preferred,
70 uint32_t lifetime_valid) {
71
72 _cleanup_address_free_ Address *addr = NULL;
73 char buffer[INET6_ADDRSTRLEN];
74 int r;
75
76 r = address_new(&addr);
77 if (r < 0)
78 return r;
79
80 addr->family = AF_INET6;
81 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
82
83 addr->flags = IFA_F_NOPREFIXROUTE;
84 addr->prefixlen = 128;
85
86 addr->cinfo.ifa_prefered = lifetime_preferred;
87 addr->cinfo.ifa_valid = lifetime_valid;
88
89 log_link_info(link,
90 "DHCPv6 address %s/%d timeout preferred %d valid %d",
91 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
92 addr->prefixlen, lifetime_preferred, lifetime_valid);
93
94 r = address_configure(addr, link, dhcp6_address_handler, true);
95 if (r < 0)
96 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
97
98 return r;
99 }
100
101 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
102 int r;
103 sd_dhcp6_lease *lease;
104 struct in6_addr ip6_addr;
105 uint32_t lifetime_preferred, lifetime_valid;
106
107 r = sd_dhcp6_client_get_lease(client, &lease);
108 if (r < 0)
109 return r;
110
111 sd_dhcp6_lease_reset_address_iter(lease);
112
113 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
114 &lifetime_preferred,
115 &lifetime_valid) >= 0) {
116
117 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
118 if (r < 0)
119 return r;
120 }
121
122 return 0;
123 }
124
125 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
126 int r;
127 Link *link = userdata;
128
129 assert(link);
130 assert(link->network);
131
132 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
133 return;
134
135 switch(event) {
136 case SD_DHCP6_CLIENT_EVENT_STOP:
137 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
138 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
139 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
140 log_link_warning(link, "DHCPv6 lease lost");
141
142 link->dhcp6_configured = false;
143 break;
144
145 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
146 r = dhcp6_lease_address_acquired(client, link);
147 if (r < 0) {
148 link_enter_failed(link);
149 return;
150 }
151
152 /* fall through */
153 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
154 r = dhcp6_lease_information_acquired(client, link);
155 if (r < 0) {
156 link_enter_failed(link);
157 return;
158 }
159
160 link->dhcp6_configured = true;
161 break;
162
163 default:
164 if (event < 0)
165 log_link_warning_errno(link, event, "DHCPv6 error: %m");
166 else
167 log_link_warning(link, "DHCPv6 unknown event: %d", event);
168 return;
169 }
170
171 link_check_ready(link);
172 }
173
174 int dhcp6_request_address(Link *link, int ir) {
175 int r, inf_req;
176 bool running;
177
178 assert(link);
179 assert(link->dhcp6_client);
180 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
181
182 r = sd_dhcp6_client_is_running(link->dhcp6_client);
183 if (r < 0)
184 return r;
185 else
186 running = !!r;
187
188 if (running) {
189 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
190 if (r < 0)
191 return r;
192
193 if (inf_req == ir)
194 return 0;
195
196 r = sd_dhcp6_client_stop(link->dhcp6_client);
197 if (r < 0)
198 return r;
199 } else {
200 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
201 if (r < 0)
202 return r;
203 }
204
205 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
206 if (r < 0)
207 return r;
208
209 r = sd_dhcp6_client_start(link->dhcp6_client);
210 if (r < 0)
211 return r;
212
213 return 0;
214 }
215
216 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
217 _cleanup_free_ char *hostname = NULL;
218 const char *hn;
219 int r;
220
221 assert(link);
222
223 if (!link->network->dhcp_send_hostname)
224 hn = NULL;
225 else if (link->network->dhcp_hostname)
226 hn = link->network->dhcp_hostname;
227 else {
228 r = gethostname_strict(&hostname);
229 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
230 return r;
231
232 hn = hostname;
233 }
234
235 return sd_dhcp6_client_set_fqdn(client, hn);
236 }
237
238 int dhcp6_configure(Link *link) {
239 sd_dhcp6_client *client = NULL;
240 int r;
241 const DUID *duid;
242
243 assert(link);
244
245 if (link->dhcp6_client)
246 return 0;
247
248 r = sd_dhcp6_client_new(&client);
249 if (r < 0)
250 return r;
251
252 r = sd_dhcp6_client_attach_event(client, NULL, 0);
253 if (r < 0)
254 goto error;
255
256 r = sd_dhcp6_client_set_mac(client,
257 (const uint8_t *) &link->mac,
258 sizeof (link->mac), ARPHRD_ETHER);
259 if (r < 0)
260 goto error;
261
262 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
263 if (r < 0)
264 goto error;
265
266 duid = link_duid(link);
267 r = sd_dhcp6_client_set_duid(client,
268 duid->type,
269 duid->raw_data_len > 0 ? duid->raw_data : NULL,
270 duid->raw_data_len);
271 if (r < 0)
272 goto error;
273
274 r = dhcp6_set_hostname(client, link);
275 if (r < 0)
276 goto error;
277
278 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
279 if (r < 0)
280 goto error;
281
282 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
283 if (r < 0)
284 goto error;
285
286 link->dhcp6_client = client;
287
288 return 0;
289
290 error:
291 sd_dhcp6_client_unref(client);
292 return r;
293 }