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(