Skip to content
Permalink
Browse files

net: core: Always have a timeout when allocating a net_buf

Instead of waiting forever for a network buffer, have a timeout
when allocating net_buf. This way we cannot left hanging for a
long time waiting for a buffer and possibly deadlock the system.
This commit only adds checks to core IP stack in subsys/net/ip

Fixes #7571

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
  • Loading branch information...
jukkar committed Jul 25, 2018
1 parent 4b9d980 commit 7c7cfdda50b066f4d9d1d8b5d2143a0cf19fda5e
@@ -1272,6 +1272,102 @@ static inline bool net_pkt_append_le32(struct net_pkt *pkt, u32_t data)
K_FOREVER);
}

/**
* @brief Append u8_t data to last fragment in fragment list of a packet
*
* @details Append data to last fragment. If there is not enough space in last
* fragment then new data fragment will be created and will be added to
* fragment list. Caller has to take care of endianness if needed.
*
* @param pkt Network packet.
* @param data Data to be added
* @param timeout Timeout for buffer allocations
*
* @return True if all the data is placed at end of fragment list,
* False otherwise (In-case of false pkt might contain input
* data in the process of placing into fragments).
*/
static inline bool net_pkt_append_u8_timeout(struct net_pkt *pkt, u8_t data,
s32_t timeout)
{
return net_pkt_append_all(pkt, 1, &data, timeout);
}

/**
* @brief Append u16_t data to last fragment in fragment list of a packet
*
* @details Append data to last fragment. If there is not enough space in last
* fragment then new data fragment will be created and will be added to
* fragment list. Caller has to take care of endianness if needed.
*
* @param pkt Network packet.
* @param data Data to be added
* @param timeout Timeout for buffer allocations
*
* @return True if all the data is placed at end of fragment list,
* False otherwise (In-case of false pkt might contain input data
* in the process of placing into fragments).
*/
static inline bool net_pkt_append_be16_timeout(struct net_pkt *pkt,
u16_t data,
s32_t timeout)
{
u16_t value = sys_cpu_to_be16(data);

return net_pkt_append_all(pkt, sizeof(u16_t), (u8_t *)&value,
timeout);
}

/**
* @brief Append u32_t data to last fragment in fragment list of a packet
*
* @details Append data to last fragment. If there is not enough space in last
* fragment then new data fragment will be created and will be added to
* fragment list. Caller has to take care of endianness if needed.
*
* @param pkt Network packet.
* @param data Data to be added
* @param timeout Timeout for buffer allocations
*
* @return True if all the data is placed at end of fragment list,
* False otherwise (In-case of false pkt might contain input data
* in the process of placing into fragments).
*/
static inline bool net_pkt_append_be32_timeout(struct net_pkt *pkt,
u32_t data,
s32_t timeout)
{
u32_t value = sys_cpu_to_be32(data);

return net_pkt_append_all(pkt, sizeof(u32_t), (u8_t *)&value,
timeout);
}

/**
* @brief Append u32_t data to last fragment in fragment list
*
* @details Append data to last fragment. If there is not enough space in last
* fragment then new data fragment will be created and will be added to
* fragment list. Convert data to LE.
*
* @param pkt Network packet fragment list.
* @param data Data to be added
* @param timeout Timeout for buffer allocations
*
* @return True if all the data is placed at end of fragment list,
* False otherwise (In-case of false pkt might contain input data
* in the process of placing into fragments).
*/
static inline bool net_pkt_append_le32_timeout(struct net_pkt *pkt,
u32_t data,
s32_t timeout)
{
u32_t value = sys_cpu_to_le32(data);

return net_pkt_append_all(pkt, sizeof(u32_t), (u8_t *)&value,
timeout);
}

/**
* @brief Get data from buffer
*
@@ -1471,6 +1567,46 @@ static inline struct net_buf *net_pkt_write_be32(struct net_pkt *pkt,
(u8_t *)&value, K_FOREVER);
}

/* Write u8_t data to an arbitrary offset in fragment. */
static inline struct net_buf *net_pkt_write_u8_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u16_t *pos,
u8_t data,
s32_t timeout)
{
return net_pkt_write(pkt, frag, offset, pos, sizeof(u8_t),
&data, timeout);
}

/* Write u16_t big endian value to an arbitrary offset in fragment. */
static inline struct net_buf *net_pkt_write_be16_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u16_t *pos,
u16_t data,
s32_t timeout)
{
u16_t value = htons(data);

return net_pkt_write(pkt, frag, offset, pos, sizeof(u16_t),
(u8_t *)&value, timeout);
}

/* Write u32_t big endian value to an arbitrary offset in fragment. */
static inline struct net_buf *net_pkt_write_be32_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u16_t *pos,
u32_t data,
s32_t timeout)
{
u32_t value = htonl(data);

return net_pkt_write(pkt, frag, offset, pos, sizeof(u32_t),
(u8_t *)&value, timeout);
}

/**
* @brief Insert data at an arbitrary offset in a series of fragments.
*
@@ -1539,6 +1675,47 @@ static inline bool net_pkt_insert_be32(struct net_pkt *pkt,
(u8_t *)&value, K_FOREVER);
}

/* Insert u8_t data at an arbitrary offset in a series of fragments. */
static inline bool net_pkt_insert_u8_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u8_t data,
s32_t timeout)
{
return net_pkt_insert(pkt, frag, offset, sizeof(u8_t), &data,
timeout);
}

/* Insert u16_t big endian value at an arbitrary offset in a series of
* fragments.
*/
static inline bool net_pkt_insert_be16_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u16_t data,
s32_t timeout)
{
u16_t value = htons(data);

return net_pkt_insert(pkt, frag, offset, sizeof(u16_t),
(u8_t *)&value, timeout);
}

/* Insert u32_t big endian value at an arbitrary offset in a series of
* fragments.
*/
static inline bool net_pkt_insert_be32_timeout(struct net_pkt *pkt,
struct net_buf *frag,
u16_t offset,
u32_t data,
s32_t timeout)
{
u32_t value = htonl(data);

return net_pkt_insert(pkt, frag, offset, sizeof(u32_t),
(u8_t *)&value, timeout);
}

/**
* @brief Split a fragment into two parts at arbitrary offset.
*
@@ -150,16 +150,19 @@ static inline enum net_verdict icmpv4_handle_echo_request(struct net_pkt *pkt)
return NET_OK;
}

static void icmpv4_create(struct net_pkt *pkt, u8_t icmp_type, u8_t icmp_code)
static struct net_buf *icmpv4_create(struct net_pkt *pkt, u8_t icmp_type,
u8_t icmp_code)
{
struct net_buf *frag = pkt->frags;
u16_t pos;

net_buf_add(frag, sizeof(struct net_icmp_hdr));

frag = net_pkt_write_u8(pkt, frag, net_pkt_ip_hdr_len(pkt), &pos,
icmp_type);
frag = net_pkt_write_u8(pkt, frag, pos, &pos, icmp_code);
frag = net_pkt_write_u8_timeout(pkt, frag, net_pkt_ip_hdr_len(pkt),
&pos, icmp_type, PKT_WAIT_TIME);
frag = net_pkt_write_u8_timeout(pkt, frag, pos, &pos, icmp_code,
PKT_WAIT_TIME);
return frag;
}

int net_icmpv4_send_echo_request(struct net_if *iface,
@@ -170,6 +173,7 @@ int net_icmpv4_send_echo_request(struct net_if *iface,
struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
const struct in_addr *src;
struct net_pkt *pkt;
int ret;

if (!ipv4) {
return -EINVAL;
@@ -184,12 +188,22 @@ int net_icmpv4_send_echo_request(struct net_if *iface,
*/
pkt = net_pkt_get_reserve_tx(net_if_get_ll_reserve(iface,
(const struct in6_addr *)dst),
K_FOREVER);
PKT_WAIT_TIME);
if (!pkt) {
return -ENOMEM;
}

net_pkt_set_iface(pkt, iface);

net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP);
if (!net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP)) {
ret = -ENOMEM;
goto drop;
}

icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0);
if (!icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) {
ret = -ENOMEM;
goto drop;
}

net_buf_add(pkt->frags, sizeof(struct net_icmpv4_echo_req));

@@ -218,11 +232,23 @@ int net_icmpv4_send_echo_request(struct net_if *iface,

net_stats_update_icmp_drop(iface);

ret = -EIO;

drop:
net_pkt_unref(pkt);

return -EIO;
return ret;
}

#define append(pkt, type, value) \
do { \
if (!net_pkt_append_##type##_timeout(pkt, value, \
PKT_WAIT_TIME)) { \
err = -ENOMEM; \
goto drop; \
} \
} while (0)

int net_icmpv4_send_error(struct net_pkt *orig, u8_t type, u8_t code)
{
int err = -EIO;
@@ -257,12 +283,18 @@ int net_icmpv4_send_error(struct net_pkt *orig, u8_t type, u8_t code)

net_pkt_set_iface(pkt, iface);

net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP);
if (!net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP)) {
err = -ENOMEM;
goto drop;
}

icmpv4_create(pkt, type, code);
if (!icmpv4_create(pkt, type, code)) {
err = -ENOMEM;
goto drop;
}

/* Appending unused part, filled with 0s */
net_pkt_append_be32(pkt, 0);
append(pkt, be32, 0);

if (NET_IPV4_HDR(orig)->proto == IPPROTO_UDP) {
copy_len = sizeof(struct net_ipv4_hdr) +
@@ -555,34 +555,51 @@ int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code,
return err;
}

#define append(pkt, type, value) \
do { \
if (!net_pkt_append_##type##_timeout(pkt, value, \
PKT_WAIT_TIME)) { \
ret = -ENOMEM; \
goto drop; \
} \
} while (0)

int net_icmpv6_send_echo_request(struct net_if *iface,
struct in6_addr *dst,
u16_t identifier,
u16_t sequence)
{
const struct in6_addr *src;
struct net_pkt *pkt;
int ret;

src = net_if_ipv6_select_src_addr(iface, dst);

pkt = net_pkt_get_reserve_tx(net_if_get_ll_reserve(iface, dst),
K_FOREVER);
PKT_WAIT_TIME);
if (!pkt) {
return -ENOMEM;
}

pkt = net_ipv6_create(pkt, src, dst, iface, IPPROTO_ICMPV6);
if (!net_ipv6_create(pkt, src, dst, iface, IPPROTO_ICMPV6)) {
ret = -ENOMEM;
goto drop;
}

net_pkt_set_family(pkt, AF_INET6);
net_pkt_set_iface(pkt, iface);

net_pkt_append_u8(pkt, NET_ICMPV6_ECHO_REQUEST);
net_pkt_append_u8(pkt, 0); /* code */
net_pkt_append_be16(pkt, 0); /* checksum */
net_pkt_append_be16(pkt, identifier);
net_pkt_append_be16(pkt, sequence);
append(pkt, u8, NET_ICMPV6_ECHO_REQUEST);
append(pkt, u8, 0); /* code */
append(pkt, be16, 0); /* checksum */
append(pkt, be16, identifier);
append(pkt, be16, sequence);

net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src);
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst);

if (net_ipv6_finalize(pkt, IPPROTO_ICMPV6) < 0) {
ret = -ENOMEM;
goto drop;
}

@@ -603,11 +620,13 @@ int net_icmpv6_send_echo_request(struct net_if *iface,
return 0;
}

ret = -EIO;

drop:
net_pkt_unref(pkt);
net_stats_update_icmp_drop(iface);

return -EIO;
return ret;
}

enum net_verdict net_icmpv6_input(struct net_pkt *pkt,
@@ -27,13 +27,21 @@
#include "tcp_internal.h"
#include "ipv4.h"

/* Timeout for various buffer allocations in this file. */
#define NET_BUF_TIMEOUT K_MSEC(50)

struct net_pkt *net_ipv4_create(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst,
struct net_if *iface,
u8_t next_header_proto)
{
struct net_buf *header = net_pkt_get_frag(pkt, K_FOREVER);
struct net_buf *header;

header = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT);
if (!header) {
return NULL;
}

net_pkt_frag_insert(pkt, header);

Oops, something went wrong.

0 comments on commit 7c7cfdd

Please sign in to comment.
You can’t perform that action at this time.