]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
rules: drivers - do not reset RUN list
[thirdparty/systemd.git] / src / network / networkd-link.c
CommitLineData
f579559b
TG
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
29int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
8cd11a0f 31 const char *mac;
602cc437 32 struct ether_addr *mac_addr;
c166a070 33 const char *ifname;
f579559b
TG
34 int r;
35
36 assert(device);
37 assert(ret);
38
39 link = new0(Link, 1);
40 if (!link)
41 return -ENOMEM;
42
5a3eb5a7
TG
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
45
0617ffab
TG
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
f579559b
TG
48 return -EINVAL;
49
8cd11a0f 50 mac = udev_device_get_sysattr_value(device, "address");
5a3eb5a7
TG
51 if (mac) {
52 mac_addr = ether_aton(mac);
53 if (mac_addr)
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55 }
f579559b 56
c166a070
TG
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
59
0617ffab 60 r = hashmap_put(manager->links, &link->ifindex, link);
f579559b
TG
61 if (r < 0)
62 return r;
63
64 *ret = link;
65 link = NULL;
66
67 return 0;
68}
69
70void link_free(Link *link) {
71 if (!link)
72 return;
73
0617ffab 74 assert(link->manager);
f579559b 75
f5be5601
TG
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
0617ffab 85 hashmap_remove(link->manager->links, &link->ifindex);
f579559b 86
c166a070
TG
87 free(link->ifname);
88
f579559b
TG
89 free(link);
90}
91
92int link_add(Manager *m, struct udev_device *device) {
93 Link *link;
94 Network *network;
95 int r;
96 uint64_t ifindex;
02b59d57 97 const char *devtype;
f579559b
TG
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) {
c166a070 109 log_error("Could not create link: %s", strerror(-r));
f579559b
TG
110 return r;
111 }
112
02b59d57
TG
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
f579559b
TG
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
f882c247 131static int link_enter_configured(Link *link) {
ef1ba606
TG
132 assert(link);
133 assert(link->state == LINK_STATE_SETTING_ROUTES);
134
449f7554 135 log_info("%s: link configured", link->ifname);
f882c247
TG
136
137 link->state = LINK_STATE_CONFIGURED;
138
139 return 0;
140}
141
ef1ba606
TG
142static void link_enter_failed(Link *link) {
143 assert(link);
f882c247 144
449f7554
TG
145 log_warning("%s: failed", link->ifname);
146
ef1ba606 147 link->state = LINK_STATE_FAILED;
f882c247
TG
148}
149
f882c247
TG
150static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
151 Link *link = userdata;
152 int r;
153
f5be5601
TG
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);
f882c247 158
f5be5601 159 link->route_messages --;
f882c247
TG
160
161 if (link->state == LINK_STATE_FAILED)
162 return 1;
163
164 r = sd_rtnl_message_get_errno(m);
c166a070 165 if (r < 0 && r != -EEXIST)
449f7554 166 log_warning("%s: could not set route: %s",
c166a070 167 link->ifname, strerror(-r));
f882c247 168
f5be5601
TG
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) {
449f7554 172 log_debug("%s: routes set", link->ifname);
dd3efc09
TG
173 link_enter_configured(link);
174 }
f882c247
TG
175
176 return 1;
177}
178
179static int link_enter_set_routes(Link *link) {
180 Route *route;
181 int r;
182
183 assert(link);
184 assert(link->network);
ef1ba606 185 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 186
ef1ba606 187 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 188
f5be5601 189 if (!link->network->static_routes && !link->dhcp_route)
dd3efc09 190 return link_enter_configured(link);
f882c247 191
449f7554
TG
192 log_debug("%s: setting routes", link->ifname);
193
f048a16b 194 LIST_FOREACH(static_routes, route, link->network->static_routes) {
f882c247 195 r = route_configure(route, link, &route_handler);
dd3efc09 196 if (r < 0) {
449f7554 197 log_warning("%s: could not set routes", link->ifname);
ef1ba606
TG
198 link_enter_failed(link);
199 return r;
dd3efc09 200 }
c166a070 201
f5be5601
TG
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) {
449f7554 208 log_warning("%s: could not set routes", link->ifname);
f5be5601
TG
209 link_enter_failed(link);
210 return r;
211 }
212
213 link->route_messages ++;
f882c247
TG
214 }
215
216 return 0;
217}
218
f882c247
TG
219static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
220 Link *link = userdata;
221 int r;
222
f5be5601
TG
223 assert(m);
224 assert(link);
225 assert(link->ifname);
226 assert(link->addr_messages > 0);
ef1ba606 227 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
f882c247 228
f5be5601 229 link->addr_messages --;
f882c247
TG
230
231 if (link->state == LINK_STATE_FAILED)
232 return 1;
233
234 r = sd_rtnl_message_get_errno(m);
c166a070 235 if (r < 0 && r != -EEXIST)
449f7554 236 log_warning("%s: could not set address: %s",
c166a070 237 link->ifname, strerror(-r));
f882c247 238
f5be5601 239 if (link->addr_messages == 0) {
449f7554 240 log_debug("%s: addresses set", link->ifname);
ef1ba606 241 link_enter_set_routes(link);
dd3efc09 242 }
f882c247
TG
243
244 return 1;
245}
246
247static int link_enter_set_addresses(Link *link) {
248 Address *address;
249 int r;
250
251 assert(link);
252 assert(link->network);
f5be5601 253 assert(link->state != _LINK_STATE_INVALID);
f882c247 254
ef1ba606 255 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 256
f5be5601 257 if (!link->network->static_addresses && !link->dhcp_address)
ef1ba606 258 return link_enter_set_routes(link);
f882c247 259
449f7554
TG
260 log_debug("%s: setting addresses", link->ifname);
261
f048a16b 262 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
f882c247 263 r = address_configure(address, link, &address_handler);
dd3efc09 264 if (r < 0) {
449f7554 265 log_warning("%s: could not set addresses", link->ifname);
ef1ba606
TG
266 link_enter_failed(link);
267 return r;
dd3efc09 268 }
c166a070 269
f5be5601
TG
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) {
449f7554 276 log_warning("%s: could not set addresses", link->ifname);
f5be5601
TG
277 link_enter_failed(link);
278 return r;
279 }
280
281 link->addr_messages ++;
f882c247
TG
282 }
283
284 return 0;
285}
286
dd3efc09
TG
287static 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) {
449f7554 293 log_warning("%s: could not bring up interface: %s",
dd3efc09
TG
294 link->ifname, strerror(-r));
295 link_enter_failed(link);
296 }
f882c247
TG
297
298 return 1;
299}
300
301static int link_up(Link *link) {
f579559b
TG
302 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
303 int r;
304
f882c247
TG
305 assert(link);
306 assert(link->manager);
307 assert(link->manager->rtnl);
308
449f7554
TG
309 log_debug("%s: bringing up link", link->ifname);
310
0f49a5f7 311 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 312 if (r < 0) {
0f49a5f7 313 log_error("Could not allocate RTM_SETLINK message");
f579559b
TG
314 return r;
315 }
316
fc25d7f8
TG
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
dd3efc09 323 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 324 if (r < 0) {
f882c247 325 log_error("Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
326 return r;
327 }
328
f882c247
TG
329 return 0;
330}
331
ef1ba606 332static int link_bridge_joined(Link *link) {
f882c247
TG
333 int r;
334
ef1ba606
TG
335 assert(link);
336 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f5be5601 337 assert(link->network);
dd3efc09 338
f882c247 339 r = link_up(link);
ef1ba606
TG
340 if (r < 0) {
341 link_enter_failed(link);
342 return r;
343 }
f882c247 344
f5be5601
TG
345 if (!link->network->dhcp) {
346 r = link_enter_set_addresses(link);
347 if (r < 0)
348 link_enter_failed(link);
349 return r;
ef1ba606
TG
350 }
351
352 return 0;
02b59d57
TG
353}
354
355static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
356 Link *link = userdata;
357 int r;
358
ef1ba606
TG
359 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
360 assert(link->network);
02b59d57
TG
361
362 if (link->state == LINK_STATE_FAILED)
363 return 1;
364
365 r = sd_rtnl_message_get_errno(m);
ef1ba606 366 if (r < 0) {
449f7554 367 log_warning("%s: could not join bridge '%s': %s",
dd3efc09 368 link->ifname, link->network->bridge->name, strerror(-r));
ef1ba606
TG
369 link_enter_failed(link);
370 return 1;
371 } else
449f7554 372 log_debug("%s: joined bridge '%s'",
dd3efc09 373 link->ifname, link->network->bridge->name);
02b59d57 374
ef1ba606 375 link_bridge_joined(link);
02b59d57
TG
376
377 return 1;
378}
379
380static int link_enter_join_bridge(Link *link) {
381 int r;
382
383 assert(link);
384 assert(link->network);
ef1ba606 385 assert(link->state == _LINK_STATE_INVALID);
02b59d57 386
ef1ba606 387 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 388
ef1ba606
TG
389 if (!link->network->bridge)
390 return link_bridge_joined(link);
02b59d57 391
449f7554
TG
392 log_debug("%s: joining bridge", link->ifname);
393
02b59d57 394 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09 395 if (r < 0) {
449f7554 396 log_warning("%s: could not join bridge '%s'", link->ifname,
dd3efc09 397 link->network->bridge->name);
ef1ba606
TG
398 link_enter_failed(link);
399 return r;
400 }
401
402 return 0;
403}
404
405static 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) {
449f7554 411 log_warning("%s: could not get state: %s",
ef1ba606
TG
412 link->ifname, strerror(-r));
413 link_enter_failed(link);
414 }
415
5eb036ca
TG
416 link_update(link, m);
417
ef1ba606
TG
418 return 1;
419}
420
421static 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
449f7554
TG
429 log_debug("%s: requesting link status", link->ifname);
430
ef1ba606
TG
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;
dd3efc09 441 }
02b59d57
TG
442
443 return 0;
444}
445
446int link_configure(Link *link) {
447 int r;
448
ef1ba606
TG
449 assert(link);
450 assert(link->network);
451 assert(link->state == _LINK_STATE_INVALID);
452
dd3efc09 453 r = link_get(link);
ef1ba606
TG
454 if (r < 0) {
455 link_enter_failed(link);
456 return r;
457 }
dd3efc09 458
02b59d57 459 r = link_enter_join_bridge(link);
f882c247 460 if (r < 0)
ef1ba606 461 return r;
f579559b
TG
462
463 return 0;
464}
dd3efc09 465
f5be5601
TG
466static 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)
449f7554 479 log_warning("%s: could not drop address: %s",
f5be5601
TG
480 link->ifname, strerror(-r));
481
482 return 1;
483}
484
485static 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) {
449f7554 497 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
f5be5601
TG
498 link_enter_failed(link);
499 return;
500 }
501
502 if (event == DHCP_EVENT_NO_LEASE)
449f7554 503 log_debug("%s: IP address in use.", link->ifname);
f5be5601
TG
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) {
449f7554 518 log_warning("%s: DHCP error: no address", link->ifname);
f5be5601
TG
519 link_enter_failed(link);
520 return;
521 }
522
523 r = sd_dhcp_client_get_netmask(client, &netmask);
524 if (r < 0) {
449f7554 525 log_warning("%s: DHCP error: no netmask", link->ifname);
f5be5601
TG
526 link_enter_failed(link);
527 return;
528 }
529
530 prefixlen = sd_dhcp_client_prefixlen(&netmask);
531 if (prefixlen < 0) {
449f7554 532 log_warning("%s: DHCP error: no prefixlen", link->ifname);
f5be5601
TG
533 link_enter_failed(link);
534 return;
535 }
536
537 r = sd_dhcp_client_get_router(client, &gateway);
538 if (r < 0) {
449f7554 539 log_warning("%s: DHCP error: no router", link->ifname);
f5be5601
TG
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
449f7554 548 log_info("%s: received config over DHCPv4", link->ifname);
f5be5601
TG
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
583static 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
22936833
TG
617int link_update(Link *link, sd_rtnl_message *m) {
618 unsigned flags;
619 int r;
620
dd3efc09 621 assert(link);
06a6e593 622 assert(link->network);
22936833
TG
623 assert(m);
624
625 r = sd_rtnl_message_link_get_flags(m, &flags);
626 if (r < 0) {
449f7554 627 log_warning("%s: could not get link flags", link->ifname);
22936833
TG
628 return r;
629 }
dd3efc09
TG
630
631 if (link->flags & IFF_UP && !(flags & IFF_UP))
449f7554 632 log_info("%s: interface is down", link->ifname);
dd3efc09 633 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
449f7554 634 log_info("%s: interface is up", link->ifname);
dd3efc09 635
f5be5601 636 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
449f7554 637 log_info("%s: disconnected", link->ifname);
f5be5601
TG
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) {
449f7554 647 log_info("%s: connected", link->ifname);
dd3efc09 648
06a6e593 649 if (link->network->dhcp) {
f5be5601
TG
650 r = link_acquire_conf(link);
651 if (r < 0) {
652 link_enter_failed(link);
653 return r;
654 }
655 }
656 }
657
dd3efc09
TG
658 link->flags = flags;
659
449f7554
TG
660 log_debug("%s: updated state", link->ifname);
661
dd3efc09
TG
662 return 0;
663}