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