diff options
| author | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
| commit | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch) | |
| tree | 3469f17ea9af91f7ff169b890633bda68b0cf76e /shared/n-dhcp4 | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'shared/n-dhcp4')
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-connection.c | 1256 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-lease.c | 405 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-probe.c | 1294 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-client.c | 990 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-incoming.c | 428 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-outgoing.c | 373 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-private.h | 759 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-socket.c | 661 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4.h | 300 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/util/packet.c | 448 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/util/packet.h | 61 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/util/socket.c | 121 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/util/socket.h | 11 |
13 files changed, 0 insertions, 7107 deletions
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c deleted file mode 100644 index 31235253..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* - * DHCPv4 Client Connection - * - * XXX - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <errno.h> -#include <limits.h> -#include <sys/socket.h> /* needed by linux/netdevice.h */ -#include <linux/netdevice.h> -#include <net/if_arp.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include "n-dhcp4-private.h" -#include "util/packet.h" - -/** - * n_dhcp4_c_connection_init() - initialize client connection - * @connection: connection to operate on - * @client_config: client configuration to use - * @probe_config: client probe configuration to use - * @log_queue: the log queue for logging events - * @fd_epoll: epoll context to attach to, or -1 - * - * This initializes a new client connection using the configuration given in - * @client_config and @probe_config. - * - * The client-configuration given as @client_config must survive the lifetime - * of @connection. It is pinned in the connection and used all over the place. - * The caller must guarantee that the configuration is not deallocated in the - * meantime. Same is true for @probe_config. - * - * The new connection automatically attaches to the epoll context given as - * @fd_epoll. The epoll FD is retained in the connection and the caller must - * guarantee that it lives as long as the connection. - * The caller is explicitly allowed to pass -1 as @fd_epoll, in which case the - * connection will initialize correctly, but will not be in a usable state. - * That is, any call to n_dhcp4_c_connection_listen() will fail, since it will - * be unable to attach to the epoll context. Such a connection can be used to - * get a detached object that behaves sound, but provides no runtime. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, - NDhcp4ClientConfig *client_config, - NDhcp4ClientProbeConfig *probe_config, - NDhcp4LogQueue *log_queue, - int fd_epoll) { - *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); - connection->client_config = client_config; - connection->probe_config = probe_config; - connection->fd_epoll = fd_epoll; - connection->log_queue = log_queue; - - /* - * We explicitly allow initializing connections with an invalid - * epoll-fd. The resulting connection immediately transitions into the - * CLOSED state. This allows the caller to create dummy connections - * useful to provide asynchronous constructor-feedback in the API. - * - * The effect of this is as if you immediately call - * n_dhcp4_c_connection_close() on the new connection. However, by - * directly passing -1 in the constructor, you are guaranteed not even - * the constructor can ever mess with your epoll-set. - */ - if (connection->fd_epoll < 0) - connection->state = N_DHCP4_C_CONNECTION_STATE_CLOSED; - - return 0; -} - -/** - * n_dhcp4_c_connection_deinit() - deinitialize client connection - * @connection: connection to operate on - * - * This deinitializes a connection that was previously initialized via - * n_dhcp4_c_connection_init(). It will tear down all allocated state and - * release it. - * - * Once this function returns, @connection is re-initialized to - * N_DHCP4_C_CONNECTION_NULL. If this function is called on a deinitialized - * connection, it is a no-op. - */ -void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection) { - n_dhcp4_c_connection_close(connection); - n_dhcp4_outgoing_free(connection->request); - *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); -} - -static void n_dhcp4_c_connection_outgoing_set_secs(NDhcp4Outgoing *message) { - uint64_t secs; - - /* - * This function sets the `secs` field for outgoing messages. It - * expects the base-time and start-time to be already set by the - * caller. - * For a given outgoing message, its `secs` field describes the time - * (in seconds) between the start of the transaction this message is - * part of and the start of the operational process (also called the - * base time here). - * - * The operational process in the DHCP sense describes the entire - * process of requesting a lease and acquiring it. That is, it starts - * with the caller's intent to request a lease, and it ends when we - * got granted a lease. The act of refreshing a lease is, in itself, a - * new operational process. The base-time describes the start-time - * recorded when such a process as initiated. - * - * A transaction in the DHCP sense describes a request+reply - * combination, in most cases. That is, the time a request is sent is - * the start-time of a transaction. In the ideal case, the start-time - * of the first transaction in an operational process matches the - * base-time. However, transactions are often delayed with a randomized - * offset to reduce traffic during network bursts. - * In some cases, however, transactions are composed out of multiple - * requests+reply combinations. This includes, for instance, the SELECT - * message following an OFFER. The specification clearly says that - * those must be considered a single transaction and thus share the - * transaction start-time. - * - * The `secs` field, thus, describes how long a client has been busy - * requesting a lease. DHCP servers and proxies do use it to prioritize - * clients. - * - * Note: Some DHCP relays reject a `secs` value of 0 (which might look - * like it is uninitialized). Hence, we always clamp the value to - * the range `[1, 65535]`. - */ - - secs = message->userdata.base_time - message->userdata.start_time; - secs /= 1000ULL * 1000ULL * 1000ULL; /* nsecs to secs */ - secs = C_CLAMP(secs, 1, UINT16_MAX); - - n_dhcp4_outgoing_set_secs(message, secs); -} - -int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection) { - _c_cleanup_(c_closep) int fd_packet = -1; - int r; - - if (connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET) - return 0; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT || - connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - if (connection->fd_packet >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - connection->fd_packet = c_close(connection->fd_packet); - } - - if (connection->fd_udp >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL); - connection->fd_udp = c_close(connection->fd_udp); - } - - r = n_dhcp4_c_socket_packet_new(&fd_packet, connection->client_config->ifindex); - if (r) - return r; - - r = epoll_ctl(connection->fd_epoll, - EPOLL_CTL_ADD, - fd_packet, - &(struct epoll_event){ - .events = EPOLLIN, - .data = { .u32 = N_DHCP4_CLIENT_EPOLL_IO }, - }); - if (r < 0) - return -errno; - - connection->state = N_DHCP4_C_CONNECTION_STATE_PACKET; - connection->fd_packet = fd_packet; - fd_packet = -1; - return 0; -} - -int n_dhcp4_c_connection_connect(NDhcp4CConnection *connection, - const struct in_addr *client, - const struct in_addr *server) { - _c_cleanup_(c_closep) int fd_udp = -1; - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET); - - r = n_dhcp4_c_socket_udp_new(&fd_udp, - connection->client_config->ifindex, - client, - server); - if (r) - return r; - - r = epoll_ctl(connection->fd_epoll, - EPOLL_CTL_ADD, - fd_udp, - &(struct epoll_event){ - .events = EPOLLIN, - .data = { .u32 = N_DHCP4_CLIENT_EPOLL_IO }, - }); - if (r < 0) - return -errno; - - r = packet_shutdown(connection->fd_packet); - if (r < 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, fd_udp, NULL); - return r; - } - - connection->state = N_DHCP4_C_CONNECTION_STATE_DRAINING; - connection->fd_udp = fd_udp; - fd_udp = -1; - connection->client_ip = client->s_addr; - connection->server_ip = server->s_addr; - return 0; -} - -void n_dhcp4_c_connection_close(NDhcp4CConnection *connection) { - if (connection->fd_udp >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL); - connection->fd_udp = c_close(connection->fd_udp); - } - - if (connection->fd_packet >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - connection->fd_packet = c_close(connection->fd_packet); - } - - connection->fd_epoll = -1; - connection->state = N_DHCP4_C_CONNECTION_STATE_CLOSED; -} - -static int n_dhcp4_c_connection_verify_incoming(NDhcp4CConnection *connection, - NDhcp4Incoming *message, - uint8_t *typep) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - uint8_t type; - uint32_t request_xid; - uint8_t *id; - size_t n_id; - int r; - - r = n_dhcp4_incoming_query_message_type(message, &type); - if (r) { - if (r == N_DHCP4_E_UNSET) - return N_DHCP4_E_MALFORMED; - else - return r; - } - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - case N_DHCP4_MESSAGE_ACK: - case N_DHCP4_MESSAGE_NAK: - /* - * Only accept replies if there is a pending request, and it - * has a matching transaction id. - */ - if (!connection->request) - return N_DHCP4_E_UNEXPECTED; - - n_dhcp4_outgoing_get_xid(connection->request, &request_xid); - if (header->xid != request_xid) - return N_DHCP4_E_UNEXPECTED; - - break; - case N_DHCP4_MESSAGE_FORCERENEW: - /* - * Force renew messages are triggered by a server, and do not - * match a pending request. - */ - break; - default: - return N_DHCP4_E_UNEXPECTED; - } - - /* - * In case our transport makes use of the 'chaddr' field, make sure it - * matches exactly our address. - */ - switch (connection->client_config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - c_assert(connection->client_config->n_mac == ETH_ALEN); - - if (header->hlen != ETH_ALEN) - return N_DHCP4_E_UNEXPECTED; - if (memcmp(header->chaddr, connection->client_config->mac, ETH_ALEN) != 0) - return N_DHCP4_E_UNEXPECTED; - - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - if (header->hlen != 0) - return N_DHCP4_E_UNEXPECTED; - - break; - } - - /* - * If a server passes us back a client ID, it must be the one we - * provided. We ignore any packets that have mismatching client-ids. - */ - id = NULL; - n_id = 0; - r = n_dhcp4_incoming_query(message, N_DHCP4_OPTION_CLIENT_IDENTIFIER, &id, &n_id); - if (r) { - if (r != N_DHCP4_E_UNSET) - return r; - } else { - if (n_id != connection->client_config->n_client_id) - return N_DHCP4_E_UNEXPECTED; - if (memcmp(id, connection->client_config->client_id, n_id) != 0) - return N_DHCP4_E_UNEXPECTED; - } - - *typep = type; - return 0; -} - -void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection, - uint64_t *timeoutp) { - uint64_t timeout; - size_t n_send; - - if (!connection->request) { - *timeoutp = 0; - return; - } - - switch (connection->request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_INFORM: - /* - * Resend with an exponential backoff and a one second random - * slack, from a minimum of two seconds to a maximum of sixty - * four. - * - * Note that the RFC says to start at four rather than two - * seconds, and use [-1,1] slack, rather than [0,1]. - */ - n_send = connection->request->userdata.n_send; - if (n_send >= 6) - n_send = 6; - - timeout = connection->request->userdata.send_time + ((1ULL << n_send) * 1000000000ULL) + connection->request->userdata.send_jitter; - - break; - case N_DHCP4_C_MESSAGE_REBIND: - case N_DHCP4_C_MESSAGE_RENEW: - case N_DHCP4_C_MESSAGE_REBOOT: - /* - * Resend every sixty seconds with a one second random slack. - * - * Note that the RFC says to do this at most once, but we do - * it until we are cancelled. - */ - timeout = connection->request->userdata.send_time + (60ULL * 1000000000ULL) + connection->request->userdata.send_jitter; - - break; - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_RELEASE: - /* XXX make sure these message types are never pinned? */ - timeout = 0; - break; - default: - c_assert(0); - } - - *timeoutp = timeout; -} - -static int n_dhcp4_c_connection_packet_broadcast(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET); - - r = n_dhcp4_c_socket_packet_send(connection->fd_packet, - connection->client_config->ifindex, - connection->client_config->broadcast_mac, - connection->client_config->n_broadcast_mac, - message); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_c_connection_udp_broadcast(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - r = n_dhcp4_c_socket_udp_broadcast(connection->fd_udp, message); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_c_connection_udp_send(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - r = n_dhcp4_c_socket_udp_send(connection->fd_udp, message); - if (r) - return r; - - return 0; -} - -static void n_dhcp4_c_connection_init_header(NDhcp4CConnection *connection, - NDhcp4Header *header) { - bool broadcast = connection->client_config->request_broadcast; - - header->op = N_DHCP4_OP_BOOTREQUEST; - - switch (connection->client_config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - c_assert(connection->client_config->n_mac == ETH_ALEN); - - header->htype = ARPHRD_ETHER; - header->hlen = ETH_ALEN; - memcpy(header->chaddr, connection->client_config->mac, ETH_ALEN); - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - header->htype = ARPHRD_INFINIBAND; - header->hlen = 0; - - /* infiniband mandates to request broadcasts */ - broadcast = true; - break; - default: - abort(); - break; - } - - if (connection->client_ip != INADDR_ANY) { - header->ciaddr = connection->client_ip; - } else { - /* - * When the IP stack has not been configured, we may - * not be able to receive unicast packets, depending - * on the hardware. If that is the case we must request - * replies from the server to be broadcast. - * - * Once the IP stack has been configured, receiving - * unicast packets is never a problem, so the broadcast - * flag should not be set. - */ - if (broadcast) - header->flags |= N_DHCP4_MESSAGE_FLAG_BROADCAST; - } -} - -static int n_dhcp4_c_connection_new_message(NDhcp4CConnection *connection, - NDhcp4Outgoing **messagep, - uint8_t type) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - NDhcp4Header *header; - uint8_t message_type; - bool via_packet_socket = false; - int r; - - switch (type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - message_type = N_DHCP4_MESSAGE_DISCOVER; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_INFORM: - message_type = N_DHCP4_MESSAGE_INFORM; - break; - case N_DHCP4_C_MESSAGE_SELECT: - message_type = N_DHCP4_MESSAGE_REQUEST; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_RENEW: - message_type = N_DHCP4_MESSAGE_REQUEST; - break; - case N_DHCP4_C_MESSAGE_REBIND: - message_type = N_DHCP4_MESSAGE_REQUEST; - break; - case N_DHCP4_C_MESSAGE_REBOOT: - message_type = N_DHCP4_MESSAGE_REQUEST; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_RELEASE: - message_type = N_DHCP4_MESSAGE_RELEASE; - break; - case N_DHCP4_C_MESSAGE_DECLINE: - message_type = N_DHCP4_MESSAGE_DECLINE; - via_packet_socket = true; - break; - default: - abort(); - return -ENOTRECOVERABLE; - } - - /* - * We explicitly pass 0 as maximum message size, which makes - * NDhcp4Outgoing use the mandated default value from the spec (see its - * implementation). While the transport and like layers might support - * bigger MTUs (and we very likely know about them through - * n_dhcp4_client_update_mtu()), we cannot assume the target DHCP - * server supports parsing packets bigger than the minimum (and it is - * allowed to refuse bigger IP packets, even if the network supports - * transmission of them). - * - * We could theoretically increase this for packets other than the - * initial discovery. However, clients are unlikely to ever send large - * packets, so we just keep the same default for all outgoing packets. - */ - r = n_dhcp4_outgoing_new(&message, 0, N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME); - if (r) - return r; - - header = n_dhcp4_outgoing_get_header(message); - n_dhcp4_c_connection_init_header(connection, header); - - message->userdata.type = type; - message->userdata.message_type = message_type; - - /* - * Note that some implementations expect the MESSAGE_TYPE option to be - * the first option, and possibly even hard-code access to it. Hence, - * we really should make sure to pass it first as well. - */ - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MESSAGE_TYPE, &message_type, sizeof(message_type)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, - N_DHCP4_OPTION_CLIENT_IDENTIFIER, - connection->client_config->client_id, - connection->client_config->n_client_id); - if (r) - return r; - - switch (message_type) { - case N_DHCP4_MESSAGE_DISCOVER: - case N_DHCP4_MESSAGE_REQUEST: - case N_DHCP4_MESSAGE_INFORM: { - uint16_t mtu; - - if (connection->probe_config->n_request_parameters > 0) { - r = n_dhcp4_outgoing_append(message, - N_DHCP4_OPTION_PARAMETER_REQUEST_LIST, - connection->probe_config->request_parameters, - connection->probe_config->n_request_parameters); - if (r) - return r; - } - - if (via_packet_socket) { - /* - * In case of packet sockets, we do not support - * fragmentation. Hence, our maximum message size - * equals the transport MTU. In case no mtu is given, - * we use the minimum size mandated by the IP spec. If - * we omit the field, some implementations will - * interpret this to mean any packet size is supported, - * which we rather not want as default behavior (we can - * always support suppressing this field, if that is - * what the caller wants). - */ - mtu = htons(connection->mtu ?: N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE); - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, &mtu, sizeof(mtu)); - if (r) - return r; - } else { - /* - * Once we use UDP sockets, we support fragmentation - * through the kernel IP stack. This means, the biggest - * message we can receive is the maximum UDP size plus - * the possible IP header. This would sum up to - * 2^16-1 + 20 (or even 2^16-1 + 60 if pedantic) and - * thus exceed the option field. Hence, we simply set - * the option to the maximum possible value. - */ - mtu = htons(UINT16_MAX); - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, &mtu, sizeof(mtu)); - if (r) - return r; - } - - break; - } - default: - break; - } - - *messagep = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.1 - * - * The client broadcasts a DHCPDISCOVER message on its local physical - * subnet. The DHCPDISCOVER message MAY include options that suggest - * values for the network address and lease duration. BOOTP relay - * agents may pass the message on to DHCP servers not on the same - * physical subnet. - * - * RFC2131 3.5 - * - * [...] in its initial DHCPDISCOVER or DHCPREQUEST message, a client - * may provide the server with a list of specific parameters the - * client is interested in. If the client includes a list of - * parameters in a DHCPDISCOVER message, it MUST include that list in - * any subsequent DHCPREQUEST messages. - * - * [...] - * - * In addition, the client may suggest values for the network address - * and lease time in the DHCPDISCOVER message. The client may include - * the 'requested IP address' option to suggest that a particular IP - * address be assigned, and may include the 'IP address lease time' - * option to suggest the lease time it would like. Other options - * representing "hints" at configuration parameters are allowed in a - * DHCPDISCOVER or DHCPREQUEST message. - * - * RFC2131 4.4.1 - * - * The client generates and records a random transaction identifier and - * inserts that identifier into the 'xid' field. The client records its - * own local time for later use in computing the lease expiration. The - * client then broadcasts the DHCPDISCOVER on the local hardware - * broadcast address to the 0xffffffff IP broadcast address and 'DHCP - * server' UDP port. - * - * If the 'xid' of an arriving DHCPOFFER message does not match the - * 'xid' of the most recent DHCPDISCOVER message, the DHCPOFFER message - * must be silently discarded. Any arriving DHCPACK messages must be - * silently discarded. - */ -int n_dhcp4_c_connection_discover_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_DISCOVER); - if (r) - return r; - - *requestp = message; - message = NULL; - return 0; -} - -/* - * - * RFC2131 4.1.1 - * - * The DHCPREQUEST message contains the same 'xid' as the DHCPOFFER - * message. - * - * RFC2131 4.3.2 - * - * Client inserts the address of the selected server in 'server - * identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be - * filled in with the yiaddr value from the chosen DHCPOFFER. - */ -int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - NDhcp4Incoming *offer) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - struct in_addr client; - struct in_addr server; - uint32_t xid; - int r; - - n_dhcp4_incoming_get_yiaddr(offer, &client); - - r = n_dhcp4_incoming_query_server_identifier(offer, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_SELECT); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, &client, sizeof(client)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &server, sizeof(server)); - if (r) - return r; - - /* - * SELECT continues the transaction started by DISCOVER, and as such - * keeps the same start time. We also have to preserve the base time - * of the selected lease as well as the transaction ID. - */ - message->userdata.start_time = offer->userdata.start_time; - message->userdata.base_time = offer->userdata.base_time; - message->userdata.client_addr = client.s_addr; - n_dhcp4_incoming_get_xid(offer, &xid); - n_dhcp4_outgoing_set_xid(message, xid); - - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST be filled in with client's notion of its previously - * assigned address. 'ciaddr' MUST be zero. The client is seeking to - * verify a previously allocated, cached configuration. Server SHOULD - * send a DHCPNAK message to the client if the 'requested IP address' - * is incorrect, or is on the wrong network. - */ -int n_dhcp4_c_connection_reboot_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - const struct in_addr *client) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_REBOOT); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, client, sizeof(*client)); - if (r) - return r; - - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST NOT be filled in, 'ciaddr' MUST be filled in with - * client's IP address. In this situation, the client is completely - * configured, and is trying to extend its lease. This message will - * be unicast, so no relay agents will be involved in its - * transmission. Because 'giaddr' is therefore not filled in, the - * DHCP server will trust the value in 'ciaddr', and use it when - * replying to the client. - * - * A client MAY choose to renew or extend its lease prior to T1. The - * server may choose not to extend the lease (as a policy decision by - * the network administrator), but should return a DHCPACK message - * regardless. - * - * RFC2131 4.4.5 - * - * At time T1 the client moves to RENEWING state and sends (via unicast) - * a DHCPREQUEST message to the server to extend its lease. The client - * sets the 'ciaddr' field in the DHCPREQUEST to its current network - * address. The client records the local time at which the DHCPREQUEST - * message is sent for computation of the lease expiration time. The - * client MUST NOT include a 'server identifier' in the DHCPREQUEST - * message. - */ -int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_RENEW); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST NOT be filled in, 'ciaddr' MUST be filled in with - * client's IP address. In this situation, the client is completely - * configured, and is trying to extend its lease. This message MUST - * be broadcast to the 0xffffffff IP broadcast address. The DHCP - * server SHOULD check 'ciaddr' for correctness before replying to - * the DHCPREQUEST. - * - * RFC2131 4.4.5 - * - * If no DHCPACK arrives before time T2, the client moves to REBINDING - * state and sends (via broadcast) a DHCPREQUEST message to extend its - * lease. The client sets the 'ciaddr' field in the DHCPREQUEST to its - * current network address. The client MUST NOT include a 'server - * identifier' in the DHCPREQUEST message. - */ -int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_REBIND); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.2 - * - * If the client detects that the IP address in the DHCPACK message - * is already in use, the client MUST send a DHCPDECLINE message to the - * server and restarts the configuration process by requesting a - * new network address. - * - * RFC2131 4.4.4 - * - * Because the client is declining the use of the IP address supplied by - * the server, the client broadcasts DHCPDECLINE messages. - */ -int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - NDhcp4Incoming *ack, - const char *error) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - struct in_addr client; - struct in_addr server; - int r; - - n_dhcp4_incoming_get_yiaddr(ack, &client); - - r = n_dhcp4_incoming_query_server_identifier(ack, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_DECLINE); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, &client, sizeof(client)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &server, sizeof(server)); - if (r) - return r; - - if (error) { - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_ERROR_MESSAGE, error, strlen(error) + 1); - if (r) - return r; - } - - message->userdata.client_addr = client.s_addr; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.4 - * - * If a client has obtained a network address through some other means - * (e.g., manual configuration), it may use a DHCPINFORM request message - * to obtain other local configuration parameters. - * - * RFC2131 4.4 - * - * The DHCPINFORM message is not shown in figure 5. A client simply - * sends the DHCPINFORM and waits for DHCPACK messages. Once the client - * has selected its parameters, it has completed the configuration - * process. - * - * RFC2131 4.4.3 - * - * The client sends a DHCPINFORM message. The client may request - * specific configuration parameters by including the 'parameter request - * list' option. The client generates and records a random transaction - * identifier and inserts that identifier into the 'xid' field. The - * client places its own network address in the 'ciaddr' field. The - * client SHOULD NOT request lease time parameters. - * - * The client then unicasts the DHCPINFORM to the DHCP server if it - * knows the server's address, otherwise it broadcasts the message to - * the limited (all 1s) broadcast address. DHCPINFORM messages MUST be - * directed to the 'DHCP server' UDP port. - */ -int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_INFORM); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.1 - * - * The client may choose to relinquish its lease on a network address - * by sending a DHCPRELEASE message to the server. The client - * identifies the lease to be released with its 'client identifier', - * or 'chaddr' and network address in the DHCPRELEASE message. If the - * client used a 'client identifier' when it obtained the lease, it - * MUST use the same 'client identifier' in the DHCPRELEASE message. - * - * RFC2131 3.2 - * - * The client may choose to relinquish its lease on a network - * address by sending a DHCPRELEASE message to the server. The - * client identifies the lease to be released with its - * 'client identifier', or 'chaddr' and network address in the - * DHCPRELEASE message. - * - * Note that in this case, where the client retains its network - * address locally, the client will not normally relinquish its - * lease during a graceful shutdown. Only in the case where the - * client explicitly needs to relinquish its lease, e.g., the client - * is about to be moved to a different subnet, will the client send - * a DHCPRELEASE message. - * - * RFC2131 4.4.4 - * - * The client unicasts DHCPRELEASE messages to the server. - * - * RFC2131 4.4.6 - * - * If the client no longer requires use of its assigned network address - * (e.g., the client is gracefully shut down), the client sends a - * DHCPRELEASE message to the server. Note that the correct operation - * of DHCP does not depend on the transmission of DHCPRELEASE messages. - */ -int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - const char *error) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_RELEASE); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &connection->server_ip, sizeof(connection->server_ip)); - if (r) - return r; - - if (error) { - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_ERROR_MESSAGE, error, strlen(error) + 1); - if (r) - return r; - } - - *requestp = message; - message = NULL; - return 0; -} - -static const char *message_type_to_str(uint8_t type) { - switch (type) { - case N_DHCP4_MESSAGE_DISCOVER: - return "DISCOVER"; - case N_DHCP4_MESSAGE_OFFER: - return "OFFER"; - case N_DHCP4_MESSAGE_REQUEST: - return "REQUEST"; - case N_DHCP4_MESSAGE_DECLINE: - return "DECLINE"; - case N_DHCP4_MESSAGE_ACK: - return "ACK"; - case N_DHCP4_MESSAGE_NAK: - return "NACK"; - case N_DHCP4_MESSAGE_RELEASE: - return "RELEASE"; - case N_DHCP4_MESSAGE_INFORM: - return "INFORM"; - case N_DHCP4_MESSAGE_FORCERENEW: - return "FORCERENEW"; - default: - return "UNKNOWN"; - } -} - -static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp) { - char server_addr[INET_ADDRSTRLEN]; - char client_addr[INET_ADDRSTRLEN]; - char error_msg[128]; - int r; - bool broadcast = false; - - /* - * Increment the base time and reset the xid field, - * where applicable. We never alter the header on - * resends of SELECT, as it must always match the - * OFFER message they are in reply to. - */ - switch (request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_INFORM: - case N_DHCP4_C_MESSAGE_REBOOT: - case N_DHCP4_C_MESSAGE_REBIND: - case N_DHCP4_C_MESSAGE_RENEW: - request->userdata.base_time = timestamp; - n_dhcp4_outgoing_set_xid(request, n_dhcp4_client_probe_config_get_random(connection->probe_config)); - - break; - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_RELEASE: - break; - default: - c_assert(0); - } - - request->userdata.send_time = timestamp; - request->userdata.send_jitter = (n_dhcp4_client_probe_config_get_random(connection->probe_config) % 1000000000ULL); - n_dhcp4_c_connection_outgoing_set_secs(request); - - switch (request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_REBOOT: - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_REBIND: - broadcast = true; - r = n_dhcp4_c_connection_packet_broadcast(connection, request); - break; - case N_DHCP4_C_MESSAGE_INFORM: - broadcast = true; - r = n_dhcp4_c_connection_udp_broadcast(connection, request); - break; - case N_DHCP4_C_MESSAGE_RENEW: - case N_DHCP4_C_MESSAGE_RELEASE: - r = n_dhcp4_c_connection_udp_send(connection, request); - break; - default: - c_assert(0); - } - - if (r) { - snprintf(error_msg, sizeof(error_msg), ": error %d", r); - } else { - error_msg[0] = '\0'; - } - - if (request->userdata.client_addr == INADDR_ANY) { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "send %s to %s%s", - message_type_to_str(request->userdata.message_type), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); - } else { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "send %s of %s to %s%s", - message_type_to_str(request->userdata.message_type), - inet_ntop(AF_INET, &request->userdata.client_addr, - client_addr, sizeof(client_addr)), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); - } - - ++request->userdata.n_send; - return 0; -} - -int n_dhcp4_c_connection_start_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp) { - int r; - - /* - * This function starts a request, but in the case of SELECT it - * continues a previous transaction, so we do not want to reset - * the start time. Only set the start time if it was not already - * set. - */ - if (request->userdata.start_time == 0) - request->userdata.start_time = timestamp; - - connection->request = n_dhcp4_outgoing_free(connection->request); - - r = n_dhcp4_c_connection_send_request(connection, request, timestamp); - if (r) - return r; - - connection->request = request; - - return 0; -} - -int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection, - uint64_t timestamp) { - uint64_t timeout; - int r; - - if (!connection->request) - return 0; - - n_dhcp4_c_connection_get_timeout(connection, &timeout); - - if (timeout > timestamp) - return 0; - - r = n_dhcp4_c_connection_send_request(connection, connection->request, timestamp); - if (r) - return r; - - return 0; -} - -/* - * Returns: - * 0 on success - * N_DHCP4_E_MALFORMED if a malformed packet was received - * N_DHCP4_E_UNEXPECTED if the packet received contains unexpected data - * N_DHCP4_E_AGAIN if there was another error (non fatal for the client) - */ -int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, - NDhcp4Incoming **messagep) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - char serv_addr[INET_ADDRSTRLEN]; - char client_addr[INET_ADDRSTRLEN]; - uint8_t type; - int r; - - switch (connection->state) { - case N_DHCP4_C_CONNECTION_STATE_PACKET: - r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - return N_DHCP4_E_AGAIN; - case N_DHCP4_C_CONNECTION_STATE_DRAINING: - r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - else if (r != N_DHCP4_E_AGAIN) - return N_DHCP4_E_AGAIN; - - /* - * The UDP socket is open and the packet socket has been shut down - * and drained, clean up the packet socket and fall through to - * dispatching the UDP socket. - */ - r = epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - c_assert(!r); - connection->fd_packet = c_close(connection->fd_packet); - connection->state = N_DHCP4_C_CONNECTION_STATE_UDP; - - /* fall-through */ - case N_DHCP4_C_CONNECTION_STATE_UDP: - r = n_dhcp4_c_socket_udp_recv(connection->fd_udp, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - return N_DHCP4_E_AGAIN; - default: - abort(); - return -ENOTRECOVERABLE; - } - - r = n_dhcp4_c_connection_verify_incoming(connection, message, &type); - if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) - return r; - else if (r != 0) - return N_DHCP4_E_AGAIN; - - if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "received %s of %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.yiaddr, - client_addr, sizeof(client_addr)), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); - } else { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "received %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); - } - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - case N_DHCP4_MESSAGE_ACK: - case N_DHCP4_MESSAGE_NAK: - /* - * Remember the start time of the transaction, and the base - * time of any relative timestamps from the pending request. - * The same time applies to the response, and should be - * copied over. - */ - message->userdata.start_time = connection->request->userdata.start_time; - message->userdata.base_time = connection->request->userdata.base_time; - - if (type != N_DHCP4_MESSAGE_OFFER) { - /* - * We only allow one reply to ACK or NAK, but for OFFER we must - * accept several, so we do not free the pinned request. - */ - connection->request = n_dhcp4_outgoing_free(connection->request); - } - - break; - default: - break; - } - - *messagep = message; - message = NULL; - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-c-lease.c b/shared/n-dhcp4/src/n-dhcp4-c-lease.c deleted file mode 100644 index 515814d4..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-lease.c +++ /dev/null @@ -1,405 +0,0 @@ -/* - * DHCP4 Client Leases - * - * This implements the public API wrapping DHCP4 client leases. A lease object - * consists 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 { - 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 argument 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 contained 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_siaddr() - get the server IP address - * @lease: the lease to operate on - * @siaddr: return argument for the IP address - * - * Gets the server IP address contained in the lease. Or INADDR_ANY if the - * lease does not contain an IP address. - */ -_c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message); - - siaddr->s_addr = header->siaddr; -} - -/** - * n_dhcp4_client_lease_get_basetime() - get the timestamp when the lease was received. - * @lease: the lease to operate on - * @ns_basetimep: return argument for the base time in nano seconds - * - * Gets the timestamp when the lease was received in CLOCK_BOOTTIME. This - * is also the base timestamp for the expiration of the lifetime and t1/t2. - */ -_c_public_ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep) { - *ns_basetimep = lease->message->userdata.base_time; -} - -/** - * 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_get_server_identifier() - get the server identifier - * @lease: the lease to operate on - * @addr: return argument for the server identifier - * - * Gets the address contained in the server-identifier DHCP option, in network - * byte order. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr) { - uint8_t *data; - size_t n_data; - int r; - - r = n_dhcp4_incoming_query(lease->message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &data, &n_data); - if (r) - return r; - if (n_data < sizeof(struct in_addr)) - return N_DHCP4_E_MALFORMED; - - memcpy(addr, data, sizeof(struct in_addr)); - - return 0; -} - -/** - * 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; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c deleted file mode 100644 index 7f20ac05..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c +++ /dev/null @@ -1,1294 +0,0 @@ -/* - * DHCPv4 Client Probes - * - * The probe object is used to represent the lifetime of a DHCP client session. - * A running probe discovers DHCP servers, requests a lease, and maintains that - * lease. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-siphash.h> -#include <c-stdaux.h> -#include <errno.h> -#include <inttypes.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/auxv.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - - -static int n_dhcp4_client_probe_option_new(NDhcp4ClientProbeOption **optionp, - uint8_t option, - const void *data, - uint8_t n_data) { - NDhcp4ClientProbeOption *op; - - op = malloc(sizeof(*op) + n_data); - if (!op) - return -ENOMEM; - - op->option = option; - op->n_data = n_data; - memcpy(op->data, data, n_data); - - *optionp = op; - return 0; -} - -static void n_dhcp4_client_probe_option_free(NDhcp4ClientProbeOption *option) { - if (option) - free(option); -} - -/** - * n_dhcp4_client_probe_config_new() - create new probe configuration - * @configp: output argument to store new configuration - * - * This creates a new probe configuration object. The object is a collection of - * parameters for probes. No data verification is done by the configuration - * object. Instead, when passing the configuration to the constructor of a - * probe, this constructor will perform parameter validation. - * - * A probe configuration is an unlinked object only used to pass information to - * a probe constructor. The caller fully owns the returned configuration object - * and is responsible to free it when no longer needed. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe_config_new(NDhcp4ClientProbeConfig **configp) { - _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL; - - config = calloc(1, sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NDhcp4ClientProbeConfig)N_DHCP4_CLIENT_PROBE_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_config_free() - destroy probe configuration - * @config: configuration to operate on, or NULL - * - * This destroys a probe configuration object and deallocates all its - * resources. - * - * If @config is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientProbeConfig *n_dhcp4_client_probe_config_free(NDhcp4ClientProbeConfig *config) { - if (!config) - return NULL; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) - n_dhcp4_client_probe_option_free(config->options[i]); - - free(config); - - return NULL; -} - -/** - * n_dhcp4_client_probe_config_dup() - duplicate probe configuration - * @config: configuration to operate on - * @dupp: output argument for duplicate - * - * This duplicates the probe configuration given as @config and returns it in - * @dupp to the caller. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_probe_config_dup(NDhcp4ClientProbeConfig *config, - NDhcp4ClientProbeConfig **dupp) { - _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *dup = NULL; - int r; - - r = n_dhcp4_client_probe_config_new(&dup); - if (r) - return r; - - dup->inform_only = config->inform_only; - dup->init_reboot = config->init_reboot; - dup->requested_ip = config->requested_ip; - dup->ms_start_delay = config->ms_start_delay; - - for (unsigned int i = 0; i < config->n_request_parameters; ++i) - dup->request_parameters[dup->n_request_parameters++] = config->request_parameters[i]; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (!config->options[i]) - break; - - r = n_dhcp4_client_probe_option_new(&dup->options[i], - config->options[i]->option, - config->options[i]->data, - config->options[i]->n_data); - if (r) - return r; - } - - *dupp = dup; - dup = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_config_set_inform_only() - set inform-only property - * @config: configuration to operate on - * @inform_only: value to set - * - * This sets the inform-only property of the given configuration object. This - * property controls whether the client probe should request a full lease, or - * whether it should just ask for auxiliary information without requesting an - * address. - * - * The default is to request a full lease and address. If inform-only is set to - * true, only auxiliary information will be requested. - * - * XXX: This is currently not implemented, and setting the property has no effect. - */ -_c_public_ void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeConfig *config, bool inform_only) { - config->inform_only = inform_only; -} - -/** - * n_dhcp4_client_probe_config_set_init_reboot() - set init-reboot property - * @config: configuration to operate on - * @init_reboot: value to set - * - * This sets the init-reboot property of the given configuration object. If this - * is enabled, a requested IP address must also be set. - * - * The default is false. If set to true, a probe will make use of the - * INIT-REBOOT path, as described by the DHCP specification. In most cases, you - * do not want this. - * - * Background: The INIT-REBOOT path allows a DHCP client to skip - * server-discovery when rebooting/resuming their machine. The DHCP - * client simply re-requests the lease it had acquired before. This - * saves one roundtrip in the success-case, since the DISCOVER step - * is skipped. However, there are little to no timeouts involved, - * so the roundtrip should be barely noticeable. In contrast, if - * the INIT-REBOOT fails (because the lease is no longer valid, or - * not valid on this network), the client has to wait for a - * possible answer to the request before actually starting the DHCP - * process all over. This significantly increases the time needed - * to switch networks. - * The INIT-REBOOT state might have been a real improvements with - * the old resend-timeouts mandated by the DHCP specification. - * However, on modern networks with improved timeout values we - * recommend against using it. - */ -_c_public_ void n_dhcp4_client_probe_config_set_init_reboot(NDhcp4ClientProbeConfig *config, bool init_reboot) { - config->init_reboot = init_reboot; -} - -/** - * n_dhcp4_client_probe_config_set_requested_ip() - set requested-ip property - * @config: configuration to operate on - * @ip: value to set - * - * This sets the requested-ip property of the given configuration object. - * - * The default is all 0. If set to something else, the DHCP discovery will - * include this IP in its requests to tell DHCP servers which address to pick. - * Servers are not required to honor this, nor does this have any effect on - * servers not serving this address. - * - * This field should always be set if the caller knows of an address that was - * previously acquired on this network. It serves as hint to servers and will - * allow them to provide the same address again. - */ -_c_public_ void n_dhcp4_client_probe_config_set_requested_ip(NDhcp4ClientProbeConfig *config, struct in_addr ip) { - config->requested_ip = ip; -} - -/** - * n_dhcp4_client_probe_config_set_start_delay() - set start delay - * @config: configuration to operate on - * @msecs: value to set - * - * This sets the start delay property of the given configuration object. - * - * The default is 9000 ms, which is based on RFC2131. In the RFC the start - * delay is specified to be a random value in the range 1000 to 10.000 ms. - * However, there does not appear to be any particular reason to - * unconditionally wait at least one second, so we move the range down to - * start at 0 ms. The reason for the random delay is to avoid network-wide - * events causing too much simultaneous network traffic. However, on modern - * networks, a more reasonable value may be in the 10 ms range. - */ -_c_public_ void n_dhcp4_client_probe_config_set_start_delay(NDhcp4ClientProbeConfig *config, uint64_t msecs) { - config->ms_start_delay = msecs; -} - -/** - * n_dhpc4_client_probe_config_request_option() - append option to request from the server - * @config: configuration to operate on - * @option: option to request - * - * This adds an option to the list of options to request from the server. - * - * A server may send options that we do not request, and it may omit options - * that we do request. However, to increase the likelyhood of uniform behavior - * between server implementations, we do not expose options that were not - * explicitly requested. - * - * When called multiple times, the order matters. Earlier requests are - * considered higher priority than later requests, in case the server must omit - * some, due to a lack of space. If the same option is requested more than once, - * only the first call has an effect. - */ -_c_public_ void n_dhcp4_client_probe_config_request_option(NDhcp4ClientProbeConfig *config, uint8_t option) { - for (unsigned int i = 0; i < config->n_request_parameters; ++i) { - if (config->request_parameters[i] == option) - return; - } - - c_assert(config->n_request_parameters <= UINT8_MAX); - - config->request_parameters[config->n_request_parameters++] = option; -} - -/** - * n_dhcp4_client_probe_config_append_option() - append option to outgoing messages - * @config: configuration to operate on - * @option: DHCP option number - * @data: payload - * @n_data: number of bytes in payload - * - * This sets extra options on a given configuration object. - * - * These options are appended verbatim to outgoing messages where - * that is supported by the specification. The same options are - * appended to all messages. - * - * No option may be appended more than once. Options considered internal - * to the DHCP protocol may not be appended. - * - * Return: 0 on success, N_DHCP4_E_DUPLICATE_OPTION if an option has already been - * appended, N_DHCP4_E_INTERNAL if the option is not configurable, or - * a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe_config_append_option(NDhcp4ClientProbeConfig *config, - uint8_t option, - const void *data, - uint8_t n_data) { - int r; - - /* XXX: filter internal options */ - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (config->options[i]) { - if (config->options[i]->option == option) - return N_DHCP4_E_DUPLICATE_OPTION; - - continue; - } - - r = n_dhcp4_client_probe_option_new(&config->options[i], - option, - data, - n_data); - if (r) - return r; - - return 0; - } - - c_assert(0); - return -ENOTRECOVERABLE; -} - -static void n_dhcp4_client_probe_config_initialize_random_seed(NDhcp4ClientProbeConfig *config) { - uint8_t hash_seed[] = { - 0x25, 0x3f, 0x02, 0x75, 0x3a, 0xb8, 0x4f, 0x91, - 0x9d, 0x0a, 0xd6, 0x15, 0x9d, 0x72, 0x7b, 0xcb, - }; - CSipHash hash = C_SIPHASH_NULL; - unsigned short int seed16v[3]; - const uint8_t *p; - uint64_t u64; - - /* - * Initialize config's entropy buffer for successive jrand48(3) calls. - * - * We need random jitter for all timeouts and delays, used to reduce - * network traffic during bursts. This is not meant as security measure - * but only meant to improve network utilization during bursts. The - * random source is thus negligible. However, we want, under all - * circumstances, avoid two instances running with the same seed. Thus - * we source the seed from AT_RANDOM, which grants us a per-process - * unique seed. We then add the current time to make sure consequetive - * instances use different seeds (to avoid clashes if processes are - * duplicated, or similar), and lastly we add the config memory address - * to avoid clashes of multiple parallel instances. - * - * Again, none of these are meant as security measure, but only to - * avoid *ACCIDENTAL* seed clashes. That is, in the case that many - * transactions are started in parallel, we delay the individual - * messages (as described in the spec), to reduce the traffic on the - * network and the chance of packets being dropped (and thus triggering - * timeouts and resends). - * - * We hash everything through SipHash, to avoid exposing AT_RANDOM and - * other sources to the network. We use a static salt to distinguish it - * from other implementations using the same random source. - */ - c_siphash_init(&hash, hash_seed); - - p = (const uint8_t *)getauxval(AT_RANDOM); - if (p) - c_siphash_append(&hash, p, 16); - - u64 = n_dhcp4_gettime(CLOCK_MONOTONIC); - c_siphash_append(&hash, (const uint8_t *)&u64, sizeof(u64)); - - c_siphash_append(&hash, (const uint8_t *)&config, sizeof(config)); - - u64 = c_siphash_finalize(&hash); - - seed16v[0] = (u64 >> 0) ^ (u64 >> 48); - seed16v[1] = (u64 >> 16) ^ (u64 >> 0); - seed16v[2] = (u64 >> 32) ^ (u64 >> 16); - - memcpy(config->entropy, seed16v, sizeof(seed16v)); -} - -/** - * n_dhcp4_client_probe_config_get_random() - get random data - * @config: config object to operate on - * - * Fetch the next 32bit random number from the entropy pool in @config. - * Note that this is in no way suitable for security purposes. - * - * Return: the random data. - */ -uint32_t n_dhcp4_client_probe_config_get_random(NDhcp4ClientProbeConfig *config) { - return jrand48(config->entropy); -}; - -/** - * n_dhcp4_client_probe_new() - create new client probe - * @probep: output argument for new client probe - * @config: probe configuration - * @client: client to probe on behalf of - * @ns_now: the current time - * - * This creates a new client probe object. - * - * If one is already running, the new one will be immediately (but asynchronously) - * cancelled. Otherwise, a DISCOVER event is scheduled after a randomized delay. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config, - NDhcp4Client *client, - uint64_t ns_now) { - _c_cleanup_(n_dhcp4_client_probe_freep) NDhcp4ClientProbe *probe = NULL; - bool active; - int r; - - /* - * If there is already a probe attached, we create the new probe in - * detached state. It will not be linked into the epoll context and not - * be useful in any way. We immediately raise the CANCELLED event to - * notify the caller about it. - */ - active = !client->current_probe; - - probe = calloc(1, sizeof(*probe)); - if (!probe) - return -ENOMEM; - - *probe = (NDhcp4ClientProbe)N_DHCP4_CLIENT_PROBE_NULL(*probe); - probe->client = n_dhcp4_client_ref(client); - - r = n_dhcp4_client_probe_config_dup(config, &probe->config); - if (r) - return r; - - /* - * XXX: make seed initialization optional, so the entropy can be reused. - */ - n_dhcp4_client_probe_config_initialize_random_seed(probe->config); - - /* The new probe keeps a reference on @client. So we are sure that &client->log_queue - * stays alive as long as we need it. */ - - r = n_dhcp4_c_connection_init(&probe->connection, - client->config, - probe->config, - &client->log_queue, - active ? client->fd_epoll : -1); - if (r) - return r; - - if (probe->config->requested_ip.s_addr != INADDR_ANY) - probe->last_address = probe->config->requested_ip; - - if (probe->config->init_reboot && probe->last_address.s_addr != INADDR_ANY) - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT; - else - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - - if (active) { - /* - * Defer the sending of DISCOVER by a random amount (by default up to 9 seconds). - */ - if (probe->state == N_DHCP4_CLIENT_PROBE_STATE_INIT) - probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL)); - probe->client->current_probe = probe; - } else { - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_CANCELLED); - if (r) - return r; - } - - *probep = probe; - probe = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_free() - destroy a probe - * @probe: probe to operate on, or NULL - * - * This destroys a probe object and deallocates all its resources. - * - * If @probe is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe) { - NDhcp4CEventNode *node, *t_node; - NDhcp4ClientLease *lease, *t_lease; - - if (!probe) - return NULL; - - c_list_for_each_entry_safe(lease, t_lease, &probe->lease_list, probe_link) - n_dhcp4_client_lease_unlink(lease); - - c_list_for_each_entry_safe(node, t_node, &probe->event_list, probe_link) - n_dhcp4_c_event_node_free(node); - - if (probe == probe->client->current_probe) - probe->client->current_probe = NULL; - - n_dhcp4_client_lease_unref(probe->current_lease); - n_dhcp4_c_connection_deinit(&probe->connection); - n_dhcp4_client_unref(probe->client); - n_dhcp4_client_probe_config_free(probe->config); - - c_assert(c_list_is_empty(&probe->lease_list)); - c_assert(c_list_is_empty(&probe->event_list)); - free(probe); - - return NULL; -} - -/** - * n_dhcp4_client_probe_set_userdata() - set userdata pointer - * @probe: the probe to operate on - * @userdata: pointer to userdata - * - * Set a userdata pointer. The pointed to data is still owned by the caller, and - * is completely opaque to the probe. - */ -_c_public_ void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata) { - probe->userdata = userdata; -} - -/** - * n_dhcp4_client_probe_get_userdata() - get userdata pointer - * @probe: the probe to operate on - * @userdatap: return pointer for userdata pointer - * - * Get the userdata pointer. The lifetime of the userdata and making sure it is - * still valid when accessed via the probe is the responsibility of the caller. - */ -_c_public_ void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap) { - *userdatap = probe->userdata; -} - -/** - * n_dhcp4_client_probe_raise() - XXX - */ -int n_dhcp4_client_probe_raise(NDhcp4ClientProbe *probe, NDhcp4CEventNode **nodep, unsigned int event) { - NDhcp4CEventNode *node; - int r; - - r = n_dhcp4_client_raise(probe->client, &node, event); - if (r) - return r; - - switch (event) { - case N_DHCP4_CLIENT_EVENT_OFFER: - node->event.offer.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_GRANTED: - node->event.granted.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_RETRACTED: - node->event.retracted.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_EXTENDED: - node->event.extended.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_EXPIRED: - node->event.expired.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_CANCELLED: - node->event.cancelled.probe = probe; - break; - default: - c_assert(0); - n_dhcp4_c_event_node_free(node); - return -ENOTRECOVERABLE; - } - - if (nodep) - *nodep = node; - return 0; -} - -void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeoutp) { - uint64_t t1 = 0; - uint64_t t2 = 0; - uint64_t lifetime = 0; - uint64_t timeout = 0; - - if (probe->current_lease) { - t1 = probe->current_lease->t1; - t2 = probe->current_lease->t2; - lifetime = probe->current_lease->lifetime; - } - - n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout); - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - /* send DHCP request immediately */ - timeout = 1; - break; - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - if (probe->ns_reinit && (!timeout || probe->ns_reinit < timeout)) - timeout = probe->ns_reinit; - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout)) - timeout = probe->ns_deferred; - - break; - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - if (t1 && (!timeout || t1 < timeout)) - timeout = t1; - - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - if (t2 && (!timeout || t2 < timeout)) - timeout = t2; - - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - if (lifetime && (!timeout || lifetime < timeout)) - timeout = lifetime; - break; - default: - /* ignore */ - break; - } - - *timeoutp = timeout; -} - -static int n_dhcp4_client_probe_outgoing_append_options(NDhcp4ClientProbe *probe, NDhcp4Outgoing *outgoing) { - int r; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (!probe->config->options[i]) - break; - - r = n_dhcp4_outgoing_append(outgoing, - probe->config->options[i]->option, - probe->config->options[i]->data, - probe->config->options[i]->n_data); - if (r) { - if (r == N_DHCP4_E_NO_SPACE) - /* XXX */ - break; - - return r; - } - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_reboot(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - - r = n_dhcp4_c_connection_reboot_new(&probe->connection, &request, &probe->last_address); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBOOTING; - probe->ns_reinit = ns_now + 2000000000ULL; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - /* fall-through */ - - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - r = n_dhcp4_c_connection_discover_new(&probe->connection, &request); - if (r) - return r; - - if (probe->last_address.s_addr != INADDR_ANY) { - r = n_dhcp4_outgoing_append_requested_ip(request, probe->last_address); - if (r) - return r; - } - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_SELECTING; - probe->ns_deferred = 0; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_t1(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - r = n_dhcp4_c_connection_renew_new(&probe->connection, &request); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_RENEWING; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_t2(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - - r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBINDING; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - - /* XXX */ - - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_EXPIRED); - if (r) - return r; - - c_assert(probe->client->current_probe == probe); - - probe->current_lease = n_dhcp4_client_lease_unref(probe->current_lease); - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + UINT64_C(1); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take; - _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; - NDhcp4CEventNode *node; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_OFFER); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.offer.lease = n_dhcp4_client_lease_ref(lease); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take; - _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; - NDhcp4CEventNode *node; - struct in_addr client = {}; - struct in_addr server = {}; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - n_dhcp4_incoming_get_yiaddr(message, &client); - - r = n_dhcp4_incoming_query_server_identifier(message, &server); - if (r) - return r; - r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server); - if (r) - return r; - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_EXTENDED); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.extended.lease = n_dhcp4_client_lease_ref(lease); - n_dhcp4_client_lease_unref(probe->current_lease); - probe->current_lease = n_dhcp4_client_lease_ref(lease); - probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - n_dhcp4_client_lease_get_yiaddr(lease, &probe->last_address); - probe->ns_nak_restart_delay = 0; - break; - - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_GRANTED); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.granted.lease = n_dhcp4_client_lease_ref(lease); - probe->current_lease = n_dhcp4_client_lease_ref(lease); - probe->state = N_DHCP4_CLIENT_PROBE_STATE_GRANTED; - probe->ns_nak_restart_delay = 0; - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_nak(NDhcp4ClientProbe *probe) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - - /* XXX */ - - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_RETRACTED); - if (r) - return r; - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + probe->ns_nak_restart_delay; - probe->ns_nak_restart_delay = C_CLAMP(probe->ns_nak_restart_delay * 2u, - UINT64_C(2) * UINT64_C(1000000000), - UINT64_C(300) * UINT64_C(1000000000)); - break; - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -int n_dhcp4_client_probe_transition_select(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - r = n_dhcp4_c_connection_select_new(&probe->connection, &request, offer); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - /* XXX: ignore other offers */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REQUESTING; - - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_transition_accept() - XXX - */ -int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incoming *ack) { - struct in_addr client = {}; - struct in_addr server = {}; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - n_dhcp4_incoming_get_yiaddr(ack, &client); - - r = n_dhcp4_incoming_query_server_identifier(ack, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server); - if (r) - return r; - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - - n_dhcp4_client_arm_timer(probe->client); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhc4_client_probe_transition_decline() - XXX - */ -int n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, const char *error, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - r = n_dhcp4_c_connection_decline_new(&probe->connection, &request, offer, error); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - /* XXX: what state to transition to? */ - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_dispatch_timer() - XXX - */ -int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_now) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - r = n_dhcp4_client_probe_transition_reboot(probe, ns_now); - if (r) - return r; - break; - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - if (ns_now >= probe->ns_reinit) { - r = n_dhcp4_client_probe_transition_deferred(probe, ns_now); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - if (ns_now >= probe->ns_deferred) { - r = n_dhcp4_client_probe_transition_deferred(probe, ns_now); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - if (ns_now >= probe->current_lease->lifetime) { - r = n_dhcp4_client_probe_transition_lifetime(probe); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - if (ns_now >= probe->current_lease->lifetime) { - r = n_dhcp4_client_probe_transition_lifetime(probe); - if (r) - return r; - } else if (ns_now >= probe->current_lease->t2 && - probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) { - r = n_dhcp4_client_probe_transition_t2(probe, ns_now); - if (r) - return r; - } else if (ns_now >= probe->current_lease->t1 && - probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) { - r = n_dhcp4_client_probe_transition_t1(probe, ns_now); - if (r) - return r; - } - break; - default: - /* ignore */ - break; - } - - r = n_dhcp4_c_connection_dispatch_timer(&probe->connection, ns_now); - if (r) - return r; - - return 0; -} - -/** - * n_dhcp4_client_probe_dispatch_connection() - XXX - */ -int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - uint8_t type; - int r; - - r = n_dhcp4_c_connection_dispatch_io(&probe->connection, &message); - if (r) { - if (r == N_DHCP4_E_AGAIN) - return 0; - else if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) { - /* - * We fetched something from the sockets, which we - * discarded. We don't know whether there is more data - * to fetch, so we set the preempted flag to notify the - * caller we want to be called again. - */ - probe->client->preempted = true; - return 0; - } - - abort(); - return r; - } - - /* - * We fetched something from the sockets, which we will handle below. - * We don't know whether there is more data to fetch, so we set the - * preempted flag to notify the caller we want to be called again. - */ - probe->client->preempted = true; - - r = n_dhcp4_incoming_query_message_type(message, &type); - if (r == N_DHCP4_E_UNSET || r == N_DHCP4_E_MALFORMED) - /* - * XXX: this can never happen as we already queried the message - * type. - */ - return 0; - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - r = n_dhcp4_client_probe_transition_offer(probe, message); - message = NULL; /* consumed */ - if (r) - return r; - break; - case N_DHCP4_MESSAGE_ACK: - r = n_dhcp4_client_probe_transition_ack(probe, message); - message = NULL; /* consumed */ - if (r) - return r; - break; - case N_DHCP4_MESSAGE_NAK: - r = n_dhcp4_client_probe_transition_nak(probe); - if (r) - return r; - break; - default: - /* - * We receiveda message type we do not support, simply discard - * it. - */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_update_mtu() - XXX - */ -int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu) { - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c deleted file mode 100644 index 1dedbf30..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-client.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Client Side of the Dynamic Host Configuration Protocol for IPv4 - * - * This implements the public API around the NDhcp4Client object. The client - * object is simply a context to track running probes. It manages pending - * events of all probes, as well as forwards the dispatching requests whenever - * the dispatcher is run. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-stdaux.h> -#include <errno.h> -#include <linux/if_ether.h> -#include <linux/if_infiniband.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <time.h> -#include <unistd.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -/** - * n_dhcp4_client_config_new() - allocate new client configuration - * @configp: output argument for new client config - * - * This creates a new client configuration object. Client configurations are - * unlinked objects that merely serve as collection of parameters. They do not - * perform validity checks. - * - * The new client configuration is fully owned by the caller. They are - * responsible to free the object if no longer needed. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_config_new(NDhcp4ClientConfig **configp) { - _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL; - - config = calloc(1, sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NDhcp4ClientConfig)N_DHCP4_CLIENT_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_dhcp4_client_config_free() - destroy client configuration - * @config: client configuration to operate on, or NULL - * - * This destroys a client configuration and deallocates all its resources. If - * NULL is passed, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientConfig *n_dhcp4_client_config_free(NDhcp4ClientConfig *config) { - if (!config) - return NULL; - - free(config->client_id); - free(config); - - return NULL; -} - -/** - * n_dhcp4_client_config_dup() - duplicate client configuration - * @config: client configuration to operate on - * @dupp: output argument for duplicate - * - * This duplicates the client configuration given as @config and returns it in - * @dupp to the caller. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **dupp) { - _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *dup = NULL; - int r; - - r = n_dhcp4_client_config_new(&dup); - if (r) - return r; - - dup->ifindex = config->ifindex; - dup->transport = config->transport; - dup->request_broadcast = config->request_broadcast; - memcpy(dup->mac, config->mac, sizeof(dup->mac)); - dup->n_mac = config->n_mac; - memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac)); - dup->n_broadcast_mac = config->n_broadcast_mac; - - r = n_dhcp4_client_config_set_client_id(dup, - config->client_id, - config->n_client_id); - if (r) - return r; - - *dupp = dup; - dup = NULL; - return 0; -} - -/** - * n_dhcp4_client_config_set_ifindex() - set ifindex property - * @config: client configuration to operate on - * @ifindex: ifindex to set - * - * This sets the ifindex property of the client configuration. The ifindex - * specifies the network device that a DHCP client will run on. - */ -_c_public_ void n_dhcp4_client_config_set_ifindex(NDhcp4ClientConfig *config, int ifindex) { - config->ifindex = ifindex; -} - -/** - * n_dhcp4_client_config_set_transport() - set transport property - * @config: client configuration to operate on - * @transport: transport to set - * - * This sets the transport property of the client configuration. The transport - * defines the hardware transport of the network device that a DHCP client - * runs on. - * - * This takes one of the N_DHCP4_TRANSPORT_* identifiers as argument. - */ -_c_public_ void n_dhcp4_client_config_set_transport(NDhcp4ClientConfig *config, unsigned int transport) { - config->transport = transport; -} - -/** - * n_dhcp4_client_config_set_request_broadcast() - set request-broadcast property - * @config: configuration to operate on - * @request_broadcast: value to set - * - * This sets the request_broadcast property of the given configuration object. - * - * The default is false. If set to true, a the server will be told to not unicast - * replies to the client's IP address before it has been configured, but broadcast - * to INADDR_ANY instead. In most cases, you do not want this. - * - * Background: OFFER and ACK messages from DHCP servers to clients are unicast - * to the IP address handed out, even before the IP address has - * been configured on the target interface. This usually works - * because the correct destination hardware address is explicitly - * set on the outgoing packets, rather than being resolved (which - * would not work). However, some hardware does not accept incoming - * IP packets destined for addresses they do not own, even if the - * hardware address is correct. In this case, the server must - * broadcast the replies in order for the client to receive them. - * In general, unnecessary broadcasting is something one wants to - * avoid, and some networks will not deliver broadcasts to the - * client at all, in which case this flag must not be set. - */ -_c_public_ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, bool request_broadcast) { - config->request_broadcast = request_broadcast; -} - -/** - * n_dhcp4_client_config_set_mac() - set mac property - * @config: client configuration to operate on - * @mac: hardware address to set - * @n_mac: length of the hardware address - * - * This sets the mac property of the client configuration. It specifies the - * hardware address of the local interface that the DHCP client runs on. - * - * This function copies the specified hardware address into @config. Any - * hardware address is supported. It is up to the consumer of the client - * configuration to verify the validity of the hardware address. - * - * Note: This function may truncate the hardware address internally, but - * retains the original length. The consumer of this configuration can - * thus tell whether the data was truncated and will refuse it. - * The internal buffer is big enough to hold any hardware address of all - * supported transports. Thus, truncation only happens if you use - * unsupported transports, and those will be rejected, anyway. - */ -_c_public_ void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { - config->n_mac = n_mac; - - if (n_mac > sizeof(config->mac)) - n_mac = sizeof(config->mac); - - memcpy(config->mac, mac, n_mac); -} - -/** - * n_dhcp4_client_config_set_broadcast_mac() - set broadcast-mac property - * @config: client configuration to operate on - * @mac: hardware address to set - * @n_mac: length of the hardware address - * - * This sets the broadcast-mac property of the client configuration. It - * specifies the destination hardware address to use for broadcasts on the - * local interface that the DHCP client runs on. - * - * This function copies the specified hardware address into @config. Any - * hardware address is supported. It is up to the consumer of the client - * configuration to verify the validity of the hardware address. - * - * Note: This function may truncate the hardware address internally, but - * retains the original length. The consumer of this configuration can - * thus tell whether the data was truncated and will refuse it. - * The internal buffer is big enough to hold any hardware address of all - * supported transports. Thus, truncation only happens if you use - * unsupported transports, and those will be rejected, anyway. - */ -_c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { - config->n_broadcast_mac = n_mac; - - if (n_mac > sizeof(config->mac)) - n_mac = sizeof(config->mac); - - memcpy(config->broadcast_mac, mac, n_mac); -} - -/** - * n_dhcp4_client_config_set_client_id() - set client-id property - * @config: client configuration to operate on - * @id: client id - * @n_id: length of the client id in bytes. The length - * must be from 2 up to 255 bytes. Set it to 0 - * to unset the client-id. - * - * This sets the client-id property of @config. It copies the entire client-id - * buffer into the configuration. - * See RFC 2132 (section 9.14) for the format of the Client Identifier. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id) { - uint8_t *t; - - if (n_id == 0) { - config->client_id = c_free(config->client_id); - config->n_client_id = 0; - return 0; - } - - if (n_id < 2 || n_id > 255) - return -EINVAL; - - t = malloc(n_id + 1); - if (!t) - return -ENOMEM; - - memcpy(t, id, n_id); - t[n_id] = 0; /* safety 0 for debugging */ - - free(config->client_id); - config->client_id = t; - config->n_client_id = n_id; - return 0; -} - -/** - * n_dhcp4_client_set_log_level() - set the logging level of the client - * @client: the client to operate on - * @level: the minimum syslog logging level that is - * still logged. For example, set to LOG_NOTICE - * to receive logging events with level LOG_NOTICE - * and higher. Set to -1 to disable generating - * logging events (which is also the default). - * - * By enabling logging, you can get N_DHCP4_CLIENT_EVENT_LOG events. - * - * From the logging event you may steal the message if (and only if) "allow_steal_message" - * is true. In that case, clear the message field and free the message yourself. - * - * If a logging event cannot be logged due to out of memory, one message - * gets logged that messages are missing. Until the event with that message - * gets dropped, no further logging events will be queued. - * - * You may change the logging level at any time, but it does not affect - * logging events that are already queued. - */ -_c_public_ void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level) { - client->log_queue.log_level = level; -} - -/** - * n_dhcp4_c_event_node_new() - allocate new event - * @nodep: output argument for new event - * - * This allocates a new event node and returns it to the caller. The caller - * fully owns the event-node and is reposonsible to either link it somewhere, - * or release it. - * - * Event nodes can be linked on a client object, as well as optionally on a - * probe object. As long as an event-node is linked, it will be retrievable by - * the API user through n_dhcp4_client_pop_event(). Furthermore, destruction of - * the client, or probe respectively, will clean-up all pending events. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_c_event_node_new(NDhcp4CEventNode **nodep) { - NDhcp4CEventNode *node; - - node = calloc(1, sizeof(*node)); - if (!node) - return -ENOMEM; - - *node = (NDhcp4CEventNode)N_DHCP4_C_EVENT_NODE_NULL(*node); - - *nodep = node; - return 0; -} - -/** - * n_dhcp4_c_event_node_free() - deallocate event - * @node: node to operate on, or NULL - * - * This deallocates the node given as @node. If the node is linked on a client - * or probe, it is unlinked automatically. - * - * If @probe is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node) { - if (!node) - return NULL; - - switch (node->event.event) { - case N_DHCP4_CLIENT_EVENT_OFFER: - node->event.offer.lease = n_dhcp4_client_lease_unref(node->event.offer.lease); - break; - case N_DHCP4_CLIENT_EVENT_GRANTED: - node->event.granted.lease = n_dhcp4_client_lease_unref(node->event.granted.lease); - break; - case N_DHCP4_CLIENT_EVENT_EXTENDED: - node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease); - break; - case N_DHCP4_CLIENT_EVENT_LOG: - if (_c_unlikely_(!node->event.log.allow_steal_message)) { - /* @node is the static node "nomem_node". It must not be - * freed. */ - c_list_unlink(&node->client_link); - node->is_public = false; - return NULL; - } - node->event.log.message = c_free((char *)node->event.log.message); - break; - default: - break; - } - - c_list_unlink(&node->probe_link); - c_list_unlink(&node->client_link); - free(node); - - return NULL; -} - -/** - * n_dhcp4_client_new() - allocate new client - * @clientp: output argument for new client - * @config: configuration to use - * - * This allocates a new DHCP4 client object and returns it in @clientp to the - * caller. The caller then owns a single ref-count to the object and is - * responsible to drop it, when no longer needed. - * - * The configuration given as @config is used to initialize the client. The - * caller is free to destroy the configuration once this function returns. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *config) { - _c_cleanup_(n_dhcp4_client_unrefp) NDhcp4Client *client = NULL; - struct epoll_event ev = { - .events = EPOLLIN, - }; - int r; - - c_assert(clientp); - - /* verify configuration */ - { - if (config->ifindex < 1) - return N_DHCP4_E_INVALID_IFINDEX; - - switch (config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - if (config->n_mac != ETH_ALEN || - config->n_broadcast_mac != ETH_ALEN) - return N_DHCP4_E_INVALID_ADDRESS; - - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - if (config->n_mac != INFINIBAND_ALEN || - config->n_broadcast_mac != INFINIBAND_ALEN) - return N_DHCP4_E_INVALID_ADDRESS; - - break; - default: - return N_DHCP4_E_INVALID_TRANSPORT; - } - - if (config->n_client_id < 1) - return N_DHCP4_E_INVALID_CLIENT_ID; - } - - client = malloc(sizeof(*client)); - if (!client) - return -ENOMEM; - - *client = (NDhcp4Client)N_DHCP4_CLIENT_NULL(*client); - - r = n_dhcp4_client_config_dup(config, &client->config); - if (r) - return r; - - client->fd_epoll = epoll_create1(EPOLL_CLOEXEC); - if (client->fd_epoll < 0) - return -errno; - - client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK); - if (client->fd_timer < 0 && errno == EINVAL) - client->fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); - if (client->fd_timer < 0) - return -errno; - - ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER; - r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev); - if (r < 0) { - close(client->fd_timer); - client->fd_timer = -1; - return -errno; - } - - *clientp = client; - client = NULL; - return 0; -} - -static void n_dhcp4_client_free(NDhcp4Client *client) { - NDhcp4CEventNode *node, *t_node; - - c_assert(!client->current_probe); - - c_list_for_each_entry_safe(node, t_node, &client->event_list, client_link) - n_dhcp4_c_event_node_free(node); - - if (client->fd_timer >= 0) { - epoll_ctl(client->fd_epoll, EPOLL_CTL_DEL, client->fd_timer, NULL); - close(client->fd_timer); - } - - if (client->fd_epoll >= 0) - close(client->fd_epoll); - - n_dhcp4_client_config_free(client->config); - free(client); -} - -/** - * n_dhcp4_client_ref() - acquire client reference - * @client: client to operate on, or NULL - * - * This acquires a reference to the client given as @client. If @client is - * NULL, this function is a no-op. - * - * Return: @client is returned. - */ -_c_public_ NDhcp4Client *n_dhcp4_client_ref(NDhcp4Client *client) { - if (client) - ++client->n_refs; - return client; -} - -/** - * n_dhcp4_client_unref() - release client reference - * @client: client to operate on, or NULL - * - * This releases a reference to the client given as @client. If @client is - * NULL, this is a no-op. - * - * Once the last reference is dropped, the client object will get destroyed and - * deallocated. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4Client *n_dhcp4_client_unref(NDhcp4Client *client) { - if (client && !--client->n_refs) - n_dhcp4_client_free(client); - return NULL; -} - -/** - * n_dhcp4_client_raise() - raise event - * @client: client to operate on - * @nodep: output argument for new event, or NULL - * @event: event type to use - * - * This creates a new event-node on @client, setting the event-type to @event. - * The newly created event-node is returned to the caller in @nodep (unless - * @nodep is NULL). - * - * The event-node is automatically linked on @client. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigned int event) { - NDhcp4CEventNode *node; - int r; - - r = n_dhcp4_c_event_node_new(&node); - if (r) - return r; - - node->event.event = event; - c_list_link_tail(&client->event_list, &node->client_link); - - if (nodep) - *nodep = node; - return 0; -} - -/** - * n_dhcp4_log_queue_fmt() - add a logging event. - * @client: the NDhcp4LogQueue to operate on - * @level: the syslog logging level - * @fmt: the format string for the message - * @... printf arguments for logging - * - * Appends a logging event to the event queue if logging is - * enabled and the logging level sufficiently high. - * - * Queuing a logging event might fail with out of memory. - * In that case, a static event will be queued that informs - * about lost messages. - */ -void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, - int level, - const char *fmt, - ...) { - NDhcp4CEventNode *node; - char *message; - va_list ap; - int r; - - if (level > log_queue->log_level) - return; - - /* Currently, the logging queue is only implemented for - * the client. Nobody would enable logging except a - * client instance. */ - c_assert(log_queue->is_client); - - if (!c_list_is_empty (&log_queue->nomem_node.client_link)) { - /* we have the nomem_node queued after a recent out - * of memory. This disables all logging messages until - * the event gets popped. - * - * The reason is that we can only queue the nomem_node once, - * so if we now try to append another event and succeed, the - * user wouldn't know which messages got dropped. Instead, - * just drop them all!! */ - return; - } - - r = n_dhcp4_c_event_node_new(&node); - if (r < 0) - goto handle_nomem; - - va_start(ap, fmt); - r = vasprintf(&message, fmt, ap); - va_end(ap); - - if (r < 0) { - n_dhcp4_c_event_node_free(node); - goto handle_nomem; - } - - node->event = (NDhcp4ClientEvent) { - .event = N_DHCP4_CLIENT_EVENT_LOG, - .log = { - .level = level, - .message = message, - .allow_steal_message = true, - }, - }; - - c_list_link_tail(log_queue->event_list, &node->client_link); - return; - -handle_nomem: - c_list_link_tail(log_queue->event_list, &log_queue->nomem_node.client_link); -} - -/** - * n_dhcp4_client_arm_timer() - update timer - * @client: client to operate on - * - * This updates the timer on @client to fire on the next pending timeout. This - * must be called whenever a timeout on @client might have changed. - */ -void n_dhcp4_client_arm_timer(NDhcp4Client *client) { - uint64_t now, offset, timeout = 0; - int r; - - if (client->current_probe) - n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout); - - if (timeout != client->scheduled_timeout) { - /* - * Across our codebase, timeouts are specified as absolute - * timestamps on CLOCK_BOOTTIME. Unfortunately, there are - * systems with CLOCK_BOOTTIME support, but timerfd lacks it - * (in particular RHEL). Therefore, our timerfd might be on - * CLOCK_MONOTONIC. - * To account for this, we always schedule a relative timeout. - * We fetch the current time and then calculate the offset - * which we then schedule as relative timeout on the timerfd. - * This works regardless which clock the timerfd runs on. - * Once we no longer support CLOCK_MONOTONIC as fallback, we - * can simply switch to TFD_TIMER_ABSTIME here and specify - * `timeout` directly as value. - */ - now = n_dhcp4_gettime(CLOCK_BOOTTIME); - if (now >= timeout) - offset = 1; /* 0 would disarm the timerfd */ - else - offset = timeout - now; - - r = timerfd_settime(client->fd_timer, - 0, - &(struct itimerspec){ - .it_value = { - .tv_sec = offset / UINT64_C(1000000000), - .tv_nsec = offset % UINT64_C(1000000000), - }, - }, - NULL); - c_assert(r >= 0); - - client->scheduled_timeout = timeout; - } -} - -/** - * n_dhcp4_client_get_fd() - retrieve event FD - * @client: client to operate on - * @fdp: output argument to store FD - * - * This retrieves the FD used by the client object given as @client. The FD is - * always valid, and returned in @fdp. - * - * The caller is expected to poll this FD for readable events and call - * n_dhcp4_client_dispatch() whenever the FD is readable. - */ -_c_public_ void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp) { - *fdp = client->fd_epoll; -} - -static int n_dhcp4_client_dispatch_timer(NDhcp4Client *client, struct epoll_event *event) { - uint64_t v, ns_now; - int r; - - if (event->events & (EPOLLHUP | EPOLLERR)) { - /* - * There is no way to handle either gracefully. If we ignored - * them, we would busy-loop, so lets rather forward the error - * to the caller. - */ - return -ENOTRECOVERABLE; - } - - if (event->events & EPOLLIN) { - r = read(client->fd_timer, &v, sizeof(v)); - if (r < 0) { - if (errno == EAGAIN) { - /* - * There are no more pending events, so nothing - * to be done. Return to the caller. - */ - return 0; - } - - /* - * Something failed. We use CLOCK_BOOTTIME/MONOTONIC, - * so ECANCELED cannot happen. Hence, there is no error - * that we could gracefully handle. Fail hard and let - * the caller deal with it. - */ - return -errno; - } else if (r != sizeof(v) || v == 0) { - /* - * Kernel guarantees 8-byte reads, and only to return - * data if at least one timer triggered; fail hard if - * it suddenly starts exposing unexpected behavior. - */ - return -ENOTRECOVERABLE; - } - - /* - * Forward the timer-event to the active probe. Timers should - * not fire if there is no probe running, but lets ignore them - * for now, so probe-internals are not leaked to this generic - * client dispatcher. - */ - if (client->current_probe) { - /* - * Read the current time *after* dispatching the timer, - * to make sure we do not miss wakeups. - */ - ns_now = n_dhcp4_gettime(CLOCK_BOOTTIME); - - r = n_dhcp4_client_probe_dispatch_timer(client->current_probe, - ns_now); - if (r) - return r; - } - } - - return 0; -} - -static int n_dhcp4_client_dispatch_io(NDhcp4Client *client, struct epoll_event *event) { - int r; - - if (client->current_probe) - r = n_dhcp4_client_probe_dispatch_io(client->current_probe, - event->events); - else - return -ENOTRECOVERABLE; - - return r; -} - -/** - * n_dhcp4_client_dispatch() - dispatch client - * @client: client to operate on - * - * This dispatches pending operations on @client. It will read incoming - * messages, write pending data, and handle any timeouts. - * - * This function never blocks. - * - * If there are more events to dispatch, than would be reasonable to do in a - * single dispatch, this will return N_DHCP4_E_PREEMPTED. In this case the - * caller is expected to call into this function again when it is ready to - * dispatch more events. - * If your event loop is level-triggered (it very likely is), you can - * optionally ignore this return code and treat it as success. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_PREEMPTED if - * there is more data to dispatch. - */ -_c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { - struct epoll_event events[2]; - int n, i, r = 0; - - n = epoll_wait(client->fd_epoll, events, sizeof(events) / sizeof(*events), 0); - if (n < 0) { - /* Linux never returns EINTR if `timeout == 0'. */ - return -errno; - } - - client->preempted = false; - - for (i = 0; i < n; ++i) { - switch (events[i].data.u32) { - case N_DHCP4_CLIENT_EPOLL_TIMER: - r = n_dhcp4_client_dispatch_timer(client, events + i); - break; - case N_DHCP4_CLIENT_EPOLL_IO: - r = n_dhcp4_client_dispatch_io(client, events + i); - break; - default: - c_assert(0); - r = 0; - break; - } - - if (r) { - if (r == N_DHCP4_E_DOWN) { - r = n_dhcp4_client_raise(client, - NULL, - N_DHCP4_CLIENT_EVENT_DOWN); - if (r) - return r; - - /* continue normally */ - } else if (r) { - if (r >= _N_DHCP4_E_INTERNAL) { - n_dhcp4_log(&client->log_queue, - LOG_ERR, - "invalid internal error code %d after dispatch", - r); - return N_DHCP4_E_INTERNAL; - } - return r; - } - } - } - - n_dhcp4_client_arm_timer(client); - - return client->preempted ? N_DHCP4_E_PREEMPTED : 0; -} - -/** - * n_dhcp4_client_pop_event() - fetch pending event - * @client: client to operate on - * @eventp: output argument to store next event - * - * This fetches the next pending event from the event-queue and returns it to a - * caller. A pointer to the event is stored in @eventp. If there is no more - * event queued, NULL is returned. - * - * If a valid event is returned, it is accessible until the next call to this - * function, or the destruction of the context object (this might be either the - * client object or the probe object, pointed to by the event), whichever - * happens first. - * That is, the caller should not pin the returned event object, but copy - * required information into their own state tracking contexts. - * - * The possible events are: - * * N_DHCP4_CLIENT_EVENT_OFFER: A lease offered from a server in response - * to a probe. Several such offers may be - * received until one of them is selected by - * the caller. Only one lease may be selected. - * The attached lease object may be queried - * for information in order to decide which - * lease to select, though the information is - * not guaranteed to stay the same in the - * final lease. - * * N_DHCP4_CLIENT_EVENT_GRANTED: A selected lease was granted by the server. - * The information in the attached lease - * object should be used to configure the - * client. Once the client has been - * configured, the lease should be accepted. - * * N_DHCP4_CLIENT_EVENT_RETRACTED: A selected lease offer was retracted by the - * server. This can happen in case the server - * offers the same lease to several clients, - * or the server discovers that the IP address - * in the lease is already in use. - * * N_DHCP4_CLIENT_EVENT_EXTENDED: An active lease is extended, if applicable - * the kernel should be updated with the new - * lifetime information for addresses and/or - * routes. - * * N_DHCP4_CLIENT_EVENT_EXPIRED: An active lease failed to be extended by - * the end of its lifetime. The client should - * immediately stop using the information - * contained in the lease. - * * N_DHCP4_CLIENT_EVENT_DOWN: The network interface was put down down. - * The user is recommended to reestablish the - * lease at the first opportunity when the - * network comes back up. Note that this is - * purely informational, the probe will keep - * running, and if the network topology does - * not change any lease we have will still be - * valid. - * * N_DHCP4_CLIENT_EVENT_CANCELLED: The probe was cancelled. This can happen if - * the client attempted several incompatible - * probes in parallel, then the most recent - * ones will be cancelled asynchronously. - * * N_DHCP4_CLIENT_EVENT_LOG: A logging event if n_dhcp4_client_set_log_level() - * is enabled. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp) { - NDhcp4CEventNode *node, *t_node; - - c_list_for_each_entry_safe(node, t_node, &client->event_list, client_link) { - if (node->is_public) { - n_dhcp4_c_event_node_free(node); - continue; - } - - node->is_public = true; - *eventp = &node->event; - return 0; - } - - *eventp = NULL; - return 0; -} - -/** - * n_dhcp4_client_update_mtu() - update link mtu - * @client: client to operate on - * @mtu: new mtu - * - * This updates the link MTU used by the client object. By default, the minimum - * requirement given by the IP specification is assumed, which means 576 - * bytes. The caller is advised to update this to the actual MTU used by the - * link layer. - * - * This value reflects the MTU of the link layer. That is, it is the maximum - * packet size that you can send on that link, excluding the link-header but - * including the IP-header. On ethernet-v2 this would be 1500. - * - * If unsure, it is safe to leave this unset. However, in this case a DHCP - * server will be required to omit information if it does not fit into the - * default MTU. - * - * Unless you keep the default MTU, you should update the MTU whenever the link - * MTU changes. That is, when it is increased *and* when it is decreased. - * However, you must be aware that decreasing the MTU on a link might cause - * temporary data loss. - * - * Background: Knowing the link MTU guarantees that we can possibly transmit - * packets bigger than the IP minimum (i.e., 576 bytes). However, - * it does not guarantee that a possible target supports parsing - * packets bigger than the IP minimum. Hence, the MTU is used by a - * client to send a hint to a server that it can receive replies - * bigger than the minimum. As such, a server can reply with more - * information than otherwise possible. - * Since this DHCP client does not support fragmented packets, we - * simply set the allowed packet-size to the local link MTU. - * Note that DHCP relays might cause DHCP packets to be routed. - * However, such relays are required to always reassemble any - * fragments they receive into full DHCP packets, before they - * forward them either way. This guarantees that incoming packets - * are never fragmented, unless they exceed the local link MTU - * (this would otherwise not neccessarily be true, if some other - * part of the routed network had a lower MTU). - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu) { - int r; - - if (mtu == client->mtu) - return 0; - - if (client->current_probe) { - r = n_dhcp4_client_probe_update_mtu(client->current_probe, mtu); - if (r) - return r; - } - - client->mtu = mtu; - return 0; -} - -/** - * n_dhcp4_client_probe() - create a new probe - * @client: client to operate on - * @probep: output argument to store new probe - * @config: probe configuration to use - * - * This creates a new probe on @client. Probes represent DHCP requests and - * track the state over the entire lifetime of a lease. Once a probe is created - * it will start looking for DHCP servers, request a lease from them, and renew - * the lease continuously whenever it expires. Furthermore, if a lease cannot be - * renewed, a new lease will be requested. - * - * The API allows for many probes to be run at the same time. However, the DHCP - * specification forbids many of those cases (e.g., you must not reuse a client - * id, otherwise it will be impossible to track who to forward received packets - * to). Hence, so far only a single probe can run at a time. If you create a - * new probe, all older probes that conflict with that probe will be canceled - * (their state machine is halted and a N_DHCP4_CLIENT_EVENT_CANCELLED event is - * raised. - * This might change in the future, though. There might be cases where multiple - * probes can be run in parallel (e.g., with different client-ids, or an INFORM - * in parallel to a REQUEST, ...). - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe(NDhcp4Client *client, - NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config) { - _c_cleanup_(n_dhcp4_client_probe_freep) NDhcp4ClientProbe *probe = NULL; - uint64_t ns_now; - int r; - - ns_now = n_dhcp4_gettime(CLOCK_BOOTTIME); - - r = n_dhcp4_client_probe_new(&probe, config, client, ns_now); - if (r) - return r; - - n_dhcp4_client_arm_timer(client); - - *probep = probe; - probe = NULL; - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-incoming.c b/shared/n-dhcp4/src/n-dhcp4-incoming.c deleted file mode 100644 index f739413b..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-incoming.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * DHCPv4 Incoming Messages - * - * This file implements the message parser object for incoming DHCP4 messages. - * It takes a linear data blob as input, and provides accessors for the message - * content. - * - * This wrapper mainly deals with the OPTIONs array. That is, in hides the - * different overload-sections the DHCP4 spec defines, it concatenates - * duplicate option fields (as described by the spec), and provides a - * consistent view to the caller. - * - * Internally, for every incoming message we linearize its OPTIONs. This means, - * we create a copy of the contents, and merge all duplicate options into a - * single option entry. We then provide accessors to the caller to easily get - * O(1) access to individual fields. - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -static void n_dhcp4_incoming_prefetch(NDhcp4Incoming *incoming, size_t *offset, uint8_t option, const uint8_t *raw, size_t n_raw) { - uint8_t o, l; - size_t pos; - - for (pos = 0; pos < n_raw; ) { - o = raw[pos++]; - if (o == N_DHCP4_OPTION_PAD) - continue; - if (o == N_DHCP4_OPTION_END) - return; - - /* bail out if no remaining space for length field */ - if (pos >= n_raw) - return; - - /* bail out if length exceeds the available space */ - l = raw[pos++]; - if (l > n_raw || pos > n_raw - l) - return; - - /* prefetch content if it matches @option */ - if (o == option) { - memcpy((uint8_t *)&incoming->message + *offset, raw + pos, l); - *offset += l; - } - - pos += l; - } -} - -static void n_dhcp4_incoming_merge(NDhcp4Incoming *incoming, size_t *offset, uint8_t overload, uint8_t option) { - uint8_t *m = (uint8_t *)&incoming->message; - size_t pos; - - /* - * Prefetch all options matching @option from the 3 sections, - * concatenating their content. Remember the offset and size of the - * option in our message state. - */ - - pos = *offset; - - /* prefetch option from OPTIONS */ - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, options), - incoming->n_message - offsetof(NDhcp4Message, options)); - - /* prefetch option from FILE */ - if (overload & N_DHCP4_OVERLOAD_FILE) - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, file), - sizeof(incoming->message.file)); - - /* prefetch option from SNAME */ - if (overload & N_DHCP4_OVERLOAD_SNAME) - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, sname), - sizeof(incoming->message.sname)); - - incoming->options[option].value = m + pos; - incoming->options[option].size = *offset - pos; -} - -static void n_dhcp4_incoming_linearize(NDhcp4Incoming *incoming) { - uint8_t *m, o, l, overload; - size_t i, pos, end, offset; - - /* - * Linearize all OPTIONs of the incoming message. We know that - * @incoming->message is preallocated to be big enough to hold the - * entire linearized message _trailing_ the original copy. All we have - * to do is walk the raw message in @incoming->message and for each - * option we find, copy it into the trailing space, concatenating all - * instances we find. - * - * Before we can copy the individual options, we must scan for the - * OVERLOAD option. This is required so our prefetcher knows which data - * arrays to scan for prefetching. - * - * So far, we require the OVERLOAD option to be present in the - * options-array (which is obvious and a given). However, if the option - * occurs multiple times outside of the options-array (i.e., SNAME or - * FILE), we silently ignore them. The specification does not allow - * multiple OVERLOAD options, anyway. Hence, this behavior only defines - * what we do when we see broken implementations, and we currently seem - * to support all styles we saw in the wild so far. - */ - - m = (uint8_t *)&incoming->message; - offset = incoming->n_message; - - n_dhcp4_incoming_merge(incoming, &offset, 0, N_DHCP4_OPTION_OVERLOAD); - if (incoming->options[N_DHCP4_OPTION_OVERLOAD].size >= 1) - overload = *incoming->options[N_DHCP4_OPTION_OVERLOAD].value; - else - overload = 0; - - for (i = 0; i < 3; ++i) { - if (i == 0) { /* walk OPTIONS */ - pos = offsetof(NDhcp4Message, options); - end = incoming->n_message; - } else if (i == 1) { /* walk FILE */ - if (!(overload & N_DHCP4_OVERLOAD_FILE)) - continue; - - pos = offsetof(NDhcp4Message, file); - end = pos + sizeof(incoming->message.file); - } else { /* walk SNAME */ - if (!(overload & N_DHCP4_OVERLOAD_SNAME)) - continue; - - pos = offsetof(NDhcp4Message, sname); - end = pos + sizeof(incoming->message.sname); - } - - while (pos < end) { - o = m[pos++]; - if (o == N_DHCP4_OPTION_PAD) - continue; - if (o == N_DHCP4_OPTION_END) - break; - if (pos >= end) - break; - - l = m[pos++]; - if (l > end || pos > end - l) - break; - - if (!incoming->options[o].value) - n_dhcp4_incoming_merge(incoming, &offset, overload, o); - - pos += l; - } - } -} - -/** - * n_dhcp4_incoming_new() - Allocate new incoming message object - * @incomingp: output argument for new object - * @raw: raw message blob - * @n_raw: length of the raw message blob - * - * This function allocates a new incoming-message object to wrap a received - * message blob. It performs basic verification of the message length and - * header, and then linearizes the DHCP4 options. - * - * The incoming-message object mainly provides accessors for the option-array. - * It handles all the different quirks around parsing and concatenating the - * options array, and provides the assembled data to the caller. It does not, - * however, in any way interpret the data of the individual options. This is up - * to the caller to do. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_MALFORMED if - * the message is not a valid DHCP4 message. - */ -int n_dhcp4_incoming_new(NDhcp4Incoming **incomingp, const void *raw, size_t n_raw) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming = NULL; - size_t size; - - if (n_raw < sizeof(NDhcp4Message) || n_raw > UINT16_MAX) - return N_DHCP4_E_MALFORMED; - - /* - * Allocate enough space for book-keeping, a copy of @raw and trailing - * space for linearized options. The trailing space must be big enough - * to hold the entire options array unmodified (linearizing can only - * make it smaller). Hence, just allocate enough space to hold the raw - * message without the header. - */ - size = sizeof(*incoming) + n_raw - sizeof(NDhcp4Message); - size += n_raw - sizeof(NDhcp4Header); - - incoming = calloc(1, size); - if (!incoming) - return -ENOMEM; - - *incoming = (NDhcp4Incoming)N_DHCP4_INCOMING_NULL(*incoming); - incoming->n_message = n_raw; - memcpy(&incoming->message, raw, n_raw); - - if (incoming->message.magic != htobe32(N_DHCP4_MESSAGE_MAGIC)) - return N_DHCP4_E_MALFORMED; - - /* linearize options */ - n_dhcp4_incoming_linearize(incoming); - - *incomingp = incoming; - incoming = NULL; - return 0; -} - -/** - * n_dhcp4_incoming_free() - Deallocate message object - * @incoming: object to operate on, or NULL - * - * This deallocates and frees the given incoming-message object. If NULL is - * passed, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4Incoming *n_dhcp4_incoming_free(NDhcp4Incoming *incoming) { - if (!incoming) - return NULL; - - free(incoming); - - return NULL; -} - -/** - * n_dhcp4_incoming_get_header() - Return message header - * @incoming: message to operate on - * - * This returns a pointer to the message header. Note that modifications to - * this header are permanent and will affect the original message. - * - * Return: A pointer to the message header is returned. - */ -NDhcp4Header *n_dhcp4_incoming_get_header(NDhcp4Incoming *incoming) { - return &incoming->message.header; -} - -/** - * n_dhcp4_incoming_get_raw() - Get access to the raw original message - * @incoming: message to operate on - * @rawp: output argument for the raw blob, or NULL - * - * This hands out a pointer to the raw message blob to the caller. This will - * point to the original message content, rather than the linearized version. - * - * Note that if the caller queried the contents of the message before, any - * modifications done by the caller will not affect the original message. It - * only affects the linearized content (which is a duplicate trailing the - * original message). However, modifications to the message header *DO* also - * appear in the original, since the message header is not duplicated. - * - * In either case, it is better to never modify the message, if you intend to - * forward it further. - * - * Return: The length of the raw message blob is returned. - */ -size_t n_dhcp4_incoming_get_raw(NDhcp4Incoming *incoming, const void **rawp) { - if (rawp) - *rawp = &incoming->message; - return incoming->n_message; -} - -/** - * n_dhcp4_incoming_query() - Query the contents of a specific option - * @incoming: message to query - * @option: option to look for - * @datap: output argument for the option-data, or NULL - * @n_datap: output argument for the length of the option, or NULL - * - * This returns a pointer to the requested option blob in the message. It - * points to a linearized version of all respective option-fields of the same - * type. Hence, the caller is not required to deal with multiple occurrences of - * the same option. - * - * If an option was not present in the incoming message, N_DHCP4_E_UNSET is - * returned. Note that this is different from an empty option! And empty option - * will return a valid pointer and size 0. - * - * Note that the pointer to the option-blob does not point to the original - * message, but a duplicated version. Modifications to the blob will not be - * reflected in the original message, but they will be permanent regarding - * further queries through this function. - * - * Note that the original message alignment might no longer be reflected in the - * returned blob. You must not alias the content of the blob, but always copy - * it out, or consume piecemeal. - * - * This function runs in O(1). - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_UNSET if the - * option was not found, - */ -int n_dhcp4_incoming_query(NDhcp4Incoming *incoming, uint8_t option, uint8_t **datap, size_t *n_datap) { - if (!incoming->options[option].value) - return N_DHCP4_E_UNSET; - - if (datap) - *datap = incoming->options[option].value; - if (n_datap) - *n_datap = incoming->options[option].size; - return 0; -} - -static int n_dhcp4_incoming_query_u8(NDhcp4Incoming *message, uint8_t option, uint8_t *u8p) { - uint8_t *data; - size_t n_data; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(*data)) - return N_DHCP4_E_MALFORMED; - - *u8p = *data; - return 0; -} - -static int n_dhcp4_incoming_query_u16(NDhcp4Incoming *message, uint8_t option, uint16_t *u16p) { - uint8_t *data; - size_t n_data; - uint16_t be16; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be16)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be16, data, sizeof(be16)); - - *u16p = ntohs(be16); - return 0; -} - -static int n_dhcp4_incoming_query_u32(NDhcp4Incoming *message, uint8_t option, uint32_t *u32p) { - uint8_t *data; - size_t n_data; - uint32_t be32; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be32)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be32, data, sizeof(be32)); - - *u32p = ntohl(be32); - return 0; -} - -static int n_dhcp4_incoming_query_in_addr(NDhcp4Incoming *message, uint8_t option, struct in_addr *addrp) { - uint8_t *data; - size_t n_data; - uint32_t be32; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be32)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be32, data, sizeof(be32)); - - addrp->s_addr = be32; - return 0; -} - -int n_dhcp4_incoming_query_message_type(NDhcp4Incoming *message, uint8_t *typep) { - return n_dhcp4_incoming_query_u8(message, N_DHCP4_OPTION_MESSAGE_TYPE, typep); -} - -int n_dhcp4_incoming_query_lifetime(NDhcp4Incoming *message, uint32_t *lifetimep) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME, lifetimep); -} - -int n_dhcp4_incoming_query_t2(NDhcp4Incoming *message, uint32_t *t2p) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_REBINDING_T2_TIME, t2p); -} - -int n_dhcp4_incoming_query_t1(NDhcp4Incoming *message, uint32_t *t1p) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_RENEWAL_T1_TIME, t1p); -} - -int n_dhcp4_incoming_query_server_identifier(NDhcp4Incoming *message, struct in_addr *idp) { - return n_dhcp4_incoming_query_in_addr(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, idp); -} - -int n_dhcp4_incoming_query_max_message_size(NDhcp4Incoming *message, uint16_t *max_message_sizep) { - return n_dhcp4_incoming_query_u16(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, max_message_sizep); -} - -int n_dhcp4_incoming_query_requested_ip(NDhcp4Incoming *message, struct in_addr *requested_ipp) { - return n_dhcp4_incoming_query_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, requested_ipp); -} - -void n_dhcp4_incoming_get_xid(NDhcp4Incoming *message, uint32_t *xidp) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - - *xidp = header->xid; -} - -void n_dhcp4_incoming_get_yiaddr(NDhcp4Incoming *message, struct in_addr *yiaddr) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - - yiaddr->s_addr = header->yiaddr; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-outgoing.c b/shared/n-dhcp4/src/n-dhcp4-outgoing.c deleted file mode 100644 index bcab407f..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-outgoing.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * DHCPv4 Outgoing Messages - * - * XXX - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -/** - * N_DHCP4_OUTGOING_MAX_PHDR - maximum protocol header size - * - * All DHCP4 messages-limits specify the size of the entire packet including - * the protocol layer (i.e., including the IP headers and UDP headers). To - * calculate the size we have remaining for the actual DHCP message, we need to - * substract the maximum possible header-length the linux-kernel might prepend - * to our messages. This turns out to be the maximum IP-header size (including - * optional IP headers, hence 60 bytes) plus the UDP header size (i.e., 8 - * bytes). - */ -#define N_DHCP4_OUTGOING_MAX_PHDR (N_DHCP4_NETWORK_IP_MAXIMUM_HEADER_SIZE + sizeof(struct udphdr)) - -/** - * n_dhcp4_outgoing_new() - Allocate new outgoing message - * @outgoingp: output argument to return allocate object through - * @max_size: maximum transmission size to use - * @overload: select sections to overload - * - * This allocates a new outgoing message and returns it to the caller. The - * caller can then append data to it and send it over the wire. - * - * The @max_size parameter specifies the transport-layer MTU to consider. If 0, - * N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE is used. Note that this argument - * specifies the maximum packet size *INCLUDING* the IP-headers and UDP-header. - * Internally, the allocator makes sure to never create packets bigger than the - * specified MTU. The append functions will return an error, if the packet size - * would exceed the MTU. - * If you use a full UDP stack that supports packet fragmentation, you can - * specify the maximum packet size here (e.g., UINT16_MAX). - * - * Return: 0 on success, error code on failure. - */ -int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL; - - /* - * Make sure the minimum limit is bigger than the maximum protocol - * header plus the DHCP-message-header plus a single OPTION_END byte. - */ - static_assert(N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE >= N_DHCP4_OUTGOING_MAX_PHDR + - sizeof(NDhcp4Message) + 1, - "Invalid minimum IP packet limit"); - - c_assert(!(overload & ~(N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME))); - - outgoing = calloc(1, sizeof(*outgoing)); - if (!outgoing) - return -ENOMEM; - - *outgoing = (NDhcp4Outgoing)N_DHCP4_OUTGOING_NULL(*outgoing); - outgoing->n_message = N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE - N_DHCP4_OUTGOING_MAX_PHDR; - outgoing->i_message = offsetof(NDhcp4Message, options); - outgoing->max_size = outgoing->n_message; - outgoing->overload = overload; - - if (max_size > N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE) - outgoing->max_size = max_size - N_DHCP4_OUTGOING_MAX_PHDR; - - outgoing->message = calloc(1, outgoing->n_message); - if (!outgoing->message) - return -ENOMEM; - - outgoing->message->magic = htonl(N_DHCP4_MESSAGE_MAGIC); - outgoing->message->options[0] = N_DHCP4_OPTION_END; - - *outgoingp = outgoing; - outgoing = NULL; - return 0; -} - -/** - * n_dhcp4_outgoing_free() - Deallocate outgoing message - * @outgoing: message to deallocate, or NULL - * - * This is the opposite to n_dhcp4_outgoing_new(). It deallocates and frees the - * passed object. If @outgoing is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4Outgoing *n_dhcp4_outgoing_free(NDhcp4Outgoing *outgoing) { - if (!outgoing) - return NULL; - - free(outgoing->message); - free(outgoing); - - return NULL; -} - -/** - * n_dhcp4_outgoing_get_header() - Get pointer to the message header - * @outgoing: message to operate on - * - * This returns a pointer to the DHCP4 message header to the caller. The caller - * can use this to fill-in the header-fields. Note that all fields are - * initialized to their default values. Hence, you only need to override the - * fields where the default is not sufficient. - * - * Return: A pointer to the message header is returned. - */ -NDhcp4Header *n_dhcp4_outgoing_get_header(NDhcp4Outgoing *outgoing) { - return &outgoing->message->header; -} - -/** - * n_dhcp4_outgoing_get_raw() - Get the raw message blob - * @outgoing: message to operat on - * @rawp: output argument for the message-blob - * - * This function gives the caller access to the raw message-blob. That is, once - * message-marshaling is complete, use this to get the raw blob for sending. - * Note that this blob is only valid as long as you no longer append any - * further options to the message, nor modify it in any other way. - * - * Return: The size of the raw message blob is returned. - */ -size_t n_dhcp4_outgoing_get_raw(NDhcp4Outgoing *outgoing, const void **rawp) { - if (rawp) - *rawp = outgoing->message; - - /* - * Return the DHCP message until the END option, excluding any - * trailing padding. We overallocate during append, so the - * allocated message might be bigger than what we want to - * send on the wire. - */ - return outgoing->i_message + 1; -} - -static void n_dhcp4_outgoing_append_option(NDhcp4Outgoing *outgoing, - uint8_t option, - const void *data, - uint8_t n_data) { - uint8_t *blob = (void *)outgoing->message; - - blob[outgoing->i_message++] = option; - blob[outgoing->i_message++] = n_data; - memcpy(blob + outgoing->i_message, data, n_data); - outgoing->i_message += n_data; - blob[outgoing->i_message] = N_DHCP4_OPTION_END; -} - -/** - * n_dhcp4_outgoing_append() - Append option to outgoing message - * @outgoing: message to operate on - * @option: option code to append - * @data: data to append in the option - * @n_data: length of the data blob - * - * This appends another option to the given outgoing message. The data is taken - * verbatim and copied into the message. Note that no validation is done. If - * you provide an option multiple times, it will be added multiple times (spec - * then requires them to be interpreted as concatenated option, in case the - * option is marked as such). - * - * The size of a message is limited, based on the restriction passed to the - * outgoing-message constructor. If there is not enough free space to copy in - * the new option, N_DHCP4_E_NO_SPACE is returned. - * - * The order in which you append options might matter to some implementations. - * For example, the message-type is often expected to be the first option. We - * do not place such restrictions, but for compatibility with external - * implementations, you should follow these recommendations. - * Furthermore, we do not implement any kind of smart allocators. That is, all - * options are simply appended when you call this. But due to the overloading - * feature, fragmentation might matter. Hence, if you use overloading, overly - * big options might cause padding, and as such waste space. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_NO_SPACE - * when there is not sufficient free space in the message. - */ -int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing, - uint8_t option, - const void *data, - uint8_t n_data) { - NDhcp4Message *m; - uint8_t overload; - size_t rem, n; - - c_assert(option != N_DHCP4_OPTION_PAD); - c_assert(option != N_DHCP4_OPTION_END); - c_assert(option != N_DHCP4_OPTION_OVERLOAD); - - /* - * If the iterator is on the OPTIONs field, try appending the new blob. - * We need 2 header-bytes plus @n_data bytes. Additionally, we always - * reserve 3 trailing bytes for a possible OVERLOAD option, and 1 byte - * for the END marker. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, options)) { - rem = outgoing->n_message - outgoing->i_message; - - /* try fitting into remaining OPTIONs space */ - if (rem >= n_data + 2U + 3U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - /* try fitting into allowed OPTIONs space */ - if (outgoing->max_size - outgoing->i_message >= n_data + 2U + 3U + 1U) { - /* try over-allocation to reduce allocation pressure */ - n = outgoing->n_message + n_data + 128; - if (n > outgoing->max_size) - n = outgoing->max_size; - m = realloc(outgoing->message, n); - if (!m) - return -ENOMEM; - - memset((void *)m + outgoing->i_message, 0, n - outgoing->i_message); - outgoing->message = m; - outgoing->n_message = n; - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - /* not enough remaining space, try OVERLOAD */ - if (!outgoing->overload) - return N_DHCP4_E_NO_SPACE; - - /* - * We ran out of space in the OPTIONs array, but overloading - * was enabled. This means, we can insert an OVERLOAD option - * and then use SNAME/FILE to store more options. - * Note that the three different sections cannot overlap and - * all must have an END marker. So as soon as we add the - * OVERLOAD option, we must make sure the other sections have - * the valid END marker. From then on, our *_append_option() - * helper makes sure to move the END marker with every - * insertion. - */ - overload = outgoing->overload; - n_dhcp4_outgoing_append_option(outgoing, N_DHCP4_OPTION_OVERLOAD, &overload, 1); - - if (overload & N_DHCP4_OVERLOAD_FILE) - outgoing->message->file[0] = N_DHCP4_OPTION_END; - if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->message->sname[0] = N_DHCP4_OPTION_END; - - if (overload & N_DHCP4_OVERLOAD_FILE) - outgoing->i_message = offsetof(NDhcp4Message, file); - else if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->i_message = offsetof(NDhcp4Message, sname); - } - - /* - * The OPTIONs section is full and OVERLOAD was enabled. Try writing - * into the FILE section. Always reserve 1 byte for the trailing END - * marker. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, file)) { - rem = sizeof(outgoing->message->file); - rem -= outgoing->i_message - offsetof(NDhcp4Message, file); - - if (rem >= n_data + 2U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - overload = outgoing->overload; - if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->i_message = offsetof(NDhcp4Message, sname); - else - return N_DHCP4_E_NO_SPACE; - } - - /* - * OPTIONs and FILE are full, try putting data into the SNAME section - * as a last resort. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, sname)) { - rem = sizeof(outgoing->message->sname); - rem -= outgoing->i_message - offsetof(NDhcp4Message, sname); - - if (rem >= n_data + 2U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - } - - return N_DHCP4_E_NO_SPACE; -} - -static int n_dhcp4_outgoing_append_u32(NDhcp4Outgoing *message, uint8_t option, uint32_t u32) { - uint32_t be32 = htonl(u32); - int r; - - r = n_dhcp4_outgoing_append(message, option, &be32, sizeof(be32)); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_outgoing_append_in_addr(NDhcp4Outgoing *message, uint8_t option, struct in_addr addr) { - int r; - - r = n_dhcp4_outgoing_append(message, option, &addr.s_addr, sizeof(addr.s_addr)); - if (r) - return r; - - return 0; -} - -int n_dhcp4_outgoing_append_t1(NDhcp4Outgoing *message, uint32_t t1) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_RENEWAL_T1_TIME, t1); -} - -int n_dhcp4_outgoing_append_t2(NDhcp4Outgoing *message, uint32_t t2) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_REBINDING_T2_TIME, t2); -} - -int n_dhcp4_outgoing_append_lifetime(NDhcp4Outgoing *message, uint32_t lifetime) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME, lifetime); -} - -int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr) { - return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, addr); -} - -int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr) { - return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, addr); -} - -void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - /* - * Some DHCP servers will reject DISCOVER or REQUEST messages if 'secs' - * is not set (i.e., set to 0), even though the spec allows it. - */ - c_assert(secs); - - header->secs = htons(secs); -} - -void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - header->xid = xid; -} - -void n_dhcp4_outgoing_get_xid(NDhcp4Outgoing *message, uint32_t *xidp) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - *xidp = header->xid; -} - -void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - header->yiaddr = yiaddr.s_addr; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h deleted file mode 100644 index db7b24ff..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-private.h +++ /dev/null @@ -1,759 +0,0 @@ -#pragma once - -#include <arpa/inet.h> -#include <assert.h> -#include <c-list.h> -#include <c-stdaux.h> -#include <endian.h> -#include <inttypes.h> -#include <limits.h> -#include <stdbool.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> -#include <syslog.h> -#include "n-dhcp4.h" - -typedef struct NDhcp4CConnection NDhcp4CConnection; -typedef struct NDhcp4CEventNode NDhcp4CEventNode; -typedef struct NDhcp4ClientProbeOption NDhcp4ClientProbeOption; -typedef struct NDhcp4Header NDhcp4Header; -typedef struct NDhcp4Incoming NDhcp4Incoming; -typedef struct NDhcp4Message NDhcp4Message; -typedef struct NDhcp4Outgoing NDhcp4Outgoing; -typedef struct NDhcp4SConnection NDhcp4SConnection; -typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp; -typedef struct NDhcp4SEventNode NDhcp4SEventNode; -typedef struct NDhcp4LogQueue NDhcp4LogQueue; - -/* specs */ - -#define N_DHCP4_NETWORK_IP_MAXIMUM_HEADER_SIZE (60) /* See RFC791 */ -#define N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE (576) /* See RFC791 */ -#define N_DHCP4_NETWORK_SERVER_PORT (67) -#define N_DHCP4_NETWORK_CLIENT_PORT (68) -#define N_DHCP4_MESSAGE_MAGIC ((uint32_t)(0x63825363)) -#define N_DHCP4_MESSAGE_FLAG_BROADCAST (htons(0x8000)) - -enum { - N_DHCP4_OP_BOOTREQUEST = 1, - N_DHCP4_OP_BOOTREPLY = 2, -}; - -enum { - N_DHCP4_OPTION_PAD = 0, - N_DHCP4_OPTION_SUBNET_MASK = 1, - N_DHCP4_OPTION_TIME_OFFSET = 2, - N_DHCP4_OPTION_ROUTER = 3, - N_DHCP4_OPTION_DOMAIN_NAME_SERVER = 6, - N_DHCP4_OPTION_HOST_NAME = 12, - N_DHCP4_OPTION_BOOT_FILE_SIZE = 13, - N_DHCP4_OPTION_DOMAIN_NAME = 15, - N_DHCP4_OPTION_ROOT_PATH = 17, - N_DHCP4_OPTION_ENABLE_IP_FORWARDING = 19, - N_DHCP4_OPTION_ENABLE_IP_FORWARDING_NL = 20, - N_DHCP4_OPTION_POLICY_FILTER = 21, - N_DHCP4_OPTION_INTERFACE_MDR = 22, - N_DHCP4_OPTION_INTERFACE_TTL = 23, - N_DHCP4_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - N_DHCP4_OPTION_INTERFACE_MTU = 26, - N_DHCP4_OPTION_BROADCAST = 28, - N_DHCP4_OPTION_STATIC_ROUTE = 33, - N_DHCP4_OPTION_NTP_SERVER = 42, - N_DHCP4_OPTION_VENDOR_SPECIFIC = 43, - N_DHCP4_OPTION_REQUESTED_IP_ADDRESS = 50, - N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME = 51, - N_DHCP4_OPTION_OVERLOAD = 52, - N_DHCP4_OPTION_MESSAGE_TYPE = 53, - N_DHCP4_OPTION_SERVER_IDENTIFIER = 54, - N_DHCP4_OPTION_PARAMETER_REQUEST_LIST = 55, - N_DHCP4_OPTION_ERROR_MESSAGE = 56, - N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - N_DHCP4_OPTION_RENEWAL_T1_TIME = 58, - N_DHCP4_OPTION_REBINDING_T2_TIME = 59, - N_DHCP4_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - N_DHCP4_OPTION_CLIENT_IDENTIFIER = 61, - N_DHCP4_OPTION_FQDN = 81, - N_DHCP4_OPTION_NEW_POSIX_TIMEZONE = 100, - N_DHCP4_OPTION_NEW_TZDB_TIMEZONE = 101, - N_DHCP4_OPTION_CLASSLESS_STATIC_ROUTE = 121, - N_DHCP4_OPTION_PRIVATE_BASE = 224, - N_DHCP4_OPTION_PRIVATE_LAST = 254, - N_DHCP4_OPTION_END = 255, - _N_DHCP4_OPTION_N = 256, -}; - -enum { - N_DHCP4_OVERLOAD_FILE = 1, - N_DHCP4_OVERLOAD_SNAME = 2, -}; - -enum { - N_DHCP4_MESSAGE_DISCOVER = 1, - N_DHCP4_MESSAGE_OFFER = 2, - N_DHCP4_MESSAGE_REQUEST = 3, - N_DHCP4_MESSAGE_DECLINE = 4, - N_DHCP4_MESSAGE_ACK = 5, - N_DHCP4_MESSAGE_NAK = 6, - N_DHCP4_MESSAGE_RELEASE = 7, - N_DHCP4_MESSAGE_INFORM = 8, - N_DHCP4_MESSAGE_FORCERENEW = 9, -}; - -struct NDhcp4Header { - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - uint32_t xid; - uint16_t secs; - uint16_t flags; - uint32_t ciaddr; - uint32_t yiaddr; - uint32_t siaddr; - uint32_t giaddr; - uint8_t chaddr[16]; -} _c_packed_; - -struct NDhcp4Message { - NDhcp4Header header; - uint8_t sname[64]; - uint8_t file[128]; - uint32_t magic; - uint8_t options[]; -} _c_packed_; - -/* objects */ - -enum { - _N_DHCP4_E_INTERNAL = _N_DHCP4_E_N, - - N_DHCP4_E_UNEXPECTED, - - N_DHCP4_E_NO_SPACE, - N_DHCP4_E_MALFORMED, - - N_DHCP4_E_DROPPED, - N_DHCP4_E_DOWN, - N_DHCP4_E_AGAIN, -}; - -enum { - N_DHCP4_C_CONNECTION_STATE_INIT, - N_DHCP4_C_CONNECTION_STATE_PACKET, - N_DHCP4_C_CONNECTION_STATE_DRAINING, - N_DHCP4_C_CONNECTION_STATE_UDP, - N_DHCP4_C_CONNECTION_STATE_CLOSED, -}; - -enum { - N_DHCP4_CLIENT_EPOLL_TIMER, - N_DHCP4_CLIENT_EPOLL_IO, -}; - -enum { - N_DHCP4_CLIENT_PROBE_STATE_INIT, - N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT, - N_DHCP4_CLIENT_PROBE_STATE_SELECTING, - N_DHCP4_CLIENT_PROBE_STATE_REBOOTING, - N_DHCP4_CLIENT_PROBE_STATE_REQUESTING, - N_DHCP4_CLIENT_PROBE_STATE_GRANTED, - N_DHCP4_CLIENT_PROBE_STATE_BOUND, - N_DHCP4_CLIENT_PROBE_STATE_RENEWING, - N_DHCP4_CLIENT_PROBE_STATE_REBINDING, - N_DHCP4_CLIENT_PROBE_STATE_EXPIRED, -}; - -enum { - N_DHCP4_CLIENT_LEASE_STATE_INIT, - N_DHCP4_CLIENT_LEASE_STATE_OFFERED, - N_DHCP4_CLIENT_LEASE_STATE_SELECTED, - N_DHCP4_CLIENT_LEASE_STATE_DECLINED, - N_DHCP4_CLIENT_LEASE_STATE_ACKED, -}; - -enum { - N_DHCP4_SERVER_EPOLL_TIMER, - N_DHCP4_SERVER_EPOLL_IO, -}; - -enum { - _N_DHCP4_C_MESSAGE_INVALID = 0, - N_DHCP4_C_MESSAGE_DISCOVER, - N_DHCP4_C_MESSAGE_INFORM, - N_DHCP4_C_MESSAGE_SELECT, - N_DHCP4_C_MESSAGE_IGNORE, - N_DHCP4_C_MESSAGE_RENEW, - N_DHCP4_C_MESSAGE_REBIND, - N_DHCP4_C_MESSAGE_REBOOT, - N_DHCP4_C_MESSAGE_RELEASE, - N_DHCP4_C_MESSAGE_DECLINE, -}; - -struct NDhcp4Outgoing { - NDhcp4Message *message; - size_t n_message; - size_t i_message; - size_t max_size; - - uint8_t overload : 2; - - struct { - uint8_t type; - uint8_t message_type; - uint32_t client_addr; - uint64_t start_time; - uint64_t base_time; - uint64_t send_time; - uint64_t send_jitter; - size_t n_send; - } userdata; -}; - -#define N_DHCP4_OUTGOING_NULL(_x) { \ - } - -struct NDhcp4Incoming { - struct { - uint8_t *value; - size_t size; - } options[_N_DHCP4_OPTION_N]; - - struct { - uint8_t type; - uint64_t start_time; - uint64_t base_time; - } userdata; - - size_t n_message; - NDhcp4Message message; - /* @message must be the last member */ -}; - -#define N_DHCP4_INCOMING_NULL(_x) { \ - } - -struct NDhcp4ClientConfig { - int ifindex; - unsigned int transport; - bool request_broadcast; - uint8_t mac[32]; /* MAX_ADDR_LEN */ - size_t n_mac; - uint8_t broadcast_mac[32]; /* MAX_ADDR_LEN */ - size_t n_broadcast_mac; - uint8_t *client_id; - size_t n_client_id; -}; - -#define N_DHCP4_CLIENT_CONFIG_NULL(_x) { \ - .transport = _N_DHCP4_TRANSPORT_N, \ - } - -struct NDhcp4ClientProbeOption { - uint8_t option; - uint8_t n_data; - uint8_t data[]; -}; - -#define N_DHCP4_CLIENT_PROBE_OPTION_NULL(_x) { \ - .option = N_DHCP4_OPTION_PAD, \ - } - -struct NDhcp4ClientProbeConfig { - bool inform_only; - bool init_reboot; - struct in_addr requested_ip; - unsigned short int entropy[3]; - uint64_t ms_start_delay; /* max ms to wait before starting probe */ - NDhcp4ClientProbeOption *options[UINT8_MAX + 1]; - int8_t request_parameters[UINT8_MAX + 1]; - size_t n_request_parameters; -}; - -#define N_DHCP4_CLIENT_PROBE_CONFIG_NULL(_x) { \ - .ms_start_delay = N_DHCP4_CLIENT_START_DELAY_RFC2131, \ - } - -struct NDhcp4CEventNode { - CList client_link; - CList probe_link; - NDhcp4ClientEvent event; - bool is_public : 1; -}; - -#define N_DHCP4_C_EVENT_NODE_NULL(_x) { \ - .client_link = C_LIST_INIT((_x).client_link), \ - .probe_link = C_LIST_INIT((_x).probe_link), \ - } - -struct NDhcp4LogQueue { - CList *event_list; - NDhcp4CEventNode nomem_node; - int log_level; - bool is_client : 1; -}; - -#define N_DHCP4_LOG_QUEUE_NULL_DEFUNCT() { \ - .log_level = -1, \ - .is_client = false, \ - } - -#define N_DHCP4_LOG_QUEUE_NULL_CLIENT(client) { \ - .event_list = &((client).event_list), \ - .log_level = -1, \ - .is_client = true, \ - .nomem_node = { \ - .client_link = C_LIST_INIT((client).log_queue.nomem_node.client_link), \ - .probe_link = C_LIST_INIT((client).log_queue.nomem_node.probe_link), \ - .event = { \ - .event = N_DHCP4_CLIENT_EVENT_LOG, \ - .log = { \ - .level = LOG_CRIT, \ - .message = "one or more logging messages dropped due to out of memory", \ - .allow_steal_message = false, \ - }, \ - }, \ - .is_public = false, \ - }, \ - } - -struct NDhcp4CConnection { - NDhcp4ClientConfig *client_config; - NDhcp4ClientProbeConfig *probe_config; - NDhcp4LogQueue *log_queue; - - int fd_epoll; - - unsigned int state; /* current connection state */ - int fd_packet; /* packet socket */ - int fd_udp; /* udp socket */ - - NDhcp4Outgoing *request; /* current request */ - - uint32_t client_ip; /* client IP address, or 0 */ - uint32_t server_ip; /* server IP address, or 0 */ - uint16_t mtu; /* client mtu, or 0 */ - - /* - * When we get DHCP packets from the kernel, we need a buffer to read - * the data into. Since UDP packets can be up to 2^16 bytes in size, we - * avoid placing it on the stack and instead read into this scratch - * buffer. It is purely meant as stack replacement, no data is returned - * through this buffer. - */ - uint8_t scratch_buffer[UINT16_MAX]; -}; - -#define N_DHCP4_C_CONNECTION_NULL(_x) { \ - .fd_packet = -1, \ - .fd_udp = -1, \ - } - -struct NDhcp4Client { - unsigned long n_refs; - NDhcp4ClientConfig *config; - CList event_list; - - NDhcp4LogQueue log_queue; - - int fd_epoll; - int fd_timer; - - uint16_t mtu; - NDhcp4ClientProbe *current_probe; - uint64_t scheduled_timeout; - - bool preempted : 1; -}; - -#define N_DHCP4_CLIENT_NULL(_x) { \ - .n_refs = 1, \ - .event_list = C_LIST_INIT((_x).event_list), \ - .fd_epoll = -1, \ - .fd_timer = -1, \ - .log_queue = N_DHCP4_LOG_QUEUE_NULL_CLIENT(_x), \ - } - -struct NDhcp4ClientProbe { - NDhcp4ClientProbeConfig *config; - NDhcp4Client *client; - CList event_list; - CList lease_list; - void *userdata; - - unsigned int state; /* current probe state */ - struct in_addr last_address; /* last address obtained */ - uint64_t ns_deferred; /* timeout for deferred action */ - uint64_t ns_reinit; - uint64_t ns_nak_restart_delay; /* restart delay after a nak */ - NDhcp4ClientLease *current_lease; /* current lease */ - - NDhcp4CConnection connection; /* client connection wrapper */ -}; - -#define N_DHCP4_CLIENT_PROBE_NULL(_x) { \ - .event_list = C_LIST_INIT((_x).event_list), \ - .lease_list = C_LIST_INIT((_x).lease_list), \ - .connection = N_DHCP4_C_CONNECTION_NULL((_x).connection), \ - } - -struct NDhcp4ClientLease { - unsigned long n_refs; - - NDhcp4ClientProbe *probe; - CList probe_link; - - NDhcp4Incoming *message; - - uint64_t t1; - uint64_t t2; - uint64_t lifetime; -}; - -#define N_DHCP4_CLIENT_LEASE_NULL(_x) { \ - .n_refs = 1, \ - .probe_link = C_LIST_INIT((_x).probe_link), \ - } - -struct NDhcp4ServerConfig { - int ifindex; -}; - -#define N_DHCP4_SERVER_CONFIG_NULL(_x) { \ - } - -struct NDhcp4SEventNode { - CList server_link; - NDhcp4ServerEvent event; - bool is_public : 1; -}; - -#define N_DHCP4_S_EVENT_NODE_NULL(_x) { \ - .server_link = C_LIST_INIT((_x).server_link), \ - } - -struct NDhcp4SConnection { - int ifindex; /* interface index */ - int fd_packet; /* packet socket */ - int fd_udp; /* udp socket */ - uint8_t buf[UINT16_MAX]; /* scratch recevie buffer */ - - /* XXX: support a set of server addresses */ - NDhcp4SConnectionIp *ip; /* server IP address, or NULL */ -}; - -#define N_DHCP4_S_CONNECTION_NULL(_x) { \ - .fd_packet = -1, \ - .fd_udp = -1, \ - } - -struct NDhcp4SConnectionIp { - NDhcp4SConnection *connection; - struct in_addr ip; -}; - -#define N_DHCP4_S_CONNECTION_IP_NULL(_x) { \ -} - -struct NDhcp4Server { - unsigned long n_refs; - CList event_list; - CList lease_list; - - bool preempted : 1; - - NDhcp4SConnection connection; -}; - -#define N_DHCP4_SERVER_NULL(_x) { \ - .n_refs = 1, \ - .event_list = C_LIST_INIT((_x).event_list), \ - .lease_list = C_LIST_INIT((_x).lease_list), \ - .connection = N_DHCP4_S_CONNECTION_NULL((_x).connection), \ - } - -struct NDhcp4ServerIp { - NDhcp4SConnectionIp ip; -}; - -#define N_DHCP4_SERVER_IP_NULL(_x) { \ - .ip = N_DHCP4_S_CONNECTION_IP_NULL((_x).ip), \ - } - -struct NDhcp4ServerLease { - unsigned long n_refs; - - NDhcp4Server *server; - CList server_link; - - NDhcp4Incoming *request; - NDhcp4Incoming *reply; -}; - -#define N_DHCP4_SERVER_LEASE_NULL(_x) { \ - .n_refs = 1, \ - .server_link = C_LIST_INIT((_x).server_link), \ - } - -/* outgoing messages */ - -int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload); -NDhcp4Outgoing *n_dhcp4_outgoing_free(NDhcp4Outgoing *outgoing); - -NDhcp4Header *n_dhcp4_outgoing_get_header(NDhcp4Outgoing *outgoing); -size_t n_dhcp4_outgoing_get_raw(NDhcp4Outgoing *outgoing, const void **rawp); -int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing, uint8_t option, const void *data, uint8_t n_data); - -int n_dhcp4_outgoing_append_t1(NDhcp4Outgoing *message, uint32_t t1); -int n_dhcp4_outgoing_append_t2(NDhcp4Outgoing *message, uint32_t t2); -int n_dhcp4_outgoing_append_lifetime(NDhcp4Outgoing *message, uint32_t lifetime); -int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr); -int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr); - -void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs); -void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid); -void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr); - -void n_dhcp4_outgoing_get_xid(NDhcp4Outgoing *message, uint32_t *xidp); - -/* incoming messages */ - -int n_dhcp4_incoming_new(NDhcp4Incoming **incomingp, const void *raw, size_t n_raw); -NDhcp4Incoming *n_dhcp4_incoming_free(NDhcp4Incoming *incoming); - -NDhcp4Header *n_dhcp4_incoming_get_header(NDhcp4Incoming *incoming); -size_t n_dhcp4_incoming_get_raw(NDhcp4Incoming *incoming, const void **rawp); -int n_dhcp4_incoming_query(NDhcp4Incoming *incoming, uint8_t option, uint8_t **datap, size_t *n_datap); - -int n_dhcp4_incoming_query_message_type(NDhcp4Incoming *message, uint8_t *typep); -int n_dhcp4_incoming_query_lifetime(NDhcp4Incoming *message, uint32_t *lifetimep); -int n_dhcp4_incoming_query_t2(NDhcp4Incoming *message, uint32_t *t2p); -int n_dhcp4_incoming_query_t1(NDhcp4Incoming *message, uint32_t *t1p); -int n_dhcp4_incoming_query_server_identifier(NDhcp4Incoming *message, struct in_addr *idp); -int n_dhcp4_incoming_query_max_message_size(NDhcp4Incoming *message, uint16_t *max_message_sizep); -int n_dhcp4_incoming_query_requested_ip(NDhcp4Incoming *message, struct in_addr *requested_ipp); - -void n_dhcp4_incoming_get_xid(NDhcp4Incoming *message, uint32_t *xidp); -void n_dhcp4_incoming_get_yiaddr(NDhcp4Incoming *message, struct in_addr *yiaddr); - -/* sockets */ - -int n_dhcp4_c_socket_packet_new(int *sockfdp, int ifindex); -int n_dhcp4_c_socket_udp_new(int *sockfdp, - int ifindex, - const struct in_addr *client_addr, - const struct in_addr *server_addr); -int n_dhcp4_s_socket_packet_new(int *sockfdp); -int n_dhcp4_s_socket_udp_new(int *sockfdp, int ifindex); - -int n_dhcp4_c_socket_packet_send(int sockfd, - int ifindex, - const unsigned char *dest_haddr, - unsigned char halen, - NDhcp4Outgoing *message); -int n_dhcp4_c_socket_udp_send(int sockfd, NDhcp4Outgoing *message); -int n_dhcp4_c_socket_udp_broadcast(int sockfd, NDhcp4Outgoing *message); -int n_dhcp4_s_socket_packet_send(int sockfd, - int ifindex, - const struct in_addr *src_inaddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct in_addr *dest_inaddr, - NDhcp4Outgoing *message); -int n_dhcp4_s_socket_udp_send(int sockfd, - const struct in_addr *inaddr_src, - const struct in_addr *inaddr_dest, - NDhcp4Outgoing *message); -int n_dhcp4_s_socket_udp_broadcast(int sockfd, - const struct in_addr *inaddr_src, - NDhcp4Outgoing *message); - -int n_dhcp4_c_socket_packet_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep); -int n_dhcp4_c_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep); -int n_dhcp4_s_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct sockaddr_in *dest); - -/* client configs */ - -int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, - NDhcp4ClientConfig **dupp); - -/* client probe configs */ - -int n_dhcp4_client_probe_config_dup(NDhcp4ClientProbeConfig *config, - NDhcp4ClientProbeConfig **dupp); -uint32_t n_dhcp4_client_probe_config_get_random(NDhcp4ClientProbeConfig *config); - -/* client events */ - -int n_dhcp4_c_event_node_new(NDhcp4CEventNode **nodep); -NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node); - -/* client connections */ - -int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, - NDhcp4ClientConfig *client_config, - NDhcp4ClientProbeConfig *probe_config, - NDhcp4LogQueue *log_queue, - int fd_epoll); -void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection); - -int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection); -int n_dhcp4_c_connection_connect(NDhcp4CConnection *connection, - const struct in_addr *client, - const struct in_addr *server); -void n_dhcp4_c_connection_close(NDhcp4CConnection *connection); - -void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection, - uint64_t *timeoutp); - -int n_dhcp4_c_connection_discover_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - NDhcp4Incoming *offer); -int n_dhcp4_c_connection_reboot_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - const struct in_addr *client); -int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - NDhcp4Incoming *ack, - const char *error); -int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - const char *error); - -int n_dhcp4_c_connection_start_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp); -int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection, - uint64_t timestamp); -int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, - NDhcp4Incoming **messagep); - -/* clients */ - -int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigned int event); -void n_dhcp4_client_arm_timer(NDhcp4Client *client); - -/* client probes */ - -int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config, - NDhcp4Client *client, - uint64_t ns_now); - -int n_dhcp4_client_probe_raise(NDhcp4ClientProbe *probe, NDhcp4CEventNode **nodep, unsigned int event); -void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeoutp); -int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_now); -int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events); -int n_dhcp4_client_probe_transition_select(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, uint64_t ns_now); -int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incoming *ack); -int n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, const char *error, uint64_t ns_now); -int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu); - -/* client leases */ - -int n_dhcp4_client_lease_new(NDhcp4ClientLease **leasep, NDhcp4Incoming *message); -void n_dhcp4_client_lease_link(NDhcp4ClientLease *lease, NDhcp4ClientProbe *probe); -void n_dhcp4_client_lease_unlink(NDhcp4ClientLease *lease); - -/* server connections */ - -int n_dhcp4_s_connection_init(NDhcp4SConnection *connection, int ifindex); -void n_dhcp4_s_connection_deinit(NDhcp4SConnection *connection); - -void n_dhcp4_s_connection_get_fd(NDhcp4SConnection *connection, int *fdp); -int n_dhcp4_s_connection_dispatch_io(NDhcp4SConnection *connection, NDhcp4Incoming **messagep); - -int n_dhcp4_s_connection_offer_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address, - const struct in_addr *client_address, - uint32_t lifetime); -int n_dhcp4_s_connection_ack_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address, - const struct in_addr *client_address, - uint32_t lifetime); -int n_dhcp4_s_connection_nak_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address); - -int n_dhcp4_s_connection_send_reply(NDhcp4SConnection *connection, - const struct in_addr *server_addr, - NDhcp4Outgoing *reply); - -/* server connection ips */ - -void n_dhcp4_s_connection_ip_init(NDhcp4SConnectionIp *ip, struct in_addr addr); -void n_dhcp4_s_connection_ip_deinit(NDhcp4SConnectionIp *ip); - -void n_dhcp4_s_connection_ip_link(NDhcp4SConnectionIp *ip, NDhcp4SConnection *connection); -void n_dhcp4_s_connection_ip_unlink(NDhcp4SConnectionIp *ip); - -/* inline helpers */ - -static inline void n_dhcp4_outgoing_freep(NDhcp4Outgoing **outgoing) { - if (*outgoing) - n_dhcp4_outgoing_free(*outgoing); -} - -static inline void n_dhcp4_incoming_freep(NDhcp4Incoming **incoming) { - if (*incoming) - n_dhcp4_incoming_free(*incoming); -} - -static inline uint64_t n_dhcp4_gettime(clockid_t clock) { - struct timespec ts; - int r; - - r = clock_gettime(clock, &ts); - c_assert(r >= 0); - - return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec; -} - -void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, - int level, - const char *fmt, - ...) _c_printf_(3, 4); - -/** - * n_dhcp4_log() - append a logging event - * @x_log_queue: the logging event queue - * @x_level: the syslog logging level for the message. - * @...: the format string and arguments. - * - * Warning: this macro only evaluates the format arguments if the logging - * level is enabled. - */ -#define n_dhcp4_log(x_log_queue, x_level, ...) \ - do { \ - NDhcp4LogQueue *const _log_queue = (x_log_queue); \ - const int _level = (x_level); \ - \ - if (_level <= _log_queue->log_level) { \ - n_dhcp4_log_queue_fmt(_log_queue, \ - _level, \ - __VA_ARGS__); \ - } \ - } while (0) diff --git a/shared/n-dhcp4/src/n-dhcp4-socket.c b/shared/n-dhcp4/src/n-dhcp4-socket.c deleted file mode 100644 index 3e703b4f..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-socket.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * DHCP specific low-level socket helpers - */ - -#include <c-stdaux.h> -#include <errno.h> -#include <linux/filter.h> -#include <sys/socket.h> /* needed by linux/if.h */ -#include <linux/if.h> -#include <linux/if_packet.h> -#include <linux/netdevice.h> -#include <linux/udp.h> -#include <netinet/ip.h> -#include <stddef.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <sys/types.h> -#include "n-dhcp4-private.h" -#include "util/packet.h" -#include "util/socket.h" - -/** - * n_dhcp4_c_socket_packet_new() - create a new DHCP4 client packet socket - * @sockfdp: return argument for the new socket - * @ifindex: interface index to bind to - * - * Create a new AF_PACKET/SOCK_DGRAM socket usable to listen to and send DHCP client - * packets before an IP address has been configured. - * - * Only unfragmented DHCP packets from a server to a client destined for the given - * ifindex is returned. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_c_socket_packet_new(int *sockfdp, int ifindex) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP - * - * Check - * - UDP - * - Unfragmented - * - Large enough to fit the DHCP header - * - * Leave X the size of the IP header, for future indirect reads. - */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct iphdr, protocol)), /* A <- IP protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct iphdr, frag_off)), /* A <- Flags + Fragment offset */ - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, IP_MF | IP_OFFMASK), /* A <- A & (IP_MF | IP_OFFMASK) */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* fragmented packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_STMT(BPF_ALU + BPF_SUB + BPF_X, 0), /* A -= X */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct udphdr) + sizeof(NDhcp4Message), 1, 0), /* packet >= DHCPPacket ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - /* - * UDP - * - * Check - * - DHCP client port - * - * Leave X the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, dest)), /* A <- UDP destination port */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_NETWORK_CLIENT_PORT, 1, 0), /* UDP destination port == DHCP client port ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREPLY (from server to client) - * - DHCP magic cookie - */ - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_ll addr = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IP), - .sll_ifindex = ifindex, - }; - int r, on = 1; - - sockfd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - /* We need the flag that tells us if the checksum is correct. */ - r = setsockopt(sockfd, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_c_socket_udp_new() - create a new DHCP4 client UDP socket - * @sockfdp: return argument for the new socket - * @ifindex: interface index to bind to - * @client_addr: client address to bind to - * @server_addr: server address to connect to - * - * Create a new AF_INET/SOCK_DGRAM socket usable to listen to and send DHCP client - * packets. - * - * The client address given in @addr must be configured on the interface @ifindex - * before the socket is created. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_c_socket_udp_new(int *sockfdp, - int ifindex, - const struct in_addr *client_addr, - const struct in_addr *server_addr) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP/UDP - * - * Set X to the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREPLY (from server to client) - * - DHCP magic cookie - */ - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_in saddr = { - .sin_family = AF_INET, - .sin_addr = *client_addr, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - }; - struct sockaddr_in daddr = { - .sin_family = AF_INET, - .sin_addr = *server_addr, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - }; - int r, tos = IPTOS_CLASS_CS6, on = 1; - - sockfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = socket_bind_if(sockfd, ifindex); - if (r) - return r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); - if (r < 0) - return -errno; - - r = connect(sockfd, (struct sockaddr*)&daddr, sizeof(daddr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_s_socket_packet_new() - create a new DHCP4 server packet socket - * @sockfdp: return argument for the new socket - * - * Create a new AF_PACKET/SOCK_DGRAM socket usable to send DHCP packets to clients - * before they have an IP address configured, on the given interface. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_s_socket_packet_new(int *sockfdp) { - _c_cleanup_(c_closep) int sockfd = -1; - - sockfd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_s_socket_udp_new() - create a new DHCP4 server UDP socket - * @sockfdp: return argument for the new socket - * @ifindex: intercafe index to bind to - * - * Create a new AF_INET/SOCK_DGRAM socket usable to listen to DHCP server packets, - * on the given interface. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_s_socket_udp_new(int *sockfdp, int ifindex) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP/UDP - * - * Set X to the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREQUEST (from client to server) - * - DHCP magic cookie - */ - - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREQUEST, 1, 0), /* op == BOOTREQUEST ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr = { INADDR_ANY }, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - }; - int r, tos = IPTOS_CLASS_CS6, on = 1; - - sockfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = socket_bind_if(sockfd, ifindex); - if (r) - return r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -static int n_dhcp4_socket_packet_send(int sockfd, - int ifindex, - const struct sockaddr_in *src_paddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct sockaddr_in *dest_paddr, - NDhcp4Outgoing *message) { - struct packet_sockaddr_ll haddr = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IP), - .sll_ifindex = ifindex, - .sll_halen = halen, - }; - const void *buf; - size_t n_buf, len; - int r; - - c_assert(halen <= sizeof(haddr.sll_addr)); - - memcpy(haddr.sll_addr, dest_haddr, halen); - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - r = packet_sendto_udp(sockfd, buf, n_buf, &len, src_paddr, &haddr, dest_paddr); - if (r < 0) { - if (r == -EAGAIN || r == -ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (r == -ENETDOWN || r == -ENXIO) - return N_DHCP4_E_DOWN; - else - return r; - } else if (len != n_buf) { - return N_DHCP4_E_DROPPED; - } - - return 0; -} - -/** - * n_dhcp4_c_socket_packet_send() - XXX - */ -int n_dhcp4_c_socket_packet_send(int sockfd, - int ifindex, - const unsigned char *dest_haddr, - unsigned char halen, - NDhcp4Outgoing *message) { - struct sockaddr_in src_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = { INADDR_ANY }, - }; - struct sockaddr_in dest_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = { INADDR_BROADCAST } - }; - - return n_dhcp4_socket_packet_send(sockfd, - ifindex, - &src_paddr, - dest_haddr, - halen, - &dest_paddr, - message); -} - -/** - * n_dhcp4_c_socket_udp_send() - XXX - */ -int n_dhcp4_c_socket_udp_send(int sockfd, - NDhcp4Outgoing *message) { - const void *buf; - size_t n_buf; - ssize_t len; - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - len = send(sockfd, buf, n_buf, 0); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != n_buf) - return N_DHCP4_E_DROPPED; - - return 0; -} - -/** - * n_dhcp4_c_socket_udp_broadcast() - XXX - */ -int n_dhcp4_c_socket_udp_broadcast(int sockfd, NDhcp4Outgoing *message) { - struct sockaddr_in sockaddr_dest = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = { INADDR_BROADCAST }, - }; - const void *buf; - size_t n_buf; - ssize_t len; - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - len = sendto(sockfd, - buf, - n_buf, - 0, - (struct sockaddr*)&sockaddr_dest, - sizeof(sockaddr_dest)); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != n_buf) - return N_DHCP4_E_DROPPED; - - return 0; -} - -/** - * n_dhcp4_s_socket_packet_send() - XXX - */ -int n_dhcp4_s_socket_packet_send(int sockfd, - int ifindex, - const struct in_addr *src_inaddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct in_addr *dest_inaddr, - NDhcp4Outgoing *message) { - struct sockaddr_in src_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = *src_inaddr, - }; - struct sockaddr_in dest_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = *dest_inaddr, - }; - - return n_dhcp4_socket_packet_send(sockfd, - ifindex, - &src_paddr, - dest_haddr, - halen, - &dest_paddr, - message); -} - -/** - * n_dhcp4_s_socket_udp_send() - XXX - */ -int n_dhcp4_s_socket_udp_send(int sockfd, - const struct in_addr *inaddr_src, - const struct in_addr *inaddr_dest, - NDhcp4Outgoing *message) { - struct sockaddr_in sockaddr_dest = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = *inaddr_dest, - }; - struct iovec iov = {}; - union { - struct cmsghdr align; /* ensure correct stack alignment */ - char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; - } control = {}; - struct in_pktinfo pktinfo = { - .ipi_spec_dst = *inaddr_src, - }; - struct msghdr msg = { - .msg_name = (void*)&sockaddr_dest, - .msg_namelen = sizeof(sockaddr_dest), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control.buf, - .msg_controllen = sizeof(control.buf), - }; - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - ssize_t len; - - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo)); - - iov.iov_len = n_dhcp4_outgoing_get_raw(message, (const void **)&iov.iov_base); - - len = sendmsg(sockfd, &msg, 0); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != iov.iov_len) - return N_DHCP4_E_DROPPED; - - return 0; -} - -int n_dhcp4_s_socket_udp_broadcast(int sockfd, - const struct in_addr *inaddr_src, - NDhcp4Outgoing *message) { - return n_dhcp4_s_socket_udp_send(sockfd, - inaddr_src, - &(const struct in_addr){INADDR_BROADCAST}, - message); -} - -int n_dhcp4_c_socket_packet_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - size_t len; - int r; - - r = packet_recv_udp(sockfd, buf, n_buf, &len); - if (r < 0) { - if (r == -ENETDOWN) - return N_DHCP4_E_DOWN; - else if (r == -EAGAIN) - return N_DHCP4_E_AGAIN; - else - return -errno; - } else if (len == 0) { - return N_DHCP4_E_MALFORMED; - } - - r = n_dhcp4_incoming_new(&message, buf, len); - if (r) - return r; - - *messagep = message; - message = NULL; - return 0; -} - -static int n_dhcp4_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct in_pktinfo *pktinfo) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - struct iovec iov = { - .iov_base = buf, - .iov_len = n_buf, - }; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))]; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - ssize_t len; - int r; - - len = recvmsg(sockfd, &msg, MSG_TRUNC); - if (len < 0) { - if (errno == ENETDOWN) - return N_DHCP4_E_DOWN; - else if (errno == EAGAIN) - return N_DHCP4_E_AGAIN; - else - return -errno; - } else if (len == 0 || (size_t)len > n_buf) { - return N_DHCP4_E_MALFORMED; - } - - r = n_dhcp4_incoming_new(&message, buf, len); - if (r) - return r; - - if (pktinfo) { - struct cmsghdr *cmsg; - - cmsg = CMSG_FIRSTHDR(&msg); - c_assert(cmsg); - c_assert(cmsg->cmsg_level == IPPROTO_IP); - c_assert(cmsg->cmsg_type == IP_PKTINFO); - c_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))); - - memcpy(pktinfo, (void*)CMSG_DATA(cmsg), sizeof(struct in_pktinfo)); - } - - *messagep = message; - message = NULL; - return 0; -} - -int n_dhcp4_c_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep) { - return n_dhcp4_socket_udp_recv(sockfd, buf, n_buf, messagep, NULL); -} - -int n_dhcp4_s_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct sockaddr_in *dest) { - struct in_pktinfo pktinfo = {}; - int r; - - r = n_dhcp4_socket_udp_recv(sockfd, buf, n_buf, messagep, &pktinfo); - if (r) - return r; - - if (dest) { - dest->sin_family = AF_INET; - dest->sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT); - dest->sin_addr.s_addr = pktinfo.ipi_addr.s_addr; - } - - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h deleted file mode 100644 index 435c5600..00000000 --- a/shared/n-dhcp4/src/n-dhcp4.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/* - * Dynamic Host Configuration Protocol for IPv4 - * - * This is the public header of the n-dhcp4 library, implementing IPv4 Dynamic - * Host Configuration Protocol as described in RFC-2132. This header defines - * the public API and all entry points of n-dhcp4. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <inttypes.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stdlib.h> - -typedef struct NDhcp4Client NDhcp4Client; -typedef struct NDhcp4ClientConfig NDhcp4ClientConfig; -typedef struct NDhcp4ClientEvent NDhcp4ClientEvent; -typedef struct NDhcp4ClientLease NDhcp4ClientLease; -typedef struct NDhcp4ClientProbe NDhcp4ClientProbe; -typedef struct NDhcp4ClientProbeConfig NDhcp4ClientProbeConfig; -typedef struct NDhcp4Server NDhcp4Server; -typedef struct NDhcp4ServerConfig NDhcp4ServerConfig; -typedef struct NDhcp4ServerEvent NDhcp4ServerEvent; -typedef struct NDhcp4ServerIp NDhcp4ServerIp; -typedef struct NDhcp4ServerLease NDhcp4ServerLease; - -#define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000)) - -enum { - _N_DHCP4_E_SUCCESS, - - N_DHCP4_E_PREEMPTED, - N_DHCP4_E_INTERNAL, - - N_DHCP4_E_INVALID_IFINDEX, - N_DHCP4_E_INVALID_TRANSPORT, - N_DHCP4_E_INVALID_ADDRESS, - N_DHCP4_E_INVALID_CLIENT_ID, - N_DHCP4_E_DUPLICATE_OPTION, - N_DHCP4_E_UNSET, - - _N_DHCP4_E_N, -}; - -enum { - N_DHCP4_TRANSPORT_ETHERNET, - N_DHCP4_TRANSPORT_INFINIBAND, - _N_DHCP4_TRANSPORT_N, -}; - -enum { - N_DHCP4_CLIENT_EVENT_DOWN, - N_DHCP4_CLIENT_EVENT_OFFER, - N_DHCP4_CLIENT_EVENT_GRANTED, - N_DHCP4_CLIENT_EVENT_RETRACTED, - N_DHCP4_CLIENT_EVENT_EXTENDED, - N_DHCP4_CLIENT_EVENT_EXPIRED, - N_DHCP4_CLIENT_EVENT_CANCELLED, - N_DHCP4_CLIENT_EVENT_LOG, - _N_DHCP4_CLIENT_EVENT_N, -}; - -enum { - N_DHCP4_SERVER_EVENT_DOWN, - N_DHCP4_SERVER_EVENT_DISCOVER, - N_DHCP4_SERVER_EVENT_REQUEST, - N_DHCP4_SERVER_EVENT_RENEW, - N_DHCP4_SERVER_EVENT_DECLINE, - N_DHCP4_SERVER_EVENT_RELEASE, - _N_DHCP4_SERVER_EVENT_N, -}; - -struct NDhcp4ClientEvent { - unsigned int event; - union { - struct { - } down; - struct { - NDhcp4ClientProbe *probe; - NDhcp4ClientLease *lease; - } offer, granted, extended; - struct { - NDhcp4ClientProbe *probe; - } retracted, expired, cancelled; - struct { - /* If allow_steal_message is true, then the user may steal the message when handling - * the event. In that case, set the message field to %NULL and free it yourself - * with free(). */ - const char *message; - int level; - bool allow_steal_message; - } log; - }; -}; - -struct NDhcp4ServerEvent { - unsigned int event; - union { - struct { - } down; - struct { - NDhcp4ServerLease *lease; - } discover, request, decline, release; - }; -}; - -/* client configs */ - -int n_dhcp4_client_config_new(NDhcp4ClientConfig **configp); -NDhcp4ClientConfig *n_dhcp4_client_config_free(NDhcp4ClientConfig *config); - -void n_dhcp4_client_config_set_ifindex(NDhcp4ClientConfig *config, int ifindex); -void n_dhcp4_client_config_set_transport(NDhcp4ClientConfig *config, unsigned int transport); -void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, bool request_broadcast); -void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); -void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); -int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id); - -/* client-probe configs */ - -int n_dhcp4_client_probe_config_new(NDhcp4ClientProbeConfig **configp); -NDhcp4ClientProbeConfig *n_dhcp4_client_probe_config_free(NDhcp4ClientProbeConfig *config); - -void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeConfig *config, bool inform_only); -void n_dhcp4_client_probe_config_set_init_reboot(NDhcp4ClientProbeConfig *config, bool init_reboot); -void n_dhcp4_client_probe_config_set_requested_ip(NDhcp4ClientProbeConfig *config, struct in_addr ip); -void n_dhcp4_client_probe_config_set_start_delay(NDhcp4ClientProbeConfig *config, uint64_t msecs); -void n_dhcp4_client_probe_config_request_option(NDhcp4ClientProbeConfig *config, uint8_t option); -int n_dhcp4_client_probe_config_append_option(NDhcp4ClientProbeConfig *config, - uint8_t option, - const void *data, - uint8_t n_data); - -/* clients */ - -int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *config); -NDhcp4Client *n_dhcp4_client_ref(NDhcp4Client *client); -NDhcp4Client *n_dhcp4_client_unref(NDhcp4Client *client); - -void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp); -int n_dhcp4_client_dispatch(NDhcp4Client *client); -int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp); - -void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level); - -int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu); - -int n_dhcp4_client_probe(NDhcp4Client *client, - NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config); - -/* client probes */ - -NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe); - -void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata); -void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap); - -/* client leases */ - -NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease); -NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease); - -void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr); -void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr); -void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep); -void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); -int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr); - -int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); -int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease); -int n_dhcp4_client_lease_decline(NDhcp4ClientLease *lease, const char *error); - -/* server configs */ - -int n_dhcp4_server_config_new(NDhcp4ServerConfig **configp); -NDhcp4ServerConfig *n_dhcp4_server_config_free(NDhcp4ServerConfig *config); - -void n_dhcp4_server_config_set_ifindex(NDhcp4ServerConfig *config, int ifindex); - -/* servers */ - -int n_dhcp4_server_new(NDhcp4Server **serverp, NDhcp4ServerConfig *config); -NDhcp4Server *n_dhcp4_server_ref(NDhcp4Server *server); -NDhcp4Server *n_dhcp4_server_unref(NDhcp4Server *server); - -void n_dhcp4_server_get_fd(NDhcp4Server *server, int *fdp); -int n_dhcp4_server_dispatch(NDhcp4Server *server); -int n_dhcp4_server_pop_event(NDhcp4Server *server, NDhcp4ServerEvent **eventp); - -int n_dhcp4_server_add_ip(NDhcp4Server *server, NDhcp4ServerIp **ipp, struct in_addr ip); - -/* server ip addresses */ - -NDhcp4ServerIp *n_dhcp4_server_ip_free(NDhcp4ServerIp *ip); - -/* server leases */ - -NDhcp4ServerLease *n_dhcp4_server_lease_ref(NDhcp4ServerLease *lease); -NDhcp4ServerLease *n_dhcp4_server_lease_unref(NDhcp4ServerLease *lease); - -int n_dhcp4_server_lease_query(NDhcp4ServerLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_server_lease_append(NDhcp4ServerLease *lease, uint8_t option, uint8_t *data, size_t n_data); - -int n_dhcp4_server_lease_offer(NDhcp4ServerLease *lease); -int n_dhcp4_server_lease_ack(NDhcp4ServerLease *lease); -int n_dhcp4_server_lease_nack(NDhcp4ServerLease *lease); - -/* inline helpers */ - -static inline void n_dhcp4_client_config_freep(NDhcp4ClientConfig **p) { - if (*p) - n_dhcp4_client_config_free(*p); -} - -static inline void n_dhcp4_client_config_freev(NDhcp4ClientConfig *p) { - n_dhcp4_client_config_free(p); -} - -static inline void n_dhcp4_client_probe_config_freep(NDhcp4ClientProbeConfig **p) { - if (*p) - n_dhcp4_client_probe_config_free(*p); -} - -static inline void n_dhcp4_client_probe_config_freev(NDhcp4ClientProbeConfig *p) { - n_dhcp4_client_probe_config_free(p); -} - -static inline void n_dhcp4_client_unrefp(NDhcp4Client **p) { - if (*p) - n_dhcp4_client_unref(*p); -} - -static inline void n_dhcp4_client_unrefv(NDhcp4Client *p) { - n_dhcp4_client_unref(p); -} - -static inline void n_dhcp4_client_probe_freep(NDhcp4ClientProbe **p) { - if (*p) - n_dhcp4_client_probe_free(*p); -} - -static inline void n_dhcp4_client_probe_freev(NDhcp4ClientProbe *p) { - n_dhcp4_client_probe_free(p); -} - -static inline void n_dhcp4_client_lease_unrefp(NDhcp4ClientLease **p) { - if (*p) - n_dhcp4_client_lease_unref(*p); -} - -static inline void n_dhcp4_client_lease_unrefv(NDhcp4ClientLease *p) { - n_dhcp4_client_lease_unref(p); -} - -static inline void n_dhcp4_server_config_freep(NDhcp4ServerConfig **p) { - if (*p) - n_dhcp4_server_config_free(*p); -} - -static inline void n_dhcp4_server_config_freev(NDhcp4ServerConfig *p) { - n_dhcp4_server_config_free(p); -} - -static inline void n_dhcp4_server_unrefp(NDhcp4Server **p) { - if (*p) - n_dhcp4_server_unref(*p); -} - -static inline void n_dhcp4_server_unrefv(NDhcp4Server *p) { - n_dhcp4_server_unref(p); -} - -static inline void n_dhcp4_server_ip_freep(NDhcp4ServerIp **p) { - if (*p) - n_dhcp4_server_ip_free(*p); -} - -static inline void n_dhcp4_server_ip_freev(NDhcp4ServerIp *p) { - n_dhcp4_server_ip_free(p); -} - -static inline void n_dhcp4_server_lease_unrefp(NDhcp4ServerLease **p) { - if (*p) - n_dhcp4_server_lease_unref(*p); -} - -static inline void n_dhcp4_server_lease_unrefv(NDhcp4ServerLease *p) { - n_dhcp4_server_lease_unref(p); -} - -#ifdef __cplusplus -} -#endif diff --git a/shared/n-dhcp4/src/util/packet.c b/shared/n-dhcp4/src/util/packet.c deleted file mode 100644 index 48f2c85e..00000000 --- a/shared/n-dhcp4/src/util/packet.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Packet Sockets - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <linux/filter.h> -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <linux/udp.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include "packet.h" - -/** - * packet_internet_checksum() - compute the internet checksum - * @data: the data to checksum - * @size: the length of @data in bytes - * - * Computes the internet checksum for a given blob according to RFC1071. - * - * The internet checksum is the one's complement of the one's complement sum of - * the 16-bit words of the data, padded with zero-bytes if the data does not - * end on a 16-bit boundary. - * - * Return: Checksum is returned. - */ -uint16_t packet_internet_checksum(const uint8_t *data, size_t size) { - uint64_t acc = 0; - uint32_t local; - - while (size >= sizeof(local)) { - memcpy(&local, data, sizeof(local)); - acc += local; - - data += sizeof(local); - size -= sizeof(local); - } - - if (size) { - local = 0; - memcpy(&local, data, size); - acc += local; - } - - while (acc >> 16) - acc = (acc & 0xffff) + (acc >> 16); - - return ~acc; -} - -/** - * packet_internet_checksum_udp() - compute the internet checkum for UDP packets - * @src_addr: source IP address - * @dst_addr: destination IP address - * @src_port: source port - * @dst_port: destination port - * @data: payload - * @size: length of payload in bytes - * @checksum: current checksum, or 0 - * - * Computes the internet checksum for a UDP packet, given the relevant IP and - * UDP header fields. - * - * Note that since a UDP packet contains the checksum itself, the resulting - * checksum will always be 0 (this fact is used to verify that a UDP packet is - * valid). - * Inversely, when calculating the checksum for outgoing packets, you have to - * specify 0 as @checksum, and this function will return the checksum for the - * caller to use for the packet. In this case, though, the caller must check - * whether the returned checksum might coincidentally be 0, in which case it - * must be flipped to -1 (0xffff), since 0 is not allowed as checksum in UDP - * packets, and -1 is arithmetically equivalent in the checksum calculation. - * - * Return: Checksum is returned. - */ -uint16_t packet_internet_checksum_udp(const struct in_addr *src_addr, - const struct in_addr *dst_addr, - uint16_t src_port, - uint16_t dst_port, - const uint8_t *data, - size_t size, - uint16_t checksum) { - struct { - uint32_t src; - uint32_t dst; - uint8_t _zeros; - uint8_t protocol; - uint16_t length; - struct udphdr udp; - } _c_packed_ udp_phdr = { - .src = src_addr->s_addr, - .dst = dst_addr->s_addr, - .protocol = IPPROTO_UDP, - .length = htons(sizeof(struct udphdr) + size), - .udp = { - .source = htons(src_port), - .dest = htons(dst_port), - .len = htons(sizeof(struct udphdr) + size), - .check = checksum, - }, - }; - const uint8_t *iter; - uint64_t acc = 0; - uint32_t local; - - _Static_assert(!(sizeof(udp_phdr) % sizeof(local)), - "UDP header structure size is not a multiple of 4"); - - for (iter = (const uint8_t *)&udp_phdr; - iter < (const uint8_t *)(&udp_phdr + 1); - iter += sizeof(local)) { - memcpy(&local, iter, sizeof(local)); - acc += local; - } - - while (size >= sizeof(local)) { - memcpy(&local, data, sizeof(local)); - acc += local; - - data += sizeof(local); - size -= sizeof(local); - } - - if (size) { - local = 0; - memcpy(&local, data, size); - acc += local; - } - - while (acc >> 16) - acc = (acc & 0xffff) + (acc >> 16); - - return ~acc; -} - -/** - * packet_sendto_udp() - send UDP packet on AF_PACKET socket - * @sockfd: AF_PACKET/SOCK_DGRAM socket - * @buf: payload - * @n_buf: length of payload in bytes - * @n_transmittedp: output argument for number of transmitted bytes - * @src_paddr: source protocol address, see ip(7) - * @dest_haddr: destination hardware address, see packet(7) - * @dest_paddr: destination protocol address, see ip(7) - * - * Sends an UDP packet on a AF_PACKET socket directly to a hardware - * address. The difference between this and sendto() on an AF_INET - * socket is that no routing is performed, so the packet is delivered - * even if the destination IP is not yet configured on the destination - * host. - * - * Return: 0 on success, negative error code on failure. - */ -int packet_sendto_udp(int sockfd, - const void *buf, - size_t n_buf, - size_t *n_transmittedp, - const struct sockaddr_in *src_paddr, - const struct packet_sockaddr_ll *dest_haddr, - const struct sockaddr_in *dest_paddr) { - struct iphdr ip_hdr = { - .version = IPVERSION, - .ihl = sizeof(ip_hdr) / 4, /* Length of header in multiples of four bytes */ - .tos = IPTOS_CLASS_CS6, /* Class Selector for network control */ - .tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + n_buf), - .frag_off = htons(IP_DF), /* Do not fragment */ - .ttl = IPDEFTTL, - .protocol = IPPROTO_UDP, - .saddr = src_paddr->sin_addr.s_addr, - .daddr = dest_paddr->sin_addr.s_addr, - }; - struct udphdr udp_hdr = { - .source = src_paddr->sin_port, - .dest = dest_paddr->sin_port, - .len = htons(sizeof(udp_hdr) + n_buf), - }; - struct iovec iov[3] = { - { - .iov_base = &ip_hdr, - .iov_len = sizeof(ip_hdr), - }, - { - .iov_base = &udp_hdr, - .iov_len = sizeof(udp_hdr), - }, - { - .iov_base = (void *)buf, - .iov_len = n_buf, - }, - }; - struct msghdr msg = { - .msg_name = (void*)dest_haddr, - .msg_namelen = sizeof(*dest_haddr), - .msg_iov = iov, - .msg_iovlen = sizeof(iov) / sizeof(iov[0]), - }; - ssize_t pktlen; - - ip_hdr.check = packet_internet_checksum((void*)&ip_hdr, sizeof(ip_hdr)); - udp_hdr.check = packet_internet_checksum_udp(&src_paddr->sin_addr, - &dest_paddr->sin_addr, - ntohs(src_paddr->sin_port), - ntohs(dest_paddr->sin_port), - buf, - n_buf, - 0); - - /* - * 0x0000 and 0xffff are equivalent for computing the UDP checksum, - * but 0x0000 is reserved in UDP headers, to mean that the checksum is - * not set and should be ignored by the receiver. Hence, flip it to - * 0xffff in that case. - */ - udp_hdr.check = udp_hdr.check ?: 0xffff; - - pktlen = sendmsg(sockfd, &msg, 0); - if (pktlen < 0) - return -c_errno(); - - /* - * Kernel never truncates. Worst case, we get -EMSGSIZE. Kernel *might* - * prepend VNET headers, in which case a bigger length than sent is - * returned. - * Lets assert on this, and then return to the caller the proportion of - * its own buffer that we sent (which is always exactly the requested - * size). - */ - c_assert((size_t)pktlen >= sizeof(ip_hdr) + sizeof(udp_hdr) + n_buf); - *n_transmittedp = n_buf; - return 0; -} - -/** - * packet_recvfrom_upd() - receive UDP packet from AF_PACKET socket - * @sockfd: AF_PACKET/SOCK_DGRAM socket - * @buf: buffor for payload - * @n_buf: max length of payload in bytes - * @n_transmittedp: output argument for number transmitted bytes - * @src: return argument for source address, or NULL, see ip(7) - * - * Receives an UDP packet on a AF_PACKET socket. The difference between - * this and recvfrom() on an AF_INET socket is that the packet will be - * received even if the destination IP address has not been configured - * on the interface. - * - * Return: 0 on success, negative error code on failure. - */ -int packet_recvfrom_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp, - struct sockaddr_in *src) { - union { - struct iphdr hdr; - /* - * Maximum IP-header length is 15 * 4, since it is specified in - * the `ihl` field, which is four bits and interpreted as - * factor of 4. So maximum `ihl` value is `(2^4 - 1) * 4`. - */ - uint8_t data[15 * 4]; - } ip_hdr; - struct udphdr udp_hdr; - struct iovec iov[3] = { - { - .iov_base = &ip_hdr, - }, - { - .iov_base = &udp_hdr, - .iov_len = sizeof(udp_hdr), - }, - { - .iov_base = buf, - .iov_len = n_buf, - }, - }; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; - struct msghdr msg = { - .msg_iov = iov, - .msg_iovlen = sizeof(iov) / sizeof(iov[0]), - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - struct cmsghdr *cmsg; - bool checksum = true; - ssize_t pktlen; - size_t hdrlen; - - *n_transmittedp = 0; - - /* Peek packet to obtain the real IP header length */ - pktlen = recv(sockfd, &ip_hdr.hdr, sizeof(ip_hdr.hdr), MSG_PEEK); - if (pktlen < 0) - return -errno; - - if ((size_t)pktlen < sizeof(ip_hdr.hdr)) { - /* - * Received packet is smaller than the minimal IP header length, - * discard it. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - if (ip_hdr.hdr.version != IPVERSION) { - /* - * This is not an IPv4 packet, discard it. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - hdrlen = ip_hdr.hdr.ihl * 4; - if (hdrlen < sizeof(ip_hdr.hdr)) { - /* - * The length given in the header is smaller than the minimum - * header length, discard the packet. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - /* - * Now that we know the ip-header length, we can prepare the iovec to - * read the entire packet into the correct buffers. - */ - iov[0].iov_len = hdrlen; - pktlen = recvmsg(sockfd, &msg, 0); - if (pktlen < 0) - return -errno; - - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg) { - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - } - } - - if (ntohs(ip_hdr.hdr.tot_len) > pktlen) { - /* - * The IP-packet is bigger than the chunk returned by the - * kernel. So either the packet is corrupt, or our caller - * provided too small a buffer. In both cases, we simply drop - * the packet. - */ - return 0; - } - - /* Truncate trailing garbage. */ - pktlen = ntohs(ip_hdr.hdr.tot_len); - - if ((size_t)pktlen < hdrlen + sizeof(udp_hdr)) { - /* - * The packet is too small to even contain an entire UDP - * header, so discard it entirely. - */ - return 0; - } else if ((size_t)pktlen < hdrlen + ntohs(udp_hdr.len)) { - /* - * The UDP header specified a longer length than the returned - * packet, so discard it entirely. - */ - return 0; - } - - /* - * Make @pktlen the length of the packet payload, without IP/UDP - * headers, since that is what the caller is interested in. - */ - pktlen = ntohs(udp_hdr.len) - sizeof(struct udphdr); - - /* IP */ - - if (ip_hdr.hdr.protocol != IPPROTO_UDP) { - return 0; /* not a UDP packet, discard it */ - } else if (ip_hdr.hdr.frag_off & htons(IP_MF | IP_OFFMASK)) { - return 0; /* fragmented packet, discard it */ - } else if (checksum && packet_internet_checksum(ip_hdr.data, hdrlen)) { - return 0; /* invalid checksum, discard it */ - } - - /* UDP */ - - if (checksum && udp_hdr.check) { - /* - * Computing the checksum of a packet that has the checksum set - * must yield 0. If it does not yield 0, the packet is invalid, - * in which case we discard it. - */ - if (packet_internet_checksum_udp(&(struct in_addr){ ip_hdr.hdr.saddr }, - &(struct in_addr){ ip_hdr.hdr.daddr }, - ntohs(udp_hdr.source), - ntohs(udp_hdr.dest), - buf, - pktlen, - udp_hdr.check)) { - return 0; - } - } - - if (src) { - src->sin_family = AF_INET; - src->sin_addr.s_addr = ip_hdr.hdr.saddr; - src->sin_port = udp_hdr.source; - } - - /* Return length of UDP payload (i.e., data written to @buf). */ - *n_transmittedp = pktlen; - return 0; -} - -/** - * packet_shutdown() - shutdown socket for future receive operations - * @sockfd: socket - * - * Partially emulates `shutdown(sockfd, SHUT_RD)`, in the sense that no - * further packets may be queued on the socket. All packets that are - * already queued will still be delivered, but once -EAGAIN is returned - * we are guaranteed never to be able to read more packets in the future. - * - * Return: 0 on success, or a negative error code on failure. - */ -int packet_shutdown(int sockfd) { - struct sock_filter filter[] = { - BPF_STMT(BPF_RET + BPF_K, 0), /* discard all packets */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - int r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/shared/n-dhcp4/src/util/packet.h b/shared/n-dhcp4/src/util/packet.h deleted file mode 100644 index 98dabf7f..00000000 --- a/shared/n-dhcp4/src/util/packet.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/* - * Packet Sockets - */ - -#include <c-stdaux.h> -#include <inttypes.h> -#include <linux/if_packet.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <unistd.h> - -/* - * `struct sockaddr_ll` is too small to fit the Infiniband hardware address. - * Introduce `struct packet_sockaddr_ll` which is the same as the original, - * except the `sl_addr` field is extended to fit all the supported hardware - * addresses. - */ -struct packet_sockaddr_ll { - unsigned short sll_family; - __be16 sll_protocol; - int sll_ifindex; - unsigned short sll_hatype; - unsigned char sll_pkttype; - unsigned char sll_halen; - unsigned char sll_addr[32]; /* MAX_ADDR_LEN */ -}; - -uint16_t packet_internet_checksum(const uint8_t *data, size_t len); -uint16_t packet_internet_checksum_udp(const struct in_addr *src_addr, - const struct in_addr *dst_addr, - uint16_t src_port, - uint16_t dst_port, - const uint8_t *data, - size_t size, - uint16_t checksum); - -int packet_sendto_udp(int sockfd, - const void *buf, - size_t n_buf, - size_t *n_transmittedp, - const struct sockaddr_in *src_paddr, - const struct packet_sockaddr_ll *dest_haddr, - const struct sockaddr_in *dest_paddr); -int packet_recvfrom_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp, - struct sockaddr_in *src); - -int packet_shutdown(int sockfd); - -/* inline helpers */ - -static inline int packet_recv_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp) { - return packet_recvfrom_udp(sockfd, buf, n_buf, n_transmittedp, NULL); -} diff --git a/shared/n-dhcp4/src/util/socket.c b/shared/n-dhcp4/src/util/socket.c deleted file mode 100644 index c25f76fd..00000000 --- a/shared/n-dhcp4/src/util/socket.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Socket Utilities - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <errno.h> -#include <net/if.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include "socket.h" - -/** - * socket_SIOCGIFNAME() - resolve an ifindex to an ifname - * @socket: socket to operate on - * @ifindex: index of network interface to resolve - * @ifname: buffer to store resolved name - * - * This uses the SIOCGIFNAME ioctl to resolve an ifindex to an ifname. The - * buffer provided in @ifnamep must be at least IFNAMSIZ bytes in size. The - * maximum ifname length is IFNAMSIZ-1, and this function always - * zero-terminates the result. - * - * This function is similar to if_indextoname(3) provided by glibc, but it - * allows to specify the target socket explicitly. This allows the caller to - * control the target network-namespace, rather than relying on the network - * namespace of the running process. - * - * Return: 0 on success, negative kernel error code on failure. - */ -int socket_SIOCGIFNAME(int socket, int ifindex, char (*ifnamep)[IFNAMSIZ]) { - struct ifreq req = { .ifr_ifindex = ifindex }; - int r; - - r = ioctl(socket, SIOCGIFNAME, &req); - if (r < 0) - return -errno; - - /* - * The linux kernel guarantees that an interface name is always - * zero-terminated, and it always fully fits into IFNAMSIZ bytes, - * including the zero-terminator. - */ - memcpy(ifnamep, req.ifr_name, IFNAMSIZ); - return 0; -} - -/** - * socket_bind_if() - bind socket to a network interface - * @socket: socket to operate on - * @ifindex: index of network interface to bind to, or 0 - * - * This binds the socket given via @socket to the network interface specified - * via @ifindex. It uses the underlying SO_BINDTODEVICE ioctl of the linux - * kernel. However, if available, if prefers the newer SO_BINDTOIFINDEX ioctl, - * which avoids resolving the interface name temporarily, and thus does not - * suffer from a race-condition. - * - * Return: 0 on success, negative error code on failure. - */ -int socket_bind_if(int socket, int ifindex) { - char ifname[IFNAMSIZ] = {}; - int r; - - c_assert(ifindex >= 0); - - /* - * We first try the newer SO_BINDTOIFINDEX. If it is not available on - * the running kernel, we fall back to SO_BINDTODEVICE. This, however, - * requires us to first resolve the ifindex to an ifname. Note that - * this is racy, since the device name might theoretically change - * asynchronously. - * - * Using 0 as ifindex will remove the device-binding. For - * SO_BINDTOIFINDEX we simply pass-through the 0 to the kernel, which - * recognizes this correctly. For SO_BINDTODEVICE we pass the empty - * string, which the kernel recognizes as a request to remove the - * binding. - * - * The commit introducing SO_BINDTOIFINDEX first appeared in linux-5.1: - * - * commit f5dd3d0c9638a9d9a02b5964c4ad636f06cf7e2c - * Author: David Herrmann <dh.herrmann@gmail.com> - * Date: Tue Jan 15 14:42:14 2019 +0100 - * - * net: introduce SO_BINDTOIFINDEX sockopt - * - * In older kernels, setsockopt(2) is guaranteed to return ENOPROTOOPT - * for this ioctl. - */ - -#ifdef SO_BINDTOIFINDEX - r = setsockopt(socket, - SOL_SOCKET, - SO_BINDTOIFINDEX, - &ifindex, - sizeof(ifindex)); - if (r >= 0) - return 0; - else if (errno != ENOPROTOOPT) - return -errno; -#endif /* SO_BINDTOIFINDEX */ - - if (ifindex > 0) { - r = socket_SIOCGIFNAME(socket, ifindex, &ifname); - if (r) - return r; - } - - r = setsockopt(socket, - SOL_SOCKET, - SO_BINDTODEVICE, - ifname, - strlen(ifname)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/shared/n-dhcp4/src/util/socket.h b/shared/n-dhcp4/src/util/socket.h deleted file mode 100644 index b5ecf2c2..00000000 --- a/shared/n-dhcp4/src/util/socket.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -/* - * Socket Utilities - */ - -#include <c-stdaux.h> -#include <stdlib.h> - -int socket_SIOCGIFNAME(int socket, int ifindex, char (*ifnamep)[IFNAMSIZ]); -int socket_bind_if(int socket, int ifindex); |