1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <netinet/ether.h>
26 #include "libudev-private.h"
29 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
32 struct ether_addr *mac_addr;
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
50 mac = udev_device_get_sysattr_value(device, "address");
52 mac_addr = ether_aton(mac);
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
60 r = hashmap_put(manager->links, &link->ifindex, link);
70 void link_free(Link *link) {
74 assert(link->manager);
77 sd_dhcp_client_free(link->dhcp);
79 route_free(link->dhcp_route);
80 link->dhcp_route = NULL;
82 address_free(link->dhcp_address);
83 link->dhcp_address = NULL;
85 hashmap_remove(link->manager->links, &link->ifindex);
92 int link_add(Manager *m, struct udev_device *device) {
102 ifindex = udev_device_get_ifindex(device);
103 link = hashmap_get(m->links, &ifindex);
107 r = link_new(m, device, &link);
109 log_error("Could not create link: %s", strerror(-r));
113 devtype = udev_device_get_devtype(device);
114 if (streq_ptr(devtype, "bridge")) {
115 r = bridge_set_link(m, link);
117 return r == -ENOENT ? 0 : r;
120 r = network_get(m, device, &network);
122 return r == -ENOENT ? 0 : r;
124 r = network_apply(m, network, link);
131 static int link_enter_configured(Link *link) {
133 assert(link->state == LINK_STATE_SETTING_ROUTES);
135 log_info("%s: link configured", link->ifname);
137 link->state = LINK_STATE_CONFIGURED;
142 static void link_enter_failed(Link *link) {
145 log_warning("%s: failed", link->ifname);
147 link->state = LINK_STATE_FAILED;
150 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
151 Link *link = userdata;
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);
159 link->route_messages --;
161 if (link->state == LINK_STATE_FAILED)
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));
169 /* we might have received an old reply after moving back to SETTING_ADDRESSES,
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);
179 static int link_enter_set_routes(Link *link) {
184 assert(link->network);
185 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
187 link->state = LINK_STATE_SETTING_ROUTES;
189 if (!link->network->static_routes && !link->dhcp_route)
190 return link_enter_configured(link);
192 log_debug("%s: setting routes", link->ifname);
194 LIST_FOREACH(static_routes, route, link->network->static_routes) {
195 r = route_configure(route, link, &route_handler);
197 log_warning("%s: could not set routes", link->ifname);
198 link_enter_failed(link);
202 link->route_messages ++;
205 if (link->dhcp_route) {
206 r = route_configure(link->dhcp_route, link, &route_handler);
208 log_warning("%s: could not set routes", link->ifname);
209 link_enter_failed(link);
213 link->route_messages ++;
219 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
220 Link *link = userdata;
225 assert(link->ifname);
226 assert(link->addr_messages > 0);
227 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
229 link->addr_messages --;
231 if (link->state == LINK_STATE_FAILED)
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));
239 if (link->addr_messages == 0) {
240 log_debug("%s: addresses set", link->ifname);
241 link_enter_set_routes(link);
247 static int link_enter_set_addresses(Link *link) {
252 assert(link->network);
253 assert(link->state != _LINK_STATE_INVALID);
255 link->state = LINK_STATE_SETTING_ADDRESSES;
257 if (!link->network->static_addresses && !link->dhcp_address)
258 return link_enter_set_routes(link);
260 log_debug("%s: setting addresses", link->ifname);
262 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
263 r = address_configure(address, link, &address_handler);
265 log_warning("%s: could not set addresses", link->ifname);
266 link_enter_failed(link);
270 link->addr_messages ++;
273 if (link->dhcp_address) {
274 r = address_configure(link->dhcp_address, link, &address_handler);
276 log_warning("%s: could not set addresses", link->ifname);
277 link_enter_failed(link);
281 link->addr_messages ++;
287 static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
288 Link *link = userdata;
293 if (link->state == LINK_STATE_FAILED)
296 r = sd_rtnl_message_get_errno(m);
298 log_warning("%s: could not bring up interface: %s",
299 link->ifname, strerror(-r));
300 link_enter_failed(link);
303 log_debug("%s: brought up interface", link->ifname);
308 static int link_up(Link *link) {
309 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
313 assert(link->manager);
314 assert(link->manager->rtnl);
316 log_debug("%s: bringing up link", link->ifname);
318 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
320 log_error("Could not allocate RTM_SETLINK message");
324 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
326 log_error("Could not set link flags");
330 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
332 log_error("Could not send rtnetlink message: %s", strerror(-r));
339 static int link_bridge_joined(Link *link) {
343 assert(link->state == LINK_STATE_JOINING_BRIDGE);
344 assert(link->network);
348 link_enter_failed(link);
352 if (!link->network->dhcp)
353 return link_enter_set_addresses(link);
358 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
359 Link *link = userdata;
363 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
364 assert(link->network);
366 if (link->state == LINK_STATE_FAILED)
369 r = sd_rtnl_message_get_errno(m);
371 log_warning("%s: could not join bridge '%s': %s",
372 link->ifname, link->network->bridge->name, strerror(-r));
373 link_enter_failed(link);
376 log_debug("%s: joined bridge '%s'",
377 link->ifname, link->network->bridge->name);
379 link_bridge_joined(link);
384 static int link_enter_join_bridge(Link *link) {
388 assert(link->network);
389 assert(link->state == _LINK_STATE_INVALID);
391 link->state = LINK_STATE_JOINING_BRIDGE;
393 if (!link->network->bridge)
394 return link_bridge_joined(link);
396 log_debug("%s: joining bridge", link->ifname);
398 r = bridge_join(link->network->bridge, link, &bridge_handler);
400 log_warning("%s: could not join bridge '%s'", link->ifname,
401 link->network->bridge->name);
402 link_enter_failed(link);
409 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
410 Link *link = userdata;
415 if (link->state == LINK_STATE_FAILED)
418 r = sd_rtnl_message_get_errno(m);
420 log_warning("%s: could not get state: %s",
421 link->ifname, strerror(-r));
422 link_enter_failed(link);
425 log_debug("%s: got link state", link->ifname);
427 link_update(link, m);
432 static int link_get(Link *link) {
433 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
437 assert(link->manager);
438 assert(link->manager->rtnl);
440 log_debug("%s: requesting link status", link->ifname);
442 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
444 log_error("Could not allocate RTM_GETLINK message");
448 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
450 log_error("Could not send rtnetlink message: %s", strerror(-r));
457 int link_configure(Link *link) {
461 assert(link->network);
462 assert(link->state == _LINK_STATE_INVALID);
466 link_enter_failed(link);
470 return link_enter_join_bridge(link);
473 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
474 Link *link = userdata;
479 assert(link->ifname);
481 if (link->state == LINK_STATE_FAILED)
484 r = sd_rtnl_message_get_errno(m);
485 if (r < 0 && r != -EEXIST)
486 log_warning("%s: could not drop address: %s",
487 link->ifname, strerror(-r));
492 static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
493 Link *link = userdata;
494 struct in_addr address;
495 struct in_addr netmask;
496 struct in_addr gateway;
502 if (link->state == LINK_STATE_FAILED)
506 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
507 link_enter_failed(link);
511 if (event == DHCP_EVENT_NO_LEASE)
512 log_debug("%s: IP address in use.", link->ifname);
514 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
515 event == DHCP_EVENT_STOP) {
516 if (link->dhcp_address) {
517 address_drop(link->dhcp_address, link, address_drop_handler);
519 address_free(link->dhcp_address);
520 link->dhcp_address = NULL;
523 if (link->dhcp_route) {
524 route_free(link->dhcp_route);
525 link->dhcp_route = NULL;
529 r = sd_dhcp_client_get_address(client, &address);
531 log_warning("%s: DHCP error: no address", link->ifname);
532 link_enter_failed(link);
536 r = sd_dhcp_client_get_netmask(client, &netmask);
538 log_warning("%s: DHCP error: no netmask", link->ifname);
539 link_enter_failed(link);
543 prefixlen = sd_dhcp_client_prefixlen(&netmask);
545 log_warning("%s: DHCP error: no prefixlen", link->ifname);
546 link_enter_failed(link);
550 r = sd_dhcp_client_get_router(client, &gateway);
552 log_warning("%s: DHCP error: no router", link->ifname);
553 link_enter_failed(link);
557 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
558 _cleanup_address_free_ Address *addr = NULL;
559 _cleanup_route_free_ Route *rt = NULL;
561 log_info("%s: received config over DHCPv4", link->ifname);
563 r = address_new_dynamic(&addr);
565 log_error("Could not allocate address");
566 link_enter_failed(link);
570 addr->family = AF_INET;
571 addr->in_addr.in = address;
572 addr->prefixlen = prefixlen;
573 addr->netmask = netmask;
575 r = route_new_dynamic(&rt);
577 log_error("Could not allocate route");
578 link_enter_failed(link);
582 rt->family = AF_INET;
583 rt->in_addr.in = gateway;
585 link->dhcp_address = addr;
586 link->dhcp_route = rt;
590 link_enter_set_addresses(link);
596 static int link_acquire_conf(Link *link) {
600 assert(link->network);
601 assert(link->network->dhcp);
602 assert(link->manager);
603 assert(link->manager->event);
606 link->dhcp = sd_dhcp_client_new(link->manager->event);
610 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
614 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
618 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
623 r = sd_dhcp_client_start(link->dhcp);
630 int link_update(Link *link, sd_rtnl_message *m) {
635 assert(link->network);
638 if (link->state == LINK_STATE_FAILED)
641 r = sd_rtnl_message_link_get_flags(m, &flags);
643 log_warning("%s: could not get link flags", link->ifname);
647 if (link->flags & IFF_UP && !(flags & IFF_UP))
648 log_info("%s: interface is down", link->ifname);
649 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
650 log_info("%s: interface is up", link->ifname);
652 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
653 log_info("%s: disconnected", link->ifname);
655 if (link->network->dhcp) {
656 r = sd_dhcp_client_stop(link->dhcp);
658 link_enter_failed(link);
662 } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
663 log_info("%s: connected", link->ifname);
665 if (link->network->dhcp) {
666 r = link_acquire_conf(link);
668 link_enter_failed(link);
676 log_debug("%s: updated link state", link->ifname);