Mike Pattrick via dev <ovs-dev@openvswitch.org> writes:

> Currently ipf will inject expired fragments into the odp execution
> pipeline in an indeterminate location. Previously this was changed to
> segregate by IP version. However, there was nothing to stop fragments
> from being inserted into different tables, zones, or being sent in
> different directions.
>
> The kernel module just terminates these fragments, and userspace should
> as well. There are also new coverage counters to indicate how fragments
> expired.
>
> Reported-at: https://issues.redhat.com/browse/FDP-1052
> Fixes: 4ea96698f667 ("Userspace datapath: Add fragmentation handling.")
> Signed-off-by: Mike Pattrick <m...@redhat.com>
> ---

Thanks Mike - I've applied this and backported down to 3.2.

>  lib/ipf.c             | 49 +++++++++++++++-------------------
>  tests/ofproto-dpif.at | 61 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 82 insertions(+), 28 deletions(-)
>
> diff --git a/lib/ipf.c b/lib/ipf.c
> index b76181e79..53dfe6664 100644
> --- a/lib/ipf.c
> +++ b/lib/ipf.c
> @@ -35,6 +35,7 @@
>  #include "util.h"
>  
>  VLOG_DEFINE_THIS_MODULE(ipf);
> +COVERAGE_DEFINE(ipf_stuck_frag_list_expired);
>  COVERAGE_DEFINE(ipf_stuck_frag_list_purged);
>  COVERAGE_DEFINE(ipf_l3csum_err);
>  
> @@ -77,7 +78,7 @@ enum {
>  enum ipf_counter_type {
>      IPF_NFRAGS_ACCEPTED,
>      IPF_NFRAGS_COMPL_SENT,
> -    IPF_NFRAGS_EXPD_SENT,
> +    IPF_NFRAGS_EXPIRED,
>      IPF_NFRAGS_TOO_SMALL,
>      IPF_NFRAGS_OVERLAP,
>      IPF_NFRAGS_PURGED,
> @@ -1030,8 +1031,7 @@ ipf_purge_list_check(struct ipf *ipf, struct ipf_list 
> *ipf_list,
>   * with 'ipf_send_completed_frags()' and 'ipf_send_expired_frags()'. */
>  static bool
>  ipf_send_frags_in_list(struct ipf *ipf, struct ipf_list *ipf_list,
> -                       struct dp_packet_batch *pb,
> -                       enum ipf_list_type list_type, bool v6, long long now)
> +                       struct dp_packet_batch *pb, bool v6, long long now)
>      OVS_REQUIRES(ipf->ipf_lock)
>  {
>      if (ipf_purge_list_check(ipf, ipf_list, now)) {
> @@ -1045,12 +1045,7 @@ ipf_send_frags_in_list(struct ipf *ipf, struct 
> ipf_list *ipf_list,
>              ipf_list->last_sent_idx++;
>              atomic_count_dec(&ipf->nfrag);
>  
> -            if (list_type == IPF_FRAG_COMPLETED_LIST) {
> -                ipf_count(ipf, v6, IPF_NFRAGS_COMPL_SENT);
> -            } else {
> -                ipf_count(ipf, v6, IPF_NFRAGS_EXPD_SENT);
> -                pkt->md.ct_state = CS_INVALID;
> -            }
> +            ipf_count(ipf, v6, IPF_NFRAGS_COMPL_SENT);
>  
>              if (ipf_list->last_sent_idx == ipf_list->last_inuse_idx) {
>                  return true;
> @@ -1080,8 +1075,7 @@ ipf_send_completed_frags(struct ipf *ipf, struct 
> dp_packet_batch *pb,
>          if ((ipf_list->key.dl_type == htons(ETH_TYPE_IPV6)) != v6) {
>              continue;
>          }
> -        if (ipf_send_frags_in_list(ipf, ipf_list, pb, 
> IPF_FRAG_COMPLETED_LIST,
> -                                   v6, now)) {
> +        if (ipf_send_frags_in_list(ipf, ipf_list, pb, v6, now)) {
>              ipf_completed_list_clean(&ipf->frag_lists, ipf_list);
>          } else {
>              break;
> @@ -1091,12 +1085,9 @@ ipf_send_completed_frags(struct ipf *ipf, struct 
> dp_packet_batch *pb,
>      ovs_mutex_unlock(&ipf->ipf_lock);
>  }
>  
> -/* Conservatively adds fragments associated with a expired fragment list to
> - * a packet batch to be processed by the calling application, typically
> - * conntrack. Also cleans up the list context when it is empty.*/
> +/* Remove expired fragment lists and clean up the list context. */
>  static void
> -ipf_send_expired_frags(struct ipf *ipf, struct dp_packet_batch *pb,
> -                       long long now, bool v6)
> +ipf_delete_expired_frags(struct ipf *ipf, long long now)
>  {
>      enum {
>          /* Very conservative, due to DOS probability. */
> @@ -1113,21 +1104,23 @@ ipf_send_expired_frags(struct ipf *ipf, struct 
> dp_packet_batch *pb,
>      size_t lists_removed = 0;
>  
>      LIST_FOR_EACH_SAFE (ipf_list, list_node, &ipf->frag_exp_list) {
> -        if ((ipf_list->key.dl_type == htons(ETH_TYPE_IPV6)) != v6) {
> -            continue;
> -        }
>          if (now <= ipf_list->expiration ||
>              lists_removed >= IPF_FRAG_LIST_MAX_EXPIRED) {
>              break;
>          }
>  
> -        if (ipf_send_frags_in_list(ipf, ipf_list, pb, IPF_FRAG_EXPIRY_LIST,
> -                                   v6, now)) {
> -            ipf_expiry_list_clean(&ipf->frag_lists, ipf_list);
> -            lists_removed++;
> -        } else {
> -            break;
> +        while (ipf_list->last_sent_idx < ipf_list->last_inuse_idx) {
> +            struct dp_packet * pkt
> +                = ipf_list->frag_list[ipf_list->last_sent_idx + 1].pkt;
> +            dp_packet_delete(pkt);
> +            atomic_count_dec(&ipf->nfrag);
> +            COVERAGE_INC(ipf_stuck_frag_list_expired);
> +            ipf_count(ipf, ipf_list->key.dl_type == htons(ETH_TYPE_IPV6),
> +                      IPF_NFRAGS_EXPIRED);
> +            ipf_list->last_sent_idx++;
>          }
> +        ipf_expiry_list_clean(&ipf->frag_lists, ipf_list);
> +        lists_removed++;
>      }
>  
>      ovs_mutex_unlock(&ipf->ipf_lock);
> @@ -1275,7 +1268,7 @@ ipf_postprocess_conntrack(struct ipf *ipf, struct 
> dp_packet_batch *pb,
>          bool v6 = dl_type == htons(ETH_TYPE_IPV6);
>          ipf_post_execute_reass_pkts(ipf, pb, v6);
>          ipf_send_completed_frags(ipf, pb, now, v6);
> -        ipf_send_expired_frags(ipf, pb, now, v6);
> +        ipf_delete_expired_frags(ipf, now);
>      }
>  }
>  
> @@ -1448,7 +1441,7 @@ ipf_get_status(struct ipf *ipf, struct ipf_status 
> *ipf_status)
>                          &ipf_status->v4.nfrag_accepted);
>      atomic_read_relaxed(&ipf->n4frag_cnt[IPF_NFRAGS_COMPL_SENT],
>                          &ipf_status->v4.nfrag_completed_sent);
> -    atomic_read_relaxed(&ipf->n4frag_cnt[IPF_NFRAGS_EXPD_SENT],
> +    atomic_read_relaxed(&ipf->n4frag_cnt[IPF_NFRAGS_EXPIRED],
>                          &ipf_status->v4.nfrag_expired_sent);
>      atomic_read_relaxed(&ipf->n4frag_cnt[IPF_NFRAGS_TOO_SMALL],
>                          &ipf_status->v4.nfrag_too_small);
> @@ -1464,7 +1457,7 @@ ipf_get_status(struct ipf *ipf, struct ipf_status 
> *ipf_status)
>                          &ipf_status->v6.nfrag_accepted);
>      atomic_read_relaxed(&ipf->n6frag_cnt[IPF_NFRAGS_COMPL_SENT],
>                          &ipf_status->v6.nfrag_completed_sent);
> -    atomic_read_relaxed(&ipf->n6frag_cnt[IPF_NFRAGS_EXPD_SENT],
> +    atomic_read_relaxed(&ipf->n6frag_cnt[IPF_NFRAGS_EXPIRED],
>                          &ipf_status->v6.nfrag_expired_sent);
>      atomic_read_relaxed(&ipf->n6frag_cnt[IPF_NFRAGS_TOO_SMALL],
>                          &ipf_status->v6.nfrag_too_small);
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 6f7cc166f..a35b80077 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -5312,6 +5312,67 @@ 
> 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 - fragment handling - reassembly])
> +OVS_VSWITCHD_START
> +add_of_ports br0 1 2 90
> +
> +AT_DATA([flows.txt], [dnl
> +table=0 tcp actions=ct(commit, table=1)
> +table=1 tcp actions=2
> +])
> +AT_CHECK([ovs-ofctl -O OpenFlow11 replace-flows br0 flows.txt])
> +
> +dnl Test frag expiry.
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 0014 0001 8192 40 06 3316 ac11370d ac11370b"])
> +ovs-appctl time/warp 10000
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 0014 0001 8192 40 06 3316 ac11370d ac11370b"])
> +
> +dnl Test that no packets flow.
> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\) | sed 
> s'/recirc(.*)/recirc(X)/'], [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=later),
>  packets:0, bytes:0, used:never, actions:ct(commit),recirc(X)
> +])
> +
> +dnl Expire second frag.
> +ovs-appctl time/warp 10000
> +
> +dnl Test frag purge
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 0014 0002 8192 40 06 3315 ac11370d ac11370b"])
> +ovs-appctl time/warp 33000
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 0014 0003 8192 40 06 3314 ac11370d ac11370b"])
> +
> +dnl Test that no packets flow.
> +AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\) | sed 
> s'/recirc(.*)/recirc(X)/'], [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=later),
>  packets:0, bytes:0, used:never, actions:ct(commit),recirc(X)
> +])
> +
> +dnl Purge second frag
> +ovs-appctl time/warp 33000
> +
> +dnl Make sure all four packets are counted properly in the coverage.
> +AT_CHECK([ovs-appctl coverage/show | grep -c "^ipf.*total: 2"], [0], [2
> +])
> +
> +zero1208=$(printf '0%.0s' $(seq 2416))
> +dnl Test that reassembled packets flow.
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 04c4 0002 2000 40 06 8ff7 ac11370d ac11370b dnl
> +0000 0001 00000000 00000000 50 10 8000 604c 0000 dnl
> +$zero1208"])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 
> 0800 4500 04c4 0002 0096 40 06 af61 ac11370d ac11370b dnl
> +$zero1208"])
> +
> +AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/recirc[[^)]]*)/recirc()/g' | 
> strip_used | sort], [0], [dnl
> +flow-dump from the main thread:
> +recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),
>  packets:0, bytes:0, used:never, actions:2
> +recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),
>  packets:0, bytes:0, used:never, actions:ct(commit),recirc()
> +recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later),
>  packets:0, bytes:0, used:never, actions:2
> +recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later),
>  packets:0, bytes:0, used:never, actions:ct(commit),recirc()
> +])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  AT_SETUP([ofproto-dpif - handling of malformed TCP packets])
>  OVS_VSWITCHD_START
>  add_of_ports br0 1 90

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to