zephyr/net/ip/ip_buf.c
Paul Sokolovsky dbb9094b8a net: Set uIP context on newly allocated buffers immediately.
This context must be set for TX buffers, and there's no better place to
set it than at the allocation time. If not set, it may end up NULL,
causing adverse effects (one seen by few parties is dereferencing random
memory locations to get (random again) MSS values). For RX buffers, uIP
context is set elsewhere too, but anyway, common sense says that if a
buffer is allocated for network context X, and that context uses uIP
connection Y, the a buffer should be just cross-linked with Y during
allocation time, not somewhere later.

Change-Id: Icdb3cd724802ca263c1cd0e3909be811e53822ba
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
2016-09-22 12:00:24 +00:00

412 lines
9.1 KiB
C

/** @file
@brief Network buffers for IP stack
IP data is passed between application and IP stack via
a net_buf struct.
*/
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nanokernel.h>
#include <toolchain.h>
#include <string.h>
#include <stdint.h>
#include <net/net_core.h>
#include <net/buf.h>
#include <net/ip_buf.h>
#include <net/net_ip.h>
#include "ip/uip.h"
#if !defined(CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF)
#undef NET_DBG
#define NET_DBG(...)
#endif
extern struct net_tuple *net_context_get_tuple(struct net_context *context);
extern void *net_context_get_internal_connection(struct net_context *context);
/* Available (free) buffers queue */
#ifndef IP_BUF_RX_SIZE
#if CONFIG_IP_BUF_RX_SIZE > 0
#define IP_BUF_RX_SIZE CONFIG_IP_BUF_RX_SIZE
#else
#define IP_BUF_RX_SIZE 1
#endif
#endif
#ifndef IP_BUF_TX_SIZE
#if CONFIG_IP_BUF_TX_SIZE > 0
#define IP_BUF_TX_SIZE CONFIG_IP_BUF_TX_SIZE
#else
#define IP_BUF_TX_SIZE 1
#endif
#endif
#ifdef DEBUG_IP_BUFS
static int num_free_rx_bufs = IP_BUF_RX_SIZE;
static int num_free_tx_bufs = IP_BUF_TX_SIZE;
static inline void dec_free_rx_bufs(struct net_buf *buf)
{
if (!buf) {
return;
}
num_free_rx_bufs--;
if (num_free_rx_bufs < 0) {
NET_DBG("*** ERROR *** Invalid RX buffer count.\n");
num_free_rx_bufs = 0;
}
}
static inline void inc_free_rx_bufs(struct net_buf *buf)
{
if (!buf) {
return;
}
if (num_free_rx_bufs > IP_BUF_RX_SIZE) {
num_free_rx_bufs = IP_BUF_RX_SIZE;
} else {
num_free_rx_bufs++;
}
}
static inline void dec_free_tx_bufs(struct net_buf *buf)
{
if (!buf) {
return;
}
num_free_tx_bufs--;
if (num_free_tx_bufs < 0) {
NET_DBG("*** ERROR *** Invalid TX buffer count.\n");
num_free_tx_bufs = 0;
}
}
static inline void inc_free_tx_bufs(struct net_buf *buf)
{
if (!buf) {
return;
}
if (num_free_tx_bufs > IP_BUF_TX_SIZE) {
num_free_tx_bufs = IP_BUF_TX_SIZE;
} else {
num_free_tx_bufs++;
}
}
static inline int get_frees(enum ip_buf_type type)
{
switch (type) {
case IP_BUF_RX:
return num_free_rx_bufs;
case IP_BUF_TX:
return num_free_tx_bufs;
}
return 0xffffffff;
}
#define inc_free_rx_bufs_func inc_free_rx_bufs
#define inc_free_tx_bufs_func inc_free_tx_bufs
#else
#define dec_free_rx_bufs(...)
#define inc_free_rx_bufs(...)
#define dec_free_tx_bufs(...)
#define inc_free_tx_bufs(...)
#define inc_free_rx_bufs_func(...)
#define inc_free_tx_bufs_func(...)
#endif
static struct nano_fifo free_rx_bufs;
static struct nano_fifo free_tx_bufs;
static inline void free_rx_bufs_func(struct net_buf *buf)
{
inc_free_rx_bufs_func(buf);
nano_fifo_put(buf->free, buf);
}
static inline void free_tx_bufs_func(struct net_buf *buf)
{
inc_free_tx_bufs_func(buf);
nano_fifo_put(buf->free, buf);
}
static NET_BUF_POOL(rx_buffers, IP_BUF_RX_SIZE, IP_BUF_MAX_DATA, \
&free_rx_bufs, free_rx_bufs_func, \
sizeof(struct ip_buf));
static NET_BUF_POOL(tx_buffers, IP_BUF_TX_SIZE, IP_BUF_MAX_DATA, \
&free_tx_bufs, free_tx_bufs_func, \
sizeof(struct ip_buf));
static inline const char *type2str(enum ip_buf_type type)
{
switch (type) {
case IP_BUF_RX:
return "RX";
case IP_BUF_TX:
return "TX";
}
return NULL;
}
#ifdef DEBUG_IP_BUFS
static struct net_buf *ip_buf_get_reserve_debug(enum ip_buf_type type,
uint16_t reserve_head,
const char *caller,
int line)
#else
static struct net_buf *ip_buf_get_reserve(enum ip_buf_type type,
uint16_t reserve_head)
#endif
{
struct net_buf *buf = NULL;
/* Note that we do not reserve any space in front of the
* buffer so buf->data points to first byte of the IP header.
* This is done like this so that IP stack works the same
* way as BT and 802.15.4 stacks.
*
* The reserve_head variable in the function will tell
* the size of the IP + other headers if there are any.
* That variable is only used to calculate the pointer
* where the application data starts.
*/
switch (type) {
case IP_BUF_RX:
buf = net_buf_get(&free_rx_bufs, 0);
dec_free_rx_bufs(buf);
break;
case IP_BUF_TX:
buf = net_buf_get(&free_tx_bufs, 0);
dec_free_tx_bufs(buf);
break;
}
if (!buf) {
#ifdef DEBUG_IP_BUFS
NET_ERR("Failed to get free %s buffer (%s():%d)\n",
type2str(type), caller, line);
#else
NET_ERR("Failed to get free %s buffer\n", type2str(type));
#endif
return NULL;
}
ip_buf_type(buf) = type;
ip_buf_appdata(buf) = buf->data + reserve_head;
ip_buf_appdatalen(buf) = 0;
ip_buf_reserve(buf) = reserve_head;
net_buf_add(buf, reserve_head);
NET_BUF_CHECK_IF_NOT_IN_USE(buf);
#ifdef DEBUG_IP_BUFS
NET_DBG("%s [%d] buf %p reserve %u ref %d (%s():%d)\n",
type2str(type), get_frees(type),
buf, reserve_head, buf->ref, caller, line);
#else
NET_DBG("%s buf %p reserve %u ref %d\n", type2str(type), buf,
reserve_head, buf->ref);
#endif
return buf;
}
#ifdef DEBUG_IP_BUFS
struct net_buf *ip_buf_get_reserve_rx_debug(uint16_t reserve_head, const char *caller, int line)
#else
struct net_buf *ip_buf_get_reserve_rx(uint16_t reserve_head)
#endif
{
#ifdef DEBUG_IP_BUFS
return ip_buf_get_reserve_debug(IP_BUF_RX, reserve_head,
caller, line);
#else
return ip_buf_get_reserve(IP_BUF_RX, reserve_head);
#endif
}
#ifdef DEBUG_IP_BUFS
struct net_buf *ip_buf_get_reserve_tx_debug(uint16_t reserve_head, const char *caller, int line)
#else
struct net_buf *ip_buf_get_reserve_tx(uint16_t reserve_head)
#endif
{
#ifdef DEBUG_IP_BUFS
return ip_buf_get_reserve_debug(IP_BUF_TX, reserve_head,
caller, line);
#else
return ip_buf_get_reserve(IP_BUF_TX, reserve_head);
#endif
}
#ifdef DEBUG_IP_BUFS
static struct net_buf *ip_buf_get_debug(enum ip_buf_type type,
struct net_context *context,
const char *caller, int line)
#else
static struct net_buf *ip_buf_get(enum ip_buf_type type,
struct net_context *context)
#endif
{
struct net_buf *buf;
struct net_tuple *tuple;
uint16_t reserve = 0;
tuple = net_context_get_tuple(context);
if (!tuple) {
return NULL;
}
switch (tuple->ip_proto) {
case IPPROTO_UDP:
reserve = UIP_IPUDPH_LEN + UIP_LLH_LEN;
break;
case IPPROTO_TCP:
reserve = UIP_IPTCPH_LEN + UIP_LLH_LEN;
break;
case IPPROTO_ICMPV6:
reserve = UIP_IPICMPH_LEN + UIP_LLH_LEN;
break;
}
#ifdef DEBUG_IP_BUFS
buf = ip_buf_get_reserve_debug(type, reserve, caller, line);
#else
buf = ip_buf_get_reserve(type, reserve);
#endif
if (!buf) {
return buf;
}
ip_buf_context(buf) = context;
uip_set_conn(buf) = net_context_get_internal_connection(context);
return buf;
}
#ifdef DEBUG_IP_BUFS
struct net_buf *ip_buf_get_rx_debug(struct net_context *context,
const char *caller, int line)
#else
struct net_buf *ip_buf_get_rx(struct net_context *context)
#endif
{
#ifdef DEBUG_IP_BUFS
return ip_buf_get_debug(IP_BUF_RX, context, caller, line);
#else
return ip_buf_get(IP_BUF_RX, context);
#endif
}
#ifdef DEBUG_IP_BUFS
struct net_buf *ip_buf_get_tx_debug(struct net_context *context,
const char *caller, int line)
#else
struct net_buf *ip_buf_get_tx(struct net_context *context)
#endif
{
#ifdef DEBUG_IP_BUFS
return ip_buf_get_debug(IP_BUF_TX, context, caller, line);
#else
return ip_buf_get(IP_BUF_TX, context);
#endif
}
#ifdef DEBUG_IP_BUFS
void ip_buf_unref_debug(struct net_buf *buf, const char *caller, int line)
#else
void ip_buf_unref(struct net_buf *buf)
#endif
{
if (!buf) {
#ifdef DEBUG_IP_BUFS
NET_DBG("*** ERROR *** buf %p (%s():%d)\n", buf, caller, line);
#else
NET_DBG("*** ERROR *** buf %p\n", buf);
#endif
return;
}
if (!buf->ref) {
#ifdef DEBUG_IP_BUFS
NET_DBG("*** ERROR *** buf %p is freed already (%s():%d)\n",
buf, caller, line);
#else
NET_DBG("*** ERROR *** buf %p is freed already\n", buf);
#endif
return;
}
#ifdef DEBUG_IP_BUFS
NET_DBG("%s [%d] buf %p ref %d (%s():%d)\n",
type2str(ip_buf_type(buf)), get_frees(ip_buf_type(buf)),
buf, buf->ref - 1, caller, line);
#else
NET_DBG("%s buf %p ref %d\n",
type2str(ip_buf_type(buf)), buf, buf->ref - 1);
#endif
net_buf_unref(buf);
}
#ifdef DEBUG_IP_BUFS
struct net_buf *ip_buf_ref_debug(struct net_buf *buf, const char *caller, int line)
#else
struct net_buf *ip_buf_ref(struct net_buf *buf)
#endif
{
if (!buf) {
#ifdef DEBUG_IP_BUFS
NET_DBG("*** ERROR *** buf %p (%s():%d)\n", buf, caller, line);
#else
NET_DBG("*** ERROR *** buf %p\n", buf);
#endif
return NULL;
}
#ifdef DEBUG_IP_BUFS
NET_DBG("%s [%d] buf %p ref %d (%s():%d)\n",
type2str(ip_buf_type(buf)), get_frees(ip_buf_type(buf)),
buf, buf->ref + 1, caller, line);
#else
NET_DBG("%s buf %p ref %d\n",
type2str(ip_buf_type(buf)), buf, buf->ref + 1);
#endif
return net_buf_ref(buf);
}
void ip_buf_init(void)
{
NET_DBG("Allocating %d RX and %d TX buffers for IP stack\n",
IP_BUF_RX_SIZE, IP_BUF_TX_SIZE);
net_buf_pool_init(rx_buffers);
net_buf_pool_init(tx_buffers);
}