All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton
@ 2020-10-16 20:23 James Prestwood
  2020-10-16 20:23 ` [PATCH v4 02/11] dhcp-server: add transport framework James Prestwood
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 12097 bytes --]

---
 Makefile.am       |   1 +
 ell/dhcp-server.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++
 ell/dhcp.h        |  36 +++++
 ell/ell.sym       |  13 ++
 4 files changed, 397 insertions(+)
 create mode 100644 ell/dhcp-server.c

v4:
 * Fixed endianness issues when auto-assigning IP range

diff --git a/Makefile.am b/Makefile.am
index beba535..9bdf4dc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -128,6 +128,7 @@ ell_libell_la_SOURCES = $(linux_headers) \
 			ell/dhcp6-transport.c \
 			ell/dhcp6-lease.c \
 			ell/dhcp-util.c \
+			ell/dhcp-server.c \
 			ell/cert.c \
 			ell/cert-private.h \
 			ell/ecc-private.h \
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
new file mode 100644
index 0000000..97479ae
--- /dev/null
+++ b/ell/dhcp-server.c
@@ -0,0 +1,347 @@
+/*
+ *
+ *  Embedded Linux library
+ *
+ *  Copyright (C) 2020  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <net/ethernet.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <time.h>
+
+#include "private.h"
+#include "time.h"
+#include "net.h"
+#include "dhcp.h"
+#include "dhcp-private.h"
+#include "queue.h"
+#include "util.h"
+#include "strv.h"
+
+/* 8 hours */
+#define DEFAULT_DHCP_LEASE_SEC (8*60*60)
+
+struct l_dhcp_server {
+	bool started;
+	int ifindex;
+	char *ifname;
+	uint32_t start_ip;
+	uint32_t end_ip;
+	uint32_t address;
+	uint32_t netmask;
+	uint32_t gateway;
+	uint32_t *dns_list;
+	uint32_t lease_seconds;
+
+	struct l_queue *lease_list;
+
+	l_dhcp_debug_cb_t debug_handler;
+	void *debug_data;
+
+	l_dhcp_server_event_cb_t event_handler;
+	void *user_data;
+	l_dhcp_destroy_cb_t event_destroy;
+};
+
+#define MAC "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_STR(a) a[0], a[1], a[2], a[3], a[4], a[5]
+
+#define IP_STR(uint_ip) \
+({ \
+	struct in_addr _in; \
+	char *_out; \
+	_in.s_addr = uint_ip; \
+	_out = inet_ntoa(_in); \
+	_out; \
+})
+
+#define SERVER_DEBUG(fmt, args...)					\
+	l_util_debug(server->debug_handler, server->debug_data,		\
+			"%s:%i " fmt, __func__, __LINE__, ## args)
+
+LIB_EXPORT struct l_dhcp_server *l_dhcp_server_new(int ifindex)
+{
+	struct l_dhcp_server *server = l_new(struct l_dhcp_server, 1);
+	if (!server)
+		return NULL;
+
+	server->lease_list = l_queue_new();
+
+	server->started = false;
+
+	server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
+
+	server->ifindex = ifindex;
+	server->debug_handler = NULL;
+	server->debug_data = NULL;
+
+	return server;
+}
+
+LIB_EXPORT void l_dhcp_server_destroy(struct l_dhcp_server *server)
+{
+	if (unlikely(!server))
+		return;
+
+	l_dhcp_server_stop(server);
+
+	if (server->event_destroy)
+		server->event_destroy(server->user_data);
+
+	l_free(server->ifname);
+
+	l_queue_destroy(server->lease_list,
+				(l_queue_destroy_func_t) _dhcp_lease_free);
+
+	if (server->dns_list)
+		l_free(server->dns_list);
+
+	l_free(server);
+}
+
+LIB_EXPORT bool l_dhcp_server_start(struct l_dhcp_server *server)
+{
+	struct in_addr ia;
+
+	if (unlikely(!server))
+		return false;
+
+	if (server->started)
+		return false;
+
+	if (!server->address) {
+		if (!l_net_get_address(server->ifindex, &ia))
+			return false;
+
+		server->address = ia.s_addr;
+	}
+
+	/* Assign a default gateway if not already set */
+	if (!server->gateway)
+		server->gateway = server->address;
+
+	/* Assign a default netmask if not already */
+	if (!server->netmask) {
+		if (inet_aton("255.255.255.0", &ia) < 0)
+			return false;
+
+		server->netmask = ia.s_addr;
+	}
+
+	/*
+	 * Assign a default ip range if not already. This will default to
+	 * server->address + 1 ... 254
+	 */
+	if (!server->start_ip) {
+		uint8_t *ptr = (uint8_t *) &server->start_ip;
+
+		server->start_ip = server->address;
+		ptr[3] += 1;
+
+		ptr = (uint8_t *) &server->end_ip;
+		server->end_ip = server->address;
+		ptr[3] = 254;
+	}
+
+	if (!server->ifname) {
+		server->ifname = l_net_get_name(server->ifindex);
+
+		if (!server->ifname)
+			return false;
+	}
+
+	SERVER_DEBUG("Starting DHCP server on %s", server->ifname);
+
+	server->started = true;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_stop(struct l_dhcp_server *server)
+{
+	if (unlikely(!server))
+		return false;
+
+	if (!server->started)
+		return true;
+
+	server->started = false;
+
+	/* TODO: Add ability to save leases */
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_ip_range(struct l_dhcp_server *server,
+				const char *start_ip,
+				const char *end_ip)
+{
+	struct in_addr _host_addr;
+	uint32_t start;
+
+	if (unlikely(!server || !start_ip || !end_ip))
+		return false;
+
+	if (inet_aton((const char *)start_ip, &_host_addr) == 0)
+		return false;
+
+	start = _host_addr.s_addr;
+
+	if (inet_aton((const char *) end_ip, &_host_addr) == 0)
+		return false;
+
+	server->start_ip = start;
+	server->end_ip = _host_addr.s_addr;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_debug(struct l_dhcp_server *server,
+				l_dhcp_debug_cb_t function,
+				void *user_data, l_dhcp_destroy_cb_t destory)
+{
+	if (unlikely(!server))
+		return false;
+
+	server->debug_handler = function;
+	server->debug_data = user_data;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_lease_time(struct l_dhcp_server *server,
+					unsigned int lease_time)
+{
+	if (unlikely(!server))
+		return false;
+
+	server->lease_seconds = lease_time;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_event_handler(struct l_dhcp_server *server,
+					l_dhcp_server_event_cb_t handler,
+					void *user_data,
+					l_dhcp_destroy_cb_t destroy)
+{
+	if (unlikely(!server))
+		return false;
+
+	server->event_handler = handler;
+	server->user_data = user_data;
+	server->event_destroy = destroy;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_ip_address(struct l_dhcp_server *server,
+						const char *ip)
+{
+	struct in_addr ia;
+
+	if (unlikely(!server))
+		return false;
+
+	if (inet_aton(ip, &ia) < 0)
+		return false;
+
+	server->address = ia.s_addr;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_interface_name(struct l_dhcp_server *server,
+							const char *ifname)
+{
+	if (unlikely(!server || !ifname))
+		return false;
+
+	l_free(server->ifname);
+	server->ifname = l_strdup(ifname);
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_netmask(struct l_dhcp_server *server,
+						const char *mask)
+{
+	struct in_addr ia;
+
+	if (unlikely(!server || !mask))
+		return false;
+
+	if (inet_aton(mask, &ia) < 0)
+		return false;
+
+	server->netmask = ia.s_addr;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_gateway(struct l_dhcp_server *server,
+						const char *ip)
+{
+	struct in_addr ia;
+
+	if (unlikely(!server || !ip))
+		return false;
+
+	if (inet_aton(ip, &ia) < 0)
+		return false;
+
+	server->gateway = ia.s_addr;
+
+	return true;
+}
+
+LIB_EXPORT bool l_dhcp_server_set_dns(struct l_dhcp_server *server, char **dns)
+{
+	unsigned int i;
+	uint32_t *dns_list;
+
+	if (unlikely(!server || !dns))
+		return false;
+
+	dns_list = l_new(uint32_t, l_strv_length(dns) + 1);
+
+	for (i = 0; dns[i]; i++) {
+		struct in_addr ia;
+
+		if (inet_aton(dns[i], &ia) < 0)
+			goto failed;
+
+		dns_list[i] = ia.s_addr;
+	}
+
+	if (server->dns_list)
+		l_free(server->dns_list);
+
+	server->dns_list = dns_list;
+
+	return true;
+
+failed:
+	l_free(dns_list);
+	return false;
+}
diff --git a/ell/dhcp.h b/ell/dhcp.h
index c78b605..a92f6aa 100644
--- a/ell/dhcp.h
+++ b/ell/dhcp.h
@@ -31,6 +31,7 @@ extern "C" {
 
 struct l_dhcp_client;
 struct l_dhcp_lease;
+struct l_dhcp_server;
 
 /* RFC 2132 */
 enum l_dhcp_option {
@@ -56,12 +57,22 @@ enum l_dhcp_client_event {
 	L_DHCP_CLIENT_EVENT_NO_LEASE,
 };
 
+enum l_dhcp_server_event {
+	L_DHCP_SERVER_EVENT_NEW_LEASE,
+	L_DHCP_SERVER_EVENT_LEASE_EXPIRED,
+};
+
 typedef void (*l_dhcp_client_event_cb_t)(struct l_dhcp_client *client,
 						enum l_dhcp_client_event event,
 						void *userdata);
 typedef void (*l_dhcp_debug_cb_t)(const char *str, void *user_data);
 typedef void (*l_dhcp_destroy_cb_t)(void *userdata);
 
+typedef void (*l_dhcp_server_event_cb_t)(struct l_dhcp_server *server,
+					enum l_dhcp_server_event event,
+					void *user_data,
+					const struct l_dhcp_lease *lease);
+
 struct l_dhcp_client *l_dhcp_client_new(uint32_t ifindex);
 bool l_dhcp_client_add_request_option(struct l_dhcp_client *client,
 								uint8_t option);
@@ -89,6 +100,7 @@ bool l_dhcp_client_set_debug(struct l_dhcp_client *client,
 				l_dhcp_debug_cb_t function,
 				void *user_data, l_dhcp_destroy_cb_t destroy);
 
+
 char *l_dhcp_lease_get_address(const struct l_dhcp_lease *lease);
 char *l_dhcp_lease_get_gateway(const struct l_dhcp_lease *lease);
 char *l_dhcp_lease_get_netmask(const struct l_dhcp_lease *lease);
@@ -101,6 +113,30 @@ const uint8_t *l_dhcp_lease_get_mac(const struct l_dhcp_lease *lease);
 uint32_t l_dhcp_lease_get_t1(const struct l_dhcp_lease *lease);
 uint32_t l_dhcp_lease_get_t2(const struct l_dhcp_lease *lease);
 uint32_t l_dhcp_lease_get_lifetime(const struct l_dhcp_lease *lease);
+
+struct l_dhcp_server *l_dhcp_server_new(int ifindex);
+void l_dhcp_server_destroy(struct l_dhcp_server *server);
+bool l_dhcp_server_start(struct l_dhcp_server *server);
+bool l_dhcp_server_stop(struct l_dhcp_server *server);
+bool l_dhcp_server_set_ip_range(struct l_dhcp_server *server,
+				const char *start_ip,
+				const char *end_ip);
+bool l_dhcp_server_set_debug(struct l_dhcp_server *server,
+				l_dhcp_debug_cb_t function,
+				void *user_data, l_dhcp_destroy_cb_t destory);
+bool l_dhcp_server_set_event_handler(struct l_dhcp_server *server,
+					l_dhcp_server_event_cb_t handler,
+					void *user_data,
+					l_dhcp_destroy_cb_t destroy);
+bool l_dhcp_server_set_lease_time(struct l_dhcp_server *server,
+					unsigned int lease_time);
+bool l_dhcp_server_set_interface_name(struct l_dhcp_server *server,
+					const char *ifname);
+bool l_dhcp_server_set_ip_address(struct l_dhcp_server *server,
+						const char *ip);
+bool l_dhcp_server_set_netmask(struct l_dhcp_server *server, const char *mask);
+bool l_dhcp_server_set_gateway(struct l_dhcp_server *server, const char *ip);
+bool l_dhcp_server_set_dns(struct l_dhcp_server *server, char **dns);
 #ifdef __cplusplus
 }
 #endif
diff --git a/ell/ell.sym b/ell/ell.sym
index 5268b48..7a920ed 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -242,6 +242,19 @@ global:
 	l_dhcp_client_stop;
 	l_dhcp_client_set_event_handler;
 	l_dhcp_client_set_debug;
+	l_dhcp_server_new;
+	l_dhcp_server_destroy;
+	l_dhcp_server_start;
+	l_dhcp_server_stop;
+	l_dhcp_server_set_ip_range;
+	l_dhcp_server_set_debug;
+	l_dhcp_server_set_lease_time;
+	l_dhcp_server_set_event_handler;
+	l_dhcp_server_set_ip_address;
+	l_dhcp_server_set_interface_name;
+	l_dhcp_server_set_netmaks;
+	l_dhcp_server_set_gateway;
+	l_dhcp_server_set_dns;
 	/* dhcp6 */
 	l_dhcp6_client_new;
 	l_dhcp6_client_destroy;
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 02/11] dhcp-server: add transport framework
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 03/11] dhcp-server: process DISCOVER and send OFFER James Prestwood
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3021 bytes --]

---
 ell/dhcp-private.h |  4 ++++
 ell/dhcp-server.c  | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/ell/dhcp-private.h b/ell/dhcp-private.h
index 4276479..62abfca 100644
--- a/ell/dhcp-private.h
+++ b/ell/dhcp-private.h
@@ -21,6 +21,7 @@
  */
 
 struct l_dhcp_client;
+struct l_dhcp_server;
 
 enum {
 	DHCP_PORT_SERVER = 67,
@@ -141,6 +142,9 @@ bool _dhcp_client_set_transport(struct l_dhcp_client *client,
 					struct dhcp_transport *transport);
 void _dhcp_client_override_xid(struct l_dhcp_client *client, uint32_t xid);
 
+bool _dhcp_server_set_transport(struct l_dhcp_server *server,
+					struct dhcp_transport *transport);
+
 struct l_dhcp_lease {
 	uint32_t address;
 	uint32_t server_address;
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 97479ae..b59ec69 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -61,6 +61,8 @@ struct l_dhcp_server {
 	l_dhcp_server_event_cb_t event_handler;
 	void *user_data;
 	l_dhcp_destroy_cb_t event_destroy;
+
+	struct dhcp_transport *transport;
 };
 
 #define MAC "%02x:%02x:%02x:%02x:%02x:%02x"
@@ -79,6 +81,23 @@ struct l_dhcp_server {
 	l_util_debug(server->debug_handler, server->debug_data,		\
 			"%s:%i " fmt, __func__, __LINE__, ## args)
 
+static void listener_event(const void *data, size_t len, void *user_data)
+{
+}
+
+bool _dhcp_server_set_transport(struct l_dhcp_server *server,
+					struct dhcp_transport *transport)
+{
+	if (unlikely(!server))
+		return false;
+
+	if (server->transport)
+		_dhcp_transport_free(server->transport);
+
+	server->transport = transport;
+	return true;
+}
+
 LIB_EXPORT struct l_dhcp_server *l_dhcp_server_new(int ifindex)
 {
 	struct l_dhcp_server *server = l_new(struct l_dhcp_server, 1);
@@ -108,6 +127,7 @@ LIB_EXPORT void l_dhcp_server_destroy(struct l_dhcp_server *server)
 	if (server->event_destroy)
 		server->event_destroy(server->user_data);
 
+	_dhcp_transport_free(server->transport);
 	l_free(server->ifname);
 
 	l_queue_destroy(server->lease_list,
@@ -170,8 +190,22 @@ LIB_EXPORT bool l_dhcp_server_start(struct l_dhcp_server *server)
 			return false;
 	}
 
+	if (!server->transport) {
+		server->transport = _dhcp_default_transport_new(server->ifindex,
+					server->ifname, DHCP_PORT_SERVER);
+		if (!server->transport)
+			return false;
+	}
+
 	SERVER_DEBUG("Starting DHCP server on %s", server->ifname);
 
+	if (server->transport->open)
+		if (server->transport->open(server->transport, 0) < 0)
+			return false;
+
+	_dhcp_transport_set_rx_callback(server->transport, listener_event,
+						server);
+
 	server->started = true;
 
 	return true;
@@ -185,6 +219,9 @@ LIB_EXPORT bool l_dhcp_server_stop(struct l_dhcp_server *server)
 	if (!server->started)
 		return true;
 
+	if (server->transport->close)
+		server->transport->close(server->transport);
+
 	server->started = false;
 
 	/* TODO: Add ability to save leases */
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 03/11] dhcp-server: process DISCOVER and send OFFER
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
  2020-10-16 20:23 ` [PATCH v4 02/11] dhcp-server: add transport framework James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 04/11] dhcp-server: add REQUEST processing James Prestwood
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 9109 bytes --]

Add handling of DISCOVER messages and ability to send offers.
---
 ell/dhcp-server.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 336 insertions(+)

diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index b59ec69..c55471a 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -41,6 +41,17 @@
 /* 8 hours */
 #define DEFAULT_DHCP_LEASE_SEC (8*60*60)
 
+/* 5 minutes  */
+#define OFFER_TIME (5*60)
+
+static const uint8_t MAC_BCAST_ADDR[ETH_ALEN] __attribute__((aligned(2))) = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t MAC_ANY_ADDR[ETH_ALEN] __attribute__((aligned(2))) = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
 struct l_dhcp_server {
 	bool started;
 	int ifindex;
@@ -81,8 +92,333 @@ struct l_dhcp_server {
 	l_util_debug(server->debug_handler, server->debug_data,		\
 			"%s:%i " fmt, __func__, __LINE__, ## args)
 
+static bool is_expired_lease(struct l_dhcp_lease *lease)
+{
+	if (lease->lifetime < time(NULL))
+		return true;
+
+	return false;
+}
+
+static bool match_lease_mac(const void *data, const void *user_data)
+{
+	const struct l_dhcp_lease *lease = data;
+	const uint8_t *mac = user_data;
+
+	return !memcmp(lease->mac, mac, 6);
+}
+
+static struct l_dhcp_lease *find_lease_by_mac(struct l_dhcp_server *server,
+						const uint8_t *mac)
+{
+	return l_queue_find(server->lease_list, match_lease_mac, mac);
+}
+
+/* Clear the old lease and create the new one */
+static int get_lease(struct l_dhcp_server *server, uint32_t yiaddr,
+				const uint8_t *mac,
+				struct l_dhcp_lease **lease_out)
+{
+	struct l_dhcp_lease *lease;
+
+	if (yiaddr == 0)
+		return -ENXIO;
+
+	if (yiaddr < server->start_ip)
+		return -ENXIO;
+
+	if (yiaddr > server->end_ip)
+		return -ENXIO;
+
+	if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
+		return -ENXIO;
+
+	if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
+		return -ENXIO;
+
+	lease = find_lease_by_mac(server, mac);
+
+	if (lease) {
+		l_queue_remove(server->lease_list, lease);
+
+		*lease_out = lease;
+
+		return 0;
+	}
+
+	*lease_out = l_new(struct l_dhcp_lease, 1);
+	if (!*lease_out)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int compare_lifetime(const void *a, const void *b, void *user_data)
+{
+	const struct l_dhcp_lease *lease1 = a;
+	const struct l_dhcp_lease *lease2 = b;
+
+	return lease2->lifetime - lease1->lifetime;
+}
+
+static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server,
+					uint32_t expire, const uint8_t *chaddr,
+					uint32_t yiaddr)
+{
+	struct l_dhcp_lease *lease = NULL;
+	int ret;
+
+	ret = get_lease(server, yiaddr, chaddr, &lease);
+	if (ret != 0)
+		return NULL;
+
+	memset(lease, 0, sizeof(*lease));
+
+	memcpy(lease->mac, chaddr, ETH_ALEN);
+	lease->address = yiaddr;
+
+	if (expire == 0)
+		lease->lifetime = time(NULL) + server->lease_seconds;
+	else
+		lease->lifetime = expire;
+
+	l_queue_insert(server->lease_list, lease, compare_lifetime, NULL);
+
+	SERVER_DEBUG("added lease IP %s for "MAC " lifetime=%u",
+			IP_STR(yiaddr), MAC_STR(chaddr),
+			lease->lifetime);
+
+	return lease;
+}
+
+static bool match_lease_ip(const void *data, const void *user_data)
+{
+	const struct l_dhcp_lease *lease = data;
+
+	return lease->address == L_PTR_TO_UINT(user_data);
+}
+
+static struct l_dhcp_lease *find_lease_by_ip(struct l_dhcp_server *server,
+						uint32_t nip)
+{
+	return l_queue_find(server->lease_list, match_lease_ip,
+				L_INT_TO_PTR(nip));
+}
+
+static bool check_requested_ip(struct l_dhcp_server *server,
+				uint32_t requested_nip)
+{
+	struct l_dhcp_lease *lease;
+
+	if (requested_nip == 0)
+		return false;
+
+	if (requested_nip < server->start_ip)
+		return false;
+
+	if (requested_nip > server->end_ip)
+		return false;
+
+	lease = find_lease_by_ip(server, requested_nip);
+	if (!lease)
+		return true;
+
+	if (!is_expired_lease(lease))
+		return false;
+
+	return true;
+}
+
+/* Check if the IP is taken; if it is, add it to the lease table */
+static bool arp_check(uint32_t nip, const uint8_t *safe_mac)
+{
+	/* TODO: Add ARP checking */
+	return true;
+}
+
+static uint32_t find_free_or_expired_ip(struct l_dhcp_server *server,
+						const uint8_t *safe_mac)
+{
+	uint32_t ip_addr;
+	struct l_dhcp_lease *lease;
+
+	for (ip_addr = server->start_ip; ip_addr <= server->end_ip; ip_addr++) {
+		/* e.g. 192.168.55.0 */
+		if ((ntohl(ip_addr) & 0xff) == 0)
+			continue;
+
+		/* e.g. 192.168.55.255 */
+		if ((ntohl(ip_addr) & 0xff) == 0xff)
+			continue;
+
+		lease = find_lease_by_ip(server, ip_addr);
+		if (lease)
+			continue;
+
+		if (arp_check(ip_addr, safe_mac))
+			return ip_addr;
+	}
+
+	lease = l_queue_peek_tail(server->lease_list);
+	if (!lease)
+		return 0;
+
+	if (!is_expired_lease(lease))
+		return 0;
+
+	if (!arp_check(lease->address, safe_mac))
+		return 0;
+
+	return lease->address;
+}
+
+static void server_message_init(struct l_dhcp_server *server,
+				const struct dhcp_message *client_msg,
+				struct dhcp_message *reply)
+{
+	reply->xid = client_msg->xid;
+	memcpy(reply->chaddr, client_msg->chaddr, sizeof(client_msg->chaddr));
+	reply->flags = client_msg->flags;
+	reply->giaddr = client_msg->giaddr;
+	reply->ciaddr = client_msg->ciaddr;
+}
+
+static void add_server_options(struct l_dhcp_server *server,
+				struct dhcp_message_builder *builder)
+{
+	int i;
+
+	if (server->netmask)
+		_dhcp_message_builder_append(builder, L_DHCP_OPTION_SUBNET_MASK,
+						4, &server->netmask);
+
+	if (server->gateway)
+		_dhcp_message_builder_append(builder, L_DHCP_OPTION_ROUTER,
+						4, &server->gateway);
+
+	if (server->dns_list) {
+		for (i = 0; server->dns_list[i]; i++);
+
+		_dhcp_message_builder_append(builder,
+					L_DHCP_OPTION_DOMAIN_NAME_SERVER,
+					i * 4, server->dns_list);
+	}
+}
+
+static void send_offer(struct l_dhcp_server *server,
+			const struct dhcp_message *client_msg,
+			struct l_dhcp_lease *lease, uint32_t requested_ip)
+{
+	struct dhcp_message_builder builder;
+	size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE;
+	L_AUTO_FREE_VAR(struct dhcp_message *, reply);
+	uint32_t lease_time = L_CPU_TO_BE32(server->lease_seconds);
+
+	reply = (struct dhcp_message *) l_new(uint8_t, len);
+
+	if (lease)
+		reply->yiaddr = lease->address;
+	else if (check_requested_ip(server, requested_ip))
+		reply->yiaddr = requested_ip;
+	else
+		reply->yiaddr = find_free_or_expired_ip(server,
+							client_msg->chaddr);
+
+	if (!reply->yiaddr) {
+		SERVER_DEBUG("Could not find lease or send offer");
+		return;
+	}
+
+	lease = add_lease(server, OFFER_TIME, client_msg->chaddr,
+				reply->yiaddr);
+	if (!lease) {
+		SERVER_DEBUG("No free IP addresses, OFFER abandoned");
+		return;
+	}
+
+	server_message_init(server, client_msg, reply);
+
+	_dhcp_message_builder_init(&builder, reply, len,
+					DHCP_MESSAGE_TYPE_OFFER);
+
+	_dhcp_message_builder_append(&builder,
+					L_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
+					4, &lease_time);
+
+	_dhcp_message_builder_append(&builder, L_DHCP_OPTION_SERVER_IDENTIFIER,
+					4, &server->address);
+
+	add_server_options(server, &builder);
+
+	_dhcp_message_builder_finalize(&builder, &len);
+
+	SERVER_DEBUG("Sending OFFER of %s to "MAC, IP_STR(reply->yiaddr),
+			MAC_STR(reply->chaddr));
+
+	if (server->transport->l2_send(server->transport, server->address,
+					DHCP_PORT_SERVER,
+					reply->ciaddr, DHCP_PORT_CLIENT,
+					reply->chaddr, reply, len) < 0)
+		SERVER_DEBUG("Failed to send OFFER");
+}
+
 static void listener_event(const void *data, size_t len, void *user_data)
 {
+	struct l_dhcp_server *server = user_data;
+	const struct dhcp_message *message = data;
+	struct dhcp_message_iter iter;
+	uint8_t t, l;
+	const void *v;
+	struct l_dhcp_lease *lease;
+	uint8_t type = 0;
+	uint32_t server_id_opt = 0;
+	uint32_t requested_ip_opt = 0;
+
+	SERVER_DEBUG("");
+
+	if (!_dhcp_message_iter_init(&iter, message, len))
+		return;
+
+	while (_dhcp_message_iter_next(&iter, &t, &l, &v)) {
+		switch (t) {
+		case DHCP_OPTION_MESSAGE_TYPE:
+			if (l == 1)
+				type = l_get_u8(v);
+
+			break;
+		case L_DHCP_OPTION_SERVER_IDENTIFIER:
+			if (l == 4)
+				server_id_opt = l_get_u32(v);
+
+			if (server->address != server_id_opt)
+				return;
+
+			break;
+		case L_DHCP_OPTION_REQUESTED_IP_ADDRESS:
+			if (l == 4)
+				requested_ip_opt = l_get_u32(v);
+
+			break;
+		}
+	}
+
+	if (type == 0)
+		return;
+
+	lease = find_lease_by_mac(server, message->chaddr);
+	if (!lease)
+		SERVER_DEBUG("No lease found for "MAC,
+					MAC_STR(message->chaddr));
+
+	switch (type) {
+	case DHCP_MESSAGE_TYPE_DISCOVER:
+		SERVER_DEBUG("Received DISCOVER, requested IP %s",
+					IP_STR(requested_ip_opt));
+
+		send_offer(server, message, lease, requested_ip_opt);
+
+		break;
+	}
 }
 
 bool _dhcp_server_set_transport(struct l_dhcp_server *server,
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 04/11] dhcp-server: add REQUEST processing
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
  2020-10-16 20:23 ` [PATCH v4 02/11] dhcp-server: add transport framework James Prestwood
  2020-10-16 20:23 ` [PATCH v4 03/11] dhcp-server: process DISCOVER and send OFFER James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 05/11] dhcp-server: handle DECLINE messages James Prestwood
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3364 bytes --]

Handle REQUEST packets and implement ACK/NAK responses.
---
 ell/dhcp-server.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index c55471a..093b153 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -362,6 +362,73 @@ static void send_offer(struct l_dhcp_server *server,
 		SERVER_DEBUG("Failed to send OFFER");
 }
 
+static void send_nak(struct l_dhcp_server *server,
+			const struct dhcp_message *client_msg)
+{
+	struct dhcp_message_builder builder;
+	size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE;
+	L_AUTO_FREE_VAR(struct dhcp_message *, reply);
+
+	reply = (struct dhcp_message *) l_new(uint8_t, len);
+
+	server_message_init(server, client_msg, reply);
+
+	_dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_NAK);
+
+	_dhcp_message_builder_finalize(&builder, &len);
+
+	if (server->transport->l2_send(server->transport, server->address,
+					DHCP_PORT_SERVER, reply->ciaddr,
+					DHCP_PORT_CLIENT, MAC_BCAST_ADDR,
+					reply, len) < 0)
+		SERVER_DEBUG("Failed to send NACK");
+}
+
+static void send_ack(struct l_dhcp_server *server,
+			const struct dhcp_message *client_msg, uint32_t dest)
+{
+	struct dhcp_message_builder builder;
+	size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE;
+	L_AUTO_FREE_VAR(struct dhcp_message *, reply);
+	uint32_t lease_time = L_CPU_TO_BE32(server->lease_seconds);
+	struct l_dhcp_lease *lease;
+
+	reply = (struct dhcp_message *) l_new(uint8_t, len);
+
+	server_message_init(server, client_msg, reply);
+
+	_dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_ACK);
+
+	reply->yiaddr = dest;
+
+	_dhcp_message_builder_append(&builder,
+					L_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
+					4, &lease_time);
+
+	add_server_options(server, &builder);
+
+	_dhcp_message_builder_append(&builder, L_DHCP_OPTION_SERVER_IDENTIFIER,
+					4, &server->address);
+
+	_dhcp_message_builder_finalize(&builder, &len);
+
+	SERVER_DEBUG("Sending ACK to %s", IP_STR(reply->yiaddr));
+
+	if (server->transport->l2_send(server->transport, server->address,
+					DHCP_PORT_SERVER, reply->ciaddr,
+					DHCP_PORT_CLIENT,
+					reply->chaddr, reply, len) < 0) {
+		SERVER_DEBUG("Failed to send ACK");
+		return;
+	}
+
+	lease = add_lease(server, 0, reply->chaddr, reply->yiaddr);
+
+	if (server->event_handler)
+		server->event_handler(server, L_DHCP_SERVER_EVENT_NEW_LEASE,
+					server->user_data, lease);
+}
+
 static void listener_event(const void *data, size_t len, void *user_data)
 {
 	struct l_dhcp_server *server = user_data;
@@ -417,6 +484,26 @@ static void listener_event(const void *data, size_t len, void *user_data)
 
 		send_offer(server, message, lease, requested_ip_opt);
 
+		break;
+	case DHCP_MESSAGE_TYPE_REQUEST:
+		SERVER_DEBUG("Received REQUEST, requested IP %s",
+				IP_STR(requested_ip_opt));
+
+		if (requested_ip_opt == 0) {
+			requested_ip_opt = message->ciaddr;
+			if (requested_ip_opt == 0)
+				break;
+		}
+
+		if (lease && requested_ip_opt == lease->address) {
+			send_ack(server, message, lease->address);
+			break;
+		}
+
+		if (server_id_opt || !lease) {
+			send_nak(server, message);
+			break;
+		}
 		break;
 	}
 }
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 05/11] dhcp-server: handle DECLINE messages
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (2 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 04/11] dhcp-server: add REQUEST processing James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 06/11] dhcp-server: add RELEASE message support James Prestwood
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1349 bytes --]

Process DECLINE messages from clients and remove any pending
leases that were created during DISCOVER.
---
 ell/dhcp-server.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 093b153..3cfbf4b 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -114,6 +114,18 @@ static struct l_dhcp_lease *find_lease_by_mac(struct l_dhcp_server *server,
 	return l_queue_find(server->lease_list, match_lease_mac, mac);
 }
 
+static void remove_lease(struct l_dhcp_server *server,
+				struct l_dhcp_lease *lease)
+{
+	l_queue_remove(server->lease_list, lease);
+
+	if (server->event_handler)
+		server->event_handler(server, L_DHCP_SERVER_EVENT_LEASE_EXPIRED,
+					server->user_data, lease);
+
+	_dhcp_lease_free(lease);
+}
+
 /* Clear the old lease and create the new one */
 static int get_lease(struct l_dhcp_server *server, uint32_t yiaddr,
 				const uint8_t *mac,
@@ -505,6 +517,16 @@ static void listener_event(const void *data, size_t len, void *user_data)
 			break;
 		}
 		break;
+	case DHCP_MESSAGE_TYPE_DECLINE:
+		SERVER_DEBUG("Received DECLINE");
+
+		if (!server_id_opt || !requested_ip_opt || !lease)
+			break;
+
+		if (requested_ip_opt == lease->address)
+			remove_lease(server, lease);
+
+		break;
 	}
 }
 
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 06/11] dhcp-server: add RELEASE message support
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (3 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 05/11] dhcp-server: handle DECLINE messages James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 07/11] dhcp-server: add INFORM message handling James Prestwood
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1534 bytes --]

Handle RELEASE messages from clients. If there is a valid lease
set the expire time to now which releases the IP but also allows
the client to reconnect and maintain the same IP so long as
it hadn't been leased out again.
---
 ell/dhcp-server.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 3cfbf4b..9327456 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -203,6 +203,20 @@ static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server,
 	return lease;
 }
 
+static void lease_set_expire(struct l_dhcp_server *server,
+			struct l_dhcp_lease *lease, uint32_t expire)
+{
+	l_queue_remove(server->lease_list, lease);
+
+	lease->lifetime = expire;
+
+	l_queue_insert(server->lease_list, lease, compare_lifetime, NULL);
+
+	if (server->event_handler)
+		server->event_handler(server, L_DHCP_SERVER_EVENT_LEASE_EXPIRED,
+					server->user_data, lease);
+}
+
 static bool match_lease_ip(const void *data, const void *user_data)
 {
 	const struct l_dhcp_lease *lease = data;
@@ -526,6 +540,16 @@ static void listener_event(const void *data, size_t len, void *user_data)
 		if (requested_ip_opt == lease->address)
 			remove_lease(server, lease);
 
+		break;
+	case DHCP_MESSAGE_TYPE_RELEASE:
+		SERVER_DEBUG("Received RELEASE");
+
+		if (!server_id_opt || !lease)
+			break;
+
+		if (message->ciaddr == lease->address)
+			lease_set_expire(server, lease, time(NULL));
+
 		break;
 	}
 }
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 07/11] dhcp-server: add INFORM message handling
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (4 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 06/11] dhcp-server: add RELEASE message support James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 08/11] unit: update dhcp test to use new builder APIs James Prestwood
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1636 bytes --]

This allows clients to request local configuration details
if their IP is already configured.
---
 ell/dhcp-server.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 9327456..1ce20aa 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -388,6 +388,30 @@ static void send_offer(struct l_dhcp_server *server,
 		SERVER_DEBUG("Failed to send OFFER");
 }
 
+static void send_inform(struct l_dhcp_server *server,
+				const struct dhcp_message *client_msg)
+{
+	struct dhcp_message_builder builder;
+	size_t len = sizeof(struct dhcp_message) + DHCP_MIN_OPTIONS_SIZE;
+	L_AUTO_FREE_VAR(struct dhcp_message *, reply);
+
+	reply = (struct dhcp_message *) l_new(uint8_t, len);
+
+	server_message_init(server, client_msg, reply);
+
+	_dhcp_message_builder_init(&builder, reply, len, DHCP_MESSAGE_TYPE_ACK);
+
+	add_server_options(server, &builder);
+
+	_dhcp_message_builder_finalize(&builder, &len);
+
+	if (server->transport->l2_send(server->transport, server->address,
+					DHCP_PORT_SERVER, reply->ciaddr,
+					DHCP_PORT_CLIENT, reply->chaddr,
+					reply, len) < 0)
+		SERVER_DEBUG("Failed to send INFORM");
+}
+
 static void send_nak(struct l_dhcp_server *server,
 			const struct dhcp_message *client_msg)
 {
@@ -551,6 +575,11 @@ static void listener_event(const void *data, size_t len, void *user_data)
 			lease_set_expire(server, lease, time(NULL));
 
 		break;
+	case DHCP_MESSAGE_TYPE_INFORM:
+		SERVER_DEBUG("Received INFORM");
+
+		send_inform(server, message);
+		break;
 	}
 }
 
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 08/11] unit: update dhcp test to use new builder APIs
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (5 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 07/11] dhcp-server: add INFORM message handling James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 09/11] examples: basic DHCP server example James Prestwood
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3497 bytes --]

---
 unit/test-dhcp.c | 83 +++++++++++++++++++++---------------------------
 1 file changed, 37 insertions(+), 46 deletions(-)

diff --git a/unit/test-dhcp.c b/unit/test-dhcp.c
index 6ae8d71..955ed29 100644
--- a/unit/test-dhcp.c
+++ b/unit/test-dhcp.c
@@ -33,7 +33,7 @@
 #include <ell/ell.h>
 #include "ell/dhcp-private.h"
 
-static bool verbose = false;
+static bool verbose = true;
 static uint8_t client_packet[1024];
 static size_t client_packet_len;
 
@@ -365,9 +365,13 @@ static void test_option_8(const void *data)
 
 static void test_option_set(const void *data)
 {
-	static uint8_t result[64] = {'A', 'B', 'C', 'D' };
+	struct dhcp_message_builder builder;
+	struct dhcp_message *message;
+	size_t outlen;
+	uint8_t *msg_out;
+	unsigned int i;
+	static uint8_t result[sizeof(struct dhcp_message) + 64];
 	static uint8_t options[64] = {
-			'A', 'B', 'C', 'D',
 			160, 2, 0x11, 0x12,
 			0,
 			31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
@@ -377,51 +381,38 @@ static void test_option_set(const void *data)
 			255
 	};
 
-	size_t len, oldlen;
-	int pos, i;
-	uint8_t *opt;
+	message = (struct dhcp_message *)result;
 
-	assert(_dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
-
-	len = 0;
-	opt = &result[0];
-	assert(_dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
-	assert(opt == &result[0] && len == 0);
-
-	assert(_dhcp_option_append(&opt, &len, 0, 0, NULL) == -ENOBUFS);
-	assert(opt == &result[0] && len == 0);
-
-	opt = &result[4];
-	len = 1;
-	assert(_dhcp_option_append(&opt, &len, 0, 0, NULL) >= 0);
-	assert(opt == &result[5] && len == 0);
-
-	pos = 4;
-	len = 60;
-	while (pos < 64 && options[pos] != 255) {
-		opt = &result[pos];
-		oldlen = len;
-
-		assert(_dhcp_option_append(&opt, &len, options[pos],
-						options[pos + 1],
-						&options[pos + 2]) >= 0);
-
-		if (options[pos] == 0) {
-			assert(opt == &result[pos + 1]);
-			assert(len == oldlen - 1);
-			pos++;
-		} else {
-			assert(opt == &result[pos + 2 + options[pos + 1]]);
-			assert(len == oldlen - 2 - options[pos + 1]);
-			pos += 2 + options[pos + 1];
-		}
-	}
+	/* test a few failure conditions */
+	assert(!_dhcp_message_builder_init(NULL, NULL, 0, 0));
+	assert(!_dhcp_message_builder_init(&builder, message, 0, 0));
+
+	_dhcp_message_builder_init(&builder, message, sizeof(result),
+					DHCP_MESSAGE_TYPE_DISCOVER);
+	_dhcp_message_builder_append(&builder, 160, 2, options + 2);
+	_dhcp_message_builder_append(&builder, 0, 0, NULL);
+	_dhcp_message_builder_append(&builder, 31, 8, options + 7);
+	_dhcp_message_builder_append(&builder, 0, 0, NULL);
+	_dhcp_message_builder_append(&builder, 55, 3, options + 18);
+	_dhcp_message_builder_append(&builder, 17, 7, options + 23);
+	msg_out = _dhcp_message_builder_finalize(&builder, &outlen);
 
-	for (i = 0; i < pos; i++) {
-		if (verbose)
-			l_info("%2d: 0x%02x(0x%02x)\n",
-					i, result[i], options[i]);
-		assert(result[i] == options[i]);
+	/*
+	 * The builde APIs automatically append the type passed in during init
+	 * so we can skip over that in order to test the expected static data
+	 */
+
+	msg_out += sizeof(struct dhcp_message) + 3;
+
+	for (i = 0; i < outlen - sizeof(struct dhcp_message); i++) {
+		if (msg_out[i] != options[i]) {
+			if (verbose) {
+				l_info("byte[%d] did not match 0x%02x 0x%02x",
+					i, msg_out[i], options[i]);
+			}
+
+			assert(false);
+		}
 	}
 }
 
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 09/11] examples: basic DHCP server example
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (6 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 08/11] unit: update dhcp test to use new builder APIs James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 10/11] unit: add complete client/server dhcp test James Prestwood
  2020-10-16 20:23 ` [PATCH v4 11/11] dhcp: Add support for setting address James Prestwood
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 4992 bytes --]

---
 .gitignore             |   1 +
 Makefile.am            |   4 +-
 examples/dhcp-server.c | 129 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+), 1 deletion(-)
 create mode 100644 examples/dhcp-server.c

diff --git a/.gitignore b/.gitignore
index c3d2790..e963359 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ examples/dbus-client
 examples/glib-eventloop
 examples/dhcp-client
 examples/dhcp6-client
+examples/dhcp-server
 test-suite.log
 tools/certchain-verify
 tools/genl-discover
diff --git a/Makefile.am b/Makefile.am
index 9bdf4dc..adedd1f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -326,7 +326,8 @@ unit_test_data_files = unit/settings.test unit/dbus.conf
 
 examples = examples/dbus-service examples/https-client-test \
 		examples/https-server-test examples/dbus-client \
-		examples/dhcp-client examples/dhcp6-client
+		examples/dhcp-client examples/dhcp6-client \
+		examples/dhcp-server
 
 if GLIB
 examples += examples/glib-eventloop
@@ -344,6 +345,7 @@ examples_glib_eventloop_CFLAGS = @GLIB_CFLAGS@
 examples_glib_eventloop_LDADD = ell/libell-private.la @GLIB_LIBS@
 examples_dhcp_client_LDADD = ell/libell-private.la
 examples_dhcp6_client_LDADD = ell/libell-private.la
+examples_dhcp_server_LDADD = ell/libell-private.la
 
 noinst_PROGRAMS += tools/certchain-verify tools/genl-discover \
 		   tools/genl-watch tools/genl-request tools/gpio
diff --git a/examples/dhcp-server.c b/examples/dhcp-server.c
new file mode 100644
index 0000000..33c0983
--- /dev/null
+++ b/examples/dhcp-server.c
@@ -0,0 +1,129 @@
+/*
+ *
+ *  Embedded Linux library
+ *
+ *  Copyright (C) 2020  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <linux/if_arp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ell/ell.h>
+
+static void do_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	l_info("%s%s", prefix, str);
+}
+
+static void signal_handler(uint32_t signo, void *user_data)
+{
+	switch (signo) {
+	case SIGINT:
+	case SIGTERM:
+		l_info("Terminate");
+		l_main_quit();
+		break;
+	}
+}
+
+static void destroy_handler(void *data)
+{
+	l_info("DHCP server destroyed");
+}
+
+static void event_handler(struct l_dhcp_server *server,
+					enum l_dhcp_server_event event,
+					void *userdata,
+					const struct l_dhcp_lease *lease)
+{
+	const uint8_t *mac;
+	char *ip;
+
+	switch (event) {
+	case L_DHCP_SERVER_EVENT_NEW_LEASE:
+		mac = l_dhcp_lease_get_mac(lease);
+		ip = l_dhcp_lease_get_address(lease);
+
+		l_info("New lease client %02x:%02x:%02x:%02x:%02x:%02x %s",
+				mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+				ip);
+		l_free(ip);
+		break;
+	case L_DHCP_SERVER_EVENT_LEASE_EXPIRED:
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	struct l_dhcp_server *server;
+	int ifindex;
+	uint8_t mac[6];
+	char *dns[] = { "192.168.1.1", NULL };
+
+	if (argc < 2) {
+		printf("Usage: %s <interface index>\n", argv[0]);
+		exit(0);
+	}
+
+	ifindex = atoi(argv[1]);
+
+	if (!l_net_get_mac_address(ifindex, mac)) {
+		printf("Unable to get address from interface %d\n", ifindex);
+		exit(0);
+	}
+
+	if (!l_main_init())
+		return -1;
+
+	l_log_set_stderr();
+	l_debug_enable("*");
+
+	server = l_dhcp_server_new(ifindex);
+	l_dhcp_server_set_ip_range(server, "192.168.1.2", "192.168.1.100");
+	l_dhcp_server_set_netmask(server, "255.255.255.0");
+	l_dhcp_server_set_gateway(server, "192.168.1.1");
+	l_dhcp_server_set_dns(server, dns);
+	l_dhcp_server_set_lease_time(server, 10);
+	l_dhcp_server_set_debug(server, do_debug, "[DHCP SERV] ", NULL);
+	l_dhcp_server_set_event_handler(server, event_handler, NULL,
+						destroy_handler);
+	l_dhcp_server_start(server);
+
+	l_main_run_with_signal(signal_handler, NULL);
+
+	l_dhcp_server_stop(server);
+	l_dhcp_server_destroy(server);
+	l_main_exit();
+
+	return 0;
+}
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 10/11] unit: add complete client/server dhcp test
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (7 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 09/11] examples: basic DHCP server example James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  2020-10-16 20:23 ` [PATCH v4 11/11] dhcp: Add support for setting address James Prestwood
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 5627 bytes --]

---
 unit/test-dhcp.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/unit/test-dhcp.c b/unit/test-dhcp.c
index 955ed29..a481247 100644
--- a/unit/test-dhcp.c
+++ b/unit/test-dhcp.c
@@ -36,6 +36,8 @@
 static bool verbose = true;
 static uint8_t client_packet[1024];
 static size_t client_packet_len;
+static uint8_t server_packet[1024];
+static size_t server_packet_len;
 
 static void test_request_option(const void *data)
 {
@@ -736,6 +738,163 @@ static void test_discover(const void *data)
 	l_dhcp_client_destroy(client);
 }
 
+static bool l2_send_called = false;
+
+static int fake_transport_server_l2_send(struct dhcp_transport *s,
+					uint32_t source_ip,
+					uint16_t source_port,
+					uint32_t dest_ip,
+					uint16_t dest_port,
+					const uint8_t *dest_arp,
+					const void *data, size_t len)
+{
+	assert(len <= sizeof(server_packet));
+	memcpy(server_packet, data, len);
+	server_packet_len = len;
+
+	l2_send_called = true;
+
+	return 0;
+}
+
+static void server_event_handler(struct l_dhcp_server *server,
+					enum l_dhcp_server_event event,
+					void *userdata,
+					const struct l_dhcp_lease *lease)
+{
+	const uint8_t expected_mac[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const uint8_t *mac;
+	char *ip;
+
+	switch (event) {
+	case L_DHCP_SERVER_EVENT_NEW_LEASE:
+		mac = l_dhcp_lease_get_mac(lease);
+		ip = l_dhcp_lease_get_address(lease);
+
+		assert(!memcmp(mac, expected_mac, sizeof(expected_mac)));
+		assert(!strcmp(ip, "192.168.1.2"));
+
+		l_free(ip);
+		break;
+	case L_DHCP_SERVER_EVENT_LEASE_EXPIRED:
+		break;
+	}
+}
+
+static void do_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	l_info("%s%s", prefix, str);
+}
+
+static void test_complete_run(const void *data)
+{
+	static const uint8_t addr[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	char *dns[] = { "192.168.1.1", "192.168.1.254", NULL };
+	struct l_dhcp_client *client;
+	struct l_dhcp_server *server;
+	struct dhcp_transport *cli_transport = l_new(struct dhcp_transport, 1);
+	struct dhcp_transport *srv_transport = l_new(struct dhcp_transport, 1);
+	const struct l_dhcp_lease *cli_lease;
+	/* client IP address */
+	char *cli_addr;
+	/* servers IP address */
+	char *srv_addr;
+	char *tmp_addr;
+	char **dns_list;
+
+	server = l_dhcp_server_new(41);
+
+	assert(l_dhcp_server_set_interface_name(server, "fake"));
+	assert(l_dhcp_server_set_ip_address(server, "192.168.1.1"));
+	assert(l_dhcp_server_set_ip_range(server, "192.168.1.2",
+						"192.168.1.100"));
+	assert(l_dhcp_server_set_netmask(server, "255.255.255.0"));
+	assert(l_dhcp_server_set_gateway(server, "192.168.1.1"));
+	assert(l_dhcp_server_set_dns(server, dns));
+	assert(l_dhcp_server_set_event_handler(server, server_event_handler,
+						NULL, NULL));
+
+	if (verbose)
+		l_dhcp_server_set_debug(server, do_debug, "[DHCP SERV] ", NULL);
+
+	srv_transport->ifindex = 41;
+	srv_transport->l2_send = fake_transport_server_l2_send;
+
+	assert(_dhcp_server_set_transport(server, srv_transport));
+
+	assert(l_dhcp_server_start(server));
+
+	client = l_dhcp_client_new(42);
+
+	assert(l_dhcp_client_set_address(client, ARPHRD_ETHER, addr, 6));
+	assert(l_dhcp_client_set_interface_name(client, "fake"));
+	assert(l_dhcp_client_set_hostname(client, "<hostname>"));
+	_dhcp_client_override_xid(client, 0x4d7c67c6);
+	assert(l_dhcp_client_set_event_handler(client,
+				event_handler_lease_obtained, NULL, NULL));
+
+	if (verbose)
+		l_dhcp_client_set_debug(client, do_debug, "[DHCP] ", NULL);
+
+	cli_transport->send = fake_transport_send;
+	cli_transport->l2_send = fake_transport_l2_send;
+	cli_transport->ifindex = 42;
+
+	assert(_dhcp_client_set_transport(client, cli_transport));
+
+	assert(l_dhcp_client_start(client));
+
+	/* RX DISCOVER */
+	srv_transport->rx_cb(client_packet, client_packet_len, server);
+	assert(l2_send_called);
+	l2_send_called = false;
+
+	/* RX OFFER */
+	cli_transport->rx_cb(server_packet, server_packet_len, client);
+
+	/* RX REQUEST */
+	srv_transport->rx_cb(client_packet, client_packet_len, server);
+	assert(l2_send_called);
+	l2_send_called = false;
+
+	/* RX ACK */
+	cli_transport->rx_cb(server_packet, server_packet_len, client);
+
+	assert(event_handler_called);
+
+	cli_lease = l_dhcp_client_get_lease(client);
+	assert(cli_lease);
+	cli_addr = l_dhcp_lease_get_address(cli_lease);
+	assert(cli_addr);
+	assert(!strcmp(cli_addr, "192.168.1.2"));
+	srv_addr = l_dhcp_lease_get_server_id(cli_lease);
+	assert(!strcmp(srv_addr, "192.168.1.1"));
+	l_free(srv_addr);
+	l_free(cli_addr);
+
+	tmp_addr = l_dhcp_lease_get_gateway(cli_lease);
+	assert(!strcmp(tmp_addr, "192.168.1.1"));
+	l_free(tmp_addr);
+
+	tmp_addr = l_dhcp_lease_get_netmask(cli_lease);
+	assert(!strcmp(tmp_addr, "255.255.255.0"));
+	l_free(tmp_addr);
+
+	dns_list = l_dhcp_lease_get_dns(cli_lease);
+	assert(dns_list && dns_list[0] && dns_list[1]);
+	assert(!strcmp(dns_list[0], "192.168.1.1"));
+	assert(!strcmp(dns_list[1], "192.168.1.254"));
+	l_strv_free(dns_list);
+
+	l_dhcp_client_stop(client);
+	l_dhcp_client_destroy(client);
+
+	l_dhcp_server_stop(server);
+	l_dhcp_server_destroy(server);
+}
+
 int main(int argc, char *argv[])
 {
 	l_test_init(&argc, &argv);
@@ -759,5 +918,7 @@ int main(int argc, char *argv[])
 
 	l_test_add("discover", test_discover, NULL);
 
+	l_test_add("complete run", test_complete_run, NULL);
+
 	return l_test_run();
 }
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 11/11] dhcp: Add support for setting address
  2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
                   ` (8 preceding siblings ...)
  2020-10-16 20:23 ` [PATCH v4 10/11] unit: add complete client/server dhcp test James Prestwood
@ 2020-10-16 20:23 ` James Prestwood
  9 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2020-10-16 20:23 UTC (permalink / raw
  To: ell

[-- Attachment #1: Type: text/plain, Size: 4736 bytes --]

Add optional support for the dhcp client to set the DHCP
address automatically. This is done by providing an RTNL
object with l_dhcp_client_set_rtnl.
---
 ell/dhcp.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ell/dhcp.h |  4 +++
 2 files changed, 88 insertions(+)

diff --git a/ell/dhcp.c b/ell/dhcp.c
index d75d86f..1e261c2 100644
--- a/ell/dhcp.c
+++ b/ell/dhcp.c
@@ -29,6 +29,7 @@
 #include <linux/types.h>
 #include <net/if_arp.h>
 #include <errno.h>
+#include <arpa/inet.h>
 
 #include "private.h"
 #include "random.h"
@@ -38,6 +39,8 @@
 #include "timeout.h"
 #include "dhcp.h"
 #include "dhcp-private.h"
+#include "netlink.h"
+#include "rtnl.h"
 
 #define CLIENT_DEBUG(fmt, args...)					\
 	l_util_debug(client->debug_handler, client->debug_data,		\
@@ -178,6 +181,9 @@ struct l_dhcp_client {
 	struct l_timeout *timeout_resend;
 	struct l_timeout *timeout_lease;
 	struct l_dhcp_lease *lease;
+	struct l_netlink *rtnl;
+	uint32_t rtnl_add_cmdid;
+	struct l_rtnl_address *rtnl_configured_address;
 	uint8_t attempt;
 	l_dhcp_client_event_cb_t event_handler;
 	void *event_data;
@@ -583,6 +589,24 @@ error:
 	l_dhcp_client_stop(client);
 }
 
+static void dhcp_client_address_add_cb(int error, uint16_t type,
+						const void *data, uint32_t len,
+						void *user_data)
+{
+	struct l_dhcp_client *client = user_data;
+
+	client->rtnl_add_cmdid = 0;
+
+	if (error < 0 && error != -EEXIST) {
+		l_rtnl_address_free(client->rtnl_configured_address);
+		client->rtnl_configured_address = NULL;
+		CLIENT_DEBUG("Unable to set address on ifindex: %u: %d(%s)",
+				client->ifindex, error,
+				strerror(-error));
+		return;
+	}
+}
+
 static int dhcp_client_receive_ack(struct l_dhcp_client *client,
 					const struct dhcp_message *ack,
 					size_t len)
@@ -626,6 +650,40 @@ static int dhcp_client_receive_ack(struct l_dhcp_client *client,
 			client->state == DHCP_STATE_REBOOTING)
 		r = L_DHCP_CLIENT_EVENT_LEASE_OBTAINED;
 
+	if (client->rtnl) {
+		struct l_rtnl_address *a;
+		L_AUTO_FREE_VAR(char *, ip) =
+			l_dhcp_lease_get_address(client->lease);
+		uint8_t prefix_len;
+		uint32_t l = l_dhcp_lease_get_lifetime(client->lease);
+		char *netmask = l_dhcp_lease_get_netmask(client->lease);
+		char *broadcast = l_dhcp_lease_get_broadcast(client->lease);
+		struct in_addr in_addr;
+
+		if (inet_pton(AF_INET, netmask, &in_addr) > 0)
+			prefix_len = __builtin_popcountl(in_addr.s_addr);
+		else
+			prefix_len = 24;
+
+		l_free(netmask);
+
+		a = l_rtnl_address_new(ip, prefix_len);
+		l_rtnl_address_set_noprefixroute(a, true);
+		l_rtnl_address_set_lifetimes(a, l, l);
+		l_rtnl_address_set_broadcast(a, broadcast);
+
+		l_free(broadcast);
+
+		client->rtnl_add_cmdid =
+			l_rtnl_ifaddr_add(client->rtnl, client->ifindex, a,
+						dhcp_client_address_add_cb,
+						client, NULL);
+		if (client->rtnl_add_cmdid)
+			client->rtnl_configured_address = a;
+		else
+			CLIENT_DEBUG("Configuring address via RTNL failed");
+	}
+
 	return r;
 }
 
@@ -989,6 +1047,19 @@ LIB_EXPORT bool l_dhcp_client_stop(struct l_dhcp_client *client)
 	if (unlikely(!client))
 		return false;
 
+	if (client->rtnl_add_cmdid) {
+		l_netlink_cancel(client->rtnl, client->rtnl_add_cmdid);
+		client->rtnl_add_cmdid = 0;
+	}
+
+	if (client->rtnl_configured_address) {
+		l_rtnl_ifaddr_delete(client->rtnl, client->ifindex,
+					client->rtnl_configured_address,
+					NULL, NULL, NULL);
+		l_rtnl_address_free(client->rtnl_configured_address);
+		client->rtnl_configured_address = NULL;
+	}
+
 	l_timeout_remove(client->timeout_resend);
 	client->timeout_resend = NULL;
 
@@ -1042,3 +1113,16 @@ LIB_EXPORT bool l_dhcp_client_set_debug(struct l_dhcp_client *client,
 
 	return true;
 }
+
+LIB_EXPORT bool l_dhcp_client_set_rtnl(struct l_dhcp_client *client,
+					struct l_netlink *rtnl)
+{
+	if (unlikely(!client))
+		return false;
+
+	if (unlikely(client->state != DHCP_STATE_INIT))
+		return false;
+
+	client->rtnl = rtnl;
+	return true;
+}
diff --git a/ell/dhcp.h b/ell/dhcp.h
index a92f6aa..2af75cc 100644
--- a/ell/dhcp.h
+++ b/ell/dhcp.h
@@ -31,6 +31,7 @@ extern "C" {
 
 struct l_dhcp_client;
 struct l_dhcp_lease;
+struct l_netlink;
 struct l_dhcp_server;
 
 /* RFC 2132 */
@@ -85,6 +86,9 @@ bool l_dhcp_client_set_interface_name(struct l_dhcp_client *client,
 bool l_dhcp_client_set_hostname(struct l_dhcp_client *client,
 							const char *hostname);
 
+bool l_dhcp_client_set_rtnl(struct l_dhcp_client *client,
+					struct l_netlink *rtnl);
+
 const struct l_dhcp_lease *l_dhcp_client_get_lease(
 					const struct l_dhcp_client *client);
 
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-10-16 20:23 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-10-16 20:23 [PATCH v4 01/11] dhcp-server: basic DHCP server skeleton James Prestwood
2020-10-16 20:23 ` [PATCH v4 02/11] dhcp-server: add transport framework James Prestwood
2020-10-16 20:23 ` [PATCH v4 03/11] dhcp-server: process DISCOVER and send OFFER James Prestwood
2020-10-16 20:23 ` [PATCH v4 04/11] dhcp-server: add REQUEST processing James Prestwood
2020-10-16 20:23 ` [PATCH v4 05/11] dhcp-server: handle DECLINE messages James Prestwood
2020-10-16 20:23 ` [PATCH v4 06/11] dhcp-server: add RELEASE message support James Prestwood
2020-10-16 20:23 ` [PATCH v4 07/11] dhcp-server: add INFORM message handling James Prestwood
2020-10-16 20:23 ` [PATCH v4 08/11] unit: update dhcp test to use new builder APIs James Prestwood
2020-10-16 20:23 ` [PATCH v4 09/11] examples: basic DHCP server example James Prestwood
2020-10-16 20:23 ` [PATCH v4 10/11] unit: add complete client/server dhcp test James Prestwood
2020-10-16 20:23 ` [PATCH v4 11/11] dhcp: Add support for setting address James Prestwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.