diff options
Diffstat (limited to 'shared/n-dhcp4/src/n-dhcp4-c-lease.c')
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-lease.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-lease.c b/shared/n-dhcp4/src/n-dhcp4-c-lease.c new file mode 100644 index 00000000..db1ffbae --- /dev/null +++ b/shared/n-dhcp4/src/n-dhcp4-c-lease.c @@ -0,0 +1,356 @@ +/* + * DHCP4 Client Leases + * + * This implements the public API wrapping DHCP4 client leases. A lease object + * conists of the information given to us from the server, together with the + * timestamp recording the start of the validity of the lease. + * + * A probe may yield many OFFERS, each of which contains a lease object. One of + * these offers may be SELECTED, which implicitly rejects all the others. + * The server may then ACK or NAK the lease which tells us whether or not we + * are permitted to start using it. Once an ACK has been received, we can + * configure the address, and only then can we SELECT the lease. If we + * determine that the offered lease was not appropriate after all we + * may DECLINE it instead. + */ + +#include <assert.h> +#include <c-list.h> +#include <c-stdaux.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "n-dhcp4.h" +#include "n-dhcp4-private.h" + +/* + * Compute the absolute timeouts from an incoming message. A message contains relative timeouts and the userdata + * of the incoming message is set to the offset we must apply to get the absolute values. + * + * The special value UINT64_MAX is returned to indicate no or infinite timeouts. In case the given timeouts + * are invalid relative to each other, we recompute T1 and/or T2 to take their default values. Later timeouts + * take predecende above earlier ones (T1 is adjusted if it conflicts with T2, etc). + */ +static int n_dhcp4_incoming_get_timeouts(NDhcp4Incoming *message, uint64_t *t1p, uint64_t *t2p, uint64_t *lifetimep) { + uint64_t lifetime, t2, t1; + uint32_t u32; + int r; + + r = n_dhcp4_incoming_query_lifetime(message, &u32); + if (r == N_DHCP4_E_UNSET) { + lifetime = UINT64_MAX; + } else if (r) { + return r; + } else if (u32 == UINT32_MAX) { + lifetime = UINT64_MAX; + } else { + if (u32 == UINT32_MAX) + lifetime = UINT64_MAX; + else + lifetime = u32 * (1000000000ULL); + } + + r = n_dhcp4_incoming_query_t2(message, &u32); + if (r == N_DHCP4_E_UNSET) { + if (lifetime == UINT64_MAX) + t2 = UINT64_MAX; + else + t2 = (lifetime * 7) / 8; + } else if (r) { + return r; + } else { + if (u32 == UINT32_MAX) + t2 = UINT64_MAX; + else + t2 = u32 * (1000000000ULL); + + if (t2 > lifetime) + t2 = (lifetime * 7) / 8; + } + + r = n_dhcp4_incoming_query_t1(message, &u32); + if (r == N_DHCP4_E_UNSET) { + if (t2 == UINT64_MAX) + t1 = UINT64_MAX; + else + t1 = (t2 * 4) / 7; + } else if (r) { + return r; + } else { + if (u32 == UINT32_MAX) + t1 = UINT64_MAX; + else + t1 = u32 * (1000000000ULL); + + if (t1 > t2) + t1 = (t2 * 4) / 7; + } + + if (lifetime != UINT64_MAX) + lifetime += message->userdata.base_time; + if (t2 != UINT64_MAX) + t2 += message->userdata.base_time; + if (t1 != UINT64_MAX) + t1 += message->userdata.base_time; + + *lifetimep = lifetime; + *t2p = t2; + *t1p = t1; + return 0; +} + +/** + * n_dhcp4_client_lease_new() - allocate new client lease object + * @leasep: output argumnet for new client lease object + * @message: incoming message representing the lease + * + * This creates a new client lease object. Client lease objects are simple + * wrappers around an incoming message representing a lease. + * + * Return: 0 on success, negative error code on failure. + */ +int n_dhcp4_client_lease_new(NDhcp4ClientLease **leasep, NDhcp4Incoming *message) { + _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; + int r; + + c_assert(leasep); + + lease = malloc(sizeof(*lease)); + if (!lease) + return -ENOMEM; + + *lease = (NDhcp4ClientLease)N_DHCP4_CLIENT_LEASE_NULL(*lease); + + r = n_dhcp4_incoming_get_timeouts(message, &lease->t1, &lease->t2, &lease->lifetime); + if (r) + return r; + + lease->message = message; + *leasep = lease; + lease = NULL; + return 0; +} + +static void n_dhcp4_client_lease_free(NDhcp4ClientLease *lease) { + n_dhcp4_client_lease_unlink(lease); + n_dhcp4_incoming_free(lease->message); + free(lease); +} + +/** + * n_dhcp4_client_lease_ref() - reference client lease + * @lease: the client lease object to reference + * + * Take a new reference to a client lease. + * + * Return: the lease. + */ +_c_public_ NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease) { + if (lease) + ++lease->n_refs; + return lease; +} + +/** + * n_dhcp4_client_lease_unref() - dereference client lease + * @lease: the client lease object to dereference + * + * Relase a reference to a client lease. + * + * Return: NULL. + */ +_c_public_ NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease) { + if (lease && !--lease->n_refs) + n_dhcp4_client_lease_free(lease); + return NULL; +} + +/** + * n_dhcp4_client_lease_link() - link lease into probe + * @lease: the lease to operate on + * @probe: the probe to link the lease into + * + * Associate a lease with a probe. The lease may not already be linked. + */ +void n_dhcp4_client_lease_link(NDhcp4ClientLease *lease, NDhcp4ClientProbe *probe) { + c_assert(!lease->probe); + c_assert(!c_list_is_linked(&lease->probe_link)); + + lease->probe = probe; + c_list_link_tail(&probe->lease_list, &lease->probe_link); +} + +/** + * n_dhcp4_client_lease_unlink() - unlinke lease from its probe + * @lease: the lease to operate on + * + * Dissassociate a lease from a probe if it is associated with one. Otherwise, + * this is a noop. + */ +void n_dhcp4_client_lease_unlink(NDhcp4ClientLease *lease) { + lease->probe = NULL; + c_list_unlink(&lease->probe_link); +} + +/** + * n_dhcp4_client_lease_get_yiaddr() - get the IP address + * @lease: the lease to operate on + * @yiaddr: return argument for the IP address + * + * Gets the IP address cotained in the lease. Or INADDR_ANY if the lease + * does not contain an IP address. + */ +_c_public_ void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr) { + NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message); + + yiaddr->s_addr = header->yiaddr; +} + +/** + * n_dhcp4_client_lease_get_lifetime() - get the lifetime + * @lease: the lease to operate on + * @ns_lifetimep: return argument for the lifetime in nano seconds + * + * Gets the end of the lease's lifetime in nanoseconds according to CLOCK_BOOTTIME, + * or (uint64_t)-1 for permanent leases. + */ +_c_public_ void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep) { + *ns_lifetimep = lease->lifetime; +} + +/** + * n_dhcp4_client_lease_query() - query the lease for an option + * @lease: the lease to operate on + * @option: the DHCP4 option code + * @datap: return argument of the data pointer + * @n_datap: return argument of data length in bytes + * + * Query the lease for a given option. Options internal to the DHCP protocol cannot + * be queried, and only options that were explicitly requested can be queried. + * + * Return: 0 on success, + * N_DCHP4_E_INTERNAL if an invalid option is queried, + * N_DHCP4_E_UNSET if the lease did not contain the option, or + * a negative error code on failure. + */ +_c_public_ int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap) { + switch (option) { + case N_DHCP4_OPTION_PAD: + case N_DHCP4_OPTION_REQUESTED_IP_ADDRESS: + case N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME: + case N_DHCP4_OPTION_OVERLOAD: + case N_DHCP4_OPTION_MESSAGE_TYPE: + case N_DHCP4_OPTION_SERVER_IDENTIFIER: + case N_DHCP4_OPTION_PARAMETER_REQUEST_LIST: + case N_DHCP4_OPTION_ERROR_MESSAGE: + case N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE: + case N_DHCP4_OPTION_RENEWAL_T1_TIME: + case N_DHCP4_OPTION_REBINDING_T2_TIME: + case N_DHCP4_OPTION_END: + return N_DHCP4_E_INTERNAL; + } + + /* XXX: refuse to return options that were not requested */ + + return n_dhcp4_incoming_query(lease->message, option, datap, n_datap); +} + +/** + * n_dhcp4_client_lease_select() - select an offered lease + * @lease: lease to operate on + * + * Select a lease. This must be a lease that was offered, once + * one of the leases that were offered in response to a probe was + * selected none of the others can be. + * + * Return: 0 on success, or a negative error code on failure. + */ +_c_public_ int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease) { + NDhcp4ClientLease *l, *t_l; + NDhcp4ClientProbe *probe; + int r; + + /* XXX error handling, this must be an OFFER */ + + if (!lease->probe) + return -ENOTRECOVERABLE; + if (lease->probe->current_lease) + return -ENOTRECOVERABLE; + + r = n_dhcp4_client_probe_transition_select(lease->probe, lease->message, n_dhcp4_gettime(CLOCK_BOOTTIME)); + if (r) + return r; + + /* + * Only one of the offered leases can be selected, so flush the list. + * All offered lease, including this one are now dead. + */ + probe = lease->probe; + c_list_for_each_entry_safe(l, t_l, &probe->lease_list, probe_link) + n_dhcp4_client_lease_unlink(l); + + return 0; +} + +/** + * n_dhcp4_client_lease_accept() - accept an ack'ed lease + * @lease: lease to operate on + * + * Accept a lease. This must be a lease that was ack'ed by the + * server. + * + * The offered IP address must be fully configured before the lease + * can be accepted. + * + * Return: 0 on success, or a negative error code on failure. + */ +_c_public_ int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease) { + int r; + + /* XXX error handling, this must be an ACK */ + + if (!lease->probe) + return -ENOTRECOVERABLE; + if (lease->probe->current_lease != lease) + return -ENOTRECOVERABLE; + + r = n_dhcp4_client_probe_transition_accept(lease->probe, lease->message); + if (r) + return r; + + n_dhcp4_client_lease_unlink(lease); + + return 0; +} + +/** + * n_dhcp4_client_lease_decline() - decline an ack'ed lease + * @lease: lease to operate on + * + * Decline a lease. This must be a lease that was ack'ed by the + * server. + * + * The offered IP address must not be used once the lease has been + * decline. + * + * Return: 0 on success, or a negative error code on failure. + */ +_c_public_ int n_dhcp4_client_lease_decline(NDhcp4ClientLease *lease, const char *error) { + int r; + + /* XXX: error handling, this must be an ACK */ + + if (!lease->probe) + return -ENOTRECOVERABLE; + if (lease->probe->current_lease != lease) + return -ENOTRECOVERABLE; + + r = n_dhcp4_client_probe_transition_decline(lease->probe, lease->message, error, n_dhcp4_gettime(CLOCK_BOOTTIME)); + if (r) + return r; + + lease->probe->current_lease = n_dhcp4_client_lease_unref(lease->probe->current_lease); + n_dhcp4_client_lease_unlink(lease); + + return 0; +} |