zephyr/subsys/net/ip/net_context.c
Paul Sokolovsky 19ff963693 net: tcp: Allow to explicitly manage TCP receive window
This fixes the existing situation that "if application buffers data,
it's the problem of application". It's actually the problem of the
stack, as it doesn't allow application to control receive window,
and without this control, any buffer will overflow, peer packets
will be dropped, peer won't receive acks for them, and will employ
exponential backoff, the connection will crawl to a halt.

This patch adds net_context_tcp_recved() function which an
application must explicitly call when it *processes* data, to
advance receive window.

Jira: ZEP-1999

Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
2017-08-05 11:40:50 +03:00

2482 lines
58 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
*/
#if defined(CONFIG_NET_DEBUG_CONTEXT)
#define SYS_LOG_DOMAIN "net/ctx"
#define NET_LOG_ENABLED 1
#endif
#include <kernel.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <net/net_pkt.h>
#include <net/net_ip.h>
#include <net/net_context.h>
#include <net/net_offload.h>
#include "connection.h"
#include "net_private.h"
#include "ipv6.h"
#include "ipv4.h"
#include "udp_internal.h"
#include "tcp.h"
#include "net_stats.h"
#define NET_MAX_CONTEXT CONFIG_NET_MAX_CONTEXTS
#if defined(CONFIG_NET_TCP_ACK_TIMEOUT)
#define ACK_TIMEOUT CONFIG_NET_TCP_ACK_TIMEOUT
#else
#define ACK_TIMEOUT K_SECONDS(1)
#endif
#define FIN_TIMEOUT K_SECONDS(1)
/* Declares a wrapper function for a net_conn callback that refs the
* context around the invocation (to protect it from premature
* deletion). Long term would be nice to see this feature be part of
* the connection type itself, but right now it has opaque "user_data"
* pointers and doesn't understand what a net_context is.
*/
#define NET_CONN_CB(name) \
static enum net_verdict _##name(struct net_conn *conn, \
struct net_pkt *pkt, \
void *user_data); \
static enum net_verdict name(struct net_conn *conn, \
struct net_pkt *pkt, \
void *user_data) \
{ \
enum net_verdict result; \
\
net_context_ref(user_data); \
result = _##name(conn, pkt, user_data); \
net_context_unref(user_data); \
return result; \
} \
static enum net_verdict _##name(struct net_conn *conn, \
struct net_pkt *pkt, \
void *user_data) \
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;
static enum net_verdict packet_received(struct net_conn *conn,
struct net_pkt *pkt,
void *user_data);
static void set_appdata_values(struct net_pkt *pkt, enum net_ip_protocol proto);
#if defined(CONFIG_NET_TCP)
static int send_reset(struct net_context *context, struct sockaddr *remote);
static struct tcp_backlog_entry {
struct net_tcp *tcp;
struct sockaddr remote;
u32_t recv_max_ack;
u32_t send_seq;
u32_t send_ack;
struct k_delayed_work ack_timer;
bool cancelled;
} tcp_backlog[CONFIG_NET_TCP_BACKLOG_SIZE];
static void backlog_ack_timeout(struct k_work *work)
{
struct tcp_backlog_entry *backlog =
CONTAINER_OF(work, struct tcp_backlog_entry, ack_timer);
if (!backlog->cancelled) {
NET_DBG("Did not receive ACK in %dms", ACK_TIMEOUT);
send_reset(backlog->tcp->context, &backlog->remote);
}
memset(backlog, 0, sizeof(struct tcp_backlog_entry));
}
static struct sockaddr *create_sockaddr(struct net_pkt *pkt,
struct sockaddr *addr)
{
struct net_tcp_hdr hdr, *tcp_hdr;
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return NULL;
}
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
net_ipaddr_copy(&net_sin6(addr)->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
net_sin6(addr)->sin6_port = tcp_hdr->src_port;
net_sin6(addr)->sin6_family = AF_INET6;
} else
#endif
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
net_ipaddr_copy(&net_sin(addr)->sin_addr,
&NET_IPV4_HDR(pkt)->src);
net_sin(addr)->sin_port = tcp_hdr->src_port;
net_sin(addr)->sin_family = AF_INET;
} else
#endif
{
return NULL;
}
return addr;
}
static int tcp_backlog_find(struct net_pkt *pkt, int *empty_slot)
{
struct net_tcp_hdr hdr, *tcp_hdr;
int i, empty = -1;
for (i = 0; i < CONFIG_NET_TCP_BACKLOG_SIZE; i++) {
if (tcp_backlog[i].tcp == NULL && empty < 0) {
empty = i;
continue;
}
if (net_pkt_family(pkt) != tcp_backlog[i].remote.family) {
continue;
}
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
switch (net_pkt_family(pkt)) {
#if defined(CONFIG_NET_IPV6)
case AF_INET6:
if (net_sin6(&tcp_backlog[i].remote)->sin6_port !=
tcp_hdr->src_port) {
continue;
}
if (memcmp(&net_sin6(&tcp_backlog[i].remote)->sin6_addr,
&NET_IPV6_HDR(pkt)->src,
sizeof(struct in6_addr))) {
continue;
}
break;
#endif
#if defined(CONFIG_NET_IPV4)
case AF_INET:
if (net_sin(&tcp_backlog[i].remote)->sin_port !=
tcp_hdr->src_port) {
continue;
}
if (memcmp(&net_sin(&tcp_backlog[i].remote)->sin_addr,
&NET_IPV4_HDR(pkt)->src,
sizeof(struct in_addr))) {
continue;
}
break;
#endif
}
return i;
}
if (empty_slot) {
*empty_slot = empty;
}
return -EADDRNOTAVAIL;
}
static int tcp_backlog_syn(struct net_pkt *pkt, struct net_context *context)
{
int empty_slot = -1;
if (tcp_backlog_find(pkt, &empty_slot) >= 0) {
return -EADDRINUSE;
}
if (empty_slot < 0) {
return -ENOSPC;
}
tcp_backlog[empty_slot].tcp = context->tcp;
create_sockaddr(pkt, &tcp_backlog[empty_slot].remote);
tcp_backlog[empty_slot].recv_max_ack = context->tcp->recv_max_ack;
tcp_backlog[empty_slot].send_seq = context->tcp->send_seq;
tcp_backlog[empty_slot].send_ack = context->tcp->send_ack;
k_delayed_work_init(&tcp_backlog[empty_slot].ack_timer,
backlog_ack_timeout);
k_delayed_work_submit(&tcp_backlog[empty_slot].ack_timer, ACK_TIMEOUT);
return 0;
}
static int tcp_backlog_ack(struct net_pkt *pkt, struct net_context *context)
{
struct net_tcp_hdr hdr, *tcp_hdr;
int r;
r = tcp_backlog_find(pkt, NULL);
if (r < 0) {
return r;
}
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return -EINVAL;
}
/* Sent SEQ + 1 needs to be the same as the received ACK */
if (tcp_backlog[r].send_seq + 1 != sys_get_be32(tcp_hdr->ack)) {
return -EINVAL;
}
memcpy(&context->remote, &tcp_backlog[r].remote,
sizeof(struct sockaddr));
context->tcp->recv_max_ack = tcp_backlog[r].recv_max_ack;
context->tcp->send_seq = tcp_backlog[r].send_seq;
context->tcp->send_ack = tcp_backlog[r].send_ack;
if (k_delayed_work_cancel(&tcp_backlog[r].ack_timer) < 0) {
/* Too late to cancel - just set flag for worker.
* TODO: Note that in this case, we can be preempted
* anytime (could have been preempted even before we did
* the check), so access to tcp_backlog should be synchronized
* between this function and worker.
*/
tcp_backlog[r].cancelled = true;
} else {
memset(&tcp_backlog[r], 0, sizeof(struct tcp_backlog_entry));
}
return 0;
}
static int tcp_backlog_rst(struct net_pkt *pkt)
{
struct net_tcp_hdr hdr, *tcp_hdr;
int r;
r = tcp_backlog_find(pkt, NULL);
if (r < 0) {
return r;
}
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return -EINVAL;
}
/* The ACK sent needs to be the same as the received SEQ */
if (tcp_backlog[r].send_ack != sys_get_be32(tcp_hdr->seq)) {
return -EINVAL;
}
if (k_delayed_work_cancel(&tcp_backlog[r].ack_timer) < 0) {
/* Too late to cancel - just set flag for worker.
* TODO: Note that in this case, we can be preempted
* anytime (could have been preempted even before we did
* the check), so access to tcp_backlog should be synchronized
* between this function and worker.
*/
tcp_backlog[r].cancelled = true;
} else {
memset(&tcp_backlog[r], 0, sizeof(struct tcp_backlog_entry));
}
return 0;
}
static void handle_fin_timeout(struct k_work *work)
{
struct net_tcp *tcp =
CONTAINER_OF(work, struct net_tcp, fin_timer);
if (!tcp->fin_timer_cancelled) {
NET_DBG("Did not receive FIN in %dms", FIN_TIMEOUT);
net_context_unref(tcp->context);
}
}
static void handle_ack_timeout(struct k_work *work)
{
/* This means that we did not receive ACK response in time. */
struct net_tcp *tcp = CONTAINER_OF(work, struct net_tcp, ack_timer);
if (tcp->ack_timer_cancelled) {
return;
}
NET_DBG("Did not receive ACK in %dms while in %s", ACK_TIMEOUT,
net_tcp_state_str(net_tcp_get_state(tcp)));
if (net_tcp_get_state(tcp) == NET_TCP_LAST_ACK) {
/* We did not receive the last ACK on time. We can only
* close the connection at this point. We will not send
* anything to peer in this last state, but will go directly
* to to CLOSED state.
*/
net_tcp_change_state(tcp, NET_TCP_CLOSED);
net_context_unref(tcp->context);
}
}
#endif /* CONFIG_NET_TCP */
static int check_used_port(enum net_ip_protocol ip_proto,
u16_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 (local_addr->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 (net_ipv4_addr_cmp(
net_sin_ptr(&contexts[i].local)->
sin_addr,
&((struct sockaddr_in *)
local_addr)->sin_addr)) {
return -EEXIST;
}
}
}
return 0;
}
static u16_t find_available_port(struct net_context *context,
const struct sockaddr *addr)
{
if (!net_sin(addr)->sin_port) {
u16_t local_port;
do {
local_port = sys_rand32_get() | 0x8000;
if (local_port <= 1023) {
/* 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);
}
return net_sin(addr)->sin_port;
}
int net_context_get(sa_family_t family,
enum net_sock_type type,
enum net_ip_protocol ip_proto,
struct net_context **context)
{
int i, ret = -ENOENT;
#if defined(CONFIG_NET_CONTEXT_CHECK)
#if !defined(CONFIG_NET_IPV4)
if (family == AF_INET) {
NET_ASSERT_INFO(family != AF_INET, "IPv4 disabled");
return -EPFNOSUPPORT;
}
#endif
#if !defined(CONFIG_NET_IPV6)
if (family == AF_INET6) {
NET_ASSERT_INFO(family != AF_INET6, "IPv6 disabled");
return -EPFNOSUPPORT;
}
#endif
#if !defined(CONFIG_NET_UDP)
if (type == SOCK_DGRAM) {
NET_ASSERT_INFO(type != SOCK_DGRAM,
"Datagram context disabled");
return -EPROTOTYPE;
}
if (ip_proto == IPPROTO_UDP) {
NET_ASSERT_INFO(ip_proto != IPPROTO_UDP, "UDP disabled");
return -EPROTONOSUPPORT;
}
#endif
#if !defined(CONFIG_NET_TCP)
if (type == SOCK_STREAM) {
NET_ASSERT_INFO(type != SOCK_STREAM,
"Stream context disabled");
return -EPROTOTYPE;
}
if (ip_proto == IPPROTO_TCP) {
NET_ASSERT_INFO(ip_proto != IPPROTO_TCP, "TCP disabled");
return -EPROTONOSUPPORT;
}
#endif
if (family != AF_INET && family != AF_INET6) {
NET_ASSERT_INFO(family == AF_INET || family == AF_INET6,
"Unknown address family %d", family);
return -EAFNOSUPPORT;
}
if (type != SOCK_DGRAM && type != SOCK_STREAM) {
NET_ASSERT_INFO(type == SOCK_DGRAM || type == SOCK_STREAM,
"Unknown context type");
return -EPROTOTYPE;
}
if (ip_proto != IPPROTO_UDP && ip_proto != IPPROTO_TCP) {
NET_ASSERT_INFO(ip_proto == IPPROTO_UDP ||
ip_proto == IPPROTO_TCP,
"Unknown IP protocol %d", ip_proto);
return -EPROTONOSUPPORT;
}
if ((type == SOCK_STREAM && ip_proto == IPPROTO_UDP) ||
(type == SOCK_DGRAM && ip_proto == IPPROTO_TCP)) {
NET_ASSERT_INFO(\
(type != SOCK_STREAM || ip_proto != IPPROTO_UDP) &&
(type != SOCK_DGRAM || ip_proto != IPPROTO_TCP),
"Context type and protocol mismatch, type %d proto %d",
type, ip_proto);
return -EOPNOTSUPP;
}
if (!context) {
NET_ASSERT_INFO(context, "Invalid context");
return -EINVAL;
}
#endif /* CONFIG_NET_CONTEXT_CHECK */
k_sem_take(&contexts_lock, K_FOREVER);
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (net_context_is_used(&contexts[i])) {
continue;
}
#if defined(CONFIG_NET_TCP)
if (ip_proto == IPPROTO_TCP) {
contexts[i].tcp = net_tcp_alloc(&contexts[i]);
if (!contexts[i].tcp) {
NET_ASSERT_INFO(contexts[i].tcp,
"Cannot allocate TCP context");
return -ENOBUFS;
}
k_delayed_work_init(&contexts[i].tcp->ack_timer,
handle_ack_timeout);
k_delayed_work_init(&contexts[i].tcp->fin_timer,
handle_fin_timeout);
}
#endif /* CONFIG_NET_TCP */
contexts[i].flags = 0;
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);
contexts[i].flags |= NET_CONTEXT_IN_USE;
contexts[i].iface = 0;
memset(&contexts[i].remote, 0, sizeof(struct sockaddr));
memset(&contexts[i].local, 0, sizeof(struct sockaddr_ptr));
#if defined(CONFIG_NET_IPV6)
if (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) {
return -EADDRINUSE;
}
}
#endif
#if defined(CONFIG_NET_IPV4)
if (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) {
return -EADDRINUSE;
}
}
#endif
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
k_sem_init(&contexts[i].recv_data_wait, 1, UINT_MAX);
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
*context = &contexts[i];
ret = 0;
break;
}
k_sem_give(&contexts_lock);
#if defined(CONFIG_NET_OFFLOAD)
/* FIXME - Figure out a way to get the correct network interface
* as it is not known at this point yet.
*/
if (!ret && 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;
}
#endif /* CONFIG_NET_OFFLOAD */
return ret;
}
#if defined(CONFIG_NET_TCP)
static void queue_fin(struct net_context *ctx)
{
struct net_pkt *pkt = NULL;
int ret;
ret = net_tcp_prepare_segment(ctx->tcp, NET_TCP_FIN, NULL, 0,
NULL, &ctx->remote, &pkt);
if (ret || !pkt) {
return;
}
ret = net_tcp_send_pkt(pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
}
#endif /* CONFIG_NET_TCP */
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_sem_take(&contexts_lock, K_FOREVER);
#if defined(CONFIG_NET_TCP)
if (context->tcp) {
net_tcp_release(context->tcp);
context->tcp = NULL;
}
#endif /* CONFIG_NET_TCP */
if (context->conn_handler) {
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_sem_give(&contexts_lock);
return 0;
}
int net_context_put(struct net_context *context)
{
NET_ASSERT(context);
if (!PART_OF_ARRAY(contexts, context)) {
return -EINVAL;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_context_get_iface(context))) {
k_sem_take(&contexts_lock, K_FOREVER);
context->flags &= ~NET_CONTEXT_IN_USE;
return net_offload_put(
net_context_get_iface(context), context);
}
#endif /* CONFIG_NET_OFFLOAD */
context->connect_cb = NULL;
context->recv_cb = NULL;
context->send_cb = NULL;
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
if ((net_context_get_state(context) == NET_CONTEXT_CONNECTED ||
net_context_get_state(context) == NET_CONTEXT_LISTENING)
&& !context->tcp->fin_rcvd) {
NET_DBG("TCP connection in active close, not "
"disposing yet (waiting %dms)", FIN_TIMEOUT);
k_delayed_work_submit(&context->tcp->fin_timer,
FIN_TIMEOUT);
context->tcp->fin_timer_cancelled = false;
queue_fin(context);
return 0;
}
}
#endif /* CONFIG_NET_TCP */
net_context_unref(context);
return 0;
}
/* 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 defined(CONFIG_NET_IPV6)
if (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));
}
#endif
#if defined(CONFIG_NET_IPV4)
if (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));
}
#endif
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 defined(CONFIG_NET_IPV6)
if (addr->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_is_ipv6_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_is_ipv6_addr_unspecified(&addr6->sin6_addr)) {
iface = net_if_get_default();
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",
net_sprint_ipv6_addr(&addr6->sin6_addr));
return -EADDRNOTAVAIL;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
#endif /* CONFIG_NET_OFFLOAD */
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(net_context_get_ip_proto(context)),
net_sprint_ipv6_addr(ptr), ntohs(addr6->sin6_port),
iface);
return 0;
}
#endif
#if defined(CONFIG_NET_IPV4)
if (addr->family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
struct net_if_addr *ifaddr;
struct net_if *iface;
struct in_addr *ptr;
int ret;
if (addrlen < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
if (addr4->sin_addr.s_addr == INADDR_ANY) {
iface = net_if_get_default();
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",
net_sprint_ipv4_addr(&addr4->sin_addr));
return -EADDRNOTAVAIL;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(iface)) {
net_context_set_iface(context, iface);
return net_offload_bind(iface,
context,
addr,
addrlen);
}
#endif /* CONFIG_NET_OFFLOAD */
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));
return ret;
}
} 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(net_context_get_ip_proto(context)),
net_sprint_ipv4_addr(ptr),
ntohs(addr4->sin_port), iface);
return 0;
}
#endif
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 defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_context_get_iface(context))) {
return net_offload_listen(
net_context_get_iface(context), context, backlog);
}
#endif /* CONFIG_NET_OFFLOAD */
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
net_tcp_change_state(context->tcp, NET_TCP_LISTEN);
net_context_set_state(context, NET_CONTEXT_LISTENING);
return 0;
}
#endif
return -EOPNOTSUPP;
}
#if defined(CONFIG_NET_TCP)
#if defined(CONFIG_NET_DEBUG_CONTEXT)
#define net_tcp_print_recv_info(str, pkt, port) \
do { \
if (net_context_get_family(context) == AF_INET6) { \
NET_DBG("%s received from %s port %d", str, \
net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src),\
ntohs(port)); \
} else if (net_context_get_family(context) == AF_INET) {\
NET_DBG("%s received from %s port %d", str, \
net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src),\
ntohs(port)); \
} \
} while (0)
#define net_tcp_print_send_info(str, pkt, port) \
do { \
struct net_context *ctx = net_pkt_context(pkt); \
if (net_context_get_family(ctx) == AF_INET6) { \
NET_DBG("%s sent to %s port %d", str, \
net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst),\
ntohs(port)); \
} else if (net_context_get_family(ctx) == AF_INET) { \
NET_DBG("%s sent to %s port %d", str, \
net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst),\
ntohs(port)); \
} \
} while (0)
#else
#define net_tcp_print_recv_info(...)
#define net_tcp_print_send_info(...)
#endif /* CONFIG_NET_DEBUG_CONTEXT */
static void print_send_info(struct net_pkt *pkt, const char *msg)
{
if (IS_ENABLED(CONFIG_NET_DEBUG_CONTEXT)) {
struct net_tcp_hdr hdr, *tcp_hdr;
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (tcp_hdr) {
net_tcp_print_send_info(msg, pkt, tcp_hdr->dst_port);
}
}
}
static inline int send_control_segment(struct net_context *context,
const struct sockaddr_ptr *local,
const struct sockaddr *remote,
int flags, const char *msg)
{
struct net_pkt *pkt = NULL;
int ret;
ret = net_tcp_prepare_segment(context->tcp, flags, NULL, 0,
local, remote, &pkt);
if (ret) {
return ret;
}
ret = net_send_data(pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
print_send_info(pkt, msg);
return ret;
}
static inline int send_syn(struct net_context *context,
const struct sockaddr *remote)
{
net_tcp_change_state(context->tcp, NET_TCP_SYN_SENT);
return send_control_segment(context, NULL, remote, NET_TCP_SYN, "SYN");
}
static inline int send_syn_ack(struct net_context *context,
struct sockaddr_ptr *local,
struct sockaddr *remote)
{
return send_control_segment(context, local, remote,
NET_TCP_SYN | NET_TCP_ACK,
"SYN_ACK");
}
static int send_ack(struct net_context *context,
struct sockaddr *remote, bool force)
{
struct net_pkt *pkt = NULL;
int ret;
/* Something (e.g. a data transmission under the user
* callback) already sent the ACK, no need
*/
if (!force && context->tcp->send_ack == context->tcp->sent_ack) {
return 0;
}
ret = net_tcp_prepare_ack(context->tcp, remote, &pkt);
if (ret) {
return ret;
}
print_send_info(pkt, "ACK");
ret = net_tcp_send_pkt(pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
return ret;
}
static int send_reset(struct net_context *context,
struct sockaddr *remote)
{
struct net_pkt *pkt = NULL;
int ret;
ret = net_tcp_prepare_reset(context->tcp, remote, &pkt);
if (ret || !pkt) {
return ret;
}
print_send_info(pkt, "RST");
ret = net_send_data(pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
return ret;
}
static int tcp_hdr_len(struct net_pkt *pkt)
{
struct net_tcp_hdr hdr, *tcp_hdr;
/* "Offset": 4-bit field in high nibble, units of dwords */
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (tcp_hdr) {
return 4 * (tcp_hdr->offset >> 4);
}
return 0;
}
/* This is called when we receive data after the connection has been
* established. The core TCP logic is located here.
*/
NET_CONN_CB(tcp_established)
{
struct net_context *context = (struct net_context *)user_data;
struct net_tcp_hdr hdr, *tcp_hdr;
enum net_verdict ret;
u8_t tcp_flags;
u16_t data_len;
NET_ASSERT(context && context->tcp);
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return NET_DROP;
}
if (net_tcp_get_state(context->tcp) < NET_TCP_ESTABLISHED) {
NET_ERR("Context %p in wrong state %d",
context, net_tcp_get_state(context->tcp));
return NET_DROP;
}
net_tcp_print_recv_info("DATA", pkt, tcp_hdr->src_port);
tcp_flags = NET_TCP_FLAGS(tcp_hdr);
if (tcp_flags & NET_TCP_ACK) {
net_tcp_ack_received(context,
sys_get_be32(tcp_hdr->ack));
}
/*
* If we receive RST here, we close the socket. See RFC 793 chapter
* called "Reset Processing" for details.
*/
if (tcp_flags & NET_TCP_RST) {
/* We only accept RST packet that has valid seq field. */
if (!net_tcp_validate_seq(context->tcp, pkt)) {
net_stats_update_tcp_seg_rsterr();
return NET_DROP;
}
net_stats_update_tcp_seg_rst();
net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port);
if (context->recv_cb) {
context->recv_cb(context, NULL, -ECONNRESET,
context->tcp->recv_user_data);
}
net_context_unref(context);
return NET_DROP;
}
if (net_tcp_seq_cmp(sys_get_be32(tcp_hdr->seq),
context->tcp->send_ack) < 0) {
/* Peer sent us packet we've already seen. Apparently,
* our ack was lost.
*/
/* RFC793 specifies that "highest" (i.e. current from our PoV)
* ack # value can/should be sent, so we just force resend.
*/
send_ack(context, &conn->remote_addr, true);
return NET_DROP;
}
if (sys_get_be32(tcp_hdr->seq) - context->tcp->send_ack) {
/* Don't try to reorder packets. If it doesn't
* match the next segment exactly, drop and wait for
* retransmit
*/
return NET_DROP;
}
set_appdata_values(pkt, IPPROTO_TCP);
data_len = net_pkt_appdatalen(pkt);
if (data_len > net_tcp_get_recv_wnd(context->tcp)) {
NET_ERR("Context %p: overflow of recv window (%d vs %d), pkt dropped",
context, net_tcp_get_recv_wnd(context->tcp), data_len);
return NET_DROP;
}
ret = packet_received(conn, pkt, context->tcp->recv_user_data);
context->tcp->send_ack += data_len;
if (tcp_flags & NET_TCP_FIN) {
/* Sending an ACK in the CLOSE_WAIT state will transition to
* LAST_ACK state
*/
context->tcp->fin_rcvd = 1;
if (net_tcp_get_state(context->tcp) == NET_TCP_ESTABLISHED) {
net_tcp_change_state(context->tcp, NET_TCP_CLOSE_WAIT);
}
context->tcp->send_ack += 1;
if (context->recv_cb) {
context->recv_cb(context, NULL, 0,
context->tcp->recv_user_data);
}
/* We should receive ACK next in order to get rid of LAST_ACK
* state that we are entering in a short while. But we need to
* be prepared to NOT to receive it as otherwise the connection
* would be stuck forever.
*/
k_delayed_work_submit(&context->tcp->ack_timer, ACK_TIMEOUT);
context->tcp->ack_timer_cancelled = false;
}
send_ack(context, &conn->remote_addr, false);
if (sys_slist_is_empty(&context->tcp->sent_list)
&& context->tcp->fin_rcvd
&& context->tcp->fin_sent) {
net_context_unref(context);
}
return ret;
}
NET_CONN_CB(tcp_synack_received)
{
struct net_context *context = (struct net_context *)user_data;
struct net_tcp_hdr hdr, *tcp_hdr;
int ret;
NET_ASSERT(context && context->tcp);
switch (net_tcp_get_state(context->tcp)) {
case NET_TCP_SYN_SENT:
net_context_set_iface(context, net_pkt_iface(pkt));
break;
default:
NET_DBG("Context %p in wrong state %d",
context, net_tcp_get_state(context->tcp));
return NET_DROP;
}
net_pkt_set_context(pkt, context);
NET_ASSERT(net_pkt_iface(pkt));
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return NET_DROP;
}
if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_RST) {
/* We only accept RST packet that has valid seq field. */
if (!net_tcp_validate_seq(context->tcp, pkt)) {
net_stats_update_tcp_seg_rsterr();
return NET_DROP;
}
net_stats_update_tcp_seg_rst();
if (context->connect_cb) {
context->connect_cb(context, -ECONNREFUSED,
context->user_data);
}
return NET_DROP;
}
if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN) {
context->tcp->send_ack =
sys_get_be32(tcp_hdr->seq) + 1;
context->tcp->recv_max_ack = context->tcp->send_seq + 1;
}
/*
* If we receive SYN, we send SYN-ACK and go to SYN_RCVD state.
*/
if (NET_TCP_FLAGS(tcp_hdr) == (NET_TCP_SYN | NET_TCP_ACK)) {
struct sockaddr *laddr;
struct sockaddr *raddr;
#if defined(CONFIG_NET_IPV6)
struct sockaddr_in6 r6addr;
struct sockaddr_in6 l6addr;
#endif
#if defined(CONFIG_NET_IPV4)
struct sockaddr_in r4addr;
struct sockaddr_in l4addr;
#endif
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
laddr = (struct sockaddr *)&l6addr;
raddr = (struct sockaddr *)&r6addr;
r6addr.sin6_family = AF_INET6;
r6addr.sin6_port = tcp_hdr->src_port;
net_ipaddr_copy(&r6addr.sin6_addr,
&NET_IPV6_HDR(pkt)->src);
l6addr.sin6_family = AF_INET6;
l6addr.sin6_port = htons(tcp_hdr->dst_port);
net_ipaddr_copy(&l6addr.sin6_addr,
&NET_IPV6_HDR(pkt)->dst);
} else
#endif
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
laddr = (struct sockaddr *)&l4addr;
raddr = (struct sockaddr *)&r4addr;
r4addr.sin_family = AF_INET;
r4addr.sin_port = tcp_hdr->src_port;
net_ipaddr_copy(&r4addr.sin_addr,
&NET_IPV4_HDR(pkt)->src);
l4addr.sin_family = AF_INET;
l4addr.sin_port = htons(tcp_hdr->dst_port);
net_ipaddr_copy(&l4addr.sin_addr,
&NET_IPV4_HDR(pkt)->dst);
} else
#endif
{
NET_DBG("Invalid family (%d)", net_pkt_family(pkt));
return NET_DROP;
}
/* Remove the temporary connection handler and register
* a proper now as we have an established connection.
*/
net_tcp_unregister(context->conn_handler);
ret = net_tcp_register(raddr,
laddr,
ntohs(tcp_hdr->src_port),
ntohs(tcp_hdr->dst_port),
tcp_established,
context,
&context->conn_handler);
if (ret < 0) {
NET_DBG("Cannot register TCP handler (%d)", ret);
send_reset(context, raddr);
return NET_DROP;
}
net_tcp_change_state(context->tcp, NET_TCP_ESTABLISHED);
net_context_set_state(context, NET_CONTEXT_CONNECTED);
send_ack(context, raddr, false);
k_sem_give(&context->tcp->connect_wait);
if (context->connect_cb) {
context->connect_cb(context, 0, context->user_data);
}
}
return NET_DROP;
}
#endif /* CONFIG_NET_TCP */
int net_context_connect(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen,
net_context_connect_cb_t cb,
s32_t timeout,
void *user_data)
{
#if defined(CONFIG_NET_TCP)
struct sockaddr *laddr = NULL;
struct sockaddr local_addr;
u16_t lport, rport;
#endif
int ret;
NET_ASSERT(addr);
NET_ASSERT(PART_OF_ARRAY(contexts, context));
#if defined(CONFIG_NET_TCP)
NET_ASSERT(context->tcp);
#endif
if (!net_context_is_used(context)) {
return -EBADF;
}
ret = bind_default(context);
if (ret) {
return ret;
}
if (addr->family != net_context_get_family(context)) {
NET_ASSERT_INFO(addr->family == \
net_context_get_family(context),
"Family mismatch %d should be %d",
addr->family,
net_context_get_family(context));
return -EINVAL;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_context_get_iface(context))) {
return net_offload_connect(
net_context_get_iface(context),
context,
addr,
addrlen,
cb,
timeout,
user_data);
}
#endif /* CONFIG_NET_OFFLOAD */
if (net_context_get_state(context) == NET_CONTEXT_LISTENING) {
return -EOPNOTSUPP;
}
#if defined(CONFIG_NET_IPV6)
if (net_context_get_family(context) == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)
&context->remote;
if (addrlen < sizeof(struct sockaddr_in6)) {
return -EINVAL;
}
#if defined(CONFIG_NET_TCP)
if (net_is_ipv6_addr_mcast(&addr6->sin6_addr)) {
return -EADDRNOTAVAIL;
}
#endif /* CONFIG_NET_TCP */
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_is_ipv6_addr_unspecified(&addr6->sin6_addr)) {
context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;
} else {
context->flags &= ~NET_CONTEXT_REMOTE_ADDR_SET;
}
#if defined(CONFIG_NET_TCP)
rport = addr6->sin6_port;
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;
}
#endif
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_context_get_family(context) == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)
&context->remote;
if (addrlen < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
#if defined(CONFIG_NET_TCP)
/* FIXME - Add multicast and broadcast address check */
#endif /* CONFIG_NET_TCP */
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;
}
#if defined(CONFIG_NET_TCP)
rport = addr4->sin_port;
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;
}
#endif
} else
#endif /* CONFIG_NET_IPV4 */
{
return -EINVAL; /* Not IPv4 or IPv6 */
}
#if defined(CONFIG_NET_UDP)
if (net_context_get_type(context) == SOCK_DGRAM) {
if (cb) {
cb(context, 0, user_data);
}
return 0;
}
#endif /* CONFIG_NET_UDP */
#if defined(CONFIG_NET_TCP)
if (net_context_get_type(context) != SOCK_STREAM) {
return -ENOTSUP;
}
/* We need to register a handler, otherwise the SYN-ACK
* packet would not be received.
*/
ret = net_tcp_register(addr,
laddr,
ntohs(rport),
ntohs(lport),
tcp_synack_received,
context,
&context->conn_handler);
if (ret < 0) {
return ret;
}
context->connect_cb = cb;
context->user_data = user_data;
net_context_set_state(context, NET_CONTEXT_CONNECTING);
send_syn(context, addr);
/* in tcp_synack_received() we give back this semaphore */
if (timeout != 0 && k_sem_take(&context->tcp->connect_wait, timeout)) {
return -ETIMEDOUT;
}
#endif
return 0;
}
#if defined(CONFIG_NET_TCP)
static void pkt_get_sockaddr(sa_family_t family, struct net_pkt *pkt,
struct sockaddr_ptr *addr)
{
struct net_tcp_hdr hdr, *tcp_hdr;
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return;
}
memset(addr, 0, sizeof(*addr));
#if defined(CONFIG_NET_IPV4)
if (family == AF_INET) {
struct sockaddr_in_ptr *addr4 = net_sin_ptr(addr);
addr4->sin_family = AF_INET;
addr4->sin_port = tcp_hdr->dst_port;
addr4->sin_addr = &NET_IPV4_HDR(pkt)->dst;
}
#endif
#if defined(CONFIG_NET_IPV6)
if (family == AF_INET6) {
struct sockaddr_in6_ptr *addr6 = net_sin6_ptr(addr);
addr6->sin6_family = AF_INET6;
addr6->sin6_port = tcp_hdr->dst_port;
addr6->sin6_addr = &NET_IPV6_HDR(pkt)->dst;
}
#endif
}
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
static inline void copy_pool_vars(struct net_context *new_context,
struct net_context *listen_context)
{
new_context->tx_slab = listen_context->tx_slab;
new_context->data_pool = listen_context->data_pool;
}
#else
#define copy_pool_vars(...)
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
/* This callback is called when we are waiting connections and we receive
* a packet. We need to check if we are receiving proper msg (SYN) here.
* The ACK could also be received, in which case we have an established
* connection.
*/
NET_CONN_CB(tcp_syn_rcvd)
{
struct net_context *context = (struct net_context *)user_data;
struct net_tcp_hdr hdr, *tcp_hdr;
struct net_tcp *tcp;
struct sockaddr_ptr pkt_src_addr;
NET_ASSERT(context && context->tcp);
tcp = context->tcp;
switch (net_tcp_get_state(tcp)) {
case NET_TCP_LISTEN:
net_context_set_iface(context, net_pkt_iface(pkt));
break;
case NET_TCP_SYN_RCVD:
if (net_pkt_iface(pkt) != net_context_get_iface(context)) {
return NET_DROP;
}
break;
default:
NET_DBG("Context %p in wrong state %d",
context, tcp->state);
return NET_DROP;
}
net_pkt_set_context(pkt, context);
NET_ASSERT(net_pkt_iface(pkt));
tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
if (!tcp_hdr) {
return NET_DROP;
}
/*
* If we receive SYN, we send SYN-ACK and go to SYN_RCVD state.
*/
if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_SYN) {
struct sockaddr peer, *remote;
int r;
net_tcp_print_recv_info("SYN", pkt, tcp_hdr->src_port);
net_tcp_change_state(tcp, NET_TCP_SYN_RCVD);
remote = create_sockaddr(pkt, &peer);
/* Set TCP seq and ack which are then stored in the backlog */
context->tcp->send_seq = tcp_init_isn();
context->tcp->send_ack =
sys_get_be32(tcp_hdr->seq) + 1;
context->tcp->recv_max_ack = context->tcp->send_seq + 1;
r = tcp_backlog_syn(pkt, context);
if (r < 0) {
if (r == -EADDRINUSE) {
NET_DBG("TCP connection already exists");
} else {
NET_DBG("No free TCP backlog entries");
}
return NET_DROP;
}
pkt_get_sockaddr(net_context_get_family(context),
pkt, &pkt_src_addr);
send_syn_ack(context, &pkt_src_addr, remote);
return NET_DROP;
}
/*
* See RFC 793 chapter 3.4 "Reset Processing" and RFC 793, page 65
* for more details.
*/
if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_RST) {
if (tcp_backlog_rst(pkt) < 0) {
net_stats_update_tcp_seg_rsterr();
return NET_DROP;
}
net_stats_update_tcp_seg_rst();
net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port);
return NET_DROP;
}
/*
* If we receive ACK, we go to ESTABLISHED state.
*/
if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_ACK) {
struct net_context *new_context;
struct sockaddr local_addr;
struct sockaddr remote_addr;
struct net_tcp *tmp_tcp;
socklen_t addrlen;
int ret;
net_tcp_print_recv_info("ACK", pkt, tcp_hdr->src_port);
if (!context->tcp->accept_cb) {
NET_DBG("No accept callback, connection reset.");
goto reset;
}
/* We create a new context that starts to wait data.
*/
ret = net_context_get(net_pkt_family(pkt),
SOCK_STREAM, IPPROTO_TCP,
&new_context);
if (ret < 0) {
NET_DBG("Cannot get accepted context, "
"connection reset");
goto conndrop;
}
ret = tcp_backlog_ack(pkt, new_context);
if (ret < 0) {
NET_DBG("Cannot find context from TCP backlog");
net_context_unref(new_context);
goto conndrop;
}
#if defined(CONFIG_NET_IPV6)
if (net_context_get_family(context) == AF_INET6) {
struct sockaddr_in6 *local_addr6 =
net_sin6(&local_addr);
struct sockaddr_in6 *remote_addr6 =
net_sin6(&remote_addr);
remote_addr6->sin6_family = AF_INET6;
local_addr6->sin6_family = AF_INET6;
local_addr6->sin6_port = tcp_hdr->dst_port;
remote_addr6->sin6_port = tcp_hdr->src_port;
net_ipaddr_copy(&local_addr6->sin6_addr,
&NET_IPV6_HDR(pkt)->dst);
net_ipaddr_copy(&remote_addr6->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
addrlen = sizeof(struct sockaddr_in6);
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_context_get_family(context) == AF_INET) {
struct sockaddr_in *local_addr4 =
net_sin(&local_addr);
struct sockaddr_in *remote_addr4 =
net_sin(&remote_addr);
remote_addr4->sin_family = AF_INET;
local_addr4->sin_family = AF_INET;
local_addr4->sin_port = tcp_hdr->dst_port;
remote_addr4->sin_port = tcp_hdr->src_port;
net_ipaddr_copy(&local_addr4->sin_addr,
&NET_IPV4_HDR(pkt)->dst);
net_ipaddr_copy(&remote_addr4->sin_addr,
&NET_IPV4_HDR(pkt)->src);
addrlen = sizeof(struct sockaddr_in);
} else
#endif /* CONFIG_NET_IPV4 */
{
NET_ASSERT_INFO(false, "Invalid protocol family %d",
net_context_get_family(context));
net_context_unref(new_context);
return NET_DROP;
}
ret = net_context_bind(new_context, &local_addr,
sizeof(local_addr));
if (ret < 0) {
NET_DBG("Cannot bind accepted context, "
"connection reset");
net_context_unref(new_context);
goto conndrop;
}
new_context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;
memcpy(&new_context->remote, &remote_addr,
sizeof(remote_addr));
ret = net_tcp_register(&new_context->remote,
&local_addr,
ntohs(net_sin(&new_context->remote)->sin_port),
ntohs(net_sin(&local_addr)->sin_port),
tcp_established,
new_context,
&new_context->conn_handler);
if (ret < 0) {
NET_DBG("Cannot register accepted TCP handler (%d)",
ret);
net_context_unref(new_context);
goto conndrop;
}
/* Swap the newly-created TCP states with the one that
* was used to establish this connection. The new TCP
* must be listening to accept other connections.
*/
tmp_tcp = new_context->tcp;
tmp_tcp->accept_cb = tcp->accept_cb;
tcp->accept_cb = NULL;
new_context->tcp = tcp;
copy_pool_vars(new_context, context);
context->tcp = tmp_tcp;
tcp->context = new_context;
tmp_tcp->context = context;
net_tcp_change_state(tmp_tcp, NET_TCP_LISTEN);
net_tcp_change_state(new_context->tcp, NET_TCP_ESTABLISHED);
net_context_set_state(new_context, NET_CONTEXT_CONNECTED);
context->tcp->accept_cb(new_context,
&new_context->remote,
addrlen,
0,
context->user_data);
}
return NET_DROP;
conndrop:
net_stats_update_tcp_seg_conndrop();
reset:
{
struct sockaddr peer;
send_reset(tcp->context, create_sockaddr(pkt, &peer));
}
return NET_DROP;
}
#endif /* CONFIG_NET_TCP */
int net_context_accept(struct net_context *context,
net_tcp_accept_cb_t cb,
s32_t timeout,
void *user_data)
{
#if defined(CONFIG_NET_TCP)
struct sockaddr local_addr;
struct sockaddr *laddr = NULL;
u16_t lport = 0;
int ret;
#endif /* CONFIG_NET_TCP */
NET_ASSERT(PART_OF_ARRAY(contexts, context));
if (!net_context_is_used(context)) {
return -EBADF;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_context_get_iface(context))) {
return net_offload_accept(
net_context_get_iface(context),
context,
cb,
timeout,
user_data);
}
#endif /* CONFIG_NET_OFFLOAD */
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));
return -EINVAL;
}
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
NET_ASSERT(context->tcp);
if (net_tcp_get_state(context->tcp) != NET_TCP_LISTEN) {
NET_DBG("Context %p in wrong state %d, should be %d",
context, context->tcp->state, NET_TCP_LISTEN);
return -EINVAL;
}
}
local_addr.family = net_context_get_family(context);
#if defined(CONFIG_NET_IPV6)
if (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 = lport =
net_sin6((struct sockaddr *)&context->local)->sin6_port;
}
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (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;
}
net_sin(&local_addr)->sin_port = lport =
net_sin((struct sockaddr *)&context->local)->sin_port;
}
#endif /* CONFIG_NET_IPV4 */
ret = net_tcp_register(context->flags & NET_CONTEXT_REMOTE_ADDR_SET ?
&context->remote : NULL,
laddr,
ntohs(net_sin(&context->remote)->sin_port),
ntohs(lport),
tcp_syn_rcvd,
context,
&context->conn_handler);
if (ret < 0) {
return ret;
}
context->user_data = user_data;
/* accept callback is only valid for TCP contexts */
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
context->tcp->accept_cb = cb;
}
#endif /* CONFIG_NET_TCP */
return 0;
}
static int send_data(struct net_context *context,
struct net_pkt *pkt,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
context->send_cb = cb;
context->user_data = user_data;
net_pkt_set_token(pkt, token);
if (net_context_get_ip_proto(context) == IPPROTO_UDP) {
return net_send_data(pkt);
}
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
int ret = net_tcp_send_data(context);
/* Just make the callback synchronously even if it didn't
* go over the wire. In theory it would be nice to track
* specific ACK locations in the stream and make the
* callback at that time, but there's nowhere to store the
* potentially-separate token/user_data values right now.
*/
if (cb) {
cb(context, ret, token, user_data);
}
return ret;
}
#endif
return -EPROTONOSUPPORT;
}
#if defined(CONFIG_NET_UDP)
static int create_udp_packet(struct net_context *context,
struct net_pkt *pkt,
const struct sockaddr *dst_addr,
struct net_pkt **out_pkt)
{
int r = 0;
struct net_pkt *tmp;
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr;
pkt = net_ipv6_create(context, pkt, NULL, &addr6->sin6_addr);
tmp = net_udp_insert(context, pkt,
net_pkt_ip_hdr_len(pkt) +
net_pkt_ipv6_ext_len(pkt),
addr6->sin6_port);
if (!tmp) {
return -ENOMEM;
}
pkt = tmp;
r = net_ipv6_finalize(context, pkt);
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr;
pkt = net_ipv4_create(context, pkt, NULL, &addr4->sin_addr);
tmp = net_udp_insert(context, pkt, net_pkt_ip_hdr_len(pkt),
addr4->sin_port);
if (!tmp) {
return -ENOMEM;
}
pkt = tmp;
r = net_ipv4_finalize(context, pkt);
} else
#endif /* CONFIG_NET_IPV4 */
{
return -EPROTONOSUPPORT;
}
*out_pkt = pkt;
return r;
}
#endif /* CONFIG_NET_UDP */
static int sendto(struct net_pkt *pkt,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
struct net_context *context = net_pkt_context(pkt);
int ret;
if (!net_context_is_used(context)) {
return -EBADF;
}
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
if (net_context_get_state(context) != NET_CONTEXT_CONNECTED) {
return -ENOTCONN;
}
NET_ASSERT(context->tcp);
if (context->tcp->flags & NET_TCP_IS_SHUTDOWN) {
return -ESHUTDOWN;
}
}
#endif /* CONFIG_NET_TCP */
if (!dst_addr) {
return -EDESTADDRREQ;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_pkt_iface(pkt))) {
return net_offload_sendto(
net_pkt_iface(pkt),
pkt, dst_addr, addrlen,
cb, timeout, token, user_data);
}
#endif /* CONFIG_NET_OFFLOAD */
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr;
if (addrlen < sizeof(struct sockaddr_in6)) {
return -EINVAL;
}
if (net_is_ipv6_addr_unspecified(&addr6->sin6_addr)) {
return -EDESTADDRREQ;
}
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr;
if (addrlen < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
if (!addr4->sin_addr.s_addr) {
return -EDESTADDRREQ;
}
} else
#endif /* CONFIG_NET_IPV4 */
{
NET_DBG("Invalid protocol family %d", net_pkt_family(pkt));
return -EINVAL;
}
#if defined(CONFIG_NET_UDP)
if (net_context_get_ip_proto(context) == IPPROTO_UDP) {
ret = create_udp_packet(context, pkt, dst_addr, &pkt);
} else
#endif /* CONFIG_NET_UDP */
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
net_pkt_set_appdatalen(pkt, net_pkt_get_len(pkt));
ret = net_tcp_queue_data(context, pkt);
} else
#endif /* CONFIG_NET_TCP */
{
NET_DBG("Unknown protocol while sending packet: %d",
net_context_get_ip_proto(context));
return -EPROTONOSUPPORT;
}
if (ret < 0) {
NET_DBG("Could not create network packet to send (%d)", ret);
return ret;
}
return send_data(context, pkt, cb, timeout, token, user_data);
}
int net_context_send(struct net_pkt *pkt,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
struct net_context *context = net_pkt_context(pkt);
socklen_t addrlen;
NET_ASSERT(PART_OF_ARRAY(contexts, context));
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_pkt_iface(pkt))) {
return net_offload_send(
net_pkt_iface(pkt),
pkt, cb, timeout,
token, user_data);
}
#endif /* CONFIG_NET_OFFLOAD */
if (!(context->flags & NET_CONTEXT_REMOTE_ADDR_SET) ||
!net_sin(&context->remote)->sin_port) {
return -EDESTADDRREQ;
}
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
addrlen = sizeof(struct sockaddr_in6);
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
addrlen = sizeof(struct sockaddr_in);
} else
#endif /* CONFIG_NET_IPV4 */
{
addrlen = 0;
}
return sendto(pkt, &context->remote, addrlen, cb, timeout, token,
user_data);
}
int net_context_sendto(struct net_pkt *pkt,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
s32_t timeout,
void *token,
void *user_data)
{
#if defined(CONFIG_NET_TCP)
struct net_context *context = net_pkt_context(pkt);
NET_ASSERT(PART_OF_ARRAY(contexts, context));
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
/* Match POSIX behavior and ignore dst_address and addrlen */
return net_context_send(pkt, cb, timeout, token, user_data);
}
#endif /* CONFIG_NET_TCP */
return sendto(pkt, dst_addr, addrlen, cb, timeout, token, user_data);
}
static void set_appdata_values(struct net_pkt *pkt, enum net_ip_protocol proto)
{
size_t total_len = net_pkt_get_len(pkt);
u16_t proto_len = 0;
struct net_buf *frag;
u16_t offset;
#if defined(CONFIG_NET_UDP)
if (proto == IPPROTO_UDP) {
proto_len = sizeof(struct net_udp_hdr);
}
#endif /* CONFIG_NET_UDP */
#if defined(CONFIG_NET_TCP)
if (proto == IPPROTO_TCP) {
proto_len = tcp_hdr_len(pkt);
}
#endif /* CONFIG_NET_TCP */
frag = net_frag_get_pos(pkt, net_pkt_ip_hdr_len(pkt) +
net_pkt_ipv6_ext_len(pkt) +
proto_len,
&offset);
if (frag) {
net_pkt_set_appdata(pkt, frag->data + offset);
}
net_pkt_set_appdatalen(pkt, total_len - net_pkt_ip_hdr_len(pkt) -
net_pkt_ipv6_ext_len(pkt) - proto_len);
NET_ASSERT_INFO(net_pkt_appdatalen(pkt) < total_len,
"Wrong appdatalen %u, total %zu",
net_pkt_appdatalen(pkt), total_len);
}
static enum net_verdict packet_received(struct net_conn *conn,
struct net_pkt *pkt,
void *user_data)
{
struct net_context *context = find_context(conn);
NET_ASSERT(context);
NET_ASSERT(net_pkt_iface(pkt));
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) {
return NET_DROP;
}
if (net_context_get_ip_proto(context) != IPPROTO_TCP) {
/* TCP packets get appdata earlier in tcp_established(). */
set_appdata_values(pkt, IPPROTO_UDP);
}
#if defined(CONFIG_NET_TCP)
else if (net_context_get_type(context) == SOCK_STREAM) {
if (net_pkt_appdatalen(pkt) == 0) {
net_pkt_unref(pkt);
return NET_OK;
}
}
#endif /* CONFIG_NET_TCP */
NET_DBG("Set appdata %p to len %u (total %zu)",
net_pkt_appdata(pkt), net_pkt_appdatalen(pkt),
net_pkt_get_len(pkt));
net_stats_update_tcp_recv(net_pkt_appdatalen(pkt));
context->recv_cb(context, pkt, 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;
}
#if defined(CONFIG_NET_UDP)
static int recv_udp(struct net_context *context,
net_context_recv_cb_t cb,
s32_t timeout,
void *user_data)
{
struct sockaddr local_addr = {
.family = net_context_get_family(context),
};
struct sockaddr *laddr = NULL;
u16_t lport = 0;
int ret;
ARG_UNUSED(timeout);
if (context->conn_handler) {
net_conn_unregister(context->conn_handler);
context->conn_handler = NULL;
}
#if defined(CONFIG_NET_IPV6)
if (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;
}
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (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;
}
#endif /* CONFIG_NET_IPV4 */
context->recv_cb = cb;
ret = net_conn_register(net_context_get_ip_proto(context),
context->flags & NET_CONTEXT_REMOTE_ADDR_SET ?
&context->remote : NULL,
laddr,
ntohs(net_sin(&context->remote)->sin_port),
ntohs(lport),
packet_received,
user_data,
&context->conn_handler);
return ret;
}
#endif /* CONFIG_NET_UDP */
int net_context_recv(struct net_context *context,
net_context_recv_cb_t cb,
s32_t timeout,
void *user_data)
{
NET_ASSERT(context);
if (!net_context_is_used(context)) {
return -EBADF;
}
#if defined(CONFIG_NET_OFFLOAD)
if (net_if_is_ip_offloaded(net_context_get_iface(context))) {
return net_offload_recv(
net_context_get_iface(context),
context, cb, timeout, user_data);
}
#endif /* CONFIG_NET_OFFLOAD */
#if defined(CONFIG_NET_UDP)
if (net_context_get_ip_proto(context) == IPPROTO_UDP) {
int ret = recv_udp(context, cb, timeout, user_data);
if (ret < 0) {
return ret;
}
} else
#endif /* CONFIG_NET_UDP */
#if defined(CONFIG_NET_TCP)
if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
NET_ASSERT(context->tcp);
if (context->tcp->flags & NET_TCP_IS_SHUTDOWN) {
return -ESHUTDOWN;
} else if (net_context_get_state(context)
!= NET_CONTEXT_CONNECTED) {
return -ENOTCONN;
}
context->recv_cb = cb;
context->tcp->recv_user_data = user_data;
} else
#endif /* CONFIG_NET_TCP */
{
return -EPROTOTYPE;
}
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
if (timeout) {
/* Make sure we have the lock, then the packet_received()
* callback will release the semaphore when data has been
* received.
*/
while (k_sem_take(&context->recv_data_wait, K_NO_WAIT)) {
;
}
if (!k_sem_take(&context->recv_data_wait, timeout)) {
/* timeout */
return -ETIMEDOUT;
}
}
#endif /* CONFIG_NET_CONTEXT_SYNC_RECV */
return 0;
}
int net_context_update_recv_wnd(struct net_context *context,
s32_t delta)
{
#if defined(CONFIG_NET_TCP)
s32_t new_win;
if (!context->tcp) {
NET_ERR("context->tcp == NULL");
return -EPROTOTYPE;
}
new_win = context->tcp->recv_wnd + delta;
if (new_win < 0 || new_win > UINT16_MAX) {
return -EINVAL;
}
context->tcp->recv_wnd = new_win;
return 0;
#else
return -EPROTOTYPE;
#endif
}
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;
}
cb(&contexts[i], user_data);
}
k_sem_give(&contexts_lock);
}
void net_context_init(void)
{
k_sem_init(&contexts_lock, 1, UINT_MAX);
}