chiark / gitweb /
e451af86433b84e3204a3319adc5a9fffb9eacaa
[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 = link->network->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 = link->network->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 = link->network->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 = 0;
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                 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);
245
246                         address->family = AF_INET;
247                         address->in_addr.in = addr;
248                         address->prefixlen = prefixlen;
249
250                        address_drop(address, link, &link_address_drop_handler);
251                 }
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) {
261                                 log_warning_link(link,
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)
276                                 log_error_link(link,
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
287 static 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) {
296                 log_error_link(link, "could not set DHCPv4 address: %s",
297                                strerror(-r));
298                 link_enter_failed(link);
299         } else if (r >= 0) {
300                 /* calling handler directly so take a ref */
301                 link_ref(link);
302                 link_get_address_handler(rtnl, m, link);
303         }
304
305         link_set_dhcp_routes(link);
306
307         return 1;
308 }
309
310 static int dhcp4_update_address(Link *link,
311                                 struct in_addr *address,
312                                 struct in_addr *netmask,
313                                 uint32_t lifetime) {
314         _cleanup_address_free_ Address *addr = NULL;
315         unsigned prefixlen;
316         int r;
317
318         assert(address);
319         assert(netmask);
320         assert(lifetime);
321
322         prefixlen = in_addr_netmask_to_prefixlen(netmask);
323
324         r = address_new_dynamic(&addr);
325         if (r < 0)
326                 return r;
327
328         addr->family = AF_INET;
329         addr->in_addr.in.s_addr = address->s_addr;
330         addr->cinfo.ifa_prefered = lifetime;
331         addr->cinfo.ifa_valid = lifetime;
332         addr->prefixlen = prefixlen;
333         addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
334
335         /* use update rather than configure so that we will update the
336          * lifetime of an existing address if it has already been configured */
337         r = address_update(addr, link, &dhcp4_address_handler);
338         if (r < 0)
339                 return r;
340
341         return 0;
342 }
343
344 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
345         sd_dhcp_lease *lease;
346         struct in_addr address;
347         struct in_addr netmask;
348         uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
349         int r;
350
351         assert(link);
352         assert(client);
353         assert(link->network);
354
355         r = sd_dhcp_client_get_lease(client, &lease);
356         if (r < 0) {
357                 log_warning_link(link, "DHCP error: no lease %s",
358                                  strerror(-r));
359                 return r;
360         }
361
362         sd_dhcp_lease_unref(link->dhcp_lease);
363         link->dhcp4_configured = false;
364         link->dhcp_lease = lease;
365
366         r = sd_dhcp_lease_get_address(lease, &address);
367         if (r < 0) {
368                 log_warning_link(link, "DHCP error: no address: %s",
369                                  strerror(-r));
370                 return r;
371         }
372
373         r = sd_dhcp_lease_get_netmask(lease, &netmask);
374         if (r < 0) {
375                 log_warning_link(link, "DHCP error: no netmask: %s",
376                                  strerror(-r));
377                 return r;
378         }
379
380         if (!link->network->dhcp_critical) {
381                 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
382                                                &lifetime);
383                 if (r < 0) {
384                         log_warning_link(link,
385                                          "DHCP error: no lifetime: %s",
386                                          strerror(-r));
387                         return r;
388                 }
389         }
390
391         r = dhcp4_update_address(link, &address, &netmask, lifetime);
392         if (r < 0) {
393                 log_warning_link(link, "could not update IP address: %s",
394                                  strerror(-r));
395                 link_enter_failed(link);
396                 return r;
397         }
398
399         return 0;
400 }
401
402 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
403         sd_dhcp_lease *lease;
404         struct in_addr address;
405         struct in_addr netmask;
406         struct in_addr gateway;
407         unsigned prefixlen;
408         uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
409         int r;
410
411         assert(client);
412         assert(link);
413
414         r = sd_dhcp_client_get_lease(client, &lease);
415         if (r < 0) {
416                 log_warning_link(link, "DHCP error: no lease: %s",
417                                  strerror(-r));
418                 return r;
419         }
420
421         r = sd_dhcp_lease_get_address(lease, &address);
422         if (r < 0) {
423                 log_warning_link(link, "DHCP error: no address: %s",
424                                  strerror(-r));
425                 return r;
426         }
427
428         r = sd_dhcp_lease_get_netmask(lease, &netmask);
429         if (r < 0) {
430                 log_warning_link(link, "DHCP error: no netmask: %s",
431                                  strerror(-r));
432                 return r;
433         }
434
435         prefixlen = in_addr_netmask_to_prefixlen(&netmask);
436
437         r = sd_dhcp_lease_get_router(lease, &gateway);
438         if (r < 0 && r != -ENOENT) {
439                 log_warning_link(link, "DHCP error: could not get gateway: %s",
440                                  strerror(-r));
441                 return r;
442         }
443
444         if (r >= 0)
445                 log_struct_link(LOG_INFO, link,
446                                 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
447                                  IFNAMSIZ,
448                                  link->ifname,
449                                  ADDRESS_FMT_VAL(address),
450                                  prefixlen,
451                                  ADDRESS_FMT_VAL(gateway),
452                                  "ADDRESS=%u.%u.%u.%u",
453                                  ADDRESS_FMT_VAL(address),
454                                  "PREFIXLEN=%u",
455                                  prefixlen,
456                                  "GATEWAY=%u.%u.%u.%u",
457                                  ADDRESS_FMT_VAL(gateway),
458                                  NULL);
459         else
460                 log_struct_link(LOG_INFO, link,
461                                 "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u",
462                                  IFNAMSIZ,
463                                  link->ifname,
464                                  ADDRESS_FMT_VAL(address),
465                                  prefixlen,
466                                  "ADDRESS=%u.%u.%u.%u",
467                                  ADDRESS_FMT_VAL(address),
468                                  "PREFIXLEN=%u",
469                                  prefixlen,
470                                  NULL);
471
472         link->dhcp_lease = lease;
473
474         if (link->network->dhcp_mtu) {
475                 uint16_t mtu;
476
477                 r = sd_dhcp_lease_get_mtu(lease, &mtu);
478                 if (r >= 0) {
479                         r = link_set_mtu(link, mtu);
480                         if (r < 0)
481                                 log_error_link(link, "Failed to set MTU "
482                                                "to %" PRIu16, mtu);
483                 }
484         }
485
486         if (link->network->dhcp_hostname) {
487                 const char *hostname;
488
489                 r = sd_dhcp_lease_get_hostname(lease, &hostname);
490                 if (r >= 0) {
491                         r = link_set_hostname(link, hostname);
492                         if (r < 0)
493                                 log_error_link(link,
494                                                "Failed to set transient hostname to '%s'",
495                                                hostname);
496                 }
497         }
498
499         if (!link->network->dhcp_critical) {
500                 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
501                                                &lifetime);
502                 if (r < 0) {
503                         log_warning_link(link,
504                                          "DHCP error: no lifetime: %s",
505                                          strerror(-r));
506                         return r;
507                 }
508         }
509
510         r = dhcp4_update_address(link, &address, &netmask, lifetime);
511         if (r < 0) {
512                 log_warning_link(link, "could not update IP address: %s",
513                                  strerror(-r));
514                 link_enter_failed(link);
515                 return r;
516         }
517
518         return 0;
519 }
520 static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
521         Link *link = userdata;
522         int r = 0;
523
524         assert(link);
525         assert(link->network);
526         assert(link->manager);
527
528         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
529                 return;
530
531         switch (event) {
532                 case DHCP_EVENT_EXPIRED:
533                 case DHCP_EVENT_STOP:
534                 case DHCP_EVENT_IP_CHANGE:
535                         if (link->network->dhcp_critical) {
536                                 log_error_link(link,
537                                                "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
538                                 return;
539                         }
540
541                         if (link->dhcp_lease) {
542                                 r = dhcp_lease_lost(link);
543                                 if (r < 0) {
544                                         link_enter_failed(link);
545                                         return;
546                                 }
547                         }
548
549                         if (event == DHCP_EVENT_IP_CHANGE) {
550                                 r = dhcp_lease_acquired(client, link);
551                                 if (r < 0) {
552                                         link_enter_failed(link);
553                                         return;
554                                 }
555                         }
556
557                         break;
558                 case DHCP_EVENT_RENEW:
559                         r = dhcp_lease_renew(client, link);
560                         if (r < 0) {
561                                 link_enter_failed(link);
562                                 return;
563                         }
564                         break;
565                 case DHCP_EVENT_IP_ACQUIRE:
566                         r = dhcp_lease_acquired(client, link);
567                         if (r < 0) {
568                                 link_enter_failed(link);
569                                 return;
570                         }
571                         break;
572                 default:
573                         if (event < 0)
574                                 log_warning_link(link,
575                                                  "DHCP error: client failed: %s",
576                                                  strerror(-event));
577                         else
578                                 log_warning_link(link,
579                                                  "DHCP unknown event: %d",
580                                                  event);
581                         break;
582         }
583
584         return;
585 }
586
587 int dhcp4_configure(Link *link) {
588         int r;
589
590         assert(link);
591         assert(link->network);
592         assert(IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4));
593
594         r = sd_dhcp_client_new(&link->dhcp_client);
595         if (r < 0)
596                 return r;
597
598         r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
599         if (r < 0)
600                 return r;
601
602         r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac);
603         if (r < 0)
604                 return r;
605
606         r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
607         if (r < 0)
608                 return r;
609
610         r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
611         if (r < 0)
612                 return r;
613
614         r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
615                                                  link->network->dhcp_broadcast);
616         if (r < 0)
617                 return r;
618
619         if (link->mtu) {
620                 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
621                 if (r < 0)
622                         return r;
623         }
624
625         if (link->network->dhcp_mtu) {
626              r = sd_dhcp_client_set_request_option(link->dhcp_client,
627                                                    DHCP_OPTION_INTERFACE_MTU);
628              if (r < 0)
629                 return r;
630         }
631
632         if (link->network->dhcp_routes) {
633                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
634                                                       DHCP_OPTION_STATIC_ROUTE);
635                 if (r < 0)
636                         return r;
637                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
638                                                       DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
639                         if (r < 0)
640                                 return r;
641         }
642
643         if (link->network->dhcp_sendhost) {
644                 _cleanup_free_ char *hostname = NULL;
645
646                 hostname = gethostname_malloc();
647                 if (!hostname)
648                         return -ENOMEM;
649
650                 if (!is_localhost(hostname)) {
651                         r = sd_dhcp_client_set_hostname(link->dhcp_client,
652                                                         hostname);
653                         if (r < 0)
654                                 return r;
655                 }
656         }
657
658         if (link->network->dhcp_vendor_class_identifier) {
659                 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
660                                                                link->network->dhcp_vendor_class_identifier);
661                 if (r < 0)
662                         return r;
663         }
664
665         return 0;
666 }