chiark / gitweb /
networkd: only track state of links we are managing
[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 link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
288         Link *link = userdata;
289         int r;
290
291         r = sd_rtnl_message_get_errno(m);
292         if (r < 0) {
293                 log_warning("%s: could not bring up interface: %s",
294                             link->ifname, strerror(-r));
295                 link_enter_failed(link);
296         }
297
298         return 1;
299 }
300
301 static int link_up(Link *link) {
302         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
303         int r;
304
305         assert(link);
306         assert(link->manager);
307         assert(link->manager->rtnl);
308
309         log_debug("%s: bringing up link", link->ifname);
310
311         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
312         if (r < 0) {
313                 log_error("Could not allocate RTM_SETLINK message");
314                 return r;
315         }
316
317         r = sd_rtnl_message_link_set_flags(req, IFF_UP);
318         if (r < 0) {
319                 log_error("Could not set link flags");
320                 return r;
321         }
322
323         r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
324         if (r < 0) {
325                 log_error("Could not send rtnetlink message: %s", strerror(-r));
326                 return r;
327         }
328
329         return 0;
330 }
331
332 static int link_bridge_joined(Link *link) {
333         int r;
334
335         assert(link);
336         assert(link->state == LINK_STATE_JOINING_BRIDGE);
337         assert(link->network);
338
339         r = link_up(link);
340         if (r < 0) {
341                 link_enter_failed(link);
342                 return r;
343         }
344
345         if (!link->network->dhcp) {
346                 r = link_enter_set_addresses(link);
347                 if (r < 0)
348                         link_enter_failed(link);
349                         return r;
350         }
351
352         return 0;
353 }
354
355 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
356         Link *link = userdata;
357         int r;
358
359         assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
360         assert(link->network);
361
362         if (link->state == LINK_STATE_FAILED)
363                 return 1;
364
365         r = sd_rtnl_message_get_errno(m);
366         if (r < 0) {
367                 log_warning("%s: could not join bridge '%s': %s",
368                             link->ifname, link->network->bridge->name, strerror(-r));
369                 link_enter_failed(link);
370                 return 1;
371         } else
372                 log_debug("%s: joined bridge '%s'",
373                             link->ifname, link->network->bridge->name);
374
375         link_bridge_joined(link);
376
377         return 1;
378 }
379
380 static int link_enter_join_bridge(Link *link) {
381         int r;
382
383         assert(link);
384         assert(link->network);
385         assert(link->state == _LINK_STATE_INVALID);
386
387         link->state = LINK_STATE_JOINING_BRIDGE;
388
389         if (!link->network->bridge)
390                 return link_bridge_joined(link);
391
392         log_debug("%s: joining bridge", link->ifname);
393
394         r = bridge_join(link->network->bridge, link, &bridge_handler);
395         if (r < 0) {
396                 log_warning("%s: could not join bridge '%s'", link->ifname,
397                             link->network->bridge->name);
398                 link_enter_failed(link);
399                 return r;
400         }
401
402         return 0;
403 }
404
405 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
406         Link *link = userdata;
407         int r;
408
409         r = sd_rtnl_message_get_errno(m);
410         if (r < 0) {
411                 log_warning("%s: could not get state: %s",
412                             link->ifname, strerror(-r));
413                 link_enter_failed(link);
414         }
415
416         link_update(link, m);
417
418         return 1;
419 }
420
421 static int link_get(Link *link) {
422         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
423         int r;
424
425         assert(link);
426         assert(link->manager);
427         assert(link->manager->rtnl);
428
429         log_debug("%s: requesting link status", link->ifname);
430
431         r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
432         if (r < 0) {
433                 log_error("Could not allocate RTM_GETLINK message");
434                 return r;
435         }
436
437         r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
438         if (r < 0) {
439                 log_error("Could not send rtnetlink message: %s", strerror(-r));
440                 return r;
441         }
442
443         return 0;
444 }
445
446 int link_configure(Link *link) {
447         int r;
448
449         assert(link);
450         assert(link->network);
451         assert(link->state == _LINK_STATE_INVALID);
452
453         r = link_get(link);
454         if (r < 0) {
455                 link_enter_failed(link);
456                 return r;
457         }
458
459         r = link_enter_join_bridge(link);
460         if (r < 0)
461                 return r;
462
463         return 0;
464 }
465
466 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
467         Link *link = userdata;
468         int r;
469
470         assert(m);
471         assert(link);
472         assert(link->ifname);
473
474         if (link->state == LINK_STATE_FAILED)
475                 return 1;
476
477         r = sd_rtnl_message_get_errno(m);
478         if (r < 0 && r != -EEXIST)
479                 log_warning("%s: could not drop address: %s",
480                             link->ifname, strerror(-r));
481
482         return 1;
483 }
484
485 static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
486         Link *link = userdata;
487         struct in_addr address;
488         struct in_addr netmask;
489         struct in_addr gateway;
490         int prefixlen;
491         int r;
492
493         if (link->state == LINK_STATE_FAILED)
494                 return;
495
496         if (event < 0) {
497                 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
498                 link_enter_failed(link);
499                 return;
500         }
501
502         if (event == DHCP_EVENT_NO_LEASE)
503                 log_debug("%s: IP address in use.", link->ifname);
504
505         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
506             event == DHCP_EVENT_STOP) {
507                 address_drop(link->dhcp_address, link, address_drop_handler);
508
509                 address_free(link->dhcp_address);
510                 link->dhcp_address = NULL;
511
512                 route_free(link->dhcp_route);
513                 link->dhcp_route = NULL;
514         }
515
516         r = sd_dhcp_client_get_address(client, &address);
517         if (r < 0) {
518                 log_warning("%s: DHCP error: no address", link->ifname);
519                 link_enter_failed(link);
520                 return;
521         }
522
523         r = sd_dhcp_client_get_netmask(client, &netmask);
524         if (r < 0) {
525                 log_warning("%s: DHCP error: no netmask", link->ifname);
526                 link_enter_failed(link);
527                 return;
528         }
529
530         prefixlen = sd_dhcp_client_prefixlen(&netmask);
531         if (prefixlen < 0) {
532                 log_warning("%s: DHCP error: no prefixlen", link->ifname);
533                 link_enter_failed(link);
534                 return;
535         }
536
537         r = sd_dhcp_client_get_router(client, &gateway);
538         if (r < 0) {
539                 log_warning("%s: DHCP error: no router", link->ifname);
540                 link_enter_failed(link);
541                 return;
542         }
543
544         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
545                 _cleanup_address_free_ Address *addr = NULL;
546                 _cleanup_route_free_ Route *rt = NULL;
547
548                 log_info("%s: received config over DHCPv4", link->ifname);
549
550                 r = address_new_dynamic(&addr);
551                 if (r < 0) {
552                         log_error("Could not allocate address");
553                         link_enter_failed(link);
554                         return;
555                 }
556
557                 addr->family = AF_INET;
558                 addr->in_addr.in = address;
559                 addr->prefixlen = prefixlen;
560                 addr->netmask = netmask;
561
562                 r = route_new_dynamic(&rt);
563                 if (r < 0) {
564                         log_error("Could not allocate route");
565                         link_enter_failed(link);
566                         return;
567                 }
568
569                 rt->family = AF_INET;
570                 rt->in_addr.in = gateway;
571
572                 link->dhcp_address = addr;
573                 link->dhcp_route = rt;
574                 addr = NULL;
575                 rt = NULL;
576
577                 link_enter_set_addresses(link);
578         }
579
580         return;
581 }
582
583 static int link_acquire_conf(Link *link) {
584         int r;
585
586         assert(link);
587         assert(link->network);
588         assert(link->network->dhcp);
589         assert(link->manager);
590         assert(link->manager->event);
591
592         if (!link->dhcp) {
593                 link->dhcp = sd_dhcp_client_new(link->manager->event);
594                 if (!link->dhcp)
595                         return -ENOMEM;
596
597                 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
598                 if (r < 0)
599                         return r;
600
601                 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
602                 if (r < 0)
603                         return r;
604
605                 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
606                 if (r < 0)
607                         return r;
608         }
609
610         r = sd_dhcp_client_start(link->dhcp);
611         if (r < 0)
612                 return r;
613
614         return 0;
615 }
616
617 int link_update(Link *link, sd_rtnl_message *m) {
618         unsigned flags;
619         int r;
620
621         assert(link);
622         assert(link->network);
623         assert(m);
624
625         r = sd_rtnl_message_link_get_flags(m, &flags);
626         if (r < 0) {
627                 log_warning("%s: could not get link flags", link->ifname);
628                 return r;
629         }
630
631         if (link->flags & IFF_UP && !(flags & IFF_UP))
632                 log_info("%s: interface is down", link->ifname);
633         else if (!(link->flags & IFF_UP) && flags & IFF_UP)
634                 log_info("%s: interface is up", link->ifname);
635
636         if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
637                 log_info("%s: disconnected", link->ifname);
638
639                 if (link->network->dhcp) {
640                         r = sd_dhcp_client_stop(link->dhcp);
641                         if (r < 0) {
642                                 link_enter_failed(link);
643                                 return r;
644                         }
645                 }
646         } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
647                 log_info("%s: connected", link->ifname);
648
649                 if (link->network->dhcp) {
650                         r = link_acquire_conf(link);
651                         if (r < 0) {
652                                 link_enter_failed(link);
653                                 return r;
654                         }
655                 }
656         }
657
658         link->flags = flags;
659
660         log_debug("%s: updated state", link->ifname);
661
662         return 0;
663 }