diff mbox series

[ovs-dev,branch-3.2,v4] dp-packet: Correct IPv4 checksum calculation.

Message ID 20240815200331.1460372-1-mkp@redhat.com
State Changes Requested, archived
Delegated to: Ilya Maximets
Headers show
Series [ovs-dev,branch-3.2,v4] dp-packet: Correct IPv4 checksum calculation. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Mike Pattrick Aug. 15, 2024, 8:03 p.m. UTC
During the transition towards checksum offloading, the function to
handle software fallback of IPv4 checksums didn't account for the
options field.

Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
Reported-by: Jun Wang <junwang01@cestc.cn>
Signed-off-by: Mike Pattrick <mkp@redhat.com>
---
NB: In 3.2 the early checksum offloading patches caused packets from
the netdev-dummy/receive appctl command to resolve checksums prior to
odp-execute. This fixed in 3.3, but that can't easily be backported
without also backporting unrelated code.

To enable this patch to be backported, I gave one of the packets a
correct checksum and changed the comment so it was correct but also the
same number of lines as in the previous patch.

---
 lib/dp-packet.h      | 11 +++++++++--
 tests/dpif-netdev.at | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 2 deletions(-)

--
2.43.5

Comments

Ilya Maximets Aug. 16, 2024, 2:04 p.m. UTC | #1
On 8/15/24 22:03, Mike Pattrick wrote:
> During the transition towards checksum offloading, the function to
> handle software fallback of IPv4 checksums didn't account for the
> options field.
> 
> Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
> Reported-by: Jun Wang <junwang01@cestc.cn>
> Signed-off-by: Mike Pattrick <mkp@redhat.com>
> ---
> NB: In 3.2 the early checksum offloading patches caused packets from
> the netdev-dummy/receive appctl command to resolve checksums prior to
> odp-execute. This fixed in 3.3, but that can't easily be backported
> without also backporting unrelated code.
> 
> To enable this patch to be backported, I gave one of the packets a
> correct checksum and changed the comment so it was correct but also the
> same number of lines as in the previous patch.
> 

Hi, Mike.  Thanks for looking into this, but I'm still a bit confused
about what is going on.  The packets in the test do not trigger the
dp_packet_ip_set_header_csum() call, i.e. these tests do not actually
test the code change in this patch.  However, the previous ${bad_frame}
does trigger the function and the checksum is re-calculated for it.

What's the difference between these two?  Are IP options affecting the
logic somehow?

Best regards, Ilya Maximets.

> ---
>  lib/dp-packet.h      | 11 +++++++++--
>  tests/dpif-netdev.at | 35 +++++++++++++++++++++++++++++++++++
>  2 files changed, 44 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/dp-packet.h b/lib/dp-packet.h
> index 70ddf8aa4..25a0d31a1 100644
> --- a/lib/dp-packet.h
> +++ b/lib/dp-packet.h
> @@ -1142,10 +1142,17 @@ static inline void
>  dp_packet_ip_set_header_csum(struct dp_packet *p)
>  {
>      struct ip_header *ip = dp_packet_l3(p);
> +    size_t l3_size = dp_packet_l3_size(p);
> +    size_t ip_len;
> 
>      ovs_assert(ip);
> -    ip->ip_csum = 0;
> -    ip->ip_csum = csum(ip, sizeof *ip);
> +
> +    ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
> +
> +    if (OVS_LIKELY(ip_len >= IP_HEADER_LEN && ip_len < l3_size)) {
> +        ip->ip_csum = 0;
> +        ip->ip_csum = csum(ip, ip_len);
> +    }
>  }
> 
>  /* Returns 'true' if the packet 'p' has good integrity and the
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 061e13af8..93226521a 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -807,6 +807,41 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
>  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
>  AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
>  ])
> +
> +dnl Test with IP optional fields in a valid packet.  Note that this packet
> +dnl contains a correct checksum, but the following packet doesn't. This is
> +dnl because the dummy interface resets checksum offload on packet recipt.
> +m4_define([OPT_PKT], m4_join([],
> +dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
> +[aaaaaaaaaaaabbbbbbbbbbbb0800],
> +dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0x94d6)
> +[4f000044abab0000400194d60a0000010a000002],
> +dnl IPv4 Opt: type 7 (Record Route) len 39 + type 0 (EOL).
> +[07270c010203040a000003000000000000000000],
> +[0000000000000000000000000000000000000000],
> +dnl icmp(type=8,code=0), csum 0x3e2f incorrect, should be 0x412f.
> +[08003e2fb6d00000]))
> +
> +dnl IP header indicates optional fields but doesn't contain any.
> +m4_define([MICROGRAM], m4_join([],
> +dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
> +[aaaaaaaaaaaabbbbbbbbbbbb0800],
> +dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0xeeee)
> +[4f000044abab00004001eeee0a0000010a000002]))
> +
> +AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
> +AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 OPT_PKT])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 MICROGRAM])
> +AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> +
> +dnl Build the expected modified packets. The first packet has a valid IPv4
> +dnl checksum and modified destination IP address. The second packet isn't
> +dnl expected to change.
> +AT_CHECK([echo "OPT_PKT" | sed -e "s/0a000002/c0a80101/" -e "s/94d6/dd2e/" > expout])
> +AT_CHECK([echo "MICROGRAM" >> expout])
> +AT_CHECK([tail -n 2 p2.pcap.txt], [0], [expout])
> +
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
> 
> --
> 2.43.5
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Mike Pattrick Aug. 19, 2024, 7:23 a.m. UTC | #2
On Fri, Aug 16, 2024 at 10:04 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> On 8/15/24 22:03, Mike Pattrick wrote:
> > During the transition towards checksum offloading, the function to
> > handle software fallback of IPv4 checksums didn't account for the
> > options field.
> >
> > Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
> > Reported-by: Jun Wang <junwang01@cestc.cn>
> > Signed-off-by: Mike Pattrick <mkp@redhat.com>
> > ---
> > NB: In 3.2 the early checksum offloading patches caused packets from
> > the netdev-dummy/receive appctl command to resolve checksums prior to
> > odp-execute. This fixed in 3.3, but that can't easily be backported
> > without also backporting unrelated code.
> >
> > To enable this patch to be backported, I gave one of the packets a
> > correct checksum and changed the comment so it was correct but also the
> > same number of lines as in the previous patch.
> >
>
> Hi, Mike.  Thanks for looking into this, but I'm still a bit confused
> about what is going on.  The packets in the test do not trigger the
> dp_packet_ip_set_header_csum() call, i.e. these tests do not actually
> test the code change in this patch.  However, the previous ${bad_frame}
> does trigger the function and the checksum is re-calculated for it.
>
> What's the difference between these two?  Are IP options affecting the
> logic somehow?

This is unrelated to the content of the packet, and is instead related
to how dummy/receive packets are handled in offloading scenarios. This
was fixed in: 084c8087292c ("userspace: Support VXLAN and GENEVE
TSO.")



>
> Best regards, Ilya Maximets.
>
> > ---
> >  lib/dp-packet.h      | 11 +++++++++--
> >  tests/dpif-netdev.at | 35 +++++++++++++++++++++++++++++++++++
> >  2 files changed, 44 insertions(+), 2 deletions(-)
> >
> > diff --git a/lib/dp-packet.h b/lib/dp-packet.h
> > index 70ddf8aa4..25a0d31a1 100644
> > --- a/lib/dp-packet.h
> > +++ b/lib/dp-packet.h
> > @@ -1142,10 +1142,17 @@ static inline void
> >  dp_packet_ip_set_header_csum(struct dp_packet *p)
> >  {
> >      struct ip_header *ip = dp_packet_l3(p);
> > +    size_t l3_size = dp_packet_l3_size(p);
> > +    size_t ip_len;
> >
> >      ovs_assert(ip);
> > -    ip->ip_csum = 0;
> > -    ip->ip_csum = csum(ip, sizeof *ip);
> > +
> > +    ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
> > +
> > +    if (OVS_LIKELY(ip_len >= IP_HEADER_LEN && ip_len < l3_size)) {
> > +        ip->ip_csum = 0;
> > +        ip->ip_csum = csum(ip, ip_len);
> > +    }
> >  }
> >
> >  /* Returns 'true' if the packet 'p' has good integrity and the
> > diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> > index 061e13af8..93226521a 100644
> > --- a/tests/dpif-netdev.at
> > +++ b/tests/dpif-netdev.at
> > @@ -807,6 +807,41 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
> >  AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> >  AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
> >  ])
> > +
> > +dnl Test with IP optional fields in a valid packet.  Note that this packet
> > +dnl contains a correct checksum, but the following packet doesn't. This is
> > +dnl because the dummy interface resets checksum offload on packet recipt.
> > +m4_define([OPT_PKT], m4_join([],
> > +dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
> > +[aaaaaaaaaaaabbbbbbbbbbbb0800],
> > +dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0x94d6)
> > +[4f000044abab0000400194d60a0000010a000002],
> > +dnl IPv4 Opt: type 7 (Record Route) len 39 + type 0 (EOL).
> > +[07270c010203040a000003000000000000000000],
> > +[0000000000000000000000000000000000000000],
> > +dnl icmp(type=8,code=0), csum 0x3e2f incorrect, should be 0x412f.
> > +[08003e2fb6d00000]))
> > +
> > +dnl IP header indicates optional fields but doesn't contain any.
> > +m4_define([MICROGRAM], m4_join([],
> > +dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
> > +[aaaaaaaaaaaabbbbbbbbbbbb0800],
> > +dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0xeeee)
> > +[4f000044abab00004001eeee0a0000010a000002]))
> > +
> > +AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
> > +AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 OPT_PKT])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 MICROGRAM])
> > +AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
> > +
> > +dnl Build the expected modified packets. The first packet has a valid IPv4
> > +dnl checksum and modified destination IP address. The second packet isn't
> > +dnl expected to change.
> > +AT_CHECK([echo "OPT_PKT" | sed -e "s/0a000002/c0a80101/" -e "s/94d6/dd2e/" > expout])
> > +AT_CHECK([echo "MICROGRAM" >> expout])
> > +AT_CHECK([tail -n 2 p2.pcap.txt], [0], [expout])
> > +
> >  OVS_VSWITCHD_STOP
> >  AT_CLEANUP
> >
> > --
> > 2.43.5
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
>
Ilya Maximets Aug. 20, 2024, 10:09 p.m. UTC | #3
On 8/19/24 09:23, Mike Pattrick wrote:
> On Fri, Aug 16, 2024 at 10:04 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>>
>> On 8/15/24 22:03, Mike Pattrick wrote:
>>> During the transition towards checksum offloading, the function to
>>> handle software fallback of IPv4 checksums didn't account for the
>>> options field.
>>>
>>> Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
>>> Reported-by: Jun Wang <junwang01@cestc.cn>
>>> Signed-off-by: Mike Pattrick <mkp@redhat.com>
>>> ---
>>> NB: In 3.2 the early checksum offloading patches caused packets from
>>> the netdev-dummy/receive appctl command to resolve checksums prior to
>>> odp-execute. This fixed in 3.3, but that can't easily be backported
>>> without also backporting unrelated code.
>>>
>>> To enable this patch to be backported, I gave one of the packets a
>>> correct checksum and changed the comment so it was correct but also the
>>> same number of lines as in the previous patch.
>>>
>>
>> Hi, Mike.  Thanks for looking into this, but I'm still a bit confused
>> about what is going on.  The packets in the test do not trigger the
>> dp_packet_ip_set_header_csum() call, i.e. these tests do not actually
>> test the code change in this patch.  However, the previous ${bad_frame}
>> does trigger the function and the checksum is re-calculated for it.
>>
>> What's the difference between these two?  Are IP options affecting the
>> logic somehow?
> 
> This is unrelated to the content of the packet, and is instead related
> to how dummy/receive packets are handled in offloading scenarios. This
> was fixed in: 084c8087292c ("userspace: Support VXLAN and GENEVE
> TSO.")

So, I traced the packets through the code.  And it is, in fact, related to
the content of the packet.  All the previous packets in the test have the
same signature - IPv4 / TCP with 192.168.123.1 destination.  So, after the
first good frame we have a datapath flow installed that looks like this:

  in_port(1),eth_type(0x0800),ipv4(dst=192.168.123.1,proto=6,frag=no),
  actions:set(ipv4(dst=192.168.1.1)),2

We have a match on destination IP, because our OF rule is changing it.
All the incoming packets have the same offlad flags set.  They are all
marked for Tx IPv4 checksum offload and all marked as having good csum.

However, the newly added packets in this patch are ICMP packets and have
a different destination address, so they are going through upcall.
And on 3.2 branch all the packets sent through upcall have their checksums
explicitly fixed up beforehand:

  dp_netdev_upcall() -> dp_packet_ol_send_prepare(packet_, 0);

Since we have csum_good flag set, the preparation removes the Tx offload
flag.  Later, the IP address modification no longer sees the Tx offload flag
and modifies checksum incrementally.

If the packet will not go through upcall, then the Tx offload flag will be
preserved up until dp_packet_ip_set_header_csum() and the checksum will be
fully re-calculated triggering the issue fixed in this patch.

So, we either need to send the same TCP packet, but with IP options
to not trigger an upcall, or we could just send the same packet twice
and only check the second pass that will go through the datapath without
triggering an upcall.

Best regards, Ilya Maximets.
Mike Pattrick Aug. 22, 2024, 2:30 p.m. UTC | #4
On Tue, Aug 20, 2024 at 6:09 PM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> On 8/19/24 09:23, Mike Pattrick wrote:
> > On Fri, Aug 16, 2024 at 10:04 AM Ilya Maximets <i.maximets@ovn.org> wrote:
> >>
> >> On 8/15/24 22:03, Mike Pattrick wrote:
> >>> During the transition towards checksum offloading, the function to
> >>> handle software fallback of IPv4 checksums didn't account for the
> >>> options field.
> >>>
> >>> Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
> >>> Reported-by: Jun Wang <junwang01@cestc.cn>
> >>> Signed-off-by: Mike Pattrick <mkp@redhat.com>
> >>> ---
> >>> NB: In 3.2 the early checksum offloading patches caused packets from
> >>> the netdev-dummy/receive appctl command to resolve checksums prior to
> >>> odp-execute. This fixed in 3.3, but that can't easily be backported
> >>> without also backporting unrelated code.
> >>>
> >>> To enable this patch to be backported, I gave one of the packets a
> >>> correct checksum and changed the comment so it was correct but also the
> >>> same number of lines as in the previous patch.
> >>>
> >>
> >> Hi, Mike.  Thanks for looking into this, but I'm still a bit confused
> >> about what is going on.  The packets in the test do not trigger the
> >> dp_packet_ip_set_header_csum() call, i.e. these tests do not actually
> >> test the code change in this patch.  However, the previous ${bad_frame}
> >> does trigger the function and the checksum is re-calculated for it.
> >>
> >> What's the difference between these two?  Are IP options affecting the
> >> logic somehow?
> >
> > This is unrelated to the content of the packet, and is instead related
> > to how dummy/receive packets are handled in offloading scenarios. This
> > was fixed in: 084c8087292c ("userspace: Support VXLAN and GENEVE
> > TSO.")
>
> So, I traced the packets through the code.  And it is, in fact, related to
> the content of the packet.  All the previous packets in the test have the
> same signature - IPv4 / TCP with 192.168.123.1 destination.  So, after the
> first good frame we have a datapath flow installed that looks like this:
>
>   in_port(1),eth_type(0x0800),ipv4(dst=192.168.123.1,proto=6,frag=no),
>   actions:set(ipv4(dst=192.168.1.1)),2
>
> We have a match on destination IP, because our OF rule is changing it.
> All the incoming packets have the same offlad flags set.  They are all
> marked for Tx IPv4 checksum offload and all marked as having good csum.
>
> However, the newly added packets in this patch are ICMP packets and have
> a different destination address, so they are going through upcall.
> And on 3.2 branch all the packets sent through upcall have their checksums
> explicitly fixed up beforehand:
>
>   dp_netdev_upcall() -> dp_packet_ol_send_prepare(packet_, 0);
>
> Since we have csum_good flag set, the preparation removes the Tx offload
> flag.  Later, the IP address modification no longer sees the Tx offload flag
> and modifies checksum incrementally.
>
> If the packet will not go through upcall, then the Tx offload flag will be
> preserved up until dp_packet_ip_set_header_csum() and the checksum will be
> fully re-calculated triggering the issue fixed in this patch.
>
> So, we either need to send the same TCP packet, but with IP options
> to not trigger an upcall, or we could just send the same packet twice
> and only check the second pass that will go through the datapath without
> triggering an upcall.

I said that the behaviour wasn't content dependent because the call to
send_prepare happened in the upcall - regardless of packet content. If
you'll accept the solution, I can resubmit with sending the options
packet twice, which also resolves the issue.


Cheers,
M

>
> Best regards, Ilya Maximets.
>
Ilya Maximets Aug. 22, 2024, 4:07 p.m. UTC | #5
On 8/22/24 16:30, Mike Pattrick wrote:
> On Tue, Aug 20, 2024 at 6:09 PM Ilya Maximets <i.maximets@ovn.org> wrote:
>>
>> On 8/19/24 09:23, Mike Pattrick wrote:
>>> On Fri, Aug 16, 2024 at 10:04 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>>>>
>>>> On 8/15/24 22:03, Mike Pattrick wrote:
>>>>> During the transition towards checksum offloading, the function to
>>>>> handle software fallback of IPv4 checksums didn't account for the
>>>>> options field.
>>>>>
>>>>> Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
>>>>> Reported-by: Jun Wang <junwang01@cestc.cn>
>>>>> Signed-off-by: Mike Pattrick <mkp@redhat.com>
>>>>> ---
>>>>> NB: In 3.2 the early checksum offloading patches caused packets from
>>>>> the netdev-dummy/receive appctl command to resolve checksums prior to
>>>>> odp-execute. This fixed in 3.3, but that can't easily be backported
>>>>> without also backporting unrelated code.
>>>>>
>>>>> To enable this patch to be backported, I gave one of the packets a
>>>>> correct checksum and changed the comment so it was correct but also the
>>>>> same number of lines as in the previous patch.
>>>>>
>>>>
>>>> Hi, Mike.  Thanks for looking into this, but I'm still a bit confused
>>>> about what is going on.  The packets in the test do not trigger the
>>>> dp_packet_ip_set_header_csum() call, i.e. these tests do not actually
>>>> test the code change in this patch.  However, the previous ${bad_frame}
>>>> does trigger the function and the checksum is re-calculated for it.
>>>>
>>>> What's the difference between these two?  Are IP options affecting the
>>>> logic somehow?
>>>
>>> This is unrelated to the content of the packet, and is instead related
>>> to how dummy/receive packets are handled in offloading scenarios. This
>>> was fixed in: 084c8087292c ("userspace: Support VXLAN and GENEVE
>>> TSO.")
>>
>> So, I traced the packets through the code.  And it is, in fact, related to
>> the content of the packet.  All the previous packets in the test have the
>> same signature - IPv4 / TCP with 192.168.123.1 destination.  So, after the
>> first good frame we have a datapath flow installed that looks like this:
>>
>>   in_port(1),eth_type(0x0800),ipv4(dst=192.168.123.1,proto=6,frag=no),
>>   actions:set(ipv4(dst=192.168.1.1)),2
>>
>> We have a match on destination IP, because our OF rule is changing it.
>> All the incoming packets have the same offlad flags set.  They are all
>> marked for Tx IPv4 checksum offload and all marked as having good csum.
>>
>> However, the newly added packets in this patch are ICMP packets and have
>> a different destination address, so they are going through upcall.
>> And on 3.2 branch all the packets sent through upcall have their checksums
>> explicitly fixed up beforehand:
>>
>>   dp_netdev_upcall() -> dp_packet_ol_send_prepare(packet_, 0);
>>
>> Since we have csum_good flag set, the preparation removes the Tx offload
>> flag.  Later, the IP address modification no longer sees the Tx offload flag
>> and modifies checksum incrementally.
>>
>> If the packet will not go through upcall, then the Tx offload flag will be
>> preserved up until dp_packet_ip_set_header_csum() and the checksum will be
>> fully re-calculated triggering the issue fixed in this patch.
>>
>> So, we either need to send the same TCP packet, but with IP options
>> to not trigger an upcall, or we could just send the same packet twice
>> and only check the second pass that will go through the datapath without
>> triggering an upcall.
> 
> I said that the behaviour wasn't content dependent because the call to
> send_prepare happened in the upcall - regardless of packet content.

My point was that the fact the we're having an upcall depends on the
packet content.  If we use a TCP packet with 192.168.123.1 destination,
we'll not have an upcall.

> If you'll accept the solution, I can resubmit with sending the options
> packet twice, which also resolves the issue.

Double sending seems reasonable.

As a separate change we may also want to enhance the test on the main
branch by sending two packets as well (and checking both of them), so
we can cover both the upcall and non-upcall path.

Best regards, Ilya Maximets.
diff mbox series

Patch

diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 70ddf8aa4..25a0d31a1 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -1142,10 +1142,17 @@  static inline void
 dp_packet_ip_set_header_csum(struct dp_packet *p)
 {
     struct ip_header *ip = dp_packet_l3(p);
+    size_t l3_size = dp_packet_l3_size(p);
+    size_t ip_len;

     ovs_assert(ip);
-    ip->ip_csum = 0;
-    ip->ip_csum = csum(ip, sizeof *ip);
+
+    ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
+
+    if (OVS_LIKELY(ip_len >= IP_HEADER_LEN && ip_len < l3_size)) {
+        ip->ip_csum = 0;
+        ip->ip_csum = csum(ip, ip_len);
+    }
 }

 /* Returns 'true' if the packet 'p' has good integrity and the
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 061e13af8..93226521a 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -807,6 +807,41 @@  AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
 AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
 AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_expected}
 ])
+
+dnl Test with IP optional fields in a valid packet.  Note that this packet
+dnl contains a correct checksum, but the following packet doesn't. This is
+dnl because the dummy interface resets checksum offload on packet recipt.
+m4_define([OPT_PKT], m4_join([],
+dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
+[aaaaaaaaaaaabbbbbbbbbbbb0800],
+dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0x94d6)
+[4f000044abab0000400194d60a0000010a000002],
+dnl IPv4 Opt: type 7 (Record Route) len 39 + type 0 (EOL).
+[07270c010203040a000003000000000000000000],
+[0000000000000000000000000000000000000000],
+dnl icmp(type=8,code=0), csum 0x3e2f incorrect, should be 0x412f.
+[08003e2fb6d00000]))
+
+dnl IP header indicates optional fields but doesn't contain any.
+m4_define([MICROGRAM], m4_join([],
+dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800)
+[aaaaaaaaaaaabbbbbbbbbbbb0800],
+dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0xeeee)
+[4f000044abab00004001eeee0a0000010a000002]))
+
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 OPT_PKT])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 MICROGRAM])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+
+dnl Build the expected modified packets. The first packet has a valid IPv4
+dnl checksum and modified destination IP address. The second packet isn't
+dnl expected to change.
+AT_CHECK([echo "OPT_PKT" | sed -e "s/0a000002/c0a80101/" -e "s/94d6/dd2e/" > expout])
+AT_CHECK([echo "MICROGRAM" >> expout])
+AT_CHECK([tail -n 2 p2.pcap.txt], [0], [expout])
+
 OVS_VSWITCHD_STOP
 AT_CLEANUP