Files

789 lines
25 KiB
C

/*
Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _HIO_DHCP_H_
#define _HIO_DHCP_H_
#include <hio.h>
#include <hio-sck.h>
/* ---------------------------------------------------------------- */
#define HIO_DHCP4_SERVER_PORT (67)
#define HIO_DHCP4_CLIENT_PORT (68)
#define HIO_DHCP4_MAGIC_COOKIE (0x63825363)
/* operation code */
enum hio_dhcp4_op_t
{
HIO_DHCP4_OP_BOOTREQUEST = 1,
HIO_DHCP4_OP_BOOTREPLY = 2
};
enum hio_dhcp4_htype_t
{
HIO_DHCP4_HTYPE_ETHERNET = 1,
HIO_DHCP4_HTYPE_IEEE802 = 6,
HIO_DHCP4_HTYPE_ARCNET = 7,
HIO_DHCP4_HTYPE_APPLETALK = 8,
HIO_DHCP4_HTYPE_HDLC = 17,
HIO_DHCP4_HTYPE_ATM = 19,
HIO_DHCP4_HTYPE_INFINIBAND = 32
};
/* option codes (partial) */
/** The only flag RFC 2131 defines in the bootp flags field. A client sets it
* to say it cannot receive a unicast reply before it has an address, so a
* reply to such a request has to be broadcast. The field is on the wire in
* network order, so test it as hio_ntoh16(hdr->flags) & this. */
#define HIO_DHCP4_FLAG_BROADCAST (0x8000)
enum hio_dhcp4_opt_t
{
HIO_DHCP4_OPT_PADDING = 0x00,
HIO_DHCP4_OPT_SUBNET = 0x01,
HIO_DHCP4_OPT_TIME_OFFSET = 0x02,
HIO_DHCP4_OPT_ROUTER = 0x03,
HIO_DHCP4_OPT_TIME_SERVER = 0x04,
HIO_DHCP4_OPT_NAME_SERVER = 0x05,
HIO_DHCP4_OPT_DNS_SERVER = 0x06,
HIO_DHCP4_OPT_LOG_SERVER = 0x07,
HIO_DHCP4_OPT_COOKIE_SERVER = 0x08,
HIO_DHCP4_OPT_LPR_SERVER = 0x09,
HIO_DHCP4_OPT_HOST_NAME = 0x0c,
HIO_DHCP4_OPT_BOOT_SIZE = 0x0d,
HIO_DHCP4_OPT_DOMAIN_NAME = 0x0f,
HIO_DHCP4_OPT_SWAP_SERVER = 0x10,
HIO_DHCP4_OPT_ROOT_PATH = 0x11,
HIO_DHCP4_OPT_IP_TTL = 0x17,
HIO_DHCP4_OPT_MTU = 0x1a,
HIO_DHCP4_OPT_BROADCAST = 0x1c,
HIO_DHCP4_OPT_NTP_SERVER = 0x2a,
HIO_DHCP4_OPT_VENDOR_INFO = 0x2b, /* 43 - vendor specific information */
HIO_DHCP4_OPT_WINS_SERVER = 0x2c,
HIO_DHCP4_OPT_REQUESTED_IPADDR = 0x32,
HIO_DHCP4_OPT_LEASE_TIME = 0x33,
HIO_DHCP4_OPT_OVERLOAD = 0x34, /* overload sname or file */
HIO_DHCP4_OPT_MESSAGE_TYPE = 0x35,
HIO_DHCP4_OPT_SERVER_ID = 0x36,
HIO_DHCP4_OPT_PARAM_REQ = 0x37,
HIO_DHCP4_OPT_MESSAGE = 0x38,
HIO_DHCP4_OPT_MAX_SIZE = 0x39,
HIO_DHCP4_OPT_T1 = 0x3a,
HIO_DHCP4_OPT_T2 = 0x3b,
/* [NOTE] this was HIO_DHCP4_OPT_VENDOR, which read as option 43's meaning
* while carrying option 60's value. RFC 2132 calls 60 the vendor class
* identifier; vendor specific information is 43, above. */
HIO_DHCP4_OPT_CLASS_ID = 0x3c, /* 60 - vendor class identifier */
HIO_DHCP4_OPT_CLIENT_ID = 0x3d,
HIO_DHCP4_OPT_RELAY = 0x52,
HIO_DHCP4_OPT_SUBNET_SELECTION = 0x76,
HIO_DHCP4_OPT_VENDOR_VC = 0x7c, /* 124 - vendor-identifying vendor class */
HIO_DHCP4_OPT_VENDOR_VSI = 0x7d, /* 125 - vendor-identifying vendor-specific information */
HIO_DHCP4_OPT_PRIVATE_SRCADDR = 0xFE, /* site-local. used by dhcpa and dhcpb */
HIO_DHCP4_OPT_END = 0xFF
};
/* flags for HIO_DHCP4_OPT_OVERLOAD */
enum hio_dhcp4_opt_overload_t
{
HIO_DHCP4_OPT_OVERLOAD_FILE = (1 << 0),
HIO_DHCP4_OPT_OVERLOAD_SNAME = (1 << 1)
};
/* flags for HIO_DHCP4_OPT_OVERLOAD */
enum hio_dhcp4_opt_relay_t
{
HIO_DHCP4_OPT_RELAY_CIRCUIT_ID = 1,
HIO_DHCP4_OPT_RELAY_REMOTE_ID = 2
};
/* message type */
enum hio_dhcp4_msg_t
{
HIO_DHCP4_MSG_DISCOVER = 1,
HIO_DHCP4_MSG_OFFER = 2,
HIO_DHCP4_MSG_REQUEST = 3,
HIO_DHCP4_MSG_DECLINE = 4,
HIO_DHCP4_MSG_ACK = 5,
HIO_DHCP4_MSG_NAK = 6,
HIO_DHCP4_MSG_RELEASE = 7,
HIO_DHCP4_MSG_INFORM = 8,
HIO_DHCP4_MSG_FORCE_RENEW = 9,
HIO_DHCP4_MSG_LEASE_QUERY = 10,
HIO_DHCP4_MSG_LEASE_UNASSIGNED = 11,
HIO_DHCP4_MSG_LEASE_UNKNOWN = 12,
HIO_DHCP4_MSG_LEASE_ACTIVE = 13,
HIO_DHCP4_MSG_BULK_LEASE_QUERY = 14,
HIO_DHCP4_MSG_LEASE_QUERY_DONE = 15,
HIO_DHCP4_MSG_ACTIVE_LEASE_QUERY = 16,
HIO_DHCP4_MSG_LEASE_QUERY_STATUS = 17,
HIO_DHCP4_MSG_TLS = 18
};
/* --------------------------------------------------- */
#include <hio-pac1.h>
/* --------------------------------------------------- */
struct hio_dhcp4_pkt_hdr_t
{
hio_uint8_t op;
hio_uint8_t htype;
hio_uint8_t hlen;
hio_uint8_t hops;
hio_uint32_t xid; /* transaction id */
hio_uint16_t secs; /* seconds elapsed */
hio_uint16_t flags; /* bootp flags. see #HIO_DHCP4_FLAG_BROADCAST */
hio_uint32_t ciaddr; /* client ip */
hio_uint32_t yiaddr; /* your ip */
hio_uint32_t siaddr; /* next server ip */
hio_uint32_t giaddr; /* relay agent ip */
hio_uint8_t chaddr[16]; /* client mac */
char sname[64]; /* server host name */
char file[128]; /* boot file name */
/* options are placed after the header.
* the first four bytes of the options compose a magic cookie
* 0x63 0x82 0x53 0x63 */
};
typedef struct hio_dhcp4_pkt_hdr_t hio_dhcp4_pkt_hdr_t;
struct hio_dhcp4_opt_hdr_t
{
hio_uint8_t code;
hio_uint8_t len;
};
typedef struct hio_dhcp4_opt_hdr_t hio_dhcp4_opt_hdr_t;
/* --------------------------------------------------- */
#include <hio-upac.h>
/* --------------------------------------------------- */
typedef int (*hio_dhcp4_opt_walker_t) (hio_dhcp4_opt_hdr_t* opt);
struct hio_dhcp4_pktinf_t
{
hio_dhcp4_pkt_hdr_t* hdr;
hio_oow_t len;
};
typedef struct hio_dhcp4_pktinf_t hio_dhcp4_pktinf_t;
struct hio_dhcp4_pktbuf_t
{
hio_dhcp4_pkt_hdr_t* hdr;
hio_oow_t len;
hio_oow_t capa;
};
typedef struct hio_dhcp4_pktbuf_t hio_dhcp4_pktbuf_t;
/* ---------------------------------------------------------------- */
#define HIO_DHCP6_SERVER_PORT (547)
#define HIO_DHCP6_CLIENT_PORT (546)
#define HIO_DHCP6_HOP_COUNT_LIMIT (32)
enum hio_dhcp6_msg_t
{
HIO_DHCP6_MSG_SOLICIT = 1,
HIO_DHCP6_MSG_ADVERTISE = 2,
HIO_DHCP6_MSG_REQUEST = 3,
HIO_DHCP6_MSG_CONFIRM = 4,
HIO_DHCP6_MSG_RENEW = 5,
HIO_DHCP6_MSG_REBIND = 6,
HIO_DHCP6_MSG_REPLY = 7,
HIO_DHCP6_MSG_RELEASE = 8,
HIO_DHCP6_MSG_DECLINE = 9,
HIO_DHCP6_MSG_RECONFIGURE = 10,
HIO_DHCP6_MSG_INFOREQ = 11,
HIO_DHCP6_MSG_RELAYFORW = 12,
HIO_DHCP6_MSG_RELAYREPL = 13,
};
typedef enum hio_dhcp6_msg_t hio_dhcp6_msg_t;
enum hio_dhcp6_opt_t
{
HIO_DHCP6_OPT_CLIENTID = 1,
HIO_DHCP6_OPT_SERVERID = 2,
HIO_DHCP6_OPT_IA_NA = 3,
HIO_DHCP6_OPT_IA_TA = 4,
HIO_DHCP6_OPT_IAADDR = 5,
HIO_DHCP6_OPT_PREFERENCE = 7,
HIO_DHCP6_OPT_ELAPSED_TIME = 8,
HIO_DHCP6_OPT_RELAY_MESSAGE = 9,
HIO_DHCP6_OPT_RAPID_COMMIT = 14,
HIO_DHCP6_OPT_USER_CLASS = 15,
HIO_DHCP6_OPT_VENDOR_CLASS = 16,
HIO_DHCP6_OPT_VENDOR_VSI = 17, /* vendor-specific information */
HIO_DHCP6_OPT_INTERFACE_ID = 18,
HIO_DHCP6_OPT_IA_PD = 25,
HIO_DHCP6_OPT_IAPREFIX = 26
};
typedef enum hio_dhcp6_opt_t hio_dhcp6_opt_t;
/* --------------------------------------------------- */
#include <hio-pac1.h>
/* --------------------------------------------------- */
struct hio_dhcp6_pkt_hdr_t
{
hio_uint8_t msgtype;
hio_uint8_t transid[3];
};
typedef struct hio_dhcp6_pkt_hdr_t hio_dhcp6_pkt_hdr_t;
struct hio_dhcp6_relay_hdr_t
{
hio_uint8_t msgtype; /* RELAY-FORW, RELAY-REPL */
hio_uint8_t hopcount;
hio_uint8_t linkaddr[16];
hio_uint8_t peeraddr[16];
};
typedef struct hio_dhcp6_relay_hdr_t hio_dhcp6_relay_hdr_t;
struct hio_dhcp6_opt_hdr_t
{
hio_uint16_t code;
hio_uint16_t len; /* length of option data, excludes the option header */
};
typedef struct hio_dhcp6_opt_hdr_t hio_dhcp6_opt_hdr_t;
/* --------------------------------------------------- */
#include <hio-upac.h>
/* --------------------------------------------------- */
struct hio_dhcp6_pktinf_t
{
hio_dhcp6_pkt_hdr_t* hdr;
hio_oow_t len;
};
typedef struct hio_dhcp6_pktinf_t hio_dhcp6_pktinf_t;
struct hio_dhcp6_pktbuf_t
{
/* ---------------------------------------------------------------- */
/* this part must match hio_dhcp6_pktinf_t: a hio_dhcp6_pktbuf_t* is */
/* cast down to a hio_dhcp6_pktinf_t* by the functions that only */
/* read, so that a caller holding a buffer need not build a second */
/* structure to inspect it. */
hio_dhcp6_pkt_hdr_t* hdr;
hio_oow_t len;
/* ---------------------------------------------------------------- */
hio_oow_t capa;
};
typedef struct hio_dhcp6_pktbuf_t hio_dhcp6_pktbuf_t;
/* ---------------------------------------------------------------- */
typedef struct hio_svc_dhcs_t hio_svc_dhcs_t;
/* ---------------------------------------------------------------- */
#if defined(__cplusplus)
extern "C" {
#endif
HIO_EXPORT int hio_dhcp4_init_pktbuf (
hio_dhcp4_pktbuf_t* pkt,
void* buf,
hio_oow_t capa
);
HIO_EXPORT int hio_dhcp4_add_option (
hio_dhcp4_pktbuf_t* pkt,
int code,
void* optr, /**< option data pointer */
hio_uint8_t olen /**< option data length */
);
HIO_EXPORT int hio_dhcp4_delete_option (
hio_dhcp4_pktbuf_t* pkt,
int code
);
HIO_EXPORT void hio_dhcp4_compact_options (
hio_dhcp4_pktbuf_t* pkt
);
#if 0
HIO_EXPORT int hio_dhcp4_add_options (
hio_dhcp4_pkt_hdr_t* pkt,
hio_oow_t len,
hio_oow_t max,
int code,
hio_uint8_t* optr, /* option data */
hio_uint8_t olen /* option length */
);
#endif
/* ---------------------------------------------------------------- */
/* typed option access */
/* */
/* option payloads are not aligned - they sit wherever the preceding */
/* options left them - so a multi-octet value must be copied out */
/* rather than read through a cast. these do that, and the byte */
/* order conversion, so that no caller has to remember either. */
/* ---------------------------------------------------------------- */
HIO_EXPORT int hio_dhcp4_add_option_uint8 (
hio_dhcp4_pktbuf_t* pkt,
int code,
hio_uint8_t value
);
HIO_EXPORT int hio_dhcp4_add_option_uint16 (
hio_dhcp4_pktbuf_t* pkt,
int code,
hio_uint16_t value /**< in host order */
);
HIO_EXPORT int hio_dhcp4_add_option_uint32 (
hio_dhcp4_pktbuf_t* pkt,
int code,
hio_uint32_t value /**< in host order */
);
/**
* Read a fixed-width option. Returns 0 and stores the value, or -1 if the
* option is absent or is not exactly the width asked for - a length that
* disagrees with the option's definition is a malformed packet, not a value
* to be salvaged.
*/
HIO_EXPORT int hio_dhcp4_get_option_uint8 (
const hio_dhcp4_pktinf_t* pkt,
int code,
hio_uint8_t* value
);
HIO_EXPORT int hio_dhcp4_get_option_uint16 (
const hio_dhcp4_pktinf_t* pkt,
int code,
hio_uint16_t* value /**< stored in host order */
);
HIO_EXPORT int hio_dhcp4_get_option_uint32 (
const hio_dhcp4_pktinf_t* pkt,
int code,
hio_uint32_t* value /**< stored in host order */
);
/**
* Read an option's payload without copying it. Returns 0 and points 'ptr' into
* the packet, or -1 if the option is absent. The pointer is only valid while
* the packet is.
*/
HIO_EXPORT int hio_dhcp4_get_option_data (
const hio_dhcp4_pktinf_t* pkt,
int code,
const hio_uint8_t** ptr,
hio_uint8_t* len
);
/* ---------------------------------------------------------------- */
/* message-level helpers shared by both ends */
/* ---------------------------------------------------------------- */
/**
* The DHCP message type, option 53. Returns 0 and stores it, or -1 if the
* option is missing or malformed - which is what tells a BOOTP packet from a
* DHCP one, so both ends check it before anything else.
*/
HIO_EXPORT int hio_dhcp4_get_msg_type (
const hio_dhcp4_pktinf_t* pkt,
hio_uint8_t* mtype
);
/**
* What identifies the client: option 61 if it sent one, otherwise the hardware
* type and address from the header. Both ends have to derive this the same way
* or a lease looked up by one will not be the lease recorded by the other, so
* it lives here rather than in either of them.
*
* On success 'ptr' points into the packet and 'len' is its length. -1 means
* there is nothing usable - no client id option and no hardware address.
*/
HIO_EXPORT int hio_dhcp4_get_client_id (
const hio_dhcp4_pktinf_t* pkt,
const hio_uint8_t** ptr,
hio_uint8_t* len
);
/**
* Check that a received buffer is shaped like a DHCP packet at all: long
* enough for the fixed header, carrying the magic cookie, and with a hardware
* address length that fits the field. Returns 0 or -1.
*
* Both ends call this on every datagram before looking at anything in it.
*/
HIO_EXPORT int hio_dhcp4_check_pkt (
const hio_dhcp4_pktinf_t* pkt
);
/**
* Start a reply to a request, copying over the fields a reply must echo: the
* transaction id, the broadcast flag, the relay address, and the client's
* hardware address. The caller sets yiaddr/siaddr and adds options.
*/
HIO_EXPORT int hio_dhcp4_init_reply_pktbuf (
hio_dhcp4_pktbuf_t* pkt,
void* buf,
hio_oow_t capa,
const hio_dhcp4_pktinf_t* req
);
HIO_EXPORT int hio_dhcp4_walk_options (
const hio_dhcp4_pktinf_t* pkt,
hio_dhcp4_opt_walker_t walker
);
HIO_EXPORT hio_dhcp4_opt_hdr_t* hio_dhcp4_find_option (
const hio_dhcp4_pktinf_t* pkt,
int code
);
/**
* Return a pointer to an option's value, past its header, and its length.
* #HIO_NULL if the option is absent.
*/
HIO_EXPORT hio_uint8_t* hio_dhcp4_get_option_value (
const hio_dhcp4_pktinf_t* pkt,
int code,
hio_uint8_t* olen
);
/**
* Find a suboption inside the value of a relay agent information option
* (#HIO_DHCP4_OPT_RELAY). 'ptr' points at the option's value, not its header:
*
* | 82 | N | SUBOPT... | SUBOPT... |
* ^ ptr
*
* Each suboption is a one-octet code, a one-octet length, then the value.
* Returns a pointer to the value, or #HIO_NULL.
*
* [NOTE] this was hio_dhcp4_get_relay_suboption(). Renamed to say that what
* comes back is the value rather than the suboption header, which is what
* hio_dhcp4_find_option() returns for an option and is easy to confuse.
*/
HIO_EXPORT hio_uint8_t* hio_dhcp4_get_relay_suboption_value (
const hio_uint8_t* ptr,
hio_uint8_t len,
int relay_subcode,
hio_uint8_t* olen
);
/**
* Find the relay agent information option in a packet and a suboption within
* it, in one step. Returns a pointer to the suboption's value, or #HIO_NULL if
* either is absent.
*/
HIO_EXPORT hio_uint8_t* hio_dhcp4_find_relay_suboption_value (
const hio_dhcp4_pktinf_t* pkt,
int relay_subcode,
hio_uint8_t* olen
);
/**
* Remove one suboption from the relay agent information option, and the
* option itself if that was its last suboption.
*
* Returns 0, or -1 if the suboption is not there or the option's length
* disagrees with what it contains.
*/
HIO_EXPORT int hio_dhcp4_delete_relay_suboption (
hio_dhcp4_pktbuf_t* pkt,
int relay_subcode
);
/**
* Append a suboption to the relay agent information option, creating that
* option if the packet does not have one yet.
*
* Fails with -1 if the packet has no room, if the suboption would take the
* option's length past the 255 an option length field can express, or if the
* existing option lives in the overloaded sname/file area - which cannot be
* grown. Delete it and add it again to move it into the options field.
*/
HIO_EXPORT int hio_dhcp4_add_relay_suboption (
hio_dhcp4_pktbuf_t* pkt,
int relay_subcode,
const hio_uint8_t* dptr,
hio_uint8_t dlen
);
/* ---------------------------------------------------------------- */
/** The most a relay chain may be nested. RFC 8415 caps a relay's hop count at
* 32, so a packet claiming more nesting than that is malformed - and without a
* cap the recursive traversals below would exhaust the stack on one. */
#define HIO_DHCP6_MAX_RELAY_LEVEL HIO_DHCP6_HOP_COUNT_LIMIT
/** An option's length field is 16 bits, so this is the most option data one
* can carry. */
#define HIO_DHCP6_MAX_OPT_DLEN (0xFFFF)
/**
* How many relay layers wrap a packet. 0 for a message that is not relayed.
*
* A relayed DHCPv6 message is not a header with a flag - the whole inner
* message is carried as the value of a RELAY-MSG option inside the outer one,
* so "level" below means how far in to look.
*/
HIO_EXPORT int hio_dhcp6_get_relay_level (
const hio_dhcp6_pktinf_t* pkt
);
/**
* The message header at a given relay level. A negative level means the
* innermost. #HIO_NULL if the packet is too short or does not nest that far.
*/
HIO_EXPORT hio_dhcp6_pkt_hdr_t* hio_dhcp6_get_pkt_hdr (
const hio_dhcp6_pktinf_t* pkt,
int level
);
/**
* Find an option at a relay level. A negative level searches the innermost
* message.
*/
HIO_EXPORT hio_dhcp6_opt_hdr_t* hio_dhcp6_find_option (
const hio_dhcp6_pktinf_t* pkt,
int level,
int code
);
/**
* The next option with this code after 'pos', or the first if 'pos' is
* #HIO_NULL - for walking the several options that may share a code.
*/
HIO_EXPORT hio_dhcp6_opt_hdr_t* hio_dhcp6_find_option_after (
const hio_dhcp6_pktinf_t* pkt,
int level,
const hio_dhcp6_opt_hdr_t* pos,
int code
);
/** The next option of any code after 'pos', for walking every option. */
HIO_EXPORT hio_dhcp6_opt_hdr_t* hio_dhcp6_get_option_after (
const hio_dhcp6_pktinf_t* pkt,
int level,
const hio_dhcp6_opt_hdr_t* pos
);
HIO_EXPORT int hio_dhcp6_init_pktbuf (
hio_dhcp6_pktbuf_t* pkt,
void* buf,
hio_oow_t capa
);
/**
* Insert an option at 'pos', or append it if 'pos' is #HIO_NULL. When the
* level is inside a relay chain, the enclosing RELAY-MSG options grow to
* match.
*
* 'safe' asks for 'pos' to be checked against the options actually present
* first. Without it a wrong position corrupts the packet, so pass 0 only for a
* position this same packet just returned.
*
* #HIO_NULL if the packet has no room, if the data is longer than an option
* length field can express, or if growing an enclosing relay option would
* take it past that.
*/
HIO_EXPORT hio_dhcp6_opt_hdr_t* hio_dhcp6_insert_option_at (
hio_dhcp6_pktbuf_t* pkt,
int level,
hio_dhcp6_opt_hdr_t* pos,
int safe,
int code,
const void* data,
hio_oow_t dlen
);
/** Replace the option at 'pos'. The packet grows or shrinks to fit, and the
* enclosing relay options follow. See hio_dhcp6_insert_option_at() for
* 'safe'. */
HIO_EXPORT hio_dhcp6_opt_hdr_t* hio_dhcp6_replace_option_at (
hio_dhcp6_pktbuf_t* pkt,
int level,
hio_dhcp6_opt_hdr_t* pos,
int safe,
int code,
const void* data,
hio_oow_t dlen
);
/** Delete the option at 'pos'. See hio_dhcp6_insert_option_at() for 'safe'. */
HIO_EXPORT int hio_dhcp6_delete_option_at (
hio_dhcp6_pktbuf_t* pkt,
int level,
hio_dhcp6_opt_hdr_t* pos,
int safe
);
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/* dhcpv4 server service */
/* ---------------------------------------------------------------- */
#define HIO_SVC_DHCS_DFL_LEASE_SECS (3600)
/** how many addresses one pool may span. a bound keeps a mistyped pool from
* turning into an allocation the size of the address space. */
#define HIO_SVC_DHCS_MAX_POOL_SIZE (65536)
enum hio_svc_dhcs_lease_state_t
{
/** offered in reply to a DISCOVER but not yet requested. held briefly so
* two clients discovering at once are not offered the same address. */
HIO_SVC_DHCS_LEASE_OFFERED = 0,
/** requested and acknowledged */
HIO_SVC_DHCS_LEASE_BOUND,
/** a client reported the address already in use. it is kept out of the
* pool rather than handed to the next client to find the same thing. */
HIO_SVC_DHCS_LEASE_DECLINED
};
typedef enum hio_svc_dhcs_lease_state_t hio_svc_dhcs_lease_state_t;
typedef struct hio_svc_dhcs_lease_t hio_svc_dhcs_lease_t;
struct hio_svc_dhcs_lease_t
{
hio_uint32_t ipaddr; /**< host order */
hio_ntime_t expiry; /**< when this stops being reserved */
hio_uint8_t* cid; /**< the client identifier, as hio_dhcp4_get_client_id() derived it */
hio_uint8_t cidlen;
int state; /**< #hio_svc_dhcs_lease_state_t */
};
typedef struct hio_svc_dhcs_cfg_t hio_svc_dhcs_cfg_t;
struct hio_svc_dhcs_cfg_t
{
/** where to listen. usually 0.0.0.0:67. */
hio_skad_t bind_addr;
/** this server's own address - option 54, and what a client's REQUEST is
* checked against to see whether it picked us. host order. */
hio_uint32_t server_id;
/** the pool, inclusive at both ends, host order. */
hio_uint32_t pool_first;
hio_uint32_t pool_last;
/** offered alongside an address. zero omits the option entirely rather
* than offering a zero, which would be worse than saying nothing. */
hio_uint32_t netmask;
hio_uint32_t router;
hio_uint32_t dns1;
hio_uint32_t dns2;
/** lease duration in seconds. zero means #HIO_SVC_DHCS_DFL_LEASE_SECS. */
hio_uint32_t lease_secs;
/** offered as option 15 if not #HIO_NULL. copied, so the caller need not
* keep it alive. */
const hio_bch_t* domain;
};
/**
* Start a DHCPv4 server. It binds the configured address and answers from the
* configured pool.
*
* Fails and reports through hio_geterrnum() if the pool is empty, spans more
* than #HIO_SVC_DHCS_MAX_POOL_SIZE addresses, or the socket cannot be bound.
*/
HIO_EXPORT hio_svc_dhcs_t* hio_svc_dhcs_start (
hio_t* hio,
const hio_svc_dhcs_cfg_t* cfg
);
HIO_EXPORT void hio_svc_dhcs_stop (
hio_svc_dhcs_t* dhcs
);
/**
* Run one request through the server and produce the reply, without any
* socket being involved.
*
* This is the whole state machine, and it is exposed rather than buried in the
* read callback for two reasons: a caller embedding the server may want to
* carry the packets itself, and it means the protocol can be tested by feeding
* it packets instead of by standing up a network.
*
* Returns 1 with a reply in 'rep' addressed to 'dstaddr', 0 if the request
* warrants no answer (a RELEASE, or a REQUEST that selected another server),
* or -1 on error.
*/
HIO_EXPORT int hio_svc_dhcs_process (
hio_svc_dhcs_t* dhcs,
const hio_dhcp4_pktinf_t* req,
hio_dhcp4_pktbuf_t* rep,
hio_skad_t* dstaddr
);
/** How many leases the server is currently holding, expired ones included
* until they are reclaimed. */
HIO_EXPORT hio_oow_t hio_svc_dhcs_getleasecount (
hio_svc_dhcs_t* dhcs
);
/** Read one lease out by index, for inspection. Returns 0 or -1 if the index
* is past the end. The 'cid' pointer in the copy belongs to the service. */
HIO_EXPORT int hio_svc_dhcs_getlease (
hio_svc_dhcs_t* dhcs,
hio_oow_t index,
hio_svc_dhcs_lease_t* lease
);
/** Discard leases whose time has passed, returning how many went. Called
* automatically when the pool is exhausted; exposed so a caller can do it on
* its own schedule. */
HIO_EXPORT hio_oow_t hio_svc_dhcs_purgeexpiredleases (
hio_svc_dhcs_t* dhcs
);
#if defined(HIO_HAVE_INLINE)
static HIO_INLINE hio_t* hio_svc_dhcs_gethio(hio_svc_dhcs_t* svc) { return hio_svc_gethio((hio_svc_t*)svc); }
#else
#define hio_svc_dhcs_gethio(svc) hio_svc_gethio(svc)
#endif
#if defined(__cplusplus)
}
#endif
#endif