Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions include/zephyr/net/coap.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ enum coap_option_num {
COAP_OPTION_SIZE1 = 60, /**< Size1 */
COAP_OPTION_ECHO = 252, /**< Echo (RFC 9175) */
COAP_OPTION_NO_RESPONSE = 258, /**< No-Response (RFC 7967) */
COAP_OPTION_REQUEST_TAG = 292 /**< Request-Tag (RFC 9175) */
COAP_OPTION_REQUEST_TAG = 292, /**< Request-Tag (RFC 9175) */
COAP_OPTION_SIGNAL_701_MMS = 2, /**< Signal 7.01 Max message size (RFC 8323) */
COAP_OPTION_SIGNAL_701_BWT = 4 /**< Signal 7.01 Block-wise transfer (RFC 8323) */
};

/**
Expand Down Expand Up @@ -195,7 +197,9 @@ enum coap_response_code {
COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = COAP_MAKE_RESPONSE_CODE(5, 4),
/** 5.05 - Proxying Not Supported */
COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED =
COAP_MAKE_RESPONSE_CODE(5, 5)
COAP_MAKE_RESPONSE_CODE(5, 5),
/** 7.01 - Capabilities and Settings Message */
COAP_SIGNAL_CODE_CSM = COAP_MAKE_RESPONSE_CODE(7, 1)
};

/** @cond INTERNAL_HIDDEN */
Expand Down Expand Up @@ -747,6 +751,7 @@ enum coap_block_size {
COAP_BLOCK_256, /**< 256-byte block size */
COAP_BLOCK_512, /**< 512-byte block size */
COAP_BLOCK_1024, /**< 1024-byte block size */
COAP_BLOCK_BERT, /**< BERT block size (RFC 8323) - acts like 1024 for calculations */
};

/**
Expand All @@ -760,6 +765,11 @@ enum coap_block_size {
static inline uint16_t coap_block_size_to_bytes(
enum coap_block_size block_size)
{
/* BERT (SZX=7) acts like 1024 bytes for size calculations per RFC 8323 */
if (block_size == COAP_BLOCK_BERT) {
return 1024;
}

return (1 << (block_size + 4));
}

Expand All @@ -779,7 +789,11 @@ static inline enum coap_block_size coap_bytes_to_block_size(uint16_t bytes)
return COAP_BLOCK_16;
}
if (sz > COAP_BLOCK_1024) {
#if defined(CONFIG_COAP_RELIABLE)
return COAP_BLOCK_BERT;
#else
return COAP_BLOCK_1024;
#endif /* defined(CONFIG_COAP_RELIABLE) */
}
return (enum coap_block_size)sz;
}
Expand Down Expand Up @@ -1276,6 +1290,34 @@ struct coap_transmission_parameters coap_get_transmission_parameters(void);
*/
void coap_set_transmission_parameters(const struct coap_transmission_parameters *params);

#if defined(CONFIG_COAP_RELIABLE)

uint8_t coap_header_get_token_tcp(const struct coap_packet *cpkt, uint8_t *token);

uint8_t coap_header_get_code_tcp(const struct coap_packet *cpkt);

const uint8_t *coap_packet_get_payload_tcp(const struct coap_packet *cpkt,
uint32_t *len);

int coap_packet_parse_tcp(struct coap_packet *cpkt, uint8_t *data, uint16_t len,
struct coap_option *options, uint8_t opt_num);

int coap_packet_init_tcp(struct coap_packet *cpkt, uint8_t *data, uint16_t max_len,
uint8_t token_len, const uint8_t *token, uint8_t code);

int coap_packet_tcp_update_len(struct coap_packet *cpkt);

int coap_append_block2_option_tcp(struct coap_packet *cpkt,
struct coap_block_context *ctx);

int coap_update_from_block_tcp(const struct coap_packet *cpkt,
struct coap_block_context *ctx);

size_t coap_next_block_tcp(const struct coap_packet *cpkt,
struct coap_block_context *ctx);

#endif /* CONFIG_COAP_RELIABLE */

#ifdef __cplusplus
}
#endif
Expand Down
234 changes: 234 additions & 0 deletions include/zephyr/net/coap_client_tcp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_NET_COAP_CLIENT_TCP_H_
#define ZEPHYR_INCLUDE_NET_COAP_CLIENT_TCP_H_

/**
* @brief CoAP TCP Client API
* @defgroup coap_client_tcp CoAP TCP Client API
* @ingroup networking
* @{
*/

#include <zephyr/net/coap.h>
#include <zephyr/kernel.h>

#ifdef __cplusplus
extern "C" {
#endif

/** Maximum size of a CoAP TCP message */
#define MAX_COAP_TCP_MSG_LEN (CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE + \
CONFIG_COAP_CLIENT_MESSAGE_SIZE)

/**
* @brief Representation for CoAP TCP client response data.
*/
struct coap_client_tcp_response_data {
/** Result code of the response. Negative if there was a failure. */
int16_t result_code;
/** A pointer to the response CoAP packet. NULL for error result. */
const struct coap_packet *packet;
/** Payload offset from the beginning of a blockwise transfer. */
size_t offset;
/** Buffer containing the payload from the response. NULL for empty payload. */
const uint8_t *payload;
/** Size of the payload. */
size_t payload_len;
/** Indicates the last block of the response. */
bool last_block;
};

/**
* @typedef coap_client_tcp_response_cb_t
* @brief Callback for CoAP TCP request.
*
* @param data The CoAP response data.
* @param user_data User provided context.
*/
typedef void (*coap_client_tcp_response_cb_t)(const struct coap_client_tcp_response_data *data,
void *user_data);

/**
* @typedef coap_client_tcp_payload_cb_t
* @brief Callback for providing a payload for the CoAP TCP request.
*
* @param offset Payload offset from the beginning of a blockwise transfer.
* @param payload A pointer for the buffer containing the payload block.
* @param len Requested (maximum) block size on input. The actual payload length on output.
* @param last_block A pointer to the flag indicating whether more payload blocks are expected.
* @param user_data User provided context.
*
* @return Zero on success, a negative error code to abort upload.
*/
typedef int (*coap_client_tcp_payload_cb_t)(size_t offset, const uint8_t **payload,
size_t *len, bool *last_block,
void *user_data);

/**
* @brief Representation of extra options for the CoAP TCP client request
*/
struct coap_client_tcp_option {
/** Option code */
uint16_t code;
#if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
/** Option len */
uint16_t len;
/** Buffer for the length */
uint8_t value[CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE];
#else
/** Option len */
uint8_t len;
/** Buffer for the length */
uint8_t value[12];
#endif
};

/** @cond INTERNAL_HIDDEN */
#define MAX_TCP_PATH_SIZE (CONFIG_COAP_CLIENT_MAX_PATH_LENGTH + 1)
#define MAX_TCP_EXTRA_OPTIONS CONFIG_COAP_CLIENT_MAX_EXTRA_OPTIONS
/** @endcond */

/**
* @brief Representation of a CoAP TCP client request.
*/
struct coap_client_tcp_request {
enum coap_method method; /**< Method of the request */
char path[MAX_TCP_PATH_SIZE]; /**< Path of the requested resource */
enum coap_content_format fmt; /**< Content format to be used */
const uint8_t *payload; /**< User allocated buffer for send request */
size_t len; /**< Length of the payload */
coap_client_tcp_payload_cb_t payload_cb; /**< Optional payload callback */
coap_client_tcp_response_cb_t cb; /**< Callback when response received */
struct coap_client_tcp_option
options[MAX_TCP_EXTRA_OPTIONS]; /**< Extra options to be added to request */
uint8_t num_options; /**< Number of extra options */
void *user_data; /**< User provided context */
};

/** @cond INTERNAL_HIDDEN */
struct coap_client_tcp_internal_request {
uint8_t request_token[COAP_TOKEN_MAX_LEN];
uint32_t offset;
uint8_t request_tkl;
bool request_ongoing;
atomic_t in_callback;
struct coap_block_context recv_blk_ctx;
struct coap_block_context send_blk_ctx;
struct coap_client_tcp_request coap_request;
struct coap_packet request;
uint8_t request_tag[COAP_TOKEN_MAX_LEN];
uint8_t send_buf[MAX_COAP_TCP_MSG_LEN];

/* TCP-specific timeout handling (no retransmission) */
int64_t tcp_t0;
uint32_t tcp_timeout_ms;

/* For blockwise transfers */
uint32_t last_payload_len;

/* For GETs with observe option set */
bool is_observe;
};

struct coap_client_tcp {
int fd;
struct net_sockaddr address;
net_socklen_t socklen;
struct k_mutex lock;
uint8_t recv_buf[MAX_COAP_TCP_MSG_LEN];
struct coap_client_tcp_internal_request requests[CONFIG_COAP_CLIENT_MAX_REQUESTS];
struct coap_option echo_option;
bool send_echo;

/* Max block/message size from CSM negotiation */
uint32_t max_block_size;
};
/** @endcond */

/**
* @brief Initialize the TCP CoAP client.
*
* @param[in] client Client instance.
* @param[in] info Name for the receiving thread (NULL for default).
*
* @return 0 on success, negative error code otherwise.
*/
int coap_client_tcp_init(struct coap_client_tcp *client, const char *info);

/**
* @brief Send CoAP request over TCP
*
* @param client Client instance.
* @param sock Open TCP socket file descriptor.
* @param addr Destination address (NULL if socket is already connected).
* @param req CoAP request structure.
* @return 0 on success, negative error code otherwise.
*/
int coap_client_tcp_req(struct coap_client_tcp *client, int sock,
const struct net_sockaddr *addr,
struct coap_client_tcp_request *req);

/**
* @brief Send CSM (Capabilities and Settings Message) over TCP
*
* Per RFC 8323, CSM should be exchanged when a CoAP-over-TCP connection is
* established. This negotiates capabilities like max message size and BERT.
*
* @param client Client instance.
* @param sock TCP socket.
* @param addr Server address (or NULL if socket is connected).
* @param max_block_size Maximum block size to advertise.
* @param cb Response callback (can be NULL since server may not respond).
* @param user_data User data for callback.
* @return 0 on success, negative error code otherwise.
*/
int coap_client_tcp_csm_req(struct coap_client_tcp *client, int sock,
const struct net_sockaddr *addr,
uint32_t max_block_size, coap_client_tcp_response_cb_t cb,
void *user_data);

/**
* @brief Cancel all current TCP requests.
*
* @param client Client instance.
*/
void coap_client_tcp_cancel_requests(struct coap_client_tcp *client);

/**
* @brief Cancel and fully reset all requests when changing socket.
*
* Use when tearing down a transport to ensure clean state for a new socket.
*
* @param client Client instance.
*/
void coap_client_tcp_cancel_and_reset_all(struct coap_client_tcp *client);

/**
* @brief Get initial Block2 option for TCP (BERT-aware)
*
* @param client Client instance.
* @return CoAP client initial Block2 option structure.
*/
struct coap_client_tcp_option coap_client_tcp_option_initial_block2(struct coap_client_tcp *client);

/**
* @brief Check if TCP client has ongoing exchange.
*
* @param client Pointer to the CoAP TCP client instance.
* @return true if there is an ongoing exchange, false otherwise.
*/
bool coap_client_tcp_has_ongoing_exchange(struct coap_client_tcp *client);

#ifdef __cplusplus
}
#endif

/**
* @}
*/

#endif /* ZEPHYR_INCLUDE_NET_COAP_CLIENT_TCP_H_ */
4 changes: 4 additions & 0 deletions subsys/net/lib/coap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ zephyr_sources_ifdef(CONFIG_COAP_CLIENT
coap_client.c
)

zephyr_sources_ifdef(CONFIG_COAP_CLIENT_TCP
coap_client_tcp.c
)

zephyr_sources_ifdef(CONFIG_COAP_SERVER
coap_server.c
)
Expand Down
39 changes: 39 additions & 0 deletions subsys/net/lib/coap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ config COAP_KEEP_USER_DATA
help
This option enables keeping application-specific user data

config COAP_RELIABLE
bool "CoAP over reliable transports"
help
Enables the implementation support of RFC8323
(CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets)

config COAP_CLIENT
bool "CoAP client support [EXPERIMENTAL]"
select EXPERIMENTAL
Expand Down Expand Up @@ -170,6 +176,39 @@ config COAP_CLIENT_TRUNCATE_MSGS
receive network stack notifications about block truncation.
Otherwise it happens silently.

if COAP_RELIABLE

config COAP_CLIENT_TCP
bool
default y

config COAP_CLIENT_TCP_REQUEST_TIMEOUT
int "Timeout for TCP CoAP requests in milliseconds"
default 30000
range 1000 300000
help
Timeout for TCP-based CoAP requests. Unlike UDP, TCP requests
have a single timeout with no retransmission. Default is 30 seconds
as recommended by RFC 8323. Range is 1-300 seconds.

config COAP_CLIENT_TCP_EXCHANGE_LIFETIME
int "Exchange lifetime for TCP CoAP in milliseconds"
default 247000
range 60000 600000
help
Maximum lifetime of a TCP CoAP exchange from initial request to
final response. Default is 247 seconds as per RFC 8323.
Used to determine when request contexts can be safely released.

config COAP_CLIENT_TCP_BLOCKWISE_REUSE_TOKEN
bool "Reuse the request token when requesting further blocks"
default n
help
This config exists for compatibility reasons when communicating with other
CoAP libraries (ie. go-coap)

endif # COAP_RELIABLE

endif # COAP_CLIENT

config COAP_SERVER
Expand Down
Loading