networkd-link.c revision 5d4795f3722911ccd7953c0cf112c1f7624ea834
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2013 Tom Gundersen <teg@jklm.no>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poetteringint link_new(Manager *manager, struct udev_device *device, Link **ret) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen link->ifindex = udev_device_get_ifindex(device);
99634696183dfabae20104e58157c69029a11594Tom Gundersen mac = udev_device_get_sysattr_value(device, "address");
99634696183dfabae20104e58157c69029a11594Tom Gundersen memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
99634696183dfabae20104e58157c69029a11594Tom Gundersen r = hashmap_put(manager->links, &link->ifindex, link);
99634696183dfabae20104e58157c69029a11594Tom Gundersen hashmap_remove(link->manager->links, &link->ifindex);
99634696183dfabae20104e58157c69029a11594Tom Gundersenint link_add(Manager *m, struct udev_device *device, Link **ret) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0 && r != -ENOENT)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return r == -ENOENT ? 0 : r;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen assert(link->state == LINK_STATE_SETTING_ROUTES);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering link->state == LINK_STATE_SETTING_ROUTES ||
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0 && r != -EEXIST)
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering log_warning_link(link, "could not set route: %s", strerror(-r));
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering /* we might have received an old reply after moving back to SETTING_ADDRESSES,
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poettering * ignore it */
8eb9058dc1f99a5eb9b8726a978fcc0720837a10Lennart Poettering if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
3bdace9bf779ce051f00c14914b35c3a26164aa9Lennart Poetteringstatic int link_enter_set_routes(Link *link) {
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen assert(link->state == LINK_STATE_SETTING_ADDRESSES);
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen if (!link->network->static_routes && !link->dhcp_route)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen LIST_FOREACH(static_routes, route, link->network->static_routes) {
3733eec3e292e4ddb4cba5eb8d3bd8cbee7102d8Lennart Poettering r = route_configure(route, link, &route_handler);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen r = route_configure(link->dhcp_route, link, &route_handler);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (r < 0 && r != -EEXIST)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen "MESSAGE=%s: could not set address: %s",
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen "ERRNO=%d", -r,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenstatic int link_enter_set_addresses(Link *link) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (!link->network->static_addresses && !link->dhcp_address)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = address_configure(address, link, &address_handler);
b3ec603ce8053ba3f95da1d36f15ea762c83d1e1Lennart Poettering r = address_configure(link->dhcp_address, link, &address_handler);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0 && r != -EEXIST)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_warning_link(link, "could not drop address: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_warning("Could not set hostname: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int set_hostname(sd_bus *bus, const char *hostname) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_debug("Setting transient hostname: '%s'", hostname);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (!bus) { /* TODO: replace by assert when we can rely on kdbus */
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_info("Not connected to system bus, ignoring transient hostname.");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen "SetHostname",
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sd_bus_message_append(m, "sb", hostname, false);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sd_bus_call_async(bus, m, set_hostname_handler, NULL, 0, NULL);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_error("Could not set transient hostname: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (r < 0 && r != -EEXIST)
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_warning_link(link, "Could not set MTU: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int link_set_mtu(Link *link, uint32_t mtu) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_debug_link(link, "setting MTU: %" PRIu32, mtu);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_error_link(link, "Could not allocate RTM_SETLINK message");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sd_rtnl_message_append_u32(req, IFLA_MTU, mtu);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_error_link(link, "Could not append MTU: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = sd_rtnl_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen "Could not send rtnetlink message: %s", strerror(-r));
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_warning_link(link, "DHCP error: %s", strerror(-event));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen address_drop(link->dhcp_address, link, address_drop_handler);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_warning_link(link, "DHCP error: could not reset MTU");
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_error("Failed to reset transient hostname");
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = sd_dhcp_client_get_address(client, &address);
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen log_warning_link(link, "DHCP error: no address");
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen r = sd_dhcp_client_get_netmask(client, &netmask);
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen log_warning_link(link, "DHCP error: no netmask");
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen prefixlen = sd_dhcp_client_prefixlen(&netmask);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen log_warning_link(link, "DHCP error: no prefixlen");
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = sd_dhcp_client_get_router(client, &gateway);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_warning_link(link, "DHCP error: no router");
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen "MESSAGE=%s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen "ADDRESS=%u.%u.%u.%u",
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen "PREFIXLEN=%u",
59b8f6b628145586e87b8a4f6e29c755ad7d61edTom Gundersen "GATEWAY=%u.%u.%u.%u",
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering log_error_link(link, "Could not allocate address");
1a04db0fc9d08fffe80d6d7b5b60459295922b11Lennart Poettering log_error_link(link, "Could not allocate route");
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = sd_dhcp_client_get_dns(client, &nameservers, &nameservers_size);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen if (r >= 0) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = manager_update_resolv_conf(link->manager);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = sd_dhcp_client_get_hostname(client, &hostname);
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen if (r >= 0) {
527503444ef24ae03c73cf85128c7acbb1146f3cTom Gundersen r = set_hostname(link->manager->bus, hostname);
c15fb62a731f1a457af94e60ac6a4d23f219a8f6Thomas Hindoe Paaboel Andersen assert(link->manager->event);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = sd_dhcp_client_attach_event(link->dhcp, NULL, 0);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = sd_dhcp_client_set_request_option(link->dhcp, 26);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen log_debug_link(link, "acquiring DHCPv4 lease");
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersenstatic int link_update_flags(Link *link, unsigned flags) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen log_debug_link(link, "link status unchanged: %#.8x", flags);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if ((link->flags & IFF_UP) != (flags & IFF_UP))
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen if ((link->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen log_warning_link(link, "Could not acquire DHCPv4 lease: %s", strerror(-r));
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen log_warning_link(link, "Could not stop DHCPv4 client: %s", strerror(-r));
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen "link status updated: %#.8x -> %#.8x", link->flags, flags);
586ac6f711e2eccceb12421df22fca4f117226c4Lennart Poetteringstatic int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
99634696183dfabae20104e58157c69029a11594Tom Gundersen "could not bring up interface: %s", strerror(-r));
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen link_update_flags(link, link->flags | IFF_UP);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen log_error_link(link, "Could not allocate RTM_SETLINK message");
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen log_error_link(link, "Could not set link flags: %s", strerror(-r));
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen "Could not send rtnetlink message: %s", strerror(-r));
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen assert(link->state == LINK_STATE_JOINING_BRIDGE);
83cedf7ae28925e37931e7e92d22be9c936a1defTom Gundersenstatic int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
933f9caeeb2b3c1b951d330e04beb04226e5a890Daniel Mack assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "MESSAGE=%s: could not join bridge '%s': %s",
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen link->ifname, link->network->bridge->name, strerror(-r),
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen "MESSAGE=%s: joined bridge '%s'",
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersenstatic int link_enter_join_bridge(Link *link) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen "MESSAGE=%s: joining bridge '%s'",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = bridge_join(link->network->bridge, link, &bridge_handler);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen "MESSAGE=%s: could not join bridge '%s': %s",
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenstatic int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen log_warning_link(link, "could not get state: %s", strerror(-r));
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen log_debug_link(link, "requesting link status");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
a7f7d1bde43fc825c49afea3f946f5b4b3d563e0Harald Hoyer log_error_link(link, "Could not allocate RTM_GETLINK message");
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
02557f973aed0fed7154fefe53d67e2935f918dcThomas Hindoe Paaboel Andersen "Could not send rtnetlink message: %s", strerror(-r));
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenint link_update(Link *link, sd_rtnl_message *m) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = sd_rtnl_message_link_get_flags(m, &flags);
a6f1e036de8f212f33ead7f5387c297afd8be26eTom Gundersen log_warning_link(link, "Could not get link flags");
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen while (sd_rtnl_message_read(m, &type, &data) > 0) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen if (type == IFLA_MTU && link->network->dhcp &&
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen link->network->dhcp_mtu && !link->original_mtu) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen log_debug_link(link, "saved original MTU: %" PRIu16,