Like Infiniband. See RFC 4390 section 2.1 for details on DHCP and Infiniband; chaddr is zeroed, hlen is set to 0, and htype is set to ARPHRD_INFINIBAND because IB hardware addresses are 20 bytes in length. --- v3: use arp_type for identifying client MAC address types, and fix non-Ethernet hardware addresses in sd-dhcp6-client
src/libsystemd-network/dhcp-internal.h | 10 ++-- src/libsystemd-network/dhcp-network.c | 54 ++++++++++++++++++---- src/libsystemd-network/dhcp-packet.c | 8 ++-- src/libsystemd-network/sd-dhcp-client.c | 74 +++++++++++++++++++++++------- src/libsystemd-network/sd-dhcp-server.c | 8 ++-- src/libsystemd-network/sd-dhcp6-client.c | 34 ++++++++++---- src/libsystemd-network/test-dhcp-client.c | 14 ++++-- src/libsystemd-network/test-dhcp-option.c | 2 +- src/libsystemd-network/test-dhcp6-client.c | 8 +++- src/network/networkd-dhcp4.c | 4 +- src/network/networkd-link.c | 12 +++-- src/systemd/sd-dhcp-client.h | 4 +- src/systemd/sd-dhcp6-client.h | 4 +- 13 files changed, 178 insertions(+), 58 deletions(-) diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 1069c8a..d358a49 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -20,22 +20,25 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ #include <stdint.h> #include <linux/if_packet.h> +#include <net/if_arp.h> #include <net/ethernet.h> #include "socket-util.h" #include "sd-dhcp-client.h" #include "dhcp-protocol.h" -int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, uint32_t xid, struct ether_addr mac_addr); +int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, + uint32_t xid, const uint8_t *mac_addr, + size_t mac_addr_len, uint16_t arp_type); int dhcp_network_bind_udp_socket(be32_t address, uint16_t port); int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len); int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len); int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, @@ -43,16 +46,17 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_ typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, const uint8_t *option, void *user_data); int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *user_data); -int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, - size_t optlen, size_t *optoffset); +int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, + uint8_t type, uint16_t arp_type, size_t optlen, + size_t *optoffset); uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, uint16_t source, be32_t destination_addr, uint16_t destination, uint16_t len); diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index 1ced5cf..29e9993 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -18,27 +18,31 @@ ***/ #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <linux/if_packet.h> +#include <linux/if_infiniband.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <stdio.h> #include <unistd.h> #include <linux/filter.h> #include "socket-util.h" #include "dhcp-internal.h" -int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, - uint32_t xid, struct ether_addr mac_addr) { - +static int _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, + const struct ether_addr *eth_mac, + uint16_t arp_type, uint8_t dhcp_hlen) { struct sock_filter filter[] = { BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ @@ -53,29 +57,29 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header type == ARPHRD_ETHER ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- mac address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHER_ADDR_LEN, 1, 0), /* address length == ETHER_ADDR_LEN ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) &mac_addr))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) &mac_addr) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ @@ -103,27 +107,59 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) return -errno; link->ll.sll_family = AF_PACKET; link->ll.sll_protocol = htons(ETH_P_IP); link->ll.sll_ifindex = ifindex; - link->ll.sll_halen = ETH_ALEN; - memset(link->ll.sll_addr, 0xff, ETH_ALEN); + link->ll.sll_hatype = htons(arp_type); + link->ll.sll_halen = mac_addr_len; + memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len); r = bind(s, &link->sa, sizeof(link->ll)); if (r < 0) return -errno; r = s; s = -1; return r; } +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) { + 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; + uint8_t dhcp_hlen = 0; + + assert_return(mac_addr_len > 0, -EINVAL); + + 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; + } else if (arp_type == ARPHRD_INFINIBAND) { + assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL); + bcast_addr = ib_bcast; + } else + return -EINVAL; + + return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len, + bcast_addr, ð_mac, arp_type, dhcp_hlen); +} + int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { union sockaddr_union src = { .in.sin_family = AF_INET, .in.sin_port = htobe16(port), .in.sin_addr.s_addr = address, }; _cleanup_close_ int s = -1; diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 9f850fd..7581dae 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -34,23 +34,25 @@ #include "dhcp-internal.h" #include "sd-dhcp-lease.h" #include "sd-dhcp-client.h" #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, - uint8_t type, size_t optlen, size_t *optoffset) { + uint8_t type, uint16_t arp_type, size_t optlen, + size_t *optoffset) { size_t offset = 0; int r; assert(op == BOOTREQUEST || op == BOOTREPLY); + assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND); message->op = op; - message->htype = ARPHRD_ETHER; - message->hlen = ETHER_ADDR_LEN; + message->htype = arp_type; + message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0; message->xid = htobe32(xid); message->magic = htobe32(DHCP_MAGIC_COOKIE); r = dhcp_option_append(message, optlen, &offset, 0, DHCP_OPTION_MESSAGE_TYPE, 1, &type); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 2f94c16..0eba4c3 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -19,28 +19,31 @@ #include <stdlib.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <net/ethernet.h> #include <net/if_arp.h> +#include <linux/if_infiniband.h> #include <netinet/ether.h> #include <sys/param.h> #include <sys/ioctl.h> #include "util.h" #include "list.h" #include "refcnt.h" #include "async.h" #include "dhcp-protocol.h" #include "dhcp-internal.h" #include "dhcp-lease-internal.h" #include "sd-dhcp-client.h" +#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN + struct sd_dhcp_client { RefCount n_ref; DHCPState state; sd_event *event; int event_priority; sd_event_source *timeout_resend; @@ -53,14 +56,17 @@ struct sd_dhcp_client { size_t req_opts_allocated; size_t req_opts_size; be32_t last_addr; struct { uint8_t type; struct ether_addr mac_addr; } _packed_ client_id; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; char *hostname; char *vendor_class_identifier; uint32_t mtu; uint32_t xid; usec_t start_time; uint16_t secs; unsigned int attempt; @@ -159,32 +165,46 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) { assert_return(interface_index > 0, -EINVAL); client->index = interface_index; return 0; } -int sd_dhcp_client_set_mac(sd_dhcp_client *client, - const struct ether_addr *addr) { +int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; assert_return(client, -EINVAL); assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); - if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0) + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) return 0; if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Changing MAC address on running DHCP " "client, restarting"); need_restart = true; client_stop(client, DHCP_EVENT_STOP); } + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; + memcpy(&client->client_id.mac_addr, addr, ETH_ALEN); client->client_id.type = 0x01; if (need_restart && client->state != DHCP_STATE_STOPPED) sd_dhcp_client_start(client); return 0; @@ -314,15 +334,15 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, size = sizeof(DHCPPacket) + optlen; packet = malloc0(size); if (!packet) return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, - optlen, &optoffset); + client->arp_type, optlen, &optoffset); if (r < 0) return r; /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ packet->dhcp.secs = htobe16(client->secs); @@ -333,22 +353,25 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, DHCPREQUEST messages that client sends. The BROADCAST bit will provide a hint to the DHCP server and BOOTP relay agent to broadcast any messages to the client on the client's subnet. Note: some interfaces needs this to be enabled, but some networks needs this to be disabled as broadcasts are filteretd, so this needs to be configurable */ - if (client->request_broadcast) + if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) packet->dhcp.flags = htobe16(0x8000); /* RFC2132 section 4.1.1: The client MUST include its hardware address in the ’chaddr’ field, if - necessary for delivery of DHCP reply messages. + necessary for delivery of DHCP reply messages. Non-Ethernet + interfaces will leave 'chaddr' empty and use the client identifier + instead (eg, RFC 4390 section 2.1). */ - memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN); + if (client->arp_type == ARPHRD_ETHER) + memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, DHCP_OPTION_CLIENT_IDENTIFIER, sizeof(client->client_id), &client->client_id); if (r < 0) @@ -839,15 +862,17 @@ static int client_start(sd_dhcp_client *client) { assert_return(client->fd < 0, -EBUSY); assert_return(client->xid == 0, -EINVAL); assert_return(client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT, -EBUSY); client->xid = random_u32(); - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return r; } client->fd = r; if (client->state == DHCP_STATE_INIT) { @@ -883,15 +908,17 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->receive_message = sd_event_source_unref(client->receive_message); client->fd = asynchronous_close(client->fd); client->state = DHCP_STATE_REBINDING; client->attempt = 1; - r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid, client->client_id.mac_addr); + r = dhcp_network_bind_raw_socket(client->index, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); if (r < 0) { client_stop(client, r); return 0; } client->fd = r; return client_initialize_events(client, client_receive_message_raw); @@ -1327,14 +1354,17 @@ error: } static int client_receive_message_udp(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; int buflen = 0, len, r; + const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } }; + const struct ether_addr *expected_chaddr = NULL; + uint8_t expected_hlen = 0; assert(s); assert(client); r = ioctl(fd, FIONREAD, &buflen); if (r < 0) return r; @@ -1363,21 +1393,34 @@ static int client_receive_message_udp(sd_event_source *s, int fd, } if (message->op != BOOTREPLY) { log_dhcp_client(client, "not a BOOTREPLY message: ignoring"); return 0; } - if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) { - log_dhcp_client(client, "not an ethernet packet"); + if (message->htype != client->arp_type) { + log_dhcp_client(client, "packet type does not match client type"); return 0; } - if (memcmp(&message->chaddr[0], &client->client_id.mac_addr, - ETH_ALEN)) { + if (client->arp_type == ARPHRD_ETHER) { + expected_hlen = ETH_ALEN; + expected_chaddr = (const struct ether_addr *) &client->mac_addr; + } else { + /* Non-ethernet links expect zero chaddr */ + expected_hlen = 0; + expected_chaddr = &zero_mac; + } + + if (message->hlen != expected_hlen) { + log_dhcp_client(client, "unexpected packet hlen %d", message->hlen); + return 0; + } + + if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { log_dhcp_client(client, "received chaddr does not match " "expected: ignoring"); return 0; } if (client->state != DHCP_STATE_BOUND && be32toh(message->xid) != client->xid) { @@ -1451,31 +1494,28 @@ static int client_receive_message_raw(sd_event_source *s, int fd, len -= DHCP_IP_UDP_SIZE; return client_handle_message(client, &packet->dhcp, len); } int sd_dhcp_client_start(sd_dhcp_client *client) { - char buffer[ETHER_ADDR_TO_STRING_MAX]; int r; assert_return(client, -EINVAL); r = client_initialize(client); if (r < 0) return r; if (client->last_addr) client->state = DHCP_STATE_INIT_REBOOT; r = client_start(client); if (r >= 0) - log_dhcp_client(client, "STARTED on ifindex %u with address %s", - client->index, - ether_addr_to_string(&client->client_id.mac_addr, buffer)); + log_dhcp_client(client, "STARTED on ifindex %u", client->index); return r; } int sd_dhcp_client_stop(sd_dhcp_client *client) { DHCP_CLIENT_DONT_DESTROY(client); diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index a6d6178..24fedd2 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -388,16 +388,16 @@ static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret, assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)); packet = malloc0(sizeof(DHCPPacket) + req->max_optlen); if (!packet) return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREPLY, - be32toh(req->message->xid), type, req->max_optlen, - &optoffset); + be32toh(req->message->xid), type, ARPHRD_ETHER, + req->max_optlen, &optoffset); if (r < 0) return r; packet->dhcp.flags = req->message->flags; packet->dhcp.giaddr = req->message->giaddr; memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN); @@ -509,16 +509,16 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, assert(chaddr); packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE); if (!packet) return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0, - DHCP_FORCERENEW, DHCP_MIN_OPTIONS_SIZE, - &optoffset); + DHCP_FORCERENEW, ARPHRD_ETHER, + DHCP_MIN_OPTIONS_SIZE, &optoffset); if (r < 0) return r; r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE, &optoffset, 0, DHCP_OPTION_END, 0, NULL); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 6ea68c9..fa4f9b5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -18,14 +18,15 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ #include <errno.h> #include <string.h> #include <sys/ioctl.h> +#include <linux/if_infiniband.h> #include "udev.h" #include "udev-util.h" #include "virt.h" #include "siphash24.h" #include "util.h" #include "refcnt.h" @@ -40,22 +41,26 @@ #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) /* RFC 3315 section 9.1: * A DUID can be no more than 128 octets long (not including the type code). */ #define MAX_DUID_LEN 128 +#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN + struct sd_dhcp6_client { RefCount n_ref; enum DHCP6State state; sd_event *event; int event_priority; int index; - struct ether_addr mac_addr; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; DHCP6IA ia_na; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; int fd; be16_t *req_opts; size_t req_opts_allocated; @@ -156,23 +161,36 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) assert_return(interface_index >= -1, -EINVAL); client->index = interface_index; return 0; } -int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, - const struct ether_addr *mac_addr) +int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { assert_return(client, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); - if (mac_addr) - memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr)); + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - memset(&client->mac_addr, 0x00, sizeof(client->mac_addr)); + return -EINVAL; + + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) + return 0; + + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; return 0; } int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, size_t duid_len) { @@ -642,16 +660,16 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { name = net_get_name(device); } if (name) siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); else /* fall back to mac address if no predictable name available */ - siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN, - HASH_KEY.bytes); + siphash24((uint8_t*)&id, &client->mac_addr, + client->mac_addr_len, HASH_KEY.bytes); /* fold into 32 bits */ client->ia_na.id = (id & 0xffffffff) ^ (id >> 32); return 0; } diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index c48aa04..7dab97d 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -192,15 +192,17 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, assert_se(callback_recv); callback_recv(size, &discover->dhcp); return 575; } -int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, uint32_t id, struct ether_addr mac) +int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, + uint32_t id, const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) return -errno; return test_fd[0]; } @@ -240,15 +242,18 @@ static void test_discover_message(sd_event *e) assert_se(r >= 0); assert_se(client); r = sd_dhcp_client_attach_event(client, e, 0); assert_se(r >= 0); assert_se(sd_dhcp_client_set_index(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0); + assert_se(sd_dhcp_client_set_mac(client, + (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0); callback_recv = test_discover_message_verify; res = sd_dhcp_client_start(client); @@ -458,15 +463,18 @@ static void test_addr_acq(sd_event *e) { assert_se(r >= 0); assert_se(client); r = sd_dhcp_client_attach_event(client, e, 0); assert_se(r >= 0); assert_se(sd_dhcp_client_set_index(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0); + assert_se(sd_dhcp_client_set_mac(client, + (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0); callback_recv = test_addr_acq_recv_discover; assert_se(sd_event_add_time(e, &test_hangcheck, diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 63cdc7a..eac3844 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -88,15 +88,15 @@ static void test_message_init(void) size_t optlen = 4, optoffset; size_t len = sizeof(DHCPMessage) + optlen; uint8_t *magic; message = malloc0(len); assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, - DHCP_DISCOVER, optlen, &optoffset) >= 0); + DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0); assert_se(message->xid == htobe32(0x12345678)); assert_se(message->op == BOOTREQUEST); magic = (uint8_t*)&message->magic; assert_se(magic[0] == 99); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 37ddfc2..26b28a2 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -62,15 +62,17 @@ static int test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); assert_se(sd_dhcp6_client_set_index(client, 15) == 0); assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL); assert_se(sd_dhcp6_client_set_index(client, -1) == 0); assert_se(sd_dhcp6_client_set_index(client, 42) >= 0); - assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0); + assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); @@ -568,15 +570,17 @@ static int test_client_solicit(sd_event *e) { assert_se(sd_dhcp6_client_new(&client) >= 0); assert_se(client); assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); assert_se(sd_dhcp6_client_set_index(client, test_index) == 0); - assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0); + assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(), time_now + 2 * USEC_PER_SEC, 0, test_hangcheck, NULL) >= 0); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index e451af8..63bfa86 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -595,15 +595,17 @@ int dhcp4_configure(Link *link) { if (r < 0) return r; r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); if (r < 0) return r; - r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac); + r = sd_dhcp_client_set_mac(link->dhcp_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); if (r < 0) return r; r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index dcbe38a..c6e173f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -905,15 +905,17 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) { r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); if (r < 0) { link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); return; } - r = sd_dhcp6_client_set_mac(link->dhcp6_client, &link->mac); + r = sd_dhcp6_client_set_mac(link->dhcp6_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); if (r < 0) { link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); return; } r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); if (r < 0) { @@ -1643,26 +1645,30 @@ int link_update(Link *link, sd_rtnl_message *m) { strerror(-r)); return r; } } if (link->dhcp_client) { r = sd_dhcp_client_set_mac(link->dhcp_client, - &link->mac); + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); if (r < 0) { log_warning_link(link, "Could not update MAC address in DHCP client: %s", strerror(-r)); return r; } } if (link->dhcp6_client) { r = sd_dhcp6_client_set_mac(link->dhcp6_client, - &link->mac); + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); if (r < 0) { log_warning_link(link, "Could not update MAC address in DHCPv6 client: %s", strerror(-r)); return r; } } diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 98c6782..7416f82 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -45,16 +45,16 @@ int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option); int sd_dhcp_client_set_request_address(sd_dhcp_client *client, const struct in_addr *last_address); int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast); int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index); -int sd_dhcp_client_set_mac(sd_dhcp_client *client, - const struct ether_addr *addr); +int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type); int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu); int sd_dhcp_client_set_hostname(sd_dhcp_client *client, const char *hostname); int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, const char *vci); int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index a4409e8..c7f168f 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -39,16 +39,16 @@ typedef struct sd_dhcp6_client sd_dhcp6_client; typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event, void *userdata); int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata); int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); -int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, - const struct ether_addr *mac_addr); +int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, + size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, size_t duid_len); int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option); int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret); -- 1.9.3 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel