chiark / gitweb /
networkd: add DHCPv4 support
[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("Link '%s' 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         link->state = LINK_STATE_FAILED;
146 }
147
148 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
149         Link *link = userdata;
150         int r;
151
152         assert(link->route_messages > 0);
153         assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
154                link->state == LINK_STATE_SETTING_ROUTES ||
155                link->state == LINK_STATE_FAILED);
156
157         link->route_messages --;
158
159         if (link->state == LINK_STATE_FAILED)
160                 return 1;
161
162         r = sd_rtnl_message_get_errno(m);
163         if (r < 0 && r != -EEXIST)
164                 log_warning("Could not set route on interface '%s': %s",
165                             link->ifname, strerror(-r));
166
167         /* we might have received an old reply after moving back to SETTING_ADDRESSES,
168          * ignore it */
169         if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
170                 log_info("Routes set for link '%s'", link->ifname);
171                 link_enter_configured(link);
172         }
173
174         return 1;
175 }
176
177 static int link_enter_set_routes(Link *link) {
178         Route *route;
179         int r;
180
181         assert(link);
182         assert(link->network);
183         assert(link->state == LINK_STATE_SETTING_ADDRESSES);
184
185         link->state = LINK_STATE_SETTING_ROUTES;
186
187         if (!link->network->static_routes && !link->dhcp_route)
188                 return link_enter_configured(link);
189
190         LIST_FOREACH(static_routes, route, link->network->static_routes) {
191                 r = route_configure(route, link, &route_handler);
192                 if (r < 0) {
193                         log_warning("Could not set routes for link '%s'", link->ifname);
194                         link_enter_failed(link);
195                         return r;
196                 }
197
198                 link->route_messages ++;
199         }
200
201         if (link->dhcp_route) {
202                 r = route_configure(link->dhcp_route, link, &route_handler);
203                 if (r < 0) {
204                         log_warning("Could not set routes for link '%s'", link->ifname);
205                         link_enter_failed(link);
206                         return r;
207                 }
208
209                 link->route_messages ++;
210         }
211
212         return 0;
213 }
214
215 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
216         Link *link = userdata;
217         int r;
218
219         assert(m);
220         assert(link);
221         assert(link->ifname);
222         assert(link->addr_messages > 0);
223         assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
224
225         link->addr_messages --;
226
227         if (link->state == LINK_STATE_FAILED)
228                 return 1;
229
230         r = sd_rtnl_message_get_errno(m);
231         if (r < 0 && r != -EEXIST)
232                 log_warning("Could not set address on interface '%s': %s",
233                             link->ifname, strerror(-r));
234
235         if (link->addr_messages == 0) {
236                 log_info("Addresses set for link '%s'", link->ifname);
237                 link_enter_set_routes(link);
238         }
239
240         return 1;
241 }
242
243 static int link_enter_set_addresses(Link *link) {
244         Address *address;
245         int r;
246
247         assert(link);
248         assert(link->network);
249         assert(link->state != _LINK_STATE_INVALID);
250
251         link->state = LINK_STATE_SETTING_ADDRESSES;
252
253         if (!link->network->static_addresses && !link->dhcp_address)
254                 return link_enter_set_routes(link);
255
256         LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
257                 r = address_configure(address, link, &address_handler);
258                 if (r < 0) {
259                         log_warning("Could not set addresses for link '%s'", link->ifname);
260                         link_enter_failed(link);
261                         return r;
262                 }
263
264                 link->addr_messages ++;
265         }
266
267         if (link->dhcp_address) {
268                 r = address_configure(link->dhcp_address, link, &address_handler);
269                 if (r < 0) {
270                         log_warning("Could not set addresses for link '%s'", link->ifname);
271                         link_enter_failed(link);
272                         return r;
273                 }
274
275                 link->addr_messages ++;
276         }
277
278         return 0;
279 }
280
281 static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
282         Link *link = userdata;
283         int r;
284
285         r = sd_rtnl_message_get_errno(m);
286         if (r < 0) {
287                 log_warning("Could not bring up interface '%s': %s",
288                             link->ifname, strerror(-r));
289                 link_enter_failed(link);
290         }
291
292         return 1;
293 }
294
295 static int link_up(Link *link) {
296         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
297         int r;
298
299         assert(link);
300         assert(link->manager);
301         assert(link->manager->rtnl);
302
303         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
304         if (r < 0) {
305                 log_error("Could not allocate RTM_SETLINK message");
306                 return r;
307         }
308
309         r = sd_rtnl_message_link_set_flags(req, IFF_UP);
310         if (r < 0) {
311                 log_error("Could not set link flags");
312                 return r;
313         }
314
315         r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
316         if (r < 0) {
317                 log_error("Could not send rtnetlink message: %s", strerror(-r));
318                 return r;
319         }
320
321         return 0;
322 }
323
324 static int link_bridge_joined(Link *link) {
325         int r;
326
327         assert(link);
328         assert(link->state == LINK_STATE_JOINING_BRIDGE);
329         assert(link->network);
330
331         r = link_up(link);
332         if (r < 0) {
333                 link_enter_failed(link);
334                 return r;
335         }
336
337         if (!link->network->dhcp) {
338                 r = link_enter_set_addresses(link);
339                 if (r < 0)
340                         link_enter_failed(link);
341                         return r;
342         }
343
344         return 0;
345 }
346
347 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
348         Link *link = userdata;
349         int r;
350
351         assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
352         assert(link->network);
353
354         if (link->state == LINK_STATE_FAILED)
355                 return 1;
356
357         r = sd_rtnl_message_get_errno(m);
358         if (r < 0) {
359                 log_warning("Could not join interface '%s' to bridge '%s': %s",
360                             link->ifname, link->network->bridge->name, strerror(-r));
361                 link_enter_failed(link);
362                 return 1;
363         } else
364                 log_info("Join interface '%s' to bridge: %s",
365                             link->ifname, link->network->bridge->name);
366
367         link_bridge_joined(link);
368
369         return 1;
370 }
371
372 static int link_enter_join_bridge(Link *link) {
373         int r;
374
375         assert(link);
376         assert(link->network);
377         assert(link->state == _LINK_STATE_INVALID);
378
379         link->state = LINK_STATE_JOINING_BRIDGE;
380
381         if (!link->network->bridge)
382                 return link_bridge_joined(link);
383
384         r = bridge_join(link->network->bridge, link, &bridge_handler);
385         if (r < 0) {
386                 log_warning("Could not join link '%s' to bridge '%s'", link->ifname,
387                             link->network->bridge->name);
388                 link_enter_failed(link);
389                 return r;
390         }
391
392         return 0;
393 }
394
395 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
396         Link *link = userdata;
397         int r;
398
399         r = sd_rtnl_message_get_errno(m);
400         if (r < 0) {
401                 log_warning("Could not get state of interface '%s': %s",
402                             link->ifname, strerror(-r));
403                 link_enter_failed(link);
404         }
405
406         return 1;
407 }
408
409 static int link_get(Link *link) {
410         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
411         int r;
412
413         assert(link);
414         assert(link->manager);
415         assert(link->manager->rtnl);
416
417         r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
418         if (r < 0) {
419                 log_error("Could not allocate RTM_GETLINK message");
420                 return r;
421         }
422
423         r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
424         if (r < 0) {
425                 log_error("Could not send rtnetlink message: %s", strerror(-r));
426                 return r;
427         }
428
429         return 0;
430 }
431
432 int link_configure(Link *link) {
433         int r;
434
435         assert(link);
436         assert(link->network);
437         assert(link->state == _LINK_STATE_INVALID);
438
439         r = link_get(link);
440         if (r < 0) {
441                 link_enter_failed(link);
442                 return r;
443         }
444
445         r = link_enter_join_bridge(link);
446         if (r < 0)
447                 return r;
448
449         return 0;
450 }
451
452 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
453         Link *link = userdata;
454         int r;
455
456         assert(m);
457         assert(link);
458         assert(link->ifname);
459
460         if (link->state == LINK_STATE_FAILED)
461                 return 1;
462
463         r = sd_rtnl_message_get_errno(m);
464         if (r < 0 && r != -EEXIST)
465                 log_warning("Could not drop address from interface '%s': %s",
466                             link->ifname, strerror(-r));
467
468         return 1;
469 }
470
471 static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
472         Link *link = userdata;
473         struct in_addr address;
474         struct in_addr netmask;
475         struct in_addr gateway;
476         int prefixlen;
477         int r;
478
479         if (link->state == LINK_STATE_FAILED)
480                 return;
481
482         if (event < 0) {
483                 log_warning("DHCP error: %s", strerror(-event));
484                 link_enter_failed(link);
485                 return;
486         }
487
488         if (event == DHCP_EVENT_NO_LEASE)
489                 log_info("IP address in use.");
490
491         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
492             event == DHCP_EVENT_STOP) {
493                 address_drop(link->dhcp_address, link, address_drop_handler);
494
495                 address_free(link->dhcp_address);
496                 link->dhcp_address = NULL;
497
498                 route_free(link->dhcp_route);
499                 link->dhcp_route = NULL;
500         }
501
502         r = sd_dhcp_client_get_address(client, &address);
503         if (r < 0) {
504                 log_warning("DHCP error: no address");
505                 link_enter_failed(link);
506                 return;
507         }
508
509         r = sd_dhcp_client_get_netmask(client, &netmask);
510         if (r < 0) {
511                 log_warning("DHCP error: no netmask");
512                 link_enter_failed(link);
513                 return;
514         }
515
516         prefixlen = sd_dhcp_client_prefixlen(&netmask);
517         if (prefixlen < 0) {
518                 log_warning("DHCP error: no prefixlen");
519                 link_enter_failed(link);
520                 return;
521         }
522
523         r = sd_dhcp_client_get_router(client, &gateway);
524         if (r < 0) {
525                 log_warning("DHCP error: no router");
526                 link_enter_failed(link);
527                 return;
528         }
529
530         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
531                 _cleanup_address_free_ Address *addr = NULL;
532                 _cleanup_route_free_ Route *rt = NULL;
533
534                 log_info("Received config over DHCPv4");
535
536                 r = address_new_dynamic(&addr);
537                 if (r < 0) {
538                         log_error("Could not allocate address");
539                         link_enter_failed(link);
540                         return;
541                 }
542
543                 addr->family = AF_INET;
544                 addr->in_addr.in = address;
545                 addr->prefixlen = prefixlen;
546                 addr->netmask = netmask;
547
548                 r = route_new_dynamic(&rt);
549                 if (r < 0) {
550                         log_error("Could not allocate route");
551                         link_enter_failed(link);
552                         return;
553                 }
554
555                 rt->family = AF_INET;
556                 rt->in_addr.in = gateway;
557
558                 link->dhcp_address = addr;
559                 link->dhcp_route = rt;
560                 addr = NULL;
561                 rt = NULL;
562
563                 link_enter_set_addresses(link);
564         }
565
566         return;
567 }
568
569 static int link_acquire_conf(Link *link) {
570         int r;
571
572         assert(link);
573         assert(link->network);
574         assert(link->network->dhcp);
575         assert(link->manager);
576         assert(link->manager->event);
577
578         if (!link->dhcp) {
579                 link->dhcp = sd_dhcp_client_new(link->manager->event);
580                 if (!link->dhcp)
581                         return -ENOMEM;
582
583                 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
584                 if (r < 0)
585                         return r;
586
587                 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
588                 if (r < 0)
589                         return r;
590
591                 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
592                 if (r < 0)
593                         return r;
594         }
595
596         r = sd_dhcp_client_start(link->dhcp);
597         if (r < 0)
598                 return r;
599
600         return 0;
601 }
602
603 int link_update(Link *link, sd_rtnl_message *m) {
604         unsigned flags;
605         int r;
606
607         assert(link);
608         assert(m);
609
610         r = sd_rtnl_message_link_get_flags(m, &flags);
611         if (r < 0) {
612                 log_warning("Could not get link flags of '%s'", link->ifname);
613                 return r;
614         }
615
616         if (link->flags & IFF_UP && !(flags & IFF_UP))
617                 log_info("Interface '%s' is down", link->ifname);
618         else if (!(link->flags & IFF_UP) && flags & IFF_UP)
619                 log_info("Interface '%s' is up", link->ifname);
620
621         if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
622                 log_info("Interface '%s' is disconnected", link->ifname);
623
624                 if (link->network->dhcp) {
625                         r = sd_dhcp_client_stop(link->dhcp);
626                         if (r < 0) {
627                                 link_enter_failed(link);
628                                 return r;
629                         }
630                 }
631         } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
632                 log_info("Interface '%s' is connected", link->ifname);
633
634                 if (link->network->dhcp) {
635                         r = link_acquire_conf(link);
636                         if (r < 0) {
637                                 link_enter_failed(link);
638                                 return r;
639                         }
640                 }
641         }
642
643         link->flags = flags;
644
645         return 0;
646 }