X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-client.c;h=0cff6c4f33dfc165f65383c6124459e0a3c04e25;hb=11f93798663009d3a83b60ce899c778d9c1f5180;hp=af3ede1b54adbc1408dab8f144a94e6b8f176c5c;hpb=944e23b8a01b327c573ad89f603e2dafd8f5dbb7;p=thirdparty%2Fsystemd.git diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index af3ede1b54a..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; @@ -500,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) { @@ -548,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) { @@ -691,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; @@ -885,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); @@ -921,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, @@ -930,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) @@ -1049,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) @@ -1911,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) @@ -2012,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); @@ -2076,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); }