net: tcp: Remove NET_TCP_HDR() macro and direct access to net_buf

Remove NET_TCP_HDR() macro as we cannot safely access TCP header
via it if the network packet header spans over multiple net_buf
fragments.

Fixed also the TCP unit tests so that they pass correctly.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
diff --git a/include/net/net_pkt.h b/include/net/net_pkt.h
index d548240..40defb0 100644
--- a/include/net/net_pkt.h
+++ b/include/net/net_pkt.h
@@ -321,12 +321,6 @@
 	return pkt->frags->data;
 }
 
-static inline u8_t *net_pkt_tcp_data(struct net_pkt *pkt)
-{
-	return &pkt->frags->data[net_pkt_ip_hdr_len(pkt) +
-				 net_pkt_ipv6_ext_len(pkt)];
-}
-
 static inline u8_t *net_pkt_appdata(struct net_pkt *pkt)
 {
 	return pkt->appdata;
@@ -402,7 +396,6 @@
 
 #define NET_IPV6_HDR(pkt) ((struct net_ipv6_hdr *)net_pkt_ip_data(pkt))
 #define NET_IPV4_HDR(pkt) ((struct net_ipv4_hdr *)net_pkt_ip_data(pkt))
-#define NET_TCP_HDR(pkt)  ((struct net_tcp_hdr *)(net_pkt_tcp_data(pkt)))
 
 static inline void net_pkt_set_src_ipv6_addr(struct net_pkt *pkt)
 {
diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c
index 1d117ba..d1767e2 100644
--- a/subsys/net/ip/connection.c
+++ b/subsys/net/ip/connection.c
@@ -24,6 +24,7 @@
 #include "icmpv6.h"
 #include "icmpv4.h"
 #include "udp_internal.h"
+#include "tcp.h"
 #include "connection.h"
 #include "net_stats.h"
 
@@ -801,7 +802,7 @@
 	}
 
 	if (proto == IPPROTO_TCP) {
-		chksum = NET_TCP_HDR(pkt)->chksum;
+		chksum = net_tcp_get_chksum(pkt, pkt->frags);
 	} else {
 		chksum = udp_hdr->chksum;
 	}
@@ -886,8 +887,8 @@
 			   proto == IPPROTO_TCP) {
 			u16_t chksum_calc;
 
-			NET_TCP_HDR(pkt)->chksum = 0;
-			chksum_calc = ~net_calc_chksum_tcp(pkt);
+			net_tcp_set_chksum(pkt, pkt->frags);
+			chksum_calc = net_tcp_get_chksum(pkt, pkt->frags);
 
 			if (chksum != chksum_calc) {
 				net_stats_update_tcp_seg_chkerr();
diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c
index 5cbe81f..f556259 100644
--- a/subsys/net/ip/ipv4.c
+++ b/subsys/net/ip/ipv4.c
@@ -23,6 +23,7 @@
 #include "net_stats.h"
 #include "icmpv4.h"
 #include "udp_internal.h"
+#include "tcp.h"
 #include "ipv4.h"
 
 struct net_pkt *net_ipv4_create_raw(struct net_pkt *pkt,
@@ -103,8 +104,7 @@
 #endif
 #if defined(CONFIG_NET_TCP)
 	if (next_header == IPPROTO_TCP) {
-		NET_TCP_HDR(pkt)->chksum = 0;
-		NET_TCP_HDR(pkt)->chksum = ~net_calc_chksum_tcp(pkt);
+		net_tcp_set_chksum(pkt, pkt->frags);
 	}
 #endif
 
diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c
index b212d5a..af9d6f9 100644
--- a/subsys/net/ip/ipv6.c
+++ b/subsys/net/ip/ipv6.c
@@ -28,6 +28,7 @@
 #include "connection.h"
 #include "icmpv6.h"
 #include "udp_internal.h"
+#include "tcp.h"
 #include "ipv6.h"
 #include "nbr.h"
 #include "6lo.h"
@@ -706,8 +707,7 @@
 
 #if defined(CONFIG_NET_TCP)
 	if (next_header == IPPROTO_TCP) {
-		NET_TCP_HDR(pkt)->chksum = 0;
-		NET_TCP_HDR(pkt)->chksum = ~net_calc_chksum_tcp(pkt);
+		net_tcp_set_chksum(pkt, pkt->frags);
 	} else
 #endif
 
diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c
index cd375c2..957110b 100644
--- a/subsys/net/ip/net_context.c
+++ b/subsys/net/ip/net_context.c
@@ -111,11 +111,18 @@
 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 = NET_TCP_HDR(pkt)->src_port;
+		net_sin6(addr)->sin6_port = tcp_hdr->src_port;
 		net_sin6(addr)->sin6_family = AF_INET6;
 	} else
 #endif
@@ -124,7 +131,7 @@
 	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 = NET_TCP_HDR(pkt)->src_port;
+		net_sin(addr)->sin_port = tcp_hdr->src_port;
 		net_sin(addr)->sin_family = AF_INET;
 	} else
 #endif
@@ -137,6 +144,7 @@
 
 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++) {
@@ -149,11 +157,13 @@
 			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 !=
-			    NET_TCP_HDR(pkt)->src_port) {
+			    tcp_hdr->src_port) {
 				continue;
 			}
 
@@ -168,7 +178,7 @@
 #if defined(CONFIG_NET_IPV4)
 		case AF_INET:
 			if (net_sin(&tcp_backlog[i].remote)->sin_port !=
-			    NET_TCP_HDR(pkt)->src_port) {
+			    tcp_hdr->src_port) {
 				continue;
 			}
 
@@ -221,6 +231,7 @@
 
 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);
@@ -229,9 +240,13 @@
 		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(NET_TCP_HDR(pkt)->ack)) {
+	if (tcp_backlog[r].send_seq + 1 != sys_get_be32(tcp_hdr->ack)) {
 		return -EINVAL;
 	}
 
@@ -258,6 +273,7 @@
 
 static int tcp_backlog_rst(struct net_pkt *pkt)
 {
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int r;
 
 	r = tcp_backlog_find(pkt, NULL);
@@ -265,8 +281,13 @@
 		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(NET_TCP_HDR(pkt)->seq)) {
+	if (tcp_backlog[r].send_ack != sys_get_be32(tcp_hdr->seq)) {
 		return -EINVAL;
 	}
 
@@ -852,7 +873,7 @@
 
 #if defined(CONFIG_NET_TCP)
 #if defined(CONFIG_NET_DEBUG_CONTEXT)
-#define net_tcp_print_recv_info(str, pkt, port)		\
+#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,	\
@@ -865,23 +886,37 @@
 		}							\
 	} while (0)
 
-#define net_tcp_print_send_info(str, pkt, port)		\
+#define net_tcp_print_send_info(str, pkt, port)				\
 	do {								\
-		if (net_context_get_family(context) == AF_INET6) {	\
+		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(context) == AF_INET) {\
+		} 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,
@@ -901,7 +936,7 @@
 		net_pkt_unref(pkt);
 	}
 
-	net_tcp_print_send_info(msg, pkt, NET_TCP_HDR(pkt)->dst_port);
+	print_send_info(pkt, msg);
 
 	return ret;
 }
@@ -941,7 +976,7 @@
 		return ret;
 	}
 
-	net_tcp_print_send_info("ACK", pkt, NET_TCP_HDR(pkt)->dst_port);
+	print_send_info(pkt, "ACK");
 
 	ret = net_tcp_send_pkt(pkt);
 	if (ret < 0) {
@@ -962,7 +997,7 @@
 		return ret;
 	}
 
-	net_tcp_print_send_info("RST", pkt, NET_TCP_HDR(pkt)->dst_port);
+	print_send_info(pkt, "RST");
 
 	ret = net_send_data(pkt);
 	if (ret < 0) {
@@ -974,10 +1009,15 @@
 
 static int tcp_hdr_len(struct net_pkt *pkt)
 {
-	/* "Offset": 4-bit field in high nibble, units of dwords */
-	struct net_tcp_hdr *hdr = (void *)net_pkt_tcp_data(pkt);
+	struct net_tcp_hdr hdr, *tcp_hdr;
 
-	return 4 * (hdr->offset >> 4);
+	/* "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
@@ -986,23 +1026,29 @@
 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;
 
 	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, NET_TCP_HDR(pkt)->src_port);
+	net_tcp_print_recv_info("DATA", pkt, tcp_hdr->src_port);
 
-	tcp_flags = NET_TCP_FLAGS(pkt);
+	tcp_flags = NET_TCP_FLAGS(tcp_hdr);
 	if (tcp_flags & NET_TCP_ACK) {
 		net_tcp_ack_received(context,
-				     sys_get_be32(NET_TCP_HDR(pkt)->ack));
+				     sys_get_be32(tcp_hdr->ack));
 	}
 
 	/*
@@ -1018,7 +1064,7 @@
 
 		net_stats_update_tcp_seg_rst();
 
-		net_tcp_print_recv_info("RST", pkt, NET_TCP_HDR(pkt)->src_port);
+		net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port);
 
 		if (context->recv_cb) {
 			context->recv_cb(context, NULL, -ECONNRESET,
@@ -1030,7 +1076,7 @@
 		return NET_DROP;
 	}
 
-	if (net_tcp_seq_cmp(sys_get_be32(NET_TCP_HDR(pkt)->seq),
+	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.
@@ -1043,7 +1089,7 @@
 		return NET_DROP;
 	}
 
-	if (sys_get_be32(NET_TCP_HDR(pkt)->seq) - context->tcp->send_ack) {
+	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
@@ -1097,6 +1143,7 @@
 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);
@@ -1115,7 +1162,12 @@
 
 	NET_ASSERT(net_pkt_iface(pkt));
 
-	if (NET_TCP_FLAGS(pkt) & NET_TCP_RST) {
+	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();
@@ -1132,15 +1184,15 @@
 		return NET_DROP;
 	}
 
-	if (NET_TCP_FLAGS(pkt) & NET_TCP_SYN) {
+	if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN) {
 		context->tcp->send_ack =
-			sys_get_be32(NET_TCP_HDR(pkt)->seq) + 1;
+			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(pkt) == (NET_TCP_SYN | NET_TCP_ACK)) {
+	if (NET_TCP_FLAGS(tcp_hdr) == (NET_TCP_SYN | NET_TCP_ACK)) {
 		struct sockaddr *laddr;
 		struct sockaddr *raddr;
 
@@ -1159,12 +1211,12 @@
 			raddr = (struct sockaddr *)&r6addr;
 
 			r6addr.sin6_family = AF_INET6;
-			r6addr.sin6_port = NET_TCP_HDR(pkt)->src_port;
+			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 = NET_TCP_HDR(pkt)->dst_port;
+			l6addr.sin6_port = htons(tcp_hdr->dst_port);
 			net_ipaddr_copy(&l6addr.sin6_addr,
 					&NET_IPV6_HDR(pkt)->dst);
 		} else
@@ -1175,12 +1227,12 @@
 			raddr = (struct sockaddr *)&r4addr;
 
 			r4addr.sin_family = AF_INET;
-			r4addr.sin_port = NET_TCP_HDR(pkt)->src_port;
+			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 = NET_TCP_HDR(pkt)->dst_port;
+			l4addr.sin_port = htons(tcp_hdr->dst_port);
 			net_ipaddr_copy(&l4addr.sin_addr,
 					&NET_IPV4_HDR(pkt)->dst);
 		} else
@@ -1197,8 +1249,8 @@
 
 		ret = net_tcp_register(raddr,
 				       laddr,
-				       ntohs(NET_TCP_HDR(pkt)->src_port),
-				       ntohs(NET_TCP_HDR(pkt)->dst_port),
+				       ntohs(tcp_hdr->src_port),
+				       ntohs(tcp_hdr->dst_port),
 				       tcp_established,
 				       context,
 				       &context->conn_handler);
@@ -1424,6 +1476,13 @@
 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)
@@ -1431,7 +1490,7 @@
 		struct sockaddr_in_ptr *addr4 = net_sin_ptr(addr);
 
 		addr4->sin_family = AF_INET;
-		addr4->sin_port = NET_TCP_HDR(pkt)->dst_port;
+		addr4->sin_port = tcp_hdr->dst_port;
 		addr4->sin_addr = &NET_IPV4_HDR(pkt)->dst;
 	}
 #endif
@@ -1441,7 +1500,7 @@
 		struct sockaddr_in6_ptr *addr6 = net_sin6_ptr(addr);
 
 		addr6->sin6_family = AF_INET6;
-		addr6->sin6_port = NET_TCP_HDR(pkt)->dst_port;
+		addr6->sin6_port = tcp_hdr->dst_port;
 		addr6->sin6_addr = &NET_IPV6_HDR(pkt)->dst;
 	}
 #endif
@@ -1466,6 +1525,7 @@
 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;
 
@@ -1492,14 +1552,19 @@
 
 	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(pkt) == NET_TCP_SYN) {
+	if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_SYN) {
 		struct sockaddr peer, *remote;
 		int r;
 
-		net_tcp_print_recv_info("SYN", pkt, NET_TCP_HDR(pkt)->src_port);
+		net_tcp_print_recv_info("SYN", pkt, tcp_hdr->src_port);
 
 		net_tcp_change_state(tcp, NET_TCP_SYN_RCVD);
 
@@ -1508,7 +1573,7 @@
 		/* 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(NET_TCP_HDR(pkt)->seq) + 1;
+			sys_get_be32(tcp_hdr->seq) + 1;
 		context->tcp->recv_max_ack = context->tcp->send_seq + 1;
 
 		r = tcp_backlog_syn(pkt, context);
@@ -1533,7 +1598,7 @@
 	 * See RFC 793 chapter 3.4 "Reset Processing" and RFC 793, page 65
 	 * for more details.
 	 */
-	if (NET_TCP_FLAGS(pkt) == NET_TCP_RST) {
+	if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_RST) {
 
 		if (tcp_backlog_rst(pkt) < 0) {
 			net_stats_update_tcp_seg_rsterr();
@@ -1542,7 +1607,7 @@
 
 		net_stats_update_tcp_seg_rst();
 
-		net_tcp_print_recv_info("RST", pkt, NET_TCP_HDR(pkt)->src_port);
+		net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port);
 
 		return NET_DROP;
 	}
@@ -1550,7 +1615,7 @@
 	/*
 	 * If we receive ACK, we go to ESTABLISHED state.
 	 */
-	if (NET_TCP_FLAGS(pkt) == NET_TCP_ACK) {
+	if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_ACK) {
 		struct net_context *new_context;
 		struct sockaddr local_addr;
 		struct sockaddr remote_addr;
@@ -1558,7 +1623,7 @@
 		socklen_t addrlen;
 		int ret;
 
-		net_tcp_print_recv_info("ACK", pkt, NET_TCP_HDR(pkt)->src_port);
+		net_tcp_print_recv_info("ACK", pkt, tcp_hdr->src_port);
 
 		if (!context->tcp->accept_cb) {
 			NET_DBG("No accept callback, connection reset.");
@@ -1595,8 +1660,8 @@
 			remote_addr6->sin6_family = AF_INET6;
 			local_addr6->sin6_family = AF_INET6;
 
-			local_addr6->sin6_port = NET_TCP_HDR(pkt)->dst_port;
-			remote_addr6->sin6_port = NET_TCP_HDR(pkt)->src_port;
+			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);
@@ -1616,8 +1681,8 @@
 			remote_addr4->sin_family = AF_INET;
 			local_addr4->sin_family = AF_INET;
 
-			local_addr4->sin_port = NET_TCP_HDR(pkt)->dst_port;
-			remote_addr4->sin_port = NET_TCP_HDR(pkt)->src_port;
+			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);
@@ -2077,8 +2142,7 @@
 
 #if defined(CONFIG_NET_TCP)
 	if (proto == IPPROTO_TCP) {
-		net_pkt_set_appdata(pkt, net_pkt_tcp_data(pkt) +
-				    tcp_hdr_len(pkt));
+		proto_len = tcp_hdr_len(pkt);
 	}
 #endif /* CONFIG_NET_TCP */
 
diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c
index 8545753..5149a69 100644
--- a/subsys/net/ip/net_pkt.c
+++ b/subsys/net/ip/net_pkt.c
@@ -1746,6 +1746,32 @@
 	return (struct net_udp_hdr *)(frag->data + offset);
 }
 
+struct net_tcp_hdr *net_pkt_tcp_data(struct net_pkt *pkt)
+{
+	struct net_buf *frag;
+	u16_t offset;
+
+	frag = net_frag_get_pos(pkt,
+				net_pkt_ip_hdr_len(pkt) +
+				net_pkt_ipv6_ext_len(pkt),
+				&offset);
+	if (!frag) {
+		/* We tried to read past the end of the data */
+		NET_ASSERT_INFO(frag,
+				"IP hdr %d ext len %d offset %d pos %d "
+				"total %zd",
+				net_pkt_ip_hdr_len(pkt),
+				net_pkt_ipv6_ext_len(pkt),
+				offset,
+				net_pkt_ip_hdr_len(pkt) +
+				net_pkt_ipv6_ext_len(pkt),
+				net_buf_frags_len(pkt->frags));
+		return NULL;
+	}
+
+	return (struct net_tcp_hdr *)(frag->data + offset);
+}
+
 void net_pkt_init(void)
 {
 	NET_DBG("Allocating %u RX (%zu bytes), %u TX (%zu bytes), "
diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h
index ea28b39..750d017 100644
--- a/subsys/net/ip/net_private.h
+++ b/subsys/net/ip/net_private.h
@@ -70,6 +70,19 @@
 	return NULL;
 }
 
+struct net_tcp_hdr *net_pkt_tcp_data(struct net_pkt *pkt);
+
+static inline
+struct net_tcp_hdr *net_tcp_header_fits(struct net_pkt *pkt,
+					struct net_tcp_hdr *hdr)
+{
+	if (net_header_fits(pkt, (u8_t *)hdr, sizeof(*hdr))) {
+		return hdr;
+	}
+
+	return NULL;
+}
+
 #if defined(CONFIG_NET_IPV4)
 extern u16_t net_calc_chksum_ipv4(struct net_pkt *pkt);
 #endif /* CONFIG_NET_IPV4 */
diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c
index d35c611..a3c5c18 100644
--- a/subsys/net/ip/tcp.c
+++ b/subsys/net/ip/tcp.c
@@ -77,10 +77,17 @@
 
 static void net_tcp_trace(struct net_pkt *pkt, struct net_tcp *tcp)
 {
-	u8_t flags = NET_TCP_FLAGS(pkt);
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	u32_t rel_ack, ack;
+	u8_t flags;
 
-	ack = sys_get_be32(NET_TCP_HDR(pkt)->ack);
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return;
+	}
+
+	flags = NET_TCP_FLAGS(tcp_hdr);
+	ack = sys_get_be32(tcp_hdr->ack);
 
 	if (!tcp->sent_ack) {
 		rel_ack = 0;
@@ -91,10 +98,10 @@
 	NET_DBG("pkt %p src %u dst %u seq 0x%04x (%u) ack 0x%04x (%u/%u) "
 		"flags %c%c%c%c%c%c win %u chk 0x%04x",
 		pkt,
-		ntohs(NET_TCP_HDR(pkt)->src_port),
-		ntohs(NET_TCP_HDR(pkt)->dst_port),
-		sys_get_be32(NET_TCP_HDR(pkt)->seq),
-		sys_get_be32(NET_TCP_HDR(pkt)->seq),
+		ntohs(tcp_hdr->src_port),
+		ntohs(tcp_hdr->dst_port),
+		sys_get_be32(tcp_hdr->seq),
+		sys_get_be32(tcp_hdr->seq),
 		ack,
 		ack,
 		/* This tells how many bytes we are acking now */
@@ -105,8 +112,8 @@
 		upper_if_set('r', flags & NET_TCP_RST),
 		upper_if_set('s', flags & NET_TCP_SYN),
 		upper_if_set('f', flags & NET_TCP_FIN),
-		sys_get_be16(NET_TCP_HDR(pkt)->wnd),
-		ntohs(NET_TCP_HDR(pkt)->chksum));
+		sys_get_be16(tcp_hdr->wnd),
+		ntohs(tcp_hdr->chksum));
 }
 #else
 #define net_tcp_trace(...)
@@ -295,8 +302,8 @@
 				       struct net_pkt *pkt)
 {
 	struct net_buf *header, *tail = NULL;
-	struct net_tcp_hdr *tcphdr;
 	struct net_context *context = tcp->context;
+	struct net_tcp_hdr *tcp_hdr;
 	u16_t dst_port, src_port;
 	u8_t optlen = 0;
 
@@ -354,23 +361,23 @@
 
 	net_pkt_frag_add(pkt, header);
 
-	tcphdr = (struct net_tcp_hdr *)net_buf_add(header, NET_TCPH_LEN);
+	tcp_hdr = (struct net_tcp_hdr *)net_buf_add(header, NET_TCPH_LEN);
 
 	if (segment->options && segment->optlen) {
 		optlen = net_tcp_add_options(header, segment->optlen,
 					segment->options);
 	}
 
-	tcphdr->offset = (NET_TCPH_LEN + optlen) << 2;
+	tcp_hdr->offset = (NET_TCPH_LEN + optlen) << 2;
 
-	tcphdr->src_port = src_port;
-	tcphdr->dst_port = dst_port;
-	sys_put_be32(segment->seq, tcphdr->seq);
-	sys_put_be32(segment->ack, tcphdr->ack);
-	tcphdr->flags = segment->flags;
-	sys_put_be16(segment->wnd, tcphdr->wnd);
-	tcphdr->urg[0] = 0;
-	tcphdr->urg[1] = 0;
+	tcp_hdr->src_port = src_port;
+	tcp_hdr->dst_port = dst_port;
+	sys_put_be32(segment->seq, tcp_hdr->seq);
+	sys_put_be32(segment->ack, tcp_hdr->ack);
+	tcp_hdr->flags = segment->flags;
+	sys_put_be16(segment->wnd, tcp_hdr->wnd);
+	tcp_hdr->urg[0] = 0;
+	tcp_hdr->urg[1] = 0;
 
 	if (tail) {
 		net_pkt_frag_add(pkt, tail);
@@ -695,9 +702,14 @@
 int net_tcp_send_pkt(struct net_pkt *pkt)
 {
 	struct net_context *ctx = net_pkt_context(pkt);
-	struct net_tcp_hdr *tcphdr = NET_TCP_HDR(pkt);
+	struct net_tcp_hdr hdr, *tcp_hdr;
 
-	sys_put_be32(ctx->tcp->send_ack, tcphdr->ack);
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return -EINVAL;
+	}
+
+	sys_put_be32(ctx->tcp->send_ack, tcp_hdr->ack);
 
 	/* The data stream code always sets this flag, because
 	 * existing stacks (Linux, anyway) seem to ignore data packets
@@ -705,10 +717,10 @@
 	 * anyway if we know we need it just to sanify edge cases.
 	 */
 	if (ctx->tcp->sent_ack != ctx->tcp->send_ack) {
-		tcphdr->flags |= NET_TCP_ACK;
+		tcp_hdr->flags |= NET_TCP_ACK;
 	}
 
-	if (tcphdr->flags & NET_TCP_FIN) {
+	if (tcp_hdr->flags & NET_TCP_FIN) {
 		ctx->tcp->fin_sent = 1;
 	}
 
@@ -716,6 +728,10 @@
 
 	net_pkt_set_sent(pkt, true);
 
+	/* As we modified the header, we need to write it back.
+	 */
+	net_tcp_set_hdr(pkt, tcp_hdr);
+
 	/* We must have special handling for some network technologies that
 	 * tweak the IP protocol headers during packet sending. This happens
 	 * with Bluetooth and IEEE 802.15.4 which use IPv6 header compression
@@ -823,7 +839,6 @@
 	sys_slist_t *list = &ctx->tcp->sent_list;
 	sys_snode_t *head;
 	struct net_pkt *pkt;
-	struct net_tcp_hdr *tcphdr;
 	u32_t seq;
 	bool valid_ack = false;
 
@@ -833,18 +848,21 @@
 	}
 
 	while (!sys_slist_is_empty(list)) {
+		struct net_tcp_hdr hdr, *tcp_hdr;
+
 		head = sys_slist_peek_head(list);
 		pkt = CONTAINER_OF(head, struct net_pkt, sent_list);
-		tcphdr = NET_TCP_HDR(pkt);
 
-		seq = sys_get_be32(tcphdr->seq) + net_pkt_appdatalen(pkt) - 1;
+		tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+
+		seq = sys_get_be32(tcp_hdr->seq) + net_pkt_appdatalen(pkt) - 1;
 
 		if (!net_tcp_seq_greater(ack, seq)) {
 			net_stats_update_tcp_seg_ackerr();
 			break;
 		}
 
-		if (tcphdr->flags & NET_TCP_FIN) {
+		if (tcp_hdr->flags & NET_TCP_FIN) {
 			enum net_tcp_state s = net_tcp_get_state(tcp);
 
 			if (s == NET_TCP_FIN_WAIT_1) {
@@ -1003,6 +1021,142 @@
 
 bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt)
 {
+	struct net_tcp_hdr hdr, *tcp_hdr;
+
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
 	return !net_tcp_seq_greater(tcp->send_ack + get_recv_wnd(tcp),
-				    sys_get_be32(NET_TCP_HDR(pkt)->seq));
+				    sys_get_be32(tcp_hdr->seq));
+}
+
+struct net_tcp_hdr *net_tcp_get_hdr(struct net_pkt *pkt,
+				    struct net_tcp_hdr *hdr)
+{
+	struct net_tcp_hdr *tcp_hdr;
+	struct net_buf *frag;
+	u16_t pos;
+
+	tcp_hdr = net_pkt_tcp_data(pkt);
+	if (net_tcp_header_fits(pkt, tcp_hdr)) {
+		return tcp_hdr;
+	}
+
+	frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) +
+			     net_pkt_ipv6_ext_len(pkt),
+			     &pos, sizeof(hdr->src_port),
+			     (u8_t *)&hdr->src_port);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->dst_port),
+			     (u8_t *)&hdr->dst_port);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->seq), hdr->seq);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->ack), hdr->ack);
+	frag = net_frag_read_u8(frag, pos, &pos, &hdr->offset);
+	frag = net_frag_read_u8(frag, pos, &pos, &hdr->flags);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->wnd), hdr->wnd);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->chksum),
+			     (u8_t *)&hdr->chksum);
+	frag = net_frag_read(frag, pos, &pos, sizeof(hdr->urg), hdr->urg);
+
+	if (!frag && pos == 0xffff) {
+		NET_ASSERT(frag);
+		return NULL;
+	}
+
+	return hdr;
+}
+
+struct net_tcp_hdr *net_tcp_set_hdr(struct net_pkt *pkt,
+				    struct net_tcp_hdr *hdr)
+{
+	struct net_buf *frag;
+	u16_t pos;
+
+	if (net_tcp_header_fits(pkt, hdr)) {
+		return hdr;
+	}
+
+	frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) +
+			     net_pkt_ipv6_ext_len(pkt),
+			     &pos, sizeof(hdr->src_port),
+			     (u8_t *)&hdr->src_port, ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->dst_port),
+			     (u8_t *)&hdr->dst_port, ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->seq), hdr->seq,
+			     ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->ack), hdr->ack,
+			     ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->offset),
+			     &hdr->offset, ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->flags),
+			     &hdr->flags, ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->wnd), hdr->wnd,
+			     ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->chksum),
+			     (u8_t *)&hdr->chksum, ALLOC_TIMEOUT);
+	frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->urg), hdr->urg,
+			     ALLOC_TIMEOUT);
+
+	if (!frag) {
+		NET_ASSERT(frag);
+		return NULL;
+	}
+
+	return hdr;
+}
+
+u16_t net_tcp_get_chksum(struct net_pkt *pkt, struct net_buf *frag)
+{
+	struct net_tcp_hdr *hdr;
+	u16_t chksum;
+	u16_t pos;
+
+	hdr = net_pkt_tcp_data(pkt);
+	if (net_tcp_header_fits(pkt, hdr)) {
+		return hdr->chksum;
+	}
+
+	frag = net_frag_read(frag,
+			     net_pkt_ip_hdr_len(pkt) +
+			     net_pkt_ipv6_ext_len(pkt) +
+			     2 + 2 + 4 + 4 + /* src + dst + seq + ack */
+			     1 + 1 + 2 /* offset + flags + wnd */,
+			     &pos, sizeof(chksum), (u8_t *)&chksum);
+	NET_ASSERT(frag);
+
+	return chksum;
+}
+
+struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag)
+{
+	struct net_tcp_hdr *hdr;
+	u16_t chksum = 0;
+	u16_t pos;
+
+	hdr = net_pkt_tcp_data(pkt);
+	if (net_tcp_header_fits(pkt, hdr)) {
+		hdr->chksum = 0;
+		hdr->chksum = ~net_calc_chksum_tcp(pkt);
+
+		return frag;
+	}
+
+	/* We need to set the checksum to 0 first before the calc */
+	frag = net_pkt_write(pkt, frag,
+			     net_pkt_ip_hdr_len(pkt) +
+			     net_pkt_ipv6_ext_len(pkt) +
+			     2 + 2 + 4 + 4 + /* src + dst + seq + ack */
+			     1 + 1 + 2 /* offset + flags + wnd */,
+			     &pos, sizeof(chksum), (u8_t *)&chksum,
+			     ALLOC_TIMEOUT);
+
+	chksum = ~net_calc_chksum_tcp(pkt);
+
+	frag = net_pkt_write(pkt, frag, pos - 2, &pos, sizeof(chksum),
+			     (u8_t *)&chksum, ALLOC_TIMEOUT);
+
+	NET_ASSERT(frag);
+
+	return frag;
 }
diff --git a/subsys/net/ip/tcp.h b/subsys/net/ip/tcp.h
index cca3bd6..f843d81 100644
--- a/subsys/net/ip/tcp.h
+++ b/subsys/net/ip/tcp.h
@@ -70,7 +70,7 @@
 #define NET_TCP_URG 0x20
 #define NET_TCP_CTL 0x3f
 
-#define NET_TCP_FLAGS(net_pkt) (NET_TCP_HDR(net_pkt)->flags & NET_TCP_CTL)
+#define NET_TCP_FLAGS(hdr) (hdr->flags & NET_TCP_CTL)
 
 /* TCP max window size */
 #define NET_TCP_MAX_WIN   (4 * 1024)
@@ -358,6 +358,58 @@
 bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt);
 
 #if defined(CONFIG_NET_TCP)
+/**
+ * @brief Get TCP packet header data from net_pkt. The array values are in
+ * network byte order and other values are in host byte order.
+ *
+ * @param pkt Network packet
+ * @param hdr Where to place the header.
+ *
+ * @return Return pointer to header or NULL if something went wrong.
+ */
+struct net_tcp_hdr *net_tcp_get_hdr(struct net_pkt *pkt,
+				    struct net_tcp_hdr *hdr);
+
+/**
+ * @brief Set TCP packet header data in net_pkt. The array values are in
+ * network byte order and other values are in host byte order.
+ *
+ * @param pkt Network packet
+ * @param hdr Header to be written
+ *
+ * @return Return hdr or NULL if error
+ */
+struct net_tcp_hdr *net_tcp_set_hdr(struct net_pkt *pkt,
+				    struct net_tcp_hdr *hdr);
+
+/**
+ * @brief Set TCP checksum in network packet.
+ *
+ * @param pkt Network packet
+ * @param frag Fragment where to start calculating the offset.
+ * Typically this is set to pkt->frags by the caller.
+ *
+ * @return Return the actual fragment where the checksum was written.
+ */
+struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag);
+
+/**
+ * @brief Get TCP checksum from network packet.
+ *
+ * @param pkt Network packet
+ * @param frag Fragment where to start calculating the offset.
+ * Typically this is set to pkt->frags by the caller.
+ *
+ * @return Return the checksum in host byte order.
+ */
+u16_t net_tcp_get_chksum(struct net_pkt *pkt, struct net_buf *frag);
+#else
+#define net_tcp_get_chksum(pkt, frag) (0)
+#define net_tcp_set_chksum(pkt, frag) NULL
+#define net_tcp_set_hdr(pkt, frag) NULL
+#endif
+
+#if defined(CONFIG_NET_TCP)
 void net_tcp_init(void);
 #else
 #define net_tcp_init(...)
diff --git a/tests/net/tcp/src/main.c b/tests/net/tcp/src/main.c
index 1b21de2..78aba86 100644
--- a/tests/net/tcp/src/main.c
+++ b/tests/net/tcp/src/main.c
@@ -61,6 +61,8 @@
 static struct sockaddr_in my_v4_addr;
 static struct sockaddr_in peer_v4_addr;
 
+#define NET_TCP_HDR(pkt)  net_pkt_tcp_data(pkt)
+
 #define MY_TCP_PORT 5545
 #define PEER_TCP_PORT 9876
 
@@ -258,27 +260,30 @@
 			   u16_t remote_port,
 			   u16_t local_port)
 {
-	NET_IPV6_HDR(pkt)->vtc = 0x60;
-	NET_IPV6_HDR(pkt)->tcflow = 0;
-	NET_IPV6_HDR(pkt)->flow = 0;
-	NET_IPV6_HDR(pkt)->len[0] = 0;
-	NET_IPV6_HDR(pkt)->len[1] = NET_TCPH_LEN;
+	struct net_ipv6_hdr ipv6;
+	struct net_tcp_hdr tcp_hdr = { 0 };
+	u8_t data[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
 
-	NET_IPV6_HDR(pkt)->nexthdr = IPPROTO_TCP;
-	NET_IPV6_HDR(pkt)->hop_limit = 255;
+	ipv6.vtc = 0x60;
+	ipv6.tcflow = 0;
+	ipv6.flow = 0;
+	ipv6.len[0] = 0;
+	ipv6.len[1] = NET_TCPH_LEN + sizeof(data);
+	ipv6.nexthdr = IPPROTO_TCP;
+	ipv6.hop_limit = 255;
 
-	net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, remote_addr);
-	net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, local_addr);
+	net_ipaddr_copy(&ipv6.src, remote_addr);
+	net_ipaddr_copy(&ipv6.dst, local_addr);
 
 	net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
-
-	NET_TCP_HDR(pkt)->src_port = htons(remote_port);
-	NET_TCP_HDR(pkt)->dst_port = htons(local_port);
-
 	net_pkt_set_ipv6_ext_len(pkt, 0);
 
-	net_buf_add(pkt->frags, net_pkt_ip_hdr_len(pkt) +
-				sizeof(struct net_tcp_hdr));
+	tcp_hdr.src_port = htons(remote_port);
+	tcp_hdr.dst_port = htons(local_port);
+
+	net_pkt_append_all(pkt, sizeof(ipv6), (u8_t *)&ipv6, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(tcp_hdr), (u8_t *)&tcp_hdr, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(data), data, K_FOREVER);
 }
 
 static void setup_ipv4_tcp(struct net_pkt *pkt,
@@ -287,26 +292,120 @@
 			   u16_t remote_port,
 			   u16_t local_port)
 {
-	NET_IPV4_HDR(pkt)->vhl = 0x45;
-	NET_IPV4_HDR(pkt)->tos = 0;
-	NET_IPV4_HDR(pkt)->len[0] = 0;
-	NET_IPV4_HDR(pkt)->len[1] = NET_TCPH_LEN +
+	struct net_ipv4_hdr ipv4;
+	struct net_tcp_hdr tcp_hdr = { 0 };
+	u8_t data[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+	ipv4.vhl = 0x45;
+	ipv4.tos = 0;
+	ipv4.len[0] = 0;
+	ipv4.len[1] = NET_TCPH_LEN + sizeof(data) +
 		sizeof(struct net_ipv4_hdr);
 
-	NET_IPV4_HDR(pkt)->proto = IPPROTO_TCP;
+	ipv4.proto = IPPROTO_TCP;
 
-	net_ipaddr_copy(&NET_IPV4_HDR(pkt)->src, remote_addr);
-	net_ipaddr_copy(&NET_IPV4_HDR(pkt)->dst, local_addr);
+	net_ipaddr_copy(&ipv4.src, remote_addr);
+	net_ipaddr_copy(&ipv4.dst, local_addr);
 
 	net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr));
 
-	NET_TCP_HDR(pkt)->src_port = htons(remote_port);
-	NET_TCP_HDR(pkt)->dst_port = htons(local_port);
+	tcp_hdr.src_port = htons(remote_port);
+	tcp_hdr.dst_port = htons(local_port);
 
-	net_pkt_set_ipv6_ext_len(pkt, 0);
+	net_pkt_append_all(pkt, sizeof(ipv4), (u8_t *)&ipv4, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(tcp_hdr), (u8_t *)&tcp_hdr, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(data), data, K_FOREVER);
+}
 
-	net_buf_add(pkt->frags, net_pkt_ip_hdr_len(pkt) +
-				sizeof(struct net_tcp_hdr));
+u8_t ipv6_hop_by_hop_ext_hdr[] = {
+/* Next header TCP */
+0x06,
+/* Length (multiple of 8 octets) */
+0x08,
+/* Experimental extension */
+0x3e,
+/* Length in bytes */
+0x20,
+0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+0x49, 0x4A, 0x4B, 0x4C, 0x4E, 0x4F, 0x50, 0x51,
+0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+0x5A, 0x5B, 0x5C, 0x5D, 0x5F, 0x60, 0x61, 0x62,
+/* Another experimental extension */
+0x3e,
+/* Length in bytes */
+0x20,
+0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+0x6B, 0x6C, 0x6D, 0x6F, 0x70, 0x71, 0x72, 0x73,
+0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B,
+0x7C, 0x7D, 0x7E, 0x21, 0x22, 0x23, 0x24, 0x25,
+/* Padding */
+0x00,
+/* Padding */
+0x00,
+};
+
+static void setup_ipv6_tcp_long(struct net_pkt *pkt,
+				struct in6_addr *remote_addr,
+				struct in6_addr *local_addr,
+				u16_t remote_port,
+				u16_t local_port)
+{
+	struct net_tcp_hdr hdr, *tcp_hdr;
+	struct net_ipv6_hdr ipv6;
+	u8_t data[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+	ipv6.vtc = 0x60;
+	ipv6.tcflow = 0;
+	ipv6.flow = 0;
+	ipv6.len[0] = 0;
+	ipv6.len[1] = NET_TCPH_LEN + sizeof(data) +
+		sizeof(ipv6_hop_by_hop_ext_hdr);
+
+	ipv6.nexthdr = 0; /* Hop-by-hop option */
+	ipv6.hop_limit = 255;
+
+	net_ipaddr_copy(&ipv6.src, remote_addr);
+	net_ipaddr_copy(&ipv6.dst, local_addr);
+
+	net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
+
+	net_pkt_append_all(pkt, sizeof(ipv6), (u8_t *)&ipv6, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(ipv6_hop_by_hop_ext_hdr),
+			   ipv6_hop_by_hop_ext_hdr, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(hdr), (u8_t *)&hdr, K_FOREVER);
+	net_pkt_append_all(pkt, sizeof(data), data, K_FOREVER);
+
+	net_pkt_set_ipv6_ext_len(pkt, sizeof(ipv6_hop_by_hop_ext_hdr));
+
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (tcp_hdr != &hdr) {
+		TC_ERROR("Invalid TCP header pointer\n");
+		return;
+	}
+
+	tcp_hdr->src_port = htons(remote_port);
+	tcp_hdr->dst_port = htons(local_port);
+
+	net_tcp_set_hdr(pkt, &hdr);
+
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (tcp_hdr != &hdr) {
+		TC_ERROR("Invalid TCP header pointer %p\n", tcp_hdr);
+		test_failed = true;
+		return;
+	}
+
+	if (tcp_hdr->src_port != htons(remote_port)) {
+		TC_ERROR("Invalid remote port, should have been %d was %d\n",
+			 remote_port, ntohs(tcp_hdr->src_port));
+		test_failed = true;
+	}
+
+	if (tcp_hdr->dst_port != htons(local_port)) {
+		TC_ERROR("Invalid local port, should have been %d was %d\n",
+			 local_port, ntohs(tcp_hdr->dst_port));
+		test_failed = true;
+	}
 }
 
 #define TIMEOUT K_MSEC(200)
@@ -342,11 +441,11 @@
 	}
 
 	if (k_sem_take(&recv_lock, TIMEOUT)) {
-		DBG("Timeout, packet not received\n");
 		if (expect_failure) {
-			return false;
-		} else {
 			return true;
+		} else {
+			DBG("Timeout, packet not received\n");
+			return false;
 		}
 	}
 
@@ -393,11 +492,11 @@
 	}
 
 	if (k_sem_take(&recv_lock, TIMEOUT)) {
-		DBG("Timeout, packet not received\n");
 		if (expect_failure) {
-			return false;
-		} else {
 			return true;
+		} else {
+			DBG("Timeout, packet not received\n");
+			return false;
 		}
 	}
 
@@ -413,6 +512,57 @@
 	return !fail;
 }
 
+static bool send_ipv6_tcp_long_msg(struct net_if *iface,
+				   struct in6_addr *src,
+				   struct in6_addr *dst,
+				   u16_t src_port,
+				   u16_t dst_port,
+				   struct ud *ud,
+				   bool expect_failure)
+{
+	struct net_pkt *pkt;
+	struct net_buf *frag;
+	int ret;
+
+	pkt = net_pkt_get_reserve_tx(0, K_FOREVER);
+
+	net_pkt_set_ll_reserve(pkt, 0);
+
+	frag = net_pkt_get_frag(pkt, K_FOREVER);
+
+	net_pkt_frag_add(pkt, frag);
+
+	net_pkt_set_iface(pkt, iface);
+
+	setup_ipv6_tcp_long(pkt, src, dst, src_port, dst_port);
+
+	ret = net_recv_data(iface, pkt);
+	if (ret < 0) {
+		DBG("Cannot recv pkt %p, ret %d\n", pkt, ret);
+		return false;
+	}
+
+	if (k_sem_take(&recv_lock, TIMEOUT)) {
+		if (expect_failure) {
+			return true;
+		} else {
+			DBG("Timeout, packet not received\n");
+			return false;
+		}
+	}
+
+	/* Check that the returned user data is the same as what was given
+	 * as a parameter.
+	 */
+	if (ud != returned_ud && !expect_failure) {
+		DBG("IPv6 wrong user data %p returned, expected %p\n",
+		    returned_ud, ud);
+		return false;
+	}
+
+	return !fail;
+}
+
 static void set_port(sa_family_t family, struct sockaddr *raddr,
 		     struct sockaddr *laddr, u16_t rport,
 		     u16_t lport)
@@ -559,10 +709,28 @@
 		return false;						\
 	}
 
+#define TEST_IPV6_LONG_OK(ud, raddr, laddr, rport, lport)		\
+	st = send_ipv6_tcp_long_msg(iface, raddr, laddr, rport, lport, ud, \
+			       false);					\
+	if (!st) {							\
+		DBG("%d: TCP long test \"%s\" fail\n", __LINE__,	\
+		    ud->test);						\
+		return false;						\
+	}
+
+#define TEST_IPV4_LONG_OK(ud, raddr, laddr, rport, lport)		\
+	st = send_ipv4_tcp_long_msg(iface, raddr, laddr, rport, lport, ud, \
+			       false);					\
+	if (!st) {							\
+		DBG("%d: TCP long_test \"%s\" fail\n", __LINE__,	\
+		    ud->test);						\
+		return false;						\
+	}
+
 #define TEST_IPV6_FAIL(ud, raddr, laddr, rport, lport)			\
 	st = send_ipv6_tcp_msg(iface, raddr, laddr, rport, lport, ud,	\
 			       true);					\
-	if (st) {							\
+	if (!st) {							\
 		DBG("%d: TCP neg test \"%s\" fail\n", __LINE__,		\
 		    ud->test);						\
 		return false;						\
@@ -571,7 +739,7 @@
 #define TEST_IPV4_FAIL(ud, raddr, laddr, rport, lport)			\
 	st = send_ipv4_tcp_msg(iface, raddr, laddr, rport, lport, ud,	\
 			       true);					\
-	if (st) {							\
+	if (!st) {							\
 		DBG("%d: TCP neg test \"%s\" fail\n", __LINE__,		\
 		    ud->test);						\
 		return false;						\
@@ -580,6 +748,8 @@
 	ud = REGISTER(AF_INET6, &any_addr6, &any_addr6, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	UNREGISTER(ud);
@@ -594,6 +764,8 @@
 	ud = REGISTER(AF_INET6, &any_addr6, NULL, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	UNREGISTER(ud);
@@ -601,6 +773,8 @@
 	ud = REGISTER(AF_INET6, NULL, &any_addr6, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 1234, 4242);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	TEST_IPV6_FAIL(ud, &in6addr_peer, &in6addr_my, 1234, 61400);
 	UNREGISTER(ud);
@@ -629,6 +803,7 @@
 	ud = REGISTER(AF_UNSPEC, NULL, NULL, 0, 0);
 	TEST_IPV4_OK(ud, &in4addr_peer, &in4addr_my, 12345, 42421);
 	TEST_IPV6_OK(ud, &in6addr_peer, &in6addr_my, 12345, 42421);
+	TEST_IPV6_LONG_OK(ud, &in6addr_peer, &in6addr_my, 12345, 42421);
 
 	/* Remote addr same as local addr, these two will never match */
 	REGISTER(AF_INET6, &my_addr6, NULL, 1234, 4242);
@@ -756,6 +931,7 @@
 	struct net_tcp *tcp = v6_ctx->tcp;
 	u8_t flags = NET_TCP_RST;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -767,7 +943,12 @@
 
 	net_hexdump_frags("TCPv6", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_RST)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_RST)) {
 		DBG("Reset flag not set\n");
 		return false;
 	}
@@ -787,6 +968,7 @@
 	struct net_tcp *tcp = v4_ctx->tcp;
 	u8_t flags = NET_TCP_RST;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -798,7 +980,12 @@
 
 	net_hexdump_frags("TCPv4", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_RST)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_RST)) {
 		DBG("Reset flag not set\n");
 		return false;
 	}
@@ -818,6 +1005,7 @@
 	struct net_tcp *tcp = v6_ctx->tcp;
 	u8_t flags = NET_TCP_SYN;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -829,7 +1017,12 @@
 
 	net_hexdump_frags("TCPv6", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_SYN)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN)) {
 		DBG("SYN flag not set\n");
 		return false;
 	}
@@ -849,6 +1042,7 @@
 	struct net_tcp *tcp = v4_ctx->tcp;
 	u8_t flags = NET_TCP_SYN;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -860,7 +1054,12 @@
 
 	net_hexdump_frags("TCPv4", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_SYN)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN)) {
 		DBG("Reset flag not set\n");
 		return false;
 	}
@@ -880,6 +1079,7 @@
 	struct net_tcp *tcp = v6_ctx->tcp;
 	u8_t flags = NET_TCP_SYN | NET_TCP_ACK;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -891,8 +1091,13 @@
 
 	net_hexdump_frags("TCPv6", pkt);
 
-	if (!((NET_TCP_FLAGS(pkt) & NET_TCP_SYN) &&
-	      (NET_TCP_FLAGS(pkt) & NET_TCP_ACK))) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!((NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN) &&
+	      (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_ACK))) {
 		DBG("SYN|ACK flag not set\n");
 		return false;
 	}
@@ -912,6 +1117,7 @@
 	struct net_tcp *tcp = v4_ctx->tcp;
 	u8_t flags = NET_TCP_SYN | NET_TCP_ACK;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -923,8 +1129,13 @@
 
 	net_hexdump_frags("TCPv4", pkt);
 
-	if (!((NET_TCP_FLAGS(pkt) & NET_TCP_SYN) &&
-	      (NET_TCP_FLAGS(pkt) & NET_TCP_ACK))) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!((NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN) &&
+	      (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_ACK))) {
 		DBG("SYN|ACK flag not set\n");
 		return false;
 	}
@@ -944,6 +1155,7 @@
 	struct net_tcp *tcp = v6_ctx->tcp;
 	u8_t flags = NET_TCP_FIN;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -955,7 +1167,12 @@
 
 	net_hexdump_frags("TCPv6", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_FIN)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_FIN)) {
 		DBG("FIN flag not set\n");
 		return false;
 	}
@@ -975,6 +1192,7 @@
 	struct net_tcp *tcp = v4_ctx->tcp;
 	u8_t flags = NET_TCP_FIN;
 	struct net_pkt *pkt = NULL;
+	struct net_tcp_hdr hdr, *tcp_hdr;
 	int ret;
 
 	ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL,
@@ -986,7 +1204,12 @@
 
 	net_hexdump_frags("TCPv4", pkt);
 
-	if (!(NET_TCP_FLAGS(pkt) & NET_TCP_FIN)) {
+	tcp_hdr = net_tcp_get_hdr(pkt, &hdr);
+	if (!tcp_hdr) {
+		return false;
+	}
+
+	if (!(NET_TCP_FLAGS(tcp_hdr) & NET_TCP_FIN)) {
 		DBG("FIN flag not set\n");
 		return false;
 	}