diff --git a/net/devif/devif.h b/net/devif/devif.h index 37179bef39..f6972cba2e 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -596,6 +596,22 @@ int devif_loopback(FAR struct net_driver_s *dev); int netdev_input(FAR struct net_driver_s *dev, devif_poll_callback_t callback, bool reply); +/**************************************************************************** + * Name: devif_get_mtu + * + * Description: + * Get mtu + * + * Parameters: + * dev Ethernet driver device structure + * + * Return: + * return (Maximum packet size - Link layer header size), + * if PMTUD enable return pmtu + ****************************************************************************/ + +uint16_t devif_get_mtu(FAR struct net_driver_s *dev); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index fd77dc3d96..24f0b87be1 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -48,6 +48,7 @@ #include "ipforward/ipforward.h" #include "sixlowpan/sixlowpan.h" #include "ipfrag/ipfrag.h" +#include "inet/inet.h" /**************************************************************************** * Private Types @@ -1152,4 +1153,70 @@ int devif_poll_out(FAR struct net_driver_s *dev, return 0; } +/**************************************************************************** + * Name: devif_get_mtu + * + * Description: + * Get mtu + * + * Parameters: + * dev Ethernet driver device structure + * + * Return: + * return (Maximum packet size - Link layer header size), + * if PMTUD enable return pmtu + ****************************************************************************/ + +uint16_t devif_get_mtu(FAR struct net_driver_s *dev) +{ + if (dev->d_iob == NULL || dev->d_len == 0) + { + return dev->d_pktsize - dev->d_llhdrlen; + } + +#if (defined(CONFIG_NET_IPv6) && \ + defined(CONFIG_NET_ICMPv6) && \ + !defined(CONFIG_NET_ICMPv6_NO_STACK) && \ + CONFIG_NET_ICMPv6_PMTU_ENTRIES > 0) + if (IFF_IS_IPv6(dev->d_flags)) + { + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + + if (!net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_unspecaddr)) + { + FAR struct icmpv6_pmtu_entry *entry = + icmpv6_find_pmtu_entry(ipv6->destipaddr); + + if (entry != NULL) + { + return entry->pmtu; + } + } + } +# endif + +#if (defined(CONFIG_NET_IPv4) && \ + defined(CONFIG_NET_ICMP) && \ + !defined(CONFIG_NET_ICMP_NO_STACK) && \ + CONFIG_NET_ICMP_PMTU_ENTRIES > 0) + if (IFF_IS_IPv4(dev->d_flags)) + { + FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; + + if (!net_ipv4addr_cmp(ipv4->destipaddr, INADDR_ANY)) + { + FAR struct icmp_pmtu_entry *entry = + icmpv4_find_pmtu_entry(net_ip4addr_conv32(ipv4->destipaddr)); + + if (entry != NULL) + { + return entry->pmtu; + } + } + } +# endif + + return dev->d_pktsize - dev->d_llhdrlen; +} + #endif /* CONFIG_NET */ diff --git a/net/icmp/Kconfig b/net/icmp/Kconfig index 6c7dd55789..6f5f9f8371 100644 --- a/net/icmp/Kconfig +++ b/net/icmp/Kconfig @@ -21,6 +21,19 @@ config NET_ICMP_NO_STACK if NET_ICMP && !NET_ICMP_NO_STACK +config NET_ICMP_PMTU_ENTRIES + int "Path MTU Discovery (PMTUD) ICMP pmtu entry number" + default 0 + ---help--- + The number of the ICMP pmtu entry + +config NET_ICMP_PMTU_TIMEOUT + int "Path MTU Discovery (PMTUD) ICMP pmtu entry timeout (Time Unit:minute)" + default 10 + depends on NET_ICMP_PMTU_ENTRIES != 0 + ---help--- + The timout of the ICMP pmtu entry + config NET_ICMP_SOCKET bool "IPPROTO_ICMP socket support" default n diff --git a/net/icmp/Make.defs b/net/icmp/Make.defs index 6586ca3f91..f5f96b3ff6 100644 --- a/net/icmp/Make.defs +++ b/net/icmp/Make.defs @@ -25,6 +25,10 @@ ifneq ($(CONFIG_NET_ICMP_NO_STACK),y) NET_CSRCS += icmp_input.c icmp_reply.c +ifneq ($(CONFIG_NET_ICMP_PMTU_ENTRIES), 0) +NET_CSRCS += icmp_pmtu.c +endif + ifeq ($(CONFIG_NET_ICMP_SOCKET),y) SOCK_CSRCS += icmp_sockif.c icmp_poll.c icmp_conn.c icmp_sendmsg.c SOCK_CSRCS += icmp_recvmsg.c icmp_netpoll.c icmp_ioctl.c diff --git a/net/icmp/icmp.h b/net/icmp/icmp.h index e72b4ae88b..21364094ba 100644 --- a/net/icmp/icmp.h +++ b/net/icmp/icmp.h @@ -104,6 +104,16 @@ struct icmp_conn_s }; #endif +#ifdef CONFIG_NET_IPv4 +struct icmp_pmtu_entry +{ + in_addr_t daddr; + uint16_t pmtu; + clock_t time; +}; + +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -396,6 +406,38 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int code); int icmp_ioctl(FAR struct socket *psock, int cmd, unsigned long arg); #endif +/**************************************************************************** + * Name: icmpv4_find_pmtu_entry + * + * Description: + * Search for a ipv4 destination cache entry + * + * Parameters: + * destipaddr the IPv4 address of the destination + * + * Return: + * not null is success; null is failure + ****************************************************************************/ + +FAR struct icmp_pmtu_entry *icmpv4_find_pmtu_entry(in_addr_t destipaddr); + +/**************************************************************************** + * Name: icmpv4_add_pmtu_entry + * + * Description: + * Create a new ipv4 destination cache entry. If no unused entry is found, + * will recycle oldest entry + * + * Parameters: + * destipaddr the IPv4 address of the destination + * mtu MTU + * + * Return: + * void + ****************************************************************************/ + +void icmpv4_add_pmtu_entry(in_addr_t destipaddr, int mtu); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index 9e0be69b90..fa123fc1a8 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -298,6 +299,39 @@ void icmp_input(FAR struct net_driver_s *dev) } #endif +#if CONFIG_NET_ICMP_PMTU_ENTRIES > 0 + else if (icmp->type == ICMP_DEST_UNREACHABLE) + { + if (icmp->icode == ICMP_FRAG_NEEDED) + { + FAR struct icmp_pmtu_entry *entry; + FAR struct ipv4_hdr_s *inner; + int mtu; + + mtu = ntohs(icmp->data[0]) << 16 | ntohs(icmp->data[1]); + if (mtu <= 0) + { + goto typeerr; + } + + inner = (FAR struct ipv4_hdr_s *)(icmp + 1); + entry = icmpv4_find_pmtu_entry( + net_ip4addr_conv32(inner->destipaddr)); + if (entry == NULL) + { + icmpv4_add_pmtu_entry( + net_ip4addr_conv32(inner->destipaddr), mtu); + } + else + { + entry->pmtu = mtu; + } + + goto icmp_send_nothing; + } + } +#endif + /* Otherwise the ICMP input was not processed */ else @@ -319,6 +353,11 @@ drop: #ifdef CONFIG_NET_STATISTICS g_netstats.icmp.drop++; #endif + +#if CONFIG_NET_ICMP_PMTU_ENTRIES > 0 +icmp_send_nothing: +#endif + dev->d_len = 0; } diff --git a/net/icmp/icmp_pmtu.c b/net/icmp/icmp_pmtu.c new file mode 100644 index 0000000000..ed6cd3f6d4 --- /dev/null +++ b/net/icmp/icmp_pmtu.c @@ -0,0 +1,118 @@ +/**************************************************************************** + * net/icmp/icmp_pmtu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "icmp/icmp.h" +#include "utils/utils.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct icmp_pmtu_entry +g_icmp_pmtu_entry[CONFIG_NET_ICMP_PMTU_ENTRIES]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv4_find_pmtu_entry + * + * Description: + * Search for a ipv4 destination cache entry + * + * Parameters: + * destipaddr the IPv4 address of the destination + * + * Return: + * not null is success; null is failure + ****************************************************************************/ + +FAR struct icmp_pmtu_entry *icmpv4_find_pmtu_entry(in_addr_t destipaddr) +{ + clock_t now = clock_systime_ticks(); + int i; + + for (i = 0; i < CONFIG_NET_ICMP_PMTU_ENTRIES; i++) + { + if (g_icmp_pmtu_entry[i].pmtu == 0) + { + continue; + } + + if (net_ipv4addr_cmp(destipaddr, g_icmp_pmtu_entry[i].daddr)) + { + g_icmp_pmtu_entry[i].time = now; + return &g_icmp_pmtu_entry[i]; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: icmpv4_add_pmtu_entry + * + * Description: + * Create a new ipv4 destination cache entry. If no unused entry is found, + * will recycle oldest entry + * + * Parameters: + * destipaddr the IPv4 address of the destination + * mtu MTU + * + * Return: + * void + ****************************************************************************/ + +void icmpv4_add_pmtu_entry(in_addr_t destipaddr, int mtu) +{ + clock_t now = clock_systime_ticks(); + int j = 0; + int i; + + for (i = 0; i < CONFIG_NET_ICMP_PMTU_ENTRIES; i++) + { + if ((g_icmp_pmtu_entry[i].pmtu == 0) || + (sclock_t)(now - g_icmp_pmtu_entry[i].time) >= + SEC2TICK(CONFIG_NET_ICMP_PMTU_TIMEOUT * 60)) + { + j = i; + break; + } + + if ((sclock_t)(g_icmp_pmtu_entry[i].time - + g_icmp_pmtu_entry[j].time) < 0) + { + j = i; + } + } + + net_ipv4addr_copy(g_icmp_pmtu_entry[j].daddr, destipaddr); + g_icmp_pmtu_entry[j].pmtu = mtu; + g_icmp_pmtu_entry[j].time = now; +} diff --git a/net/icmpv6/Kconfig b/net/icmpv6/Kconfig index 37d78a2494..3b90476f33 100644 --- a/net/icmpv6/Kconfig +++ b/net/icmpv6/Kconfig @@ -22,6 +22,19 @@ config NET_ICMPv6_NO_STACK if NET_ICMPv6 && !NET_ICMPv6_NO_STACK +config NET_ICMPv6_PMTU_ENTRIES + int "PMTUD ICMPv6 pmtu entry" + default 0 + ---help--- + The number of the ICMPv6 pmtu entry + +config NET_ICMPv6_PMTU_TIMEOUT + int "PMTUD ICMPv6 pmtu entry timeout (Time Unit:minute)" + default 10 + depends on NET_ICMPv6_PMTU_ENTRIES != 0 + ---help--- + The timout of the ICMPv6 pmtu entry + config NET_ICMPv6_SOCKET bool "IPPROTO_ICMP6 socket support" default n diff --git a/net/icmpv6/Make.defs b/net/icmpv6/Make.defs index 6bf01acf5a..4f576ef673 100644 --- a/net/icmpv6/Make.defs +++ b/net/icmpv6/Make.defs @@ -49,6 +49,10 @@ ifeq ($(CONFIG_NET_ICMPv6_ROUTER),y) NET_CSRCS += icmpv6_radvertise.c endif +ifneq ($(CONFIG_NET_ICMPv6_PMTU_ENTRIES), 0) +NET_CSRCS += icmpv6_pmtu.c +endif + # Include ICMPv6 build support DEPPATH += --dep-path icmpv6 diff --git a/net/icmpv6/icmpv6.h b/net/icmpv6/icmpv6.h index d930ca00d0..8d0bf64ce0 100644 --- a/net/icmpv6/icmpv6.h +++ b/net/icmpv6/icmpv6.h @@ -136,6 +136,13 @@ struct icmpv6_rnotify_s }; #endif +struct icmpv6_pmtu_entry +{ + net_ipv6addr_t daddr; + uint16_t pmtu; + clock_t time; +}; + /**************************************************************************** * Public Data ****************************************************************************/ @@ -783,6 +790,39 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int icmpv6_ioctl(FAR struct socket *psock, int cmd, unsigned long arg); #endif +/**************************************************************************** + * Name: icmpv6_find_pmtu_entry + * + * Description: + * Search for a ipv6 pmtu entry + * + * Parameters: + * destipaddr the IPv6 address of the destination + * + * Return: + * void + ****************************************************************************/ + +FAR struct icmpv6_pmtu_entry * +icmpv6_find_pmtu_entry(net_ipv6addr_t destipaddr); + +/**************************************************************************** + * Name: icmpv6_add_pmtu_entry + * + * Description: + * Create a new ipv6 destination cache entry. If no unused entry is found, + * will recycle oldest entry + * + * Parameters: + * destipaddr the IPv6 address of the destination + * mtu MTU + * + * Return: + * void + ****************************************************************************/ + +void icmpv6_add_pmtu_entry(net_ipv6addr_t destipaddr, int mtu); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index ec53103986..f072e70d3d 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -551,6 +551,34 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen) } break; +#if (CONFIG_NET_ICMPv6_PMTU_ENTRIES > 0) + case ICMPv6_PACKET_TOO_BIG: + { + FAR struct icmpv6_pmtu_entry *entry; + FAR struct ipv6_hdr_s *inner; + int mtu; + + mtu = (ntohs(icmpv6->data[0]) << 16) | (ntohs(icmpv6->data[1])); + if (mtu <= 0) + { + goto icmpv6_type_error; + } + + inner = (FAR struct ipv6_hdr_s *)(icmpv6 + 1); + entry = icmpv6_find_pmtu_entry(inner->destipaddr); + if (entry == NULL) + { + icmpv6_add_pmtu_entry(inner->destipaddr, mtu); + } + else + { + entry->pmtu = mtu; + } + + goto icmpv6_send_nothing; + } +#endif + #ifdef CONFIG_NET_MLD /* Dispatch received Multicast Listener Discovery (MLD) packets. */ diff --git a/net/icmpv6/icmpv6_pmtu.c b/net/icmpv6/icmpv6_pmtu.c new file mode 100644 index 0000000000..593dd846d5 --- /dev/null +++ b/net/icmpv6/icmpv6_pmtu.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * net/icmpv6/icmpv6_pmtu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "icmpv6/icmpv6.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct icmpv6_pmtu_entry +g_icmpv6_pmtu_entry[CONFIG_NET_ICMPv6_PMTU_ENTRIES]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmpv6_find_pmtu_entry + * + * Description: + * Search for a ipv6 pmtu entry + * + * Parameters: + * destipaddr the IPv6 address of the destination + * + * Return: + * not null is success; null is failure + ****************************************************************************/ + +FAR struct icmpv6_pmtu_entry * +icmpv6_find_pmtu_entry(net_ipv6addr_t destipaddr) +{ + clock_t now = clock_systime_ticks(); + int i; + + for (i = 0; i < CONFIG_NET_ICMPv6_PMTU_ENTRIES; i++) + { + if (g_icmpv6_pmtu_entry[i].pmtu == 0) + { + continue; + } + + if (net_ipv6addr_cmp(destipaddr, g_icmpv6_pmtu_entry[i].daddr)) + { + g_icmpv6_pmtu_entry[i].time = now; + return &g_icmpv6_pmtu_entry[i]; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: icmpv6_add_pmtu_entry + * + * Description: + * Create a new ipv6 destination cache entry. If no unused entry is found, + * will recycle oldest entry + * + * Parameters: + * destipaddr the IPv6 address of the destination + * mtu MTU + * + * Return: + * void + ****************************************************************************/ + +void icmpv6_add_pmtu_entry(net_ipv6addr_t destipaddr, int mtu) +{ + clock_t now = clock_systime_ticks(); + int j = 0; + int i; + + for (i = 0; i < CONFIG_NET_ICMPv6_PMTU_ENTRIES; i++) + { + if (g_icmpv6_pmtu_entry[i].pmtu == 0 || + (sclock_t)(now - g_icmpv6_pmtu_entry[i].time) >= + SEC2TICK(CONFIG_NET_ICMPv6_PMTU_TIMEOUT * 60)) + { + j = i; + break; + } + + if ((sclock_t)(g_icmpv6_pmtu_entry[i].time - + g_icmpv6_pmtu_entry[j].time) < 0) + { + j = i; + } + } + + net_ipv6addr_copy(g_icmpv6_pmtu_entry[j].daddr, destipaddr); + g_icmpv6_pmtu_entry[j].pmtu = mtu; + g_icmpv6_pmtu_entry[j].time = now; +} diff --git a/net/ipfrag/ipfrag.c b/net/ipfrag/ipfrag.c index f78d836d0a..3374a750a9 100644 --- a/net/ipfrag/ipfrag.c +++ b/net/ipfrag/ipfrag.c @@ -1236,7 +1236,7 @@ void ip_frag_remallfrags(void) int32_t ip_fragout(FAR struct net_driver_s *dev) { - uint16_t mtu = dev->d_pktsize - dev->d_llhdrlen; + uint16_t mtu = devif_get_mtu(dev); if (dev->d_iob == NULL || dev->d_len == 0) {