diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index 77ea651499b..eb880ad2e90 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -84,6 +84,7 @@ #include #include +#include #include #include @@ -166,6 +167,34 @@ static int ipv4_check_opt(FAR struct ipv4_hdr_s *ipv4) } #endif +/**************************************************************************** + * Name: ipv4_fragin_or_drop + * + * Description: + * Reassemble an incoming IPv4 fragment for local processing or drop it if + * fragment reassembly is not available. + * + ****************************************************************************/ + +static int ipv4_fragin_or_drop(FAR struct net_driver_s *dev) +{ +#ifdef CONFIG_NET_IPFRAG + if (ipv4_fragin(dev) == OK) + { + return OK; + } +#endif + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv4.drop++; + g_netstats.ipv4.fragerr++; +#endif + nwarn("WARNING: IP fragment dropped\n"); + + dev->d_len = 0; + return OK; +} + /**************************************************************************** * Name: ipv4_in * @@ -199,6 +228,7 @@ static int ipv4_in(FAR struct net_driver_s *dev) FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; in_addr_t destipaddr; uint16_t totlen; + bool isfrag; int ret = OK; /* Handle ARP on input then give the IPv4 packet to the network layer */ @@ -273,22 +303,7 @@ static int ipv4_in(FAR struct net_driver_s *dev) /* Check the fragment flag. */ - if ((ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0) - { -#ifdef CONFIG_NET_IPFRAG - if (ipv4_fragin(dev) == OK) - { - return OK; - } - -#endif -#ifdef CONFIG_NET_STATISTICS - g_netstats.ipv4.drop++; - g_netstats.ipv4.fragerr++; -#endif - nwarn("WARNING: IP fragment dropped\n"); - goto drop; - } + isfrag = (ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0; #ifdef CONFIG_DEBUG_FEATURES if (ipv4_check_opt(ipv4) != OK) @@ -301,6 +316,13 @@ static int ipv4_in(FAR struct net_driver_s *dev) } #endif +#if defined(CONFIG_NET_NAT44) || defined(CONFIG_NET_IPFILTER) + if (isfrag) + { + return ipv4_fragin_or_drop(dev); + } +#endif + #ifdef CONFIG_NET_NAT44 /* Try NAT inbound, rule matching will be performed in NAT module. */ @@ -334,6 +356,11 @@ static int ipv4_in(FAR struct net_driver_s *dev) ipv4_forward_broadcast(dev, ipv4); #endif + if (isfrag) + { + return ipv4_fragin_or_drop(dev); + } + #if defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK) if (ipv4->proto == IP_PROTO_UDP) { @@ -406,6 +433,16 @@ static int ipv4_in(FAR struct net_driver_s *dev) } } + /* Forwarding has already had a chance to consume non-local unicast + * fragments. Any remaining fragments are for local input and must be + * reassembled before the transport layer sees them. + */ + + if (isfrag) + { + return ipv4_fragin_or_drop(dev); + } + #ifdef CONFIG_NET_IPV4_CHECKSUMS if (((dev->d_features & NETDEV_RX_CSUM) == 0) && (ipv4_chksum(IPv4BUF) != 0xffff)) diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index dc1c04360de..3400fdebd79 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -151,6 +151,36 @@ static bool check_destipaddr(FAR struct net_driver_s *dev, return false; } +#if defined(CONFIG_NET_IPFRAG) || defined(CONFIG_NET_NAT66) || defined(CONFIG_NET_IPFILTER) +/**************************************************************************** + * Name: ipv6_fragin_or_drop + * + * Description: + * Reassemble an incoming IPv6 fragment for NAT or local processing or drop + * it if fragment reassembly is not available. + * + ****************************************************************************/ + +static int ipv6_fragin_or_drop(FAR struct net_driver_s *dev) +{ +#ifdef CONFIG_NET_IPFRAG + if (ipv6_fragin(dev) == OK) + { + return OK; + } +#endif + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv6.drop++; + g_netstats.ipv6.fragerr++; +#endif + nwarn("WARNING: IPv6 fragment dropped\n"); + + dev->d_len = 0; + return OK; +} +#endif + /**************************************************************************** * Name: ipv6_in * @@ -195,7 +225,7 @@ static int ipv6_in(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_IPFORWARD int ret; #endif -#ifdef CONFIG_NET_IPFRAG +#if defined(CONFIG_NET_IPFRAG) || defined(CONFIG_NET_NAT66) || defined(CONFIG_NET_IPFILTER) bool isfrag = false; #endif @@ -287,7 +317,7 @@ static int ipv6_in(FAR struct net_driver_s *dev) if (nxthdr == NEXT_FRAGMENT_EH) { extlen = EXTHDR_FRAG_LEN; -#ifdef CONFIG_NET_IPFRAG +#if defined(CONFIG_NET_IPFRAG) || defined(CONFIG_NET_NAT66) || defined(CONFIG_NET_IPFILTER) isfrag = true; #endif } @@ -301,6 +331,13 @@ static int ipv6_in(FAR struct net_driver_s *dev) nxthdr = exthdr->nxthdr; } +#if defined(CONFIG_NET_NAT66) || defined(CONFIG_NET_IPFILTER) + if (isfrag) + { + return ipv6_fragin_or_drop(dev); + } +#endif + #ifdef CONFIG_NET_NAT66 /* Try NAT inbound, rule matching will be performed in NAT module. */ @@ -410,17 +447,7 @@ static int ipv6_in(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_IPFRAG if (isfrag) { - if (ipv6_fragin(dev) == OK) - { - return OK; - } - else - { -#ifdef CONFIG_NET_STATISTICS - g_netstats.ipv6.fragerr++; -#endif - goto drop; - } + return ipv6_fragin_or_drop(dev); } #endif diff --git a/net/ipforward/ipv4_forward.c b/net/ipforward/ipv4_forward.c index 5bd509042e7..24e2d256b24 100644 --- a/net/ipforward/ipv4_forward.c +++ b/net/ipforward/ipv4_forward.c @@ -77,6 +77,11 @@ static int ipv4_hdrsize(FAR struct ipv4_hdr_s *ipv4) iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + if ((ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0) + { + return iphdrlen; + } + /* Size is also determined by the following protocol header, */ switch (ipv4->proto) diff --git a/net/ipfrag/ipv4_frag.c b/net/ipfrag/ipv4_frag.c index b5628f28a78..314ec3f15f6 100644 --- a/net/ipfrag/ipv4_frag.c +++ b/net/ipfrag/ipv4_frag.c @@ -360,6 +360,8 @@ int32_t ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu) uint32_t iter; uint32_t nfrags; uint16_t offset = 0; + uint16_t fragoff; + uint16_t morefrags; uint16_t hdrlen; FAR struct iob_s *frag = NULL; FAR struct ipv4_hdr_s *ref = NULL; @@ -374,6 +376,14 @@ int32_t ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu) hdrlen = (IPv4BUF->vhl & IPv4_HLMASK) << 2; + /* Preserve the original fragment offset when splitting an + * already-fragmented packet for a smaller egress MTU. + */ + + fragoff = (IPv4BUF->ipoffset[0] << 8) + IPv4BUF->ipoffset[1]; + morefrags = fragoff & IP_FLAG_MOREFRAGS; + fragoff &= 0x1fff; + /* Reconstruct I/O Buffer according to MTU, which will reserve * the space for the L3 header */ @@ -401,13 +411,13 @@ int32_t ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu) /* Update the IPv4 header of the first fragment */ ipv4_fragout_buildipv4header(ref, ref, frag->io_pktlen, - IP_FLAG_MOREFRAGS); + fragoff | IP_FLAG_MOREFRAGS); } else { - uint16_t ipoff = (offset - iter * hdrlen) >> 3; + uint16_t ipoff = fragoff + ((offset - iter * hdrlen) >> 3); - if (iter < nfrags - 1) + if (iter < nfrags - 1 || morefrags) { ipoff |= IP_FLAG_MOREFRAGS; } diff --git a/net/nat/ipv6_nat.c b/net/nat/ipv6_nat.c index 1c0f16ec955..4a40d2cc4be 100644 --- a/net/nat/ipv6_nat.c +++ b/net/nat/ipv6_nat.c @@ -540,7 +540,7 @@ ipv6_nat_inbound_internal(FAR struct ipv6_hdr_s *ipv6, uint8_t proto; FAR void *l4hdr = net_ipv6_payload(ipv6, &proto); - switch (ipv6->proto) + switch (proto) { #ifdef CONFIG_NET_TCP case IP_PROTO_TCP: