about summary refs log tree commit diff
path: root/shared/n-dhcp4/src/n-dhcp4-private.h
diff options
context:
space:
mode:
Diffstat (limited to 'shared/n-dhcp4/src/n-dhcp4-private.h')
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-private.h688
1 files changed, 688 insertions, 0 deletions
diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h
new file mode 100644
index 00000000..c38ddbfc
--- /dev/null
+++ b/shared/n-dhcp4/src/n-dhcp4-private.h
@@ -0,0 +1,688 @@
+#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 "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;
+
+/* 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;
+                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;
+        struct drand48_data entropy;    /* entropy pool */
+        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 NDhcp4CConnection {
+        NDhcp4ClientConfig *client_config;
+        NDhcp4ClientProbeConfig *probe_config;
+        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;
+        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,                                                 \
+        }
+
+struct NDhcp4ClientProbe {
+        NDhcp4ClientProbeConfig *config;
+        NDhcp4Client *client;
+        CList event_list;
+        CList lease_list;
+        void *userdata;
+
+        unsigned int state;                     /* current probe state */
+        uint64_t ns_deferred;                   /* timeout for deferred action */
+        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, uint32_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,
+                              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;
+}