]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp4.c
networkd: Make DHCP client ID creation configurable
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
CommitLineData
3c9b8860
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <netinet/ether.h>
23#include <linux/if.h>
24
25#include "networkd-link.h"
26#include "network-internal.h"
27#include "dhcp-lease-internal.h"
28
29static int dhcp4_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
30 void *userdata) {
31 _cleanup_link_unref_ Link *link = userdata;
32 int r;
33
34 assert(link);
35 assert(link->dhcp4_messages);
36
37 link->dhcp4_messages --;
38
39 r = sd_rtnl_message_get_errno(m);
40 if (r < 0 && r != -EEXIST) {
79008bdd 41 log_link_error(link, "could not set DHCPv4 route: %s",
3c9b8860
TG
42 strerror(-r));
43 link_enter_failed(link);
44 }
45
46 if (!link->dhcp4_messages) {
47 link->dhcp4_configured = true;
48 link_client_handler(link);
49 }
50
51 return 1;
52}
53
54static int link_set_dhcp_routes(Link *link) {
55 struct in_addr gateway;
56 struct sd_dhcp_route *static_routes;
57 int r, n, i;
58
59 assert(link);
60 assert(link->dhcp_lease);
61
62 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
63 if (r < 0 && r != -ENOENT) {
79008bdd 64 log_link_warning(link,
3c9b8860
TG
65 "DHCP error: could not get gateway: %s",
66 strerror(-r));
67 return r;
68 }
69 if (r >= 0) {
46b0c76e 70 struct in_addr address;
3c9b8860
TG
71 _cleanup_route_free_ Route *route = NULL;
72 _cleanup_route_free_ Route *route_gw = NULL;
73
46b0c76e
ERB
74 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
75 if (r < 0) {
79008bdd 76 log_link_warning(link,
46b0c76e
ERB
77 "DHCP error: could not get address: %s",
78 strerror(-r));
79 return r;
80 }
81
3c9b8860
TG
82 r = route_new_dynamic(&route, RTPROT_DHCP);
83 if (r < 0) {
79008bdd 84 log_link_error(link,
3c9b8860
TG
85 "Could not allocate route: %s",
86 strerror(-r));
87 return r;
88 }
89
90 r = route_new_dynamic(&route_gw, RTPROT_DHCP);
91 if (r < 0) {
79008bdd 92 log_link_error(link,
3c9b8860
TG
93 "Could not allocate route: %s",
94 strerror(-r));
95 return r;
96 }
97
98 /* The dhcp netmask may mask out the gateway. Add an explicit
99 * route for the gw host so that we can route no matter the
100 * netmask or existing kernel route tables. */
101 route_gw->family = AF_INET;
102 route_gw->dst_addr.in = gateway;
103 route_gw->dst_prefixlen = 32;
46b0c76e 104 route_gw->prefsrc_addr.in = address;
3c9b8860 105 route_gw->scope = RT_SCOPE_LINK;
84b5b79a 106 route_gw->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
107
108 r = route_configure(route_gw, link, &dhcp4_route_handler);
109 if (r < 0) {
79008bdd 110 log_link_warning(link,
3c9b8860
TG
111 "could not set host route: %s",
112 strerror(-r));
113 return r;
114 }
115
116 link->dhcp4_messages ++;
117
118 route->family = AF_INET;
119 route->in_addr.in = gateway;
46b0c76e 120 route->prefsrc_addr.in = address;
84b5b79a 121 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
122
123 r = route_configure(route, link, &dhcp4_route_handler);
124 if (r < 0) {
79008bdd 125 log_link_warning(link,
3c9b8860
TG
126 "could not set routes: %s",
127 strerror(-r));
128 link_enter_failed(link);
129 return r;
130 }
131
132 link->dhcp4_messages ++;
133 }
134
135 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
136 if (n == -ENOENT)
137 return 0;
138 if (n < 0) {
79008bdd 139 log_link_warning(link,
3c9b8860
TG
140 "DHCP error: could not get routes: %s",
141 strerror(-n));
142
143 return n;
144 }
145
146 for (i = 0; i < n; i++) {
147 _cleanup_route_free_ Route *route = NULL;
148
149 r = route_new_dynamic(&route, RTPROT_DHCP);
150 if (r < 0) {
79008bdd 151 log_link_error(link, "Could not allocate route: %s",
3c9b8860
TG
152 strerror(-r));
153 return r;
154 }
155
156 route->family = AF_INET;
157 route->in_addr.in = static_routes[i].gw_addr;
158 route->dst_addr.in = static_routes[i].dst_addr;
159 route->dst_prefixlen = static_routes[i].dst_prefixlen;
84b5b79a 160 route->metrics = link->network->dhcp_route_metric;
3c9b8860
TG
161
162 r = route_configure(route, link, &dhcp4_route_handler);
163 if (r < 0) {
79008bdd 164 log_link_warning(link,
3c9b8860
TG
165 "could not set host route: %s",
166 strerror(-r));
167 return r;
168 }
169
170 link->dhcp4_messages ++;
171 }
172
173 return 0;
174}
175
176static int dhcp_lease_lost(Link *link) {
177 _cleanup_address_free_ Address *address = NULL;
178 struct in_addr addr;
179 struct in_addr netmask;
180 struct in_addr gateway;
f414a269 181 unsigned prefixlen = 0;
3c9b8860
TG
182 int r;
183
184 assert(link);
185 assert(link->dhcp_lease);
186
79008bdd 187 log_link_warning(link, "DHCP lease lost");
3c9b8860
TG
188
189 if (link->network->dhcp_routes) {
190 struct sd_dhcp_route *routes;
191 int n, i;
192
193 n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
194 if (n >= 0) {
195 for (i = 0; i < n; i++) {
196 _cleanup_route_free_ Route *route = NULL;
197
198 r = route_new_dynamic(&route, RTPROT_UNSPEC);
199 if (r >= 0) {
200 route->family = AF_INET;
201 route->in_addr.in = routes[i].gw_addr;
202 route->dst_addr.in = routes[i].dst_addr;
203 route->dst_prefixlen = routes[i].dst_prefixlen;
204
205 route_drop(route, link,
206 &link_route_drop_handler);
207 }
208 }
209 }
210 }
211
212 r = address_new_dynamic(&address);
213 if (r >= 0) {
214 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
215 if (r >= 0) {
216 _cleanup_route_free_ Route *route_gw = NULL;
217 _cleanup_route_free_ Route *route = NULL;
218
219 r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
220 if (r >= 0) {
221 route_gw->family = AF_INET;
222 route_gw->dst_addr.in = gateway;
223 route_gw->dst_prefixlen = 32;
224 route_gw->scope = RT_SCOPE_LINK;
225
226 route_drop(route_gw, link,
227 &link_route_drop_handler);
228 }
229
230 r = route_new_dynamic(&route, RTPROT_UNSPEC);
231 if (r >= 0) {
232 route->family = AF_INET;
233 route->in_addr.in = gateway;
234
235 route_drop(route, link,
236 &link_route_drop_handler);
237 }
238 }
239
f414a269
TG
240 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
241 if (r >= 0) {
242 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
243 if (r >= 0)
244 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
3c9b8860 245
f414a269
TG
246 address->family = AF_INET;
247 address->in_addr.in = addr;
248 address->prefixlen = prefixlen;
3c9b8860 249
f414a269
TG
250 address_drop(address, link, &link_address_drop_handler);
251 }
3c9b8860
TG
252 }
253
254 if (link->network->dhcp_mtu) {
255 uint16_t mtu;
256
257 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
258 if (r >= 0 && link->original_mtu != mtu) {
259 r = link_set_mtu(link, link->original_mtu);
260 if (r < 0) {
79008bdd 261 log_link_warning(link,
3c9b8860
TG
262 "DHCP error: could not reset MTU");
263 link_enter_failed(link);
264 return r;
265 }
266 }
267 }
268
269 if (link->network->dhcp_hostname) {
270 const char *hostname = NULL;
271
272 r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
273 if (r >= 0 && hostname) {
274 r = link_set_hostname(link, "");
275 if (r < 0)
79008bdd 276 log_link_error(link,
3c9b8860
TG
277 "Failed to reset transient hostname");
278 }
279 }
280
281 link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
282 link->dhcp4_configured = false;
283
284 return 0;
285}
286
287static int dhcp4_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
288 void *userdata) {
289 _cleanup_link_unref_ Link *link = userdata;
290 int r;
291
292 assert(link);
293
294 r = sd_rtnl_message_get_errno(m);
295 if (r < 0 && r != -EEXIST) {
79008bdd 296 log_link_error(link, "could not set DHCPv4 address: %s",
3c9b8860
TG
297 strerror(-r));
298 link_enter_failed(link);
45af44d4
TG
299 } else if (r >= 0)
300 link_rtnl_process_address(rtnl, m, link->manager);
3c9b8860
TG
301
302 link_set_dhcp_routes(link);
303
304 return 1;
305}
306
307static int dhcp4_update_address(Link *link,
308 struct in_addr *address,
309 struct in_addr *netmask,
310 uint32_t lifetime) {
311 _cleanup_address_free_ Address *addr = NULL;
312 unsigned prefixlen;
313 int r;
314
315 assert(address);
316 assert(netmask);
317 assert(lifetime);
318
319 prefixlen = in_addr_netmask_to_prefixlen(netmask);
320
321 r = address_new_dynamic(&addr);
322 if (r < 0)
323 return r;
324
325 addr->family = AF_INET;
326 addr->in_addr.in.s_addr = address->s_addr;
327 addr->cinfo.ifa_prefered = lifetime;
328 addr->cinfo.ifa_valid = lifetime;
329 addr->prefixlen = prefixlen;
330 addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
331
332 /* use update rather than configure so that we will update the
333 * lifetime of an existing address if it has already been configured */
334 r = address_update(addr, link, &dhcp4_address_handler);
335 if (r < 0)
336 return r;
337
338 return 0;
339}
340
341static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
342 sd_dhcp_lease *lease;
343 struct in_addr address;
344 struct in_addr netmask;
345 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
346 int r;
347
348 assert(link);
349 assert(client);
350 assert(link->network);
351
352 r = sd_dhcp_client_get_lease(client, &lease);
353 if (r < 0) {
79008bdd 354 log_link_warning(link, "DHCP error: no lease %s",
3c9b8860
TG
355 strerror(-r));
356 return r;
357 }
358
359 sd_dhcp_lease_unref(link->dhcp_lease);
360 link->dhcp4_configured = false;
361 link->dhcp_lease = lease;
362
363 r = sd_dhcp_lease_get_address(lease, &address);
364 if (r < 0) {
79008bdd 365 log_link_warning(link, "DHCP error: no address: %s",
3c9b8860
TG
366 strerror(-r));
367 return r;
368 }
369
370 r = sd_dhcp_lease_get_netmask(lease, &netmask);
371 if (r < 0) {
79008bdd 372 log_link_warning(link, "DHCP error: no netmask: %s",
3c9b8860
TG
373 strerror(-r));
374 return r;
375 }
376
377 if (!link->network->dhcp_critical) {
378 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
379 &lifetime);
380 if (r < 0) {
79008bdd 381 log_link_warning(link,
3c9b8860
TG
382 "DHCP error: no lifetime: %s",
383 strerror(-r));
384 return r;
385 }
386 }
387
388 r = dhcp4_update_address(link, &address, &netmask, lifetime);
389 if (r < 0) {
79008bdd 390 log_link_warning(link, "could not update IP address: %s",
3c9b8860
TG
391 strerror(-r));
392 link_enter_failed(link);
393 return r;
394 }
395
396 return 0;
397}
398
399static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
400 sd_dhcp_lease *lease;
401 struct in_addr address;
402 struct in_addr netmask;
403 struct in_addr gateway;
404 unsigned prefixlen;
405 uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
406 int r;
407
408 assert(client);
409 assert(link);
410
411 r = sd_dhcp_client_get_lease(client, &lease);
412 if (r < 0) {
79008bdd 413 log_link_warning(link, "DHCP error: no lease: %s",
3c9b8860
TG
414 strerror(-r));
415 return r;
416 }
417
418 r = sd_dhcp_lease_get_address(lease, &address);
419 if (r < 0) {
79008bdd 420 log_link_warning(link, "DHCP error: no address: %s",
3c9b8860
TG
421 strerror(-r));
422 return r;
423 }
424
425 r = sd_dhcp_lease_get_netmask(lease, &netmask);
426 if (r < 0) {
79008bdd 427 log_link_warning(link, "DHCP error: no netmask: %s",
3c9b8860
TG
428 strerror(-r));
429 return r;
430 }
431
432 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
433
434 r = sd_dhcp_lease_get_router(lease, &gateway);
435 if (r < 0 && r != -ENOENT) {
79008bdd 436 log_link_warning(link, "DHCP error: could not get gateway: %s",
3c9b8860
TG
437 strerror(-r));
438 return r;
439 }
440
441 if (r >= 0)
6c861f0a 442 log_link_struct(link, LOG_INFO,
3c9b8860
TG
443 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
444 IFNAMSIZ,
445 link->ifname,
446 ADDRESS_FMT_VAL(address),
447 prefixlen,
448 ADDRESS_FMT_VAL(gateway),
449 "ADDRESS=%u.%u.%u.%u",
450 ADDRESS_FMT_VAL(address),
451 "PREFIXLEN=%u",
452 prefixlen,
453 "GATEWAY=%u.%u.%u.%u",
454 ADDRESS_FMT_VAL(gateway),
455 NULL);
456 else
6c861f0a 457 log_link_struct(link, LOG_INFO,
3c9b8860
TG
458 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
459 IFNAMSIZ,
460 link->ifname,
461 ADDRESS_FMT_VAL(address),
462 prefixlen,
463 "ADDRESS=%u.%u.%u.%u",
464 ADDRESS_FMT_VAL(address),
465 "PREFIXLEN=%u",
466 prefixlen,
467 NULL);
468
469 link->dhcp_lease = lease;
470
471 if (link->network->dhcp_mtu) {
472 uint16_t mtu;
473
474 r = sd_dhcp_lease_get_mtu(lease, &mtu);
475 if (r >= 0) {
476 r = link_set_mtu(link, mtu);
477 if (r < 0)
79008bdd 478 log_link_error(link, "Failed to set MTU "
3c9b8860
TG
479 "to %" PRIu16, mtu);
480 }
481 }
482
483 if (link->network->dhcp_hostname) {
484 const char *hostname;
485
486 r = sd_dhcp_lease_get_hostname(lease, &hostname);
487 if (r >= 0) {
488 r = link_set_hostname(link, hostname);
489 if (r < 0)
79008bdd 490 log_link_error(link,
3c9b8860
TG
491 "Failed to set transient hostname to '%s'",
492 hostname);
493 }
494 }
495
496 if (!link->network->dhcp_critical) {
497 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
498 &lifetime);
499 if (r < 0) {
79008bdd 500 log_link_warning(link,
3c9b8860
TG
501 "DHCP error: no lifetime: %s",
502 strerror(-r));
503 return r;
504 }
505 }
506
507 r = dhcp4_update_address(link, &address, &netmask, lifetime);
508 if (r < 0) {
79008bdd 509 log_link_warning(link, "could not update IP address: %s",
3c9b8860
TG
510 strerror(-r));
511 link_enter_failed(link);
512 return r;
513 }
514
515 return 0;
516}
517static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
518 Link *link = userdata;
519 int r = 0;
520
521 assert(link);
522 assert(link->network);
523 assert(link->manager);
524
525 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
526 return;
527
528 switch (event) {
529 case DHCP_EVENT_EXPIRED:
530 case DHCP_EVENT_STOP:
531 case DHCP_EVENT_IP_CHANGE:
532 if (link->network->dhcp_critical) {
79008bdd 533 log_link_error(link,
3c9b8860
TG
534 "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
535 return;
536 }
537
538 if (link->dhcp_lease) {
539 r = dhcp_lease_lost(link);
540 if (r < 0) {
541 link_enter_failed(link);
542 return;
543 }
544 }
545
546 if (event == DHCP_EVENT_IP_CHANGE) {
547 r = dhcp_lease_acquired(client, link);
548 if (r < 0) {
549 link_enter_failed(link);
550 return;
551 }
552 }
553
554 break;
555 case DHCP_EVENT_RENEW:
556 r = dhcp_lease_renew(client, link);
557 if (r < 0) {
558 link_enter_failed(link);
559 return;
560 }
561 break;
562 case DHCP_EVENT_IP_ACQUIRE:
563 r = dhcp_lease_acquired(client, link);
564 if (r < 0) {
565 link_enter_failed(link);
566 return;
567 }
568 break;
569 default:
570 if (event < 0)
79008bdd 571 log_link_warning(link,
3c9b8860
TG
572 "DHCP error: client failed: %s",
573 strerror(-event));
574 else
79008bdd 575 log_link_warning(link,
3c9b8860
TG
576 "DHCP unknown event: %d",
577 event);
578 break;
579 }
580
581 return;
582}
583
584int dhcp4_configure(Link *link) {
585 int r;
586
587 assert(link);
588 assert(link->network);
cb9fc36a 589 assert(IN_SET(link->network->dhcp, ADDRESS_FAMILY_YES, ADDRESS_FAMILY_IPV4));
3c9b8860
TG
590
591 r = sd_dhcp_client_new(&link->dhcp_client);
592 if (r < 0)
593 return r;
594
595 r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
596 if (r < 0)
597 return r;
598
76253e73
DW
599 r = sd_dhcp_client_set_mac(link->dhcp_client,
600 (const uint8_t *) &link->mac,
601 sizeof (link->mac), ARPHRD_ETHER);
3c9b8860
TG
602 if (r < 0)
603 return r;
604
605 r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
606 if (r < 0)
607 return r;
608
609 r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
610 if (r < 0)
611 return r;
612
613 r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
614 link->network->dhcp_broadcast);
615 if (r < 0)
616 return r;
617
618 if (link->mtu) {
619 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
620 if (r < 0)
621 return r;
622 }
623
624 if (link->network->dhcp_mtu) {
625 r = sd_dhcp_client_set_request_option(link->dhcp_client,
626 DHCP_OPTION_INTERFACE_MTU);
627 if (r < 0)
628 return r;
629 }
630
631 if (link->network->dhcp_routes) {
632 r = sd_dhcp_client_set_request_option(link->dhcp_client,
633 DHCP_OPTION_STATIC_ROUTE);
634 if (r < 0)
635 return r;
636 r = sd_dhcp_client_set_request_option(link->dhcp_client,
637 DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
638 if (r < 0)
639 return r;
640 }
641
642 if (link->network->dhcp_sendhost) {
643 _cleanup_free_ char *hostname = NULL;
644
645 hostname = gethostname_malloc();
646 if (!hostname)
647 return -ENOMEM;
648
649 if (!is_localhost(hostname)) {
650 r = sd_dhcp_client_set_hostname(link->dhcp_client,
651 hostname);
652 if (r < 0)
653 return r;
654 }
655 }
656
657 if (link->network->dhcp_vendor_class_identifier) {
658 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
659 link->network->dhcp_vendor_class_identifier);
660 if (r < 0)
661 return r;
662 }
663
3e43b2cd
JJ
664 switch (link->network->dhcp_client_identifier) {
665 case DHCP_CLIENT_ID_DUID:
666 /* Library defaults to this. */
667 break;
668 case DHCP_CLIENT_ID_MAC:
669 r = sd_dhcp_client_set_client_id(link->dhcp_client,
670 ARPHRD_ETHER,
671 (const uint8_t *) &link->mac,
672 sizeof (link->mac));
673 if (r < 0)
674 return r;
675 break;
676 default:
677 assert_not_reached("Unknown client identifier type.");
678 }
679
3c9b8860
TG
680 return 0;
681}