diff mbox series

[ovs-dev,v5] conntrack: support default timeout policy get/set cmd for netdev datapath

Message ID 1639470499-28982-1-git-send-email-wenxu@ucloud.cn
State Changes Requested
Headers show
Series [ovs-dev,v5] conntrack: support default timeout policy get/set cmd for netdev datapath | expand

Checks

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

Commit Message

wenxu Dec. 14, 2021, 8:28 a.m. UTC
From: wenxu <wenxu@ucloud.cn>

Now, the default timeout policy for netdev datapath is hard codeing. In
some case show or modify is needed.
Add command for get/set default timeout policy. Using like this:

ovs-appctl dpctl/ct-get-default-tp [dp]
ovs-appctl dpctl/ct-set-default-tp [dp] policies

Signed-off-by: wenxu <wenxu@ucloud.cn>
---
 NEWS                             |   4 ++
 lib/conntrack-tp.c               |  45 +---------------
 lib/ct-dpif.c                    | 107 +++++++++++++++++++++++++++++++++++++++
 lib/ct-dpif.h                    |  12 +++++
 lib/dpctl.c                      |  70 +++++++++++++++++++++++++
 lib/dpif-netdev.c                |  20 ++++++++
 lib/dpif-netlink.c               |   2 +
 lib/dpif-provider.h              |   7 +++
 tests/system-kmod-macros.at      |  10 ++++
 tests/system-traffic.at          |  67 ++++++++++++++++++++++++
 tests/system-userspace-macros.at |   7 +++
 11 files changed, 308 insertions(+), 43 deletions(-)

Comments

Paolo Valerio Jan. 7, 2022, 4:31 p.m. UTC | #1
wenxu@ucloud.cn writes:

> From: wenxu <wenxu@ucloud.cn>
>
> Now, the default timeout policy for netdev datapath is hard codeing. In
> some case show or modify is needed.
> Add command for get/set default timeout policy. Using like this:
>
> ovs-appctl dpctl/ct-get-default-tp [dp]
> ovs-appctl dpctl/ct-set-default-tp [dp] policies
>
> Signed-off-by: wenxu <wenxu@ucloud.cn>
> ---
>  NEWS                             |   4 ++
>  lib/conntrack-tp.c               |  45 +---------------
>  lib/ct-dpif.c                    | 107 +++++++++++++++++++++++++++++++++++++++
>  lib/ct-dpif.h                    |  12 +++++
>  lib/dpctl.c                      |  70 +++++++++++++++++++++++++
>  lib/dpif-netdev.c                |  20 ++++++++
>  lib/dpif-netlink.c               |   2 +
>  lib/dpif-provider.h              |   7 +++
>  tests/system-kmod-macros.at      |  10 ++++
>  tests/system-traffic.at          |  67 ++++++++++++++++++++++++
>  tests/system-userspace-macros.at |   7 +++
>  11 files changed, 308 insertions(+), 43 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 2a4856c..dea1044 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -23,6 +23,10 @@ Post-v2.16.0
>       * Default selection method for select groups with up to 256 buckets is
>         now dp_hash.  Previously this was limited to 64 buckets.  This change
>         is mainly for the benefit of OVN load balancing configurations.
> +   - ovs-appctl dpctl/:
> +     * New commands 'ct-set-default-tp' and
> +       'ct-set-default-tp' that allows to get or configure
> +       netdev datapath ct default timeout policy.
>  
>  
>  v2.16.0 - 16 Aug 2021
> diff --git a/lib/conntrack-tp.c b/lib/conntrack-tp.c
> index a586d3a..9988327 100644
> --- a/lib/conntrack-tp.c
> +++ b/lib/conntrack-tp.c
> @@ -25,12 +25,6 @@
>  VLOG_DEFINE_THIS_MODULE(conntrack_tp);
>  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>  
> -static const char *ct_timeout_str[] = {
> -#define CT_TIMEOUT(NAME) #NAME,
> -    CT_TIMEOUTS
> -#undef CT_TIMEOUT
> -};
> -
>  /* Default timeout policy in seconds. */
>  static unsigned int ct_dpif_netdev_tp_def[] = {
>      [CT_DPIF_TP_ATTR_TCP_SYN_SENT] = 30,
> @@ -195,41 +189,6 @@ timeout_policy_update(struct conntrack *ct,
>      return err;
>  }
>  
> -static enum ct_dpif_tp_attr
> -tm_to_ct_dpif_tp(enum ct_timeout tm)
> -{
> -    switch (tm) {
> -    case CT_TM_TCP_FIRST_PACKET:
> -        return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
> -    case CT_TM_TCP_OPENING:
> -        return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
> -    case CT_TM_TCP_ESTABLISHED:
> -        return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
> -    case CT_TM_TCP_CLOSING:
> -        return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
> -    case CT_TM_TCP_FIN_WAIT:
> -        return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
> -    case CT_TM_TCP_CLOSED:
> -        return CT_DPIF_TP_ATTR_TCP_CLOSE;
> -    case CT_TM_OTHER_FIRST:
> -        return CT_DPIF_TP_ATTR_UDP_FIRST;
> -    case CT_TM_OTHER_BIDIR:
> -        return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
> -    case CT_TM_OTHER_MULTIPLE:
> -        return CT_DPIF_TP_ATTR_UDP_SINGLE;
> -    case CT_TM_ICMP_FIRST:
> -        return CT_DPIF_TP_ATTR_ICMP_FIRST;
> -    case CT_TM_ICMP_REPLY:
> -        return CT_DPIF_TP_ATTR_ICMP_REPLY;
> -    case N_CT_TM:
> -    default:
> -        OVS_NOT_REACHED();
> -        break;
> -    }
> -    OVS_NOT_REACHED();
> -    return CT_DPIF_TP_ATTR_MAX;
> -}
> -

I don't think this should be moved under lib/ct-dpif.c, it seems
implementation specific.

>  static void
>  conn_update_expiration__(struct conntrack *ct, struct conn *conn,
>                           enum ct_timeout tm, long long now,
> @@ -276,7 +235,7 @@ conn_update_expiration(struct conntrack *ct, struct conn *conn,
>      ovs_mutex_lock(&conn->lock);
>      VLOG_DBG_RL(&rl, "Update timeout %s zone=%u with policy id=%d "
>                  "val=%u sec.",
> -                ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
> +                ct_dpif_timeout_string[tm], conn->key.zone, conn->tp_id, val);
>  
>      conn_update_expiration__(ct, conn, tm, now, val);
>  }
> @@ -307,7 +266,7 @@ conn_init_expiration(struct conntrack *ct, struct conn *conn,
>      }
>  
>      VLOG_DBG_RL(&rl, "Init timeout %s zone=%u with policy id=%d val=%u sec.",
> -                ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
> +                ct_dpif_timeout_string[tm], conn->key.zone, conn->tp_id, val);
>  
>      conn_init_expiration__(ct, conn, tm, now, val);
>  }
> diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
> index cfc2315..986f7fb 100644
> --- a/lib/ct-dpif.c
> +++ b/lib/ct-dpif.c
> @@ -20,6 +20,7 @@
>  #include <errno.h>
>  
>  #include "ct-dpif.h"
> +#include "conntrack-private.h"

I see the reason you include this header here, but this doesn't seem to
be ok. CT private headers should not be included here. The same applies
for the code moved from conntrack here.

You can, e.g. (not really suggestions, but some examples to clarify the
point), return the formatted ds up to the dpif (which means that each
datapath may potentially format it differently, although only netdev dp
is supported here), or return the data in a generic way and not
dependent on the datapath. In any case, coupling dpif with ct-private
should be avoided.

>  #include "openvswitch/ofp-parse.h"
>  #include "openvswitch/vlog.h"
>  
> @@ -180,6 +181,24 @@ ct_dpif_get_tcp_seq_chk(struct dpif *dpif, bool *enabled)
>  }
>  
>  int
> +ct_dpif_set_default_timeout_policy(struct dpif *dpif,
> +                                   struct ct_dpif_timeout_policy *tp)
> +{
> +    return (dpif->dpif_class->ct_set_timeout_policy
> +            ? dpif->dpif_class->ct_set_default_timeout_policy(dpif, tp)
> +            : EOPNOTSUPP);
> +}
> +
> +int
> +ct_dpif_get_default_timeout_policy(struct dpif *dpif,
> +                                   struct ct_dpif_timeout_policy *tp)
> +{
> +    return (dpif->dpif_class->ct_get_timeout_policy
> +            ? dpif->dpif_class->ct_get_default_timeout_policy(dpif, tp)
> +            : EOPNOTSUPP);
> +}

for both ct_dpif_{set,get}_default_timeout_policy I think you should
check against dpif->dpif_class->ct_{set,get}_default_timeout_policy
instead of dpif->dpif_class->ct_{get,set}_timeout_policy, right?

> +
> +int
>  ct_dpif_set_limits(struct dpif *dpif, const uint32_t *default_limit,
>                     const struct ovs_list *zone_limits)
>  {
> @@ -710,6 +729,43 @@ ct_dpif_free_zone_limits(struct ovs_list *zone_limits)
>      }
>  }
>  
> +
> +/* Parses a specification of a timeout policy from 's' into '*tp'
> + * .  Returns true on success.  Otherwise, returns false and
> + * and puts the error message in 'ds'. */
> +bool
> +ct_dpif_parse_timeout_policy_tuple(const char *s, struct ds *ds,
> +                                   struct ct_dpif_timeout_policy *tp)
> +{
> +    char *pos, *key, *value, *copy, *err;
> +
> +    pos = copy = xstrdup(s);
> +    while (ofputil_parse_key_value(&pos, &key, &value)) {
> +        uint32_t tmp;
> +
> +        if (!*value) {
> +            ds_put_format(ds, "field %s missing value", key);
> +            goto error;
> +        }
> +
> +        err = str_to_u32(value, &tmp);
> +        if (err) {
> +          free(err);
> +          goto error_with_msg;
> +        }
> +
> +        ct_dpif_set_timeout_policy_attr_by_name(tp, key, tmp);
> +    }
> +    free(copy);
> +
> +    return true;
> +
> +error_with_msg:
> +    ds_put_format(ds, "failed to parse field %s", key);
> +error:
> +    free(copy);
> +    return false;
> +}
>  /* Parses a specification of a conntrack zone limit from 's' into '*pzone'
>   * and '*plimit'.  Returns true on success.  Otherwise, returns false and
>   * and puts the error message in 'ds'. */
> @@ -792,6 +848,57 @@ static const char *const ct_dpif_tp_attr_string[] = {
>  #undef CT_DPIF_TP_ICMP_ATTR
>  };
>  
> +enum ct_dpif_tp_attr
> +tm_to_ct_dpif_tp(enum ct_timeout tm)
> +{
> +    switch (tm) {
> +    case CT_TM_TCP_FIRST_PACKET:
> +        return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
> +    case CT_TM_TCP_OPENING:
> +        return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
> +    case CT_TM_TCP_ESTABLISHED:
> +        return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
> +    case CT_TM_TCP_CLOSING:
> +        return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
> +    case CT_TM_TCP_FIN_WAIT:
> +        return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
> +    case CT_TM_TCP_CLOSED:
> +        return CT_DPIF_TP_ATTR_TCP_CLOSE;
> +    case CT_TM_OTHER_FIRST:
> +        return CT_DPIF_TP_ATTR_UDP_FIRST;
> +    case CT_TM_OTHER_BIDIR:
> +        return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
> +    case CT_TM_OTHER_MULTIPLE:
> +        return CT_DPIF_TP_ATTR_UDP_SINGLE;
> +    case CT_TM_ICMP_FIRST:
> +        return CT_DPIF_TP_ATTR_ICMP_FIRST;
> +    case CT_TM_ICMP_REPLY:
> +        return CT_DPIF_TP_ATTR_ICMP_REPLY;
> +    case N_CT_TM:
> +    default:
> +        OVS_NOT_REACHED();
> +        break;
> +    }
> +    OVS_NOT_REACHED();
> +    return CT_DPIF_TP_ATTR_MAX;
> +}
> +
> +const char *ct_dpif_timeout_string[] = {
> +#define CT_TIMEOUT(NAME) #NAME,
> +    CT_TIMEOUTS
> +#undef CT_TIMEOUT
> +};
> +
> +void
> +ct_dpif_format_timeout_policy(const struct ct_dpif_timeout_policy *tp,
> +                              struct ds *ds)
> +{
> +    for (unsigned i = 0; i < N_CT_TM; i++) {
> +        ds_put_format(ds, "\n\t%s = %"PRIu32, ct_dpif_timeout_string[i],
> +                      tp->attrs[tm_to_ct_dpif_tp(i)]);
> +    }
> +}
> +
>  static bool
>  ct_dpif_set_timeout_policy_attr(struct ct_dpif_timeout_policy *tp,
>                                  uint32_t attr, uint32_t value)
> diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
> index b59cba9..5a03883 100644
> --- a/lib/ct-dpif.h
> +++ b/lib/ct-dpif.h
> @@ -271,6 +271,8 @@ struct ct_dpif_timeout_policy {
>                                                   * timeout attribute values */
>  };
>  
> +extern const char *ct_dpif_timeout_string[];
> +
>  /* Conntrack Features. */
>  enum ct_features {
>      CONNTRACK_F_ZERO_SNAT = 1 << 0,  /* All-zero SNAT support. */
> @@ -292,6 +294,12 @@ int ct_dpif_set_limits(struct dpif *dpif, const uint32_t *default_limit,
>  int ct_dpif_get_limits(struct dpif *dpif, uint32_t *default_limit,
>                         const struct ovs_list *, struct ovs_list *);
>  int ct_dpif_del_limits(struct dpif *dpif, const struct ovs_list *);
> +int ct_dpif_set_default_timeout_policy(struct dpif *dpif,
> +                                       struct ct_dpif_timeout_policy *);
> +int ct_dpif_get_default_timeout_policy(struct dpif *dpif,
> +                                       struct ct_dpif_timeout_policy *tp);
> +bool ct_dpif_parse_timeout_policy_tuple(const char *s, struct ds *ds,
> +                                        struct ct_dpif_timeout_policy *);
>  int ct_dpif_ipf_set_enabled(struct dpif *, bool v6, bool enable);
>  int ct_dpif_ipf_set_min_frag(struct dpif *, bool v6, uint32_t min_frag);
>  int ct_dpif_ipf_set_max_nfrags(struct dpif *, uint32_t max_frags);
> @@ -315,6 +323,10 @@ bool ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
>                                      uint32_t *plimit, struct ds *);
>  void ct_dpif_format_zone_limits(uint32_t default_limit,
>                                  const struct ovs_list *, struct ds *);
> +void ct_dpif_format_timeout_policy(const struct ct_dpif_timeout_policy *tp,
> +                                   struct ds *ds);
> +enum ct_timeout;
> +enum ct_dpif_tp_attr tm_to_ct_dpif_tp(enum ct_timeout tm);
>  bool ct_dpif_set_timeout_policy_attr_by_name(struct ct_dpif_timeout_policy *tp,
>                                               const char *key, uint32_t value);
>  bool ct_dpif_timeout_policy_support_ipproto(uint8_t ipproto);
> diff --git a/lib/dpctl.c b/lib/dpctl.c
> index 1ba1a96..13118a8 100644
> --- a/lib/dpctl.c
> +++ b/lib/dpctl.c
> @@ -2074,6 +2074,72 @@ dpctl_ct_get_tcp_seq_chk(int argc, const char *argv[],
>  }
>  
>  static int
> +dpctl_ct_set_default_timeout_policy(int argc, const char *argv[],
> +                                    struct dpctl_params *dpctl_p)
> +{
> +    int i =  dp_arg_exists(argc, argv) ? 2 : 1;
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    struct ct_dpif_timeout_policy tp;
> +    struct dpif *dpif;
> +
> +    int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
> +    if (error) {
> +        return error;
> +    }
> +
> +    memset(&tp, 0, sizeof tp);
> +    tp.id = DEFAULT_TP_ID;
> +
> +    /* Parse timeout policy tuples */
> +    if (!ct_dpif_parse_timeout_policy_tuple(argv[i], &ds, &tp)) {
> +        error = EINVAL;
> +        goto error;
> +    }
> +
> +    error = ct_dpif_set_default_timeout_policy(dpif, &tp);
> +    if (!error) {
> +        dpif_close(dpif);
> +        return 0;
> +    } else {
> +        ds_put_cstr(&ds, "failed to set timeout policy");
> +    }
> +
> +error:
> +    dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
> +    ds_destroy(&ds);
> +    dpif_close(dpif);
> +    return error;
> +}
> +
> +static int
> +dpctl_ct_get_default_timeout_policy(int argc, const char *argv[],
> +                                    struct dpctl_params *dpctl_p)
> +{
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    struct ct_dpif_timeout_policy tp;
> +    struct dpif *dpif;
> +
> +    int error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif);
> +    if (error) {
> +        return error;
> +    }
> +
> +    error = ct_dpif_get_default_timeout_policy(dpif, &tp);
> +    if (!error) {
> +        ds_put_format(&ds, "default timeout policy (s): ");
> +        ct_dpif_format_timeout_policy(&tp, &ds);
> +        dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
> +    } else {
> +        ds_put_format(&ds, "failed to get conntrack timeout policy %s",
> +                      ovs_strerror(error));

does this get printed?

> +    }
> +
> +    ds_destroy(&ds);
> +    dpif_close(dpif);
> +    return error;
> +}
> +
> +static int
>  dpctl_ct_set_limits(int argc, const char *argv[],
>                      struct dpctl_params *dpctl_p)
>  {
> @@ -2842,6 +2908,10 @@ static const struct dpctl_command all_commands[] = {
>      { "ct-disable-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_disable_tcp_seq_chk,
>         DP_RW },
>      { "ct-get-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_get_tcp_seq_chk, DP_RO },
> +    { "ct-set-default-tp", "[dp]", 1, 2,
> +       dpctl_ct_set_default_timeout_policy, DP_RW },
> +    { "ct-get-default-tp", "[dp]", 0, 1,
> +       dpctl_ct_get_default_timeout_policy, DP_RO },
>      { "ct-set-limits", "[dp] [default=L] [zone=N,limit=L]...", 1, INT_MAX,
>          dpctl_ct_set_limits, DP_RO },
>      { "ct-del-limits", "[dp] zone=N1[,N2]...", 1, 2, dpctl_ct_del_limits,
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index a790df5..dffac54 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -8615,6 +8615,24 @@ dpif_netdev_ct_get_timeout_policy_name(struct dpif *dpif OVS_UNUSED,
>  }
>  
>  static int
> +dpif_netdev_ct_set_default_timeout_policy(struct dpif *dpif,
> +                                          struct ct_dpif_timeout_policy *tp)
> +{
> +    if (tp->id != DEFAULT_TP_ID) {
> +        return EINVAL;
> +    }

or alternatively, you can set the default ID here instead of doing it
from the caller.

> +
> +    return dpif_netdev_ct_set_timeout_policy(dpif, tp);
> +}
> +
> +static int
> +dpif_netdev_ct_get_default_timeout_policy(struct dpif *dpif,
> +                                          struct ct_dpif_timeout_policy *tp)
> +{
> +    return dpif_netdev_ct_get_timeout_policy(dpif, DEFAULT_TP_ID, tp);
> +}
> +
> +static int
>  dpif_netdev_ipf_set_enabled(struct dpif *dpif, bool v6, bool enable)
>  {
>      struct dp_netdev *dp = get_dp_netdev(dpif);
> @@ -8824,6 +8842,8 @@ const struct dpif_class dpif_netdev_class = {
>      NULL,                       /* ct_timeout_policy_dump_next */
>      NULL,                       /* ct_timeout_policy_dump_done */
>      dpif_netdev_ct_get_timeout_policy_name,
> +    dpif_netdev_ct_set_default_timeout_policy,
> +    dpif_netdev_ct_get_default_timeout_policy,
>      dpif_netdev_ct_get_features,
>      dpif_netdev_ipf_set_enabled,
>      dpif_netdev_ipf_set_min_frag,
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index 424a284..39c559a 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -4460,6 +4460,8 @@ const struct dpif_class dpif_netlink_class = {
>      dpif_netlink_ct_timeout_policy_dump_next,
>      dpif_netlink_ct_timeout_policy_dump_done,
>      dpif_netlink_ct_get_timeout_policy_name,
> +    NULL,                       /* ct_set_default_timeout_policy */
> +    NULL,                       /* ct_get_default_timeout_policy */
>      dpif_netlink_ct_get_features,
>      NULL,                       /* ipf_set_enabled */
>      NULL,                       /* ipf_set_min_frag */
> diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
> index 27e3a76..e8be2bb 100644
> --- a/lib/dpif-provider.h
> +++ b/lib/dpif-provider.h
> @@ -565,6 +565,13 @@ struct dpif_class {
>                                        uint16_t dl_type, uint8_t nw_proto,
>                                        char **tp_name, bool *is_generic);
>  
> +    /* Sets default timeout policy '*tp' into the datapath. */
> +    int (*ct_set_default_timeout_policy)(struct dpif *,
> +                                         struct ct_dpif_timeout_policy *);
> +    /* Gets the default timeout policy and stores it into '*tp'. */
> +    int (*ct_get_default_timeout_policy)(struct dpif *,
> +                                         struct ct_dpif_timeout_policy *);
> +
>      /* Stores the conntrack features supported by 'dpif' into features.
>       * The value is a bitmap of CONNTRACK_F_* bits. */
>      int (*ct_get_features)(struct dpif *, enum ct_features *features);
> diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
> index 86d633a..21fb259 100644
> --- a/tests/system-kmod-macros.at
> +++ b/tests/system-kmod-macros.at
> @@ -121,6 +121,16 @@ m4_define([CHECK_CONNTRACK_TIMEOUT],
>      on_exit 'modprobe -r nfnetlink_cttimeout'
>  ])
>  
> +# CHECK_CONNTRACK_DEFAULT_TIMEOUT()
> +#
> +# Perform requirements checks for running ovs-dpctl ct-set-default-tp or
> +# ovs-dpctl ct-get-default-tp. The kernel datapath does not support this
> +# feature.
> +m4_define([CHECK_CONNTRACK_DEFAULT_TIMEOUT],
> +[
> +    AT_SKIP_IF([:])
> +])
> +
>  # CHECK_CT_DPIF_SET_GET_MAXCONNS()
>  #
>  # Perform requirements checks for running ovs-dpctl ct-set-maxconns or
> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> index d79753a..6744ce2 100644
> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -3632,6 +3632,73 @@ udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
>  
> +AT_SETUP([conntrack - default timeout policy])
> +CHECK_CONNTRACK()
> +CHECK_CONNTRACK_DEFAULT_TIMEOUT()
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +ADD_NAMESPACES(at_ns0, at_ns1)
> +
> +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
> +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
> +
> +AT_DATA([flows.txt], [dnl
> +priority=1,action=drop
> +priority=10,arp,action=normal
> +priority=100,in_port=1,ip,action=ct(zone=5, table=1)
> +priority=100,in_port=2,ip,action=ct(zone=5, table=1)
> +table=1,in_port=2,ip,ct_state=+trk+est,action=1
> +table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit,zone=5),2
> +table=1,in_port=1,ip,ct_state=+trk+est,action=2
> +])
> +
> +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
> +
> +dnl Test with origin default timeout
> +
> +dnl Send ICMP and UDP traffic
> +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
> +
> +sleep 4
> +
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl
> +icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=0,code=0),zone=5
> +udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),zone=5
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> +
> +dnl Shorten the udp_first udp_single and
> +dnl icmp_first icmp_reply default timeout
> +VSCTL_ADD_DATAPATH_TABLE()
> +
> +dnl Modifing default timeout policies
> +AT_CHECK([ovs-appctl dpctl/ct-set-default-tp "udp_first=1,udp_single=1,icmp_first=1,icmp_reply=1"])
> +
> +dnl Send ICMP and UDP traffic
> +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
> +
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl
> +icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=0,code=0),zone=5
> +udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),zone=5
> +])
> +
> +dnl Wait until the timeout expire.
> +dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired.
> +sleep 6
> +
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
> +])
> +
> +OVS_TRAFFIC_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  dnl Check kernel datapath to make sure conntrack fills in L3 and L4
>  dnl protocol information
>  AT_SETUP([conntrack - fragment reassembly with L3 L4 protocol information])
> diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
> index f639ba5..acd9643 100644
> --- a/tests/system-userspace-macros.at
> +++ b/tests/system-userspace-macros.at
> @@ -110,6 +110,13 @@ m4_define([CHECK_CONNTRACK_ZEROIP_SNAT])
>  #
>  m4_define([CHECK_CONNTRACK_TIMEOUT])
>  
> +# CHECK_CONNTRACK_DEFAULT_TIMEOUT()
> +#
> +# Perform requirements checks for running conntrack customized
> +# default timeout tests.
> +#
> +m4_define([CHECK_CONNTRACK_DEFAULT_TIMEOUT])
> +
>  # CHECK_CT_DPIF_SET_GET_MAXCONNS()
>  #
>  # Perform requirements checks for running ovs-dpctl ct-set-maxconns or
> -- 
> 1.8.3.1
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 2a4856c..dea1044 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,10 @@  Post-v2.16.0
      * Default selection method for select groups with up to 256 buckets is
        now dp_hash.  Previously this was limited to 64 buckets.  This change
        is mainly for the benefit of OVN load balancing configurations.
+   - ovs-appctl dpctl/:
+     * New commands 'ct-set-default-tp' and
+       'ct-set-default-tp' that allows to get or configure
+       netdev datapath ct default timeout policy.
 
 
 v2.16.0 - 16 Aug 2021
diff --git a/lib/conntrack-tp.c b/lib/conntrack-tp.c
index a586d3a..9988327 100644
--- a/lib/conntrack-tp.c
+++ b/lib/conntrack-tp.c
@@ -25,12 +25,6 @@ 
 VLOG_DEFINE_THIS_MODULE(conntrack_tp);
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-static const char *ct_timeout_str[] = {
-#define CT_TIMEOUT(NAME) #NAME,
-    CT_TIMEOUTS
-#undef CT_TIMEOUT
-};
-
 /* Default timeout policy in seconds. */
 static unsigned int ct_dpif_netdev_tp_def[] = {
     [CT_DPIF_TP_ATTR_TCP_SYN_SENT] = 30,
@@ -195,41 +189,6 @@  timeout_policy_update(struct conntrack *ct,
     return err;
 }
 
-static enum ct_dpif_tp_attr
-tm_to_ct_dpif_tp(enum ct_timeout tm)
-{
-    switch (tm) {
-    case CT_TM_TCP_FIRST_PACKET:
-        return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
-    case CT_TM_TCP_OPENING:
-        return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
-    case CT_TM_TCP_ESTABLISHED:
-        return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
-    case CT_TM_TCP_CLOSING:
-        return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
-    case CT_TM_TCP_FIN_WAIT:
-        return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
-    case CT_TM_TCP_CLOSED:
-        return CT_DPIF_TP_ATTR_TCP_CLOSE;
-    case CT_TM_OTHER_FIRST:
-        return CT_DPIF_TP_ATTR_UDP_FIRST;
-    case CT_TM_OTHER_BIDIR:
-        return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
-    case CT_TM_OTHER_MULTIPLE:
-        return CT_DPIF_TP_ATTR_UDP_SINGLE;
-    case CT_TM_ICMP_FIRST:
-        return CT_DPIF_TP_ATTR_ICMP_FIRST;
-    case CT_TM_ICMP_REPLY:
-        return CT_DPIF_TP_ATTR_ICMP_REPLY;
-    case N_CT_TM:
-    default:
-        OVS_NOT_REACHED();
-        break;
-    }
-    OVS_NOT_REACHED();
-    return CT_DPIF_TP_ATTR_MAX;
-}
-
 static void
 conn_update_expiration__(struct conntrack *ct, struct conn *conn,
                          enum ct_timeout tm, long long now,
@@ -276,7 +235,7 @@  conn_update_expiration(struct conntrack *ct, struct conn *conn,
     ovs_mutex_lock(&conn->lock);
     VLOG_DBG_RL(&rl, "Update timeout %s zone=%u with policy id=%d "
                 "val=%u sec.",
-                ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
+                ct_dpif_timeout_string[tm], conn->key.zone, conn->tp_id, val);
 
     conn_update_expiration__(ct, conn, tm, now, val);
 }
@@ -307,7 +266,7 @@  conn_init_expiration(struct conntrack *ct, struct conn *conn,
     }
 
     VLOG_DBG_RL(&rl, "Init timeout %s zone=%u with policy id=%d val=%u sec.",
-                ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
+                ct_dpif_timeout_string[tm], conn->key.zone, conn->tp_id, val);
 
     conn_init_expiration__(ct, conn, tm, now, val);
 }
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index cfc2315..986f7fb 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -20,6 +20,7 @@ 
 #include <errno.h>
 
 #include "ct-dpif.h"
+#include "conntrack-private.h"
 #include "openvswitch/ofp-parse.h"
 #include "openvswitch/vlog.h"
 
@@ -180,6 +181,24 @@  ct_dpif_get_tcp_seq_chk(struct dpif *dpif, bool *enabled)
 }
 
 int
+ct_dpif_set_default_timeout_policy(struct dpif *dpif,
+                                   struct ct_dpif_timeout_policy *tp)
+{
+    return (dpif->dpif_class->ct_set_timeout_policy
+            ? dpif->dpif_class->ct_set_default_timeout_policy(dpif, tp)
+            : EOPNOTSUPP);
+}
+
+int
+ct_dpif_get_default_timeout_policy(struct dpif *dpif,
+                                   struct ct_dpif_timeout_policy *tp)
+{
+    return (dpif->dpif_class->ct_get_timeout_policy
+            ? dpif->dpif_class->ct_get_default_timeout_policy(dpif, tp)
+            : EOPNOTSUPP);
+}
+
+int
 ct_dpif_set_limits(struct dpif *dpif, const uint32_t *default_limit,
                    const struct ovs_list *zone_limits)
 {
@@ -710,6 +729,43 @@  ct_dpif_free_zone_limits(struct ovs_list *zone_limits)
     }
 }
 
+
+/* Parses a specification of a timeout policy from 's' into '*tp'
+ * .  Returns true on success.  Otherwise, returns false and
+ * and puts the error message in 'ds'. */
+bool
+ct_dpif_parse_timeout_policy_tuple(const char *s, struct ds *ds,
+                                   struct ct_dpif_timeout_policy *tp)
+{
+    char *pos, *key, *value, *copy, *err;
+
+    pos = copy = xstrdup(s);
+    while (ofputil_parse_key_value(&pos, &key, &value)) {
+        uint32_t tmp;
+
+        if (!*value) {
+            ds_put_format(ds, "field %s missing value", key);
+            goto error;
+        }
+
+        err = str_to_u32(value, &tmp);
+        if (err) {
+          free(err);
+          goto error_with_msg;
+        }
+
+        ct_dpif_set_timeout_policy_attr_by_name(tp, key, tmp);
+    }
+    free(copy);
+
+    return true;
+
+error_with_msg:
+    ds_put_format(ds, "failed to parse field %s", key);
+error:
+    free(copy);
+    return false;
+}
 /* Parses a specification of a conntrack zone limit from 's' into '*pzone'
  * and '*plimit'.  Returns true on success.  Otherwise, returns false and
  * and puts the error message in 'ds'. */
@@ -792,6 +848,57 @@  static const char *const ct_dpif_tp_attr_string[] = {
 #undef CT_DPIF_TP_ICMP_ATTR
 };
 
+enum ct_dpif_tp_attr
+tm_to_ct_dpif_tp(enum ct_timeout tm)
+{
+    switch (tm) {
+    case CT_TM_TCP_FIRST_PACKET:
+        return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
+    case CT_TM_TCP_OPENING:
+        return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
+    case CT_TM_TCP_ESTABLISHED:
+        return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
+    case CT_TM_TCP_CLOSING:
+        return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
+    case CT_TM_TCP_FIN_WAIT:
+        return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
+    case CT_TM_TCP_CLOSED:
+        return CT_DPIF_TP_ATTR_TCP_CLOSE;
+    case CT_TM_OTHER_FIRST:
+        return CT_DPIF_TP_ATTR_UDP_FIRST;
+    case CT_TM_OTHER_BIDIR:
+        return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
+    case CT_TM_OTHER_MULTIPLE:
+        return CT_DPIF_TP_ATTR_UDP_SINGLE;
+    case CT_TM_ICMP_FIRST:
+        return CT_DPIF_TP_ATTR_ICMP_FIRST;
+    case CT_TM_ICMP_REPLY:
+        return CT_DPIF_TP_ATTR_ICMP_REPLY;
+    case N_CT_TM:
+    default:
+        OVS_NOT_REACHED();
+        break;
+    }
+    OVS_NOT_REACHED();
+    return CT_DPIF_TP_ATTR_MAX;
+}
+
+const char *ct_dpif_timeout_string[] = {
+#define CT_TIMEOUT(NAME) #NAME,
+    CT_TIMEOUTS
+#undef CT_TIMEOUT
+};
+
+void
+ct_dpif_format_timeout_policy(const struct ct_dpif_timeout_policy *tp,
+                              struct ds *ds)
+{
+    for (unsigned i = 0; i < N_CT_TM; i++) {
+        ds_put_format(ds, "\n\t%s = %"PRIu32, ct_dpif_timeout_string[i],
+                      tp->attrs[tm_to_ct_dpif_tp(i)]);
+    }
+}
+
 static bool
 ct_dpif_set_timeout_policy_attr(struct ct_dpif_timeout_policy *tp,
                                 uint32_t attr, uint32_t value)
diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index b59cba9..5a03883 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -271,6 +271,8 @@  struct ct_dpif_timeout_policy {
                                                  * timeout attribute values */
 };
 
+extern const char *ct_dpif_timeout_string[];
+
 /* Conntrack Features. */
 enum ct_features {
     CONNTRACK_F_ZERO_SNAT = 1 << 0,  /* All-zero SNAT support. */
@@ -292,6 +294,12 @@  int ct_dpif_set_limits(struct dpif *dpif, const uint32_t *default_limit,
 int ct_dpif_get_limits(struct dpif *dpif, uint32_t *default_limit,
                        const struct ovs_list *, struct ovs_list *);
 int ct_dpif_del_limits(struct dpif *dpif, const struct ovs_list *);
+int ct_dpif_set_default_timeout_policy(struct dpif *dpif,
+                                       struct ct_dpif_timeout_policy *);
+int ct_dpif_get_default_timeout_policy(struct dpif *dpif,
+                                       struct ct_dpif_timeout_policy *tp);
+bool ct_dpif_parse_timeout_policy_tuple(const char *s, struct ds *ds,
+                                        struct ct_dpif_timeout_policy *);
 int ct_dpif_ipf_set_enabled(struct dpif *, bool v6, bool enable);
 int ct_dpif_ipf_set_min_frag(struct dpif *, bool v6, uint32_t min_frag);
 int ct_dpif_ipf_set_max_nfrags(struct dpif *, uint32_t max_frags);
@@ -315,6 +323,10 @@  bool ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
                                     uint32_t *plimit, struct ds *);
 void ct_dpif_format_zone_limits(uint32_t default_limit,
                                 const struct ovs_list *, struct ds *);
+void ct_dpif_format_timeout_policy(const struct ct_dpif_timeout_policy *tp,
+                                   struct ds *ds);
+enum ct_timeout;
+enum ct_dpif_tp_attr tm_to_ct_dpif_tp(enum ct_timeout tm);
 bool ct_dpif_set_timeout_policy_attr_by_name(struct ct_dpif_timeout_policy *tp,
                                              const char *key, uint32_t value);
 bool ct_dpif_timeout_policy_support_ipproto(uint8_t ipproto);
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 1ba1a96..13118a8 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -2074,6 +2074,72 @@  dpctl_ct_get_tcp_seq_chk(int argc, const char *argv[],
 }
 
 static int
+dpctl_ct_set_default_timeout_policy(int argc, const char *argv[],
+                                    struct dpctl_params *dpctl_p)
+{
+    int i =  dp_arg_exists(argc, argv) ? 2 : 1;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct ct_dpif_timeout_policy tp;
+    struct dpif *dpif;
+
+    int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+    if (error) {
+        return error;
+    }
+
+    memset(&tp, 0, sizeof tp);
+    tp.id = DEFAULT_TP_ID;
+
+    /* Parse timeout policy tuples */
+    if (!ct_dpif_parse_timeout_policy_tuple(argv[i], &ds, &tp)) {
+        error = EINVAL;
+        goto error;
+    }
+
+    error = ct_dpif_set_default_timeout_policy(dpif, &tp);
+    if (!error) {
+        dpif_close(dpif);
+        return 0;
+    } else {
+        ds_put_cstr(&ds, "failed to set timeout policy");
+    }
+
+error:
+    dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
+    dpif_close(dpif);
+    return error;
+}
+
+static int
+dpctl_ct_get_default_timeout_policy(int argc, const char *argv[],
+                                    struct dpctl_params *dpctl_p)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct ct_dpif_timeout_policy tp;
+    struct dpif *dpif;
+
+    int error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif);
+    if (error) {
+        return error;
+    }
+
+    error = ct_dpif_get_default_timeout_policy(dpif, &tp);
+    if (!error) {
+        ds_put_format(&ds, "default timeout policy (s): ");
+        ct_dpif_format_timeout_policy(&tp, &ds);
+        dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+    } else {
+        ds_put_format(&ds, "failed to get conntrack timeout policy %s",
+                      ovs_strerror(error));
+    }
+
+    ds_destroy(&ds);
+    dpif_close(dpif);
+    return error;
+}
+
+static int
 dpctl_ct_set_limits(int argc, const char *argv[],
                     struct dpctl_params *dpctl_p)
 {
@@ -2842,6 +2908,10 @@  static const struct dpctl_command all_commands[] = {
     { "ct-disable-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_disable_tcp_seq_chk,
        DP_RW },
     { "ct-get-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_get_tcp_seq_chk, DP_RO },
+    { "ct-set-default-tp", "[dp]", 1, 2,
+       dpctl_ct_set_default_timeout_policy, DP_RW },
+    { "ct-get-default-tp", "[dp]", 0, 1,
+       dpctl_ct_get_default_timeout_policy, DP_RO },
     { "ct-set-limits", "[dp] [default=L] [zone=N,limit=L]...", 1, INT_MAX,
         dpctl_ct_set_limits, DP_RO },
     { "ct-del-limits", "[dp] zone=N1[,N2]...", 1, 2, dpctl_ct_del_limits,
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index a790df5..dffac54 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -8615,6 +8615,24 @@  dpif_netdev_ct_get_timeout_policy_name(struct dpif *dpif OVS_UNUSED,
 }
 
 static int
+dpif_netdev_ct_set_default_timeout_policy(struct dpif *dpif,
+                                          struct ct_dpif_timeout_policy *tp)
+{
+    if (tp->id != DEFAULT_TP_ID) {
+        return EINVAL;
+    }
+
+    return dpif_netdev_ct_set_timeout_policy(dpif, tp);
+}
+
+static int
+dpif_netdev_ct_get_default_timeout_policy(struct dpif *dpif,
+                                          struct ct_dpif_timeout_policy *tp)
+{
+    return dpif_netdev_ct_get_timeout_policy(dpif, DEFAULT_TP_ID, tp);
+}
+
+static int
 dpif_netdev_ipf_set_enabled(struct dpif *dpif, bool v6, bool enable)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
@@ -8824,6 +8842,8 @@  const struct dpif_class dpif_netdev_class = {
     NULL,                       /* ct_timeout_policy_dump_next */
     NULL,                       /* ct_timeout_policy_dump_done */
     dpif_netdev_ct_get_timeout_policy_name,
+    dpif_netdev_ct_set_default_timeout_policy,
+    dpif_netdev_ct_get_default_timeout_policy,
     dpif_netdev_ct_get_features,
     dpif_netdev_ipf_set_enabled,
     dpif_netdev_ipf_set_min_frag,
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 424a284..39c559a 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -4460,6 +4460,8 @@  const struct dpif_class dpif_netlink_class = {
     dpif_netlink_ct_timeout_policy_dump_next,
     dpif_netlink_ct_timeout_policy_dump_done,
     dpif_netlink_ct_get_timeout_policy_name,
+    NULL,                       /* ct_set_default_timeout_policy */
+    NULL,                       /* ct_get_default_timeout_policy */
     dpif_netlink_ct_get_features,
     NULL,                       /* ipf_set_enabled */
     NULL,                       /* ipf_set_min_frag */
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 27e3a76..e8be2bb 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -565,6 +565,13 @@  struct dpif_class {
                                       uint16_t dl_type, uint8_t nw_proto,
                                       char **tp_name, bool *is_generic);
 
+    /* Sets default timeout policy '*tp' into the datapath. */
+    int (*ct_set_default_timeout_policy)(struct dpif *,
+                                         struct ct_dpif_timeout_policy *);
+    /* Gets the default timeout policy and stores it into '*tp'. */
+    int (*ct_get_default_timeout_policy)(struct dpif *,
+                                         struct ct_dpif_timeout_policy *);
+
     /* Stores the conntrack features supported by 'dpif' into features.
      * The value is a bitmap of CONNTRACK_F_* bits. */
     int (*ct_get_features)(struct dpif *, enum ct_features *features);
diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
index 86d633a..21fb259 100644
--- a/tests/system-kmod-macros.at
+++ b/tests/system-kmod-macros.at
@@ -121,6 +121,16 @@  m4_define([CHECK_CONNTRACK_TIMEOUT],
     on_exit 'modprobe -r nfnetlink_cttimeout'
 ])
 
+# CHECK_CONNTRACK_DEFAULT_TIMEOUT()
+#
+# Perform requirements checks for running ovs-dpctl ct-set-default-tp or
+# ovs-dpctl ct-get-default-tp. The kernel datapath does not support this
+# feature.
+m4_define([CHECK_CONNTRACK_DEFAULT_TIMEOUT],
+[
+    AT_SKIP_IF([:])
+])
+
 # CHECK_CT_DPIF_SET_GET_MAXCONNS()
 #
 # Perform requirements checks for running ovs-dpctl ct-set-maxconns or
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index d79753a..6744ce2 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -3632,6 +3632,73 @@  udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([conntrack - default timeout policy])
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_DEFAULT_TIMEOUT()
+OVS_TRAFFIC_VSWITCHD_START()
+
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+
+AT_DATA([flows.txt], [dnl
+priority=1,action=drop
+priority=10,arp,action=normal
+priority=100,in_port=1,ip,action=ct(zone=5, table=1)
+priority=100,in_port=2,ip,action=ct(zone=5, table=1)
+table=1,in_port=2,ip,ct_state=+trk+est,action=1
+table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit,zone=5),2
+table=1,in_port=1,ip,ct_state=+trk+est,action=2
+])
+
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+
+dnl Test with origin default timeout
+
+dnl Send ICMP and UDP traffic
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
+
+sleep 4
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl
+icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=0,code=0),zone=5
+udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),zone=5
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+dnl Shorten the udp_first udp_single and
+dnl icmp_first icmp_reply default timeout
+VSCTL_ADD_DATAPATH_TABLE()
+
+dnl Modifing default timeout policies
+AT_CHECK([ovs-appctl dpctl/ct-set-default-tp "udp_first=1,udp_single=1,icmp_first=1,icmp_reply=1"])
+
+dnl Send ICMP and UDP traffic
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl
+icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=0,code=0),zone=5
+udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),zone=5
+])
+
+dnl Wait until the timeout expire.
+dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired.
+sleep 6
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl Check kernel datapath to make sure conntrack fills in L3 and L4
 dnl protocol information
 AT_SETUP([conntrack - fragment reassembly with L3 L4 protocol information])
diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
index f639ba5..acd9643 100644
--- a/tests/system-userspace-macros.at
+++ b/tests/system-userspace-macros.at
@@ -110,6 +110,13 @@  m4_define([CHECK_CONNTRACK_ZEROIP_SNAT])
 #
 m4_define([CHECK_CONNTRACK_TIMEOUT])
 
+# CHECK_CONNTRACK_DEFAULT_TIMEOUT()
+#
+# Perform requirements checks for running conntrack customized
+# default timeout tests.
+#
+m4_define([CHECK_CONNTRACK_DEFAULT_TIMEOUT])
+
 # CHECK_CT_DPIF_SET_GET_MAXCONNS()
 #
 # Perform requirements checks for running ovs-dpctl ct-set-maxconns or