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