| From b07c83d099b6fa3c948becd2d09220821b09e9db Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 27 Jun 2019 14:37:30 +0100 |
| Subject: net: openvswitch: fix csum updates for MPLS actions |
| |
| From: John Hurley <john.hurley@netronome.com> |
| |
| [ Upstream commit 0e3183cd2a64843a95b62f8bd4a83605a4cf0615 ] |
| |
| Skbs may have their checksum value populated by HW. If this is a checksum |
| calculated over the entire packet then the CHECKSUM_COMPLETE field is |
| marked. Changes to the data pointer on the skb throughout the network |
| stack still try to maintain this complete csum value if it is required |
| through functions such as skb_postpush_rcsum. |
| |
| The MPLS actions in Open vSwitch modify a CHECKSUM_COMPLETE value when |
| changes are made to packet data without a push or a pull. This occurs when |
| the ethertype of the MAC header is changed or when MPLS lse fields are |
| modified. |
| |
| The modification is carried out using the csum_partial function to get the |
| csum of a buffer and add it into the larger checksum. The buffer is an |
| inversion of the data to be removed followed by the new data. Because the |
| csum is calculated over 16 bits and these values align with 16 bits, the |
| effect is the removal of the old value from the CHECKSUM_COMPLETE and |
| addition of the new value. |
| |
| However, the csum fed into the function and the outcome of the |
| calculation are also inverted. This would only make sense if it was the |
| new value rather than the old that was inverted in the input buffer. |
| |
| Fix the issue by removing the bit inverts in the csum_partial calculation. |
| |
| The bug was verified and the fix tested by comparing the folded value of |
| the updated CHECKSUM_COMPLETE value with the folded value of a full |
| software checksum calculation (reset skb->csum to 0 and run |
| skb_checksum_complete(skb)). Prior to the fix the outcomes differed but |
| after they produce the same result. |
| |
| Fixes: 25cd9ba0abc0 ("openvswitch: Add basic MPLS support to kernel") |
| Fixes: bc7cc5999fd3 ("openvswitch: update checksum in {push,pop}_mpls") |
| Signed-off-by: John Hurley <john.hurley@netronome.com> |
| Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> |
| Reviewed-by: Simon Horman <simon.horman@netronome.com> |
| Acked-by: Pravin B Shelar <pshelar@ovn.org> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/openvswitch/actions.c | 6 ++---- |
| 1 file changed, 2 insertions(+), 4 deletions(-) |
| |
| diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c |
| index fd6c587b6a040..828fdced4ecd8 100644 |
| --- a/net/openvswitch/actions.c |
| +++ b/net/openvswitch/actions.c |
| @@ -143,8 +143,7 @@ static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr, |
| if (skb->ip_summed == CHECKSUM_COMPLETE) { |
| __be16 diff[] = { ~(hdr->h_proto), ethertype }; |
| |
| - skb->csum = ~csum_partial((char *)diff, sizeof(diff), |
| - ~skb->csum); |
| + skb->csum = csum_partial((char *)diff, sizeof(diff), skb->csum); |
| } |
| |
| hdr->h_proto = ethertype; |
| @@ -227,8 +226,7 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key, |
| if (skb->ip_summed == CHECKSUM_COMPLETE) { |
| __be32 diff[] = { ~(*stack), lse }; |
| |
| - skb->csum = ~csum_partial((char *)diff, sizeof(diff), |
| - ~skb->csum); |
| + skb->csum = csum_partial((char *)diff, sizeof(diff), skb->csum); |
| } |
| |
| *stack = lse; |
| -- |
| 2.20.1 |
| |