net: more accurate checksumming in validate_xmit_skb()

skb_csum_hwoffload_help() uses netdev features and skb->csum_not_inet to
determine if skb needs software computation of Internet Checksum or crc32c
(or nothing, if this computation can be done by the hardware). Use it in
place of skb_checksum_help() in validate_xmit_skb() to avoid corruption
of non-GSO SCTP packets having skb->ip_summed equal to CHECKSUM_PARTIAL.

While at it, remove references to skb_csum_off_chk* functions, since they
are not present anymore in Linux  _ see commit cf53b1da73 ("Revert
 "net: Add driver helper functions to determine checksum offloadability"").

Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Davide Caratti 2017-05-18 15:44:41 +02:00 committed by David S. Miller
parent dba003067a
commit 43c26a1a45
4 changed files with 27 additions and 14 deletions

View File

@ -35,6 +35,9 @@ This interface only allows a single checksum to be offloaded. Where
encapsulation is used, the packet may have multiple checksum fields in encapsulation is used, the packet may have multiple checksum fields in
different header layers, and the rest will have to be handled by another different header layers, and the rest will have to be handled by another
mechanism such as LCO or RCO. mechanism such as LCO or RCO.
CRC32c can also be offloaded using this interface, by means of filling
skb->csum_start and skb->csum_offset as described above, and setting
skb->csum_not_inet: see skbuff.h comment (section 'D') for more details.
No offloading of the IP header checksum is performed; it is always done in No offloading of the IP header checksum is performed; it is always done in
software. This is OK because when we build the IP header, we obviously software. This is OK because when we build the IP header, we obviously
have it in cache, so summing it isn't expensive. It's also rather short. have it in cache, so summing it isn't expensive. It's also rather short.
@ -49,9 +52,9 @@ A driver declares its offload capabilities in netdev->hw_features; see
and csum_offset given in the SKB; if it tries to deduce these itself in and csum_offset given in the SKB; if it tries to deduce these itself in
hardware (as some NICs do) the driver should check that the values in the hardware (as some NICs do) the driver should check that the values in the
SKB match those which the hardware will deduce, and if not, fall back to SKB match those which the hardware will deduce, and if not, fall back to
checksumming in software instead (with skb_checksum_help or one of the checksumming in software instead (with skb_csum_hwoffload_help() or one of
skb_csum_off_chk* functions as mentioned in include/linux/skbuff.h). This the skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in
is a pain, but that's what you get when hardware tries to be clever. include/linux/skbuff.h).
The stack should, for the most part, assume that checksum offload is The stack should, for the most part, assume that checksum offload is
supported by the underlying device. The only place that should check is supported by the underlying device. The only place that should check is
@ -60,7 +63,7 @@ The stack should, for the most part, assume that checksum offload is
may include other offloads besides TX Checksum Offload) and, if they are may include other offloads besides TX Checksum Offload) and, if they are
not supported or enabled on the device (determined by netdev->features), not supported or enabled on the device (determined by netdev->features),
performs the corresponding offload in software. In the case of TX performs the corresponding offload in software. In the case of TX
Checksum Offload, that means calling skb_checksum_help(skb). Checksum Offload, that means calling skb_csum_hwoffload_help(skb, features).
LCO: Local Checksum Offload LCO: Local Checksum Offload

View File

@ -3930,6 +3930,9 @@ void netdev_rss_key_fill(void *buffer, size_t len);
int dev_get_nest_level(struct net_device *dev); int dev_get_nest_level(struct net_device *dev);
int skb_checksum_help(struct sk_buff *skb); int skb_checksum_help(struct sk_buff *skb);
int skb_crc32c_csum_help(struct sk_buff *skb); int skb_crc32c_csum_help(struct sk_buff *skb);
int skb_csum_hwoffload_help(struct sk_buff *skb,
const netdev_features_t features);
struct sk_buff *__skb_gso_segment(struct sk_buff *skb, struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path); netdev_features_t features, bool tx_path);
struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,

View File

@ -162,14 +162,11 @@
* *
* NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM are being deprecated in favor of * NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM are being deprecated in favor of
* NETIF_F_HW_CSUM. New devices should use NETIF_F_HW_CSUM to indicate * NETIF_F_HW_CSUM. New devices should use NETIF_F_HW_CSUM to indicate
* checksum offload capability. If a device has limited checksum capabilities * checksum offload capability.
* (for instance can only perform NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM as * skb_csum_hwoffload_help() can be called to resolve CHECKSUM_PARTIAL based
* described above) a helper function can be called to resolve * on network device checksumming capabilities: if a packet does not match
* CHECKSUM_PARTIAL. The helper functions are skb_csum_off_chk*. The helper * them, skb_checksum_help or skb_crc32c_help (depending on the value of
* function takes a spec argument that describes the protocol layer that is * csum_not_inet, see item D.) is called to resolve the checksum.
* supported for checksum offload and can be called for each packet. If a
* packet does not match the specification for offload, skb_checksum_help
* is called to resolve the checksum.
* *
* CHECKSUM_NONE: * CHECKSUM_NONE:
* *

View File

@ -2996,6 +2996,17 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
return skb; return skb;
} }
int skb_csum_hwoffload_help(struct sk_buff *skb,
const netdev_features_t features)
{
if (unlikely(skb->csum_not_inet))
return !!(features & NETIF_F_SCTP_CRC) ? 0 :
skb_crc32c_csum_help(skb);
return !!(features & NETIF_F_CSUM_MASK) ? 0 : skb_checksum_help(skb);
}
EXPORT_SYMBOL(skb_csum_hwoffload_help);
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)
{ {
netdev_features_t features; netdev_features_t features;
@ -3034,8 +3045,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
else else
skb_set_transport_header(skb, skb_set_transport_header(skb,
skb_checksum_start_offset(skb)); skb_checksum_start_offset(skb));
if (!(features & NETIF_F_CSUM_MASK) && if (skb_csum_hwoffload_help(skb, features))
skb_checksum_help(skb))
goto out_kfree_skb; goto out_kfree_skb;
} }
} }