diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 63d3f60513..5e91e86f53 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -508,7 +508,7 @@ int dhcp6_option_parse( if (buflen < offsetof(DHCP6Option, data)) return -EBADMSG; - if (*offset >= buflen - offsetof(DHCP6Option, data)) + if (*offset > buflen - offsetof(DHCP6Option, data)) return -EBADMSG; len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len)); @@ -518,14 +518,14 @@ int dhcp6_option_parse( *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code)); *ret_option_data_len = len; - *ret_option_data = buf + *offset + offsetof(DHCP6Option, data); + *ret_option_data = len == 0 ? NULL : buf + *offset + offsetof(DHCP6Option, data); *offset += offsetof(DHCP6Option, data) + len; return 0; } int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) { - assert(data); + assert(data || data_len == 0); if (data_len < sizeof(uint16_t)) return -EBADMSG; @@ -803,7 +803,7 @@ int dhcp6_option_parse_addresses( struct in6_addr **addrs, size_t *count) { - assert(optval); + assert(optval || optlen == 0); assert(addrs); assert(count); @@ -826,8 +826,8 @@ static int parse_domain(const uint8_t **data, size_t *len, char **ret) { int r; assert(data); - assert(*data); assert(len); + assert(*data || *len == 0); assert(ret); optval = *data; @@ -891,7 +891,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r _cleanup_free_ char *domain = NULL; int r; - assert(optval); + assert(optval || optlen == 0); assert(ret); r = parse_domain(&optval, &optlen, &domain); @@ -910,7 +910,7 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha _cleanup_strv_free_ char **names = NULL; int r; - assert(optval); + assert(optval || optlen == 0); assert(ret); if (optlen <= 1) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 2475cdb7ed..f588514cb6 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -467,7 +467,9 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, + "Failed to parse option header at offset %zu of total length %zu: %m", + offset, len); switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: @@ -477,7 +479,7 @@ static int dhcp6_lease_parse_message( r = dhcp6_lease_set_clientid(lease, optval, optlen); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m"); break; @@ -488,17 +490,17 @@ static int dhcp6_lease_parse_message( r = dhcp6_lease_set_serverid(lease, optval, optlen); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m"); break; case SD_DHCP6_OPTION_PREFERENCE: if (optlen != 1) - return -EINVAL; + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference."); r = dhcp6_lease_set_preference(lease, optval[0]); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to set preference: %m"); break; @@ -507,7 +509,7 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse_status(optval, optlen, &msg); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m"); if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -527,9 +529,11 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); if (r == -ENOMEM) - return r; - if (r < 0) + return log_oom_debug(); + if (r < 0) { + log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m"); continue; + } if (lease->ia_na) { log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); @@ -550,9 +554,11 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); if (r == -ENOMEM) - return r; - if (r < 0) + return log_oom_debug(); + if (r < 0) { + log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m"); continue; + } if (lease->ia_pd) { log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); @@ -564,9 +570,12 @@ static int dhcp6_lease_parse_message( break; } case SD_DHCP6_OPTION_RAPID_COMMIT: + if (optlen != 0) + log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen); + r = dhcp6_lease_set_rapid_commit(lease); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m"); break; @@ -607,7 +616,8 @@ static int dhcp6_lease_parse_message( case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: if (optlen != 4) - return -EINVAL; + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received information refresh time option with an invalid length (%zu).", optlen); irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; break; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 95a6c514c6..e5c09f9664 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -443,6 +443,48 @@ TEST(client_parse_message_issue_22099) { assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0); } +TEST(client_parse_message_issue_24002) { + static const uint8_t msg[] = { + /* Message Type */ + 0x07, + /* Transaction ID */ + 0x0e, 0xa5, 0x7c, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + 0x00, 0x02, /* DUID-EN */ + 0x00, 0x00, 0xab, 0x11, /* pen */ + 0x5c, 0x6b, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, /* id */ + /* Server ID */ + 0x00, 0x02, 0x00, 0x1a, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x83, 0x30, 0x63, 0x3a, 0x38, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + /* IA_PD */ + 0x00, 0x19, 0x00, 0x29, + 0xaa, 0xbb, 0xcc, 0xdd, /* iaid */ + 0x00, 0x00, 0x03, 0x84, /* lifetime (T1) */ + 0x00, 0x00, 0x05, 0xa0, /* lifetime (T2) */ + /* IA_PD (iaprefix suboption) */ + 0x00, 0x1a, 0x00, 0x19, + 0x00, 0x00, 0x07, 0x08, /* preferred lifetime */ + 0x00, 0x00, 0x38, 0x40, /* valid lifetime */ + 0x38, /* prefixlen */ + 0x20, 0x03, 0x00, 0xff, 0xaa, 0xbb, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* prefix */ + /* Rapid commit */ + 0x00, 0x0e, 0x00, 0x00, + }; + static const uint8_t duid[] = { + 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + }; + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + + assert_se(sd_dhcp6_client_new(&client) >= 0); + assert_se(sd_dhcp6_client_set_iaid(client, 0xaabbccdd) >= 0); + assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0); + + assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0); +} + static const uint8_t msg_information_request[] = { /* Message type */ DHCP6_MESSAGE_INFORMATION_REQUEST, diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 43c8ca42fe..ce5262fcd6 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -52,6 +52,10 @@ bool link_dhcp_enabled(Link *link, int family) { assert(link); assert(IN_SET(family, AF_INET, AF_INET6)); + /* Currently, sd-dhcp-client supports only ethernet and infiniband. */ + if (family == AF_INET && !IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND)) + return false; + if (family == AF_INET6 && !socket_ipv6_is_supported()) return false; @@ -61,13 +65,6 @@ bool link_dhcp_enabled(Link *link, int family) { if (link->iftype == ARPHRD_CAN) return false; - if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) && - !streq_ptr(link->kind, "wwan")) - /* Currently, only interfaces whose MAC address length is ETH_ALEN or INFINIBAND_ALEN - * are supported. Note, wwan interfaces may be assigned MAC address slightly later. - * Hence, let's wait for a while.*/ - return false; - if (!link->network) return false; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 5770b48767..20db4f5f83 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1545,11 +1545,6 @@ static int dhcp4_process_request(Request *req, Link *link, void *userdata) { if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; - if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) || - hw_addr_is_null(&link->hw_addr)) - /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ - return 0; - r = dhcp4_configure_duid(link); if (r <= 0) return r; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 3dc34f0e52..72ec6826bd 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -738,11 +738,6 @@ static int dhcp6_process_request(Request *req, Link *link, void *userdata) { if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; - if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) || - hw_addr_is_null(&link->hw_addr)) - /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ - return 0; - r = dhcp_configure_duid(link, link_get_dhcp6_duid(link)); if (r <= 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 29a5609483..b314f5db8f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2192,7 +2192,7 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) if (r < 0) return log_link_debug_errno(link, r, "Could not update MAC address for Router Advertisement: %m"); - if (link->ndisc) { + if (link->ndisc && link->hw_addr.length == ETH_ALEN) { r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether); if (r < 0) return log_link_debug_errno(link, r, "Could not update MAC for NDisc: %m"); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f66745a4ff..2b02978ab4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -38,12 +38,6 @@ bool link_ipv6_accept_ra_enabled(Link *link) { if (link->iftype == ARPHRD_CAN) return false; - if (link->hw_addr.length != ETH_ALEN && !streq_ptr(link->kind, "wwan")) - /* Currently, only interfaces whose MAC address length is ETH_ALEN are supported. - * Note, wwan interfaces may be assigned MAC address slightly later. - * Hence, let's wait for a while.*/ - return false; - if (!link->network) return false; @@ -1060,9 +1054,11 @@ static int ndisc_configure(Link *link) { if (r < 0) return r; - r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether); - if (r < 0) - return r; + if (link->hw_addr.length == ETH_ALEN) { + r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether); + if (r < 0) + return r; + } r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex); if (r < 0) @@ -1106,10 +1102,6 @@ static int ndisc_process_request(Request *req, Link *link, void *userdata) { if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; - if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr)) - /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ - return 0; - r = ndisc_configure(link); if (r < 0) return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m"); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 26448b518d..32ee7aa7ab 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -458,9 +458,11 @@ static int radv_configure(Link *link) { if (r < 0) return r; - r = sd_radv_set_mac(link->radv, &link->hw_addr.ether); - if (r < 0) - return r; + if (link->hw_addr.length == ETH_ALEN) { + r = sd_radv_set_mac(link->radv, &link->hw_addr.ether); + if (r < 0) + return r; + } r = sd_radv_set_ifindex(link->radv, link->ifindex); if (r < 0) @@ -518,6 +520,9 @@ int radv_update_mac(Link *link) { if (!link->radv) return 0; + if (link->hw_addr.length != ETH_ALEN) + return 0; + restart = sd_radv_is_running(link->radv); r = sd_radv_stop(link->radv);