zephyr/subsys/net/ip/net_context.c
Flavio Ceolin c4f7faea10 random: Include header where it is used
Unit tests were failing to build because random header was included by
kernel_includes.h. The problem is that rand32.h includes a generated
file that is either not generated or not included when building unit
tests. Also, it is better to limit the scope of this file to where it is
used.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
2020-07-08 21:05:36 -04:00

2181 lines
49 KiB
C

/** @file
* @brief Network context API
*
* An API for applications to define a network connection.
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_ctx, CONFIG_NET_CONTEXT_LOG_LEVEL);
#include <kernel.h>
#include <random/rand32.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <net/net_pkt.h>
#include <net/net_ip.h>
#include <net/socket.h>
#include <net/net_context.h>
#include <net/net_offload.h>
#include <net/ethernet.h>
#include <net/socket_can.h>
#include "connection.h"
#include "net_private.h"
#include "ipv6.h"
#include "ipv4.h"
#include "udp_internal.h"
#include "tcp_internal.h"
#include "net_stats.h"
#if IS_ENABLED(CONFIG_NET_TCP2)
#include "tcp2.h"
#endif
#ifndef EPFNOSUPPORT
/* Some old versions of newlib haven't got this defined in errno.h,
* Just use EPROTONOSUPPORT in this case
*/
#define EPFNOSUPPORT EPROTONOSUPPORT
#endif
#define PKT_WAIT_TIME K_SECONDS(1)
#define NET_MAX_CONTEXT CONFIG_NET_MAX_CONTEXTS
static struct net_context contexts[NET_MAX_CONTEXT];
/* We need to lock the contexts array as these APIs are typically called
* from applications which are usually run in task context.
*/
static struct k_sem contexts_lock;
#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP)
static int check_used_port(enum net_ip_protocol ip_proto,
uint16_t local_port,
const struct sockaddr *local_addr)
{
int i;
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (!net_context_is_used(&contexts[i])) {
continue;
}
if (!(net_context_get_ip_proto(&contexts[i]) == ip_proto &&
net_sin((struct sockaddr *)&
contexts[i].local)->sin_port == local_port)) {
continue;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
local_addr->sa_family == AF_INET6) {
if (net_ipv6_addr_cmp(
net_sin6_ptr(&contexts[i].local)->
sin6_addr,
&((struct sockaddr_in6 *)
local_addr)->sin6_addr)) {
return -EEXIST;
}
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
local_addr->sa_family == AF_INET) {
if (net_ipv4_addr_cmp(
net_sin_ptr(&contexts[i].local)->
sin_addr,
&((struct sockaddr_in *)
local_addr)->sin_addr)) {
return -EEXIST;
}
}
}
return 0;
}
static uint16_t find_available_port(struct net_context *context,
const struct sockaddr *addr)
{
uint16_t local_port;
do {
local_port = sys_rand32_get() | 0x8000;
if (local_port <= 1023U) {
/* 0 - 1023 ports are reserved */
continue;
}
} while (check_used_port(net_context_get_ip_proto(context),
htons(local_port), addr) == -EEXIST);
return htons(local_port);
}
#else
#define check_used_port(...) 0
#define find_available_port(...) 0
#endif
int net_context_get(sa_family_t family,
enum net_sock_type type,
uint16_t ip_proto,
struct net_context **context)
{
int i, ret = -ENOENT;
if (IS_ENABLED(CONFIG_NET_CONTEXT_CHECK)) {
if (!IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
NET_DBG("IPv4 disabled");
return -EPFNOSUPPORT;
}
if (!IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
NET_DBG("IPv6 disabled");
return -EPFNOSUPPORT;
}
if (!IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && family == AF_CAN) {
NET_DBG("AF_CAN disabled");
return -EPFNOSUPPORT;
}
if (type == SOCK_RAW) {
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
if (family != AF_PACKET && family != AF_CAN) {
NET_DBG("Invalid family");
return -EINVAL;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
!IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
if (family != AF_PACKET) {
NET_DBG("Invalid family");
return -EINVAL;
}
} else if (!IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
if (family != AF_CAN) {
NET_DBG("Invalid family");
return -EINVAL;
}
}
} else {
if (family == AF_PACKET || family == AF_CAN) {
NET_DBG("Invalid family");
return -EPROTOTYPE;
}
}
if (!IS_ENABLED(CONFIG_NET_UDP)) {
if (type == SOCK_DGRAM) {
NET_DBG("Datagram context disabled");
return -EPROTOTYPE;
}
if (ip_proto == IPPROTO_UDP) {
NET_DBG("UDP disabled");
return -EPROTONOSUPPORT;
}
}
if (!IS_ENABLED(CONFIG_NET_TCP)) {
if (type == SOCK_STREAM) {
NET_DBG("Stream context disabled");
return -EPROTOTYPE;
}
if (ip_proto == IPPROTO_TCP) {
NET_DBG("TCP disabled");
return -EPROTONOSUPPORT;
}
}
if (family != AF_INET && family != AF_INET6 &&
family != AF_PACKET && family != AF_CAN) {
NET_DBG("Unknown address family %d", family);
return -EAFNOSUPPORT;
}
if (type != SOCK_DGRAM && type != SOCK_STREAM &&
type != SOCK_RAW) {
NET_DBG("Unknown context type");
return -EPROTOTYPE;
}
if (type != SOCK_RAW && ip_proto != IPPROTO_UDP &&
ip_proto != IPPROTO_TCP) {
NET_DBG("Unknown IP protocol %d", ip_proto);
return -EPROTONOSUPPORT;
}
if ((type == SOCK_STREAM && ip_proto == IPPROTO_UDP) ||
(type == SOCK_DGRAM && ip_proto == IPPROTO_TCP)) {
NET_DBG("Context type and protocol mismatch,"
" type %d proto %d", type, ip_proto);
return -EOPNOTSUPP;
}
if (!context) {
NET_DBG("Invalid context");
return -EINVAL;
}
}
k_sem_take(&contexts_lock, K_FOREVER);
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (net_context_is_used(&contexts[i])) {
continue;
}
memset(&contexts[i], 0, sizeof(contexts[i]));
/* FIXME - Figure out a way to get the correct network interface
* as it is not known at this point yet.
*/
if (!net_if_is_ip_offloaded(net_if_get_default())
&& ip_proto == IPPROTO_TCP) {
if (net_tcp_get(&contexts[i]) < 0) {
break;
}
}
contexts[i].iface = -1;
contexts[i].flags = 0U;
atomic_set(&contexts[i].refcount, 1);
net_context_set_family(&contexts[i], family);
net_context_set_type(&contexts[i], type);
net_context_set_ip_proto(&contexts[i], ip_proto);
if (IS_ENABLED(CONFIG_NET_IPV6) ||
IS_ENABLED(CONFIG_NET_IPV4)) {
(void)memset(&contexts[i].remote, 0,
sizeof(struct sockaddr));
(void)memset(&contexts[i].local, 0,
sizeof(struct sockaddr_ptr));
}
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6
*)&contexts[i].local;
addr6->sin6_port = find_available_port(&contexts[i],
(struct sockaddr *)addr6);
if (!addr6->sin6_port) {
ret = -EADDRINUSE;
break;
}
}
if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in
*)&contexts[i].local;
addr->sin_port = find_available_port(&contexts[i],
(struct sockaddr *)addr);
if (!addr->sin_port) {
ret = -EADDRINUSE;
break;
}
}
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
k_sem_init(&contexts[i].recv_data_wait, 1, UINT_MAX);
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
k_mutex_init(&contexts[i].lock);
contexts[i].flags |= NET_CONTEXT_IN_USE;
*context = &contexts[i];
ret = 0;
break;
}
k_sem_give(&contexts_lock);
/* FIXME - Figure out a way to get the correct network interface
* as it is not known at this point yet.
*/
if (!ret && IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_if_get_default())) {
ret = net_offload_get(net_if_get_default(),
family,
type,
ip_proto,
context);
if (ret < 0) {
(*context)->flags &= ~NET_CONTEXT_IN_USE;
*context = NULL;
}
return ret;
}
return ret;
}
int net_context_ref(struct net_context *context)
{
int old_rc = atomic_inc(&context->refcount);
return old_rc + 1;
}
int net_context_unref(struct net_context *context)
{
int old_rc = atomic_dec(&context->refcount);
if (old_rc != 1) {
return old_rc - 1;
}
k_mutex_lock(&context->lock, K_FOREVER);
net_tcp_unref(context);
if (context->conn_handler) {
if (IS_ENABLED(CONFIG_NET_TCP) || IS_ENABLED(CONFIG_NET_UDP) ||
IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
net_conn_unregister(context->conn_handler);
}
context->conn_handler = NULL;
}
net_context_set_state(context, NET_CONTEXT_UNCONNECTED);
context->flags &= ~NET_CONTEXT_IN_USE;
NET_DBG("Context %p released", context);
k_mutex_unlock(&context->lock);
return 0;
}
int net_context_put(struct net_context *context)
{
int ret = 0;
NET_ASSERT(context);
if (!PART_OF_ARRAY(contexts, context)) {
return -EINVAL;
}
k_mutex_lock(&context->lock, K_FOREVER);
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
context->flags &= ~NET_CONTEXT_IN_USE;
ret = net_offload_put(net_context_get_iface(context), context);
goto unlock;
}
context->connect_cb = NULL;
context->recv_cb = NULL;
context->send_cb = NULL;
/* Decrement refcount on user app's behalf */
net_context_unref(context);
/* net_tcp_put() will handle decrementing refcount on stack's behalf */
net_tcp_put(context);
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
/* If local address is not bound, bind it to INADDR_ANY and random port. */
static int bind_default(struct net_context *context)
{
sa_family_t family = net_context_get_family(context);
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
struct sockaddr_in6 addr6;
if (net_sin6_ptr(&context->local)->sin6_addr) {
return 0;
}
addr6.sin6_family = AF_INET6;
memcpy(&addr6.sin6_addr, net_ipv6_unspecified_address(),
sizeof(addr6.sin6_addr));
addr6.sin6_port =
find_available_port(context,
(struct sockaddr *)&addr6);
return net_context_bind(context, (struct sockaddr *)&addr6,
sizeof(addr6));
}
if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
struct sockaddr_in addr4;
if (net_sin_ptr(&context->local)->sin_addr) {
return 0;
}
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = INADDR_ANY;
addr4.sin_port =
find_available_port(context,
(struct sockaddr *)&addr4);
return net_context_bind(context, (struct sockaddr *)&addr4,
sizeof(addr4));
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && family == AF_PACKET) {
struct sockaddr_ll ll_addr;
if (net_sll_ptr(&context->local)->sll_addr) {
return 0;
}
ll_addr.sll_family = AF_PACKET;
ll_addr.sll_protocol = ETH_P_ALL;
ll_addr.sll_ifindex = net_if_get_by_iface(net_if_get_default());
return net_context_bind(context, (struct sockaddr *)&ll_addr,
sizeof(ll_addr));
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && family == AF_CAN) {
struct sockaddr_can can_addr;
if (context->iface >= 0) {
return 0;
} else {
#if defined(CONFIG_NET_L2_CANBUS_RAW)
struct net_if *iface;
iface = net_if_get_first_by_type(
&NET_L2_GET_NAME(CANBUS_RAW));
if (!iface) {
return -ENOENT;
}
can_addr.can_ifindex = net_if_get_by_iface(iface);
context->iface = can_addr.can_ifindex;
#else
return -ENOENT;
#endif
}
can_addr.can_family = AF_CAN;
return net_context_bind(context, (struct sockaddr *)&can_addr,
sizeof(can_addr));
}
return -EINVAL;
}
int net_context_bind(struct net_context *context, const struct sockaddr *addr,
socklen_t addrlen)
{
NET_ASSERT(addr);
NET_ASSERT(PART_OF_ARRAY(contexts, context));
/* If we already have connection handler, then it effectively
* means that it's already bound to an interface/port, and we
* don't support rebinding connection to new address/port in
* the code below.
* TODO: Support rebinding.
*/
if (context->conn_handler) {
return -EISCONN;
}
if (IS_ENABLED(CONFIG_NET_IPV6) && addr->sa_family == AF_INET6) {
struct net_if *iface = NULL;
struct in6_addr *ptr;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
int ret;
if (addrlen < sizeof(struct sockaddr_in6)) {
return -EINVAL;
}
if (net_ipv6_is_addr_mcast(&addr6->sin6_addr)) {
struct net_if_mcast_addr *maddr;
maddr = net_if_ipv6_maddr_lookup(&addr6->sin6_addr,
&iface);
if (!maddr) {
return -ENOENT;
}
ptr = &maddr->address.in6_addr;
} else if (net_ipv6_is_addr_unspecified(&addr6->sin6_addr)) {
iface = net_if_ipv6_select_src_iface(
&net_sin6(&context->remote)->sin6_addr);
ptr = (struct in6_addr *)net_ipv6_unspecified_address();
} else {
struct net_if_addr *ifaddr;
ifaddr = net_if_ipv6_addr_lookup(&addr6->sin6_addr,
&iface);
if (!ifaddr) {
return -ENOENT;
}
ptr = &ifaddr->address.in6_addr;
}
if (!iface) {
NET_ERR("Cannot bind to %s",
log_strdup(net_sprint_ipv6_addr(
&addr6->sin6_addr)));
return -EADDRNOTAVAIL;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
net_context_set_iface(context, iface);
net_sin6_ptr(&context->local)->sin6_family = AF_INET6;
net_sin6_ptr(&context->local)->sin6_addr = ptr;
if (addr6->sin6_port) {
ret = check_used_port(AF_INET6, addr6->sin6_port,
addr);
if (!ret) {
net_sin6_ptr(&context->local)->sin6_port =
addr6->sin6_port;
} else {
NET_ERR("Port %d is in use!",
ntohs(addr6->sin6_port));
return ret;
}
} else {
addr6->sin6_port =
net_sin6_ptr(&context->local)->sin6_port;
}
NET_DBG("Context %p binding to %s [%s]:%d iface %p",
context,
net_proto2str(AF_INET6,
net_context_get_ip_proto(context)),
log_strdup(net_sprint_ipv6_addr(ptr)),
ntohs(addr6->sin6_port), iface);
return 0;
}
if (IS_ENABLED(CONFIG_NET_IPV4) && addr->sa_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
struct net_if *iface = NULL;
struct net_if_addr *ifaddr;
struct in_addr *ptr;
int ret;
if (addrlen < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
if (net_ipv4_is_addr_mcast(&addr4->sin_addr)) {
struct net_if_mcast_addr *maddr;
maddr = net_if_ipv4_maddr_lookup(&addr4->sin_addr,
&iface);
if (!maddr) {
return -ENOENT;
}
ptr = &maddr->address.in_addr;
} else if (addr4->sin_addr.s_addr == INADDR_ANY) {
iface = net_if_ipv4_select_src_iface(
&net_sin(&context->remote)->sin_addr);
ptr = (struct in_addr *)net_ipv4_unspecified_address();
} else {
ifaddr = net_if_ipv4_addr_lookup(&addr4->sin_addr,
&iface);
if (!ifaddr) {
return -ENOENT;
}
ptr = &ifaddr->address.in_addr;
}
if (!iface) {
NET_ERR("Cannot bind to %s",
log_strdup(net_sprint_ipv4_addr(
&addr4->sin_addr)));
return -EADDRNOTAVAIL;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
k_mutex_lock(&context->lock, K_FOREVER);
ret = 0;
net_context_set_iface(context, iface);
net_sin_ptr(&context->local)->sin_family = AF_INET;
net_sin_ptr(&context->local)->sin_addr = ptr;
if (addr4->sin_port) {
ret = check_used_port(AF_INET, addr4->sin_port,
addr);
if (!ret) {
net_sin_ptr(&context->local)->sin_port =
addr4->sin_port;
} else {
NET_ERR("Port %d is in use!",
ntohs(addr4->sin_port));
goto unlock;
}
} else {
addr4->sin_port =
net_sin_ptr(&context->local)->sin_port;
}
NET_DBG("Context %p binding to %s %s:%d iface %p",
context,
net_proto2str(AF_INET,
net_context_get_ip_proto(context)),
log_strdup(net_sprint_ipv4_addr(ptr)),
ntohs(addr4->sin_port), iface);
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
addr->sa_family == AF_PACKET) {
struct sockaddr_ll *ll_addr = (struct sockaddr_ll *)addr;
struct net_if *iface = NULL;
if (addrlen < sizeof(struct sockaddr_ll)) {
return -EINVAL;
}
if (ll_addr->sll_ifindex < 0) {
return -EINVAL;
}
iface = net_if_get_by_index(ll_addr->sll_ifindex);
if (!iface) {
NET_ERR("Cannot bind to interface index %d",
ll_addr->sll_ifindex);
return -EADDRNOTAVAIL;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
net_context_set_iface(context, iface);
net_sll_ptr(&context->local)->sll_family = AF_PACKET;
net_sll_ptr(&context->local)->sll_ifindex =
ll_addr->sll_ifindex;
net_sll_ptr(&context->local)->sll_protocol =
ll_addr->sll_protocol;
net_sll_ptr(&context->local)->sll_addr =
net_if_get_link_addr(iface)->addr;
NET_DBG("Context %p binding to %d iface[%d] %p addr %s",
context, net_context_get_ip_proto(context),
ll_addr->sll_ifindex, iface,
log_strdup(net_sprint_ll_addr(
net_if_get_link_addr(iface)->addr,
net_if_get_link_addr(iface)->len)));
return 0;
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) && addr->sa_family == AF_CAN) {
struct sockaddr_can *can_addr = (struct sockaddr_can *)addr;
struct net_if *iface = NULL;
if (addrlen < sizeof(struct sockaddr_can)) {
return -EINVAL;
}
if (can_addr->can_ifindex < 0) {
return -EINVAL;
}
iface = net_if_get_by_index(can_addr->can_ifindex);
if (!iface) {
NET_ERR("Cannot bind to interface index %d",
can_addr->can_ifindex);
return -EADDRNOTAVAIL;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
net_context_set_iface(context, iface);
net_context_set_family(context, AF_CAN);
net_can_ptr(&context->local)->can_family = AF_CAN;
net_can_ptr(&context->local)->can_ifindex =
can_addr->can_ifindex;
NET_DBG("Context %p binding to %d iface[%d] %p",
context, net_context_get_ip_proto(context),
can_addr->can_ifindex, iface);
return 0;
}
return -EINVAL;
}
static inline struct net_context *find_context(void *conn_handler)
{
int i;
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (!net_context_is_used(&contexts[i])) {
continue;
}
if (contexts[i].conn_handler == conn_handler) {
return &contexts[i];
}
}
return NULL;
}
int net_context_listen(struct net_context *context, int backlog)
{
ARG_UNUSED(backlog);
NET_ASSERT(PART_OF_ARRAY(contexts, context));
if (!net_context_is_used(context)) {
return -EBADF;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
return net_offload_listen(net_context_get_iface(context),
context, backlog);
}
k_mutex_lock(&context->lock, K_FOREVER);
if (net_tcp_listen(context) >= 0) {
k_mutex_unlock(&context->lock);
return 0;
}
k_mutex_unlock(&context->lock);
return -EOPNOTSUPP;
}
#if defined(CONFIG_NET_IPV4)
int net_context_create_ipv4_new(struct net_context *context,
struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst)
{
if (!src) {
NET_ASSERT(((
struct sockaddr_in_ptr *)&context->local)->sin_addr);
src = ((struct sockaddr_in_ptr *)&context->local)->sin_addr;
}
if (net_ipv4_is_addr_unspecified(src)
|| net_ipv4_is_addr_mcast(src)) {
src = net_if_ipv4_select_src_addr(net_pkt_iface(pkt),
(struct in_addr *)dst);
/* If src address is still unspecified, do not create pkt */
if (net_ipv4_is_addr_unspecified(src)) {
NET_DBG("DROP: src addr is unspecified");
return -EINVAL;
}
}
net_pkt_set_ipv4_ttl(pkt, net_context_get_ipv4_ttl(context));
return net_ipv4_create(pkt, src, dst);
}
#endif /* CONFIG_NET_IPV4 */
#if defined(CONFIG_NET_IPV6)
int net_context_create_ipv6_new(struct net_context *context,
struct net_pkt *pkt,
const struct in6_addr *src,
const struct in6_addr *dst)
{
if (!src) {
NET_ASSERT(((
struct sockaddr_in6_ptr *)&context->local)->sin6_addr);
src = ((struct sockaddr_in6_ptr *)&context->local)->sin6_addr;
}
if (net_ipv6_is_addr_unspecified(src)
|| net_ipv6_is_addr_mcast(src)) {
src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt),
(struct in6_addr *)dst);
}
net_pkt_set_ipv6_hop_limit(pkt,
net_context_get_ipv6_hop_limit(context));
return net_ipv6_create(pkt, src, dst);
}
#endif /* CONFIG_NET_IPV6 */
int net_context_connect(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen,
net_context_connect_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
struct sockaddr *laddr = NULL;
struct sockaddr local_addr __unused;
uint16_t lport, rport;
int ret;
NET_ASSERT(addr);
NET_ASSERT(PART_OF_ARRAY(contexts, context));
k_mutex_lock(&context->lock, K_FOREVER);
if (!net_context_is_used(context)) {
ret = -EBADF;
goto unlock;
}
if (addr->sa_family != net_context_get_family(context)) {
NET_ASSERT(addr->sa_family == net_context_get_family(context),
"Family mismatch %d should be %d",
addr->sa_family,
net_context_get_family(context));
ret = -EINVAL;
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
addr->sa_family == AF_PACKET) {
ret = -EOPNOTSUPP;
goto unlock;
}
if (net_context_get_state(context) == NET_CONTEXT_LISTENING) {
ret = -EOPNOTSUPP;
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)
&context->remote;
if (addrlen < sizeof(struct sockaddr_in6)) {
ret = -EINVAL;
goto unlock;
}
if (net_context_get_ip_proto(context) == IPPROTO_TCP &&
net_ipv6_is_addr_mcast(&addr6->sin6_addr)) {
ret = -EADDRNOTAVAIL;
goto unlock;
}
memcpy(&addr6->sin6_addr, &net_sin6(addr)->sin6_addr,
sizeof(struct in6_addr));
addr6->sin6_port = net_sin6(addr)->sin6_port;
addr6->sin6_family = AF_INET6;
if (!net_ipv6_is_addr_unspecified(&addr6->sin6_addr)) {
context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;
} else {
context->flags &= ~NET_CONTEXT_REMOTE_ADDR_SET;
}
rport = addr6->sin6_port;
/* The binding must be done after we have set the remote
* address but before checking the local address. Otherwise
* the laddr might not be set properly which would then cause
* issues when doing net_tcp_connect(). This issue was seen
* with socket tests and when connecting to loopback interface.
*/
ret = bind_default(context);
if (ret) {
goto unlock;
}
net_sin6_ptr(&context->local)->sin6_family = AF_INET6;
net_sin6(&local_addr)->sin6_family = AF_INET6;
net_sin6(&local_addr)->sin6_port = lport =
net_sin6((struct sockaddr *)&context->local)->sin6_port;
if (net_sin6_ptr(&context->local)->sin6_addr) {
net_ipaddr_copy(&net_sin6(&local_addr)->sin6_addr,
net_sin6_ptr(&context->local)->sin6_addr);
laddr = &local_addr;
}
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)
&context->remote;
if (addrlen < sizeof(struct sockaddr_in)) {
ret = -EINVAL;
goto unlock;
}
/* FIXME - Add multicast and broadcast address check */
addr4 = (struct sockaddr_in *)&context->remote;
memcpy(&addr4->sin_addr, &net_sin(addr)->sin_addr,
sizeof(struct in_addr));
addr4->sin_port = net_sin(addr)->sin_port;
addr4->sin_family = AF_INET;
if (addr4->sin_addr.s_addr) {
context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;
} else {
context->flags &= ~NET_CONTEXT_REMOTE_ADDR_SET;
}
rport = addr4->sin_port;
ret = bind_default(context);
if (ret) {
goto unlock;
}
net_sin_ptr(&context->local)->sin_family = AF_INET;
net_sin(&local_addr)->sin_family = AF_INET;
net_sin(&local_addr)->sin_port = lport =
net_sin((struct sockaddr *)&context->local)->sin_port;
if (net_sin_ptr(&context->local)->sin_addr) {
net_ipaddr_copy(&net_sin(&local_addr)->sin_addr,
net_sin_ptr(&context->local)->sin_addr);
laddr = &local_addr;
}
} else {
ret = -EINVAL; /* Not IPv4 or IPv6 */
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
ret = net_offload_connect(
net_context_get_iface(context),
context,
addr,
addrlen,
cb,
timeout,
user_data);
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_UDP) &&
net_context_get_type(context) == SOCK_DGRAM) {
if (cb) {
cb(context, 0, user_data);
}
ret = 0;
} else if (IS_ENABLED(CONFIG_NET_TCP) &&
net_context_get_type(context) == SOCK_STREAM) {
ret = net_tcp_connect(context, addr, laddr, rport, lport,
timeout, cb, user_data);
} else {
ret = -ENOTSUP;
}
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
int net_context_accept(struct net_context *context,
net_tcp_accept_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
int ret = 0;
NET_ASSERT(PART_OF_ARRAY(contexts, context));
if (!net_context_is_used(context)) {
return -EBADF;
}
k_mutex_lock(&context->lock, K_FOREVER);
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
ret = net_offload_accept(
net_context_get_iface(context),
context,
cb,
timeout,
user_data);
goto unlock;
}
if ((net_context_get_state(context) != NET_CONTEXT_LISTENING) &&
(net_context_get_type(context) != SOCK_STREAM)) {
NET_DBG("Invalid socket, state %d type %d",
net_context_get_state(context),
net_context_get_type(context));
ret = -EINVAL;
goto unlock;
}
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
ret = net_tcp_accept(context, cb, user_data);
goto unlock;
}
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
static int get_context_priority(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_NET_CONTEXT_PRIORITY)
*((uint8_t *)value) = context->options.priority;
if (len) {
*len = sizeof(uint8_t);
}
return 0;
#else
return -ENOTSUP;
#endif
}
static int get_context_timepstamp(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_NET_CONTEXT_TIMESTAMP)
*((bool *)value) = context->options.timestamp;
if (len) {
*len = sizeof(bool);
}
return 0;
#else
return -ENOTSUP;
#endif
}
static int get_context_proxy(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_SOCKS)
struct sockaddr *addr = (struct sockaddr *)value;
if (!value || !len) {
return -EINVAL;
}
if (*len < context->options.proxy.addrlen) {
return -EINVAL;
}
*len = MIN(context->options.proxy.addrlen, *len);
memcpy(addr, &context->options.proxy.addr, *len);
return 0;
#else
return -ENOTSUP;
#endif
}
#if defined(CONFIG_NET_CONTEXT_TIMESTAMP)
int net_context_get_timestamp(struct net_context *context,
struct net_pkt *pkt,
struct net_ptp_time *timestamp)
{
bool is_timestamped;
get_context_timepstamp(context, &is_timestamped, NULL);
if (is_timestamped) {
memcpy(timestamp, net_pkt_timestamp(pkt), sizeof(*timestamp));
return 0;
}
return -ENOENT;
}
#endif /* CONFIG_NET_CONTEXT_TIMESTAMP */
static int get_context_txtime(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_NET_CONTEXT_TXTIME)
*((bool *)value) = context->options.txtime;
if (len) {
*len = sizeof(bool);
}
return 0;
#else
return -ENOTSUP;
#endif
}
/* If buf is not NULL, then use it. Otherwise read the data to be written
* to net_pkt from msghdr.
*/
static int context_write_data(struct net_pkt *pkt, const void *buf,
int buf_len, const struct msghdr *msghdr)
{
int ret = 0;
if (msghdr) {
int i;
for (i = 0; i < msghdr->msg_iovlen; i++) {
ret = net_pkt_write(pkt, msghdr->msg_iov[i].iov_base,
msghdr->msg_iov[i].iov_len);
if (ret < 0) {
break;
}
}
} else {
ret = net_pkt_write(pkt, buf, buf_len);
}
return ret;
}
static int context_setup_udp_packet(struct net_context *context,
struct net_pkt *pkt,
const void *buf,
size_t len,
const struct msghdr *msg,
const struct sockaddr *dst_addr,
socklen_t addrlen)
{
int ret = -EINVAL;
uint16_t dst_port = 0U;
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr;
dst_port = addr6->sin6_port;
ret = net_context_create_ipv6_new(context, pkt,
NULL, &addr6->sin6_addr);
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr;
dst_port = addr4->sin_port;
ret = net_context_create_ipv4_new(context, pkt,
NULL, &addr4->sin_addr);
}
if (ret < 0) {
return ret;
}
ret = bind_default(context);
if (ret) {
return ret;
}
ret = net_udp_create(pkt,
net_sin((struct sockaddr *)
&context->local)->sin_port,
dst_port);
if (ret) {
return ret;
}
ret = context_write_data(pkt, buf, len, msg);
if (ret) {
return ret;
}
return 0;
}
static void context_finalize_packet(struct net_context *context,
struct net_pkt *pkt)
{
/* This function is meant to be temporary: once all moved to new
* API, it will be up to net_send_data() to finalize the packet.
*/
net_pkt_cursor_init(pkt);
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
net_ipv6_finalize(pkt, net_context_get_ip_proto(context));
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
net_ipv4_finalize(pkt, net_context_get_ip_proto(context));
}
}
static struct net_pkt *context_alloc_pkt(struct net_context *context,
size_t len, k_timeout_t timeout)
{
struct net_pkt *pkt;
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
if (context->tx_slab) {
pkt = net_pkt_alloc_from_slab(context->tx_slab(), timeout);
if (!pkt) {
return NULL;
}
net_pkt_set_iface(pkt, net_context_get_iface(context));
net_pkt_set_family(pkt, net_context_get_family(context));
net_pkt_set_context(pkt, context);
if (net_pkt_alloc_buffer(pkt, len,
net_context_get_ip_proto(context),
timeout)) {
net_pkt_unref(pkt);
return NULL;
}
return pkt;
}
#endif
pkt = net_pkt_alloc_with_buffer(net_context_get_iface(context), len,
net_context_get_family(context),
net_context_get_ip_proto(context),
timeout);
if (pkt) {
net_pkt_set_context(pkt, context);
}
return pkt;
}
static void set_pkt_txtime(struct net_pkt *pkt, const struct msghdr *msghdr)
{
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msghdr); cmsg != NULL;
cmsg = CMSG_NXTHDR(msghdr, cmsg)) {
if (cmsg->cmsg_len == CMSG_LEN(sizeof(uint64_t)) &&
cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_TXTIME) {
uint64_t txtime = *(uint64_t *)CMSG_DATA(cmsg);
net_pkt_set_txtime(pkt, txtime);
break;
}
}
}
static int context_sendto(struct net_context *context,
const void *buf,
size_t len,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
k_timeout_t timeout,
void *user_data,
bool sendto)
{
const struct msghdr *msghdr = NULL;
struct net_pkt *pkt;
size_t tmp_len;
int ret;
NET_ASSERT(PART_OF_ARRAY(contexts, context));
if (!net_context_is_used(context)) {
return -EBADF;
}
if (sendto && addrlen == 0 && dst_addr == NULL && buf != NULL) {
/* User wants to call sendmsg */
msghdr = buf;
}
if (!msghdr && !dst_addr &&
!(IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
net_context_get_ip_proto(context) == CAN_RAW)) {
return -EDESTADDRREQ;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
const struct sockaddr_in6 *addr6 =
(const struct sockaddr_in6 *)dst_addr;
if (msghdr) {
addr6 = msghdr->msg_name;
addrlen = msghdr->msg_namelen;
if (!addr6) {
addr6 = net_sin6(&context->remote);
addrlen = sizeof(struct sockaddr_in6);
}
/* For sendmsg(), the dst_addr is NULL so set it here.
*/
dst_addr = (const struct sockaddr *)addr6;
}
if (addrlen < sizeof(struct sockaddr_in6)) {
return -EINVAL;
}
if (net_ipv6_is_addr_unspecified(&addr6->sin6_addr)) {
return -EDESTADDRREQ;
}
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
const struct sockaddr_in *addr4 =
(const struct sockaddr_in *)dst_addr;
if (msghdr) {
addr4 = msghdr->msg_name;
addrlen = msghdr->msg_namelen;
if (!addr4) {
addr4 = net_sin(&context->remote);
addrlen = sizeof(struct sockaddr_in);
}
/* For sendmsg(), the dst_addr is NULL so set it here.
*/
dst_addr = (const struct sockaddr *)addr4;
}
if (addrlen < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
if (!addr4->sin_addr.s_addr) {
return -EDESTADDRREQ;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
net_context_get_family(context) == AF_PACKET) {
struct sockaddr_ll *ll_addr = (struct sockaddr_ll *)dst_addr;
struct net_if *iface;
if (msghdr) {
ll_addr = msghdr->msg_name;
addrlen = msghdr->msg_namelen;
if (!ll_addr) {
ll_addr = (struct sockaddr_ll *)
(&context->remote);
addrlen = sizeof(struct sockaddr_ll);
}
/* For sendmsg(), the dst_addr is NULL so set it here.
*/
dst_addr = (const struct sockaddr *)ll_addr;
}
if (addrlen < sizeof(struct sockaddr_ll)) {
return -EINVAL;
}
if (ll_addr->sll_ifindex < 0) {
return -EDESTADDRREQ;
}
iface = net_if_get_by_index(ll_addr->sll_ifindex);
if (!iface) {
NET_ERR("Cannot bind to interface index %d",
ll_addr->sll_ifindex);
return -EDESTADDRREQ;
}
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
net_context_get_family(context) == AF_CAN) {
struct sockaddr_can *can_addr = (struct sockaddr_can *)dst_addr;
struct net_if *iface;
if (addrlen < sizeof(struct sockaddr_can)) {
return -EINVAL;
}
if (can_addr->can_ifindex < 0) {
/* The index should have been set in bind */
can_addr->can_ifindex =
net_can_ptr(&context->local)->can_ifindex;
}
if (can_addr->can_ifindex < 0) {
return -EDESTADDRREQ;
}
iface = net_if_get_by_index(can_addr->can_ifindex);
if (!iface) {
NET_ERR("Cannot bind to interface index %d",
can_addr->can_ifindex);
return -EDESTADDRREQ;
}
} else {
NET_DBG("Invalid protocol family %d",
net_context_get_family(context));
return -EINVAL;
}
if (msghdr && len == 0) {
int i;
for (i = 0; i < msghdr->msg_iovlen; i++) {
len += msghdr->msg_iov[i].iov_len;
}
}
pkt = context_alloc_pkt(context, len, PKT_WAIT_TIME);
if (!pkt) {
return -ENOMEM;
}
tmp_len = net_pkt_available_payload_buffer(
pkt, net_context_get_ip_proto(context));
if (tmp_len < len) {
len = tmp_len;
}
context->send_cb = cb;
context->user_data = user_data;
if (IS_ENABLED(CONFIG_NET_CONTEXT_PRIORITY)) {
uint8_t priority;
get_context_priority(context, &priority, NULL);
net_pkt_set_priority(pkt, priority);
}
if (IS_ENABLED(CONFIG_NET_CONTEXT_TIMESTAMP)) {
bool timestamp;
get_context_timepstamp(context, &timestamp, NULL);
if (timestamp) {
struct net_ptp_time tp = {
/* Use the nanosecond field to temporarily
* store the cycle count as it is a 32-bit
* variable. The value is checked in
* net_if.c:net_if_tx()
*
* The net_pkt timestamp field is used in two
* roles here:
* 1) To calculate how long it takes the packet
* from net_context to be sent by the
* network device driver.
* 2) gPTP enabled Ethernet device driver will
* use the value to tell gPTP what time the
* packet was sent.
*
* Because these two things are happening at
* different times, we can share the variable.
*/
.nanosecond = k_cycle_get_32(),
};
net_pkt_set_timestamp(pkt, &tp);
}
}
/* If there is ancillary data in msghdr, then we need to add that
* to net_pkt as there is no other way to store it.
*/
if (msghdr && msghdr->msg_control && msghdr->msg_controllen) {
if (IS_ENABLED(CONFIG_NET_CONTEXT_TXTIME)) {
bool is_txtime;
get_context_txtime(context, &is_txtime, NULL);
if (is_txtime) {
set_pkt_txtime(pkt, msghdr);
}
}
}
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
ret = context_write_data(pkt, buf, len, msghdr);
if (ret < 0) {
goto fail;
}
net_pkt_cursor_init(pkt);
if (sendto) {
ret = net_offload_sendto(net_context_get_iface(context),
pkt, dst_addr, addrlen, cb,
timeout, user_data);
} else {
ret = net_offload_send(net_context_get_iface(context),
pkt, cb, timeout, user_data);
}
} else if (IS_ENABLED(CONFIG_NET_UDP) &&
net_context_get_ip_proto(context) == IPPROTO_UDP) {
ret = context_setup_udp_packet(context, pkt, buf, len, msghdr,
dst_addr, addrlen);
if (ret < 0) {
goto fail;
}
context_finalize_packet(context, pkt);
ret = net_send_data(pkt);
} else if (IS_ENABLED(CONFIG_NET_TCP) &&
net_context_get_ip_proto(context) == IPPROTO_TCP) {
ret = context_write_data(pkt, buf, len, msghdr);
if (ret < 0) {
goto fail;
}
net_pkt_cursor_init(pkt);
ret = net_tcp_queue_data(context, pkt);
if (ret < 0) {
goto fail;
}
ret = net_tcp_send_data(context, cb, user_data);
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
net_context_get_family(context) == AF_PACKET) {
ret = context_write_data(pkt, buf, len, msghdr);
if (ret < 0) {
goto fail;
}
net_pkt_cursor_init(pkt);
net_if_queue_tx(net_pkt_iface(pkt), pkt);
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
net_context_get_family(context) == AF_CAN &&
net_context_get_ip_proto(context) == CAN_RAW) {
ret = context_write_data(pkt, buf, len, msghdr);
if (ret < 0) {
goto fail;
}
net_pkt_cursor_init(pkt);
ret = net_send_data(pkt);
} else {
NET_DBG("Unknown protocol while sending packet: %d",
net_context_get_ip_proto(context));
ret = -EPROTONOSUPPORT;
}
if (ret < 0) {
goto fail;
}
return len;
fail:
net_pkt_unref(pkt);
return ret;
}
int net_context_send(struct net_context *context,
const void *buf,
size_t len,
net_context_send_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
socklen_t addrlen;
int ret = 0;
k_mutex_lock(&context->lock, K_FOREVER);
if (!(context->flags & NET_CONTEXT_REMOTE_ADDR_SET) ||
!net_sin(&context->remote)->sin_port) {
ret = -EDESTADDRREQ;
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
addrlen = sizeof(struct sockaddr_in6);
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
addrlen = sizeof(struct sockaddr_in);
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
net_context_get_family(context) == AF_PACKET) {
ret = -EOPNOTSUPP;
goto unlock;
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
net_context_get_family(context) == AF_CAN) {
addrlen = sizeof(struct sockaddr_can);
} else {
addrlen = 0;
}
ret = context_sendto(context, buf, len, &context->remote,
addrlen, cb, timeout, user_data, false);
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
int net_context_sendmsg(struct net_context *context,
const struct msghdr *msghdr,
int flags,
net_context_send_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
int ret;
k_mutex_lock(&context->lock, K_FOREVER);
ret = context_sendto(context, msghdr, 0, NULL, 0,
cb, timeout, user_data, true);
k_mutex_unlock(&context->lock);
return ret;
}
int net_context_sendto(struct net_context *context,
const void *buf,
size_t len,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
int ret;
k_mutex_lock(&context->lock, K_FOREVER);
ret = context_sendto(context, buf, len, dst_addr, addrlen,
cb, timeout, user_data, true);
k_mutex_unlock(&context->lock);
return ret;
}
enum net_verdict net_context_packet_received(struct net_conn *conn,
struct net_pkt *pkt,
union net_ip_header *ip_hdr,
union net_proto_header *proto_hdr,
void *user_data)
{
struct net_context *context = find_context(conn);
enum net_verdict verdict = NET_DROP;
NET_ASSERT(context);
NET_ASSERT(net_pkt_iface(pkt));
k_mutex_lock(&context->lock, K_FOREVER);
net_context_set_iface(context, net_pkt_iface(pkt));
net_pkt_set_context(pkt, context);
/* If there is no callback registered, then we can only drop
* the packet.
*/
if (!context->recv_cb) {
goto unlock;
}
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
net_stats_update_tcp_recv(net_pkt_iface(pkt),
net_pkt_remaining_data(pkt));
}
context->recv_cb(context, pkt, ip_hdr, proto_hdr, 0, user_data);
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
k_sem_give(&context->recv_data_wait);
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
verdict = NET_OK;
unlock:
k_mutex_unlock(&context->lock);
return verdict;
}
#if defined(CONFIG_NET_UDP)
static int recv_udp(struct net_context *context,
net_context_recv_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
struct sockaddr local_addr = {
.sa_family = net_context_get_family(context),
};
struct sockaddr *laddr = NULL;
uint16_t lport = 0U;
int ret;
ARG_UNUSED(timeout);
if (context->conn_handler) {
net_conn_unregister(context->conn_handler);
context->conn_handler = NULL;
}
ret = bind_default(context);
if (ret) {
return ret;
}
if (IS_ENABLED(CONFIG_NET_IPV6) &&
net_context_get_family(context) == AF_INET6) {
if (net_sin6_ptr(&context->local)->sin6_addr) {
net_ipaddr_copy(&net_sin6(&local_addr)->sin6_addr,
net_sin6_ptr(&context->local)->sin6_addr);
laddr = &local_addr;
}
net_sin6(&local_addr)->sin6_port =
net_sin6((struct sockaddr *)&context->local)->sin6_port;
lport = net_sin6((struct sockaddr *)&context->local)->sin6_port;
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
net_context_get_family(context) == AF_INET) {
if (net_sin_ptr(&context->local)->sin_addr) {
net_ipaddr_copy(&net_sin(&local_addr)->sin_addr,
net_sin_ptr(&context->local)->sin_addr);
laddr = &local_addr;
}
lport = net_sin((struct sockaddr *)&context->local)->sin_port;
}
context->recv_cb = cb;
ret = net_conn_register(net_context_get_ip_proto(context),
net_context_get_family(context),
context->flags & NET_CONTEXT_REMOTE_ADDR_SET ?
&context->remote : NULL,
laddr,
ntohs(net_sin(&context->remote)->sin_port),
ntohs(lport),
net_context_packet_received,
user_data,
&context->conn_handler);
return ret;
}
#else
#define recv_udp(...) 0
#endif /* CONFIG_NET_UDP */
static enum net_verdict net_context_raw_packet_received(
struct net_conn *conn,
struct net_pkt *pkt,
union net_ip_header *ip_hdr,
union net_proto_header *proto_hdr,
void *user_data)
{
struct net_context *context = find_context(conn);
NET_ASSERT(context);
NET_ASSERT(net_pkt_iface(pkt));
/* If there is no callback registered, then we can only drop
* the packet.
*/
if (!context->recv_cb) {
return NET_DROP;
}
net_context_set_iface(context, net_pkt_iface(pkt));
net_pkt_set_context(pkt, context);
context->recv_cb(context, pkt, ip_hdr, proto_hdr, 0, user_data);
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
k_sem_give(&context->recv_data_wait);
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
return NET_OK;
}
static int recv_raw(struct net_context *context,
net_context_recv_cb_t cb,
k_timeout_t timeout,
struct sockaddr *local_addr,
void *user_data)
{
int ret;
ARG_UNUSED(timeout);
context->recv_cb = cb;
if (context->conn_handler) {
net_conn_unregister(context->conn_handler);
context->conn_handler = NULL;
}
ret = bind_default(context);
if (ret) {
return ret;
}
ret = net_conn_register(net_context_get_ip_proto(context),
net_context_get_family(context),
NULL, local_addr, 0, 0,
net_context_raw_packet_received,
user_data,
&context->conn_handler);
return ret;
}
int net_context_recv(struct net_context *context,
net_context_recv_cb_t cb,
k_timeout_t timeout,
void *user_data)
{
int ret;
NET_ASSERT(context);
if (!net_context_is_used(context)) {
return -EBADF;
}
k_mutex_lock(&context->lock, K_FOREVER);
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
ret = net_offload_recv(
net_context_get_iface(context),
context, cb, timeout, user_data);
goto unlock;
}
if (IS_ENABLED(CONFIG_NET_UDP) &&
net_context_get_ip_proto(context) == IPPROTO_UDP) {
ret = recv_udp(context, cb, timeout, user_data);
} else if (IS_ENABLED(CONFIG_NET_TCP) &&
net_context_get_ip_proto(context) == IPPROTO_TCP) {
ret = net_tcp_recv(context, cb, user_data);
} else {
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
net_context_get_family(context) == AF_PACKET) {
struct sockaddr_ll addr;
addr.sll_family = AF_PACKET;
addr.sll_ifindex =
net_sll_ptr(&context->local)->sll_ifindex;
addr.sll_protocol =
net_sll_ptr(&context->local)->sll_protocol;
memcpy(addr.sll_addr,
net_sll_ptr(&context->local)->sll_addr,
sizeof(addr.sll_addr));
ret = recv_raw(context, cb, timeout,
(struct sockaddr *)&addr, user_data);
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
net_context_get_family(context) == AF_CAN) {
struct sockaddr_can local_addr = {
.can_family = AF_CAN,
};
ret = recv_raw(context, cb, timeout,
(struct sockaddr *)&local_addr,
user_data);
if (ret == -EALREADY) {
/* This is perfectly normal for CAN sockets.
* The SocketCAN will dispatch the packet to
* correct net_context listener.
*/
ret = 0;
}
} else {
ret = -EPROTOTYPE;
}
}
if (ret < 0) {
goto unlock;
}
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
if (!K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
int ret;
/* Make sure we have the lock, then the
* net_context_packet_received() callback will release the
* semaphore when data has been received.
*/
k_sem_reset(&context->recv_data_wait);
k_mutex_unlock(&context->lock);
ret = k_sem_take(&context->recv_data_wait, timeout);
k_mutex_lock(&context->lock, K_FOREVER);
if (ret == -EAGAIN) {
ret = -ETIMEDOUT;
goto unlock;
}
}
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
unlock:
k_mutex_unlock(&context->lock);
return ret;
}
int net_context_update_recv_wnd(struct net_context *context,
int32_t delta)
{
int ret;
if (IS_ENABLED(CONFIG_NET_OFFLOAD) &&
net_if_is_ip_offloaded(net_context_get_iface(context))) {
return 0;
}
k_mutex_lock(&context->lock, K_FOREVER);
ret = net_tcp_update_recv_wnd(context, delta);
k_mutex_unlock(&context->lock);
return ret;
}
static int set_context_priority(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_NET_CONTEXT_PRIORITY)
if (len > sizeof(uint8_t)) {
return -EINVAL;
}
context->options.priority = *((uint8_t *)value);
return 0;
#else
return -ENOTSUP;
#endif
}
static int set_context_timestamp(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_NET_CONTEXT_TIMESTAMP)
if (len > sizeof(bool)) {
return -EINVAL;
}
context->options.timestamp = *((bool *)value);
return 0;
#else
return -ENOTSUP;
#endif
}
static int set_context_txtime(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_NET_CONTEXT_TXTIME)
if (len > sizeof(bool)) {
return -EINVAL;
}
context->options.txtime = *((bool *)value);
return 0;
#else
return -ENOTSUP;
#endif
}
static int set_context_proxy(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_SOCKS)
struct sockaddr *addr = (struct sockaddr *)value;
if (len > NET_SOCKADDR_MAX_SIZE) {
return -EINVAL;
}
if (addr->sa_family != net_context_get_family(context)) {
return -EINVAL;
}
context->options.proxy.addrlen = len;
memcpy(&context->options.proxy.addr, addr, len);
return 0;
#else
return -ENOTSUP;
#endif
}
int net_context_set_option(struct net_context *context,
enum net_context_option option,
const void *value, size_t len)
{
int ret = 0;
NET_ASSERT(context);
if (!PART_OF_ARRAY(contexts, context)) {
return -EINVAL;
}
k_mutex_lock(&context->lock, K_FOREVER);
switch (option) {
case NET_OPT_PRIORITY:
ret = set_context_priority(context, value, len);
break;
case NET_OPT_TIMESTAMP:
ret = set_context_timestamp(context, value, len);
break;
case NET_OPT_TXTIME:
ret = set_context_txtime(context, value, len);
break;
case NET_OPT_SOCKS5:
ret = set_context_proxy(context, value, len);
break;
}
k_mutex_unlock(&context->lock);
return ret;
}
int net_context_get_option(struct net_context *context,
enum net_context_option option,
void *value, size_t *len)
{
int ret = 0;
NET_ASSERT(context);
if (!PART_OF_ARRAY(contexts, context)) {
return -EINVAL;
}
k_mutex_lock(&context->lock, K_FOREVER);
switch (option) {
case NET_OPT_PRIORITY:
ret = get_context_priority(context, value, len);
break;
case NET_OPT_TIMESTAMP:
ret = get_context_timepstamp(context, value, len);
break;
case NET_OPT_TXTIME:
ret = get_context_txtime(context, value, len);
break;
case NET_OPT_SOCKS5:
ret = get_context_proxy(context, value, len);
break;
}
k_mutex_unlock(&context->lock);
return ret;
}
void net_context_foreach(net_context_cb_t cb, void *user_data)
{
int i;
k_sem_take(&contexts_lock, K_FOREVER);
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (!net_context_is_used(&contexts[i])) {
continue;
}
k_mutex_lock(&contexts[i].lock, K_FOREVER);
cb(&contexts[i], user_data);
k_mutex_unlock(&contexts[i].lock);
}
k_sem_give(&contexts_lock);
}
void net_context_init(void)
{
k_sem_init(&contexts_lock, 1, UINT_MAX);
}