1 /*
2  * Copyright (c) 2023 Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file
8  * @brief CoAP Service API
9  *
10  * An API for applications to respond to CoAP requests
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_
14 #define ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_
15 
16 #include <zephyr/net/coap.h>
17 #include <zephyr/sys/iterable_sections.h>
18 
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22 
23 /**
24  * @brief CoAP Service API
25  * @defgroup coap_service CoAP service API
26  * @since 3.6
27  * @version 0.1.0
28  * @ingroup networking
29  * @{
30  */
31 
32 /**
33  * @name CoAP Service configuration flags
34  * @anchor COAP_SERVICE_FLAGS
35  * @{
36  */
37 
38 /** Start the service on boot. */
39 #define COAP_SERVICE_AUTOSTART		BIT(0)
40 
41 /** @} */
42 
43 /** @cond INTERNAL_HIDDEN */
44 
45 struct coap_service_data {
46 	int sock_fd;
47 	struct coap_observer observers[CONFIG_COAP_SERVICE_OBSERVERS];
48 	struct coap_pending pending[CONFIG_COAP_SERVICE_PENDING_MESSAGES];
49 };
50 
51 struct coap_service {
52 	const char *name;
53 	const char *host;
54 	uint16_t *port;
55 	uint8_t flags;
56 	struct coap_resource *res_begin;
57 	struct coap_resource *res_end;
58 	struct coap_service_data *data;
59 };
60 
61 #define __z_coap_service_define(_name, _host, _port, _flags, _res_begin, _res_end)		\
62 	static struct coap_service_data _CONCAT(coap_service_data_, _name) = {			\
63 		.sock_fd = -1,									\
64 	};											\
65 	const STRUCT_SECTION_ITERABLE(coap_service, _name) = {					\
66 		.name = STRINGIFY(_name),							\
67 		.host = _host,									\
68 		.port = (uint16_t *)(_port),							\
69 		.flags = _flags,								\
70 		.res_begin = (_res_begin),							\
71 		.res_end = (_res_end),								\
72 		.data = &_CONCAT(coap_service_data_, _name),					\
73 	}
74 
75 /** @endcond */
76 
77 /**
78  * @brief Define a static CoAP resource owned by the service named @p _service .
79  *
80  * @note The handlers registered with the resource can return a CoAP response code to reply with
81  * an acknowledge without any payload, nothing is sent if the return value is 0 or negative.
82  * As seen in the example.
83  *
84  * @code{.c}
85  *     static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
86  *
87  *     static int led_put(struct coap_resource *resource, struct coap_packet *request,
88  *                        struct sockaddr *addr, socklen_t addr_len)
89  *     {
90  *             const uint8_t *payload;
91  *             uint16_t payload_len;
92  *
93  *             payload = coap_packet_get_payload(request, &payload_len);
94  *             if (payload_len != 1) {
95  *                     return COAP_RESPONSE_CODE_BAD_REQUEST;
96  *             }
97  *
98  *             if (gpio_pin_set_dt(&led, payload[0]) < 0) {
99  *                     return COAP_RESPONSE_CODE_INTERNAL_ERROR;
100  *             }
101  *
102  *             return COAP_RESPONSE_CODE_CHANGED;
103  *     }
104  *
105  *     COAP_RESOURCE_DEFINE(my_resource, my_service, {
106  *             .put = led_put,
107  *     });
108  * @endcode
109  *
110  * @param _name Name of the resource.
111  * @param _service Name of the associated service.
112  */
113 #define COAP_RESOURCE_DEFINE(_name, _service, ...)						\
114 	STRUCT_SECTION_ITERABLE_ALTERNATE(_CONCAT(coap_resource_, _service), coap_resource,	\
115 					  _name) = __VA_ARGS__
116 
117 /**
118  * @brief Define a CoAP service with static resources.
119  *
120  * @note The @p _host parameter can be `NULL`. If not, it is used to specify an IP address either in
121  * IPv4 or IPv6 format a fully-qualified hostname or a virtual host, otherwise the any address is
122  * used.
123  *
124  * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
125  * number to use for the service. If the specified port number is zero, then an ephemeral port
126  * number will be used and the actual port number assigned will be written back to memory. For
127  * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
128  *
129  * @param _name Name of the service.
130  * @param _host IP address or hostname associated with the service.
131  * @param[inout] _port Pointer to port associated with the service.
132  * @param _flags Configuration flags @see @ref COAP_SERVICE_FLAGS.
133  */
134 #define COAP_SERVICE_DEFINE(_name, _host, _port, _flags)					\
135 	extern struct coap_resource _CONCAT(_CONCAT(_coap_resource_, _name), _list_start)[];	\
136 	extern struct coap_resource _CONCAT(_CONCAT(_coap_resource_, _name), _list_end)[];	\
137 	__z_coap_service_define(_name, _host, _port, _flags,					\
138 				&_CONCAT(_CONCAT(_coap_resource_, _name), _list_start)[0],	\
139 				&_CONCAT(_CONCAT(_coap_resource_, _name), _list_end)[0])
140 
141 /**
142  * @brief Count the number of CoAP services.
143  *
144  * @param[out] _dst Pointer to location where result is written.
145  */
146 #define COAP_SERVICE_COUNT(_dst) STRUCT_SECTION_COUNT(coap_service, _dst)
147 
148 /**
149  * @brief Count CoAP service static resources.
150  *
151  * @param _service Pointer to a service.
152  */
153 #define COAP_SERVICE_RESOURCE_COUNT(_service) ((_service)->res_end - (_service)->res_begin)
154 
155 /**
156  * @brief Check if service has the specified resource.
157  *
158  * @param _service Pointer to a service.
159  * @param _resource Pointer to a resource.
160  */
161 #define COAP_SERVICE_HAS_RESOURCE(_service, _resource)						\
162 	((_service)->res_begin <= _resource && _resource < (_service)->res_end)
163 
164 /**
165  * @brief Iterate over all CoAP services.
166  *
167  * @param _it Name of iterator (of type @ref coap_service)
168  */
169 #define COAP_SERVICE_FOREACH(_it) STRUCT_SECTION_FOREACH(coap_service, _it)
170 
171 /**
172  * @brief Iterate over static CoAP resources associated with a given @p _service.
173  *
174  * @note This macro requires that @p _service is defined with @ref COAP_SERVICE_DEFINE.
175  *
176  * @param _service Name of CoAP service
177  * @param _it Name of iterator (of type @ref coap_resource)
178  */
179 #define COAP_RESOURCE_FOREACH(_service, _it)							\
180 	STRUCT_SECTION_FOREACH_ALTERNATE(_CONCAT(coap_resource_, _service), coap_resource, _it)
181 
182 /**
183  * @brief Iterate over all static resources associated with @p _service .
184  *
185  * @note This macro is suitable for a @p _service defined with @ref COAP_SERVICE_DEFINE.
186  *
187  * @param _service Pointer to COAP service
188  * @param _it Name of iterator (of type @ref coap_resource)
189  */
190 #define COAP_SERVICE_FOREACH_RESOURCE(_service, _it)						\
191 	for (struct coap_resource *_it = (_service)->res_begin; ({				\
192 		__ASSERT(_it <= (_service)->res_end, "unexpected list end location");		\
193 		_it < (_service)->res_end;							\
194 	}); _it++)
195 
196 /**
197  * @brief Start the provided @p service .
198  *
199  * @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
200  *
201  * @param service Pointer to CoAP service
202  * @retval 0 in case of success.
203  * @retval -EALREADY in case of an already running service.
204  * @retval -ENOTSUP in case the server has no valid host and port configuration.
205  */
206 int coap_service_start(const struct coap_service *service);
207 
208 /**
209  * @brief Stop the provided @p service .
210  *
211  * @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
212  *
213  * @param service Pointer to CoAP service
214  * @retval 0 in case of success.
215  * @retval -EALREADY in case the service isn't running.
216  */
217 int coap_service_stop(const struct coap_service *service);
218 
219 /**
220  * @brief Query the provided @p service running state.
221  *
222  * @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
223  *
224  * @param service Pointer to CoAP service
225  * @retval 1 if the service is running
226  * @retval 0 if the service is stopped
227  * @retval negative in case of an error.
228  */
229 int coap_service_is_running(const struct coap_service *service);
230 
231 /**
232  * @brief Send a CoAP message from the provided @p service .
233  *
234  * @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
235  *
236  * @param service Pointer to CoAP service
237  * @param cpkt CoAP Packet to send
238  * @param addr Peer address
239  * @param addr_len Peer address length
240  * @param params Pointer to transmission parameters structure or NULL to use default values.
241  * @return 0 in case of success or negative in case of error.
242  */
243 int coap_service_send(const struct coap_service *service, const struct coap_packet *cpkt,
244 		      const struct sockaddr *addr, socklen_t addr_len,
245 		      const struct coap_transmission_parameters *params);
246 
247 /**
248  * @brief Send a CoAP message from the provided @p resource .
249  *
250  * @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
251  *
252  * @param resource Pointer to CoAP resource
253  * @param cpkt CoAP Packet to send
254  * @param addr Peer address
255  * @param addr_len Peer address length
256  * @param params Pointer to transmission parameters structure or NULL to use default values.
257  * @return 0 in case of success or negative in case of error.
258  */
259 int coap_resource_send(const struct coap_resource *resource, const struct coap_packet *cpkt,
260 		       const struct sockaddr *addr, socklen_t addr_len,
261 		       const struct coap_transmission_parameters *params);
262 
263 /**
264  * @brief Parse a CoAP observe request for the provided @p resource .
265  *
266  * @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
267  *
268  * If the observe option value is equal to 0, an observer will be added, if the value is equal
269  * to 1, an existing observer will be removed.
270  *
271  * @param resource Pointer to CoAP resource
272  * @param request CoAP request to parse
273  * @param addr Peer address
274  * @return the observe option value in case of success or negative in case of error.
275  */
276 int coap_resource_parse_observe(struct coap_resource *resource, const struct coap_packet *request,
277 				const struct sockaddr *addr);
278 
279 /**
280  * @brief Lookup an observer by address and remove it from the @p resource .
281  *
282  * @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
283  *
284  * @param resource Pointer to CoAP resource
285  * @param addr Peer address
286  * @return 0 in case of success or negative in case of error.
287  */
288 int coap_resource_remove_observer_by_addr(struct coap_resource *resource,
289 					  const struct sockaddr *addr);
290 
291 /**
292  * @brief Lookup an observer by token and remove it from the @p resource .
293  *
294  * @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
295  *
296  * @param resource Pointer to CoAP resource
297  * @param token Pointer to the token
298  * @param token_len Length of valid bytes in the token
299  * @return 0 in case of success or negative in case of error.
300  */
301 int coap_resource_remove_observer_by_token(struct coap_resource *resource,
302 					   const uint8_t *token, uint8_t token_len);
303 
304 /**
305  * @}
306  */
307 
308 #ifdef __cplusplus
309 }
310 #endif
311 
312 #endif /* ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_ */
313