net: pkt: Compute TX payload data length
Compute the length of the TX payload that is transported in one
IPv4 or IPv6 datagram taking into account UDP, ICMP or TCP
headers in addition to any IPv6 extension headers added by RPL.
The TCP implementation in Zephyr is known to currently carry at
maximum 8 bytes of options. If the protocol is not known to the
stack, assume that the application handles any protocol headers
as well as the data. Also, if the net_pkt does not have a
context associated, length check on the data is omitted when
appending.
Although payload length is calculated also for TCP, the TCP MSS
value is used as before.
Define IPv4 minimum MTU as 576 octets, See RFC 791, Sections 3.1.
and 3.2.
Signed-off-by: Patrik Flykt <patrik.flykt@intel.com>
diff --git a/include/net/net_ip.h b/include/net/net_ip.h
index c835222..34b2480 100644
--- a/include/net/net_ip.h
+++ b/include/net/net_ip.h
@@ -179,6 +179,7 @@
#define INADDR_ANY_INIT { { { INADDR_ANY } } }
#define NET_IPV6_MTU 1280
+#define NET_IPV4_MTU 576
/** IPv6 extension headers types */
#define NET_IPV6_NEXTHDR_HBHO 0
diff --git a/include/net/net_pkt.h b/include/net/net_pkt.h
index bb27314..9ebf537 100644
--- a/include/net/net_pkt.h
+++ b/include/net/net_pkt.h
@@ -73,6 +73,8 @@
struct net_linkaddr lladdr_src;
struct net_linkaddr lladdr_dst;
+ u16_t data_len; /* amount of payload data that can be added */
+
u16_t appdatalen;
u8_t ll_reserve; /* link layer header length */
u8_t ip_hdr_len; /* pre-filled in order to avoid func call */
diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c
index 5cc470a..cbc34dd 100644
--- a/subsys/net/ip/net_pkt.c
+++ b/subsys/net/ip/net_pkt.c
@@ -38,6 +38,7 @@
#include "net_private.h"
#include "tcp.h"
+#include "rpl.h"
#if defined(CONFIG_NET_TCP)
#define APP_PROTO_LEN NET_TCPH_LEN
@@ -519,14 +520,48 @@
pkt = net_pkt_get_reserve(slab, net_if_get_ll_reserve(iface, addr6),
timeout);
#endif
- if (pkt) {
+ if (pkt && slab != &rx_pkts) {
+ sa_family_t family;
+ uint16_t iface_len, data_len = 0;
+ enum net_ip_protocol proto;
+
net_pkt_set_context(pkt, context);
net_pkt_set_iface(pkt, iface);
- if (context) {
- net_pkt_set_family(pkt,
- net_context_get_family(context));
+ iface_len = net_if_get_mtu(iface);
+
+ family = net_context_get_family(context);
+ net_pkt_set_family(pkt, family);
+
+ if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
+ data_len = max(iface_len, NET_IPV6_MTU);
}
+
+ if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
+ data_len = max(iface_len, NET_IPV4_MTU);
+ }
+
+ proto = net_context_get_ip_proto(context);
+
+ if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) {
+ data_len -= NET_TCPH_LEN;
+ data_len -= NET_TCP_MAX_OPT_SIZE;
+ }
+
+ if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
+ data_len -= NET_UDPH_LEN;
+
+#if defined(CONFIG_NET_RPL_INSERT_HBH_OPTION)
+ data_len -= NET_RPL_HOP_BY_HOP_LEN;
+#endif
+
+ }
+
+ if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
+ data_len -= NET_ICMPH_LEN;
+ }
+
+ pkt->data_len = data_len;
}
return pkt;
@@ -1164,7 +1199,7 @@
{
struct net_buf *frag;
struct net_context *ctx;
- size_t max_len;
+ u16_t max_len, appended;
if (!pkt || !data) {
return 0;
@@ -1179,41 +1214,32 @@
net_pkt_frag_add(pkt, frag);
}
- /* Make sure we don't send more data in one packet than
- * MTU allows.
- * This check really belongs to net_pkt_append_bytes(),
- * but instead done here for efficiency, we assume
- * (at least for now) that that callers of
- * net_pkt_append_bytes() are smart enough to not
- * overflow MTU.
- */
- max_len = 0x10000;
-
ctx = net_pkt_context(pkt);
if (ctx) {
+ /* Make sure we don't send more data in one packet than
+ * protocol or MTU allows when there is a context for the
+ * packet.
+ */
+ max_len = pkt->data_len;
+
#if defined(CONFIG_NET_TCP)
if (ctx->tcp) {
max_len = ctx->tcp->send_mss;
- } else
+ }
#endif
- {
- struct net_if *iface = net_context_get_iface(ctx);
- max_len = net_if_get_mtu(iface);
- /* Optimize for number of jumps in the code ("if"
- * instead of "if/else").
- */
- max_len -= NET_IPV4TCPH_LEN;
- if (net_context_get_family(ctx) != AF_INET) {
- max_len -= NET_IPV6TCPH_LEN - NET_IPV4TCPH_LEN;
- }
+
+ if (len > max_len) {
+ len = max_len;
}
}
- if (len > max_len) {
- len = max_len;
+ appended = net_pkt_append_bytes(pkt, data, len, timeout);
+
+ if (ctx) {
+ pkt->data_len -= appended;
}
- return net_pkt_append_bytes(pkt, data, len, timeout);
+ return appended;
}
/* Helper routine to retrieve single byte from fragment and move