]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp4: fix DHCP on InfiniBand interfaces
authorTimo Rothenpieler <timo@rothenpieler.org>
Mon, 26 Oct 2020 13:09:13 +0000 (14:09 +0100)
committerTimo Rothenpieler <timo@rothenpieler.org>
Wed, 28 Oct 2020 13:44:43 +0000 (14:44 +0100)
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
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/test-dhcp-client.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-link.c
src/network/networkd-link.h
src/systemd/sd-dhcp-client.h

index 7e8149487a8d03cd71ae7072a669655711f752f9..d0c99f985b877df39939e3d5507af38847bd4676 100644 (file)
@@ -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);
index f48e7c34909291117744fbb79a8c6966a531b960..c5ee7e7ef24351f4b0faf5f001ac59a607d39270 100644 (file)
@@ -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(&eth_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, &eth_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,
+                                &eth_mac, arp_type, dhcp_hlen, port);
 }
 
 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
index 2bfe64a81c4c083640e6e08e5ecc91843809f183..d95bbd316714b85fafe21f93a29b0f20d4602c22 100644 (file)
@@ -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;
index 4d748ea66d0750eddd655b603c59a1b816b0bbda..b916212d6f9785009ba6095958458f2c5d009b70 100644 (file)
@@ -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);
 
index 5255ec3e1c73099da1983869e838da19af3b31a7..319ef6159373260d9d23576324bba3e63af8ab74 100644 (file)
@@ -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;
 
index afff1dfc1571f9294d8be28e7d3ea9959947d430..e7dc10c2bef595f68da6c9188ea93b648bb0a498 100644 (file)
@@ -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;
 
index 09622de868bcd50d9088152980dea091d849fe95..1eca07771a3fae1e5a2ffa66d2d9a36f96bddabd 100644 (file)
@@ -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");
index bdeee0fe857fa5c3b40dcb87af7b85af1c9200ab..159e7e705b93e2df7649f8546b54caaf109d45ac 100644 (file)
@@ -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;
index ac3b5b369c11319b3224c1a98cc9a872c58dec84..e0aa93a55b4dfe08ccdb30262e40350eb60426d7 100644 (file)
@@ -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(