X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp6-client.c;h=6e006624ca111990017352ebd38f62f058fe7cae;hp=97a2ba7ea9ba57ca8a9e173fd2d6dccb8876a29b;hb=54d61deb7bffec5ca22cf765b13afbb0af547868;hpb=513a6fa8679510ea1b55967bdb482dd5f8a39f21 diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 97a2ba7ea..6e006624c 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -51,6 +51,9 @@ struct sd_dhcp6_client { be32_t transaction_id; struct sd_dhcp6_lease *lease; int fd; + be16_t *req_opts; + size_t req_opts_allocated; + size_t req_opts_len; sd_event_source *receive_message; usec_t retransmit_time; uint8_t retransmit_count; @@ -66,6 +69,12 @@ struct sd_dhcp6_client { } _packed_ duid; }; +static const uint16_t default_req_opts[] = { + DHCP6_OPTION_DNS_SERVERS, + DHCP6_OPTION_DOMAIN_LIST, + DHCP6_OPTION_NTP_SERVER, +}; + const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { [DHCP6_SOLICIT] = "SOLICIT", [DHCP6_ADVERTISE] = "ADVERTISE", @@ -95,6 +104,12 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref); +#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp) + +#define DHCP6_CLIENT_DONT_DESTROY(client) \ + _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) + static int client_start(sd_dhcp6_client *client, enum DHCP6State state); int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, @@ -131,6 +146,37 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, return 0; } +int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, + uint16_t option) { + size_t t; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + switch(option) { + case DHCP6_OPTION_DNS_SERVERS: + case DHCP6_OPTION_DOMAIN_LIST: + case DHCP6_OPTION_SNTP_SERVERS: + case DHCP6_OPTION_NTP_SERVER: + break; + + default: + return -EINVAL; + } + + for (t = 0; t < client->req_opts_len; t++) + if (client->req_opts[t] == htobe16(option)) + return -EEXIST; + + if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, + client->req_opts_len + 1)) + return -ENOMEM; + + client->req_opts[client->req_opts_len++] = htobe16(option); + + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); assert_return(ret, -EINVAL); @@ -143,14 +189,9 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { return 0; } -static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) { - if (client->cb) { - client = sd_dhcp6_client_ref(client); +static void client_notify(sd_dhcp6_client *client, int event) { + if (client->cb) client->cb(client, event, client->userdata); - client = sd_dhcp6_client_unref(client); - } - - return client; } static int client_reset(sd_dhcp6_client *client) { @@ -179,14 +220,14 @@ static int client_reset(sd_dhcp6_client *client) { return 0; } -static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) { - assert_return(client, NULL); +static void client_stop(sd_dhcp6_client *client, int error) { + DHCP6_CLIENT_DONT_DESTROY(client); - client = client_notify(client, error); - if (client) - client_reset(client); + assert(client); - return client; + client_notify(client, error); + + client_reset(client); } static int client_send_message(sd_dhcp6_client *client) { @@ -211,6 +252,9 @@ static int client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_SOLICITATION: message->type = DHCP6_SOLICIT; + r = dhcp6_option_append(&opt, &optlen, + DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na); if (r < 0) return r; @@ -218,7 +262,12 @@ static int client_send_message(sd_dhcp6_client *client) { break; case DHCP6_STATE_REQUEST: - message->type = DHCP6_REQUEST; + case DHCP6_STATE_RENEW: + + if (client->state == DHCP6_STATE_REQUEST) + message->type = DHCP6_REQUEST; + else + message->type = DHCP6_RENEW; r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID, client->lease->serverid_len, @@ -232,12 +281,27 @@ static int client_send_message(sd_dhcp6_client *client) { break; + case DHCP6_STATE_REBIND: + message->type = DHCP6_REBIND; + + r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); + if (r < 0) + return r; + + break; + case DHCP6_STATE_STOPPED: case DHCP6_STATE_RS: case DHCP6_STATE_BOUND: return -EINVAL; } + r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO, + client->req_opts_len * sizeof(be16_t), + client->req_opts); + if (r < 0) + return r; + r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID, sizeof(client->duid), &client->duid); if (r < 0) @@ -267,6 +331,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, log_dhcp6_client(client, "Timeout T2"); + client_start(client, DHCP6_STATE_REBIND); + return 0; } @@ -283,19 +349,30 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, log_dhcp6_client(client, "Timeout T1"); + client_start(client, DHCP6_STATE_RENEW); + return 0; } static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; + DHCP6_CLIENT_DONT_DESTROY(client); + enum DHCP6State state; assert(s); assert(client); assert(client->event); + state = client->state; + client_stop(client, DHCP6_EVENT_RESEND_EXPIRE); + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + client_start(client, DHCP6_STATE_SOLICITATION); + return 0; } @@ -312,6 +389,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, usec_t max_retransmit_duration; uint8_t max_retransmit_count = 0; char time_string[FORMAT_TIMESPAN_MAX]; + uint32_t expire = 0; assert(s); assert(client); @@ -342,6 +420,37 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, break; + case DHCP6_STATE_RENEW: + init_retransmit_time = DHCP6_REN_TIMEOUT; + max_retransmit_time = DHCP6_REN_MAX_RT; + max_retransmit_count = 0; + + /* RFC 3315, section 18.1.3. says max retransmit duration will + be the remaining time until T2. Instead of setting MRD, + wait for T2 to trigger with the same end result */ + max_retransmit_duration = 0; + + break; + + case DHCP6_STATE_REBIND: + init_retransmit_time = DHCP6_REB_TIMEOUT; + max_retransmit_time = DHCP6_REB_MAX_RT; + max_retransmit_count = 0; + + max_retransmit_duration = 0; + + if (!client->timeout_resend_expire) { + r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, + &expire); + if (r < 0) { + client_stop(client, r); + return 0; + } + max_retransmit_duration = expire * USEC_PER_SEC; + } + + break; + case DHCP6_STATE_STOPPED: case DHCP6_STATE_RS: case DHCP6_STATE_BOUND: @@ -423,6 +532,9 @@ error: } static int client_ensure_iaid(sd_dhcp6_client *client) { + /* name is a pointer to memory in the udev_device struct, so must + have the same scope */ + _cleanup_udev_device_unref_ struct udev_device *device = NULL; const char *name = NULL; uint64_t id; @@ -434,7 +546,6 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { if (detect_container(NULL) <= 0) { /* not in a container, udev will be around */ _cleanup_udev_unref_ struct udev *udev; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; char ifindex_str[2 + DECIMAL_STR_MAX(int)]; udev = udev_new(); @@ -552,6 +663,13 @@ static int client_parse_message(sd_dhcp6_client *client, } break; + + case DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; } } @@ -574,9 +692,10 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, { int r; _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL; + bool rapid_commit; if (reply->type != DHCP6_REPLY) - return -EINVAL; + return 0; r = dhcp6_lease_new(&lease); if (r < 0) @@ -586,6 +705,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, if (r < 0) return r; + if (client->state == DHCP6_STATE_SOLICITATION) { + r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); + if (r < 0) + return r; + + if (!rapid_commit) + return 0; + } + dhcp6_lease_clear_timers(&client->lease->ia); client->lease = sd_dhcp6_lease_unref(client->lease); @@ -602,7 +730,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, uint8_t pref_advertise = 0, pref_lease = 0; if (advertise->type != DHCP6_ADVERTISE) - return -EINVAL; + return 0; r = dhcp6_lease_new(&lease); if (r < 0) @@ -633,6 +761,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp6_client *client = userdata; + DHCP6_CLIENT_DONT_DESTROY(client); _cleanup_free_ DHCP6Message *message; int r, buflen, len; @@ -650,7 +779,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, len = read(fd, message, buflen); if ((size_t)len < sizeof(DHCP6Message)) { - log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno)); + log_dhcp6_client(client, "could not receive message from UDP socket: %m"); return 0; } @@ -686,12 +815,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, case DHCP6_STATE_SOLICITATION: r = client_receive_advertise(client, message, len); - if (r == DHCP6_STATE_REQUEST) + if (r == DHCP6_STATE_REQUEST) { client_start(client, r); - break; + break; + } + /* fall through for Soliciation Rapid Commit option check */ case DHCP6_STATE_REQUEST: + case DHCP6_STATE_RENEW: + case DHCP6_STATE_REBIND: + r = client_receive_reply(client, message, len); if (r < 0) return 0; @@ -704,9 +838,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, return 0; } - client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE); - if (!client) - return 0; + client_notify(client, DHCP6_EVENT_IP_ACQUIRE); } break; @@ -776,6 +908,8 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) break; case DHCP6_STATE_REQUEST: + case DHCP6_STATE_RENEW: + case DHCP6_STATE_REBIND: client->state = state; @@ -837,6 +971,8 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) if (r < 0) return r; + client->state = state; + return 0; } @@ -927,6 +1063,7 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { sd_dhcp6_client_detach_event(client); + free(client->req_opts); free(client); return NULL; @@ -935,14 +1072,12 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { return client; } -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref); -#define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp) - int sd_dhcp6_client_new(sd_dhcp6_client **ret) { - _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL; + _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL; sd_id128_t machine_id; int r; + size_t t; assert_return(ret, -EINVAL); @@ -971,6 +1106,15 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) siphash24(client->duid.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); + client->req_opts_len = ELEMENTSOF(default_req_opts); + + client->req_opts = new0(be16_t, client->req_opts_len); + if (!client->req_opts) + return -ENOMEM; + + for (t = 0; t < client->req_opts_len; t++) + client->req_opts[t] = htobe16(default_req_opts[t]); + *ret = client; client = NULL;