chiark / gitweb /
networkctl: also show gateway address when "networkctl status" without further argume...
[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_link_error(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_link_warning(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_link_warning(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_link_error(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_link_error(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_link_warning(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_link_warning(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_link_warning(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_link_error(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_link_warning(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_link_warning(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_link_warning(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_link_error(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_link_error(link, "could not set DHCPv4 address: %s",
297                                strerror(-r));
298                 link_enter_failed(link);
299         } else if (r >= 0)
300                 link_rtnl_process_address(rtnl, m, link->manager);
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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(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_link_warning(link, "DHCP error: could not get gateway: %s",
437                                  strerror(-r));
438                 return r;
439         }
440
441         if (r >= 0)
442                 log_link_struct(link, LOG_INFO,
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_link_struct(link, LOG_INFO,
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_link_error(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_link_error(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_link_warning(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_link_warning(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_link_error(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_link_warning(link,
572                                                  "DHCP error: client failed: %s",
573                                                  strerror(-event));
574                         else
575                                 log_link_warning(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,
600                                    (const uint8_t *) &link->mac,
601                                    sizeof (link->mac), ARPHRD_ETHER);
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
664         return 0;
665 }