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;
291 r = sd_rtnl_message_get_errno(m);
293 log_warning("%s: could not bring up interface: %s",
294 link->ifname, strerror(-r));
295 link_enter_failed(link);
301 static int link_up(Link *link) {
302 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
306 assert(link->manager);
307 assert(link->manager->rtnl);
309 log_debug("%s: bringing up link", link->ifname);
311 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
313 log_error("Could not allocate RTM_SETLINK message");
317 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
319 log_error("Could not set link flags");
323 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
325 log_error("Could not send rtnetlink message: %s", strerror(-r));
332 static int link_bridge_joined(Link *link) {
336 assert(link->state == LINK_STATE_JOINING_BRIDGE);
337 assert(link->network);
341 link_enter_failed(link);
345 if (!link->network->dhcp) {
346 r = link_enter_set_addresses(link);
348 link_enter_failed(link);
355 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
356 Link *link = userdata;
359 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
360 assert(link->network);
362 if (link->state == LINK_STATE_FAILED)
365 r = sd_rtnl_message_get_errno(m);
367 log_warning("%s: could not join bridge '%s': %s",
368 link->ifname, link->network->bridge->name, strerror(-r));
369 link_enter_failed(link);
372 log_debug("%s: joined bridge '%s'",
373 link->ifname, link->network->bridge->name);
375 link_bridge_joined(link);
380 static int link_enter_join_bridge(Link *link) {
384 assert(link->network);
385 assert(link->state == _LINK_STATE_INVALID);
387 link->state = LINK_STATE_JOINING_BRIDGE;
389 if (!link->network->bridge)
390 return link_bridge_joined(link);
392 log_debug("%s: joining bridge", link->ifname);
394 r = bridge_join(link->network->bridge, link, &bridge_handler);
396 log_warning("%s: could not join bridge '%s'", link->ifname,
397 link->network->bridge->name);
398 link_enter_failed(link);
405 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
406 Link *link = userdata;
409 r = sd_rtnl_message_get_errno(m);
411 log_warning("%s: could not get state: %s",
412 link->ifname, strerror(-r));
413 link_enter_failed(link);
416 link_update(link, m);
421 static int link_get(Link *link) {
422 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
426 assert(link->manager);
427 assert(link->manager->rtnl);
429 log_debug("%s: requesting link status", link->ifname);
431 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
433 log_error("Could not allocate RTM_GETLINK message");
437 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
439 log_error("Could not send rtnetlink message: %s", strerror(-r));
446 int link_configure(Link *link) {
450 assert(link->network);
451 assert(link->state == _LINK_STATE_INVALID);
455 link_enter_failed(link);
459 r = link_enter_join_bridge(link);
466 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
467 Link *link = userdata;
472 assert(link->ifname);
474 if (link->state == LINK_STATE_FAILED)
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));
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;
493 if (link->state == LINK_STATE_FAILED)
497 log_warning("%s: DHCP error: %s", link->ifname, strerror(-event));
498 link_enter_failed(link);
502 if (event == DHCP_EVENT_NO_LEASE)
503 log_debug("%s: IP address in use.", link->ifname);
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);
509 address_free(link->dhcp_address);
510 link->dhcp_address = NULL;
512 route_free(link->dhcp_route);
513 link->dhcp_route = NULL;
516 r = sd_dhcp_client_get_address(client, &address);
518 log_warning("%s: DHCP error: no address", link->ifname);
519 link_enter_failed(link);
523 r = sd_dhcp_client_get_netmask(client, &netmask);
525 log_warning("%s: DHCP error: no netmask", link->ifname);
526 link_enter_failed(link);
530 prefixlen = sd_dhcp_client_prefixlen(&netmask);
532 log_warning("%s: DHCP error: no prefixlen", link->ifname);
533 link_enter_failed(link);
537 r = sd_dhcp_client_get_router(client, &gateway);
539 log_warning("%s: DHCP error: no router", link->ifname);
540 link_enter_failed(link);
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;
548 log_info("%s: received config over DHCPv4", link->ifname);
550 r = address_new_dynamic(&addr);
552 log_error("Could not allocate address");
553 link_enter_failed(link);
557 addr->family = AF_INET;
558 addr->in_addr.in = address;
559 addr->prefixlen = prefixlen;
560 addr->netmask = netmask;
562 r = route_new_dynamic(&rt);
564 log_error("Could not allocate route");
565 link_enter_failed(link);
569 rt->family = AF_INET;
570 rt->in_addr.in = gateway;
572 link->dhcp_address = addr;
573 link->dhcp_route = rt;
577 link_enter_set_addresses(link);
583 static int link_acquire_conf(Link *link) {
587 assert(link->network);
588 assert(link->network->dhcp);
589 assert(link->manager);
590 assert(link->manager->event);
593 link->dhcp = sd_dhcp_client_new(link->manager->event);
597 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
601 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
605 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
610 r = sd_dhcp_client_start(link->dhcp);
617 int link_update(Link *link, sd_rtnl_message *m) {
622 assert(link->network);
625 r = sd_rtnl_message_link_get_flags(m, &flags);
627 log_warning("%s: could not get link flags", link->ifname);
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);
636 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
637 log_info("%s: disconnected", link->ifname);
639 if (link->network->dhcp) {
640 r = sd_dhcp_client_stop(link->dhcp);
642 link_enter_failed(link);
646 } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
647 log_info("%s: connected", link->ifname);
649 if (link->network->dhcp) {
650 r = link_acquire_conf(link);
652 link_enter_failed(link);
660 log_debug("%s: updated state", link->ifname);