6LoWPAN: TCP logic was not obeying MTU packet size limitations. Other TCP-specific issues also fixed. There remains a major outstanding issue with ACK handling.

This commit is contained in:
Gregory Nutt 2017-06-24 09:48:41 -06:00
parent fa1d95dee2
commit 2fb938202c
4 changed files with 204 additions and 113 deletions

View File

@ -694,8 +694,8 @@ CONFIG_MM_REGIONS=1
# Common I/O Buffer Support
#
CONFIG_MM_IOB=y
CONFIG_IOB_NBUFFERS=36
CONFIG_IOB_BUFSIZE=196
CONFIG_IOB_NBUFFERS=48
CONFIG_IOB_BUFSIZE=128
CONFIG_IOB_NCHAINS=8
CONFIG_IOB_THROTTLE=8
@ -910,15 +910,15 @@ CONFIG_EXAMPLES_NETTEST_IPv6=y
#
# Server IPv6 address
#
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_1=0xfc00
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_1=0xfe80
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_2=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_3=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_4=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_5=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_6=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_7=0x0000
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_8=0x0001
CONFIG_EXAMPLES_NETTEST_SERVER_PORTNO=5471
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_6=0x00ff
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_7=0xfe00
CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_8=0xcda9
CONFIG_EXAMPLES_NETTEST_SERVER_PORTNO=61616
# CONFIG_EXAMPLES_NRF24L01TERM is not set
CONFIG_EXAMPLES_NSH=y
# CONFIG_EXAMPLES_NULL is not set

View File

@ -170,7 +170,7 @@
# define SIXLOWPAN_IPHC_TC_00 0x00 /* ECN+DSCP+4-bit Pad+Flow Label (4 bytes) */
# define SIXLOWPAN_IPHC_TC_01 0x08 /* ECN+2-bit Pad+ Flow Label (3 bytes), DSCP is elided. */
# define SIXLOWPAN_IPHC_TC_10 0x10 /* ECN+DSCP (1 byte), Flow Label is elided */
# define SIXLOWPAN_IPHC_TC_11 0x11 /* Traffic Class and Flow Label are elided */
# define SIXLOWPAN_IPHC_TC_11 0x18 /* Traffic Class and Flow Label are elided */
#define SIXLOWPAN_IPHC_NH 0x04 /* Bit 5: Next Header Compressed */
#define SIXLOWPAN_IPHC_HLIM_MASK 0x03 /* Bits 6-7: Hop Limit */
# define SIXLOWPAN_IPHC_HLIM_INLINE 0x00 /* Carried in-line */

View File

@ -289,7 +289,13 @@ static void sixlowpan_uncompress_ipv6hdr(FAR uint8_t *fptr, FAR uint8_t *bptr)
* iob - The IOB containing the frame.
*
* Returned Value:
* Ok is returned on success; Othewise a negated errno value is returned.
* On success, a value greater than equal to zero is returned, either:
*
* INPUT_PARTIAL Frame processed successful, packet incomplete
* INPUT_COMPLETE Frame processed successful, packet complete
*
* Othewise a negated errno value is returned to indicate the nature of the
* failure.
*
* Assumptions:
* Network is locked
@ -323,11 +329,8 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
fptr = iob->io_data; /* Frame data is in I/O buffer */
hdrsize = iob->io_offset; /* Offset past the MAC header */
if (hdrsize < 0)
{
nwarn("Invalid IEEE802.15.2 header: %d\n", hdrsize);
return hdrsize;
}
DEBUGASSERT((unsigned)hdrsize < iob->io_len);
/* Initialize global data. Locking the network guarantees that we have
* exclusive use of the global values for intermediate calculations.
@ -462,7 +465,7 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
nwarn("WARNING: Dropping 6LoWPAN packet that is not a fragment of "
"the packet currently being reassembled\n");
return INPUT_PARTIAL;
return -EPERM;
}
else
{
@ -488,7 +491,7 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
if (!isfirstfrag)
{
nwarn("WARNING: FRAGN 6LoWPAN fragment while not reassembling\n");
return OK;
return -EPERM;
}
/* Drop the packet if it cannot fit into the d_buf */
@ -496,7 +499,7 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
if (fragsize > CONFIG_NET_6LOWPAN_MTU)
{
nwarn("WARNING: Reassembled packet size exeeds CONFIG_NET_6LOWPAN_MTU\n");
return OK;
return -ENOSPC;
}
ieee->i_pktlen = fragsize;
@ -556,7 +559,7 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
/* Unknown or unsupported header */
nwarn("WARNING: Unknown dispatch: %u\n", hc1[SIXLOWPAN_HC1_DISPATCH]);
return OK;
return -ENOSYS;
}
#ifdef CONFIG_NET_6LOWPAN_FRAG
@ -594,7 +597,7 @@ static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee,
{
nwarn("WARNING: Packet dropped due to payload (%u) > packet buffer (%u)\n",
paysize, CONFIG_NET_6LOWPAN_MTU);
return OK;
return -ENOSPC;
}
/* Sanity-check size of incoming packet to avoid buffer overflow */
@ -795,7 +798,7 @@ int sixlowpan_input(FAR struct ieee802154_driver_s *ieee,
* reassembled?
*/
if (ret == INPUT_COMPLETE)
if (ret >= 0 && ret == INPUT_COMPLETE)
{
/* Inject the uncompressed, reassembled packet into the network */

View File

@ -131,6 +131,149 @@ static uint16_t sixlowpan_tcp_chksum(FAR struct ipv6tcp_hdr_s *ipv6tcp,
return (sum == 0) ? 0xffff : htons(sum);
}
/****************************************************************************
* Name: sixlowpan_send_packet
*
* Description:
* sixlowpan_send_packet() will send one TCP packet, respecting the
* configured 6LoWPAN MTU.
*
* Parameters:
* conn - An instance of the TCP connection structure.
* dev - The network device that will route the packet
* buf - Data to send
* bulen - Length of data to send
* timeout - Send timeout value
*
* Returned Value:
* On success, returns the number of characters sent. On error, a
* negated errnot value. Returned error numbers must be consistent with
* definition of errors reported by send() or sendto().
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
static ssize_t sixlowpan_send_packet(FAR struct tcp_conn_s *conn,
FAR struct net_driver_s *dev,
FAR const void *buf, size_t buflen,
uint16_t timeout)
{
struct sixlowpan_tagaddr_s destmac;
struct ipv6tcp_hdr_s ipv6tcp;
ssize_t pktlen;
uint16_t iplen;
int ret;
/* How much of the data can send in this packet? */
pktlen = buflen;
if ((buflen + IPv6_HDRLEN + TCP_HDRLEN) > CONFIG_NET_6LOWPAN_MTU)
{
pktlen = (CONFIG_NET_6LOWPAN_MTU - IPv6_HDRLEN - TCP_HDRLEN);
}
else
{
pktlen = buflen;
}
/* Initialize the IPv6/TCP headers */
ipv6tcp.ipv6.vtc = 0x60;
ipv6tcp.ipv6.tcf = 0x00;
ipv6tcp.ipv6.flow = 0x00;
ipv6tcp.ipv6.proto = IP_PROTO_TCP;
ipv6tcp.ipv6.ttl = IP_TTL;
/* The IPv6 header length field does not include the size of IPv6 IP
* header.
*/
iplen = pktlen + TCP_HDRLEN;
ipv6tcp.ipv6.len[0] = (iplen >> 8);
ipv6tcp.ipv6.len[1] = (iplen & 0xff);
/* Copy the source and destination addresses */
#ifdef CONFIG_NETDEV_MULTINIC
net_ipv6addr_hdrcopy(ipv6tcp.ipv6.srcipaddr, conn->u.ipv6.laddr);
#endif
net_ipv6addr_hdrcopy(ipv6tcp.ipv6.destipaddr, conn->u.ipv6.raddr);
ninfo("IPv6 length: %d\n",
((int)ipv6tcp.ipv6.len[0] << 8) + ipv6tcp.ipv6.len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv6.sent++;
#endif
/* Initialize the TCP header */
ipv6tcp.tcp.srcport = conn->lport; /* Local port */
ipv6tcp.tcp.destport = conn->rport; /* Connected remote port */
memcpy(ipv6tcp.tcp.ackno, conn->rcvseq, 4); /* ACK number */
memcpy(ipv6tcp.tcp.seqno, conn->sndseq, 4); /* Sequence number */
ipv6tcp.tcp.tcpoffset = (TCP_HDRLEN / 4) << 4; /* No optdata */
ipv6tcp.tcp.flags = 0; /* No urgent data */
ipv6tcp.tcp.urgp[0] = 0; /* No urgent data */
ipv6tcp.tcp.urgp[1] = 0;
/* Set the TCP window */
if (conn->tcpstateflags & TCP_STOPPED)
{
/* If the connection has issued TCP_STOPPED, we advertise a zero
* window so that the remote host will stop sending data.
*/
ipv6tcp.tcp.wnd[0] = 0;
ipv6tcp.tcp.wnd[1] = 0;
}
else
{
ipv6tcp.tcp.wnd[0] = ((NET_DEV_RCVWNDO(dev)) >> 8);
ipv6tcp.tcp.wnd[1] = ((NET_DEV_RCVWNDO(dev)) & 0xff);
}
/* Calculate TCP checksum. */
ipv6tcp.tcp.tcpchksum = 0;
ipv6tcp.tcp.tcpchksum = ~sixlowpan_tcp_chksum(&ipv6tcp, buf, pktlen);
ninfo("Outgoing TCP packet length: %d bytes\n", iplen + IPv6_HDRLEN);
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.sent++;
#endif
/* Get the IEEE 802.15.4 MAC address of the destination. This assumes
* an encoding of the MAC address in the IPv6 address.
*/
sixlowpan_addrfromip(conn->u.ipv6.raddr, &destmac);
/* If routable, then call sixlowpan_send() to format and send the 6LoWPAN
* packet.
*/
ret = sixlowpan_send(dev, &conn->list,
(FAR const struct ipv6_hdr_s *)&ipv6tcp,
buf, pktlen, &destmac, timeout);
if (ret < 0)
{
nerr("ERROR: sixlowpan_send() failed: %d\n", ret);
}
/* Return the amount of data that was sent from the user buffer in this
* packet.
*/
return pktlen;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -163,11 +306,11 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf,
{
FAR struct tcp_conn_s *conn;
FAR struct net_driver_s *dev;
struct ipv6tcp_hdr_s ipv6tcp;
struct sixlowpan_tagaddr_s destmac;
size_t remaining;
uint16_t timeout;
uint16_t iplen;
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
int ret;
#endif
ninfo("buflen %lu\n", (unsigned long)buflen);
sixlowpan_dumpbuffer("Outgoing TCP payload", buf, buflen);
@ -177,7 +320,7 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf,
/* Make sure that this is a valid socket */
if (psock != NULL || psock->s_crefs <= 0)
if (psock == NULL || psock->s_crefs <= 0)
{
nerr("ERROR: Invalid socket\n");
return (ssize_t)-EBADF;
@ -243,110 +386,55 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf,
}
#endif
/* Initialize the IPv6/TCP headers */
/* Initialize the IPv6/UDP headers */
ipv6tcp.ipv6.vtc = 0x60;
ipv6tcp.ipv6.tcf = 0x00;
ipv6tcp.ipv6.flow = 0x00;
ipv6tcp.ipv6.proto = IP_PROTO_TCP;
ipv6tcp.ipv6.ttl = IP_TTL;
/* The IPv6 header length field does not include the size of IPv6 IP
* header.
*/
iplen = buflen + TCP_HDRLEN;
ipv6tcp.ipv6.len[0] = (iplen >> 8);
ipv6tcp.ipv6.len[1] = (iplen & 0xff);
/* Copy the source and destination addresses */
#ifdef CONFIG_NETDEV_MULTINIC
net_ipv6addr_hdrcopy(ipv6tcp.ipv6.srcipaddr, conn->u.ipv6.laddr);
#endif
net_ipv6addr_hdrcopy(ipv6tcp.ipv6.destipaddr, conn->u.ipv6.raddr);
ninfo("IPv6 length: %d\n",
((int)ipv6tcp.ipv6.len[0] << 8) + ipv6tcp.ipv6.len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv6.sent++;
#endif
/* Initialize the TCP header */
ipv6tcp.tcp.srcport = conn->lport; /* Local port */
ipv6tcp.tcp.destport = conn->rport; /* Connected remote port */
memcpy(ipv6tcp.tcp.ackno, conn->rcvseq, 4); /* ACK number */
memcpy(ipv6tcp.tcp.seqno, conn->sndseq, 4); /* Sequence number */
ipv6tcp.tcp.tcpoffset = (TCP_HDRLEN / 4) << 4; /* No optdata */
ipv6tcp.tcp.urgp[0] = 0; /* No urgent data */
ipv6tcp.tcp.urgp[1] = 0;
/* Set the TCP window */
if (conn->tcpstateflags & TCP_STOPPED)
{
/* If the connection has issued TCP_STOPPED, we advertise a zero
* window so that the remote host will stop sending data.
*/
ipv6tcp.tcp.wnd[0] = 0;
ipv6tcp.tcp.wnd[1] = 0;
}
else
{
ipv6tcp.tcp.wnd[0] = ((NET_DEV_RCVWNDO(dev)) >> 8);
ipv6tcp.tcp.wnd[1] = ((NET_DEV_RCVWNDO(dev)) & 0xff);
}
/* Calculate TCP checksum. */
ipv6tcp.tcp.tcpchksum = 0;
ipv6tcp.tcp.tcpchksum = ~sixlowpan_tcp_chksum(&ipv6tcp, buf, buflen);
ninfo("Outgoing TCP packet length: %d bytes\n", iplen + IPv6_HDRLEN);
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.sent++;
#endif
/* Set the socket state to sending */
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND);
/* Get the IEEE 802.15.4 MAC address of the destination. This assumes
* an encoding of the MAC address in the IPv6 address.
*/
sixlowpan_addrfromip(conn->u.ipv6.raddr, &destmac);
/* If routable, then call sixlowpan_send() to format and send the 6LoWPAN
* packet.
/* Send the TCP packets, breaking down the potential large user buffer
* into smaller packets that can be reassembled in the allocated MTU
* packet buffer.
*/
#ifdef CONFIG_NET_SOCKOPTS
timeout = psock->s_sndtimeo;
timeout = psock->s_sndtimeo;
#else
timeout = 0;
timeout = 0;
#endif
remaining = buflen;
ret = sixlowpan_send(dev, &conn->list,
(FAR const struct ipv6_hdr_s *)&ipv6tcp,
buf, buflen, &destmac, timeout);
if (ret < 0)
for (; ; )
{
nerr("ERROR: sixlowpan_send() failed: %d\n", ret);
/* Send the next packet */
ssize_t pktlen = sixlowpan_send_packet(conn, dev, buf, remaining,
timeout);
if (pktlen < 0)
{
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE);
return (ssize_t)pktlen;
}
/* Check if all data has been sent */
if (pktlen >= remaining)
{
/* Yes.. we are done */
break;
}
/* No.. Update the buffer pointer and bytes remaining and send the next
* packet.
*/
remaining -= pktlen;
buf += pktlen;
}
/* Set the socket state to idle */
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE);
return ret;
return (ssize_t)buflen;
}
/****************************************************************************
@ -356,7 +444,7 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf,
* TCP output comes through three different mechansims. Either from:
*
* 1. TCP socket output. For the case of TCP output to an
* IEEE802.15.4, the TCP output is caught in the socket
* IEEE802.15.4 device, the TCP output is caught in the socket
* send()/sendto() logic and and redirected to psock_6lowpan_tcp_send().
* 2. TCP output from the TCP state machine. That will occur
* during TCP packet processing by the TCP state meachine.