diff mbox series

[ovs-dev,v3,3/3] upcall: considering dataofs when parsing tcp pkt

Message ID 2021110119033968552461@chinatelecom.cn
State Superseded
Headers show
Series bug fix: avoid install bad dp flow | 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

Cheng Li Nov. 1, 2021, 11:03 a.m. UTC
dataofs field of tcp header indicates the tcp header len. The len
should be >= 20 bytes/4. This patch is to test dataofs, and don't
parse layer 4 fields when meet ba dataofs. This behave is the consistent
with openvswitch kenrel module.

Signed-off-by: lic121 <lic121@chinatelecom.cn>
---
 lib/flow.c            | 18 ++++++++++--------
 tests/ofproto-dpif.at | 31 +++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 8 deletions(-)

Comments

Ilya Maximets Nov. 18, 2021, 3:34 p.m. UTC | #1
On 11/1/21 12:03, lic121 wrote:
> dataofs field of tcp header indicates the tcp header len. The len
> should be >= 20 bytes/4. This patch is to test dataofs, and don't
> parse layer 4 fields when meet ba dataofs. This behave is the consistent
> with openvswitch kenrel module.
> 
> Signed-off-by: lic121 <lic121@chinatelecom.cn>
> ---
>  lib/flow.c            | 18 ++++++++++--------
>  tests/ofproto-dpif.at | 31 +++++++++++++++++++++++++++++++
>  2 files changed, 41 insertions(+), 8 deletions(-)

Hi.  Thanks for the patch!  And sorry for the late response.
See some comments inline.

Bets regards, Ilya Maximets.

> 
> diff --git a/lib/flow.c b/lib/flow.c
> index 89837de..f117490 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -1006,14 +1006,16 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
>          if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {
>              if (OVS_LIKELY(size >= TCP_HEADER_LEN)) {
>                  const struct tcp_header *tcp = data;
> -
> -                miniflow_push_be32(mf, arp_tha.ea[2], 0);
> -                miniflow_push_be32(mf, tcp_flags,
> -                                   TCP_FLAGS_BE32(tcp->tcp_ctl));
> -                miniflow_push_be16(mf, tp_src, tcp->tcp_src);
> -                miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
> -                miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
> -                miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
> +                size_t tcp_hdr_len = TCP_OFFSET(tcp->tcp_ctl) * 4;

Please, add an empty line between the declarations and the code below.

> +                if (tcp_hdr_len >= TCP_HEADER_LEN) {

This check seems fine, but, IIUC, it doesn't check the case where
the TCP header length declared larger than the remaining space in
a packet.  Looking at the kernel implementation of tcphdr_ok(),
there are, basically, 3 checks that make it fail:

1. pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr))) == false
   
   This one is similar to (size >= TCP_HEADER_LEN) check here.

2. tcp_len < sizeof(struct tcphdr)

   This check you added as (tcp_hdr_len >= TCP_HEADER_LEN)

3. skb->len < th_ofs + tcp_len

   But this one is not covered by the patch.  IIUC, it should
   look like:
       if (tcp_hdr_len >= TCP_HEADER_LEN && size >= tcp_hdr_len) {

Without the third check, if TCP header length is larger than
the remaining packet size, kernel will not parse it, but
userspace will, leading to the similar issue.

Could you add this check to the patch and a test case for this
condition?

> +                    miniflow_push_be32(mf, arp_tha.ea[2], 0);
> +                    miniflow_push_be32(mf, tcp_flags,
> +                                    TCP_FLAGS_BE32(tcp->tcp_ctl));

Please, shift this line 3 spaces to the right.

> +                    miniflow_push_be16(mf, tp_src, tcp->tcp_src);
> +                    miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
> +                    miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
> +                    miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
> +                }
>              }
>          } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) {
>              if (OVS_LIKELY(size >= UDP_HEADER_LEN)) {
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 31fb163..0f372ae 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -4862,6 +4862,37 @@ recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,fr
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  
> +AT_SETUP([ofproto-dpif - malformed packets handling - upcall])
> +OVS_VSWITCHD_START
> +add_of_ports br0 1 90
> +dnl drop packet has tcp port 0-f but allow other tcp packets
> +AT_DATA([flows.txt], [dnl
> +priority=75 tcp tp_dst=0/0xfff0     actions=drop
> +priority=50 tcp                     actions=output:1
> +])
> +AT_CHECK([ovs-ofctl replace-flows br0 flows.txt])
> +dnl good tcp pkt, tcp(sport=100,dpor=16)
> +pkt1="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000005002200053330000"
> +dnl malformed tcp pkt, tcp(sport=100,dport=16,dataofs=1)
> +pkt2="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000001002200093330000"
> +AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg])
> +mode=normal
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt1"], [0], [stdout])
> +dnl for good tcp pkt, ovs can extract the tp_dst=16
> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl
> +flow-dump from the main thread:
> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=16/0xfff0), packets:0, bytes:0, used:never, actions:1
> +])
> +AT_CHECK([ovs-appctl dpctl/del-flows], [0], [stdout])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt2"], [0], [stdout])
> +dnl for malformed tcp pkt, ovs can use default value tp_dst=0
> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl
> +flow-dump from the main thread:
> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=0/0xfff0), packets:0, bytes:0, used:never, actions:drop
> +])
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  AT_SETUP([ofproto-dpif - exit])
>  OVS_VSWITCHD_START
>  add_of_ports br0 1 2 3 10 11 12 13 14
>
Cheng Li Nov. 22, 2021, 3:10 a.m. UTC | #2
>On 11/1/21 12:03, lic121 wrote:



>> dataofs field of tcp header indicates the tcp header len. The len



>> should be >= 20 bytes/4. This patch is to test dataofs, and don't



>> parse layer 4 fields when meet ba dataofs. This behave is the consistent



>> with openvswitch kenrel module.



>> 



>> Signed-off-by: lic121 <lic121@chinatelecom.cn>



>> ---



>>  lib/flow.c            | 18 ++++++++++--------



>>  tests/ofproto-dpif.at | 31 +++++++++++++++++++++++++++++++



>>  2 files changed, 41 insertions(+), 8 deletions(-)



>



>Hi.  Thanks for the patch!  And sorry for the late response.



>See some comments inline.



>

Hi Ilya, it's OK and thanks for your review.


>Bets regards, Ilya Maximets.



>



>> 



>> diff --git a/lib/flow.c b/lib/flow.c



>> index 89837de..f117490 100644



>> --- a/lib/flow.c



>> +++ b/lib/flow.c



>> @@ -1006,14 +1006,16 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)



>>          if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {



>>              if (OVS_LIKELY(size >= TCP_HEADER_LEN)) {



>>                  const struct tcp_header *tcp = data;



>> -



>> -                miniflow_push_be32(mf, arp_tha.ea[2], 0);



>> -                miniflow_push_be32(mf, tcp_flags,



>> -                                   TCP_FLAGS_BE32(tcp->tcp_ctl));



>> -                miniflow_push_be16(mf, tp_src, tcp->tcp_src);



>> -                miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);



>> -                miniflow_push_be16(mf, ct_tp_src, ct_tp_src);



>> -                miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);



>> +                size_t tcp_hdr_len = TCP_OFFSET(tcp->tcp_ctl) * 4;



>



>Please, add an empty line between the declarations and the code below.

Will address this in v4


>



>> +                if (tcp_hdr_len >= TCP_HEADER_LEN) {



>



>This check seems fine, but, IIUC, it doesn't check the case where



>the TCP header length declared larger than the remaining space in



>a packet.  Looking at the kernel implementation of tcphdr_ok(),



>there are, basically, 3 checks that make it fail:



>



>1. pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr))) == false



>   



>   This one is similar to (size >= TCP_HEADER_LEN) check here.



>



>2. tcp_len < sizeof(struct tcphdr)



>



>   This check you added as (tcp_hdr_len >= TCP_HEADER_LEN)



>



>3. skb->len < th_ofs + tcp_len



>



>   But this one is not covered by the patch.  IIUC, it should



>   look like:



>       if (tcp_hdr_len >= TCP_HEADER_LEN && size >= tcp_hdr_len) {



>



>Without the third check, if TCP header length is larger than



>the remaining packet size, kernel will not parse it, but



>userspace will, leading to the similar issue.



>



>Could you add this check to the patch and a test case for this



>condition?

Agree, will address this in v4


>



>> +                    miniflow_push_be32(mf, arp_tha.ea[2], 0);



>> +                    miniflow_push_be32(mf, tcp_flags,



>> +                                    TCP_FLAGS_BE32(tcp->tcp_ctl));



>



>Please, shift this line 3 spaces to the right.

good catch. Will do


>



>> +                    miniflow_push_be16(mf, tp_src, tcp->tcp_src);



>> +                    miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);



>> +                    miniflow_push_be16(mf, ct_tp_src, ct_tp_src);



>> +                    miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);



>> +                }



>>              }



>>          } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) {



>>              if (OVS_LIKELY(size >= UDP_HEADER_LEN)) {



>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at



>> index 31fb163..0f372ae 100644



>> --- a/tests/ofproto-dpif.at



>> +++ b/tests/ofproto-dpif.at



>> @@ -4862,6 +4862,37 @@ recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,fr



>>  OVS_VSWITCHD_STOP



>>  AT_CLEANUP



>>  



>> +AT_SETUP([ofproto-dpif - malformed packets handling - upcall])



>> +OVS_VSWITCHD_START



>> +add_of_ports br0 1 90



>> +dnl drop packet has tcp port 0-f but allow other tcp packets



>> +AT_DATA([flows.txt], [dnl



>> +priority=75 tcp tp_dst=0/0xfff0     actions=drop



>> +priority=50 tcp                     actions=output:1



>> +])



>> +AT_CHECK([ovs-ofctl replace-flows br0 flows.txt])



>> +dnl good tcp pkt, tcp(sport=100,dpor=16)



>> +pkt1="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000005002200053330000"



>> +dnl malformed tcp pkt, tcp(sport=100,dport=16,dataofs=1)



>> +pkt2="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000001002200093330000"



>> +AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg])



>> +mode=normal



>> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt1"], [0], [stdout])



>> +dnl for good tcp pkt, ovs can extract the tp_dst=16



>> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl



>> +flow-dump from the main thread:



>> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=16/0xfff0), packets:0, bytes:0, used:never, actions:1



>> +])



>> +AT_CHECK([ovs-appctl dpctl/del-flows], [0], [stdout])



>> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt2"], [0], [stdout])



>> +dnl for malformed tcp pkt, ovs can use default value tp_dst=0



>> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl



>> +flow-dump from the main thread:



>> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=0/0xfff0), packets:0, bytes:0, used:never, actions:drop



>> +])



>> +OVS_VSWITCHD_STOP



>> +AT_CLEANUP



>> +



>>  AT_SETUP([ofproto-dpif - exit])



>>  OVS_VSWITCHD_START



>>  add_of_ports br0 1 2 3 10 11 12 13 14



>> 



>



>
diff mbox series

Patch

diff --git a/lib/flow.c b/lib/flow.c
index 89837de..f117490 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1006,14 +1006,16 @@  miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
         if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {
             if (OVS_LIKELY(size >= TCP_HEADER_LEN)) {
                 const struct tcp_header *tcp = data;
-
-                miniflow_push_be32(mf, arp_tha.ea[2], 0);
-                miniflow_push_be32(mf, tcp_flags,
-                                   TCP_FLAGS_BE32(tcp->tcp_ctl));
-                miniflow_push_be16(mf, tp_src, tcp->tcp_src);
-                miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
-                miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
-                miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+                size_t tcp_hdr_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
+                if (tcp_hdr_len >= TCP_HEADER_LEN) {
+                    miniflow_push_be32(mf, arp_tha.ea[2], 0);
+                    miniflow_push_be32(mf, tcp_flags,
+                                    TCP_FLAGS_BE32(tcp->tcp_ctl));
+                    miniflow_push_be16(mf, tp_src, tcp->tcp_src);
+                    miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
+                    miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
+                    miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+                }
             }
         } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) {
             if (OVS_LIKELY(size >= UDP_HEADER_LEN)) {
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 31fb163..0f372ae 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -4862,6 +4862,37 @@  recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,fr
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - malformed packets handling - upcall])
+OVS_VSWITCHD_START
+add_of_ports br0 1 90
+dnl drop packet has tcp port 0-f but allow other tcp packets
+AT_DATA([flows.txt], [dnl
+priority=75 tcp tp_dst=0/0xfff0     actions=drop
+priority=50 tcp                     actions=output:1
+])
+AT_CHECK([ovs-ofctl replace-flows br0 flows.txt])
+dnl good tcp pkt, tcp(sport=100,dpor=16)
+pkt1="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000005002200053330000"
+dnl malformed tcp pkt, tcp(sport=100,dport=16,dataofs=1)
+pkt2="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000001002200093330000"
+AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg])
+mode=normal
+AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt1"], [0], [stdout])
+dnl for good tcp pkt, ovs can extract the tp_dst=16
+AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl
+flow-dump from the main thread:
+recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=16/0xfff0), packets:0, bytes:0, used:never, actions:1
+])
+AT_CHECK([ovs-appctl dpctl/del-flows], [0], [stdout])
+AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt2"], [0], [stdout])
+dnl for malformed tcp pkt, ovs can use default value tp_dst=0
+AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl
+flow-dump from the main thread:
+recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=0/0xfff0), packets:0, bytes:0, used:never, actions:drop
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - exit])
 OVS_VSWITCHD_START
 add_of_ports br0 1 2 3 10 11 12 13 14