diff mbox series

[ovs-dev,v3,10/11] dpctl: Implement dpctl commands for conntrack per zone limit

Message ID 1534272992-31756-11-git-send-email-yihung.wei@gmail.com
State Superseded
Headers show
Series conntrack zone limitation | expand

Commit Message

Yi-Hung Wei Aug. 14, 2018, 6:56 p.m. UTC
This patch implments the following three commands on dpctl so that
users can use ovs-dpctl or ovs-appctl to set, delete, and get the
per zone limit.

For example,

$ ovs-appctl dpctl/ct-set-limits default=10 zone=0,limit=5 zone=1,limit=3
$ ovs-appctl dpct/ct-del-limits zone=0
$ ovs-appctl dpct/ct-get-limits zone=1,2,3

Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com>
---
 NEWS          |   2 +
 lib/ct-dpif.c |  67 +++++++++++++++++++++++
 lib/ct-dpif.h |   4 ++
 lib/dpctl.c   | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/dpctl.man |  18 +++++++
 5 files changed, 259 insertions(+), 1 deletion(-)

Comments

0-day Robot Aug. 14, 2018, 8:13 p.m. UTC | #1
Bleep bloop.  Greetings Yi-Hung Wei, 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 is 115 characters long (recommended limit is 79)
#331 FILE: lib/dpctl.man:277:
\*(DX\fBct\-set\-limits\fR [\fIdp\fR] [\fBdefault=\fIdefault_limit\fR] [\fBzone=\fIzone\fR,\fBlimit=\fIlimit\fR]...

Lines checked: 350, Warnings: 1, Errors: 0


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

Thanks,
0-day Robot
Justin Pettit Aug. 17, 2018, 1:24 a.m. UTC | #2
> On Aug 14, 2018, at 11:56 AM, Yi-Hung Wei <yihung.wei@gmail.com> wrote:
> 
> 
> diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
> index a772799fe347..bb809d9920b5 100644
> --- a/lib/ct-dpif.c
> +++ b/lib/ct-dpif.c
> @@ -629,3 +629,70 @@ ct_dpif_free_zone_limits(struct ovs_list *zone_limits)
> ...
> +/* 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'. */
> +bool
> +ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
> +                               uint32_t *plimit, struct ds *ds)
> +{
> ...
> +    if (parsed_zone == false || parsed_limit == false) {

This is extremely minor, but I think we would more commonly write this as:

	if (!parsed_zone || !parsed_limit) {

> +void
> +ct_dpif_format_zone_limits(uint32_t default_limit,
> +                           const struct ovs_list *zone_limits, struct ds *ds)
> +{
> +    struct ct_dpif_zone_limit *zone_limit;
> +
> +    ds_put_format(ds, "default_limit=%"PRIu32, default_limit);

I would just drop the "_", since this is meant to be human-readable.  (If you intend to have the output readable as an argument to "ct-set-limits", then the formats are different, since that just takes "default=".)

> +
> +    LIST_FOR_EACH (zone_limit, node, zone_limits) {
> +        ds_put_format(ds, " zone=%"PRIu16, zone_limit->zone);
> +        ds_put_format(ds, ",limit=%"PRIu32, zone_limit->limit);
> +        ds_put_format(ds, ",count=%"PRIu32, zone_limit->count);
> +    }

I find it hard to parse all of this on a single line.  What about putting each zone on its own line?

> diff --git a/lib/dpctl.c b/lib/dpctl.c
> index 63cc6eb0a289..076703157eae 100644
> --- a/lib/dpctl.c
> +++ b/lib/dpctl.c
> @@ -199,7 +199,7 @@ parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
>  * to be parsed in '*indexp'.  */
> static int
> opt_dpif_open(int argc, const char *argv[], struct dpctl_params *dpctl_p,
> -              uint8_t max_args, struct dpif **dpifp, bool multi_opt,
> +              int max_args, struct dpif **dpifp, bool multi_opt,
>               int *indexp)
> {

Assuming you still need the previous patch, I suspect this change should go in there.

> diff --git a/lib/dpctl.c b/lib/dpctl.c
> index 63cc6eb0a289..076703157eae 100644
> --- a/lib/dpctl.c
> +++ b/lib/dpctl.c
> 
> +static int
> +dpctl_ct_set_limits(int argc, const char *argv[],
> +                    struct dpctl_params *dpctl_p)
> +{
> +    struct dpif *dpif;
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    int error, i = 1;
> +    uint32_t default_limit, *p_default_limit = NULL;
> +    struct ovs_list list = OVS_LIST_INITIALIZER(&list);

Minor, but I think "zone_limits" would be a more consistent and clearer name than "list".

> +static int
> +parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
> +{
> +    char *save_ptr = NULL, *argcopy, *next_zone;
> +    uint16_t zone;
> +
> +    if (strncmp(argv, "zone=", 5)) {
> +        ds_put_format(ds, "invalid argument %s", argv);
> +        return EINVAL;
> +    }
> +
> +    argcopy = xstrdup(argv + 5);
> +    next_zone = strtok_r(argcopy, ",", &save_ptr);
> +
> +    do {
> +        if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
> +            ct_dpif_push_zone_limit(list, zone, 0, 0);
> +        } else {
> +            ds_put_cstr(ds, "invalid zone");
> +            return EINVAL;

Do you need to free "argcopy"?

> diff --git a/lib/dpctl.man b/lib/dpctl.man
> index 5d987e62daaa..deb1bd32a34b 100644
> --- a/lib/dpctl.man
> +++ b/lib/dpctl.man
> @@ -272,3 +272,21 @@ Only supported for userspace datapath.
> \*(DX\fBct\-get\-nconns\fR [\fIdp\fR]
> Prints the current number of connection tracker entries on \fIdp\fR.
> Only supported for userspace datapath.
> +.
> +.TP

These are in the "CONNECTION TRACKING TABLE DEBUGGING COMMANDS" section, which doesn't seem quite right.  It may be worth dropping the "DEBUGGING" word, since these commands and "ct-set-maxconns" aren't just for debugging.

> +\*(DX\fBct\-set\-limits\fR [\fIdp\fR] [\fBdefault=\fIdefault_limit\fR] [\fBzone=\fIzone\fR,\fBlimit=\fIlimit\fR]...
> +Sets the maximum allowed number of connections in connection tracking zones.
> +If a per zone limit for a particular zone is not available in the datapath,
> +it defaults to the default per zone limit.  Initially, the default per zone
> +limit is unlimited(0).
> +.
> +.TP
> +\*(DX\fBct\-del\-limits\fR [\fIdp\fR] \fBzone=\fIzone[,zone]\fR...
> +Deletes the per zone limit on particular zones.  The \fIzone\fR must be a
> +comma-separated list.
> +.
> +.TP
> +\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone[,zone]\fR...]
> +Retrieves the maximum allowed number of connections.  The \fIzone\fR must
> +be a comma-separated list.  Prints all zone limits if no \fIzone\fR is
> +provided.

I think these could use a bit more description.  I took a stab at it with the incremental at the end.  Let me know what you think.

By the way, I think it's worth update the FAQ (Documentation/faq/releases.rst) to indicate on what datapaths and kernel versions this is supported.

Thanks,

--Justin


-=-=-=-=-=-=-=-

diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index d3e8c936d7e6..4d6cdb5951c5 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -657,7 +657,7 @@ ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
         }
     }
 
-    if (parsed_zone == false || parsed_limit == false) {
+    if (!parsed_zone || !parsed_limit) {
         ds_put_format(ds, "failed to parse zone limit");
         goto error;
     }
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 46634f67c6aa..8c625c2a9b2a 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1712,11 +1712,11 @@ dpctl_ct_set_limits(int argc, const char *argv[],
 {
     struct dpif *dpif;
     struct ds ds = DS_EMPTY_INITIALIZER;
-    int error, i = 1;
+    int i = 1;
     uint32_t default_limit, *p_default_limit = NULL;
-    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
+    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
 
-    error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif, true, &i);
+    int error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif, true, &i);
     if (error) {
         return error;
     }
@@ -1741,12 +1741,12 @@ dpctl_ct_set_limits(int argc, const char *argv[],
             error = EINVAL;
             goto error;
         }
-        ct_dpif_push_zone_limit(&list, zone, limit, 0);
+        ct_dpif_push_zone_limit(&zone_limits, zone, limit, 0);
     }
 
-    error = ct_dpif_set_limits(dpif, p_default_limit, &list);
+    error = ct_dpif_set_limits(dpif, p_default_limit, &zone_limits);
     if (!error) {
-        ct_dpif_free_zone_limits(&list);
+        ct_dpif_free_zone_limits(&zone_limits);
         dpif_close(dpif);
         return 0;
     } else {
@@ -1756,13 +1756,14 @@ dpctl_ct_set_limits(int argc, const char *argv[],
 error:
     dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
     ds_destroy(&ds);
-    ct_dpif_free_zone_limits(&list);
+    ct_dpif_free_zone_limits(&zone_limits);
     dpif_close(dpif);
     return error;
 }
 
 static int
-parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
+parse_ct_limit_zones(const char *argv, struct ovs_list *zone_limits,
+                     struct ds *ds)
 {
     char *save_ptr = NULL, *argcopy, *next_zone;
     uint16_t zone;
@@ -1777,9 +1778,10 @@ parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
 
     do {
         if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
-            ct_dpif_push_zone_limit(list, zone, 0, 0);
+            ct_dpif_push_zone_limit(zone_limits, zone, 0, 0);
         } else {
             ds_put_cstr(ds, "invalid zone");
+            free(argcopy);
             return EINVAL;
         }
     } while ((next_zone = strtok_r(NULL, ",", &save_ptr)) != NULL);
@@ -1795,19 +1797,19 @@ dpctl_ct_del_limits(int argc, const char *argv[],
     struct dpif *dpif;
     struct ds ds = DS_EMPTY_INITIALIZER;
     int error, i = 1;
-    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
+    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
 
     error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
     if (error) {
         return error;
     }
 
-    error = parse_ct_limit_zones(argv[i], &list, &ds);
+    error = parse_ct_limit_zones(argv[i], &zone_limits, &ds);
     if (error) {
         goto error;
     }
 
-    error = ct_dpif_del_limits(dpif, &list);
+    error = ct_dpif_del_limits(dpif, &zone_limits);
     if (!error) {
         goto out;
     } else {
@@ -1818,7 +1820,7 @@ error:
     dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
     ds_destroy(&ds);
 out:
-    ct_dpif_free_zone_limits(&list);
+    ct_dpif_free_zone_limits(&zone_limits);
     dpif_close(dpif);
     return error;
 }
@@ -1830,11 +1832,11 @@ dpctl_ct_get_limits(int argc, const char *argv[],
     struct dpif *dpif;
     struct ds ds = DS_EMPTY_INITIALIZER;
     uint32_t default_limit;
-    int error, i = 1;
+    int i = 1;
     struct ovs_list list_query = OVS_LIST_INITIALIZER(&list_query);
     struct ovs_list list_reply = OVS_LIST_INITIALIZER(&list_reply);
 
-    error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
+    int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
     if (error) {
         return error;
     }
diff --git a/lib/dpctl.man b/lib/dpctl.man
index deb1bd32a34b..eb59d1209999 100644
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -275,18 +275,25 @@ Only supported for userspace datapath.
 .
 .TP
 \*(DX\fBct\-set\-limits\fR [\fIdp\fR] [\fBdefault=\fIdefault_limit\fR] [\fBzone=\fIzone\fR,\fBlimit=\fIlimit\fR]...
-Sets the maximum allowed number of connections in connection tracking zones.
-If a per zone limit for a particular zone is not available in the datapath,
-it defaults to the default per zone limit.  Initially, the default per zone
-limit is unlimited(0).
+Sets the maximum allowed number of connections in a connection tracking
+zone.  A specific \fIzone\fR may be set to \fIlimit\fR, and multiple zones
+may be specified with a comma-separated list.  If a per-zone limit for a
+particular zone is not specified in the datapath, it defaults to the
+default per-zone limit.  A default zone may be specified with the
+\fBdefault=\fIdefault_limit\fR argument.   Initially, the default
+per-zone limit is unlimited.  An unlimited number of entries may be set
+with \fB0\fR limit.  Only supported for Linux kernel datapath.
 .
 .TP
 \*(DX\fBct\-del\-limits\fR [\fIdp\fR] \fBzone=\fIzone[,zone]\fR...
-Deletes the per zone limit on particular zones.  The \fIzone\fR must be a
-comma-separated list.
+Deletes the connection tracking limit for \fIzone\fR.  Multiple zones may
+be specified with a comma-separated list.  Only supported for Linux
+kernel datapath.
 .
 .TP
-\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone[,zone]\fR...]
-Retrieves the maximum allowed number of connections.  The \fIzone\fR must
-be a comma-separated list.  Prints all zone limits if no \fIzone\fR is
-provided.
+\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone\fR[\fB,\fIzone\fR]...]
+Retrieves the maximum allowed number of connections and current
+counts per-zone.  If \fIzone\fR is given, only the specified zone(s) are
+printed.  If no zones are specified, all the zone limits and counts are
+provided.  The command always displays the default zone limit.  Only
+supported for Linux kernel datapath.
Yi-Hung Wei Aug. 17, 2018, 8:35 a.m. UTC | #3
On Thu, Aug 16, 2018 at 6:24 PM Justin Pettit <jpettit@ovn.org> wrote:
> > On Aug 14, 2018, at 11:56 AM, Yi-Hung Wei <yihung.wei@gmail.com> wrote:
> >
> >
> > diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
> > index a772799fe347..bb809d9920b5 100644
> > --- a/lib/ct-dpif.c
> > +++ b/lib/ct-dpif.c
> > +void
> > +ct_dpif_format_zone_limits(uint32_t default_limit,
> > +                           const struct ovs_list *zone_limits, struct ds *ds)
> > +{
> > +    struct ct_dpif_zone_limit *zone_limit;
> > +
> > +    ds_put_format(ds, "default_limit=%"PRIu32, default_limit);
>
> I would just drop the "_", since this is meant to be human-readable.  (If you intend to have the output readable as an argument to "ct-set-limits", then the formats are different, since that just takes "default=".)

Thanks, I will drop the "_" in v4.

> > +
> > +    LIST_FOR_EACH (zone_limit, node, zone_limits) {
> > +        ds_put_format(ds, " zone=%"PRIu16, zone_limit->zone);
> > +        ds_put_format(ds, ",limit=%"PRIu32, zone_limit->limit);
> > +        ds_put_format(ds, ",count=%"PRIu32, zone_limit->count);
> > +    }
>
> I find it hard to parse all of this on a single line.  What about putting each zone on its own line?
Sure. I will put default limit, and every zone in different lines.


> > diff --git a/lib/dpctl.c b/lib/dpctl.c
> > index 63cc6eb0a289..076703157eae 100644
> > --- a/lib/dpctl.c
> > +++ b/lib/dpctl.c
> > @@ -199,7 +199,7 @@ parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
> >  * to be parsed in '*indexp'.  */
> > static int
> > opt_dpif_open(int argc, const char *argv[], struct dpctl_params *dpctl_p,
> > -              uint8_t max_args, struct dpif **dpifp, bool multi_opt,
> > +              int max_args, struct dpif **dpifp, bool multi_opt,
> >               int *indexp)
> > {
>
> Assuming you still need the previous patch, I suspect this change should go in there.
Yes, I fold in this change in the previous patch.


> > diff --git a/lib/dpctl.man b/lib/dpctl.man
> > index 5d987e62daaa..deb1bd32a34b 100644
> > --- a/lib/dpctl.man
> > +++ b/lib/dpctl.man
> > @@ -272,3 +272,21 @@ Only supported for userspace datapath.
> > \*(DX\fBct\-get\-nconns\fR [\fIdp\fR]
> > Prints the current number of connection tracker entries on \fIdp\fR.
> > Only supported for userspace datapath.
> > +.
> > +.TP
>
> These are in the "CONNECTION TRACKING TABLE DEBUGGING COMMANDS" section, which doesn't seem quite right.  It may be worth dropping the "DEBUGGING" word, since these commands and "ct-set-maxconns" aren't just for debugging.

That make sense. I make the following change in v4.
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -196,9 +196,9 @@ Fetches the flow from \fIdp\fR's flow table with
unique identifier \fIufid\fR.
 .
 .IP "\*(DX\fBdel\-flows\fR [\fIdp\fR]"
 Deletes all flow entries from datapath \fIdp\fR's flow table.
-.SS "CONNECTION TRACKING TABLE DEBUGGING COMMANDS"
-The following commands are primarily useful for debugging the connection
-tracking entries in the datapath.
+.SS "CONNECTION TRACKING TABLE COMMANDS"
+The following commands are useful for debugging and configuring
+the connection tracking table in the datapath.
 .
 .PP

> By the way, I think it's worth update the FAQ (Documentation/faq/releases.rst) to indicate on what datapaths and kernel versions this is supported.

Sure, I add the following line in FAQ.

diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
index 54c4b54b1ea0..e64fa22c4034 100644
--- a/Documentation/faq/releases.rst
+++ b/Documentation/faq/releases.rst
@@ -125,6 +125,7 @@ Q: Are all features available with all datapaths?
     NIC Bonding           YES            YES            YES       YES
     Multiple VTEPs        YES            YES            YES       YES
     Meters                4.15           YES            YES       NO
+    Conntrack zone limit  4.18           YES            NO        NO
     ===================== ============== ============== ========= =======

     Do note, however:

>
> Thanks,
>
> --Justin

Thanks. I fold in all the following changes and updates in v4.

>
>
> -=-=-=-=-=-=-=-
>
> diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
> index d3e8c936d7e6..4d6cdb5951c5 100644
> --- a/lib/ct-dpif.c
> +++ b/lib/ct-dpif.c
> @@ -657,7 +657,7 @@ ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
>          }
>      }
>
> -    if (parsed_zone == false || parsed_limit == false) {
> +    if (!parsed_zone || !parsed_limit) {
>          ds_put_format(ds, "failed to parse zone limit");
>          goto error;
>      }
> diff --git a/lib/dpctl.c b/lib/dpctl.c
> index 46634f67c6aa..8c625c2a9b2a 100644
> --- a/lib/dpctl.c
> +++ b/lib/dpctl.c
> @@ -1712,11 +1712,11 @@ dpctl_ct_set_limits(int argc, const char *argv[],
>  {
>      struct dpif *dpif;
>      struct ds ds = DS_EMPTY_INITIALIZER;
> -    int error, i = 1;
> +    int i = 1;
>      uint32_t default_limit, *p_default_limit = NULL;
> -    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
>
> -    error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif, true, &i);
> +    int error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif, true, &i);
>      if (error) {
>          return error;
>      }
> @@ -1741,12 +1741,12 @@ dpctl_ct_set_limits(int argc, const char *argv[],
>              error = EINVAL;
>              goto error;
>          }
> -        ct_dpif_push_zone_limit(&list, zone, limit, 0);
> +        ct_dpif_push_zone_limit(&zone_limits, zone, limit, 0);
>      }
>
> -    error = ct_dpif_set_limits(dpif, p_default_limit, &list);
> +    error = ct_dpif_set_limits(dpif, p_default_limit, &zone_limits);
>      if (!error) {
> -        ct_dpif_free_zone_limits(&list);
> +        ct_dpif_free_zone_limits(&zone_limits);
>          dpif_close(dpif);
>          return 0;
>      } else {
> @@ -1756,13 +1756,14 @@ dpctl_ct_set_limits(int argc, const char *argv[],
>  error:
>      dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
>      ds_destroy(&ds);
> -    ct_dpif_free_zone_limits(&list);
> +    ct_dpif_free_zone_limits(&zone_limits);
>      dpif_close(dpif);
>      return error;
>  }
>
>  static int
> -parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
> +parse_ct_limit_zones(const char *argv, struct ovs_list *zone_limits,
> +                     struct ds *ds)
>  {
>      char *save_ptr = NULL, *argcopy, *next_zone;
>      uint16_t zone;
> @@ -1777,9 +1778,10 @@ parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
>
>      do {
>          if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
> -            ct_dpif_push_zone_limit(list, zone, 0, 0);
> +            ct_dpif_push_zone_limit(zone_limits, zone, 0, 0);
>          } else {
>              ds_put_cstr(ds, "invalid zone");
> +            free(argcopy);
>              return EINVAL;
>          }
>      } while ((next_zone = strtok_r(NULL, ",", &save_ptr)) != NULL);
> @@ -1795,19 +1797,19 @@ dpctl_ct_del_limits(int argc, const char *argv[],
>      struct dpif *dpif;
>      struct ds ds = DS_EMPTY_INITIALIZER;
>      int error, i = 1;
> -    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
> +    struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
>
>      error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
>      if (error) {
>          return error;
>      }
>
> -    error = parse_ct_limit_zones(argv[i], &list, &ds);
> +    error = parse_ct_limit_zones(argv[i], &zone_limits, &ds);
>      if (error) {
>          goto error;
>      }
>
> -    error = ct_dpif_del_limits(dpif, &list);
> +    error = ct_dpif_del_limits(dpif, &zone_limits);
>      if (!error) {
>          goto out;
>      } else {
> @@ -1818,7 +1820,7 @@ error:
>      dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
>      ds_destroy(&ds);
>  out:
> -    ct_dpif_free_zone_limits(&list);
> +    ct_dpif_free_zone_limits(&zone_limits);
>      dpif_close(dpif);
>      return error;
>  }
> @@ -1830,11 +1832,11 @@ dpctl_ct_get_limits(int argc, const char *argv[],
>      struct dpif *dpif;
>      struct ds ds = DS_EMPTY_INITIALIZER;
>      uint32_t default_limit;
> -    int error, i = 1;
> +    int i = 1;
>      struct ovs_list list_query = OVS_LIST_INITIALIZER(&list_query);
>      struct ovs_list list_reply = OVS_LIST_INITIALIZER(&list_reply);
>
> -    error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
> +    int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
>      if (error) {
>          return error;
>      }
> diff --git a/lib/dpctl.man b/lib/dpctl.man
> index deb1bd32a34b..eb59d1209999 100644
> --- a/lib/dpctl.man
> +++ b/lib/dpctl.man
> @@ -275,18 +275,25 @@ Only supported for userspace datapath.
>  .
>  .TP
>  \*(DX\fBct\-set\-limits\fR [\fIdp\fR] [\fBdefault=\fIdefault_limit\fR] [\fBzone=\fIzone\fR,\fBlimit=\fIlimit\fR]...
> -Sets the maximum allowed number of connections in connection tracking zones.
> -If a per zone limit for a particular zone is not available in the datapath,
> -it defaults to the default per zone limit.  Initially, the default per zone
> -limit is unlimited(0).
> +Sets the maximum allowed number of connections in a connection tracking
> +zone.  A specific \fIzone\fR may be set to \fIlimit\fR, and multiple zones
> +may be specified with a comma-separated list.  If a per-zone limit for a
> +particular zone is not specified in the datapath, it defaults to the
> +default per-zone limit.  A default zone may be specified with the
> +\fBdefault=\fIdefault_limit\fR argument.   Initially, the default
> +per-zone limit is unlimited.  An unlimited number of entries may be set
> +with \fB0\fR limit.  Only supported for Linux kernel datapath.
>  .
>  .TP
>  \*(DX\fBct\-del\-limits\fR [\fIdp\fR] \fBzone=\fIzone[,zone]\fR...
> -Deletes the per zone limit on particular zones.  The \fIzone\fR must be a
> -comma-separated list.
> +Deletes the connection tracking limit for \fIzone\fR.  Multiple zones may
> +be specified with a comma-separated list.  Only supported for Linux
> +kernel datapath.
>  .
>  .TP
> -\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone[,zone]\fR...]
> -Retrieves the maximum allowed number of connections.  The \fIzone\fR must
> -be a comma-separated list.  Prints all zone limits if no \fIzone\fR is
> -provided.
> +\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone\fR[\fB,\fIzone\fR]...]
> +Retrieves the maximum allowed number of connections and current
> +counts per-zone.  If \fIzone\fR is given, only the specified zone(s) are
> +printed.  If no zones are specified, all the zone limits and counts are
> +provided.  The command always displays the default zone limit.  Only
> +supported for Linux kernel datapath.
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 94a7e9e32c09..a9a1026901f8 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,8 @@  v2.10.0 - xx xxx xxxx
        default it always accepts names and in interactive use it displays them;
        use --names or --no-names to override.  See ovs-ofctl(8) for details.
    - ovs-vsctl: New commands "add-bond-iface" and "del-bond-iface".
+   - ovs-dpctl:
+     * New commands "ct-set-limits", "ct-del-limits", and "ct-get-limits".
    - OpenFlow:
      * OFPT_ROLE_STATUS is now available in OpenFlow 1.3.
      * OpenFlow 1.5 extensible statistics (OXS) now implemented.
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index a772799fe347..bb809d9920b5 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -629,3 +629,70 @@  ct_dpif_free_zone_limits(struct ovs_list *zone_limits)
         free(p);
     }
 }
+
+/* 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'. */
+bool
+ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone,
+                               uint32_t *plimit, struct ds *ds)
+{
+    char *pos, *key, *value, *copy, *err;
+    bool parsed_limit = false, parsed_zone = false;
+
+    pos = copy = xstrdup(s);
+    while (ofputil_parse_key_value(&pos, &key, &value)) {
+        if (!*value) {
+            ds_put_format(ds, "field %s missing value", key);
+            goto error;
+        }
+
+        if (!strcmp(key, "zone")) {
+            err = str_to_u16(value, key, pzone);
+            if (err) {
+                free(err);
+                goto error_with_msg;
+            }
+            parsed_zone = true;
+        }  else if (!strcmp(key, "limit")) {
+            err = str_to_u32(value, plimit);
+            if (err) {
+                free(err);
+                goto error_with_msg;
+            }
+            parsed_limit = true;
+        } else {
+            ds_put_format(ds, "invalid zone limit field: %s", key);
+            goto error;
+        }
+    }
+
+    if (parsed_zone == false || parsed_limit == false) {
+        ds_put_format(ds, "failed to parse zone limit");
+        goto error;
+    }
+
+    free(copy);
+    return true;
+
+error_with_msg:
+    ds_put_format(ds, "failed to parse field %s", key);
+error:
+    free(copy);
+    return false;
+}
+
+void
+ct_dpif_format_zone_limits(uint32_t default_limit,
+                           const struct ovs_list *zone_limits, struct ds *ds)
+{
+    struct ct_dpif_zone_limit *zone_limit;
+
+    ds_put_format(ds, "default_limit=%"PRIu32, default_limit);
+
+    LIST_FOR_EACH (zone_limit, node, zone_limits) {
+        ds_put_format(ds, " zone=%"PRIu16, zone_limit->zone);
+        ds_put_format(ds, ",limit=%"PRIu32, zone_limit->limit);
+        ds_put_format(ds, ",count=%"PRIu32, zone_limit->count);
+    }
+}
diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index c80e18b72b56..c9cfb258b133 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -223,5 +223,9 @@  void ct_dpif_push_zone_limit(struct ovs_list *, uint16_t zone, uint32_t limit,
                              uint32_t count);
 struct ct_dpif_zone_limit * ct_dpif_pop_zone_limit(struct ovs_list *);
 void ct_dpif_free_zone_limits(struct ovs_list *);
+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 *);
 
 #endif /* CT_DPIF_H */
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 63cc6eb0a289..076703157eae 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -199,7 +199,7 @@  parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
  * to be parsed in '*indexp'.  */
 static int
 opt_dpif_open(int argc, const char *argv[], struct dpctl_params *dpctl_p,
-              uint8_t max_args, struct dpif **dpifp, bool multi_opt,
+              int max_args, struct dpif **dpifp, bool multi_opt,
               int *indexp)
 {
     char *dpname;
@@ -1680,6 +1680,167 @@  dpctl_ct_get_nconns(int argc, const char *argv[],
     return error;
 }
 
+static int
+dpctl_ct_set_limits(int argc, const char *argv[],
+                    struct dpctl_params *dpctl_p)
+{
+    struct dpif *dpif;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    int error, i = 1;
+    uint32_t default_limit, *p_default_limit = NULL;
+    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
+
+    error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif, true, &i);
+    if (error) {
+        return error;
+    }
+
+    /* Parse default limit */
+    if (!strncmp(argv[i], "default=", 8)) {
+        if (ovs_scan(argv[i], "default=%"SCNu32, &default_limit)) {
+            p_default_limit = &default_limit;
+            i++;
+        } else {
+            ds_put_cstr(&ds, "invalid default limit");
+            error = EINVAL;
+            goto error;
+        }
+    }
+
+    /* Parse ct zone limit tuples */
+    while (i < argc) {
+        uint16_t zone;
+        uint32_t limit;
+        if (!ct_dpif_parse_zone_limit_tuple(argv[i++], &zone, &limit, &ds)) {
+            error = EINVAL;
+            goto error;
+        }
+        ct_dpif_push_zone_limit(&list, zone, limit, 0);
+    }
+
+    error = ct_dpif_set_limits(dpif, p_default_limit, &list);
+    if (!error) {
+        ct_dpif_free_zone_limits(&list);
+        dpif_close(dpif);
+        return 0;
+    } else {
+        ds_put_cstr(&ds, "failed to set conntrack limit");
+    }
+
+error:
+    dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
+    ct_dpif_free_zone_limits(&list);
+    dpif_close(dpif);
+    return error;
+}
+
+static int
+parse_ct_limit_zones(const char *argv, struct ovs_list *list, struct ds *ds)
+{
+    char *save_ptr = NULL, *argcopy, *next_zone;
+    uint16_t zone;
+
+    if (strncmp(argv, "zone=", 5)) {
+        ds_put_format(ds, "invalid argument %s", argv);
+        return EINVAL;
+    }
+
+    argcopy = xstrdup(argv + 5);
+    next_zone = strtok_r(argcopy, ",", &save_ptr);
+
+    do {
+        if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
+            ct_dpif_push_zone_limit(list, zone, 0, 0);
+        } else {
+            ds_put_cstr(ds, "invalid zone");
+            return EINVAL;
+        }
+    } while ((next_zone = strtok_r(NULL, ",", &save_ptr)) != NULL);
+
+    free(argcopy);
+    return 0;
+}
+
+static int
+dpctl_ct_del_limits(int argc, const char *argv[],
+                    struct dpctl_params *dpctl_p)
+{
+    struct dpif *dpif;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    int error, i = 1;
+    struct ovs_list list = OVS_LIST_INITIALIZER(&list);
+
+    error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
+    if (error) {
+        return error;
+    }
+
+    error = parse_ct_limit_zones(argv[i], &list, &ds);
+    if (error) {
+        goto error;
+    }
+
+    error = ct_dpif_del_limits(dpif, &list);
+    if (!error) {
+        goto out;
+    } else {
+        ds_put_cstr(&ds, "failed to delete conntrack limit");
+    }
+
+error:
+    dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
+out:
+    ct_dpif_free_zone_limits(&list);
+    dpif_close(dpif);
+    return error;
+}
+
+static int
+dpctl_ct_get_limits(int argc, const char *argv[],
+                    struct dpctl_params *dpctl_p)
+{
+    struct dpif *dpif;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    uint32_t default_limit;
+    int error, i = 1;
+    struct ovs_list list_query = OVS_LIST_INITIALIZER(&list_query);
+    struct ovs_list list_reply = OVS_LIST_INITIALIZER(&list_reply);
+
+    error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif, true, &i);
+    if (error) {
+        return error;
+    }
+
+    if (argc > i) {
+        error = parse_ct_limit_zones(argv[i], &list_query, &ds);
+        if (error) {
+            goto error;
+        }
+    }
+
+    error = ct_dpif_get_limits(dpif, &default_limit, &list_query,
+                               &list_reply);
+    if (!error) {
+        ct_dpif_format_zone_limits(default_limit, &list_reply, &ds);
+        dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+        goto out;
+    } else {
+        ds_put_format(&ds, "failed to get conntrack limit %s",
+                      ovs_strerror(error));
+    }
+
+error:
+    dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+out:
+    ds_destroy(&ds);
+    ct_dpif_free_zone_limits(&list_query);
+    ct_dpif_free_zone_limits(&list_reply);
+    dpif_close(dpif);
+    return error;
+}
+
 /* Undocumented commands for unit testing. */
 
 static int
@@ -1979,6 +2140,12 @@  static const struct dpctl_command all_commands[] = {
     { "ct-set-maxconns", "[dp] maxconns", 1, 2, dpctl_ct_set_maxconns, DP_RW },
     { "ct-get-maxconns", "[dp]", 0, 1, dpctl_ct_get_maxconns, DP_RO },
     { "ct-get-nconns", "[dp]", 0, 1, dpctl_ct_get_nconns, 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,
+        DP_RO },
+    { "ct-get-limits", "[dp] [zone=N1[,N2]...]", 0, 2, dpctl_ct_get_limits,
+        DP_RO },
     { "help", "", 0, INT_MAX, dpctl_help, DP_RO },
     { "list-commands", "", 0, INT_MAX, dpctl_list_commands, DP_RO },
 
diff --git a/lib/dpctl.man b/lib/dpctl.man
index 5d987e62daaa..deb1bd32a34b 100644
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -272,3 +272,21 @@  Only supported for userspace datapath.
 \*(DX\fBct\-get\-nconns\fR [\fIdp\fR]
 Prints the current number of connection tracker entries on \fIdp\fR.
 Only supported for userspace datapath.
+.
+.TP
+\*(DX\fBct\-set\-limits\fR [\fIdp\fR] [\fBdefault=\fIdefault_limit\fR] [\fBzone=\fIzone\fR,\fBlimit=\fIlimit\fR]...
+Sets the maximum allowed number of connections in connection tracking zones.
+If a per zone limit for a particular zone is not available in the datapath,
+it defaults to the default per zone limit.  Initially, the default per zone
+limit is unlimited(0).
+.
+.TP
+\*(DX\fBct\-del\-limits\fR [\fIdp\fR] \fBzone=\fIzone[,zone]\fR...
+Deletes the per zone limit on particular zones.  The \fIzone\fR must be a
+comma-separated list.
+.
+.TP
+\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone[,zone]\fR...]
+Retrieves the maximum allowed number of connections.  The \fIzone\fR must
+be a comma-separated list.  Prints all zone limits if no \fIzone\fR is
+provided.