]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #3160 from htejun/cgroup-fixes-rev2
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
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
23 #include "sd-dhcp6-client.h"
24
25 #include "network-internal.h"
26 #include "networkd.h"
27
28 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
29
30 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
31 Link *link) {
32 return 0;
33 }
34
35 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
36 void *userdata) {
37 _cleanup_link_unref_ Link *link = userdata;
38 int r;
39
40 assert(link);
41
42 r = sd_netlink_message_get_errno(m);
43 if (r < 0 && r != -EEXIST) {
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
53 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
54
55 link_enter_failed(link);
56
57 } else if (r >= 0)
58 manager_rtnl_process_address(rtnl, m, link->manager);
59
60 return 1;
61 }
62
63 static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
64 uint32_t lifetime_preferred, uint32_t lifetime_valid) {
65 int r;
66 _cleanup_address_free_ Address *addr = NULL;
67
68 r = address_new(&addr);
69 if (r < 0)
70 return r;
71
72 addr->family = AF_INET6;
73 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
74
75 addr->flags = IFA_F_NOPREFIXROUTE;
76 addr->prefixlen = 128;
77
78 addr->cinfo.ifa_prefered = lifetime_preferred;
79 addr->cinfo.ifa_valid = lifetime_valid;
80
81 log_link_info(link,
82 "DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
83 SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
84 addr->prefixlen, lifetime_preferred, lifetime_valid);
85
86 r = address_configure(addr, link, dhcp6_address_handler, true);
87 if (r < 0)
88 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
89
90 return r;
91 }
92
93 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
94 int r;
95 sd_dhcp6_lease *lease;
96 struct in6_addr ip6_addr;
97 uint32_t lifetime_preferred, lifetime_valid;
98
99 r = sd_dhcp6_client_get_lease(client, &lease);
100 if (r < 0)
101 return r;
102
103 sd_dhcp6_lease_reset_address_iter(lease);
104
105 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
106 &lifetime_preferred,
107 &lifetime_valid) >= 0) {
108
109 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
110 if (r < 0)
111 return r;
112 }
113
114 return 0;
115 }
116
117 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
118 int r;
119 Link *link = userdata;
120
121 assert(link);
122 assert(link->network);
123 assert(link->manager);
124
125 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
126 return;
127
128 switch(event) {
129 case SD_DHCP6_CLIENT_EVENT_STOP:
130 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
131 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
132 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
133 log_link_warning(link, "DHCPv6 lease lost");
134
135 link->dhcp6_configured = false;
136 break;
137
138 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
139 r = dhcp6_lease_address_acquired(client, link);
140 if (r < 0) {
141 link_enter_failed(link);
142 return;
143 }
144
145 /* fall through */
146 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
147 r = dhcp6_lease_information_acquired(client, link);
148 if (r < 0) {
149 link_enter_failed(link);
150 return;
151 }
152
153 link->dhcp6_configured = true;
154 break;
155
156 default:
157 if (event < 0)
158 log_link_warning_errno(link, event, "DHCPv6 error: %m");
159 else
160 log_link_warning(link, "DHCPv6 unknown event: %d", event);
161 return;
162 }
163
164 link_check_ready(link);
165 }
166
167 int dhcp6_request_address(Link *link) {
168 int r, inf_req;
169 bool running;
170
171 assert(link);
172 assert(link->dhcp6_client);
173
174 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
175 if (r < 0)
176 return r;
177
178 if (!inf_req)
179 return 0;
180
181 r = sd_dhcp6_client_is_running(link->dhcp6_client);
182 if (r < 0)
183 return r;
184 else
185 running = !!r;
186
187 if (running) {
188 r = sd_dhcp6_client_stop(link->dhcp6_client);
189 if (r < 0)
190 return r;
191 }
192
193 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false);
194 if (r < 0)
195 return r;
196
197 if (running) {
198 r = sd_dhcp6_client_start(link->dhcp6_client);
199 if (r < 0)
200 return r;
201 }
202
203 return 0;
204 }
205
206 int dhcp6_configure(Link *link) {
207 sd_dhcp6_client *client = NULL;
208 int r;
209 const DUID *duid;
210
211 assert(link);
212
213 if (link->dhcp6_client)
214 return 0;
215
216 r = sd_dhcp6_client_new(&client);
217 if (r < 0)
218 return r;
219
220 r = sd_dhcp6_client_attach_event(client, NULL, 0);
221 if (r < 0)
222 goto error;
223
224 r = sd_dhcp6_client_set_information_request(client, true);
225 if (r < 0)
226 goto error;
227
228 r = sd_dhcp6_client_set_mac(client,
229 (const uint8_t *) &link->mac,
230 sizeof (link->mac), ARPHRD_ETHER);
231 if (r < 0)
232 goto error;
233
234 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
235 if (r < 0)
236 goto error;
237
238 duid = link_duid(link);
239 r = sd_dhcp6_client_set_duid(client,
240 duid->type,
241 duid->raw_data_len > 0 ? duid->raw_data : NULL,
242 duid->raw_data_len);
243 if (r < 0)
244 goto error;
245
246 r = sd_dhcp6_client_set_index(client, link->ifindex);
247 if (r < 0)
248 goto error;
249
250 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
251 if (r < 0)
252 goto error;
253
254 link->dhcp6_client = client;
255
256 return 0;
257
258 error:
259 sd_dhcp6_client_unref(client);
260 return r;
261 }