Message ID | 1561642650-1974-1-git-send-email-john.hurley@netronome.com |
---|---|
State | Accepted |
Delegated to: | David Miller |
Headers | show |
Series | [net,1/1] net: openvswitch: fix csum updates for MPLS actions | expand |
On Thu, Jun 27, 2019 at 6:37 AM John Hurley <john.hurley@netronome.com> wrote: > > 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> > --- Thanks for fixing it. Acked-by: Pravin B Shelar <pshelar@ovn.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 151518d..bd13146 100644 > --- a/net/openvswitch/actions.c > +++ b/net/openvswitch/actions.c > @@ -166,8 +166,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; > @@ -259,8 +258,7 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key, > if (skb->ip_summed == CHECKSUM_COMPLETE) { > __be32 diff[] = { ~(stack->label_stack_entry), lse }; > > - skb->csum = ~csum_partial((char *)diff, sizeof(diff), > - ~skb->csum); > + skb->csum = csum_partial((char *)diff, sizeof(diff), skb->csum); > } > > stack->label_stack_entry = lse; > -- > 2.7.4 >
From: John Hurley <john.hurley@netronome.com> Date: Thu, 27 Jun 2019 14:37:30 +0100 > 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> Applied and queued up for -stable, thanks.
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 151518d..bd13146 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -166,8 +166,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; @@ -259,8 +258,7 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key, if (skb->ip_summed == CHECKSUM_COMPLETE) { __be32 diff[] = { ~(stack->label_stack_entry), lse }; - skb->csum = ~csum_partial((char *)diff, sizeof(diff), - ~skb->csum); + skb->csum = csum_partial((char *)diff, sizeof(diff), skb->csum); } stack->label_stack_entry = lse;