chiark / gitweb /
networkd: improve link state change logging
[elogind.git] / src / network / networkd-link.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 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.h"
26 #include "libudev-private.h"
27 #include "util.h"
28
29 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30         _cleanup_link_free_ Link *link = NULL;
31         const char *mac;
32         struct ether_addr *mac_addr;
33         const char *ifname;
34         int r;
35
36         assert(device);
37         assert(ret);
38
39         link = new0(Link, 1);
40         if (!link)
41                 return -ENOMEM;
42
43         link->manager = manager;
44         link->state = _LINK_STATE_INVALID;
45
46         link->ifindex = udev_device_get_ifindex(device);
47         if (link->ifindex <= 0)
48                 return -EINVAL;
49
50         mac = udev_device_get_sysattr_value(device, "address");
51         if (mac) {
52                 mac_addr = ether_aton(mac);
53                 if (mac_addr)
54                         memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55         }
56
57         ifname = udev_device_get_sysname(device);
58         link->ifname = strdup(ifname);
59
60         r = hashmap_put(manager->links, &link->ifindex, link);
61         if (r < 0)
62                 return r;
63
64         *ret = link;
65         link = NULL;
66
67         return 0;
68 }
69
70 void link_free(Link *link) {
71         if (!link)
72                 return;
73
74         assert(link->manager);
75
76         if (link->dhcp)
77                 sd_dhcp_client_free(link->dhcp);
78
79         route_free(link->dhcp_route);
80         link->dhcp_route = NULL;
81
82         address_free(link->dhcp_address);
83         link->dhcp_address = NULL;
84
85         hashmap_remove(link->manager->links, &link->ifindex);
86
87         free(link->ifname);
88
89         free(link);
90 }
91
92 int link_add(Manager *m, struct udev_device *device) {
93         Link *link;
94         Network *network;
95         int r;
96         uint64_t ifindex;
97         const char *devtype;
98
99         assert(m);
100         assert(device);
101
102         ifindex = udev_device_get_ifindex(device);
103         link = hashmap_get(m->links, &ifindex);
104         if (link)
105                 return 0;
106
107         r = link_new(m, device, &link);
108         if (r < 0) {
109                 log_error("Could not create link: %s", strerror(-r));
110                 return r;
111         }
112
113         devtype = udev_device_get_devtype(device);
114         if (streq_ptr(devtype, "bridge")) {
115                 r = bridge_set_link(m, link);
116                 if (r < 0)
117                         return r == -ENOENT ? 0 : r;
118         }
119
120         r = network_get(m, device, &network);
121         if (r < 0)
122                 return r == -ENOENT ? 0 : r;
123
124         r = network_apply(m, network, link);
125         if (r < 0)
126                 return r;
127
128         return 0;
129 }
130
131 static int link_enter_configured(Link *link) {
132         assert(link);
133         assert(link->state == LINK_STATE_SETTING_ROUTES);
134
135         log_info("%s: link configured", link->ifname);
136
137         link->state = LINK_STATE_CONFIGURED;
138
139         return 0;
140 }
141
142 static void link_enter_failed(Link *link) {
143         assert(link);
144
145         log_warning("%s: failed", link->ifname);
146
147         link->state = LINK_STATE_FAILED;
148 }
149
150 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
151         Link *link = userdata;
152         int r;
153
154         assert(link->route_messages > 0);
155         assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
156                link->state == LINK_STATE_SETTING_ROUTES ||
157                link->state == LINK_STATE_FAILED);
158
159         link->route_messages --;
160
161         if (link->state == LINK_STATE_FAILED)
162                 return 1;
163
164         r = sd_rtnl_message_get_errno(m);
165         if (r < 0 && r != -EEXIST)
166                 log_warning("%s: could not set route: %s",
167                             link->ifname, strerror(-r));
168
169         /* we might have received an old reply after moving back to SETTING_ADDRESSES,
170          * ignore it */
171         if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
172                 log_debug("%s: routes set", link->ifname);
173                 link_enter_configured(link);
174         }
175
176         return 1;
177 }
178
179 static int link_enter_set_routes(Link *link) {
180         Route *route;
181         int r;
182
183         assert(link);
184         assert(link->network);
185         assert(link->state == LINK_STATE_SETTING_ADDRESSES);
186
187         link->state = LINK_STATE_SETTING_ROUTES;
188
189         if (!link->network->static_routes && !link->dhcp_route)
190                 return link_enter_configured(link);
191
192         log_debug("%s: setting routes", link->ifname);
193
194         LIST_FOREACH(static_routes, route, link->network->static_routes) {
195                 r = route_configure(route, link, &route_handler);
196                 if (r < 0) {
197                         log_warning("%s: could not set routes", link->ifname);
198                         link_enter_failed(link);
199                         return r;
200                 }
201
202                 link->route_messages ++;
203         }
204
205         if (link->dhcp_route) {
206                 r = route_configure(link->dhcp_route, link, &route_handler);
207                 if (r < 0) {
208                         log_warning("%s: could not set routes", link->ifname);
209                         link_enter_failed(link);
210                         return r;
211                 }
212
213                 link->route_messages ++;
214         }
215
216         return 0;
217 }
218
219 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
220         Link *link = userdata;
221         int r;
222
223         assert(m);
224         assert(link);
225         assert(link->ifname);
226         assert(link->addr_messages > 0);
227         assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
228
229         link->addr_messages --;
230
231         if (link->state == LINK_STATE_FAILED)
232                 return 1;
233
234         r = sd_rtnl_message_get_errno(m);
235         if (r < 0 && r != -EEXIST)
236                 log_warning("%s: could not set address: %s",
237                             link->ifname, strerror(-r));
238
239         if (link->addr_messages == 0) {
240                 log_debug("%s: addresses set", link->ifname);
241                 link_enter_set_routes(link);
242         }
243
244         return 1;
245 }
246
247 static int link_enter_set_addresses(Link *link) {
248         Address *address;
249         int r;
250
251         assert(link);
252         assert(link->network);
253         assert(link->state != _LINK_STATE_INVALID);
254
255         link->state = LINK_STATE_SETTING_ADDRESSES;
256
257         if (!link->network->static_addresses && !link->dhcp_address)
258                 return link_enter_set_routes(link);
259
260         log_debug("%s: setting addresses", link->ifname);
261
262         LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
263                 r = address_configure(address, link, &address_handler);
264                 if (r < 0) {
265                         log_warning("%s: could not set addresses", link->ifname);
266                         link_enter_failed(link);
267                         return r;
268                 }
269
270                 link->addr_messages ++;
271         }
272
273         if (link->dhcp_address) {
274                 r = address_configure(link->dhcp_address, link, &address_handler);
275                 if (r < 0) {
276                         log_warning("%s: could not set addresses", link->ifname);
277                         link_enter_failed(link);
278                         return r;
279                 }
280
281                 link->addr_messages ++;
282         }
283
284         return 0;
285 }
286
287 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
288         Link *link = userdata;
289         int r;
290
291         assert(m);
292         assert(link);
293         assert(link->ifname);
294
295         if (link->state == LINK_STATE_FAILED)
296                 return 1;
297
298         r = sd_rtnl_message_get_errno(m);
299         if (r < 0 && r != -EEXIST)
300                 log_warning("%s: could not drop address: %s",
301                             link->ifname, strerror(-r));
302
303         return 1;
304 }
305
306 static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
307         Link *link = userdata;
308         struct in_addr address;
309         struct in_addr netmask;
310         struct in_addr gateway;
311         int prefixlen;
312         int r;
313
314         assert(link);
315
316         if (link->state == LINK_STATE_FAILED)
317                 return;
318
319         if (event < 0) {
320                 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
321                 link_enter_failed(link);
322                 return;
323         }
324
325         if (event == DHCP_EVENT_NO_LEASE)
326                 log_debug("%s: IP address in use.", link->ifname);
327
328         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
329             event == DHCP_EVENT_STOP) {
330                 if (link->dhcp_address) {
331                         address_drop(link->dhcp_address, link, address_drop_handler);
332
333                         address_free(link->dhcp_address);
334                         link->dhcp_address = NULL;
335                 }
336
337                 if (link->dhcp_route) {
338                         route_free(link->dhcp_route);
339                         link->dhcp_route = NULL;
340                 }
341         }
342
343         r = sd_dhcp_client_get_address(client, &address);
344         if (r < 0) {
345                 log_warning("%s: DHCP error: no address", link->ifname);
346                 link_enter_failed(link);
347                 return;
348         }
349
350         r = sd_dhcp_client_get_netmask(client, &netmask);
351         if (r < 0) {
352                 log_warning("%s: DHCP error: no netmask", link->ifname);
353                 link_enter_failed(link);
354                 return;
355         }
356
357         prefixlen = sd_dhcp_client_prefixlen(&netmask);
358         if (prefixlen < 0) {
359                 log_warning("%s: DHCP error: no prefixlen", link->ifname);
360                 link_enter_failed(link);
361                 return;
362         }
363
364         r = sd_dhcp_client_get_router(client, &gateway);
365         if (r < 0) {
366                 log_warning("%s: DHCP error: no router", link->ifname);
367                 link_enter_failed(link);
368                 return;
369         }
370
371         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
372                 _cleanup_address_free_ Address *addr = NULL;
373                 _cleanup_route_free_ Route *rt = NULL;
374
375                 log_info("%s: received config over DHCPv4", link->ifname);
376
377                 r = address_new_dynamic(&addr);
378                 if (r < 0) {
379                         log_error("Could not allocate address");
380                         link_enter_failed(link);
381                         return;
382                 }
383
384                 addr->family = AF_INET;
385                 addr->in_addr.in = address;
386                 addr->prefixlen = prefixlen;
387                 addr->netmask = netmask;
388
389                 r = route_new_dynamic(&rt);
390                 if (r < 0) {
391                         log_error("Could not allocate route");
392                         link_enter_failed(link);
393                         return;
394                 }
395
396                 rt->family = AF_INET;
397                 rt->in_addr.in = gateway;
398
399                 link->dhcp_address = addr;
400                 link->dhcp_route = rt;
401                 addr = NULL;
402                 rt = NULL;
403
404                 link_enter_set_addresses(link);
405         }
406
407         return;
408 }
409
410 static int link_acquire_conf(Link *link) {
411         int r;
412
413         assert(link);
414         assert(link->network);
415         assert(link->network->dhcp);
416         assert(link->manager);
417         assert(link->manager->event);
418
419         if (!link->dhcp) {
420                 link->dhcp = sd_dhcp_client_new(link->manager->event);
421                 if (!link->dhcp)
422                         return -ENOMEM;
423
424                 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
425                 if (r < 0)
426                         return r;
427
428                 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
429                 if (r < 0)
430                         return r;
431
432                 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
433                 if (r < 0)
434                         return r;
435         }
436
437         r = sd_dhcp_client_start(link->dhcp);
438         if (r < 0)
439                 return r;
440
441         return 0;
442 }
443
444 static int link_update_flags(Link *link, unsigned flags) {
445         int r;
446
447         assert(link);
448         assert(link->network);
449
450         if (link->state == LINK_STATE_FAILED)
451                 return 0;
452
453         if (link->flags == flags) {
454                 log_debug("%s: link status unchanged: %#x", link->ifname, flags);
455                 return 0;
456         }
457
458         if ((link->flags & IFF_UP) != (flags & IFF_UP)) {
459                 if (flags & IFF_UP)
460                         log_info("%s: power on", link->ifname);
461                 else
462                         log_info("%s: power off", link->ifname);
463         }
464
465         if ((link->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
466                 if (flags & IFF_LOWER_UP) {
467                         log_info("%s: carrier on", link->ifname);
468
469                         if (link->network->dhcp) {
470                                 r = link_acquire_conf(link);
471                                 if (r < 0) {
472                                         link_enter_failed(link);
473                                         return r;
474                                 }
475                         }
476                 } else {
477                         log_info("%s: carrier off", link->ifname);
478
479                         if (link->network->dhcp) {
480                                 r = sd_dhcp_client_stop(link->dhcp);
481                                 if (r < 0) {
482                                         link_enter_failed(link);
483                                         return r;
484                                 }
485                         }
486                 }
487         }
488
489         log_debug("%s: link status updated: %#x -> %#x", link->ifname, link->flags, flags);
490
491         link->flags = flags;
492
493         return 0;
494 }
495
496 static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
497         Link *link = userdata;
498         int r;
499
500         assert(link);
501
502         if (link->state == LINK_STATE_FAILED)
503                 return 1;
504
505         r = sd_rtnl_message_get_errno(m);
506         if (r < 0) {
507                 log_warning("%s: could not bring up interface: %s",
508                             link->ifname, strerror(-r));
509                 link_enter_failed(link);
510         }
511
512         link_update_flags(link, link->flags | IFF_UP);
513
514         return 1;
515 }
516
517 static int link_up(Link *link) {
518         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
519         int r;
520
521         assert(link);
522         assert(link->manager);
523         assert(link->manager->rtnl);
524
525         log_debug("%s: bringing up link", link->ifname);
526
527         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
528         if (r < 0) {
529                 log_error("Could not allocate RTM_SETLINK message");
530                 return r;
531         }
532
533         r = sd_rtnl_message_link_set_flags(req, IFF_UP);
534         if (r < 0) {
535                 log_error("Could not set link flags");
536                 return r;
537         }
538
539         r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
540         if (r < 0) {
541                 log_error("Could not send rtnetlink message: %s", strerror(-r));
542                 return r;
543         }
544
545         return 0;
546 }
547
548 static int link_bridge_joined(Link *link) {
549         int r;
550
551         assert(link);
552         assert(link->state == LINK_STATE_JOINING_BRIDGE);
553         assert(link->network);
554
555         r = link_up(link);
556         if (r < 0) {
557                 link_enter_failed(link);
558                 return r;
559         }
560
561         if (!link->network->dhcp)
562                 return link_enter_set_addresses(link);
563
564         return 0;
565 }
566
567 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
568         Link *link = userdata;
569         int r;
570
571         assert(link);
572         assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
573         assert(link->network);
574
575         if (link->state == LINK_STATE_FAILED)
576                 return 1;
577
578         r = sd_rtnl_message_get_errno(m);
579         if (r < 0) {
580                 log_warning("%s: could not join bridge '%s': %s",
581                             link->ifname, link->network->bridge->name, strerror(-r));
582                 link_enter_failed(link);
583                 return 1;
584         } else
585                 log_debug("%s: joined bridge '%s'",
586                             link->ifname, link->network->bridge->name);
587
588         link_bridge_joined(link);
589
590         return 1;
591 }
592
593 static int link_enter_join_bridge(Link *link) {
594         int r;
595
596         assert(link);
597         assert(link->network);
598         assert(link->state == _LINK_STATE_INVALID);
599
600         link->state = LINK_STATE_JOINING_BRIDGE;
601
602         if (!link->network->bridge)
603                 return link_bridge_joined(link);
604
605         log_debug("%s: joining bridge", link->ifname);
606
607         r = bridge_join(link->network->bridge, link, &bridge_handler);
608         if (r < 0) {
609                 log_warning("%s: could not join bridge '%s'", link->ifname,
610                             link->network->bridge->name);
611                 link_enter_failed(link);
612                 return r;
613         }
614
615         return 0;
616 }
617
618 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
619         Link *link = userdata;
620         int r;
621
622         assert(link);
623
624         if (link->state == LINK_STATE_FAILED)
625                 return 1;
626
627         r = sd_rtnl_message_get_errno(m);
628         if (r < 0) {
629                 log_warning("%s: could not get state: %s",
630                             link->ifname, strerror(-r));
631                 link_enter_failed(link);
632         }
633
634         log_debug("%s: got link state", link->ifname);
635
636         link_update(link, m);
637
638         return 1;
639 }
640
641 static int link_get(Link *link) {
642         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
643         int r;
644
645         assert(link);
646         assert(link->manager);
647         assert(link->manager->rtnl);
648
649         log_debug("%s: requesting link status", link->ifname);
650
651         r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
652         if (r < 0) {
653                 log_error("Could not allocate RTM_GETLINK message");
654                 return r;
655         }
656
657         r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
658         if (r < 0) {
659                 log_error("Could not send rtnetlink message: %s", strerror(-r));
660                 return r;
661         }
662
663         return 0;
664 }
665
666 int link_configure(Link *link) {
667         int r;
668
669         assert(link);
670         assert(link->network);
671         assert(link->state == _LINK_STATE_INVALID);
672
673         r = link_get(link);
674         if (r < 0) {
675                 link_enter_failed(link);
676                 return r;
677         }
678
679         return link_enter_join_bridge(link);
680 }
681
682 int link_update(Link *link, sd_rtnl_message *m) {
683         unsigned flags;
684         int r;
685
686         assert(link);
687         assert(m);
688
689         if (link->state == LINK_STATE_FAILED)
690                 return 0;
691
692         r = sd_rtnl_message_link_get_flags(m, &flags);
693         if (r < 0) {
694                 log_warning("%s: could not get link flags", link->ifname);
695                 return r;
696         }
697
698         return link_update_flags(link, flags);
699 }