chiark / gitweb /
b87fa730826daba3650477c430495523ab09054d
[elogind.git] / src / network / networkd-dhcp4.c
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
29 static 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) {
41                 log_error_link(link, "could not set DHCPv4 route: %s",
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
54 static 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) {
64                 log_warning_link(link,
65                                  "DHCP error: could not get gateway: %s",
66                                  strerror(-r));
67                 return r;
68         }
69         if (r >= 0) {
70                 struct in_addr address;
71                 _cleanup_route_free_ Route *route = NULL;
72                 _cleanup_route_free_ Route *route_gw = NULL;
73
74                 r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
75                 if (r < 0) {
76                         log_warning_link(link,
77                                          "DHCP error: could not get address: %s",
78                                          strerror(-r));
79                         return r;
80                 }
81
82                 r = route_new_dynamic(&route, RTPROT_DHCP);
83                 if (r < 0) {
84                         log_error_link(link,
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) {
92                 log_error_link(link,
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;
104                 route_gw->prefsrc_addr.in = address;
105                 route_gw->scope = RT_SCOPE_LINK;
106                 route_gw->metrics = DHCP_ROUTE_METRIC;
107
108                 r = route_configure(route_gw, link, &dhcp4_route_handler);
109                 if (r < 0) {
110                         log_warning_link(link,
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;
120                 route->prefsrc_addr.in = address;
121                 route->metrics = DHCP_ROUTE_METRIC;
122
123                 r = route_configure(route, link, &dhcp4_route_handler);
124                 if (r < 0) {
125                         log_warning_link(link,
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) {
139                 log_warning_link(link,
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) {
151                         log_error_link(link, "Could not allocate route: %s",
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;
160                 route->metrics = DHCP_ROUTE_METRIC;
161
162                 r = route_configure(route, link, &dhcp4_route_handler);
163                 if (r < 0) {
164                         log_warning_link(link,
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
176 static 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;
181         unsigned prefixlen;
182         int r;
183
184         assert(link);
185         assert(link->dhcp_lease);
186
187         log_warning_link(link, "DHCP lease lost");
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
240                 sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
241                 sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
242                 prefixlen = in_addr_netmask_to_prefixlen(&netmask);
243
244                 address->family = AF_INET;
245                 address->in_addr.in = addr;
246                 address->prefixlen = prefixlen;
247
248                 address_drop(address, link, &link_address_drop_handler);
249         }
250
251         if (link->network->dhcp_mtu) {
252                 uint16_t mtu;
253
254                 r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
255                 if (r >= 0 && link->original_mtu != mtu) {
256                         r = link_set_mtu(link, link->original_mtu);
257                         if (r < 0) {
258                                 log_warning_link(link,
259                                                  "DHCP error: could not reset MTU");
260                                 link_enter_failed(link);
261                                 return r;
262                         }
263                 }
264         }
265
266         if (link->network->dhcp_hostname) {
267                 const char *hostname = NULL;
268
269                 r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
270                 if (r >= 0 && hostname) {
271                         r = link_set_hostname(link, "");
272                         if (r < 0)
273                                 log_error_link(link,
274                                                "Failed to reset transient hostname");
275                 }
276         }
277
278         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
279         link->dhcp4_configured = false;
280
281         return 0;
282 }
283
284 static int dhcp4_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
285                                  void *userdata) {
286         _cleanup_link_unref_ Link *link = userdata;
287         int r;
288
289         assert(link);
290
291         r = sd_rtnl_message_get_errno(m);
292         if (r < 0 && r != -EEXIST) {
293                 log_error_link(link, "could not set DHCPv4 address: %s",
294                                strerror(-r));
295                 link_enter_failed(link);
296         } else if (r >= 0) {
297                 /* calling handler directly so take a ref */
298                 link_ref(link);
299                 link_get_address_handler(rtnl, m, link);
300         }
301
302         link_set_dhcp_routes(link);
303
304         return 1;
305 }
306
307 static 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
341 static 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) {
354                 log_warning_link(link, "DHCP error: no lease %s",
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) {
365                 log_warning_link(link, "DHCP error: no address: %s",
366                                  strerror(-r));
367                 return r;
368         }
369
370         r = sd_dhcp_lease_get_netmask(lease, &netmask);
371         if (r < 0) {
372                 log_warning_link(link, "DHCP error: no netmask: %s",
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) {
381                         log_warning_link(link,
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) {
390                 log_warning_link(link, "could not update IP address: %s",
391                                  strerror(-r));
392                 link_enter_failed(link);
393                 return r;
394         }
395
396         return 0;
397 }
398
399 static 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) {
413                 log_warning_link(link, "DHCP error: no lease: %s",
414                                  strerror(-r));
415                 return r;
416         }
417
418         r = sd_dhcp_lease_get_address(lease, &address);
419         if (r < 0) {
420                 log_warning_link(link, "DHCP error: no address: %s",
421                                  strerror(-r));
422                 return r;
423         }
424
425         r = sd_dhcp_lease_get_netmask(lease, &netmask);
426         if (r < 0) {
427                 log_warning_link(link, "DHCP error: no netmask: %s",
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) {
436                 log_warning_link(link, "DHCP error: could not get gateway: %s",
437                                  strerror(-r));
438                 return r;
439         }
440
441         if (r >= 0)
442                 log_struct_link(LOG_INFO, link,
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
457                 log_struct_link(LOG_INFO, link,
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)
478                                 log_error_link(link, "Failed to set MTU "
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)
490                                 log_error_link(link,
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) {
500                         log_warning_link(link,
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) {
509                 log_warning_link(link, "could not update IP address: %s",
510                                  strerror(-r));
511                 link_enter_failed(link);
512                 return r;
513         }
514
515         return 0;
516 }
517 static 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) {
533                                 log_error_link(link,
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)
571                                 log_warning_link(link,
572                                                  "DHCP error: client failed: %s",
573                                                  strerror(-event));
574                         else
575                                 log_warning_link(link,
576                                                  "DHCP unknown event: %d",
577                                                  event);
578                         break;
579         }
580
581         return;
582 }
583
584 int dhcp4_configure(Link *link) {
585         int r;
586
587         assert(link);
588         assert(link->network);
589         assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
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
599         r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac);
600         if (r < 0)
601                 return r;
602
603         r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
604         if (r < 0)
605                 return r;
606
607         r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
608         if (r < 0)
609                 return r;
610
611         r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
612                                                  link->network->dhcp_broadcast);
613         if (r < 0)
614                 return r;
615
616         if (link->mtu) {
617                 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
618                 if (r < 0)
619                         return r;
620         }
621
622         if (link->network->dhcp_mtu) {
623              r = sd_dhcp_client_set_request_option(link->dhcp_client,
624                                                    DHCP_OPTION_INTERFACE_MTU);
625              if (r < 0)
626                 return r;
627         }
628
629         if (link->network->dhcp_routes) {
630                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
631                                                       DHCP_OPTION_STATIC_ROUTE);
632                 if (r < 0)
633                         return r;
634                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
635                                                       DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
636                         if (r < 0)
637                                 return r;
638         }
639
640         if (link->network->dhcp_sendhost) {
641                 _cleanup_free_ char *hostname = NULL;
642
643                 hostname = gethostname_malloc();
644                 if (!hostname)
645                         return -ENOMEM;
646
647                 if (!is_localhost(hostname)) {
648                         r = sd_dhcp_client_set_hostname(link->dhcp_client,
649                                                         hostname);
650                         if (r < 0)
651                                 return r;
652                 }
653         }
654
655         if (link->network->dhcp_vendor_class_identifier) {
656                 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
657                                                                link->network->dhcp_vendor_class_identifier);
658                 if (r < 0)
659                         return r;
660         }
661
662         return 0;
663 }