]>
Commit | Line | Data |
---|---|---|
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 |
28 | static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); |
29 | ||
c62c4628 PF |
30 | static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, |
31 | Link *link) { | |
32 | return 0; | |
33 | } | |
34 | ||
1c4baffc | 35 | static 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 |
63 | static 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 |
98 | static 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 | 122 | static 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 | 172 | int 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 |
214 | int 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 |
262 | error: |
263 | sd_dhcp6_client_unref(client); | |
5c79bd79 PF |
264 | return r; |
265 | } |