X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-client.c;h=0cff6c4f33dfc165f65383c6124459e0a3c04e25;hb=11f93798663009d3a83b60ce899c778d9c1f5180;hp=ef330fe28ebd6bd1213b9551e01909c13a8e7693;hpb=a859abf062cef1511e4879c4ee39c6036ebeaec8;p=thirdparty%2Fsystemd.git diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index ef330fe28eb..0cff6c4f33d 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -27,6 +27,7 @@ #include "random-util.h" #include "string-util.h" #include "strv.h" +#include "web-util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -34,14 +35,6 @@ #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) -struct sd_dhcp_option { - unsigned n_ref; - - uint8_t option; - void *data; - size_t length; -}; - struct sd_dhcp_client { unsigned n_ref; @@ -91,13 +84,15 @@ struct sd_dhcp_client { size_t client_id_len; char *hostname; char *vendor_class_identifier; + char *mudurl; char **user_class; uint32_t mtu; uint32_t xid; usec_t start_time; uint64_t attempt; uint64_t max_attempts; - OrderedHashmap *options; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -241,6 +236,7 @@ int sd_dhcp_client_set_mac( DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; + int r; assert_return(client, -EINVAL); assert_return(addr, -EINVAL); @@ -268,8 +264,11 @@ int sd_dhcp_client_set_mac( client->mac_addr_len = addr_len; client->arp_type = arp_type; - if (need_restart && client->state != DHCP_STATE_STOPPED) - sd_dhcp_client_start(client); + if (need_restart && client->state != DHCP_STATE_STOPPED) { + r = sd_dhcp_client_start(client); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m"); + } return 0; } @@ -305,6 +304,7 @@ int sd_dhcp_client_set_client_id( DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; + int r; assert_return(client, -EINVAL); assert_return(data, -EINVAL); @@ -337,8 +337,11 @@ int sd_dhcp_client_set_client_id( memcpy(&client->client_id.raw.data, data, data_len); client->client_id_len = data_len + sizeof (client->client_id.type); - if (need_restart && client->state != DHCP_STATE_STOPPED) - sd_dhcp_client_start(client); + if (need_restart && client->state != DHCP_STATE_STOPPED) { + r = sd_dhcp_client_start(client); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m"); + } return 0; } @@ -368,7 +371,7 @@ static int dhcp_client_set_iaid_duid_internal( if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m"); } zero(client->client_id); @@ -383,7 +386,7 @@ static int dhcp_client_set_iaid_duid_internal( true, &client->client_id.ns.iaid); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to set IAID: %m"); } } @@ -395,32 +398,32 @@ static int dhcp_client_set_iaid_duid_internal( switch (duid_type) { case DUID_TYPE_LLT: if (client->mac_addr_len == 0) - return -EOPNOTSUPP; + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); break; case DUID_TYPE_EN: r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); break; case DUID_TYPE_LL: if (client->mac_addr_len == 0) - return -EOPNOTSUPP; + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); break; case DUID_TYPE_UUID: r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); if (r < 0) - return r; + return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); break; default: - return -EINVAL; + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); } client->client_id_len = sizeof(client->client_id.type) + len + @@ -429,7 +432,9 @@ static int dhcp_client_set_iaid_duid_internal( if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); - sd_dhcp_client_start(client); + r = sd_dhcp_client_start(client); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m"); } return 0; @@ -490,6 +495,18 @@ int sd_dhcp_client_set_vendor_class_identifier( return free_and_strdup(&client->vendor_class_identifier, vci); } +int sd_dhcp_client_set_mud_url( + sd_dhcp_client *client, + const char *mudurl) { + + assert_return(client, -EINVAL); + assert_return(mudurl, -EINVAL); + assert_return(strlen(mudurl) <= 255, -EINVAL); + assert_return(http_url_is_valid(mudurl), -EINVAL); + + return free_and_strdup(&client->mudurl, mudurl); +} + int sd_dhcp_client_set_user_class( sd_dhcp_client *client, const char* const* user_class) { @@ -538,62 +555,41 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt return 0; } -static sd_dhcp_option* dhcp_option_free(sd_dhcp_option *i) { - if (!i) - return NULL; - - free(i->data); - return mfree(i); -} - -int sd_dhcp_option_new(uint8_t option, void *data, size_t length, sd_dhcp_option **ret) { - assert_return(ret, -EINVAL); - assert_return(length == 0 || data, -EINVAL); +int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) { + int r; - _cleanup_free_ void *q = memdup(data, length); - if (!q) - return -ENOMEM; + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); - sd_dhcp_option *p = new(sd_dhcp_option, 1); - if (!p) - return -ENOMEM; + r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops); + if (r < 0) + return r; - *p = (sd_dhcp_option) { - .n_ref = 1, - .option = option, - .length = length, - .data = TAKE_PTR(q), - }; + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; - *ret = TAKE_PTR(p); + sd_dhcp_option_ref(v); return 0; } -DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_option, sd_dhcp_option, dhcp_option_free); -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( - dhcp_option_hash_ops, - void, - trivial_hash_func, - trivial_compare_func, - sd_dhcp_option, - sd_dhcp_option_unref); - -int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) { +int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) { int r; assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops); + r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops); if (r < 0) - return r; + return -ENOMEM; - r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v); + r = ordered_hashmap_put(client->vendor_options, v, v); if (r < 0) return r; sd_dhcp_option_ref(v); - return 0; + + return 1; } int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { @@ -681,7 +677,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE)); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -875,7 +871,9 @@ static int client_send_discover(sd_dhcp_client *client) { address be assigned, and may include the ’IP address lease time’ option to suggest the lease time it would like. */ - if (client->last_addr != INADDR_ANY) { + /* RFC7844 section 3: + SHOULD NOT contain any other option. */ + if (!client->anonymize && client->last_addr != INADDR_ANY) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, 4, &client->last_addr); @@ -911,6 +909,15 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + if (client->mudurl) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_MUD_URL, + strlen(client->mudurl), + client->mudurl); + if (r < 0) + return r; + } + if (client->user_class) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_USER_CLASS, @@ -920,13 +927,22 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - ORDERED_HASHMAP_FOREACH(j, client->options, i) { + ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) { r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, j->option, j->length, j->data); if (r < 0) return r; } + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp_option_append( + &discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_VENDOR_SPECIFIC, + ordered_hashmap_size(client->vendor_options), client->vendor_options); + if (r < 0) + return r; + } + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -1039,6 +1055,16 @@ static int client_send_request(sd_dhcp_client *client) { return r; } + if (client->mudurl) { + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_MUD_URL, + strlen(client->mudurl), + client->mudurl); + if (r < 0) + return r; + } + + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -1901,25 +1927,21 @@ static int client_receive_message_raw( iov = IOVEC_MAKE(packet, buflen); - len = recvmsg(fd, &msg, 0); - if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) - return 0; - - return log_dhcp_client_errno(client, errno, - "Could not receive message from raw socket: %m"); - } else if ((size_t)len < sizeof(DHCPPacket)) + len = recvmsg_safe(fd, &msg, 0); + if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN)) return 0; + if (len < 0) + return log_dhcp_client_errno(client, len, + "Could not receive message from raw socket: %m"); - CMSG_FOREACH(cmsg, &msg) - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); + if ((size_t) len < sizeof(DHCPPacket)) + return 0; - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - break; - } + cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata))); + if (cmsg) { + struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg); + checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); + } r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) @@ -2002,6 +2024,48 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) { return 0; } +int sd_dhcp_client_send_decline(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE); + assert_return(client->lease, -EUNATCH); + + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset); + if (r < 0) + return r; + + release->dhcp.ciaddr = client->lease->address; + memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); + + r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &release->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "DECLINE"); + + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + + if (client->state != DHCP_STATE_STOPPED) { + r = sd_dhcp_client_start(client); + if (r < 0) + return r; + } + + return 0; +} + int sd_dhcp_client_stop(sd_dhcp_client *client) { DHCP_CLIENT_DONT_DESTROY(client); @@ -2066,8 +2130,10 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { free(client->req_opts); free(client->hostname); free(client->vendor_class_identifier); + free(client->mudurl); client->user_class = strv_free(client->user_class); - ordered_hashmap_free(client->options); + ordered_hashmap_free(client->extra_options); + ordered_hashmap_free(client->vendor_options); return mfree(client); }