diff mbox series

[ovs-dev,v2] OpenFlow: Add extn to set conntrack entries limit per zone.

Message ID 20230314105155.12571-1-naveen.yerramneni@nutanix.com
State Superseded
Headers show
Series [ovs-dev,v2] OpenFlow: Add extn to set conntrack entries limit per zone. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Naveen Yerramneni March 14, 2023, 10:51 a.m. UTC
Add OpenFlow extn to set conntrack entries limit per zone.

Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
---
Notes:
  v1 -> v2
  - Fix memory leak and added logs

 NEWS                           |  2 ++
 include/openflow/nicira-ext.h  | 10 ++++++++++
 include/openvswitch/ofp-msgs.h |  4 ++++
 lib/ofp-bundle.c               |  1 +
 lib/ofp-print.c                | 11 +++++++++++
 lib/rconn.c                    |  1 +
 ofproto/ofproto-dpif.c         | 22 ++++++++++++++++++++++
 ofproto/ofproto-provider.h     |  4 ++++
 ofproto/ofproto.c              | 25 +++++++++++++++++++++++++
 tests/ofp-print.at             | 10 ++++++++++
 tests/ovs-ofctl.at             | 12 ++++++++++++
 utilities/ovs-ofctl.8.in       |  5 +++++
 utilities/ovs-ofctl.c          | 34 ++++++++++++++++++++++++++++++++++
 13 files changed, 141 insertions(+)

Comments

0-day Robot March 14, 2023, 12:13 p.m. UTC | #1
References:  <20230314105155.12571-1-naveen.yerramneni@nutanix.com>
 

Bleep bloop.  Greetings Naveen Yerramneni, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
WARNING: Line has non-spaces leading whitespace
WARNING: Line has trailing whitespace
#157 FILE: ofproto/ofproto-dpif.c:5641:
  

WARNING: Line has trailing whitespace
#158 FILE: ofproto/ofproto-dpif.c:5642:
    ct_dpif_free_zone_limits(&zone_limits); 

Lines checked: 344, Warnings: 3, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
Simon Horman March 14, 2023, 12:47 p.m. UTC | #2
On Tue, Mar 14, 2023 at 10:51:55AM +0000, Naveen Yerramneni wrote:
> Add OpenFlow extn to set conntrack entries limit per zone.
> 
> Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>

Hi Naveen,

There were 4 postings of this patchset yesterday, and one so far today.
Please consider allowing 24h between postings to give reviewers a chance to
look at things.

Overall this patch looks nice to me, I reviewed it with reference to:

2a7c4805a76d ("Add OpenFlow command to flush conntrack table entries.")

Some minor comments inline.

I will run some tests over it.
And respond further if that shows up anything of interest.

...

> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index f87e27a8c..6797cebac 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -5631,6 +5631,27 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>      }
>  }
>  
> +static void
> +ct_set_zone_limit(const struct ofproto *ofproto_, const uint16_t zone_id,
> +                  const uint32_t limit)
> +{
> +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
> +
> +    ct_dpif_push_zone_limit(&zone_limits, zone_id, limit, 0);
> +    int err = ct_dpif_set_limits(ofproto->backer->dpif, NULL, &zone_limits);
> +    if (err) {
> +        VLOG_ERR_RL(&rl, "failed to set zone limit id=%"PRIu16", "
> +                          "limit=%"PRIu32" (%s)", zone_id, limit,
> +                          ovs_strerror(err));
> +    } else {
> +        VLOG_DBG("configured zone limit for zone=%"PRIu16", limit=%"PRIu32"",
> +                zone_id, limit);
> +    }
> +  
> +    ct_dpif_free_zone_limits(&zone_limits); 

nit: the two lines line above have trailing whitespace.

> +}
> +

...
Naveen Yerramneni March 15, 2023, 8:27 a.m. UTC | #3
Hi Simon,

Thanks, I will wait for your review.

Multiple patches were sent out due to my mistake, my apologies for that.


Thanks,
Naveen


> On 14-Mar-2023, at 6:17 PM, Simon Horman <simon.horman@corigine.com> wrote:
> 
> On Tue, Mar 14, 2023 at 10:51:55AM +0000, Naveen Yerramneni wrote:
>> Add OpenFlow extn to set conntrack entries limit per zone.
>> 
>> Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
> 
> Hi Naveen,
> 
> There were 4 postings of this patchset yesterday, and one so far today.
> Please consider allowing 24h between postings to give reviewers a chance to
> look at things.
> 
> Overall this patch looks nice to me, I reviewed it with reference to:
> 
> 2a7c4805a76d ("Add OpenFlow command to flush conntrack table entries.")
> 
> Some minor comments inline.
> 
> I will run some tests over it.
> And respond further if that shows up anything of interest.
> 
> ...
> 
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index f87e27a8c..6797cebac 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -5631,6 +5631,27 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>>     }
>> }
>> 
>> +static void
>> +ct_set_zone_limit(const struct ofproto *ofproto_, const uint16_t zone_id,
>> +                  const uint32_t limit)
>> +{
>> +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
>> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
>> +
>> +    ct_dpif_push_zone_limit(&zone_limits, zone_id, limit, 0);
>> +    int err = ct_dpif_set_limits(ofproto->backer->dpif, NULL, &zone_limits);
>> +    if (err) {
>> +        VLOG_ERR_RL(&rl, "failed to set zone limit id=%"PRIu16", "
>> +                          "limit=%"PRIu32" (%s)", zone_id, limit,
>> +                          ovs_strerror(err));
>> +    } else {
>> +        VLOG_DBG("configured zone limit for zone=%"PRIu16", limit=%"PRIu32"",
>> +                zone_id, limit);
>> +    }
>> +  
>> +    ct_dpif_free_zone_limits(&zone_limits); 
> 
> nit: the two lines line above have trailing whitespace.
> 
>> +}
>> +
> 
> ...
Simon Horman March 15, 2023, 8:54 a.m. UTC | #4
On Wed, Mar 15, 2023 at 08:27:40AM +0000, Naveen Yerramneni wrote:
> Hi Simon,
> 
> Thanks, I will wait for your review.
> 
> Multiple patches were sent out due to my mistake, my apologies for that.

Understood, sorry for jumping the gun a bit there.

> Thanks,
> Naveen
> 
> 
> > On 14-Mar-2023, at 6:17 PM, Simon Horman <simon.horman@corigine.com> wrote:
> > 
> > On Tue, Mar 14, 2023 at 10:51:55AM +0000, Naveen Yerramneni wrote:
> >> Add OpenFlow extn to set conntrack entries limit per zone.
> >> 
> >> Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
> > 
> > Hi Naveen,
> > 
> > There were 4 postings of this patchset yesterday, and one so far today.
> > Please consider allowing 24h between postings to give reviewers a chance to
> > look at things.
> > 
> > Overall this patch looks nice to me, I reviewed it with reference to:
> > 
> > 2a7c4805a76d ("Add OpenFlow command to flush conntrack table entries.")
> > 
> > Some minor comments inline.
> > 
> > I will run some tests over it.
> > And respond further if that shows up anything of interest.

I did run the automated workflow over this [1].
Including check-offloads [2].
And things seem clean to me.

So with the minor nits in my previous email addressed:

Reviewed-by: Simon Horman <simon.horman@corigine.com>

[1] https://github.com/horms/ovs/actions/runs/4415889468
[2] https://mail.openvswitch.org/pipermail/ovs-dev/2023-March/402672.html

> > 
> > ...
> > 
> >> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> >> index f87e27a8c..6797cebac 100644
> >> --- a/ofproto/ofproto-dpif.c
> >> +++ b/ofproto/ofproto-dpif.c
> >> @@ -5631,6 +5631,27 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
> >>     }
> >> }
> >> 
> >> +static void
> >> +ct_set_zone_limit(const struct ofproto *ofproto_, const uint16_t zone_id,
> >> +                  const uint32_t limit)
> >> +{
> >> +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
> >> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
> >> +
> >> +    ct_dpif_push_zone_limit(&zone_limits, zone_id, limit, 0);
> >> +    int err = ct_dpif_set_limits(ofproto->backer->dpif, NULL, &zone_limits);
> >> +    if (err) {
> >> +        VLOG_ERR_RL(&rl, "failed to set zone limit id=%"PRIu16", "
> >> +                          "limit=%"PRIu32" (%s)", zone_id, limit,
> >> +                          ovs_strerror(err));
> >> +    } else {
> >> +        VLOG_DBG("configured zone limit for zone=%"PRIu16", limit=%"PRIu32"",
> >> +                zone_id, limit);
> >> +    }
> >> +  
> >> +    ct_dpif_free_zone_limits(&zone_limits); 
> > 
> > nit: the two lines line above have trailing whitespace.
> > 
> >> +}
> >> +
> > 
> > ...
>
Naveen Yerramneni March 16, 2023, 5:59 a.m. UTC | #5
Hi Simon,

Thanks for the update. I will send v3 with nits addreesed.

Thanks,
Naveen 

> On 15-Mar-2023, at 2:24 PM, Simon Horman <simon.horman@corigine.com> wrote:
> 
> On Wed, Mar 15, 2023 at 08:27:40AM +0000, Naveen Yerramneni wrote:
>> Hi Simon,
>> 
>> Thanks, I will wait for your review.
>> 
>> Multiple patches were sent out due to my mistake, my apologies for that.
> 
> Understood, sorry for jumping the gun a bit there.
> 
>> Thanks,
>> Naveen
>> 
>> 
>>> On 14-Mar-2023, at 6:17 PM, Simon Horman <simon.horman@corigine.com> wrote:
>>> 
>>> On Tue, Mar 14, 2023 at 10:51:55AM +0000, Naveen Yerramneni wrote:
>>>> Add OpenFlow extn to set conntrack entries limit per zone.
>>>> 
>>>> Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
>>> 
>>> Hi Naveen,
>>> 
>>> There were 4 postings of this patchset yesterday, and one so far today.
>>> Please consider allowing 24h between postings to give reviewers a chance to
>>> look at things.
>>> 
>>> Overall this patch looks nice to me, I reviewed it with reference to:
>>> 
>>> 2a7c4805a76d ("Add OpenFlow command to flush conntrack table entries.")
>>> 
>>> Some minor comments inline.
>>> 
>>> I will run some tests over it.
>>> And respond further if that shows up anything of interest.
> 
> I did run the automated workflow over this [1].
> Including check-offloads [2].
> And things seem clean to me.
> 
> So with the minor nits in my previous email addressed:
> 
> Reviewed-by: Simon Horman <simon.horman@corigine.com>
> 
> [1] https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_horms_ovs_actions_runs_4415889468&d=DwIBAg&c=s883GpUCOChKOHiocYtGcg&r=2PQjSDR7A28z1kXE1ptSm6X36oL_nCq1XxeEt7FkLmA&m=1ULPoISx8VbNNUUySPVpF590U5f5xd19I9VzrZ49p5qRiK4_DMTkx2kW7x9uY55Q&s=pAhfSRiPDj3ees57ibAJlo2tAut2yPd_kIKekNDMpa4&e= 
> [2] https://urldefense.proofpoint.com/v2/url?u=https-3A__mail.openvswitch.org_pipermail_ovs-2Ddev_2023-2DMarch_402672.html&d=DwIBAg&c=s883GpUCOChKOHiocYtGcg&r=2PQjSDR7A28z1kXE1ptSm6X36oL_nCq1XxeEt7FkLmA&m=1ULPoISx8VbNNUUySPVpF590U5f5xd19I9VzrZ49p5qRiK4_DMTkx2kW7x9uY55Q&s=6ihLNNSY3oKIqfrhSJAWPXjH8G2KD-3NzQTcj9VdLr8&e= 
> 
>>> 
>>> ...
>>> 
>>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>>> index f87e27a8c..6797cebac 100644
>>>> --- a/ofproto/ofproto-dpif.c
>>>> +++ b/ofproto/ofproto-dpif.c
>>>> @@ -5631,6 +5631,27 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
>>>>    }
>>>> }
>>>> 
>>>> +static void
>>>> +ct_set_zone_limit(const struct ofproto *ofproto_, const uint16_t zone_id,
>>>> +                  const uint32_t limit)
>>>> +{
>>>> +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
>>>> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
>>>> +
>>>> +    ct_dpif_push_zone_limit(&zone_limits, zone_id, limit, 0);
>>>> +    int err = ct_dpif_set_limits(ofproto->backer->dpif, NULL, &zone_limits);
>>>> +    if (err) {
>>>> +        VLOG_ERR_RL(&rl, "failed to set zone limit id=%"PRIu16", "
>>>> +                          "limit=%"PRIu32" (%s)", zone_id, limit,
>>>> +                          ovs_strerror(err));
>>>> +    } else {
>>>> +        VLOG_DBG("configured zone limit for zone=%"PRIu16", limit=%"PRIu32"",
>>>> +                zone_id, limit);
>>>> +    }
>>>> +  
>>>> +    ct_dpif_free_zone_limits(&zone_limits); 
>>> 
>>> nit: the two lines line above have trailing whitespace.
>>> 
>>>> +}
>>>> +
>>> 
>>> ...
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index fe6055a27..f6ae60856 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,8 @@  v3.1.0 - xx xxx xxxx
    - OpenFlow:
      * New OpenFlow extension NXT_CT_FLUSH to flush connections matching
        the specified fields.
+     * New OpenFlow extension NXT_CT_SET_ZONE_LIMIT to set conntrack table
+       limit at zone level.
    - ovs-ctl:
      * New option '--dump-hugepages' to include hugepages in core dumps. This
        can assist with postmortem analysis involving DPDK, but may also produce
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 768775898..0f93ea21c 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1101,4 +1101,14 @@  struct nx_ct_flush {
 };
 OFP_ASSERT(sizeof(struct nx_ct_flush) == 8);
 
+/* NXT_CT_SET_ZONE_LIMIT.
+ *
+ * Sets connection tracking table zone limit. */
+struct nx_ct_zone_limit {
+    uint8_t zero[2];       /* Must be zero. */
+    ovs_be16 zone_id;      /* Connection tracking zone. */
+    ovs_be32 limit;        /* Drop limit. */
+};
+OFP_ASSERT(sizeof(struct nx_ct_zone_limit) == 8);
+
 #endif /* openflow/nicira-ext.h */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 708427fc0..a9518557e 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -518,6 +518,9 @@  enum ofpraw {
     /* NXT 1.0+ (32): struct nx_ct_flush, uint8_t[8][]. */
     OFPRAW_NXT_CT_FLUSH,
 
+    /* NXT 1.0+ (35): struct nx_ct_zone_limit. */
+    OFPRAW_NXT_CT_SET_ZONE_LIMIT,
+
     /* NXST 1.0+ (3): void. */
     OFPRAW_NXST_IPFIX_BRIDGE_REQUEST,
 
@@ -776,6 +779,7 @@  enum ofptype {
     OFPTYPE_IPFIX_FLOW_STATS_REPLY,   /* OFPRAW_NXST_IPFIX_FLOW_REPLY */
     OFPTYPE_CT_FLUSH_ZONE,            /* OFPRAW_NXT_CT_FLUSH_ZONE. */
     OFPTYPE_CT_FLUSH,                 /* OFPRAW_NXT_CT_FLUSH. */
+    OFPTYPE_CT_SET_ZONE_LIMIT,        /* OFPRAW_NXT_CT_SET_ZONE_LIMIT. */
 
     /* Flow monitor extension. */
     OFPTYPE_FLOW_MONITOR_CANCEL,  /* OFPRAW_NXT_FLOW_MONITOR_CANCEL.
diff --git a/lib/ofp-bundle.c b/lib/ofp-bundle.c
index 941a8370e..3ed1f30d8 100644
--- a/lib/ofp-bundle.c
+++ b/lib/ofp-bundle.c
@@ -293,6 +293,7 @@  ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
     case OFPTYPE_CT_FLUSH_ZONE:
     case OFPTYPE_CT_FLUSH:
+    case OFPTYPE_CT_SET_ZONE_LIMIT:
         break;
     }
 
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 874079b84..8a64b72c0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -967,6 +967,15 @@  ofp_print_nxt_ct_flush(struct ds *string, const struct ofp_header *oh)
     return 0;
 }
 
+static enum ofperr
+ofp_print_nxt_ct_set_zone_limit(struct ds *string,
+                                const struct nx_ct_zone_limit *nzl)
+{
+    ds_put_format(string, " zone_id=%"PRIu16, ntohs(nzl->zone_id));
+    ds_put_format(string, " limit=%"PRIu32, ntohl(nzl->limit));
+    return 0;
+}
+
 static enum ofperr
 ofp_to_string__(const struct ofp_header *oh,
                 const struct ofputil_port_map *port_map,
@@ -1204,6 +1213,8 @@  ofp_to_string__(const struct ofp_header *oh,
         return ofp_print_nxt_ct_flush_zone(string, ofpmsg_body(oh));
     case OFPTYPE_CT_FLUSH:
         return ofp_print_nxt_ct_flush(string, oh);
+    case OFPTYPE_CT_SET_ZONE_LIMIT:
+        return ofp_print_nxt_ct_set_zone_limit(string, ofpmsg_body(oh));
     }
 
     return 0;
diff --git a/lib/rconn.c b/lib/rconn.c
index 4afa21515..91c982d98 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1427,6 +1427,7 @@  is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
     case OFPTYPE_CT_FLUSH_ZONE:
     case OFPTYPE_CT_FLUSH:
+    case OFPTYPE_CT_SET_ZONE_LIMIT:
     default:
         return true;
     }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index f87e27a8c..6797cebac 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5631,6 +5631,27 @@  ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
     }
 }
 
+static void
+ct_set_zone_limit(const struct ofproto *ofproto_, const uint16_t zone_id,
+                  const uint32_t limit)
+{
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
+
+    ct_dpif_push_zone_limit(&zone_limits, zone_id, limit, 0);
+    int err = ct_dpif_set_limits(ofproto->backer->dpif, NULL, &zone_limits);
+    if (err) {
+        VLOG_ERR_RL(&rl, "failed to set zone limit id=%"PRIu16", "
+                          "limit=%"PRIu32" (%s)", zone_id, limit,
+                          ovs_strerror(err));
+    } else {
+        VLOG_DBG("configured zone limit for zone=%"PRIu16", limit=%"PRIu32"",
+                zone_id, limit);
+    }
+  
+    ct_dpif_free_zone_limits(&zone_limits); 
+}
+
 static void
 get_datapath_cap(const char *datapath_type, struct smap *cap)
 {
@@ -6920,4 +6941,5 @@  const struct ofproto_class ofproto_dpif_class = {
     ct_flush,                   /* ct_flush */
     ct_set_zone_timeout_policy,
     ct_del_zone_timeout_policy,
+    ct_set_zone_limit,          /* ct_set_zone_limit */
 };
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index a84ddc1d0..c66623637 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1916,6 +1916,10 @@  struct ofproto_class {
     /* Deletes the timeout policy associated with 'zone' in datapath type
      * 'dp_type'. */
     void (*ct_del_zone_timeout_policy)(const char *dp_type, uint16_t zone);
+
+    /* Sets conntrack zone limit */
+    void (*ct_set_zone_limit)(const struct ofproto *, const uint16_t zone,
+                              const uint32_t limit);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index e4a1bee76..e8e884937 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -966,6 +966,28 @@  handle_nxt_ct_flush(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
+static enum ofperr
+handle_nxt_ct_set_zone_limit(struct ofconn *ofconn,
+                            const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    const struct nx_ct_zone_limit *nzl = ofpmsg_body(oh);
+
+    if (!is_all_zeros(nzl->zero, sizeof nzl->zero)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    uint16_t zone_id = ntohs(nzl->zone_id);
+    uint32_t limit = ntohl(nzl->limit);
+    if (ofproto->ofproto_class->ct_set_zone_limit) {
+        ofproto->ofproto_class->ct_set_zone_limit(ofproto, zone_id, limit);
+    } else {
+        return EOPNOTSUPP;
+    }
+
+    return 0;
+}
+
 void
 ofproto_set_flow_restore_wait(bool flow_restore_wait_db)
 {
@@ -8814,6 +8836,9 @@  handle_single_part_openflow(struct ofconn *ofconn, const struct ofp_header *oh,
     case OFPTYPE_CT_FLUSH:
         return handle_nxt_ct_flush(ofconn, oh);
 
+    case OFPTYPE_CT_SET_ZONE_LIMIT:
+        return handle_nxt_ct_set_zone_limit(ofconn, oh);
+
     case OFPTYPE_HELLO:
     case OFPTYPE_ERROR:
     case OFPTYPE_FEATURES_REPLY:
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 14aa55416..5c45e1ec6 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -4181,3 +4181,13 @@  AT_CHECK([ovs-ofctl ofp-print "\
 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \
 " | grep -q OFPBPC_BAD_VALUE], [0])
 AT_CLEANUP
+
+AT_SETUP([NXT_CT_SET_ZONE_LIMIT])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 23 \
+00 00 00 12 00 01 86 a0 \
+"], [0], [dnl
+NXT_CT_SET_ZONE_LIMIT (xid=0x3): zone_id=18 limit=100000
+])
+AT_CLEANUP
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 8531b2e2e..8a17d3609 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -3309,3 +3309,15 @@  AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: <all>" ovs-vswitchd.log])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+
+AT_SETUP([ovs-ofctl ct-set-zone-limit])
+OVS_VSWITCHD_START
+
+AT_CHECK([ovs-appctl vlog/set ct_dpif:dbg])
+AT_CHECK([ovs-ofctl ct-set-zone-limit br0 1 200000])
+
+OVS_WAIT_UNTIL([grep -q "ofproto_dpif|DBG|.*zone=1.*limit=200000" ovs-vswitchd.log])
+AT_CHECK([grep -q "ofproto_dpif|DBG|.*zone=1.*limit=200000" ovs-vswitchd.log])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 0a611b2ee..8a6d5a3db 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -326,6 +326,11 @@  An example of an IPv6 TCP \fIct-[orig|reply]-tuple\fR:
 .IP
 This command uses an Open vSwitch extension that is only in Open vSwitch 3.1
 and later.
+.IP "\fBct\-set\-zone\-limit \fIswitch zone limit\fR
+Set the connection tracking entries limit in \fIzone\fR on \fIswitch\fR.
+.IP
+This command uses an Open vSwitch extension that is only in Open
+vSwitch 3.1 and later.
 .
 .SS "OpenFlow Switch Flow Table Commands"
 .
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index eabec18a3..1464827bb 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -489,6 +489,8 @@  usage(void)
            "  ct-flush SWITCH [ZONE] [CT_ORIG_TUPLE [CT_REPLY_TUPLE]]\n"
            "                              flush conntrack entries specified\n"
            "                              by CT_ORIG/REPLY_TUPLE and ZONE\n"
+           "  ct-set-zone-limit SWITCH ZONE LIMIT set conntrack entries\n"
+           "                                      limit for the ZONE\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
@@ -3098,6 +3100,35 @@  ofctl_ct_flush(struct ovs_cmdl_context *ctx)
     vconn_close(vconn);
 }
 
+static void
+ofctl_ct_set_zone_limit(struct ovs_cmdl_context *ctx)
+{
+    uint16_t zone_id;
+    uint32_t limit;
+
+    char *error = str_to_u16(ctx->argv[2], "zone_id", &zone_id);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    error = str_to_u32(ctx->argv[3], &limit);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
+    struct vconn *vconn;
+    open_vconn(ctx->argv[1], &vconn);
+    enum ofp_version version = vconn_get_version(vconn);
+
+    struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_CT_SET_ZONE_LIMIT, version,
+                                      0);
+    struct nx_ct_zone_limit *nzl = ofpbuf_put_zeros(msg, sizeof *nzl);
+    nzl->zone_id = htons(zone_id);
+    nzl->limit = htonl(limit);
+
+    transact_noreply(vconn, msg);
+    vconn_close(vconn);
+}
+
 static void
 ofctl_dump_ipfix_flow(struct ovs_cmdl_context *ctx)
 {
@@ -5114,6 +5145,9 @@  static const struct ovs_cmdl_command all_commands[] = {
     { "ct-flush", "switch [zone=N] [ct-orig-tuple [ct-reply-tuple]]",
       1, 4, ofctl_ct_flush, OVS_RO },
 
+    { "ct-set-zone-limit", "switch zone limit",
+      3, 3, ofctl_ct_set_zone_limit, OVS_RO },
+
     { "ofp-parse", "file",
       1, 1, ofctl_ofp_parse, OVS_RW },
     { "ofp-parse-pcap", "pcap",