1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
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.
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.
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/>.
21 #include <netinet/ether.h>
24 #include "sd-dhcp6-client.h"
26 #include "hostname-util.h"
27 #include "network-internal.h"
28 #include "networkd-link.h"
29 #include "networkd-manager.h"
31 static int dhcp6_lease_address_acquired(sd_dhcp6_client
*client
, Link
*link
);
33 static int dhcp6_lease_information_acquired(sd_dhcp6_client
*client
,
38 static int dhcp6_address_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
,
40 _cleanup_link_unref_ Link
*link
= userdata
;
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");
50 link
->rtnl_extended_attrs
= false;
51 dhcp6_lease_address_acquired(link
->dhcp6_client
, link
);
56 log_link_error_errno(link
, r
, "Could not set DHCPv6 address: %m");
58 link_enter_failed(link
);
61 manager_rtnl_process_address(rtnl
, m
, link
->manager
);
66 static int dhcp6_address_change(
68 struct in6_addr
*ip6_addr
,
69 uint32_t lifetime_preferred
,
70 uint32_t lifetime_valid
) {
72 _cleanup_address_free_ Address
*addr
= NULL
;
73 char buffer
[INET6_ADDRSTRLEN
];
76 r
= address_new(&addr
);
80 addr
->family
= AF_INET6
;
81 memcpy(&addr
->in_addr
.in6
, ip6_addr
, sizeof(*ip6_addr
));
83 addr
->flags
= IFA_F_NOPREFIXROUTE
;
84 addr
->prefixlen
= 128;
86 addr
->cinfo
.ifa_prefered
= lifetime_preferred
;
87 addr
->cinfo
.ifa_valid
= lifetime_valid
;
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
);
94 r
= address_configure(addr
, link
, dhcp6_address_handler
, true);
96 log_link_warning_errno(link
, r
, "Could not assign DHCPv6 address: %m");
101 static int dhcp6_lease_address_acquired(sd_dhcp6_client
*client
, Link
*link
) {
103 sd_dhcp6_lease
*lease
;
104 struct in6_addr ip6_addr
;
105 uint32_t lifetime_preferred
, lifetime_valid
;
107 r
= sd_dhcp6_client_get_lease(client
, &lease
);
111 sd_dhcp6_lease_reset_address_iter(lease
);
113 while (sd_dhcp6_lease_get_address(lease
, &ip6_addr
,
115 &lifetime_valid
) >= 0) {
117 r
= dhcp6_address_change(link
, &ip6_addr
, lifetime_preferred
, lifetime_valid
);
125 static void dhcp6_handler(sd_dhcp6_client
*client
, int event
, void *userdata
) {
127 Link
*link
= userdata
;
130 assert(link
->network
);
132 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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");
142 link
->dhcp6_configured
= false;
145 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
:
146 r
= dhcp6_lease_address_acquired(client
, link
);
148 link_enter_failed(link
);
153 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
:
154 r
= dhcp6_lease_information_acquired(client
, link
);
156 link_enter_failed(link
);
160 link
->dhcp6_configured
= true;
165 log_link_warning_errno(link
, event
, "DHCPv6 error: %m");
167 log_link_warning(link
, "DHCPv6 unknown event: %d", event
);
171 link_check_ready(link
);
174 int dhcp6_request_address(Link
*link
, int ir
) {
179 assert(link
->dhcp6_client
);
180 assert(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*)&link
->ipv6ll_address
) > 0);
182 r
= sd_dhcp6_client_is_running(link
->dhcp6_client
);
189 r
= sd_dhcp6_client_get_information_request(link
->dhcp6_client
, &inf_req
);
196 r
= sd_dhcp6_client_stop(link
->dhcp6_client
);
200 r
= sd_dhcp6_client_set_local_address(link
->dhcp6_client
, &link
->ipv6ll_address
);
205 r
= sd_dhcp6_client_set_information_request(link
->dhcp6_client
, ir
);
209 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
216 static int dhcp6_set_hostname(sd_dhcp6_client
*client
, Link
*link
) {
217 _cleanup_free_
char *hostname
= NULL
;
223 if (!link
->network
->dhcp_send_hostname
)
225 else if (link
->network
->dhcp_hostname
)
226 hn
= link
->network
->dhcp_hostname
;
228 r
= gethostname_strict(&hostname
);
229 if (r
< 0 && r
!= -ENXIO
) /* ENXIO: no hostname set or hostname is "localhost" */
235 return sd_dhcp6_client_set_fqdn(client
, hn
);
238 int dhcp6_configure(Link
*link
) {
239 sd_dhcp6_client
*client
= NULL
;
245 if (link
->dhcp6_client
)
248 r
= sd_dhcp6_client_new(&client
);
252 r
= sd_dhcp6_client_attach_event(client
, NULL
, 0);
256 r
= sd_dhcp6_client_set_mac(client
,
257 (const uint8_t *) &link
->mac
,
258 sizeof (link
->mac
), ARPHRD_ETHER
);
262 r
= sd_dhcp6_client_set_iaid(client
, link
->network
->iaid
);
266 duid
= link_duid(link
);
267 r
= sd_dhcp6_client_set_duid(client
,
269 duid
->raw_data_len
> 0 ? duid
->raw_data
: NULL
,
274 r
= dhcp6_set_hostname(client
, link
);
278 r
= sd_dhcp6_client_set_ifindex(client
, link
->ifindex
);
282 r
= sd_dhcp6_client_set_callback(client
, dhcp6_handler
, link
);
286 link
->dhcp6_client
= client
;
291 sd_dhcp6_client_unref(client
);