From 2fb938202c88c016cf70237e0375e329d0265c1c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 24 Jun 2017 09:48:41 -0600 Subject: [PATCH] 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. --- configs/sim/sixlowpan/defconfig | 14 +- include/nuttx/net/sixlowpan.h | 2 +- net/sixlowpan/sixlowpan_input.c | 27 +-- net/sixlowpan/sixlowpan_tcpsend.c | 274 ++++++++++++++++++++---------- 4 files changed, 204 insertions(+), 113 deletions(-) diff --git a/configs/sim/sixlowpan/defconfig b/configs/sim/sixlowpan/defconfig index 87a2b5a794..8cfb733c96 100644 --- a/configs/sim/sixlowpan/defconfig +++ b/configs/sim/sixlowpan/defconfig @@ -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 diff --git a/include/nuttx/net/sixlowpan.h b/include/nuttx/net/sixlowpan.h index 308112a1ea..a1f6b23ed1 100644 --- a/include/nuttx/net/sixlowpan.h +++ b/include/nuttx/net/sixlowpan.h @@ -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 */ diff --git a/net/sixlowpan/sixlowpan_input.c b/net/sixlowpan/sixlowpan_input.c index 644cb32fdc..9eaf10e49a 100644 --- a/net/sixlowpan/sixlowpan_input.c +++ b/net/sixlowpan/sixlowpan_input.c @@ -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 */ diff --git a/net/sixlowpan/sixlowpan_tcpsend.c b/net/sixlowpan/sixlowpan_tcpsend.c index 7ce5064b89..183f074c94 100644 --- a/net/sixlowpan/sixlowpan_tcpsend.c +++ b/net/sixlowpan/sixlowpan_tcpsend.c @@ -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.