]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp4.c
DHCP DUID, IAID configuration options
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
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 "alloc-util.h"
24 #include "dhcp-lease-internal.h"
25 #include "hostname-util.h"
26 #include "network-internal.h"
27 #include "networkd-link.h"
28
29 static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
30 void *userdata) {
31 _cleanup_link_unref_ Link *link = userdata;
32 int r;
33
34 assert(link);
35 assert(link->dhcp4_messages > 0);
36
37 link->dhcp4_messages--;
38
39 r = sd_netlink_message_get_errno(m);
40 if (r < 0 && r != -EEXIST) {
41 log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
42 link_enter_failed(link);
43 }
44
45 if (link->dhcp4_messages == 0) {
46 link->dhcp4_configured = true;
47 link_check_ready(link);
48 }
49
50 return 1;
51 }
52
53 static int link_set_dhcp_routes(Link *link) {
54 struct in_addr gateway;
55 _cleanup_free_ sd_dhcp_route **static_routes = NULL;
56 int r, n, i;
57
58 assert(link);
59 assert(link->dhcp_lease);
60
61 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
62 if (r < 0 && r != -ENODATA)
63 return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
64
65 if (r >= 0) {
66 struct in_addr address;
67 _cleanup_route_free_ Route *route = NULL;
68 _cleanup_route_free_ Route *route_gw = NULL;
69
70 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
71 if (r < 0)
72 return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
73
74 r = route_new(&route);
75 if (r < 0)
76 return log_link_error_errno(link, r, "Could not allocate route: %m");
77
78 route->protocol = RTPROT_DHCP;
79
80 r = route_new(&route_gw);
81 if (r < 0)
82 return log_link_error_errno(link, r, "Could not allocate route: %m");
83
84 /* The dhcp netmask may mask out the gateway. Add an explicit
85 * route for the gw host so that we can route no matter the
86 * netmask or existing kernel route tables. */
87 route_gw->family = AF_INET;
88 route_gw->dst.in = gateway;
89 route_gw->dst_prefixlen = 32;
90 route_gw->prefsrc.in = address;
91 route_gw->scope = RT_SCOPE_LINK;
92 route_gw->protocol = RTPROT_DHCP;
93 route_gw->priority = link->network->dhcp_route_metric;
94
95 r = route_configure(route_gw, link, &dhcp4_route_handler);
96 if (r < 0)
97 return log_link_warning_errno(link, r, "Could not set host route: %m");
98
99 link->dhcp4_messages++;
100
101 route->family = AF_INET;
102 route->gw.in = gateway;
103 route->prefsrc.in = address;
104 route->priority = link->network->dhcp_route_metric;
105
106 r = route_configure(route, link, &dhcp4_route_handler);
107 if (r < 0) {
108 log_link_warning_errno(link, r, "Could not set routes: %m");
109 link_enter_failed(link);
110 return r;
111 }
112
113 link->dhcp4_messages++;
114 }
115
116 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
117 if (n == -ENODATA)
118 return 0;
119 if (n < 0)
120 return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");
121
122 for (i = 0; i < n; i++) {
123 _cleanup_route_free_ Route *route = NULL;
124
125 r = route_new(&route);
126 if (r < 0)
127 return log_link_error_errno(link, r, "Could not allocate route: %m");
128
129 route->family = AF_INET;
130 route->protocol = RTPROT_DHCP;
131 assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
132 assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
133 assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
134 route->priority = link->network->dhcp_route_metric;
135
136 r = route_configure(route, link, &dhcp4_route_handler);
137 if (r < 0)
138 return log_link_warning_errno(link, r, "Could not set host route: %m");
139
140 link->dhcp4_messages++;
141 }
142
143 return 0;
144 }
145
146 static int dhcp_lease_lost(Link *link) {
147 _cleanup_address_free_ Address *address = NULL;
148 struct in_addr addr;
149 struct in_addr netmask;
150 struct in_addr gateway;
151 unsigned prefixlen = 0;
152 int r;
153
154 assert(link);
155 assert(link->dhcp_lease);
156
157 log_link_warning(link, "DHCP lease lost");
158
159 if (link->network->dhcp_use_routes) {
160 _cleanup_free_ sd_dhcp_route **routes = NULL;
161 int n, i;
162
163 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
164 if (n >= 0) {
165 for (i = 0; i < n; i++) {
166 _cleanup_route_free_ Route *route = NULL;
167
168 r = route_new(&route);
169 if (r >= 0) {
170 route->family = AF_INET;
171 assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
172 assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
173 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
174
175 route_remove(route, link,
176 &link_route_remove_handler);
177 }
178 }
179 }
180 }
181
182 r = address_new(&address);
183 if (r >= 0) {
184 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
185 if (r >= 0) {
186 _cleanup_route_free_ Route *route_gw = NULL;
187 _cleanup_route_free_ Route *route = NULL;
188
189 r = route_new(&route_gw);
190 if (r >= 0) {
191 route_gw->family = AF_INET;
192 route_gw->dst.in = gateway;
193 route_gw->dst_prefixlen = 32;
194 route_gw->scope = RT_SCOPE_LINK;
195
196 route_remove(route_gw, link,
197 &link_route_remove_handler);
198 }
199
200 r = route_new(&route);
201 if (r >= 0) {
202 route->family = AF_INET;
203 route->gw.in = gateway;
204
205 route_remove(route, link,
206 &link_route_remove_handler);
207 }
208 }
209
210 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
211 if (r >= 0) {
212 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
213 if (r >= 0)
214 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
215
216 address->family = AF_INET;
217 address->in_addr.in = addr;
218 address->prefixlen = prefixlen;
219
220 address_remove(address, link, &link_address_remove_handler);
221 }
222 }
223
224 if (link->network->dhcp_use_mtu) {
225 uint16_t mtu;
226
227 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
228 if (r >= 0 && link->original_mtu != mtu) {
229 r = link_set_mtu(link, link->original_mtu);
230 if (r < 0) {
231 log_link_warning(link,
232 "DHCP error: could not reset MTU");
233 link_enter_failed(link);
234 return r;
235 }
236 }
237 }
238
239 if (link->network->dhcp_use_hostname) {
240 const char *hostname = NULL;
241
242 if (link->network->dhcp_hostname)
243 hostname = link->network->dhcp_hostname;
244 else
245 (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
246
247 if (hostname) {
248 /* If a hostname was set due to the lease, then unset it now. */
249 r = link_set_hostname(link, NULL);
250 if (r < 0)
251 log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
252 }
253 }
254
255 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
256 link_dirty(link);
257 link->dhcp4_configured = false;
258
259 return 0;
260 }
261
262 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
263 void *userdata) {
264 _cleanup_link_unref_ Link *link = userdata;
265 int r;
266
267 assert(link);
268
269 r = sd_netlink_message_get_errno(m);
270 if (r < 0 && r != -EEXIST) {
271 log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
272 link_enter_failed(link);
273 } else if (r >= 0)
274 manager_rtnl_process_address(rtnl, m, link->manager);
275
276 link_set_dhcp_routes(link);
277
278 return 1;
279 }
280
281 static int dhcp4_update_address(Link *link,
282 struct in_addr *address,
283 struct in_addr *netmask,
284 uint32_t lifetime) {
285 _cleanup_address_free_ Address *addr = NULL;
286 unsigned prefixlen;
287 int r;
288
289 assert(address);
290 assert(netmask);
291 assert(lifetime);
292
293 prefixlen = in_addr_netmask_to_prefixlen(netmask);
294
295 r = address_new(&addr);
296 if (r < 0)
297 return r;
298
299 addr->family = AF_INET;
300 addr->in_addr.in.s_addr = address->s_addr;
301 addr->cinfo.ifa_prefered = lifetime;
302 addr->cinfo.ifa_valid = lifetime;
303 addr->prefixlen = prefixlen;
304 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
305
306 /* allow reusing an existing address and simply update its lifetime
307 * in case it already exists */
308 r = address_configure(addr, link, &dhcp4_address_handler, true);
309 if (r < 0)
310 return r;
311
312 return 0;
313 }
314
315 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
316 sd_dhcp_lease *lease;
317 struct in_addr address;
318 struct in_addr netmask;
319 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
320 int r;
321
322 assert(link);
323 assert(client);
324 assert(link->network);
325
326 r = sd_dhcp_client_get_lease(client, &lease);
327 if (r < 0)
328 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
329
330 sd_dhcp_lease_unref(link->dhcp_lease);
331 link->dhcp4_configured = false;
332 link->dhcp_lease = sd_dhcp_lease_ref(lease);
333 link_dirty(link);
334
335 r = sd_dhcp_lease_get_address(lease, &address);
336 if (r < 0)
337 return log_link_warning_errno(link, r, "DHCP error: no address: %m");
338
339 r = sd_dhcp_lease_get_netmask(lease, &netmask);
340 if (r < 0)
341 return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
342
343 if (!link->network->dhcp_critical) {
344 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
345 if (r < 0)
346 return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
347 }
348
349 r = dhcp4_update_address(link, &address, &netmask, lifetime);
350 if (r < 0) {
351 log_link_warning_errno(link, r, "Could not update IP address: %m");
352 link_enter_failed(link);
353 return r;
354 }
355
356 return 0;
357 }
358
359 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
360 sd_dhcp_lease *lease;
361 struct in_addr address;
362 struct in_addr netmask;
363 struct in_addr gateway;
364 unsigned prefixlen;
365 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
366 int r;
367
368 assert(client);
369 assert(link);
370
371 r = sd_dhcp_client_get_lease(client, &lease);
372 if (r < 0)
373 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
374
375 r = sd_dhcp_lease_get_address(lease, &address);
376 if (r < 0)
377 return log_link_error_errno(link, r, "DHCP error: No address: %m");
378
379 r = sd_dhcp_lease_get_netmask(lease, &netmask);
380 if (r < 0)
381 return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
382
383 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
384
385 r = sd_dhcp_lease_get_router(lease, &gateway);
386 if (r < 0 && r != -ENODATA)
387 return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
388
389 if (r >= 0)
390 log_struct(LOG_INFO,
391 LOG_LINK_INTERFACE(link),
392 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
393 ADDRESS_FMT_VAL(address),
394 prefixlen,
395 ADDRESS_FMT_VAL(gateway)),
396 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
397 "PREFIXLEN=%u", prefixlen,
398 "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway),
399 NULL);
400 else
401 log_struct(LOG_INFO,
402 LOG_LINK_INTERFACE(link),
403 LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
404 ADDRESS_FMT_VAL(address),
405 prefixlen),
406 "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
407 "PREFIXLEN=%u", prefixlen,
408 NULL);
409
410 link->dhcp_lease = sd_dhcp_lease_ref(lease);
411 link_dirty(link);
412
413 if (link->network->dhcp_use_mtu) {
414 uint16_t mtu;
415
416 r = sd_dhcp_lease_get_mtu(lease, &mtu);
417 if (r >= 0) {
418 r = link_set_mtu(link, mtu);
419 if (r < 0)
420 log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu);
421 }
422 }
423
424 if (link->network->dhcp_use_hostname) {
425 const char *hostname = NULL;
426
427 if (link->network->dhcp_hostname)
428 hostname = link->network->dhcp_hostname;
429 else
430 (void) sd_dhcp_lease_get_hostname(lease, &hostname);
431
432 if (hostname) {
433 r = link_set_hostname(link, hostname);
434 if (r < 0)
435 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
436 }
437 }
438
439 if (link->network->dhcp_use_timezone) {
440 const char *tz = NULL;
441
442 (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
443
444 if (tz) {
445 r = link_set_timezone(link, tz);
446 if (r < 0)
447 log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
448 }
449 }
450
451 if (!link->network->dhcp_critical) {
452 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
453 if (r < 0) {
454 log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
455 return r;
456 }
457 }
458
459 r = dhcp4_update_address(link, &address, &netmask, lifetime);
460 if (r < 0) {
461 log_link_warning_errno(link, r, "Could not update IP address: %m");
462 link_enter_failed(link);
463 return r;
464 }
465
466 return 0;
467 }
468 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
469 Link *link = userdata;
470 int r = 0;
471
472 assert(link);
473 assert(link->network);
474 assert(link->manager);
475
476 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
477 return;
478
479 switch (event) {
480 case SD_DHCP_CLIENT_EVENT_EXPIRED:
481 case SD_DHCP_CLIENT_EVENT_STOP:
482 case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
483 if (link->network->dhcp_critical) {
484 log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
485 return;
486 }
487
488 if (link->dhcp_lease) {
489 r = dhcp_lease_lost(link);
490 if (r < 0) {
491 link_enter_failed(link);
492 return;
493 }
494 }
495
496 if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
497 r = dhcp_lease_acquired(client, link);
498 if (r < 0) {
499 link_enter_failed(link);
500 return;
501 }
502 }
503
504 break;
505 case SD_DHCP_CLIENT_EVENT_RENEW:
506 r = dhcp_lease_renew(client, link);
507 if (r < 0) {
508 link_enter_failed(link);
509 return;
510 }
511 break;
512 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
513 r = dhcp_lease_acquired(client, link);
514 if (r < 0) {
515 link_enter_failed(link);
516 return;
517 }
518 break;
519 default:
520 if (event < 0)
521 log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
522 else
523 log_link_warning(link, "DHCP unknown event: %i", event);
524 break;
525 }
526
527 return;
528 }
529
530 int dhcp4_configure(Link *link) {
531 int r;
532
533 assert(link);
534 assert(link->network);
535 assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
536
537 if (!link->dhcp_client) {
538 r = sd_dhcp_client_new(&link->dhcp_client);
539 if (r < 0)
540 return r;
541 }
542
543 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
544 if (r < 0)
545 return r;
546
547 r = sd_dhcp_client_set_mac(link->dhcp_client,
548 (const uint8_t *) &link->mac,
549 sizeof (link->mac), ARPHRD_ETHER);
550 if (r < 0)
551 return r;
552
553 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
554 if (r < 0)
555 return r;
556
557 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
558 if (r < 0)
559 return r;
560
561 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
562 link->network->dhcp_broadcast);
563 if (r < 0)
564 return r;
565
566 if (link->mtu) {
567 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
568 if (r < 0)
569 return r;
570 }
571
572 if (link->network->dhcp_use_mtu) {
573 r = sd_dhcp_client_set_request_option(link->dhcp_client,
574 SD_DHCP_OPTION_INTERFACE_MTU);
575 if (r < 0)
576 return r;
577 }
578
579 if (link->network->dhcp_use_routes) {
580 r = sd_dhcp_client_set_request_option(link->dhcp_client,
581 SD_DHCP_OPTION_STATIC_ROUTE);
582 if (r < 0)
583 return r;
584 r = sd_dhcp_client_set_request_option(link->dhcp_client,
585 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
586 if (r < 0)
587 return r;
588 }
589
590 /* Always acquire the timezone and NTP */
591 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
592 if (r < 0)
593 return r;
594
595 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
596 if (r < 0)
597 return r;
598
599 if (link->network->dhcp_send_hostname) {
600 _cleanup_free_ char *hostname = NULL;
601 const char *hn = NULL;
602
603 if (!link->network->dhcp_hostname) {
604 hostname = gethostname_malloc();
605 if (!hostname)
606 return -ENOMEM;
607
608 hn = hostname;
609 } else
610 hn = link->network->dhcp_hostname;
611
612 if (!is_localhost(hn)) {
613 r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
614 if (r < 0)
615 return r;
616 }
617 }
618
619 if (link->network->dhcp_vendor_class_identifier) {
620 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
621 link->network->dhcp_vendor_class_identifier);
622 if (r < 0)
623 return r;
624 }
625
626 switch (link->network->dhcp_client_identifier) {
627 case DHCP_CLIENT_ID_DUID:
628 /* If configured, apply user specified DUID and/or IAID */
629 if (link->network->duid_type != _DUID_TYPE_INVALID)
630 r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
631 link->network->iaid,
632 link->network->dhcp_duid_type,
633 link->network->dhcp_duid,
634 link->network->dhcp_duid_len);
635 else
636 r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
637 link->network->iaid,
638 link->manager->dhcp_duid_type,
639 link->manager->dhcp_duid,
640 link->manager->dhcp_duid_len);
641 if (r < 0)
642 return r;
643 break;
644 case DHCP_CLIENT_ID_MAC:
645 r = sd_dhcp_client_set_client_id(link->dhcp_client,
646 ARPHRD_ETHER,
647 (const uint8_t *) &link->mac,
648 sizeof (link->mac));
649 if (r < 0)
650 return r;
651 break;
652 default:
653 assert_not_reached("Unknown client identifier type.");
654 }
655
656 return 0;
657 }