Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nrf fromtree] net: dhcpv4: Implement DHCPv4 server #1459

Merged
merged 9 commits into from
Jan 23, 2024
4 changes: 4 additions & 0 deletions include/zephyr/linker/common-rom/common-rom-net.ld
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@
#if defined(CONFIG_COAP_SERVER)
ITERABLE_SECTION_ROM(coap_service, 4)
#endif

#if defined(CONFIG_NET_SOCKETS_SERVICE)
ITERABLE_SECTION_ROM(net_socket_service_desc, 4)
#endif
118 changes: 118 additions & 0 deletions include/zephyr/net/dhcpv4_server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/** @file
* @brief DHCPv4 Server API
*/

/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_DHCPV4_SERVER_H_
#define ZEPHYR_INCLUDE_NET_DHCPV4_SERVER_H_

#include <zephyr/net/net_ip.h>
#include <zephyr/sys_clock.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief DHCPv4 server
* @defgroup dhcpv4_server DHCPv4 server
* @ingroup networking
* @{
*/

/** @cond INTERNAL_HIDDEN */

struct net_if;

#define DHCPV4_CLIENT_ID_MAX_SIZE 20

enum dhcpv4_server_addr_state {
DHCPV4_SERVER_ADDR_FREE,
DHCPV4_SERVER_ADDR_RESERVED,
DHCPV4_SERVER_ADDR_ALLOCATED,
DHCPV4_SERVER_ADDR_DECLINED,
};

struct dhcpv4_client_id {
uint8_t buf[DHCPV4_CLIENT_ID_MAX_SIZE];
uint8_t len;
};

struct dhcpv4_addr_slot {
enum dhcpv4_server_addr_state state;
struct dhcpv4_client_id client_id;
struct in_addr addr;
uint32_t lease_time;
k_timepoint_t expiry;
};

/** @endcond */

/**
* @brief Start DHCPv4 server instance on an iface
*
* @details Start DHCPv4 server on a given interface. The server will start
* listening for DHCPv4 Discover/Request messages on the interface and assign
* IPv4 addresses from the configured address pool accordingly.
*
* @param iface A valid pointer on an interface
* @param base_addr First IPv4 address from the DHCPv4 address pool. The number
* of addresses in the pool is configured statically with Kconfig
* (CONFIG_NET_DHCPV4_SERVER_ADDR_COUNT).
*
* @return 0 on success, a negative error code otherwise.
*/
int net_dhcpv4_server_start(struct net_if *iface, struct in_addr *base_addr);

/**
* @brief Stop DHCPv4 server instance on an iface
*
* @details Stop DHCPv4 server on a given interface. DHCPv4 requests will no
* longer be handled on the interface, and all of the allocations are cleared.
*
* @param iface A valid pointer on an interface
*
* @return 0 on success, a negative error code otherwise.
*/
int net_dhcpv4_server_stop(struct net_if *iface);

/**
* @typedef net_dhcpv4_lease_cb_t
* @brief Callback used while iterating over active DHCPv4 address leases
*
* @param iface Pointer to the network interface
* @param lease Pointer to the DHPCv4 address lease slot
* @param user_data A valid pointer to user data or NULL
*/
typedef void (*net_dhcpv4_lease_cb_t)(struct net_if *iface,
struct dhcpv4_addr_slot *lease,
void *user_data);

/**
* @brief Iterate over all DHCPv4 address leases on a given network interface
* and call callback for each lease. In case no network interface is provided
* (NULL interface pointer), will iterate over all interfaces running DHCPv4
* server instance.
*
* @param iface Pointer to the network interface, can be NULL
* @param cb User-supplied callback function to call
* @param user_data User specified data
*/
int net_dhcpv4_server_foreach_lease(struct net_if *iface,
net_dhcpv4_lease_cb_t cb,
void *user_data);

/**
* @}
*/

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_NET_DHCPV4_SERVER_H_ */
9 changes: 9 additions & 0 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,15 @@ struct in_addr *net_if_ipv4_get_ll(struct net_if *iface,
struct in_addr *net_if_ipv4_get_global_addr(struct net_if *iface,
enum net_addr_state addr_state);

/**
* @brief Get IPv4 netmask of an interface.
*
* @param iface Interface to use.
*
* @return The netmask set on the interface, unspecified address if not found.
*/
struct in_addr net_if_ipv4_get_netmask(struct net_if *iface);

/**
* @brief Set IPv4 netmask for an interface.
*
Expand Down
246 changes: 246 additions & 0 deletions include/zephyr/net/socket_service.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/**
* @file
* @brief BSD Socket service API
*
* API can be used to install a k_work that is called
* if there is data received to a socket.
*/

/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_
#define ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_

/**
* @brief BSD socket service API
* @defgroup bsd_socket_service BSD socket service API
* @ingroup networking
* @{
*/

#include <sys/types.h>
#include <zephyr/types.h>
#include <zephyr/net/socket.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* This struct contains information which socket triggered
* calls to the callback function.
*/
struct net_socket_service_event {
/** k_work that is done when there is desired activity in file descriptor. */
struct k_work work;
/** Callback to be called for desired socket activity */
k_work_handler_t callback;
/** Socket information that triggered this event. */
struct zsock_pollfd event;
/** User data */
void *user_data;
/** Service back pointer */
struct net_socket_service_desc *svc;
};

/**
* Main structure holding socket service configuration information.
* The k_work item is created so that when there is data coming
* to those fds, the k_work callback is then called.
* The workqueue can be set NULL in which case system workqueue is used.
* The service descriptor should be created at built time, and then used
* as a parameter to register the sockets to be monitored.
* User should create needed sockets and then setup the poll struct and
* then register the sockets to be monitored at runtime.
*/
struct net_socket_service_desc {
#if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG
/**
* Owner name. This can be used in debugging to see who has
* registered this service.
*/
const char *owner;
#endif
/** Workqueue where the work is submitted. */
struct k_work_q *work_q;
/** Pointer to the list of services that we are listening */
struct net_socket_service_event *pev;
/** Length of the pollable socket array for this service. */
int pev_len;
/** Where are my pollfd entries in the global list */
int *idx;
};

#define __z_net_socket_svc_get_name(_svc_id) __z_net_socket_service_##_svc_id
#define __z_net_socket_svc_get_idx(_svc_id) __z_net_socket_service_idx_##_svc_id
#define __z_net_socket_svc_get_owner __FILE__ ":" STRINGIFY(__LINE__)

extern void net_socket_service_callback(struct k_work *work);

#if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG
#define NET_SOCKET_SERVICE_OWNER .owner = __z_net_socket_svc_get_owner,
#else
#define NET_SOCKET_SERVICE_OWNER
#endif

#define NET_SOCKET_SERVICE_CALLBACK_MODE(_flag) \
IF_ENABLED(_flag, \
(.work = Z_WORK_INITIALIZER(net_socket_service_callback),))

#define __z_net_socket_service_define(_name, _work_q, _cb, _count, _async, ...) \
static int __z_net_socket_svc_get_idx(_name); \
static struct net_socket_service_event \
__z_net_socket_svc_get_name(_name)[_count] = { \
[0 ... ((_count) - 1)] = { \
.event.fd = -1, /* Invalid socket */ \
NET_SOCKET_SERVICE_CALLBACK_MODE(_async) \
.callback = _cb, \
} \
}; \
COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (), __VA_ARGS__) \
const STRUCT_SECTION_ITERABLE(net_socket_service_desc, _name) = { \
NET_SOCKET_SERVICE_OWNER \
.work_q = (_work_q), \
.pev = __z_net_socket_svc_get_name(_name), \
.pev_len = (_count), \
.idx = &__z_net_socket_svc_get_idx(_name), \
}

/**
* @brief Statically define a network socket service.
* The user callback is called asynchronously for this service meaning that
* the service API will not wait until the user callback returns before continuing
* with next socket service.
*
* The socket service can be accessed outside the module where it is defined using:
*
* @code extern struct net_socket_service_desc <name>; @endcode
*
* @note This macro cannot be used together with a static keyword.
* If such a use-case is desired, use NET_SOCKET_SERVICE_ASYNC_DEFINE_STATIC
* instead.
*
* @param name Name of the service.
* @param work_q Pointer to workqueue where the work is done. Can be null in which case
* system workqueue is used.
* @param cb Callback function that is called for socket activity.
* @param count How many pollable sockets is needed for this service.
*/
#define NET_SOCKET_SERVICE_ASYNC_DEFINE(name, work_q, cb, count) \
__z_net_socket_service_define(name, work_q, cb, count, 1)

/**
* @brief Statically define a network socket service in a private (static) scope.
* The user callback is called asynchronously for this service meaning that
* the service API will not wait until the user callback returns before continuing
* with next socket service.
*
* @param name Name of the service.
* @param work_q Pointer to workqueue where the work is done. Can be null in which case
* system workqueue is used.
* @param cb Callback function that is called for socket activity.
* @param count How many pollable sockets is needed for this service.
*/
#define NET_SOCKET_SERVICE_ASYNC_DEFINE_STATIC(name, work_q, cb, count) \
__z_net_socket_service_define(name, work_q, cb, count, 1, static)

/**
* @brief Statically define a network socket service.
* The user callback is called synchronously for this service meaning that
* the service API will wait until the user callback returns before continuing
* with next socket service.
*
* The socket service can be accessed outside the module where it is defined using:
*
* @code extern struct net_socket_service_desc <name>; @endcode
*
* @note This macro cannot be used together with a static keyword.
* If such a use-case is desired, use NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC
* instead.
*
* @param name Name of the service.
* @param work_q Pointer to workqueue where the work is done. Can be null in which case
* system workqueue is used.
* @param cb Callback function that is called for socket activity.
* @param count How many pollable sockets is needed for this service.
*/
#define NET_SOCKET_SERVICE_SYNC_DEFINE(name, work_q, cb, count) \
__z_net_socket_service_define(name, work_q, cb, count, 0)

/**
* @brief Statically define a network socket service in a private (static) scope.
* The user callback is called synchronously for this service meaning that
* the service API will wait until the user callback returns before continuing
* with next socket service.
*
* @param name Name of the service.
* @param work_q Pointer to workqueue where the work is done. Can be null in which case
* system workqueue is used.
* @param cb Callback function that is called for socket activity.
* @param count How many pollable sockets is needed for this service.
*/
#define NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(name, work_q, cb, count) \
__z_net_socket_service_define(name, work_q, cb, count, 0, static)

/**
* @brief Register pollable sockets.
*
* @param service Pointer to a service description.
* @param fds Socket array to poll.
* @param len Length of the socket array.
* @param user_data User specific data.
*
* @retval 0 No error
* @retval -ENOENT Service is not found.
* @retval -ENINVAL Invalid parameter.
*/
__syscall int net_socket_service_register(const struct net_socket_service_desc *service,
struct zsock_pollfd *fds, int len, void *user_data);

/**
* @brief Unregister pollable sockets.
*
* @param service Pointer to a service description.
*
* @retval 0 No error
* @retval -ENOENT Service is not found.
* @retval -ENINVAL Invalid parameter.
*/
static inline int net_socket_service_unregister(const struct net_socket_service_desc *service)
{
return net_socket_service_register(service, NULL, 0, NULL);
}

/**
* @typedef net_socket_service_cb_t
* @brief Callback used while iterating over socket services.
*
* @param svc Pointer to current socket service.
* @param user_data A valid pointer to user data or NULL
*/
typedef void (*net_socket_service_cb_t)(const struct net_socket_service_desc *svc,
void *user_data);

/**
* @brief Go through all the socket services and call callback for each service.
*
* @param cb User-supplied callback function to call
* @param user_data User specified data
*/
void net_socket_service_foreach(net_socket_service_cb_t cb, void *user_data);

#ifdef __cplusplus
}
#endif

#include <syscalls/socket_service.h>

/**
* @}
*/

#endif /* ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_ */
1 change: 0 additions & 1 deletion subsys/net/ip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ zephyr_library_sources_ifdef(CONFIG_NET_ROUTE route.c)
zephyr_library_sources_ifdef(CONFIG_NET_STATISTICS net_stats.c)
zephyr_library_sources_ifdef(CONFIG_NET_TCP tcp.c)
zephyr_library_sources_ifdef(CONFIG_NET_TEST_PROTOCOL tp.c)
zephyr_library_sources_ifdef(CONFIG_NET_TRICKLE trickle.c)
zephyr_library_sources_ifdef(CONFIG_NET_UDP udp.c)
zephyr_library_sources_ifdef(CONFIG_NET_PROMISCUOUS_MODE promiscuous.c)

Expand Down
Loading