From 4fc8a29a7e06debb65f4e2651a03804beb41f781 Mon Sep 17 00:00:00 2001 From: Timo Rothenpieler Date: Mon, 26 Oct 2020 16:22:13 +0100 Subject: [PATCH 1/4] sd-netlink: introduce netlink_message_{read,append}_hw_addr Hardware addresses come in various shapes and sizes, these new functions and accomapying data structures account for that instead of hard-coding a hardware address to the 6 bytes of an ethernet MAC. --- src/basic/ether-addr-util.c | 15 +++++++ src/basic/ether-addr-util.h | 24 ++++++++++++ src/libsystemd/sd-netlink/netlink-message.c | 43 +++++++++++++++++++++ src/libsystemd/sd-netlink/netlink-util.h | 3 ++ 4 files changed, 85 insertions(+) diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index e875696a1a..2af2ce02e5 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include #include @@ -9,6 +10,20 @@ #include "macro.h" #include "string-util.h" +char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) { + assert(addr); + assert(buffer); + assert(addr->length <= HW_ADDR_MAX_SIZE); + + for (size_t i = 0; i < addr->length; i++) { + sprintf(&buffer[3*i], "%02"PRIx8, addr->addr.bytes[i]); + if (i < addr->length - 1) + buffer[3*i + 2] = ':'; + } + + return buffer; +} + char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); assert(buffer); diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index 4e44b30be9..462caded03 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -1,11 +1,35 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include #include #include #include "hash-funcs.h" +/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h + * defines a macro of the same name with a much lower size. */ +#define HW_ADDR_MAX_SIZE 32 + +union hw_addr_union { + struct ether_addr ether; + uint8_t infiniband[INFINIBAND_ALEN]; + uint8_t bytes[HW_ADDR_MAX_SIZE]; +}; + +typedef struct hw_addr_data { + union hw_addr_union addr; + size_t length; +} hw_addr_data; + +#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE) +char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]); + +/* Use only as function argument, never stand-alone! */ +#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){}) + +#define HW_ADDR_NULL ((const hw_addr_data){}) + #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index da4f5ba688..2ffff861b2 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -495,6 +495,25 @@ int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short t return 0; } +int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const hw_addr_data *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + assert_return(data->length > 0, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data->addr.bytes, data->length); + if (r < 0) + return r; + + return 0; +} + int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { int r; @@ -864,6 +883,30 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ return 0; } +int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, hw_addr_data *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t) r > sizeof(union hw_addr_union)) + return -EIO; + + if (data) { + memcpy(data->addr.bytes, attr_data, r); + data->length = r; + } + + return 0; +} + int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { int r; void *attr_data; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 2768d5fdc4..8f6c06af6e 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -5,6 +5,7 @@ #include "sd-netlink.h" +#include "ether-addr-util.h" #include "in-addr-util.h" #include "ordered-set.h" #include "socket-util.h" @@ -100,9 +101,11 @@ int rtnl_log_create_error(int r); userdata, description); \ }) +int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const hw_addr_data *data); int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); +int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, hw_addr_data *data); int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data); void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length); From b8162cd200c69be509475b8f2184a5ac651fc2b0 Mon Sep 17 00:00:00 2001 From: Timo Rothenpieler Date: Mon, 26 Oct 2020 18:07:49 +0100 Subject: [PATCH 2/4] network: store full hardware address in Link struct This passes the legacy ethernet address to functions in a lot of places, which all will need migrated to handle arbitrary size hardware addresses eventually. --- src/network/networkd-address.c | 25 +++++++++++++++++-------- src/network/networkd-dhcp4.c | 14 +++++++------- src/network/networkd-dhcp6.c | 2 +- src/network/networkd-ipv4ll.c | 4 ++-- src/network/networkd-link.c | 28 +++++++++++----------------- src/network/networkd-link.h | 3 ++- src/network/networkd-lldp-rx.c | 2 +- src/network/networkd-lldp-tx.c | 2 +- src/network/networkd-ndisc.c | 9 +++++++-- src/network/networkd-radv.c | 4 ++-- 10 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 9130fae778..d24104d55a 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "firewall-util.h" @@ -21,16 +22,24 @@ int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret) { assert(link); assert(ret); + if (link->iftype == ARPHRD_INFINIBAND) { + /* see RFC4391 section 8 */ + memcpy(&ret->s6_addr[8], &link->hw_addr.addr.infiniband[12], 8); + ret->s6_addr[8] ^= 1 << 1; + + return 0; + } + /* see RFC4291 section 2.5.1 */ - ret->s6_addr[8] = link->mac.ether_addr_octet[0]; + ret->s6_addr[8] = link->hw_addr.addr.ether.ether_addr_octet[0]; ret->s6_addr[8] ^= 1 << 1; - ret->s6_addr[9] = link->mac.ether_addr_octet[1]; - ret->s6_addr[10] = link->mac.ether_addr_octet[2]; + ret->s6_addr[9] = link->hw_addr.addr.ether.ether_addr_octet[1]; + ret->s6_addr[10] = link->hw_addr.addr.ether.ether_addr_octet[2]; ret->s6_addr[11] = 0xff; ret->s6_addr[12] = 0xfe; - ret->s6_addr[13] = link->mac.ether_addr_octet[3]; - ret->s6_addr[14] = link->mac.ether_addr_octet[4]; - ret->s6_addr[15] = link->mac.ether_addr_octet[5]; + ret->s6_addr[13] = link->hw_addr.addr.ether.ether_addr_octet[3]; + ret->s6_addr[14] = link->hw_addr.addr.ether.ether_addr_octet[4]; + ret->s6_addr[15] = link->hw_addr.addr.ether.ether_addr_octet[5]; return 0; } @@ -1373,7 +1382,7 @@ static int ipv4_dad_configure(Address *address) { if (r < 0) return r; - r = sd_ipv4acd_set_mac(address->acd, &address->link->mac); + r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.addr.ether); if (r < 0) return r; @@ -1403,7 +1412,7 @@ static int ipv4_dad_update_mac_one(Address *address) { if (r < 0) return r; - r = sd_ipv4acd_set_mac(address->acd, &address->link->mac); + r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.addr.ether); if (r < 0) return r; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index bb8c34f7cc..5255ec3e1c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -650,7 +650,7 @@ static int dhcp4_configure_dad(Link *link) { if (r < 0) return r; - r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->mac); + r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.addr.ether); if (r < 0) return r; @@ -672,7 +672,7 @@ static int dhcp4_dad_update_mac(Link *link) { if (r < 0) return r; - r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->mac); + r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.addr.ether); if (r < 0) return r; @@ -1274,8 +1274,8 @@ static int dhcp4_set_client_identifier(Link *link) { case DHCP_CLIENT_ID_MAC: r = sd_dhcp_client_set_client_id(link->dhcp_client, ARPHRD_ETHER, - (const uint8_t *) &link->mac, - sizeof(link->mac)); + link->hw_addr.addr.bytes, + link->hw_addr.length); if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m"); break; @@ -1325,8 +1325,8 @@ int dhcp4_configure(Link *link) { return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to initialize DHCP4 client: %m"); r = sd_dhcp_client_set_mac(link->dhcp_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); + link->hw_addr.addr.bytes, + link->hw_addr.length, ARPHRD_ETHER); if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m"); @@ -1484,7 +1484,7 @@ int dhcp4_update_mac(Link *link) { if (!link->dhcp_client) return 0; - r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); + r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.addr.bytes, link->hw_addr.length, ARPHRD_ETHER); if (r < 0) return r; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index d9cc4ccf41..afff1dfc15 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -1357,7 +1357,7 @@ static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) { assert(link->network); assert(client); - r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); + r = sd_dhcp6_client_set_mac(client, link->hw_addr.addr.bytes, link->hw_addr.length, ARPHRD_ETHER); if (r < 0) return r; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 3be395e1ad..2f4107580b 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -181,7 +181,7 @@ int ipv4ll_configure(Link *link) { return r; } - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.addr.ether); if (r < 0) return r; @@ -211,7 +211,7 @@ int ipv4ll_update_mac(Link *link) { if (r < 0) return r; - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.addr.ether); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9dd29bb203..09622de868 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -421,9 +421,9 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { if (r < 0) log_link_debug_errno(link, r, "New device has no master, continuing without"); - r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); + r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, &link->hw_addr); if (r < 0) - log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); + log_link_debug_errno(link, r, "Hardware address not found for new device, continuing without"); r = ethtool_get_permanent_macaddr(&manager->ethtool_fd, link->ifname, &link->permanent_mac); if (r < 0) @@ -2167,7 +2167,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names, link->driver, - &link->mac, &link->permanent_mac, + &link->hw_addr.addr.ether, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network); if (r == -ENOENT) { link_enter_unmanaged(link); @@ -2302,7 +2302,7 @@ static int link_initialized_and_synced(Link *link) { r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names, link->driver, - &link->mac, &link->permanent_mac, + &link->hw_addr.addr.ether, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network); if (r == -ENOENT) { link_enter_unmanaged(link); @@ -2697,7 +2697,7 @@ static int link_admin_state_up(Link *link) { int link_update(Link *link, sd_netlink_message *m) { _cleanup_strv_free_ char **s = NULL; - struct ether_addr mac; + hw_addr_data hw_addr; const char *ifname; uint32_t mtu; bool had_carrier, carrier_gained, carrier_lost, link_was_admin_up; @@ -2756,19 +2756,13 @@ int link_update(Link *link, sd_netlink_message *m) { /* The kernel may broadcast NEWLINK messages without the MAC address set, simply ignore them. */ - r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac); - if (r >= 0 && memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN) != 0) { + r = netlink_message_read_hw_addr(m, IFLA_ADDRESS, &hw_addr); + if (r >= 0 && (link->hw_addr.length != hw_addr.length || + memcmp(link->hw_addr.addr.bytes, hw_addr.addr.bytes, hw_addr.length) != 0)) { - memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN); + memcpy(link->hw_addr.addr.bytes, hw_addr.addr.bytes, hw_addr.length); - log_link_debug(link, "Gained new MAC address: " - "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - mac.ether_addr_octet[0], - mac.ether_addr_octet[1], - mac.ether_addr_octet[2], - mac.ether_addr_octet[3], - mac.ether_addr_octet[4], - mac.ether_addr_octet[5]); + log_link_debug(link, "Gained new hardware address: %s", HW_ADDR_TO_STR(&hw_addr)); r = ipv4ll_update_mac(link); if (r < 0) @@ -2787,7 +2781,7 @@ int link_update(Link *link, sd_netlink_message *m) { return log_link_warning_errno(link, r, "Could not update MAC address for Router Advertisement: %m"); if (link->ndisc) { - r = sd_ndisc_set_mac(link->ndisc, &link->mac); + r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.addr.ether); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m"); } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a7ae2645f9..bdeee0fe85 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -16,6 +16,7 @@ #include "sd-radv.h" #include "sd-netlink.h" +#include "ether-addr-util.h" #include "log-link.h" #include "network-util.h" #include "networkd-util.h" @@ -52,7 +53,7 @@ typedef struct Link { char *kind; unsigned short iftype; char *state_file; - struct ether_addr mac; + hw_addr_data hw_addr; struct ether_addr permanent_mac; struct in6_addr ipv6ll_address; uint32_t mtu; diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index 65a8a314d6..4d431ac840 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -93,7 +93,7 @@ int link_lldp_rx_configure(Link *link) { if (r < 0) return r; - r = sd_lldp_set_filter_address(link->lldp, &link->mac); + r = sd_lldp_set_filter_address(link->lldp, &link->hw_addr.addr.ether); if (r < 0) return r; diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index c8e56a5fec..4a1b5fb5f8 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -313,7 +313,7 @@ static int link_send_lldp(Link *link) { SD_LLDP_SYSTEM_CAPABILITIES_STATION; r = lldp_make_packet(link->network->lldp_emit, - &link->mac, + &link->hw_addr.addr.ether, sd_id128_to_string(machine_id, machine_id_string), link->ifname, (uint16_t) ttl, diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 6fde8fe239..a61eaa9fc8 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include "sd-ndisc.h" @@ -593,7 +594,11 @@ static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, l = MAX(DIV_ROUND_UP(prefix_len, 8), 8); siphash24_compress(prefix, l, &state); siphash24_compress_string(link->ifname, &state); - siphash24_compress(&link->mac, sizeof(struct ether_addr), &state); + /* Only last 8 bytes of IB MAC are stable */ + if (link->iftype == ARPHRD_INFINIBAND) + siphash24_compress(&link->hw_addr.addr.infiniband[12], 8, &state); + else + siphash24_compress(link->hw_addr.addr.bytes, link->hw_addr.length, &state); siphash24_compress(&dad_counter, sizeof(uint8_t), &state); rid = htole64(siphash24_finalize(&state)); @@ -1257,7 +1262,7 @@ int ndisc_configure(Link *link) { return r; } - r = sd_ndisc_set_mac(link->ndisc, &link->mac); + r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.addr.ether); if (r < 0) return r; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index eb10f21cbd..a4162cf874 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -659,7 +659,7 @@ int radv_configure(Link *link) { if (r < 0) return r; - r = sd_radv_set_mac(link->radv, &link->mac); + r = sd_radv_set_mac(link->radv, &link->hw_addr.addr.ether); if (r < 0) return r; @@ -727,7 +727,7 @@ int radv_update_mac(Link *link) { if (r < 0) return r; - r = sd_radv_set_mac(link->radv, &link->mac); + r = sd_radv_set_mac(link->radv, &link->hw_addr.addr.ether); if (r < 0) return r; From 14b66dbc92fbae3bd7fc8be4bba476dd1bbb5795 Mon Sep 17 00:00:00 2001 From: Timo Rothenpieler Date: Mon, 26 Oct 2020 14:09:13 +0100 Subject: [PATCH 3/4] dhcp4: fix DHCP on InfiniBand interfaces With these patches applied, networkd is successfully able to get an address from a DHCP server on an IPoIB interface. 1) Makes networkd pass the actual interface type to the dhcp client, instead of hardcoding it to Ethernet. 2) Fixes some issues in handling the larger (20 Byte) IB MAC addresses in the dhcp code. 3) Add a new field to networkds Link struct, which holds the interface broadcast address. 3.1) Modify the DHCP code to also expect the broadcast address as parameter. On an Ethernet-Interface the Broadcast address never changes and is always all 6 bytes set to 0xFF. On an IB one however it is not neccesarily always the same, thus fetching the actual address from the interface is neccesary. 4) Only the last 8 bytes of an IB MAC are stable, so when using an IB MAC to generate a client ID, only pass those 8 bytes. --- src/libsystemd-network/dhcp-internal.h | 8 ++--- src/libsystemd-network/dhcp-network.c | 40 ++++++++++++++--------- src/libsystemd-network/sd-dhcp-client.c | 28 +++++++++++----- src/libsystemd-network/test-dhcp-client.c | 6 ++-- src/network/networkd-dhcp4.c | 25 ++++++++++---- src/network/networkd-dhcp6.c | 2 +- src/network/networkd-link.c | 4 +++ src/network/networkd-link.h | 1 + src/systemd/sd-dhcp-client.h | 1 + 9 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 7e8149487a..d0c99f985b 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -29,10 +29,10 @@ typedef struct DHCPServerData { extern const struct hash_ops dhcp_option_hash_ops; -int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, - uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, uint16_t arp_type, - uint16_t port); +int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, + const uint8_t *mac_addr, size_t mac_addr_len, + const uint8_t *bcast_addr, size_t bcast_addr_len, + uint16_t arp_type, uint16_t port); int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type); int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len); diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index f48e7c3490..c5ee7e7ef2 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -19,9 +19,9 @@ #include "unaligned.h" static int _bind_raw_socket(int ifindex, union sockaddr_union *link, - uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, + uint32_t xid, const uint8_t *bcast_addr, + size_t bcast_addr_len, const struct ether_addr *eth_mac, uint16_t arp_type, uint8_t dhcp_hlen, uint16_t port) { @@ -104,9 +104,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, .sll_protocol = htobe16(ETH_P_IP), .sll_ifindex = ifindex, .sll_hatype = htobe16(arp_type), - .sll_halen = mac_addr_len, + .sll_halen = bcast_addr_len, }; - memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len); + memcpy(link->ll.sll_addr, bcast_addr, bcast_addr_len); r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll)); if (r < 0) @@ -115,34 +115,44 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, return TAKE_FD(s); } -int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, - uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, uint16_t arp_type, - uint16_t port) { +int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, + const uint8_t *mac_addr, size_t mac_addr_len, + const uint8_t *bcast_addr, size_t bcast_addr_len, + uint16_t arp_type, uint16_t port) { static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* Default broadcast address for IPoIB */ static const uint8_t ib_bcast[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff - }; + }; struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } }; - const uint8_t *bcast_addr = NULL; + const uint8_t *default_bcast_addr; + size_t expected_bcast_addr_len; uint8_t dhcp_hlen = 0; if (arp_type == ARPHRD_ETHER) { assert_return(mac_addr_len == ETH_ALEN, -EINVAL); memcpy(ð_mac, mac_addr, ETH_ALEN); - bcast_addr = eth_bcast; dhcp_hlen = ETH_ALEN; + + default_bcast_addr = eth_bcast; + expected_bcast_addr_len = ETH_ALEN; } else if (arp_type == ARPHRD_INFINIBAND) { - assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL); - bcast_addr = ib_bcast; + default_bcast_addr = ib_bcast; + expected_bcast_addr_len = INFINIBAND_ALEN; } else return -EINVAL; - return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len, - bcast_addr, ð_mac, arp_type, dhcp_hlen, port); + if (bcast_addr && bcast_addr_len > 0) + assert_return(bcast_addr_len == expected_bcast_addr_len, -EINVAL); + else { + bcast_addr = default_bcast_addr; + bcast_addr_len = expected_bcast_addr_len; + } + + return _bind_raw_socket(ifindex, link, xid, bcast_addr, bcast_addr_len, + ð_mac, arp_type, dhcp_hlen, port); } int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) { diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 2bfe64a81c..d95bbd3167 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -82,6 +82,8 @@ struct sd_dhcp_client { be32_t last_addr; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; + uint8_t bcast_addr[MAX_MAC_ADDR_LEN]; + size_t bcast_addr_len; uint16_t arp_type; sd_dhcp_client_id client_id; size_t client_id_len; @@ -277,6 +279,7 @@ int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) { int sd_dhcp_client_set_mac( sd_dhcp_client *client, const uint8_t *addr, + const uint8_t *bcast_addr, size_t addr_len, uint16_t arp_type) { @@ -297,7 +300,9 @@ int sd_dhcp_client_set_mac( return -EINVAL; if (client->mac_addr_len == addr_len && - memcmp(&client->mac_addr, addr, addr_len) == 0) + memcmp(&client->mac_addr, addr, addr_len) == 0 && + (client->bcast_addr_len > 0) == !!bcast_addr && + (!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0)) return 0; if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { @@ -309,6 +314,12 @@ int sd_dhcp_client_set_mac( memcpy(&client->mac_addr, addr, addr_len); client->mac_addr_len = addr_len; client->arp_type = arp_type; + client->bcast_addr_len = 0; + + if (bcast_addr) { + memcpy(&client->bcast_addr, bcast_addr, addr_len); + client->bcast_addr_len = addr_len; + } if (need_restart && client->state != DHCP_STATE_STOPPED) { r = sd_dhcp_client_start(client); @@ -1381,9 +1392,10 @@ static int client_start_delayed(sd_dhcp_client *client) { client->xid = random_u32(); - r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, - client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type, client->port); + r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, + client->mac_addr, client->mac_addr_len, + client->bcast_addr, client->bcast_addr_len, + client->arp_type, client->port); if (r < 0) { client_stop(client, r); return r; @@ -1431,10 +1443,10 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->state = DHCP_STATE_REBINDING; client->attempt = 0; - r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, - client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type, - client->port); + r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, + client->mac_addr, client->mac_addr_len, + client->bcast_addr, client->bcast_addr_len, + client->arp_type, client->port); if (r < 0) { client_stop(client, r); return 0; diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 4d748ea66d..b916212d6f 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -23,6 +23,7 @@ #include "util.h" static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; +static uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp); @@ -247,6 +248,7 @@ int dhcp_network_bind_raw_socket( union sockaddr_union *link, uint32_t id, const uint8_t *addr, size_t addr_len, + const uint8_t *bcaddr, size_t bcaddr_len, uint16_t arp_type, uint16_t port) { if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) @@ -296,7 +298,7 @@ static void test_discover_message(sd_event *e) { assert_se(r >= 0); assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0); @@ -513,7 +515,7 @@ static void test_addr_acq(sd_event *e) { assert_se(r >= 0); assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 5255ec3e1c..319ef61593 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1271,14 +1271,24 @@ static int dhcp4_set_client_identifier(Link *link) { return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m"); break; } - case DHCP_CLIENT_ID_MAC: + case DHCP_CLIENT_ID_MAC: { + const uint8_t *hw_addr = link->hw_addr.addr.bytes; + size_t hw_addr_len = link->hw_addr.length; + + if (link->iftype == ARPHRD_INFINIBAND && hw_addr_len == INFINIBAND_ALEN) { + /* set_client_id expects only last 8 bytes of an IB address */ + hw_addr += INFINIBAND_ALEN - 8; + hw_addr_len -= INFINIBAND_ALEN - 8; + } + r = sd_dhcp_client_set_client_id(link->dhcp_client, - ARPHRD_ETHER, - link->hw_addr.addr.bytes, - link->hw_addr.length); + link->iftype, + hw_addr, + hw_addr_len); if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m"); break; + } default: assert_not_reached("Unknown client identifier type."); } @@ -1326,7 +1336,8 @@ int dhcp4_configure(Link *link) { r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.addr.bytes, - link->hw_addr.length, ARPHRD_ETHER); + link->bcast_addr.length > 0 ? link->bcast_addr.addr.bytes : NULL, + link->hw_addr.length, link->iftype); if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m"); @@ -1484,7 +1495,9 @@ int dhcp4_update_mac(Link *link) { if (!link->dhcp_client) return 0; - r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.addr.bytes, link->hw_addr.length, ARPHRD_ETHER); + r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.addr.bytes, + link->bcast_addr.length > 0 ? link->bcast_addr.addr.bytes : NULL, + link->hw_addr.length, link->iftype); if (r < 0) return r; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index afff1dfc15..e7dc10c2be 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -1357,7 +1357,7 @@ static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) { assert(link->network); assert(client); - r = sd_dhcp6_client_set_mac(client, link->hw_addr.addr.bytes, link->hw_addr.length, ARPHRD_ETHER); + r = sd_dhcp6_client_set_mac(client, link->hw_addr.addr.bytes, link->hw_addr.length, link->iftype); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 09622de868..1eca07771a 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -425,6 +425,10 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { if (r < 0) log_link_debug_errno(link, r, "Hardware address not found for new device, continuing without"); + r = netlink_message_read_hw_addr(message, IFLA_BROADCAST, &link->bcast_addr); + if (r < 0) + log_link_debug_errno(link, r, "Broadcast address not found for new device, continuing without"); + r = ethtool_get_permanent_macaddr(&manager->ethtool_fd, link->ifname, &link->permanent_mac); if (r < 0) log_link_debug_errno(link, r, "Permanent MAC address not found for new device, continuing without: %m"); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index bdeee0fe85..159e7e705b 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -54,6 +54,7 @@ typedef struct Link { unsigned short iftype; char *state_file; hw_addr_data hw_addr; + hw_addr_data bcast_addr; struct ether_addr permanent_mac; struct in6_addr ipv6ll_address; uint32_t mtu; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index ac3b5b369c..e0aa93a55b 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -126,6 +126,7 @@ int sd_dhcp_client_set_ifindex( int sd_dhcp_client_set_mac( sd_dhcp_client *client, const uint8_t *addr, + const uint8_t *bcast_addr, size_t addr_len, uint16_t arp_type); int sd_dhcp_client_set_client_id( From f1f17144114a37680847f4f2cf6c8c5930b718a7 Mon Sep 17 00:00:00 2001 From: Timo Rothenpieler Date: Wed, 28 Oct 2020 13:34:28 +0100 Subject: [PATCH 4/4] networkctl: use and print full hardware address Intentionally not checking the iftype for the ieee_oui lookup to support other ethernet-like devices. --- src/network/networkctl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 86e53e6da4..142ffc33e1 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -135,7 +135,7 @@ typedef struct LinkInfo { sd_device *sd_device; int ifindex; unsigned short iftype; - struct ether_addr mac_address; + hw_addr_data hw_address; struct ether_addr permanent_mac_address; uint32_t master; uint32_t mtu; @@ -416,13 +416,14 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b info->alternative_names = TAKE_PTR(altnames); info->has_mac_address = - sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && - memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; + netlink_message_read_hw_addr(m, IFLA_ADDRESS, &info->hw_address) >= 0 && + memcmp(&info->hw_address, &HW_ADDR_NULL, sizeof(hw_addr_data)) != 0; info->has_permanent_mac_address = ethtool_get_permanent_macaddr(NULL, info->name, &info->permanent_mac_address) >= 0 && memcmp(&info->permanent_mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0 && - memcmp(&info->permanent_mac_address, &info->mac_address, sizeof(struct ether_addr)) != 0; + (info->hw_address.length != sizeof(struct ether_addr) || + memcmp(&info->permanent_mac_address, info->hw_address.addr.bytes, sizeof(struct ether_addr)) != 0); (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu); (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu); @@ -1526,9 +1527,9 @@ static int link_status_one( if (info->has_mac_address) { _cleanup_free_ char *description = NULL; - char ea[ETHER_ADDR_TO_STRING_MAX]; - (void) ieee_oui(hwdb, &info->mac_address, &description); + if (info->hw_address.length == ETH_ALEN) + (void) ieee_oui(hwdb, &info->hw_address.addr.ether, &description); r = table_add_many(table, TABLE_EMPTY, @@ -1536,7 +1537,7 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); r = table_add_cell_stringf(table, NULL, "%s%s%s%s", - ether_addr_to_string(&info->mac_address, ea), + HW_ADDR_TO_STR(&info->hw_address), description ? " (" : "", strempty(description), description ? ")" : "");