From f4ce8757113f1a876cde58739f6be22b356f50d6 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 23 Jan 2015 09:33:18 -0600 Subject: [PATCH] Networking: Fix issues with UDP packet length and checksum calculations when IPv6 is selected --- net/udp/udp_input.c | 30 +++++++++++++++++++--- net/udp/udp_send.c | 58 +++++++++++++++++++++++++++++------------- net/utils/Kconfig | 3 ++- net/utils/net_chksum.c | 40 ++++++++++++++--------------- net/utils/utils.h | 20 ++++++++++++--- 5 files changed, 105 insertions(+), 46 deletions(-) diff --git a/net/udp/udp_input.c b/net/udp/udp_input.c index 57e7d39d4e..3176e7daff 100644 --- a/net/udp/udp_input.c +++ b/net/udp/udp_input.c @@ -99,6 +99,7 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned int iplen) FAR struct udp_conn_s *conn; unsigned int udpiplen; unsigned int hdrlen; + uint16_t chksum; int ret = OK; /* Update the count of UDP packets received */ @@ -126,13 +127,34 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned int iplen) * application sets d_sndlen, it has a packet to send. */ - dev->d_len -= udpiplen; - -#ifdef CONFIG_NET_UDP_CHECKSUMS + dev->d_len -= udpiplen; dev->d_appdata = &dev->d_buf[hdrlen]; - if (udp->udpchksum != 0 && udp_chksum(dev) != 0xffff) +#ifdef CONFIG_NET_UDP_CHECKSUMS + chksum = udp->udpchksum; + if (chksum != 0) { +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv6(dev->d_flags)) +#endif + { + chksum = ~udp_ipv6_chksum(dev); + } +#endif /* CONFIG_NET_IPv6 */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + else +#endif + { + chksum = ~udp_ipv4_chksum(dev); + } +#endif /* CONFIG_NET_IPv6 */ + } + + if (chksum != 0) + { #ifdef CONFIG_NET_STATISTICS g_netstats.udp.drop++; g_netstats.udp.chkerr++; diff --git a/net/udp/udp_send.c b/net/udp/udp_send.c index ab71fbdc34..27d5ff1c9e 100644 --- a/net/udp/udp_send.c +++ b/net/udp/udp_send.c @@ -129,8 +129,6 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) ipv4->vhl = 0x45; ipv4->tos = 0; - ipv4->len[0] = (dev->d_len >> 8); - ipv4->len[1] = (dev->d_len & 0xff); ++g_ipid; ipv4->ipid[0] = g_ipid >> 8; ipv4->ipid[1] = g_ipid & 0xff; @@ -142,17 +140,22 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr); net_ipv4addr_hdrcopy(ipv4->destipaddr, &conn->u.ipv4.raddr); - /* Calculate IP checksum. */ - - ipv4->ipchksum = 0; - ipv4->ipchksum = ~ipv4_chksum(dev); - /* The total length to send is the size of the application data * plus the IPv4 and UDP headers (and, eventually, the link layer * header) */ - dev->d_len = dev->d_sndlen + IPv4UDP_HDRLEN; + dev->d_len = dev->d_sndlen + IPv4UDP_HDRLEN; + + /* The IPv4 length includes the size of the IPv4 header */ + + ipv4->len[0] = (dev->d_len >> 8); + ipv4->len[1] = (dev->d_len & 0xff); + + /* Calculate IP checksum. */ + + ipv4->ipchksum = 0; + ipv4->ipchksum = ~ipv4_chksum(dev); } #endif /* CONFIG_NET_IPv4 */ @@ -175,20 +178,26 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) ipv6->vtc = 0x60; ipv6->tcf = 0x00; ipv6->flow = 0x00; - ipv6->len[0] = (dev->d_sndlen >> 8); - ipv6->len[1] = (dev->d_sndlen & 0xff); ipv6->proto = IP_PROTO_UDP; ipv6->ttl = conn->ttl; net_ipv6addr_copy(ipv6->srcipaddr, dev->d_ipv6addr); net_ipv6addr_copy(ipv6->destipaddr, conn->u.ipv6.raddr); + /* The IPv6 length, Includes the UDP header size but not the IPv6 + * header size + */ + + dev->d_len = dev->d_sndlen + UDP_HDRLEN; + ipv6->len[0] = (dev->d_len >> 8); + ipv6->len[1] = (dev->d_len & 0xff); + /* The total length to send is the size of the application data - * plus the IPv4 and UDP headers (and, eventually, the link layer + * plus the IPv6 and UDP headers (and, eventually, the link layer * header) */ - dev->d_len = dev->d_sndlen + IPv6UDP_HDRLEN; + dev->d_len += IPv6_HDRLEN; } #endif /* CONFIG_NET_IPv6 */ @@ -197,19 +206,34 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) udp->srcport = conn->lport; udp->destport = conn->rport; udp->udplen = HTONS(dev->d_sndlen + UDP_HDRLEN); + udp->udpchksum = 0; #ifdef CONFIG_NET_UDP_CHECKSUMS /* Calculate UDP checksum. */ - udp->udpchksum = 0; - udp->udpchksum = ~(udp_chksum(dev)); +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv5 + if (conn->domain = PF_INET) +#endif + { + udp->udpchksum = ~udp_ipv4_chksum(dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + udp->udpchksum = ~udp_ipv6_chksum(dev); + } +#endif /* CONFIG_NET_IPv6 */ + if (udp->udpchksum == 0) { udp->udpchksum = 0xffff; } -#else - udp->udpchksum = 0; -#endif +#endif /* CONFIG_NET_UDP_CHECKSUMS */ nllvdbg("Outgoing UDP packet length: %d\n", dev->d_len); diff --git a/net/utils/Kconfig b/net/utils/Kconfig index 5ba6ffe3e6..c6700624b2 100644 --- a/net/utils/Kconfig +++ b/net/utils/Kconfig @@ -23,4 +23,5 @@ config NET_ARCH_CHKSUM uint16_t ipv4_chksum(FAR struct net_driver_s *dev) uint16_t tcp_ipv4_chksum(FAR struct net_driver_s *dev); uint16_t tcp_ipv6_chksum(FAR struct net_driver_s *dev); - uint16_t udp_chksum(FAR struct net_driver_s *dev); + uint16_t udp_ipv4_chksum(FAR struct net_driver_s *dev); + uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev); diff --git a/net/utils/net_chksum.c b/net/utils/net_chksum.c index d4179470c0..98732bcc62 100644 --- a/net/utils/net_chksum.c +++ b/net/utils/net_chksum.c @@ -372,34 +372,34 @@ uint16_t tcp_chksum(FAR struct net_driver_s *dev) #endif /**************************************************************************** - * Name: udp_chksum + * Name: udp_ipv4_chksum * * Description: - * Calculate the UDP checksum of the packet in d_buf and d_appdata. + * Calculate the UDP/IPv4 checksum of the packet in d_buf and d_appdata. * ****************************************************************************/ -#if defined(CONFIG_NET_UDP_CHECKSUMS) && !defined(CONFIG_NET_ARCH_CHKSUM) -uint16_t udp_chksum(FAR struct net_driver_s *dev) +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv4) +uint16_t udp_ipv4_chksum(FAR struct net_driver_s *dev) { -#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) - if (IFF_IS_IPv6(dev->d_flags)) - { - return ipv6_upperlayer_chksum(dev, IP_PROTO_UDP); - } - else - { - return ipv4_upperlayer_chksum(dev, IP_PROTO_UDP); - } - -#elif defined(CONFIG_NET_IPv4) return ipv4_upperlayer_chksum(dev, IP_PROTO_UDP); - -#else /* if defined(CONFIG_NET_IPv6) */ - return ipv6_upperlayer_chksum(dev, IP_PROTO_UDP); -#endif } -#endif /* CONFIG_NET_UDP_CHECKSUMS && !CONFIG_NET_ARCH_CHKSUM */ +#endif + +/**************************************************************************** + * Name: udp_ipv6_chksum + * + * Description: + * Calculate the UDP/IPv6 checksum of the packet in d_buf and d_appdata. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv6) +uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev) +{ + return ipv6_upperlayer_chksum(dev, IP_PROTO_UDP); +} +#endif /**************************************************************************** * Name: icmp_chksum diff --git a/net/utils/utils.h b/net/utils/utils.h index 6eabf097ee..20aeab7d71 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -176,15 +176,27 @@ uint16_t tcp_chksum(FAR struct net_driver_s *dev); /**************************************************************************** - * Name: udp_chksum + * Name: udp_ipv4_chksum * * Description: - * Calculate the UDP checksum of the packet in d_buf and d_appdata. + * Calculate the UDP/IPv4 checksum of the packet in d_buf and d_appdata. * ****************************************************************************/ -#ifdef CONFIG_NET_UDP_CHECKSUMS -uint16_t udp_chksum(FAR struct net_driver_s *dev); +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv4) +uint16_t udp_ipv4_chksum(FAR struct net_driver_s *dev); +#endif + +/**************************************************************************** + * Name: udp_ipv6_chksum + * + * Description: + * Calculate the UDP/IPv6 checksum of the packet in d_buf and d_appdata. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv6) +uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev); #endif /****************************************************************************