X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-client.c;h=0cff6c4f33dfc165f65383c6124459e0a3c04e25;hb=11f93798663009d3a83b60ce899c778d9c1f5180;hp=02f3569edca877a0b117417318a33a4fc2aa7603;hpb=8bea7e700f5e73d9a92583f71647c9788defb843;p=thirdparty%2Fsystemd.git diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 02f3569edca..0cff6c4f33d 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -28,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) @@ -84,12 +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 *extra_options; + OrderedHashmap *vendor_options; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -233,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); @@ -260,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; } @@ -297,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); @@ -329,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; } @@ -360,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); @@ -375,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"); } } @@ -387,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 + @@ -421,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; @@ -482,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) { @@ -530,6 +555,43 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt return 0; } +int sd_dhcp_client_add_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->extra_options, &dhcp_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; + + sd_dhcp_option_ref(v); + return 0; +} + +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->vendor_options, &dhcp_option_hash_ops); + if (r < 0) + return -ENOMEM; + + r = ordered_hashmap_put(client->vendor_options, v, v); + if (r < 0) + return r; + + sd_dhcp_option_ref(v); + + return 1; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); @@ -615,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; @@ -791,6 +853,8 @@ static int dhcp_client_send_raw( static int client_send_discover(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *discover = NULL; size_t optoffset, optlen; + sd_dhcp_option *j; + Iterator i; int r; assert(client); @@ -807,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); @@ -843,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, @@ -852,6 +927,22 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + 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) @@ -964,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) @@ -1826,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) @@ -1927,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); @@ -1972,7 +2111,8 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { } static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { - assert(client); + if (!client) + return NULL; log_dhcp_client(client, "FREE"); @@ -1990,18 +2130,19 @@ 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->extra_options); + ordered_hashmap_free(client->vendor_options); return mfree(client); } DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free); int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { - _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; - assert_return(ret, -EINVAL); - client = new(sd_dhcp_client, 1); + _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = new(sd_dhcp_client, 1); if (!client) return -ENOMEM;