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