| From a329883a9552622084947afbfe1679e8146d067d Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 24 Oct 2021 22:14:25 +0200 |
| Subject: net: multicast: calculate csum of looped-back and forwarded packets |
| |
| From: Cyril Strejc <cyril.strejc@skoda.cz> |
| |
| [ Upstream commit 9122a70a6333705c0c35614ddc51c274ed1d3637 ] |
| |
| During a testing of an user-space application which transmits UDP |
| multicast datagrams and utilizes multicast routing to send the UDP |
| datagrams out of defined network interfaces, I've found a multicast |
| router does not fill-in UDP checksum into locally produced, looped-back |
| and forwarded UDP datagrams, if an original output NIC the datagrams |
| are sent to has UDP TX checksum offload enabled. |
| |
| The datagrams are sent malformed out of the NIC the datagrams have been |
| forwarded to. |
| |
| It is because: |
| |
| 1. If TX checksum offload is enabled on the output NIC, UDP checksum |
| is not calculated by kernel and is not filled into skb data. |
| |
| 2. dev_loopback_xmit(), which is called solely by |
| ip_mc_finish_output(), sets skb->ip_summed = CHECKSUM_UNNECESSARY |
| unconditionally. |
| |
| 3. Since 35fc92a9 ("[NET]: Allow forwarding of ip_summed except |
| CHECKSUM_COMPLETE"), the ip_summed value is preserved during |
| forwarding. |
| |
| 4. If ip_summed != CHECKSUM_PARTIAL, checksum is not calculated during |
| a packet egress. |
| |
| The minimum fix in dev_loopback_xmit(): |
| |
| 1. Preserves skb->ip_summed CHECKSUM_PARTIAL. This is the |
| case when the original output NIC has TX checksum offload enabled. |
| The effects are: |
| |
| a) If the forwarding destination interface supports TX checksum |
| offloading, the NIC driver is responsible to fill-in the |
| checksum. |
| |
| b) If the forwarding destination interface does NOT support TX |
| checksum offloading, checksums are filled-in by kernel before |
| skb is submitted to the NIC driver. |
| |
| c) For local delivery, checksum validation is skipped as in the |
| case of CHECKSUM_UNNECESSARY, thanks to skb_csum_unnecessary(). |
| |
| 2. Translates ip_summed CHECKSUM_NONE to CHECKSUM_UNNECESSARY. It |
| means, for CHECKSUM_NONE, the behavior is unmodified and is there |
| to skip a looped-back packet local delivery checksum validation. |
| |
| Signed-off-by: Cyril Strejc <cyril.strejc@skoda.cz> |
| Reviewed-by: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/net/udp.h | 5 +++-- |
| net/core/dev.c | 3 ++- |
| 2 files changed, 5 insertions(+), 3 deletions(-) |
| |
| diff --git a/include/net/udp.h b/include/net/udp.h |
| index fabf507bce5d1..d9d39cc20a847 100644 |
| --- a/include/net/udp.h |
| +++ b/include/net/udp.h |
| @@ -480,8 +480,9 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk, |
| * CHECKSUM_NONE in __udp_gso_segment. UDP GRO indeed builds partial |
| * packets in udp_gro_complete_segment. As does UDP GSO, verified by |
| * udp_send_skb. But when those packets are looped in dev_loopback_xmit |
| - * their ip_summed is set to CHECKSUM_UNNECESSARY. Reset in this |
| - * specific case, where PARTIAL is both correct and required. |
| + * their ip_summed CHECKSUM_NONE is changed to CHECKSUM_UNNECESSARY. |
| + * Reset in this specific case, where PARTIAL is both correct and |
| + * required. |
| */ |
| if (skb->pkt_type == PACKET_LOOPBACK) |
| skb->ip_summed = CHECKSUM_PARTIAL; |
| diff --git a/net/core/dev.c b/net/core/dev.c |
| index e4e492bf72af0..82dc094c03971 100644 |
| --- a/net/core/dev.c |
| +++ b/net/core/dev.c |
| @@ -3487,7 +3487,8 @@ int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) |
| skb_reset_mac_header(skb); |
| __skb_pull(skb, skb_network_offset(skb)); |
| skb->pkt_type = PACKET_LOOPBACK; |
| - skb->ip_summed = CHECKSUM_UNNECESSARY; |
| + if (skb->ip_summed == CHECKSUM_NONE) |
| + skb->ip_summed = CHECKSUM_UNNECESSARY; |
| WARN_ON(!skb_dst(skb)); |
| skb_dst_force(skb); |
| netif_rx_ni(skb); |
| -- |
| 2.33.0 |
| |