net: skip TCP/UDP pseudo-header checksum with hardware offload

When supporting hardware checksum offloading, the network protocol stack
does not perform TCP/UDP pseudo-header checksum calculation.

Skip TCP/UDP pseudo header checksum calculation in network protocol stack

Signed-off-by: daichuan <daichuan@xiaomi.com>
This commit is contained in:
daichuan
2025-08-30 01:18:20 +08:00
committed by Xiang Xiao
parent 42da4e322a
commit 99bf7c3c5f
8 changed files with 237 additions and 137 deletions
+38
View File
@@ -173,6 +173,44 @@ Ioctls for IP Addresses
to manage IPv6 addresses, by which you don't need to care about the to manage IPv6 addresses, by which you don't need to care about the
slot it stored. slot it stored.
Hardware Checksum Offload
=========================
The structure :c:struct:`net_driver_s` includes fields to support hardware
checksum offloading. This feature allows the network stack to delegate
checksum calculation to the network device hardware, improving performance.
Checksum Configuration Options
------------------------------
* :c:macro:`CONFIG_NETDEV_CHECKSUM`: Enable support for hardware checksum
offloading in the network stack. This option requires the architecture
to support it (:c:macro:`ARCH_HAVE_NETDEV_CHECKSUM_HW`).
Implementation Details
----------------------
When :c:macro:`CONFIG_NETDEV_CHECKSUM` is enabled, the driver should use the
following helper functions to retrieve checksum offload information:
* :c:func:`netdev_checksum_start`: Get the offset from the beginning of the
packet to the start of the L4 header (checksum calculation start).
* :c:func:`netdev_checksum_offset`: Get the offset from the start of the L4
header to the checksum field.
* :c:func:`netdev_upperlayer_header_checksum`: Calculate the pseudo-header
checksum.
.. code-block:: c
#ifdef CONFIG_NETDEV_CHECKSUM
int netdev_checksum_start(FAR struct net_driver_s *dev);
int netdev_checksum_offset(FAR struct net_driver_s *dev);
uint16_t netdev_upperlayer_header_checksum(FAR struct net_driver_s *dev);
#endif
Drivers that support hardware checksum offloading should use these functions
to configure the hardware accordingly before transmitting the packet.
[1]: https://man7.org/linux/man-pages/man7/netdevice.7.html [1]: https://man7.org/linux/man-pages/man7/netdevice.7.html
[2]: e.g. 'eth0:0' stands for the secondary address on eth0 [2]: e.g. 'eth0:0' stands for the secondary address on eth0
+4
View File
@@ -575,6 +575,10 @@ config ARCH_HAVE_FETCHADD
bool bool
default n default n
config ARCH_HAVE_NETDEV_CHECKSUM_HW
bool
default n
config ARCH_HAVE_RTC_SUBSECONDS config ARCH_HAVE_RTC_SUBSECONDS
bool bool
default n default n
+57 -86
View File
@@ -920,46 +920,6 @@ uint16_t net_chksum_iob(uint16_t sum, FAR struct iob_s *iob,
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
/****************************************************************************
* Name: ipv4_upperlayer_header_chksum
*
* Description:
* Perform the checksum calculation over the IPv4, protocol headers,
* IP source and destination addresses
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
*
* Returned Value:
* The calculated checksum with pseudo-header and IP source and
* destination addresses
*
****************************************************************************/
uint16_t ipv4_upperlayer_header_chksum(FAR struct net_driver_s *dev,
uint8_t proto);
/****************************************************************************
* Name: ipv4_upperlayer_payload_chksum
*
* Description:
* Perform the checksum calculation over the iob data payload
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* sum - The default checksum
*
* Returned Value:
* The calculated checksum with iob data payload and default checksum
*
****************************************************************************/
uint16_t ipv4_upperlayer_payload_chksum(FAR struct net_driver_s *dev,
uint16_t sum);
/**************************************************************************** /****************************************************************************
* Name: ipv4_upperlayer_chksum * Name: ipv4_upperlayer_chksum
* *
@@ -982,52 +942,6 @@ uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto);
#ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv6
/****************************************************************************
* Name: ipv6_upperlayer_header_chksum
*
* Description:
* Perform the checksum calculation over the IPv6, protocol headers,
* IP source and destination addresses.
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
* iplen - The size of the IPv6 header. This may be larger than
* IPv6_HDRLEN the IPv6 header if IPv6 extension headers are
* present.
*
* Returned Value:
* The calculated checksum
*
****************************************************************************/
uint16_t ipv6_upperlayer_header_chksum(FAR struct net_driver_s *dev,
uint8_t proto, unsigned int iplen);
/****************************************************************************
* Name: ipv6_upperlayer_payload_chksum
*
* Description:
* Perform the checksum calculation over the iob data payload and
* default checksum.
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
* iplen - The size of the IPv6 header. This may be larger than
* IPv6_HDRLEN the IPv6 header if IPv6 extension headers are
* present.
*
* Returned Value:
* The calculated checksum
*
****************************************************************************/
uint16_t ipv6_upperlayer_payload_chksum(FAR struct net_driver_s *dev,
unsigned int iplen, uint16_t sum);
/**************************************************************************** /****************************************************************************
* Name: ipv6_upperlayer_chksum * Name: ipv6_upperlayer_chksum
* *
@@ -1380,4 +1294,61 @@ int netdev_ipv6_foreach(FAR struct net_driver_s *dev,
void netdev_statistics_log(FAR void *arg); void netdev_statistics_log(FAR void *arg);
#endif #endif
/****************************************************************************
* Name: netdev_checksum_start
*
* Description:
* Get checksum start offset position with iob, then hardware can
* use to calculate the package payload checksum value.
*
* Input Parameters:
* dev - The driver structure
*
* Returned Value:
* The checksum start offset position, -EINVAL is mean not need calculate
* with hardware
*
****************************************************************************/
#ifdef CONFIG_NETDEV_CHECKSUM
int netdev_checksum_start(FAR struct net_driver_s *dev);
#endif
/****************************************************************************
* Name: netdev_checksum_offset
*
* Description:
* Get checksum field offset with tcp/udp header.
*
* Input Parameters:
* dev - The driver structure
*
* Returned Value:
* The checksum field offset with L4, -EINVAL is mean not need calculate
* with hardware
*
****************************************************************************/
#ifdef CONFIG_NETDEV_CHECKSUM
int netdev_checksum_offset(FAR struct net_driver_s *dev);
#endif
/****************************************************************************
* Name: netdev_upperlayer_header_checksum
*
* Description:
* get upperlayer header checksum with tcp/udp header.
*
* Input Parameters:
* dev - The driver structure
*
* Returned Value:
* The upperlayer header checksum
*
****************************************************************************/
#ifdef CONFIG_NETDEV_CHECKSUM
uint16_t netdev_upperlayer_header_checksum(FAR struct net_driver_s *dev);
#endif
#endif /* __INCLUDE_NUTTX_NET_NETDEV_H */ #endif /* __INCLUDE_NUTTX_NET_NETDEV_H */
+2 -1
View File
@@ -100,8 +100,9 @@ config NETDOWN_NOTIFIER
logic. logic.
config NETDEV_CHECKSUM config NETDEV_CHECKSUM
bool "netdev hardware checksum" bool "Enable support to netdev checksum in Hardware"
default n default n
depends on ARCH_HAVE_NETDEV_CHECKSUM_HW
---help--- ---help---
To support hardware checksum calculation for network cards, we To support hardware checksum calculation for network cards, we
need to know the starting position of the L4 layer header in need to know the starting position of the L4 layer header in
+93
View File
@@ -503,6 +503,99 @@ void netdev_notify_recvcpu(FAR struct net_driver_s *dev,
FAR const void *dst_addr, uint16_t dst_port); FAR const void *dst_addr, uint16_t dst_port);
#endif #endif
#ifdef CONFIG_NET_IPv4
/****************************************************************************
* Name: ipv4_upperlayer_header_chksum
*
* Description:
* Perform the checksum calculation over the IPv4, protocol headers,
* IP source and destination addresses
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
*
* Returned Value:
* The calculated checksum with pseudo-header and IP source and
* destination addresses
*
****************************************************************************/
uint16_t ipv4_upperlayer_header_chksum(FAR struct net_driver_s *dev,
uint8_t proto);
/****************************************************************************
* Name: ipv4_upperlayer_payload_chksum
*
* Description:
* Perform the checksum calculation over the iob data payload
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* sum - The default checksum
*
* Returned Value:
* The calculated checksum with iob data payload and default checksum
*
****************************************************************************/
uint16_t ipv4_upperlayer_payload_chksum(FAR struct net_driver_s *dev,
uint16_t sum);
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
/****************************************************************************
* Name: ipv6_upperlayer_header_chksum
*
* Description:
* Perform the checksum calculation over the IPv6, protocol headers,
* IP source and destination addresses.
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
* iplen - The size of the IPv6 header. This may be larger than
* IPv6_HDRLEN the IPv6 header if IPv6 extension headers are
* present.
*
* Returned Value:
* The calculated checksum
*
****************************************************************************/
uint16_t ipv6_upperlayer_header_chksum(FAR struct net_driver_s *dev,
uint8_t proto, unsigned int iplen);
/****************************************************************************
* Name: ipv6_upperlayer_payload_chksum
*
* Description:
* Perform the checksum calculation over the iob data payload and
* default checksum.
*
* Input Parameters:
* dev - The network driver instance. The packet data is in the d_buf
* of the device.
* proto - The protocol being supported
* iplen - The size of the IPv6 header. This may be larger than
* IPv6_HDRLEN the IPv6 header if IPv6 extension headers are
* present.
*
* Returned Value:
* The calculated checksum
*
****************************************************************************/
uint16_t ipv6_upperlayer_payload_chksum(FAR struct net_driver_s *dev,
unsigned int iplen, uint16_t sum);
#endif /* CONFIG_NET_IPv6 */
/**************************************************************************** /****************************************************************************
* Name: netdev_list_lock * Name: netdev_list_lock
* *
+35
View File
@@ -194,4 +194,39 @@ int netdev_checksum_offset(FAR struct net_driver_s *dev)
return offset; return offset;
} }
/****************************************************************************
* Name: netdev_upperlayer_header_checksum
*
* Description:
* get upperlayer header checksum with tcp/udp header.
*
* Input Parameters:
* dev - The driver structure
*
* Returned Value:
* The upperlayer header checksum
*
****************************************************************************/
uint16_t netdev_upperlayer_header_checksum(FAR struct net_driver_s *dev)
{
if (IFF_IS_IPv6(dev->d_flags))
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
return HTONS(ipv6_upperlayer_header_chksum(dev,
ipv6->proto,
IPv6_HDRLEN));
}
else
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
return HTONS(ipv4_upperlayer_header_chksum(dev,
ipv4->proto));
}
return 0;
}
#endif /* CONFIG_NETDEV_CHECKSUM */ #endif /* CONFIG_NETDEV_CHECKSUM */
-26
View File
@@ -204,13 +204,6 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
{ {
tcp->tcpchksum = ~tcp_ipv6_chksum(dev); tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
} }
else
{
uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
IP_PROTO_TCP,
IPv6_HDRLEN);
tcp->tcpchksum = HTONS(chksum);
}
#endif #endif
#ifdef CONFIG_NET_STATISTICS #ifdef CONFIG_NET_STATISTICS
@@ -238,12 +231,6 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
{ {
tcp->tcpchksum = ~tcp_ipv4_chksum(dev); tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
} }
else
{
uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
IP_PROTO_TCP);
tcp->tcpchksum = HTONS(chksum);
}
#endif #endif
#ifdef CONFIG_NET_STATISTICS #ifdef CONFIG_NET_STATISTICS
@@ -530,13 +517,6 @@ void tcp_reset(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
{ {
tcp->tcpchksum = ~tcp_ipv6_chksum(dev); tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
} }
else
{
uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
IP_PROTO_TCP,
IPv6_HDRLEN);
tcp->tcpchksum = HTONS(chksum);
}
#endif #endif
} }
#endif /* CONFIG_NET_IPv6 */ #endif /* CONFIG_NET_IPv6 */
@@ -560,12 +540,6 @@ void tcp_reset(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
{ {
tcp->tcpchksum = ~tcp_ipv4_chksum(dev); tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
} }
else
{
uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
IP_PROTO_TCP);
tcp->tcpchksum = HTONS(chksum);
}
#endif #endif
} }
#endif /* CONFIG_NET_IPv4 */ #endif /* CONFIG_NET_IPv4 */
+8 -24
View File
@@ -250,46 +250,30 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
#ifdef CONFIG_NET_UDP_CHECKSUMS #ifdef CONFIG_NET_UDP_CHECKSUMS
/* Calculate UDP checksum. */ /* Calculate UDP checksum. */
if ((dev->d_features & NETDEV_TX_CSUM) == 0)
{
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(dev->d_flags)) if (IFF_IS_IPv4(dev->d_flags))
#endif #endif
{
if ((dev->d_features & NETDEV_TX_CSUM) == 0)
{ {
udp->udpchksum = ~udp_ipv4_chksum(dev); udp->udpchksum = ~udp_ipv4_chksum(dev);
} }
else
{
uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
IP_PROTO_UDP);
udp->udpchksum = HTONS(chksum);
}
}
#endif /* CONFIG_NET_IPv4 */ #endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
else else
#endif #endif
{
if ((dev->d_features & NETDEV_TX_CSUM) == 0)
{ {
udp->udpchksum = ~udp_ipv6_chksum(dev); udp->udpchksum = ~udp_ipv6_chksum(dev);
} }
else
{
uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
IP_PROTO_UDP,
IPv6_HDRLEN);
udp->udpchksum = HTONS(chksum);
}
}
#endif /* CONFIG_NET_IPv6 */ #endif /* CONFIG_NET_IPv6 */
if (udp->udpchksum == 0) if (udp->udpchksum == 0)
{ {
udp->udpchksum = 0xffff; udp->udpchksum = 0xffff;
}
} }
#endif /* CONFIG_NET_UDP_CHECKSUMS */ #endif /* CONFIG_NET_UDP_CHECKSUMS */