Browse Source

net: ipv6: Add PMTU support

Catch "Packet Too Big" ICMPv6 messages and update PMTU for
a given destination IPv6 address.
Use that PMTU when sending data to the destination.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
pull/81517/head
Jukka Rissanen 8 months ago committed by Anas Nashif
parent
commit
e7e3afcd01
  1. 3
      subsys/net/ip/icmpv6.h
  2. 125
      subsys/net/ip/ipv6_nbr.c
  3. 49
      subsys/net/ip/tcp.c

3
subsys/net/ip/icmpv6.h

@ -117,6 +117,9 @@ struct net_icmpv6_mld_mcast_record { @@ -117,6 +117,9 @@ struct net_icmpv6_mld_mcast_record {
uint8_t mcast_address[NET_IPV6_ADDR_SIZE];
} __packed;
struct net_icmpv6_ptb {
uint32_t mtu;
} __packed;
#define NET_ICMPV6_ND_O_FLAG(flag) ((flag) & 0x40)
#define NET_ICMPV6_ND_M_FLAG(flag) ((flag) & 0x80)

125
subsys/net/ip/ipv6_nbr.c

@ -35,6 +35,7 @@ LOG_MODULE_REGISTER(net_ipv6_nd, CONFIG_NET_IPV6_ND_LOG_LEVEL); @@ -35,6 +35,7 @@ LOG_MODULE_REGISTER(net_ipv6_nd, CONFIG_NET_IPV6_ND_LOG_LEVEL);
#include "6lo.h"
#include "route.h"
#include "net_stats.h"
#include "pmtu.h"
/* Timeout value to be used when allocating net buffer during various
* neighbor discovery procedures.
@ -903,6 +904,26 @@ enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt) @@ -903,6 +904,26 @@ enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt)
}
try_send:
if (IS_ENABLED(CONFIG_NET_IPV6_PMTU)) {
struct net_pmtu_entry *entry;
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
};
net_ipaddr_copy(&dst.sin6_addr, (struct in6_addr *)ip_hdr->dst);
entry = net_pmtu_get_entry((struct sockaddr *)&dst);
if (entry == NULL) {
ret = net_pmtu_update_mtu((struct sockaddr *)&dst,
net_if_get_mtu(iface));
if (ret < 0) {
NET_DBG("Cannot update PMTU for %s (%d)",
net_sprint_ipv6_addr(&dst.sin6_addr),
ret);
}
}
}
net_ipv6_nbr_lock();
nbr = nbr_lookup(&net_neighbor.table, iface, nexthop);
@ -2727,6 +2748,98 @@ drop: @@ -2727,6 +2748,98 @@ drop:
}
#endif /* CONFIG_NET_IPV6_ND */
#if defined(CONFIG_NET_IPV6_PMTU)
/* Packet format described in RFC 4443 ch 3.2. Packet Too Big Message */
static int handle_ptb_input(struct net_icmp_ctx *ctx,
struct net_pkt *pkt,
struct net_icmp_ip_hdr *hdr,
struct net_icmp_hdr *icmp_hdr,
void *user_data)
{
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ptb_access, struct net_icmpv6_ptb);
struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
uint16_t length = net_pkt_get_len(pkt);
struct net_icmpv6_ptb *ptb_hdr;
struct sockaddr_in6 sockaddr_src = {
.sin6_family = AF_INET6,
};
struct net_pmtu_entry *entry;
uint32_t mtu;
int ret;
ARG_UNUSED(user_data);
ptb_hdr = (struct net_icmpv6_ptb *)net_pkt_get_data(pkt, &ptb_access);
if (!ptb_hdr) {
NET_DBG("DROP: NULL PTB header");
goto drop;
}
dbg_addr_recv("Packet Too Big", &ip_hdr->src, &ip_hdr->dst, pkt);
net_stats_update_ipv6_pmtu_recv(net_pkt_iface(pkt));
if (length < (sizeof(struct net_ipv6_hdr) +
sizeof(struct net_icmp_hdr) +
sizeof(struct net_icmpv6_ptb))) {
NET_DBG("DROP: length %d too big %zd",
length, sizeof(struct net_ipv6_hdr) +
sizeof(struct net_icmp_hdr) +
sizeof(struct net_icmpv6_ptb));
goto drop;
}
net_pkt_acknowledge_data(pkt, &ptb_access);
mtu = ntohl(ptb_hdr->mtu);
if (mtu < MIN_IPV6_MTU || mtu > MAX_IPV6_MTU) {
NET_DBG("DROP: Unsupported MTU %u, min is %u, max is %u",
mtu, MIN_IPV6_MTU, MAX_IPV6_MTU);
goto drop;
}
net_ipaddr_copy(&sockaddr_src.sin6_addr, (struct in6_addr *)&ip_hdr->src);
entry = net_pmtu_get_entry((struct sockaddr *)&sockaddr_src);
if (entry == NULL) {
NET_DBG("DROP: Cannot find PMTU entry for %s",
net_sprint_ipv6_addr(&ip_hdr->src));
goto silent_drop;
}
/* We must not accept larger PMTU value than what we already know.
* RFC 8201 chapter 4 page 8.
*/
if (entry->mtu > 0 && entry->mtu < mtu) {
NET_DBG("DROP: PMTU for %s %u larger than %u",
net_sprint_ipv6_addr(&ip_hdr->src), mtu,
entry->mtu);
goto silent_drop;
}
ret = net_pmtu_update_entry(entry, mtu);
if (ret > 0) {
NET_DBG("PMTU for %s changed from %u to %u",
net_sprint_ipv6_addr(&ip_hdr->src), ret, mtu);
}
return 0;
drop:
net_stats_update_ipv6_pmtu_drop(net_pkt_iface(pkt));
return -EIO;
silent_drop:
/* If the event is not really an error then just ignore it and
* return 0 so that icmpv6 module will not complain about it.
*/
net_stats_update_ipv6_pmtu_drop(net_pkt_iface(pkt));
return 0;
}
#endif /* CONFIG_NET_IPV6_PMTU */
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
static struct net_icmp_ctx ns_ctx;
static struct net_icmp_ctx na_ctx;
@ -2736,6 +2849,10 @@ static struct net_icmp_ctx na_ctx; @@ -2736,6 +2849,10 @@ static struct net_icmp_ctx na_ctx;
static struct net_icmp_ctx ra_ctx;
#endif /* CONFIG_NET_IPV6_ND */
#if defined(CONFIG_NET_IPV6_PMTU)
static struct net_icmp_ctx ptb_ctx;
#endif /* CONFIG_NET_IPV6_PMTU */
void net_ipv6_nbr_init(void)
{
int ret;
@ -2766,5 +2883,13 @@ void net_ipv6_nbr_init(void) @@ -2766,5 +2883,13 @@ void net_ipv6_nbr_init(void)
ipv6_nd_reachable_timeout);
#endif
#if defined(CONFIG_NET_IPV6_PMTU)
ret = net_icmp_init_ctx(&ptb_ctx, NET_ICMPV6_PACKET_TOO_BIG, 0, handle_ptb_input);
if (ret < 0) {
NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV6_PACKET_TOO_BIG),
ret);
}
#endif
ARG_UNUSED(ret);
}

49
subsys/net/ip/tcp.c

@ -25,6 +25,7 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL); @@ -25,6 +25,7 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL);
#include "net_stats.h"
#include "net_private.h"
#include "tcp_internal.h"
#include "pmtu.h"
#define ACK_TIMEOUT_MS tcp_max_timeout_ms
#define ACK_TIMEOUT K_MSEC(ACK_TIMEOUT_MS)
@ -4392,6 +4393,32 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) @@ -4392,6 +4393,32 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data)
k_mutex_unlock(&tcp_lock);
}
static uint16_t get_ipv6_destination_mtu(struct net_if *iface,
const struct in6_addr *dest)
{
#if defined(CONFIG_NET_IPV6_PMTU)
int mtu = net_pmtu_get_mtu((struct sockaddr *)&(struct sockaddr_in6){
.sin6_family = AF_INET6,
.sin6_addr = *dest });
if (mtu < 0) {
if (iface != NULL) {
return net_if_get_mtu(iface);
}
return NET_IPV6_MTU;
}
return (uint16_t)mtu;
#else
if (iface != NULL) {
return net_if_get_mtu(iface);
}
return NET_IPV6_MTU;
#endif /* CONFIG_NET_IPV6_PMTU */
}
uint16_t net_tcp_get_supported_mss(const struct tcp *conn)
{
sa_family_t family = net_context_get_family(conn->context);
@ -4416,26 +4443,16 @@ uint16_t net_tcp_get_supported_mss(const struct tcp *conn) @@ -4416,26 +4443,16 @@ uint16_t net_tcp_get_supported_mss(const struct tcp *conn)
#else
return 0;
#endif /* CONFIG_NET_IPV4 */
}
#if defined(CONFIG_NET_IPV6)
else if (family == AF_INET6) {
struct net_if *iface = net_context_get_iface(conn->context);
int mss = 0;
if (iface && net_if_get_mtu(iface) >= NET_IPV6TCPH_LEN) {
/* Detect MSS based on interface MTU minus "TCP,IP
* header size"
*/
mss = net_if_get_mtu(iface) - NET_IPV6TCPH_LEN;
}
} else if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
struct net_if *iface = net_context_get_iface(conn->context);
uint16_t dest_mtu;
if (mss == 0) {
mss = NET_IPV6_MTU - NET_IPV6TCPH_LEN;
}
dest_mtu = get_ipv6_destination_mtu(iface, &conn->dst.sin6.sin6_addr);
return mss;
/* Detect MSS based on interface MTU minus "TCP,IP header size" */
return dest_mtu - NET_IPV6TCPH_LEN;
}
#endif /* CONFIG_NET_IPV6 */
return 0;
}

Loading…
Cancel
Save