diff mbox series

[ovs-dev,v4] controller: Track individual address set constants.

Message ID 20240502132909.1917908-1-amusil@redhat.com
State Accepted
Headers show
Series [ovs-dev,v4] controller: Track individual address set constants. | expand

Checks

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

Commit Message

Ales Musil May 2, 2024, 1:29 p.m. UTC
Instead of tracking address set per struct expr_constant_set track it
per individual struct expr_constant. This allows more fine grained
control for I-P processing of address sets in controller. It helps with
scenarios like matching on two address sets in one expression e.g.
"ip4.src == {$as1, $as2}". This allows any addition or removal of
individual adress from the set to be incrementally processed instead
of reprocessing all the flows.

This unfortunately doesn't help with the following flows:
"ip4.src == $as1 && ip4.dst == $as2"
"ip4.src == $as1 || ip4.dst == $as2"

The memory impact should be minimal as there is only increase of 8 bytes
per the struct expr_constant.

Reported-at: https://issues.redhat.com/browse/FDP-509
Signed-off-by: Ales Musil <amusil@redhat.com>
---
v4: Rebase on top of current main.
    Update the "lflow_handle_addr_set_update" comment according to suggestion from Han.
v3: Rebase on top of current main.
    Address comments from Han:
    - Adjust the comment for "lflow_handle_addr_set_update" to include remaning corner cases.
    - Make sure that the flows are consistent between I-P and recompute.
v2: Rebase on top of current main.
    Adjust the comment for I-P optimization.
---
 controller/lflow.c      | 11 ++---
 include/ovn/actions.h   |  2 +-
 include/ovn/expr.h      | 46 ++++++++++---------
 lib/actions.c           | 20 ++++-----
 lib/expr.c              | 99 +++++++++++++++++------------------------
 tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
 6 files changed, 154 insertions(+), 103 deletions(-)

Comments

Han Zhou May 2, 2024, 4:22 p.m. UTC | #1
On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
>
> Instead of tracking address set per struct expr_constant_set track it
> per individual struct expr_constant. This allows more fine grained
> control for I-P processing of address sets in controller. It helps with
> scenarios like matching on two address sets in one expression e.g.
> "ip4.src == {$as1, $as2}". This allows any addition or removal of
> individual adress from the set to be incrementally processed instead
> of reprocessing all the flows.
>
> This unfortunately doesn't help with the following flows:
> "ip4.src == $as1 && ip4.dst == $as2"
> "ip4.src == $as1 || ip4.dst == $as2"
>
> The memory impact should be minimal as there is only increase of 8 bytes
> per the struct expr_constant.
>
> Reported-at: https://issues.redhat.com/browse/FDP-509
> Signed-off-by: Ales Musil <amusil@redhat.com>
> ---
> v4: Rebase on top of current main.
>     Update the "lflow_handle_addr_set_update" comment according to
suggestion from Han.

Thanks Ales. I updated the commit message for the same, and applied to main
branch.

Regards,
Han

> v3: Rebase on top of current main.
>     Address comments from Han:
>     - Adjust the comment for "lflow_handle_addr_set_update" to include
remaning corner cases.
>     - Make sure that the flows are consistent between I-P and recompute.
> v2: Rebase on top of current main.
>     Adjust the comment for I-P optimization.
> ---
>  controller/lflow.c      | 11 ++---
>  include/ovn/actions.h   |  2 +-
>  include/ovn/expr.h      | 46 ++++++++++---------
>  lib/actions.c           | 20 ++++-----
>  lib/expr.c              | 99 +++++++++++++++++------------------------
>  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
>  6 files changed, 154 insertions(+), 103 deletions(-)
>
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 760ec0b41..1e05665a1 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
*l_ctx_in,
>  }
>
>  static bool
> -as_info_from_expr_const(const char *as_name, const union expr_constant
*c,
> +as_info_from_expr_const(const char *as_name, const struct expr_constant
*c,
>                          struct addrset_info *as_info)
>  {
>      as_info->name = as_name;
> @@ -644,14 +644,11 @@ as_update_can_be_handled(const char *as_name,
struct addr_set_diff *as_diff,
>   *        generated.
>   *
>   *      - The sub expression of the address set is combined with other
sub-
> - *        expressions/constants, usually because of disjunctions between
> - *        sub-expressions/constants, e.g.:
> + *        expressions/constants on different fields, e.g.:
>   *
>   *          ip.src == $as1 || ip.dst == $as2
> - *          ip.src == {$as1, $as2}
> - *          ip.src == {$as1, ip1}
>   *
> - *        All these could have been split into separate lflows.
> + *        This could have been split into separate lflows.
>   *
>   *      - Conjunctions overlapping between lflows, which can be caused by
>   *        overlapping address sets or same address set used by multiple
lflows
> @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char *as_name,
>          if (as_diff->deleted) {
>              struct addrset_info as_info;
>              for (size_t i = 0; i < as_diff->deleted->n_values; i++) {
> -                union expr_constant *c = &as_diff->deleted->values[i];
> +                struct expr_constant *c = &as_diff->deleted->values[i];
>                  if (!as_info_from_expr_const(as_name, c, &as_info)) {
>                      continue;
>                  }
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index ae0864fdd..88cf4de79 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -241,7 +241,7 @@ struct ovnact_next {
>  struct ovnact_load {
>      struct ovnact ovnact;
>      struct expr_field dst;
> -    union expr_constant imm;
> +    struct expr_constant imm;
>  };
>
>  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> index c48f82398..e54edb5bf 100644
> --- a/include/ovn/expr.h
> +++ b/include/ovn/expr.h
> @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type, enum
expr_relop *relop);
>  struct expr {
>      struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR if
any. */
>      enum expr_type type;        /* Expression type. */
> -    char *as_name;              /* Address set name. Null if it is not an
> +    const char *as_name;        /* Address set name. Null if it is not an
>                                     address set. */
>
>      union {
> @@ -505,40 +505,42 @@ enum expr_constant_type {
>  };
>
>  /* A string or integer constant (one must know which from context). */
> -union expr_constant {
> -    /* Integer constant.
> -     *
> -     * The width of a constant isn't always clear, e.g. if you write "1",
> -     * there's no way to tell whether you mean for that to be a 1-bit
constant
> -     * or a 128-bit constant or somewhere in between. */
> -    struct {
> -        union mf_subvalue value;
> -        union mf_subvalue mask; /* Only initialized if 'masked'. */
> -        bool masked;
> -
> -        enum lex_format format; /* From the constant's lex_token. */
> -    };
> +struct expr_constant {
> +    const char *as_name;
>
> -    /* Null-terminated string constant. */
> -    char *string;
> +    union {
> +        /* Integer constant.
> +         *
> +         * The width of a constant isn't always clear, e.g. if you write
"1",
> +         * there's no way to tell whether you mean for that to be a 1-bit
> +         * constant or a 128-bit constant or somewhere in between. */
> +        struct {
> +            union mf_subvalue value;
> +            union mf_subvalue mask; /* Only initialized if 'masked'. */
> +            bool masked;
> +
> +            enum lex_format format; /* From the constant's lex_token. */
> +        };
> +
> +        /* Null-terminated string constant. */
> +        char *string;
> +    };
>  };
>
>  bool expr_constant_parse(struct lexer *,
>                           const struct expr_field *,
> -                         union expr_constant *);
> -void expr_constant_format(const union expr_constant *,
> +                         struct expr_constant *);
> +void expr_constant_format(const struct expr_constant *,
>                            enum expr_constant_type, struct ds *);
> -void expr_constant_destroy(const union expr_constant *,
> +void expr_constant_destroy(const struct expr_constant *,
>                             enum expr_constant_type);
>
>  /* A collection of "union expr_constant"s of the same type. */
>  struct expr_constant_set {
> -    union expr_constant *values;  /* Constants. */
> +    struct expr_constant *values; /* Constants. */
>      size_t n_values;              /* Number of constants. */
>      enum expr_constant_type type; /* Type of the constants. */
>      bool in_curlies;              /* Whether the constants were in {}. */
> -    char *as_name;                /* Name of an address set. It is NULL
if not
> -                                     an address set. */
>  };
>
>  bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
> diff --git a/lib/actions.c b/lib/actions.c
> index 94751d059..cff4f9e3c 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
>              const struct ovnact_encode_params *ep,
>              struct ofpbuf *ofpacts)
>  {
> -    const union expr_constant *c = &load->imm;
> +    const struct expr_constant *c = &load->imm;
>      struct mf_subfield dst = expr_resolve_field(&load->dst);
>      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
dst.field,
>                                                         NULL, NULL);
> @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct ofpbuf
*ofpacts,
>          /* All empty_lb_backends fields are of type 'str' */
>          ovs_assert(!strcmp(o->option->type, "str"));
>
> -        const union expr_constant *c = o->value.values;
> +        const struct expr_constant *c = o->value.values;
>          size_t size = strlen(c->string);
>          struct controller_event_opt_header hdr =
>              (struct controller_event_opt_header) {
> @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct action_context
*ctx,
>  {
>      for (size_t i = 0; i < n_options; i++) {
>          const struct ovnact_gen_option *o = &options[i];
> -        const union expr_constant *c = o->value.values;
> +        const struct expr_constant *c = o->value.values;
>          struct sockaddr_storage ss;
>          struct uuid uuid;
>
> @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
ovnact_gen_option *o,
>      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>      opt_header[0] = o->option->code;
>
> -    const union expr_constant *c = o->value.values;
> +    const struct expr_constant *c = o->value.values;
>      size_t n_values = o->value.n_values;
>      if (!strcmp(o->option->type, "bool") ||
>          !strcmp(o->option->type, "uint8")) {
> @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
ovnact_gen_option *o,
>                           struct ofpbuf *ofpacts)
>  {
>      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts, sizeof
*opt);
> -    const union expr_constant *c = o->value.values;
> +    const struct expr_constant *c = o->value.values;
>      size_t n_values = o->value.n_values;
>      size_t size;
>
> @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts
*pdo,
>          find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
>      if (boot_opt) {
>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> -        const union expr_constant *c = boot_opt->value.values;
> +        const struct expr_constant *c = boot_opt->value.values;
>          opt_header[0] = boot_opt->option->code;
>          opt_header[1] = strlen(c->string);
>          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts
*pdo,
>          find_opt(pdo->options, pdo->n_options,
DHCP_OPT_BOOTFILE_ALT_CODE);
>      if (boot_alt_opt) {
>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> -        const union expr_constant *c = boot_alt_opt->value.values;
> +        const struct expr_constant *c = boot_alt_opt->value.values;
>          opt_header[0] = boot_alt_opt->option->code;
>          opt_header[1] = strlen(c->string);
>          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts
*pdo,
>          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
>      if (next_server_opt) {
>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> -        const union expr_constant *c = next_server_opt->value.values;
> +        const struct expr_constant *c = next_server_opt->value.values;
>          opt_header[0] = next_server_opt->option->code;
>          opt_header[1] = sizeof(ovs_be32);
>          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
> @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context *ctx,
const struct expr_field *dst,
>      /* Let's validate the options. */
>      for (size_t i = 0; i < po->n_options; i++) {
>          const struct ovnact_gen_option *o = &po->options[i];
> -        const union expr_constant *c = o->value.values;
> +        const struct expr_constant *c = o->value.values;
>          if (o->value.n_values > 1) {
>              lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
>                          o->option->name);
> @@ -3490,7 +3490,7 @@ static void
>  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
>                          struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
>  {
> -    const union expr_constant *c = o->value.values;
> +    const struct expr_constant *c = o->value.values;
>
>      switch (o->option->code) {
>      case ND_RA_FLAG_ADDR_MODE:
> diff --git a/lib/expr.c b/lib/expr.c
> index 0cb44e1b6..ecf8a6338 100644
> --- a/lib/expr.c
> +++ b/lib/expr.c
> @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct expr *a,
struct expr *b)
>          } else {
>              ovs_list_push_back(&a->andor, &b->node);
>          }
> -        free(a->as_name);
>          a->as_name = NULL;
>          return a;
>      } else if (b->type == type) {
>          ovs_list_push_front(&b->andor, &a->node);
> -        free(b->as_name);
>          b->as_name = NULL;
>          return b;
>      } else {
> @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context *,
struct expr_field *);
>
>  static struct expr *
>  make_cmp__(const struct expr_field *f, enum expr_relop r,
> -             const union expr_constant *c)
> +             const struct expr_constant *c)
>  {
>      struct expr *e = xzalloc(sizeof *e);
>      e->type = EXPR_T_CMP;
>      e->cmp.symbol = f->symbol;
>      e->cmp.relop = r;
> +    e->as_name = c->as_name;
>      if (f->symbol->width) {
>          bitwise_copy(&c->value, sizeof c->value, 0,
>                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
> @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
expr_relop r,
>
>  /* Returns the minimum reasonable width for integer constant 'c'. */
>  static int
> -expr_constant_width(const union expr_constant *c)
> +expr_constant_width(const struct expr_constant *c)
>  {
>      if (c->masked) {
>          return mf_subvalue_width(&c->mask);
> @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
>          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
>                           e, make_cmp__(f, r, &cs->values[i]));
>      }
> -    /* Track address set */
> -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
> -        e->as_name = xstrdup(cs->as_name);
> -    }
> +
>  exit:
>      expr_constant_set_destroy(cs);
>      return e;
> @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx, struct
expr_constant_set *cs,
>          }
>      }
>
> -    struct expr_constant_set *addr_sets
> -        = (ctx->addr_sets
> -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
> -           : NULL);
> -    if (!addr_sets) {
> +    struct shash_node *node = ctx->addr_sets
> +                              ? shash_find(ctx->addr_sets,
ctx->lexer->token.s)
> +                              : NULL;
> +    if (!node) {
>          lexer_syntax_error(ctx->lexer, "expecting address set name");
>          return false;
>      }
> @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx, struct
expr_constant_set *cs,
>          return false;
>      }
>
> -    if (!cs->n_values) {
> -        cs->as_name = xstrdup(ctx->lexer->token.s);
> -    }
> -
> +    struct expr_constant_set *addr_sets = node->data;
>      size_t n_values = cs->n_values + addr_sets->n_values;
>      if (n_values >= *allocated_values) {
>          cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
>          *allocated_values = n_values;
>      }
>      for (size_t i = 0; i < addr_sets->n_values; i++) {
> -        cs->values[cs->n_values++] = addr_sets->values[i];
> +        struct expr_constant *c = &cs->values[cs->n_values++];
> +        *c = addr_sets->values[i];
> +        c->as_name = node->name;
>      }
>
>      return true;
> @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx, struct
expr_constant_set *cs,
>          *allocated_values = n_values;
>      }
>      for (size_t i = 0; i < port_group->n_values; i++) {
> -        cs->values[cs->n_values++].string =
> -            xstrdup(port_group->values[i].string);
> +        struct expr_constant *c = &cs->values[cs->n_values++];
> +        c->string = xstrdup(port_group->values[i].string);
> +        c->as_name = NULL;
>      }
>
>      return true;
> @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
>                                  sizeof *cs->values);
>      }
>
> -    /* Combining other values to the constant set that is tracking an
> -     * address set, so untrack it. */
> -    free(cs->as_name);
> -    cs->as_name = NULL;
> -
>      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>          lexer_error(ctx->lexer, "Unexpanded template.");
>          return false;
> @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
>          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
>              return false;
>          }
> -        cs->values[cs->n_values++].string = xstrdup(ctx->lexer->token.s);
> +        struct expr_constant *c = &cs->values[cs->n_values++];
> +        c->string = xstrdup(ctx->lexer->token.s);
> +        c->as_name = NULL;
>          lexer_get(ctx->lexer);
>          return true;
>      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
>              return false;
>          }
>
> -        union expr_constant *c = &cs->values[cs->n_values++];
> +        struct expr_constant *c = &cs->values[cs->n_values++];
>          c->value = ctx->lexer->token.value;
>          c->format = ctx->lexer->token.format;
>          c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
>          if (c->masked) {
>              c->mask = ctx->lexer->token.mask;
>          }
> +        c->as_name = NULL;
>          lexer_get(ctx->lexer);
>          return true;
>      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx, struct
expr_constant_set *cs)
>   * indeterminate. */
>  bool
>  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
> -                    union expr_constant *c)
> +                    struct expr_constant *c)
>  {
>      if (lexer->error) {
>          return false;
> @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const struct
expr_field *f,
>  /* Appends to 's' a re-parseable representation of constant 'c' with the
given
>   * 'type'. */
>  void
> -expr_constant_format(const union expr_constant *c,
> +expr_constant_format(const struct expr_constant *c,
>                       enum expr_constant_type type, struct ds *s)
>  {
>      if (type == EXPR_C_STRING) {
> @@ -1010,7 +1003,7 @@ expr_constant_format(const union expr_constant *c,
>   *
>   * Does not free(c). */
>  void
> -expr_constant_destroy(const union expr_constant *c,
> +expr_constant_destroy(const struct expr_constant *c,
>                        enum expr_constant_type type)
>  {
>      if (c && type == EXPR_C_STRING) {
> @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
expr_constant_set *cs, struct ds *s)
>          ds_put_char(s, '{');
>      }
>
> -    for (const union expr_constant *c = cs->values;
> +    for (const struct expr_constant *c = cs->values;
>           c < &cs->values[cs->n_values]; c++) {
>          if (c != cs->values) {
>              ds_put_cstr(s, ", ");
> @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
expr_constant_set *cs)
>              }
>          }
>          free(cs->values);
> -        free(cs->as_name);
>      }
>  }
>
>  static int
>  compare_expr_constant_integer_cb(const void *a_, const void *b_)
>  {
> -    const union expr_constant *a = a_;
> -    const union expr_constant *b = b_;
> +    const struct expr_constant *a = a_;
> +    const struct expr_constant *b = b_;
>
>      int d = memcmp(&a->value, &b->value, sizeof a->value);
>      if (d) {
> @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char *const
*values, size_t n_values)
>              VLOG_WARN("Invalid constant set entry: '%s', token type: %d",
>                        values[i], lex.token.type);
>          } else {
> -            union expr_constant *c = &cs->values[cs->n_values++];
> +            struct expr_constant *c = &cs->values[cs->n_values++];
>              c->value = lex.token.value;
>              c->format = lex.token.format;
>              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
> @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char *const
*values, size_t n_values)
>
>  static void
>  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> -                            union expr_constant *c, size_t *allocated)
> +                            struct expr_constant *c, size_t *allocated)
>  {
>      struct expr_constant_set *cs = *p_cs;
>      if (!cs) {
> @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
*const_sets, const char *name,
>                          values[i], name);
>              continue;
>          }
> -        union expr_constant *c = &cs->values[cs->n_values++];
> +        struct expr_constant *c = &cs->values[cs->n_values++];
>          c->string = xstrdup(values[i]);
>      }
>
> @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx, bool
*atomic)
>
>              *atomic = true;
>
> -            union expr_constant *cst = xzalloc(sizeof *cst);
> +            struct expr_constant *cst = xzalloc(sizeof *cst);
>              cst->format = LEX_F_HEXADECIMAL;
>              cst->masked = false;
>
> @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx, bool
*atomic)
>              c.values = cst;
>              c.n_values = 1;
>              c.in_curlies = false;
> -            c.as_name = NULL;
>              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>          } else if (parse_relop(ctx, &r) && parse_constant_set(ctx, &c)) {
>              return make_cmp(ctx, &f, r, &c);
> @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
>  static struct expr *
>  expr_clone_cmp(struct expr *expr)
>  {
> -    ovs_assert(!expr->as_name);
>      struct expr *new = xmemdup(expr, sizeof *expr);
>      if (!new->cmp.symbol->width) {
>          new->cmp.string = xstrdup(new->cmp.string);
> @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
>  static struct expr *
>  expr_clone_condition(struct expr *expr)
>  {
> -    ovs_assert(!expr->as_name);
>      struct expr *new = xmemdup(expr, sizeof *expr);
>      new->cond.string = xstrdup(new->cond.string);
>      return new;
> @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>          return;
>      }
>
> -    free(expr->as_name);
> -
>      struct expr *sub;
>
>      switch (expr->type) {
> @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const struct
expr_symbol *symbol)
>       * mask sizes. */
>      size_t n = ovs_list_size(&expr->andor);
>      struct expr **subs = xmalloc(n * sizeof *subs);
> -    bool modified = false;
> +    bool has_addr_set = false;
>      /* Linked list over the 'subs' array to quickly skip deleted
elements,
>       * i.e. the index of the next potentially non-NULL element. */
>      size_t *next = xmalloc(n * sizeof *next);
> @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const struct
expr_symbol *symbol)
>      size_t i = 0, j, max_n_bits = 0;
>      struct expr *sub;
>      LIST_FOR_EACH (sub, node, &expr->andor) {
> +        if (sub->as_name) {
> +            has_addr_set = true;
> +        }
>          if (symbol->width) {
>              const unsigned long *sub_mask;
>
> @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
>              next[last] = i;
>              last = i;
>          } else {
> +            /* Remove address set reference from the duplicate. */
> +            subs[last]->as_name = NULL;
>              expr_destroy(subs[i]);
>              subs[i] = NULL;
> -            modified = true;
>          }
>      }
>
> -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> -        || (!modified && expr->as_name)) {
> +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
has_addr_set) {
>          /* Not a fully maskable field or this expression is tracking an
>           * address set.  Don't try to optimize to preserve address set
I-P. */
>          goto done;
> @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
>              if (expr_bitmap_intersect_check(a_value, a_mask, b_value,
b_mask,
>                                              bit_width)
>                  && bitmap_is_superset(b_mask, a_mask, bit_width)) {
> -                /* 'a' is the same expression with a smaller mask. */
> +                /* 'a' is the same expression with a smaller mask.
> +                 * Remove address set reference from the duplicate. */
> +                a->as_name = NULL;
>                  expr_destroy(subs[j]);
>                  subs[j] = NULL;
> -                modified = true;
>
>                  /* Shorten the path for the next round. */
>                  if (last) {
> @@ -2685,12 +2676,6 @@ done:
>          }
>      }
>
> -    if (modified) {
> -        /* Members modified, so untrack address set. */
> -        free(expr->as_name);
> -        expr->as_name = NULL;
> -    }
> -
>      free(next);
>      free(subs);
>      return expr;
> @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
>      LIST_FOR_EACH (sub, node, &or->andor) {
>          struct expr_match *match = expr_match_new(m, clause, n_clauses,
>                                                    conj_id);
> -        if (or->as_name) {
> +        if (sub->as_name) {
>              ovs_assert(sub->type == EXPR_T_CMP);
>              ovs_assert(sub->cmp.symbol->width);
> -            match->as_name = xstrdup(or->as_name);
> +            match->as_name = xstrdup(sub->as_name);
>              match->as_ip = sub->cmp.value.ipv6;
>              match->as_mask = sub->cmp.mask.ipv6;
>          }
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index be198e00d..27cec2aec 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -1625,7 +1625,9 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
actions=lo
>  done
>
>  reprocess_count_new=$(read_counter consider_logical_flow)
> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
[10
> +# First 2 reprocess are due to change from 1 IP in AS to 2.
> +# Last 5 is due to overlap in IP addresses between as1 and as2.
> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
>  ])
>
>  # Remove the IPs from as1 and as2, 1 IP each time.
> @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
>  done
>
>  reprocess_count_new=$(read_counter consider_logical_flow)
> -# In this case the as1 and as2 are merged to a single OR expr, so we
lose track of
> -# address set information - can't handle deletion incrementally.
> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0],
[10
> +# First 5 are due to overlap in IP addresses between as1 and as2.
> +# Last 2 are due to change from 2 IP in AS to 1.
> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
>  ])
>
>  OVN_CLEANUP([hv1])
> @@ -1817,7 +1819,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
actions=co
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
>  ])
>  reprocess_count_new=$(read_counter consider_logical_flow)
> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
>  ])
>
>  # Delete 2 IPs
> @@ -1837,7 +1839,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
actions=co
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
>  ])
>  reprocess_count_new=$(read_counter consider_logical_flow)
> -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
> +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
>  ])
>
>  OVN_CLEANUP([hv1])
> @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
>
>  AT_CLEANUP
>  ])
> +
> +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> +AT_KEYWORDS([as-i-p])
> +
> +ovn_start
> +
> +net_add n1
> +sim_add hv1
> +as hv1
> +check ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> +
> +check ovn-nbctl ls-add ls1
> +
> +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> +
> +wait_for_ports_up
> +ovn-appctl -t ovn-controller vlog/set file:dbg
> +
> +# Get the OF table numbers
> +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
> +
> +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
external_ids:name=ls1))
> +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
logical_port=ls1-lp1))
> +
> +ovn-nbctl create address_set name=as1
> +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" &&
ip4.src == $as1' drop
> +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> +check ovn-nbctl --wait=hv sync
> +
> +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +])
> +
> +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> +check ovn-nbctl --wait=hv sync
> +
> +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +])
> +
> +
> +check ovn-appctl inc-engine/recompute
> +
> +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> +])
> +
> +OVN_CLEANUP([hv1])
> +AT_CLEANUP
> --
> 2.44.0
>
Ales Musil May 3, 2024, 5:35 a.m. UTC | #2
On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org> wrote:
>
>
>
> On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
> >
> > Instead of tracking address set per struct expr_constant_set track it
> > per individual struct expr_constant. This allows more fine grained
> > control for I-P processing of address sets in controller. It helps with
> > scenarios like matching on two address sets in one expression e.g.
> > "ip4.src == {$as1, $as2}". This allows any addition or removal of
> > individual adress from the set to be incrementally processed instead
> > of reprocessing all the flows.
> >
> > This unfortunately doesn't help with the following flows:
> > "ip4.src == $as1 && ip4.dst == $as2"
> > "ip4.src == $as1 || ip4.dst == $as2"
> >
> > The memory impact should be minimal as there is only increase of 8 bytes
> > per the struct expr_constant.
> >
> > Reported-at: https://issues.redhat.com/browse/FDP-509
> > Signed-off-by: Ales Musil <amusil@redhat.com>
> > ---
> > v4: Rebase on top of current main.
> >     Update the "lflow_handle_addr_set_update" comment according to suggestion from Han.
>
> Thanks Ales. I updated the commit message for the same, and applied to main branch.
>
> Regards,
> Han
>
> > v3: Rebase on top of current main.
> >     Address comments from Han:
> >     - Adjust the comment for "lflow_handle_addr_set_update" to include remaning corner cases.
> >     - Make sure that the flows are consistent between I-P and recompute.
> > v2: Rebase on top of current main.
> >     Adjust the comment for I-P optimization.
> > ---
> >  controller/lflow.c      | 11 ++---
> >  include/ovn/actions.h   |  2 +-
> >  include/ovn/expr.h      | 46 ++++++++++---------
> >  lib/actions.c           | 20 ++++-----
> >  lib/expr.c              | 99 +++++++++++++++++------------------------
> >  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
> >  6 files changed, 154 insertions(+), 103 deletions(-)
> >
> > diff --git a/controller/lflow.c b/controller/lflow.c
> > index 760ec0b41..1e05665a1 100644
> > --- a/controller/lflow.c
> > +++ b/controller/lflow.c
> > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
> >  }
> >
> >  static bool
> > -as_info_from_expr_const(const char *as_name, const union expr_constant *c,
> > +as_info_from_expr_const(const char *as_name, const struct expr_constant *c,
> >                          struct addrset_info *as_info)
> >  {
> >      as_info->name = as_name;
> > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char *as_name, struct addr_set_diff *as_diff,
> >   *        generated.
> >   *
> >   *      - The sub expression of the address set is combined with other sub-
> > - *        expressions/constants, usually because of disjunctions between
> > - *        sub-expressions/constants, e.g.:
> > + *        expressions/constants on different fields, e.g.:
> >   *
> >   *          ip.src == $as1 || ip.dst == $as2
> > - *          ip.src == {$as1, $as2}
> > - *          ip.src == {$as1, ip1}
> >   *
> > - *        All these could have been split into separate lflows.
> > + *        This could have been split into separate lflows.
> >   *
> >   *      - Conjunctions overlapping between lflows, which can be caused by
> >   *        overlapping address sets or same address set used by multiple lflows
> > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char *as_name,
> >          if (as_diff->deleted) {
> >              struct addrset_info as_info;
> >              for (size_t i = 0; i < as_diff->deleted->n_values; i++) {
> > -                union expr_constant *c = &as_diff->deleted->values[i];
> > +                struct expr_constant *c = &as_diff->deleted->values[i];
> >                  if (!as_info_from_expr_const(as_name, c, &as_info)) {
> >                      continue;
> >                  }
> > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > index ae0864fdd..88cf4de79 100644
> > --- a/include/ovn/actions.h
> > +++ b/include/ovn/actions.h
> > @@ -241,7 +241,7 @@ struct ovnact_next {
> >  struct ovnact_load {
> >      struct ovnact ovnact;
> >      struct expr_field dst;
> > -    union expr_constant imm;
> > +    struct expr_constant imm;
> >  };
> >
> >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > index c48f82398..e54edb5bf 100644
> > --- a/include/ovn/expr.h
> > +++ b/include/ovn/expr.h
> > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop);
> >  struct expr {
> >      struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR if any. */
> >      enum expr_type type;        /* Expression type. */
> > -    char *as_name;              /* Address set name. Null if it is not an
> > +    const char *as_name;        /* Address set name. Null if it is not an
> >                                     address set. */
> >
> >      union {
> > @@ -505,40 +505,42 @@ enum expr_constant_type {
> >  };
> >
> >  /* A string or integer constant (one must know which from context). */
> > -union expr_constant {
> > -    /* Integer constant.
> > -     *
> > -     * The width of a constant isn't always clear, e.g. if you write "1",
> > -     * there's no way to tell whether you mean for that to be a 1-bit constant
> > -     * or a 128-bit constant or somewhere in between. */
> > -    struct {
> > -        union mf_subvalue value;
> > -        union mf_subvalue mask; /* Only initialized if 'masked'. */
> > -        bool masked;
> > -
> > -        enum lex_format format; /* From the constant's lex_token. */
> > -    };
> > +struct expr_constant {
> > +    const char *as_name;
> >
> > -    /* Null-terminated string constant. */
> > -    char *string;
> > +    union {
> > +        /* Integer constant.
> > +         *
> > +         * The width of a constant isn't always clear, e.g. if you write "1",
> > +         * there's no way to tell whether you mean for that to be a 1-bit
> > +         * constant or a 128-bit constant or somewhere in between. */
> > +        struct {
> > +            union mf_subvalue value;
> > +            union mf_subvalue mask; /* Only initialized if 'masked'. */
> > +            bool masked;
> > +
> > +            enum lex_format format; /* From the constant's lex_token. */
> > +        };
> > +
> > +        /* Null-terminated string constant. */
> > +        char *string;
> > +    };
> >  };
> >
> >  bool expr_constant_parse(struct lexer *,
> >                           const struct expr_field *,
> > -                         union expr_constant *);
> > -void expr_constant_format(const union expr_constant *,
> > +                         struct expr_constant *);
> > +void expr_constant_format(const struct expr_constant *,
> >                            enum expr_constant_type, struct ds *);
> > -void expr_constant_destroy(const union expr_constant *,
> > +void expr_constant_destroy(const struct expr_constant *,
> >                             enum expr_constant_type);
> >
> >  /* A collection of "union expr_constant"s of the same type. */
> >  struct expr_constant_set {
> > -    union expr_constant *values;  /* Constants. */
> > +    struct expr_constant *values; /* Constants. */
> >      size_t n_values;              /* Number of constants. */
> >      enum expr_constant_type type; /* Type of the constants. */
> >      bool in_curlies;              /* Whether the constants were in {}. */
> > -    char *as_name;                /* Name of an address set. It is NULL if not
> > -                                     an address set. */
> >  };
> >
> >  bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
> > diff --git a/lib/actions.c b/lib/actions.c
> > index 94751d059..cff4f9e3c 100644
> > --- a/lib/actions.c
> > +++ b/lib/actions.c
> > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
> >              const struct ovnact_encode_params *ep,
> >              struct ofpbuf *ofpacts)
> >  {
> > -    const union expr_constant *c = &load->imm;
> > +    const struct expr_constant *c = &load->imm;
> >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> >      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, dst.field,
> >                                                         NULL, NULL);
> > @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> >          /* All empty_lb_backends fields are of type 'str' */
> >          ovs_assert(!strcmp(o->option->type, "str"));
> >
> > -        const union expr_constant *c = o->value.values;
> > +        const struct expr_constant *c = o->value.values;
> >          size_t size = strlen(c->string);
> >          struct controller_event_opt_header hdr =
> >              (struct controller_event_opt_header) {
> > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct action_context *ctx,
> >  {
> >      for (size_t i = 0; i < n_options; i++) {
> >          const struct ovnact_gen_option *o = &options[i];
> > -        const union expr_constant *c = o->value.values;
> > +        const struct expr_constant *c = o->value.values;
> >          struct sockaddr_storage ss;
> >          struct uuid uuid;
> >
> > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
> >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >      opt_header[0] = o->option->code;
> >
> > -    const union expr_constant *c = o->value.values;
> > +    const struct expr_constant *c = o->value.values;
> >      size_t n_values = o->value.n_values;
> >      if (!strcmp(o->option->type, "bool") ||
> >          !strcmp(o->option->type, "uint8")) {
> > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct ovnact_gen_option *o,
> >                           struct ofpbuf *ofpacts)
> >  {
> >      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts, sizeof *opt);
> > -    const union expr_constant *c = o->value.values;
> > +    const struct expr_constant *c = o->value.values;
> >      size_t n_values = o->value.n_values;
> >      size_t size;
> >
> > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
> >          find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
> >      if (boot_opt) {
> >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > -        const union expr_constant *c = boot_opt->value.values;
> > +        const struct expr_constant *c = boot_opt->value.values;
> >          opt_header[0] = boot_opt->option->code;
> >          opt_header[1] = strlen(c->string);
> >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
> >          find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_ALT_CODE);
> >      if (boot_alt_opt) {
> >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > -        const union expr_constant *c = boot_alt_opt->value.values;
> > +        const struct expr_constant *c = boot_alt_opt->value.values;
> >          opt_header[0] = boot_alt_opt->option->code;
> >          opt_header[1] = strlen(c->string);
> >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
> >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
> >      if (next_server_opt) {
> >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > -        const union expr_constant *c = next_server_opt->value.values;
> > +        const struct expr_constant *c = next_server_opt->value.values;
> >          opt_header[0] = next_server_opt->option->code;
> >          opt_header[1] = sizeof(ovs_be32);
> >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
> > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst,
> >      /* Let's validate the options. */
> >      for (size_t i = 0; i < po->n_options; i++) {
> >          const struct ovnact_gen_option *o = &po->options[i];
> > -        const union expr_constant *c = o->value.values;
> > +        const struct expr_constant *c = o->value.values;
> >          if (o->value.n_values > 1) {
> >              lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
> >                          o->option->name);
> > @@ -3490,7 +3490,7 @@ static void
> >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> >                          struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
> >  {
> > -    const union expr_constant *c = o->value.values;
> > +    const struct expr_constant *c = o->value.values;
> >
> >      switch (o->option->code) {
> >      case ND_RA_FLAG_ADDR_MODE:
> > diff --git a/lib/expr.c b/lib/expr.c
> > index 0cb44e1b6..ecf8a6338 100644
> > --- a/lib/expr.c
> > +++ b/lib/expr.c
> > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct expr *a, struct expr *b)
> >          } else {
> >              ovs_list_push_back(&a->andor, &b->node);
> >          }
> > -        free(a->as_name);
> >          a->as_name = NULL;
> >          return a;
> >      } else if (b->type == type) {
> >          ovs_list_push_front(&b->andor, &a->node);
> > -        free(b->as_name);
> >          b->as_name = NULL;
> >          return b;
> >      } else {
> > @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context *, struct expr_field *);
> >
> >  static struct expr *
> >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > -             const union expr_constant *c)
> > +             const struct expr_constant *c)
> >  {
> >      struct expr *e = xzalloc(sizeof *e);
> >      e->type = EXPR_T_CMP;
> >      e->cmp.symbol = f->symbol;
> >      e->cmp.relop = r;
> > +    e->as_name = c->as_name;
> >      if (f->symbol->width) {
> >          bitwise_copy(&c->value, sizeof c->value, 0,
> >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
> > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum expr_relop r,
> >
> >  /* Returns the minimum reasonable width for integer constant 'c'. */
> >  static int
> > -expr_constant_width(const union expr_constant *c)
> > +expr_constant_width(const struct expr_constant *c)
> >  {
> >      if (c->masked) {
> >          return mf_subvalue_width(&c->mask);
> > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
> >                           e, make_cmp__(f, r, &cs->values[i]));
> >      }
> > -    /* Track address set */
> > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
> > -        e->as_name = xstrdup(cs->as_name);
> > -    }
> > +
> >  exit:
> >      expr_constant_set_destroy(cs);
> >      return e;
> > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx, struct expr_constant_set *cs,
> >          }
> >      }
> >
> > -    struct expr_constant_set *addr_sets
> > -        = (ctx->addr_sets
> > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
> > -           : NULL);
> > -    if (!addr_sets) {
> > +    struct shash_node *node = ctx->addr_sets
> > +                              ? shash_find(ctx->addr_sets, ctx->lexer->token.s)
> > +                              : NULL;
> > +    if (!node) {
> >          lexer_syntax_error(ctx->lexer, "expecting address set name");
> >          return false;
> >      }
> > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx, struct expr_constant_set *cs,
> >          return false;
> >      }
> >
> > -    if (!cs->n_values) {
> > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > -    }
> > -
> > +    struct expr_constant_set *addr_sets = node->data;
> >      size_t n_values = cs->n_values + addr_sets->n_values;
> >      if (n_values >= *allocated_values) {
> >          cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
> >          *allocated_values = n_values;
> >      }
> >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > -        cs->values[cs->n_values++] = addr_sets->values[i];
> > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > +        *c = addr_sets->values[i];
> > +        c->as_name = node->name;
> >      }
> >
> >      return true;
> > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs,
> >          *allocated_values = n_values;
> >      }
> >      for (size_t i = 0; i < port_group->n_values; i++) {
> > -        cs->values[cs->n_values++].string =
> > -            xstrdup(port_group->values[i].string);
> > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > +        c->string = xstrdup(port_group->values[i].string);
> > +        c->as_name = NULL;
> >      }
> >
> >      return true;
> > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
> >                                  sizeof *cs->values);
> >      }
> >
> > -    /* Combining other values to the constant set that is tracking an
> > -     * address set, so untrack it. */
> > -    free(cs->as_name);
> > -    cs->as_name = NULL;
> > -
> >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> >          lexer_error(ctx->lexer, "Unexpanded template.");
> >          return false;
> > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
> >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
> >              return false;
> >          }
> > -        cs->values[cs->n_values++].string = xstrdup(ctx->lexer->token.s);
> > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > +        c->string = xstrdup(ctx->lexer->token.s);
> > +        c->as_name = NULL;
> >          lexer_get(ctx->lexer);
> >          return true;
> >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
> >              return false;
> >          }
> >
> > -        union expr_constant *c = &cs->values[cs->n_values++];
> > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >          c->value = ctx->lexer->token.value;
> >          c->format = ctx->lexer->token.format;
> >          c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
> >          if (c->masked) {
> >              c->mask = ctx->lexer->token.mask;
> >          }
> > +        c->as_name = NULL;
> >          lexer_get(ctx->lexer);
> >          return true;
> >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
> >   * indeterminate. */
> >  bool
> >  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
> > -                    union expr_constant *c)
> > +                    struct expr_constant *c)
> >  {
> >      if (lexer->error) {
> >          return false;
> > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
> >  /* Appends to 's' a re-parseable representation of constant 'c' with the given
> >   * 'type'. */
> >  void
> > -expr_constant_format(const union expr_constant *c,
> > +expr_constant_format(const struct expr_constant *c,
> >                       enum expr_constant_type type, struct ds *s)
> >  {
> >      if (type == EXPR_C_STRING) {
> > @@ -1010,7 +1003,7 @@ expr_constant_format(const union expr_constant *c,
> >   *
> >   * Does not free(c). */
> >  void
> > -expr_constant_destroy(const union expr_constant *c,
> > +expr_constant_destroy(const struct expr_constant *c,
> >                        enum expr_constant_type type)
> >  {
> >      if (c && type == EXPR_C_STRING) {
> > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct expr_constant_set *cs, struct ds *s)
> >          ds_put_char(s, '{');
> >      }
> >
> > -    for (const union expr_constant *c = cs->values;
> > +    for (const struct expr_constant *c = cs->values;
> >           c < &cs->values[cs->n_values]; c++) {
> >          if (c != cs->values) {
> >              ds_put_cstr(s, ", ");
> > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct expr_constant_set *cs)
> >              }
> >          }
> >          free(cs->values);
> > -        free(cs->as_name);
> >      }
> >  }
> >
> >  static int
> >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
> >  {
> > -    const union expr_constant *a = a_;
> > -    const union expr_constant *b = b_;
> > +    const struct expr_constant *a = a_;
> > +    const struct expr_constant *b = b_;
> >
> >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> >      if (d) {
> > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char *const *values, size_t n_values)
> >              VLOG_WARN("Invalid constant set entry: '%s', token type: %d",
> >                        values[i], lex.token.type);
> >          } else {
> > -            union expr_constant *c = &cs->values[cs->n_values++];
> > +            struct expr_constant *c = &cs->values[cs->n_values++];
> >              c->value = lex.token.value;
> >              c->format = lex.token.format;
> >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
> > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char *const *values, size_t n_values)
> >
> >  static void
> >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> > -                            union expr_constant *c, size_t *allocated)
> > +                            struct expr_constant *c, size_t *allocated)
> >  {
> >      struct expr_constant_set *cs = *p_cs;
> >      if (!cs) {
> > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash *const_sets, const char *name,
> >                          values[i], name);
> >              continue;
> >          }
> > -        union expr_constant *c = &cs->values[cs->n_values++];
> > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >          c->string = xstrdup(values[i]);
> >      }
> >
> > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx, bool *atomic)
> >
> >              *atomic = true;
> >
> > -            union expr_constant *cst = xzalloc(sizeof *cst);
> > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> >              cst->format = LEX_F_HEXADECIMAL;
> >              cst->masked = false;
> >
> > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx, bool *atomic)
> >              c.values = cst;
> >              c.n_values = 1;
> >              c.in_curlies = false;
> > -            c.as_name = NULL;
> >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> >          } else if (parse_relop(ctx, &r) && parse_constant_set(ctx, &c)) {
> >              return make_cmp(ctx, &f, r, &c);
> > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
> >  static struct expr *
> >  expr_clone_cmp(struct expr *expr)
> >  {
> > -    ovs_assert(!expr->as_name);
> >      struct expr *new = xmemdup(expr, sizeof *expr);
> >      if (!new->cmp.symbol->width) {
> >          new->cmp.string = xstrdup(new->cmp.string);
> > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> >  static struct expr *
> >  expr_clone_condition(struct expr *expr)
> >  {
> > -    ovs_assert(!expr->as_name);
> >      struct expr *new = xmemdup(expr, sizeof *expr);
> >      new->cond.string = xstrdup(new->cond.string);
> >      return new;
> > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> >          return;
> >      }
> >
> > -    free(expr->as_name);
> > -
> >      struct expr *sub;
> >
> >      switch (expr->type) {
> > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
> >       * mask sizes. */
> >      size_t n = ovs_list_size(&expr->andor);
> >      struct expr **subs = xmalloc(n * sizeof *subs);
> > -    bool modified = false;
> > +    bool has_addr_set = false;
> >      /* Linked list over the 'subs' array to quickly skip deleted elements,
> >       * i.e. the index of the next potentially non-NULL element. */
> >      size_t *next = xmalloc(n * sizeof *next);
> > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
> >      size_t i = 0, j, max_n_bits = 0;
> >      struct expr *sub;
> >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > +        if (sub->as_name) {
> > +            has_addr_set = true;
> > +        }
> >          if (symbol->width) {
> >              const unsigned long *sub_mask;
> >
> > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
> >              next[last] = i;
> >              last = i;
> >          } else {
> > +            /* Remove address set reference from the duplicate. */
> > +            subs[last]->as_name = NULL;
> >              expr_destroy(subs[i]);
> >              subs[i] = NULL;
> > -            modified = true;
> >          }
> >      }
> >
> > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > -        || (!modified && expr->as_name)) {
> > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL || has_addr_set) {
> >          /* Not a fully maskable field or this expression is tracking an
> >           * address set.  Don't try to optimize to preserve address set I-P. */
> >          goto done;
> > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
> >              if (expr_bitmap_intersect_check(a_value, a_mask, b_value, b_mask,
> >                                              bit_width)
> >                  && bitmap_is_superset(b_mask, a_mask, bit_width)) {
> > -                /* 'a' is the same expression with a smaller mask. */
> > +                /* 'a' is the same expression with a smaller mask.
> > +                 * Remove address set reference from the duplicate. */
> > +                a->as_name = NULL;
> >                  expr_destroy(subs[j]);
> >                  subs[j] = NULL;
> > -                modified = true;
> >
> >                  /* Shorten the path for the next round. */
> >                  if (last) {
> > @@ -2685,12 +2676,6 @@ done:
> >          }
> >      }
> >
> > -    if (modified) {
> > -        /* Members modified, so untrack address set. */
> > -        free(expr->as_name);
> > -        expr->as_name = NULL;
> > -    }
> > -
> >      free(next);
> >      free(subs);
> >      return expr;
> > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> >      LIST_FOR_EACH (sub, node, &or->andor) {
> >          struct expr_match *match = expr_match_new(m, clause, n_clauses,
> >                                                    conj_id);
> > -        if (or->as_name) {
> > +        if (sub->as_name) {
> >              ovs_assert(sub->type == EXPR_T_CMP);
> >              ovs_assert(sub->cmp.symbol->width);
> > -            match->as_name = xstrdup(or->as_name);
> > +            match->as_name = xstrdup(sub->as_name);
> >              match->as_ip = sub->cmp.value.ipv6;
> >              match->as_mask = sub->cmp.mask.ipv6;
> >          }
> > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> > index be198e00d..27cec2aec 100644
> > --- a/tests/ovn-controller.at
> > +++ b/tests/ovn-controller.at
> > @@ -1625,7 +1625,9 @@ priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8 actions=lo
> >  done
> >
> >  reprocess_count_new=$(read_counter consider_logical_flow)
> > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [10
> > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> > +# Last 5 is due to overlap in IP addresses between as1 and as2.
> > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
> >  ])
> >
> >  # Remove the IPs from as1 and as2, 1 IP each time.
> > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> >  done
> >
> >  reprocess_count_new=$(read_counter consider_logical_flow)
> > -# In this case the as1 and as2 are merged to a single OR expr, so we lose track of
> > -# address set information - can't handle deletion incrementally.
> > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [10
> > +# First 5 are due to overlap in IP addresses between as1 and as2.
> > +# Last 2 are due to change from 2 IP in AS to 1.
> > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
> >  ])
> >
> >  OVN_CLEANUP([hv1])
> > @@ -1817,7 +1819,7 @@ priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5 actions=co
> >  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
> >  ])
> >  reprocess_count_new=$(read_counter consider_logical_flow)
> > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
> > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
> >  ])
> >
> >  # Delete 2 IPs
> > @@ -1837,7 +1839,7 @@ priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3 actions=co
> >  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
> >  ])
> >  reprocess_count_new=$(read_counter consider_logical_flow)
> > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
> > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
> >  ])
> >
> >  OVN_CLEANUP([hv1])
> > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> >
> >  AT_CLEANUP
> >  ])
> > +
> > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> > +AT_KEYWORDS([as-i-p])
> > +
> > +ovn_start
> > +
> > +net_add n1
> > +sim_add hv1
> > +as hv1
> > +check ovs-vsctl add-br br-phys
> > +ovn_attach n1 br-phys 192.168.0.1
> > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > +
> > +check ovn-nbctl ls-add ls1
> > +
> > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > +
> > +wait_for_ports_up
> > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > +
> > +# Get the OF table numbers
> > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
> > +
> > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key external_ids:name=ls1))
> > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key logical_port=ls1-lp1))
> > +
> > +ovn-nbctl create address_set name=as1
> > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" && ip4.src == $as1' drop
> > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > +check ovn-nbctl --wait=hv sync
> > +
> > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +])
> > +
> > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > +check ovn-nbctl --wait=hv sync
> > +
> > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +])
> > +
> > +
> > +check ovn-appctl inc-engine/recompute
> > +
> > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > +])
> > +
> > +OVN_CLEANUP([hv1])
> > +AT_CLEANUP
> > --
> > 2.44.0
> >


Thank you Han,

I would like to start the discussion about the possibility of
backporting this change. The reason for the backport being that this
change might be beneficial to ovn-kubernetes users, as this was
actually observed in ovn-kubernetes with network policies. I know that
strictly speaking this is not a bug fix, because this limitation was
well documented, however I would like to hear the opinion of others.

Thanks,
Ales
Han Zhou May 6, 2024, 6:41 p.m. UTC | #3
On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com> wrote:
>
> On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org> wrote:
> >
> >
> >
> > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
> > >
> > > Instead of tracking address set per struct expr_constant_set track it
> > > per individual struct expr_constant. This allows more fine grained
> > > control for I-P processing of address sets in controller. It helps
with
> > > scenarios like matching on two address sets in one expression e.g.
> > > "ip4.src == {$as1, $as2}". This allows any addition or removal of
> > > individual adress from the set to be incrementally processed instead
> > > of reprocessing all the flows.
> > >
> > > This unfortunately doesn't help with the following flows:
> > > "ip4.src == $as1 && ip4.dst == $as2"
> > > "ip4.src == $as1 || ip4.dst == $as2"
> > >
> > > The memory impact should be minimal as there is only increase of 8
bytes
> > > per the struct expr_constant.
> > >
> > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > Signed-off-by: Ales Musil <amusil@redhat.com>
> > > ---
> > > v4: Rebase on top of current main.
> > >     Update the "lflow_handle_addr_set_update" comment according to
suggestion from Han.
> >
> > Thanks Ales. I updated the commit message for the same, and applied to
main branch.
> >
> > Regards,
> > Han
> >
> > > v3: Rebase on top of current main.
> > >     Address comments from Han:
> > >     - Adjust the comment for "lflow_handle_addr_set_update" to
include remaning corner cases.
> > >     - Make sure that the flows are consistent between I-P and
recompute.
> > > v2: Rebase on top of current main.
> > >     Adjust the comment for I-P optimization.
> > > ---
> > >  controller/lflow.c      | 11 ++---
> > >  include/ovn/actions.h   |  2 +-
> > >  include/ovn/expr.h      | 46 ++++++++++---------
> > >  lib/actions.c           | 20 ++++-----
> > >  lib/expr.c              | 99
+++++++++++++++++------------------------
> > >  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
> > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > >
> > > diff --git a/controller/lflow.c b/controller/lflow.c
> > > index 760ec0b41..1e05665a1 100644
> > > --- a/controller/lflow.c
> > > +++ b/controller/lflow.c
> > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
*l_ctx_in,
> > >  }
> > >
> > >  static bool
> > > -as_info_from_expr_const(const char *as_name, const union
expr_constant *c,
> > > +as_info_from_expr_const(const char *as_name, const struct
expr_constant *c,
> > >                          struct addrset_info *as_info)
> > >  {
> > >      as_info->name = as_name;
> > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char *as_name,
struct addr_set_diff *as_diff,
> > >   *        generated.
> > >   *
> > >   *      - The sub expression of the address set is combined with
other sub-
> > > - *        expressions/constants, usually because of disjunctions
between
> > > - *        sub-expressions/constants, e.g.:
> > > + *        expressions/constants on different fields, e.g.:
> > >   *
> > >   *          ip.src == $as1 || ip.dst == $as2
> > > - *          ip.src == {$as1, $as2}
> > > - *          ip.src == {$as1, ip1}
> > >   *
> > > - *        All these could have been split into separate lflows.
> > > + *        This could have been split into separate lflows.
> > >   *
> > >   *      - Conjunctions overlapping between lflows, which can be
caused by
> > >   *        overlapping address sets or same address set used by
multiple lflows
> > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char *as_name,
> > >          if (as_diff->deleted) {
> > >              struct addrset_info as_info;
> > >              for (size_t i = 0; i < as_diff->deleted->n_values; i++) {
> > > -                union expr_constant *c =
&as_diff->deleted->values[i];
> > > +                struct expr_constant *c =
&as_diff->deleted->values[i];
> > >                  if (!as_info_from_expr_const(as_name, c, &as_info)) {
> > >                      continue;
> > >                  }
> > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > > index ae0864fdd..88cf4de79 100644
> > > --- a/include/ovn/actions.h
> > > +++ b/include/ovn/actions.h
> > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > >  struct ovnact_load {
> > >      struct ovnact ovnact;
> > >      struct expr_field dst;
> > > -    union expr_constant imm;
> > > +    struct expr_constant imm;
> > >  };
> > >
> > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > > index c48f82398..e54edb5bf 100644
> > > --- a/include/ovn/expr.h
> > > +++ b/include/ovn/expr.h
> > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type,
enum expr_relop *relop);
> > >  struct expr {
> > >      struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR
if any. */
> > >      enum expr_type type;        /* Expression type. */
> > > -    char *as_name;              /* Address set name. Null if it is
not an
> > > +    const char *as_name;        /* Address set name. Null if it is
not an
> > >                                     address set. */
> > >
> > >      union {
> > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > >  };
> > >
> > >  /* A string or integer constant (one must know which from context).
*/
> > > -union expr_constant {
> > > -    /* Integer constant.
> > > -     *
> > > -     * The width of a constant isn't always clear, e.g. if you write
"1",
> > > -     * there's no way to tell whether you mean for that to be a
1-bit constant
> > > -     * or a 128-bit constant or somewhere in between. */
> > > -    struct {
> > > -        union mf_subvalue value;
> > > -        union mf_subvalue mask; /* Only initialized if 'masked'. */
> > > -        bool masked;
> > > -
> > > -        enum lex_format format; /* From the constant's lex_token. */
> > > -    };
> > > +struct expr_constant {
> > > +    const char *as_name;
> > >
> > > -    /* Null-terminated string constant. */
> > > -    char *string;
> > > +    union {
> > > +        /* Integer constant.
> > > +         *
> > > +         * The width of a constant isn't always clear, e.g. if you
write "1",
> > > +         * there's no way to tell whether you mean for that to be a
1-bit
> > > +         * constant or a 128-bit constant or somewhere in between. */
> > > +        struct {
> > > +            union mf_subvalue value;
> > > +            union mf_subvalue mask; /* Only initialized if 'masked'.
*/
> > > +            bool masked;
> > > +
> > > +            enum lex_format format; /* From the constant's
lex_token. */
> > > +        };
> > > +
> > > +        /* Null-terminated string constant. */
> > > +        char *string;
> > > +    };
> > >  };
> > >
> > >  bool expr_constant_parse(struct lexer *,
> > >                           const struct expr_field *,
> > > -                         union expr_constant *);
> > > -void expr_constant_format(const union expr_constant *,
> > > +                         struct expr_constant *);
> > > +void expr_constant_format(const struct expr_constant *,
> > >                            enum expr_constant_type, struct ds *);
> > > -void expr_constant_destroy(const union expr_constant *,
> > > +void expr_constant_destroy(const struct expr_constant *,
> > >                             enum expr_constant_type);
> > >
> > >  /* A collection of "union expr_constant"s of the same type. */
> > >  struct expr_constant_set {
> > > -    union expr_constant *values;  /* Constants. */
> > > +    struct expr_constant *values; /* Constants. */
> > >      size_t n_values;              /* Number of constants. */
> > >      enum expr_constant_type type; /* Type of the constants. */
> > >      bool in_curlies;              /* Whether the constants were in
{}. */
> > > -    char *as_name;                /* Name of an address set. It is
NULL if not
> > > -                                     an address set. */
> > >  };
> > >
> > >  bool expr_constant_set_parse(struct lexer *, struct
expr_constant_set *);
> > > diff --git a/lib/actions.c b/lib/actions.c
> > > index 94751d059..cff4f9e3c 100644
> > > --- a/lib/actions.c
> > > +++ b/lib/actions.c
> > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
> > >              const struct ovnact_encode_params *ep,
> > >              struct ofpbuf *ofpacts)
> > >  {
> > > -    const union expr_constant *c = &load->imm;
> > > +    const struct expr_constant *c = &load->imm;
> > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> > >      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
dst.field,
> > >                                                         NULL, NULL);
> > > @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct
ofpbuf *ofpacts,
> > >          /* All empty_lb_backends fields are of type 'str' */
> > >          ovs_assert(!strcmp(o->option->type, "str"));
> > >
> > > -        const union expr_constant *c = o->value.values;
> > > +        const struct expr_constant *c = o->value.values;
> > >          size_t size = strlen(c->string);
> > >          struct controller_event_opt_header hdr =
> > >              (struct controller_event_opt_header) {
> > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
action_context *ctx,
> > >  {
> > >      for (size_t i = 0; i < n_options; i++) {
> > >          const struct ovnact_gen_option *o = &options[i];
> > > -        const union expr_constant *c = o->value.values;
> > > +        const struct expr_constant *c = o->value.values;
> > >          struct sockaddr_storage ss;
> > >          struct uuid uuid;
> > >
> > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
ovnact_gen_option *o,
> > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > >      opt_header[0] = o->option->code;
> > >
> > > -    const union expr_constant *c = o->value.values;
> > > +    const struct expr_constant *c = o->value.values;
> > >      size_t n_values = o->value.n_values;
> > >      if (!strcmp(o->option->type, "bool") ||
> > >          !strcmp(o->option->type, "uint8")) {
> > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
ovnact_gen_option *o,
> > >                           struct ofpbuf *ofpacts)
> > >  {
> > >      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts,
sizeof *opt);
> > > -    const union expr_constant *c = o->value.values;
> > > +    const struct expr_constant *c = o->value.values;
> > >      size_t n_values = o->value.n_values;
> > >      size_t size;
> > >
> > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
> > >          find_opt(pdo->options, pdo->n_options,
DHCP_OPT_BOOTFILE_CODE);
> > >      if (boot_opt) {
> > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > -        const union expr_constant *c = boot_opt->value.values;
> > > +        const struct expr_constant *c = boot_opt->value.values;
> > >          opt_header[0] = boot_opt->option->code;
> > >          opt_header[1] = strlen(c->string);
> > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
> > >          find_opt(pdo->options, pdo->n_options,
DHCP_OPT_BOOTFILE_ALT_CODE);
> > >      if (boot_alt_opt) {
> > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > -        const union expr_constant *c = boot_alt_opt->value.values;
> > > +        const struct expr_constant *c = boot_alt_opt->value.values;
> > >          opt_header[0] = boot_alt_opt->option->code;
> > >          opt_header[1] = strlen(c->string);
> > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
> > >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
> > >      if (next_server_opt) {
> > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > -        const union expr_constant *c = next_server_opt->value.values;
> > > +        const struct expr_constant *c =
next_server_opt->value.values;
> > >          opt_header[0] = next_server_opt->option->code;
> > >          opt_header[1] = sizeof(ovs_be32);
> > >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
> > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context
*ctx, const struct expr_field *dst,
> > >      /* Let's validate the options. */
> > >      for (size_t i = 0; i < po->n_options; i++) {
> > >          const struct ovnact_gen_option *o = &po->options[i];
> > > -        const union expr_constant *c = o->value.values;
> > > +        const struct expr_constant *c = o->value.values;
> > >          if (o->value.n_values > 1) {
> > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
option",
> > >                          o->option->name);
> > > @@ -3490,7 +3490,7 @@ static void
> > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> > >                          struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
> > >  {
> > > -    const union expr_constant *c = o->value.values;
> > > +    const struct expr_constant *c = o->value.values;
> > >
> > >      switch (o->option->code) {
> > >      case ND_RA_FLAG_ADDR_MODE:
> > > diff --git a/lib/expr.c b/lib/expr.c
> > > index 0cb44e1b6..ecf8a6338 100644
> > > --- a/lib/expr.c
> > > +++ b/lib/expr.c
> > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct expr
*a, struct expr *b)
> > >          } else {
> > >              ovs_list_push_back(&a->andor, &b->node);
> > >          }
> > > -        free(a->as_name);
> > >          a->as_name = NULL;
> > >          return a;
> > >      } else if (b->type == type) {
> > >          ovs_list_push_front(&b->andor, &a->node);
> > > -        free(b->as_name);
> > >          b->as_name = NULL;
> > >          return b;
> > >      } else {
> > > @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context *,
struct expr_field *);
> > >
> > >  static struct expr *
> > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > > -             const union expr_constant *c)
> > > +             const struct expr_constant *c)
> > >  {
> > >      struct expr *e = xzalloc(sizeof *e);
> > >      e->type = EXPR_T_CMP;
> > >      e->cmp.symbol = f->symbol;
> > >      e->cmp.relop = r;
> > > +    e->as_name = c->as_name;
> > >      if (f->symbol->width) {
> > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
> > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
expr_relop r,
> > >
> > >  /* Returns the minimum reasonable width for integer constant 'c'. */
> > >  static int
> > > -expr_constant_width(const union expr_constant *c)
> > > +expr_constant_width(const struct expr_constant *c)
> > >  {
> > >      if (c->masked) {
> > >          return mf_subvalue_width(&c->mask);
> > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
> > >                           e, make_cmp__(f, r, &cs->values[i]));
> > >      }
> > > -    /* Track address set */
> > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
> > > -        e->as_name = xstrdup(cs->as_name);
> > > -    }
> > > +
> > >  exit:
> > >      expr_constant_set_destroy(cs);
> > >      return e;
> > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx,
struct expr_constant_set *cs,
> > >          }
> > >      }
> > >
> > > -    struct expr_constant_set *addr_sets
> > > -        = (ctx->addr_sets
> > > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
> > > -           : NULL);
> > > -    if (!addr_sets) {
> > > +    struct shash_node *node = ctx->addr_sets
> > > +                              ? shash_find(ctx->addr_sets,
ctx->lexer->token.s)
> > > +                              : NULL;
> > > +    if (!node) {
> > >          lexer_syntax_error(ctx->lexer, "expecting address set name");
> > >          return false;
> > >      }
> > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx,
struct expr_constant_set *cs,
> > >          return false;
> > >      }
> > >
> > > -    if (!cs->n_values) {
> > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > > -    }
> > > -
> > > +    struct expr_constant_set *addr_sets = node->data;
> > >      size_t n_values = cs->n_values + addr_sets->n_values;
> > >      if (n_values >= *allocated_values) {
> > >          cs->values = xrealloc(cs->values, n_values * sizeof
*cs->values);
> > >          *allocated_values = n_values;
> > >      }
> > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > > -        cs->values[cs->n_values++] = addr_sets->values[i];
> > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > +        *c = addr_sets->values[i];
> > > +        c->as_name = node->name;
> > >      }
> > >
> > >      return true;
> > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx, struct
expr_constant_set *cs,
> > >          *allocated_values = n_values;
> > >      }
> > >      for (size_t i = 0; i < port_group->n_values; i++) {
> > > -        cs->values[cs->n_values++].string =
> > > -            xstrdup(port_group->values[i].string);
> > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > +        c->string = xstrdup(port_group->values[i].string);
> > > +        c->as_name = NULL;
> > >      }
> > >
> > >      return true;
> > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
> > >                                  sizeof *cs->values);
> > >      }
> > >
> > > -    /* Combining other values to the constant set that is tracking an
> > > -     * address set, so untrack it. */
> > > -    free(cs->as_name);
> > > -    cs->as_name = NULL;
> > > -
> > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > >          lexer_error(ctx->lexer, "Unexpanded template.");
> > >          return false;
> > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
> > >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
> > >              return false;
> > >          }
> > > -        cs->values[cs->n_values++].string =
xstrdup(ctx->lexer->token.s);
> > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > +        c->string = xstrdup(ctx->lexer->token.s);
> > > +        c->as_name = NULL;
> > >          lexer_get(ctx->lexer);
> > >          return true;
> > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx, struct
expr_constant_set *cs,
> > >              return false;
> > >          }
> > >
> > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > >          c->value = ctx->lexer->token.value;
> > >          c->format = ctx->lexer->token.format;
> > >          c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
> > >          if (c->masked) {
> > >              c->mask = ctx->lexer->token.mask;
> > >          }
> > > +        c->as_name = NULL;
> > >          lexer_get(ctx->lexer);
> > >          return true;
> > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx,
struct expr_constant_set *cs)
> > >   * indeterminate. */
> > >  bool
> > >  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
> > > -                    union expr_constant *c)
> > > +                    struct expr_constant *c)
> > >  {
> > >      if (lexer->error) {
> > >          return false;
> > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const
struct expr_field *f,
> > >  /* Appends to 's' a re-parseable representation of constant 'c' with
the given
> > >   * 'type'. */
> > >  void
> > > -expr_constant_format(const union expr_constant *c,
> > > +expr_constant_format(const struct expr_constant *c,
> > >                       enum expr_constant_type type, struct ds *s)
> > >  {
> > >      if (type == EXPR_C_STRING) {
> > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union expr_constant
*c,
> > >   *
> > >   * Does not free(c). */
> > >  void
> > > -expr_constant_destroy(const union expr_constant *c,
> > > +expr_constant_destroy(const struct expr_constant *c,
> > >                        enum expr_constant_type type)
> > >  {
> > >      if (c && type == EXPR_C_STRING) {
> > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
expr_constant_set *cs, struct ds *s)
> > >          ds_put_char(s, '{');
> > >      }
> > >
> > > -    for (const union expr_constant *c = cs->values;
> > > +    for (const struct expr_constant *c = cs->values;
> > >           c < &cs->values[cs->n_values]; c++) {
> > >          if (c != cs->values) {
> > >              ds_put_cstr(s, ", ");
> > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
expr_constant_set *cs)
> > >              }
> > >          }
> > >          free(cs->values);
> > > -        free(cs->as_name);
> > >      }
> > >  }
> > >
> > >  static int
> > >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
> > >  {
> > > -    const union expr_constant *a = a_;
> > > -    const union expr_constant *b = b_;
> > > +    const struct expr_constant *a = a_;
> > > +    const struct expr_constant *b = b_;
> > >
> > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> > >      if (d) {
> > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char
*const *values, size_t n_values)
> > >              VLOG_WARN("Invalid constant set entry: '%s', token type:
%d",
> > >                        values[i], lex.token.type);
> > >          } else {
> > > -            union expr_constant *c = &cs->values[cs->n_values++];
> > > +            struct expr_constant *c = &cs->values[cs->n_values++];
> > >              c->value = lex.token.value;
> > >              c->format = lex.token.format;
> > >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
> > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char
*const *values, size_t n_values)
> > >
> > >  static void
> > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> > > -                            union expr_constant *c, size_t
*allocated)
> > > +                            struct expr_constant *c, size_t
*allocated)
> > >  {
> > >      struct expr_constant_set *cs = *p_cs;
> > >      if (!cs) {
> > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
*const_sets, const char *name,
> > >                          values[i], name);
> > >              continue;
> > >          }
> > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > >          c->string = xstrdup(values[i]);
> > >      }
> > >
> > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx,
bool *atomic)
> > >
> > >              *atomic = true;
> > >
> > > -            union expr_constant *cst = xzalloc(sizeof *cst);
> > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> > >              cst->format = LEX_F_HEXADECIMAL;
> > >              cst->masked = false;
> > >
> > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx,
bool *atomic)
> > >              c.values = cst;
> > >              c.n_values = 1;
> > >              c.in_curlies = false;
> > > -            c.as_name = NULL;
> > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > >          } else if (parse_relop(ctx, &r) && parse_constant_set(ctx,
&c)) {
> > >              return make_cmp(ctx, &f, r, &c);
> > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
> > >  static struct expr *
> > >  expr_clone_cmp(struct expr *expr)
> > >  {
> > > -    ovs_assert(!expr->as_name);
> > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > >      if (!new->cmp.symbol->width) {
> > >          new->cmp.string = xstrdup(new->cmp.string);
> > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> > >  static struct expr *
> > >  expr_clone_condition(struct expr *expr)
> > >  {
> > > -    ovs_assert(!expr->as_name);
> > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > >      new->cond.string = xstrdup(new->cond.string);
> > >      return new;
> > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > >          return;
> > >      }
> > >
> > > -    free(expr->as_name);
> > > -
> > >      struct expr *sub;
> > >
> > >      switch (expr->type) {
> > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
> > >       * mask sizes. */
> > >      size_t n = ovs_list_size(&expr->andor);
> > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > > -    bool modified = false;
> > > +    bool has_addr_set = false;
> > >      /* Linked list over the 'subs' array to quickly skip deleted
elements,
> > >       * i.e. the index of the next potentially non-NULL element. */
> > >      size_t *next = xmalloc(n * sizeof *next);
> > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
> > >      size_t i = 0, j, max_n_bits = 0;
> > >      struct expr *sub;
> > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > > +        if (sub->as_name) {
> > > +            has_addr_set = true;
> > > +        }
> > >          if (symbol->width) {
> > >              const unsigned long *sub_mask;
> > >
> > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
> > >              next[last] = i;
> > >              last = i;
> > >          } else {
> > > +            /* Remove address set reference from the duplicate. */
> > > +            subs[last]->as_name = NULL;
> > >              expr_destroy(subs[i]);
> > >              subs[i] = NULL;
> > > -            modified = true;
> > >          }
> > >      }
> > >
> > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > > -        || (!modified && expr->as_name)) {
> > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
has_addr_set) {
> > >          /* Not a fully maskable field or this expression is tracking
an
> > >           * address set.  Don't try to optimize to preserve address
set I-P. */
> > >          goto done;
> > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
> > >              if (expr_bitmap_intersect_check(a_value, a_mask,
b_value, b_mask,
> > >                                              bit_width)
> > >                  && bitmap_is_superset(b_mask, a_mask, bit_width)) {
> > > -                /* 'a' is the same expression with a smaller mask. */
> > > +                /* 'a' is the same expression with a smaller mask.
> > > +                 * Remove address set reference from the duplicate.
*/
> > > +                a->as_name = NULL;
> > >                  expr_destroy(subs[j]);
> > >                  subs[j] = NULL;
> > > -                modified = true;
> > >
> > >                  /* Shorten the path for the next round. */
> > >                  if (last) {
> > > @@ -2685,12 +2676,6 @@ done:
> > >          }
> > >      }
> > >
> > > -    if (modified) {
> > > -        /* Members modified, so untrack address set. */
> > > -        free(expr->as_name);
> > > -        expr->as_name = NULL;
> > > -    }
> > > -
> > >      free(next);
> > >      free(subs);
> > >      return expr;
> > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > >          struct expr_match *match = expr_match_new(m, clause,
n_clauses,
> > >                                                    conj_id);
> > > -        if (or->as_name) {
> > > +        if (sub->as_name) {
> > >              ovs_assert(sub->type == EXPR_T_CMP);
> > >              ovs_assert(sub->cmp.symbol->width);
> > > -            match->as_name = xstrdup(or->as_name);
> > > +            match->as_name = xstrdup(sub->as_name);
> > >              match->as_ip = sub->cmp.value.ipv6;
> > >              match->as_mask = sub->cmp.mask.ipv6;
> > >          }
> > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> > > index be198e00d..27cec2aec 100644
> > > --- a/tests/ovn-controller.at
> > > +++ b/tests/ovn-controller.at
> > > @@ -1625,7 +1625,9 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
actions=lo
> > >  done
> > >
> > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [10
> > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> > > +# Last 5 is due to overlap in IP addresses between as1 and as2.
> > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [7
> > >  ])
> > >
> > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > >  done
> > >
> > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > -# In this case the as1 and as2 are merged to a single OR expr, so we
lose track of
> > > -# address set information - can't handle deletion incrementally.
> > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [10
> > > +# First 5 are due to overlap in IP addresses between as1 and as2.
> > > +# Last 2 are due to change from 2 IP in AS to 1.
> > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [7
> > >  ])
> > >
> > >  OVN_CLEANUP([hv1])
> > > @@ -1817,7 +1819,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
actions=co
> > >
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
> > >  ])
> > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [1
> > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [0
> > >  ])
> > >
> > >  # Delete 2 IPs
> > > @@ -1837,7 +1839,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
actions=co
> > >
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
> > >  ])
> > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [1
> > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [0
> > >  ])
> > >
> > >  OVN_CLEANUP([hv1])
> > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > >
> > >  AT_CLEANUP
> > >  ])
> > > +
> > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> > > +AT_KEYWORDS([as-i-p])
> > > +
> > > +ovn_start
> > > +
> > > +net_add n1
> > > +sim_add hv1
> > > +as hv1
> > > +check ovs-vsctl add-br br-phys
> > > +ovn_attach n1 br-phys 192.168.0.1
> > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > > +
> > > +check ovn-nbctl ls-add ls1
> > > +
> > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > > +
> > > +wait_for_ports_up
> > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > > +
> > > +# Get the OF table numbers
> > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> > > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
> > > +
> > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
external_ids:name=ls1))
> > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
logical_port=ls1-lp1))
> > > +
> > > +ovn-nbctl create address_set name=as1
> > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" &&
ip4.src == $as1' drop
> > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > > +check ovn-nbctl --wait=hv sync
> > > +
> > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +])
> > > +
> > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > > +check ovn-nbctl --wait=hv sync
> > > +
> > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +])
> > > +
> > > +
> > > +check ovn-appctl inc-engine/recompute
> > > +
> > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > +])
> > > +
> > > +OVN_CLEANUP([hv1])
> > > +AT_CLEANUP
> > > --
> > > 2.44.0
> > >
>
>
> Thank you Han,
>
> I would like to start the discussion about the possibility of
> backporting this change. The reason for the backport being that this
> change might be beneficial to ovn-kubernetes users, as this was
> actually observed in ovn-kubernetes with network policies. I know that
> strictly speaking this is not a bug fix, because this limitation was
> well documented, however I would like to hear the opinion of others.


I have no objections if this improvement is strongly required for released
versions, even though it is not a bug. May I ask for which releases do you
want this to be backported?

Thanks,
Han

>
> Thanks,
> Ales
> --
>
> Ales Musil
>
> Senior Software Engineer - OVN Core
>
> Red Hat EMEA
>
> amusil@redhat.com
>
Ales Musil May 7, 2024, 5:37 a.m. UTC | #4
On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org> wrote:

>
>
> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com> wrote:
> >
> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > >
> > >
> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
> > > >
> > > > Instead of tracking address set per struct expr_constant_set track it
> > > > per individual struct expr_constant. This allows more fine grained
> > > > control for I-P processing of address sets in controller. It helps
> with
> > > > scenarios like matching on two address sets in one expression e.g.
> > > > "ip4.src == {$as1, $as2}". This allows any addition or removal of
> > > > individual adress from the set to be incrementally processed instead
> > > > of reprocessing all the flows.
> > > >
> > > > This unfortunately doesn't help with the following flows:
> > > > "ip4.src == $as1 && ip4.dst == $as2"
> > > > "ip4.src == $as1 || ip4.dst == $as2"
> > > >
> > > > The memory impact should be minimal as there is only increase of 8
> bytes
> > > > per the struct expr_constant.
> > > >
> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > > Signed-off-by: Ales Musil <amusil@redhat.com>
> > > > ---
> > > > v4: Rebase on top of current main.
> > > >     Update the "lflow_handle_addr_set_update" comment according to
> suggestion from Han.
> > >
> > > Thanks Ales. I updated the commit message for the same, and applied to
> main branch.
> > >
> > > Regards,
> > > Han
> > >
> > > > v3: Rebase on top of current main.
> > > >     Address comments from Han:
> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to
> include remaning corner cases.
> > > >     - Make sure that the flows are consistent between I-P and
> recompute.
> > > > v2: Rebase on top of current main.
> > > >     Adjust the comment for I-P optimization.
> > > > ---
> > > >  controller/lflow.c      | 11 ++---
> > > >  include/ovn/actions.h   |  2 +-
> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> > > >  lib/actions.c           | 20 ++++-----
> > > >  lib/expr.c              | 99
> +++++++++++++++++------------------------
> > > >  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > > >
> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> > > > index 760ec0b41..1e05665a1 100644
> > > > --- a/controller/lflow.c
> > > > +++ b/controller/lflow.c
> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
> *l_ctx_in,
> > > >  }
> > > >
> > > >  static bool
> > > > -as_info_from_expr_const(const char *as_name, const union
> expr_constant *c,
> > > > +as_info_from_expr_const(const char *as_name, const struct
> expr_constant *c,
> > > >                          struct addrset_info *as_info)
> > > >  {
> > > >      as_info->name = as_name;
> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char *as_name,
> struct addr_set_diff *as_diff,
> > > >   *        generated.
> > > >   *
> > > >   *      - The sub expression of the address set is combined with
> other sub-
> > > > - *        expressions/constants, usually because of disjunctions
> between
> > > > - *        sub-expressions/constants, e.g.:
> > > > + *        expressions/constants on different fields, e.g.:
> > > >   *
> > > >   *          ip.src == $as1 || ip.dst == $as2
> > > > - *          ip.src == {$as1, $as2}
> > > > - *          ip.src == {$as1, ip1}
> > > >   *
> > > > - *        All these could have been split into separate lflows.
> > > > + *        This could have been split into separate lflows.
> > > >   *
> > > >   *      - Conjunctions overlapping between lflows, which can be
> caused by
> > > >   *        overlapping address sets or same address set used by
> multiple lflows
> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char *as_name,
> > > >          if (as_diff->deleted) {
> > > >              struct addrset_info as_info;
> > > >              for (size_t i = 0; i < as_diff->deleted->n_values; i++)
> {
> > > > -                union expr_constant *c =
> &as_diff->deleted->values[i];
> > > > +                struct expr_constant *c =
> &as_diff->deleted->values[i];
> > > >                  if (!as_info_from_expr_const(as_name, c, &as_info))
> {
> > > >                      continue;
> > > >                  }
> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > > > index ae0864fdd..88cf4de79 100644
> > > > --- a/include/ovn/actions.h
> > > > +++ b/include/ovn/actions.h
> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > > >  struct ovnact_load {
> > > >      struct ovnact ovnact;
> > > >      struct expr_field dst;
> > > > -    union expr_constant imm;
> > > > +    struct expr_constant imm;
> > > >  };
> > > >
> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > > > index c48f82398..e54edb5bf 100644
> > > > --- a/include/ovn/expr.h
> > > > +++ b/include/ovn/expr.h
> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type,
> enum expr_relop *relop);
> > > >  struct expr {
> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or
> EXPR_T_OR if any. */
> > > >      enum expr_type type;        /* Expression type. */
> > > > -    char *as_name;              /* Address set name. Null if it is
> not an
> > > > +    const char *as_name;        /* Address set name. Null if it is
> not an
> > > >                                     address set. */
> > > >
> > > >      union {
> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > > >  };
> > > >
> > > >  /* A string or integer constant (one must know which from context).
> */
> > > > -union expr_constant {
> > > > -    /* Integer constant.
> > > > -     *
> > > > -     * The width of a constant isn't always clear, e.g. if you
> write "1",
> > > > -     * there's no way to tell whether you mean for that to be a
> 1-bit constant
> > > > -     * or a 128-bit constant or somewhere in between. */
> > > > -    struct {
> > > > -        union mf_subvalue value;
> > > > -        union mf_subvalue mask; /* Only initialized if 'masked'. */
> > > > -        bool masked;
> > > > -
> > > > -        enum lex_format format; /* From the constant's lex_token. */
> > > > -    };
> > > > +struct expr_constant {
> > > > +    const char *as_name;
> > > >
> > > > -    /* Null-terminated string constant. */
> > > > -    char *string;
> > > > +    union {
> > > > +        /* Integer constant.
> > > > +         *
> > > > +         * The width of a constant isn't always clear, e.g. if you
> write "1",
> > > > +         * there's no way to tell whether you mean for that to be a
> 1-bit
> > > > +         * constant or a 128-bit constant or somewhere in between.
> */
> > > > +        struct {
> > > > +            union mf_subvalue value;
> > > > +            union mf_subvalue mask; /* Only initialized if
> 'masked'. */
> > > > +            bool masked;
> > > > +
> > > > +            enum lex_format format; /* From the constant's
> lex_token. */
> > > > +        };
> > > > +
> > > > +        /* Null-terminated string constant. */
> > > > +        char *string;
> > > > +    };
> > > >  };
> > > >
> > > >  bool expr_constant_parse(struct lexer *,
> > > >                           const struct expr_field *,
> > > > -                         union expr_constant *);
> > > > -void expr_constant_format(const union expr_constant *,
> > > > +                         struct expr_constant *);
> > > > +void expr_constant_format(const struct expr_constant *,
> > > >                            enum expr_constant_type, struct ds *);
> > > > -void expr_constant_destroy(const union expr_constant *,
> > > > +void expr_constant_destroy(const struct expr_constant *,
> > > >                             enum expr_constant_type);
> > > >
> > > >  /* A collection of "union expr_constant"s of the same type. */
> > > >  struct expr_constant_set {
> > > > -    union expr_constant *values;  /* Constants. */
> > > > +    struct expr_constant *values; /* Constants. */
> > > >      size_t n_values;              /* Number of constants. */
> > > >      enum expr_constant_type type; /* Type of the constants. */
> > > >      bool in_curlies;              /* Whether the constants were in
> {}. */
> > > > -    char *as_name;                /* Name of an address set. It is
> NULL if not
> > > > -                                     an address set. */
> > > >  };
> > > >
> > > >  bool expr_constant_set_parse(struct lexer *, struct
> expr_constant_set *);
> > > > diff --git a/lib/actions.c b/lib/actions.c
> > > > index 94751d059..cff4f9e3c 100644
> > > > --- a/lib/actions.c
> > > > +++ b/lib/actions.c
> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
> > > >              const struct ovnact_encode_params *ep,
> > > >              struct ofpbuf *ofpacts)
> > > >  {
> > > > -    const union expr_constant *c = &load->imm;
> > > > +    const struct expr_constant *c = &load->imm;
> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> > > >      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
> dst.field,
> > > >                                                         NULL, NULL);
> > > > @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct
> ofpbuf *ofpacts,
> > > >          /* All empty_lb_backends fields are of type 'str' */
> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> > > >
> > > > -        const union expr_constant *c = o->value.values;
> > > > +        const struct expr_constant *c = o->value.values;
> > > >          size_t size = strlen(c->string);
> > > >          struct controller_event_opt_header hdr =
> > > >              (struct controller_event_opt_header) {
> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> action_context *ctx,
> > > >  {
> > > >      for (size_t i = 0; i < n_options; i++) {
> > > >          const struct ovnact_gen_option *o = &options[i];
> > > > -        const union expr_constant *c = o->value.values;
> > > > +        const struct expr_constant *c = o->value.values;
> > > >          struct sockaddr_storage ss;
> > > >          struct uuid uuid;
> > > >
> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
> ovnact_gen_option *o,
> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > >      opt_header[0] = o->option->code;
> > > >
> > > > -    const union expr_constant *c = o->value.values;
> > > > +    const struct expr_constant *c = o->value.values;
> > > >      size_t n_values = o->value.n_values;
> > > >      if (!strcmp(o->option->type, "bool") ||
> > > >          !strcmp(o->option->type, "uint8")) {
> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
> ovnact_gen_option *o,
> > > >                           struct ofpbuf *ofpacts)
> > > >  {
> > > >      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts,
> sizeof *opt);
> > > > -    const union expr_constant *c = o->value.values;
> > > > +    const struct expr_constant *c = o->value.values;
> > > >      size_t n_values = o->value.n_values;
> > > >      size_t size;
> > > >
> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> > > >          find_opt(pdo->options, pdo->n_options,
> DHCP_OPT_BOOTFILE_CODE);
> > > >      if (boot_opt) {
> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > > -        const union expr_constant *c = boot_opt->value.values;
> > > > +        const struct expr_constant *c = boot_opt->value.values;
> > > >          opt_header[0] = boot_opt->option->code;
> > > >          opt_header[1] = strlen(c->string);
> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> > > >          find_opt(pdo->options, pdo->n_options,
> DHCP_OPT_BOOTFILE_ALT_CODE);
> > > >      if (boot_alt_opt) {
> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > > -        const union expr_constant *c = boot_alt_opt->value.values;
> > > > +        const struct expr_constant *c = boot_alt_opt->value.values;
> > > >          opt_header[0] = boot_alt_opt->option->code;
> > > >          opt_header[1] = strlen(c->string);
> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> > > >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
> > > >      if (next_server_opt) {
> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > > -        const union expr_constant *c =
> next_server_opt->value.values;
> > > > +        const struct expr_constant *c =
> next_server_opt->value.values;
> > > >          opt_header[0] = next_server_opt->option->code;
> > > >          opt_header[1] = sizeof(ovs_be32);
> > > >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context
> *ctx, const struct expr_field *dst,
> > > >      /* Let's validate the options. */
> > > >      for (size_t i = 0; i < po->n_options; i++) {
> > > >          const struct ovnact_gen_option *o = &po->options[i];
> > > > -        const union expr_constant *c = o->value.values;
> > > > +        const struct expr_constant *c = o->value.values;
> > > >          if (o->value.n_values > 1) {
> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
> option",
> > > >                          o->option->name);
> > > > @@ -3490,7 +3490,7 @@ static void
> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> > > >                          struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
> > > >  {
> > > > -    const union expr_constant *c = o->value.values;
> > > > +    const struct expr_constant *c = o->value.values;
> > > >
> > > >      switch (o->option->code) {
> > > >      case ND_RA_FLAG_ADDR_MODE:
> > > > diff --git a/lib/expr.c b/lib/expr.c
> > > > index 0cb44e1b6..ecf8a6338 100644
> > > > --- a/lib/expr.c
> > > > +++ b/lib/expr.c
> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct expr
> *a, struct expr *b)
> > > >          } else {
> > > >              ovs_list_push_back(&a->andor, &b->node);
> > > >          }
> > > > -        free(a->as_name);
> > > >          a->as_name = NULL;
> > > >          return a;
> > > >      } else if (b->type == type) {
> > > >          ovs_list_push_front(&b->andor, &a->node);
> > > > -        free(b->as_name);
> > > >          b->as_name = NULL;
> > > >          return b;
> > > >      } else {
> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context *,
> struct expr_field *);
> > > >
> > > >  static struct expr *
> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > > > -             const union expr_constant *c)
> > > > +             const struct expr_constant *c)
> > > >  {
> > > >      struct expr *e = xzalloc(sizeof *e);
> > > >      e->type = EXPR_T_CMP;
> > > >      e->cmp.symbol = f->symbol;
> > > >      e->cmp.relop = r;
> > > > +    e->as_name = c->as_name;
> > > >      if (f->symbol->width) {
> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > > >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
> expr_relop r,
> > > >
> > > >  /* Returns the minimum reasonable width for integer constant 'c'. */
> > > >  static int
> > > > -expr_constant_width(const union expr_constant *c)
> > > > +expr_constant_width(const struct expr_constant *c)
> > > >  {
> > > >      if (c->masked) {
> > > >          return mf_subvalue_width(&c->mask);
> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
> > > >                           e, make_cmp__(f, r, &cs->values[i]));
> > > >      }
> > > > -    /* Track address set */
> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
> > > > -        e->as_name = xstrdup(cs->as_name);
> > > > -    }
> > > > +
> > > >  exit:
> > > >      expr_constant_set_destroy(cs);
> > > >      return e;
> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx,
> struct expr_constant_set *cs,
> > > >          }
> > > >      }
> > > >
> > > > -    struct expr_constant_set *addr_sets
> > > > -        = (ctx->addr_sets
> > > > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
> > > > -           : NULL);
> > > > -    if (!addr_sets) {
> > > > +    struct shash_node *node = ctx->addr_sets
> > > > +                              ? shash_find(ctx->addr_sets,
> ctx->lexer->token.s)
> > > > +                              : NULL;
> > > > +    if (!node) {
> > > >          lexer_syntax_error(ctx->lexer, "expecting address set
> name");
> > > >          return false;
> > > >      }
> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx,
> struct expr_constant_set *cs,
> > > >          return false;
> > > >      }
> > > >
> > > > -    if (!cs->n_values) {
> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > > > -    }
> > > > -
> > > > +    struct expr_constant_set *addr_sets = node->data;
> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> > > >      if (n_values >= *allocated_values) {
> > > >          cs->values = xrealloc(cs->values, n_values * sizeof
> *cs->values);
> > > >          *allocated_values = n_values;
> > > >      }
> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > > +        *c = addr_sets->values[i];
> > > > +        c->as_name = node->name;
> > > >      }
> > > >
> > > >      return true;
> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx,
> struct expr_constant_set *cs,
> > > >          *allocated_values = n_values;
> > > >      }
> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> > > > -        cs->values[cs->n_values++].string =
> > > > -            xstrdup(port_group->values[i].string);
> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > > +        c->string = xstrdup(port_group->values[i].string);
> > > > +        c->as_name = NULL;
> > > >      }
> > > >
> > > >      return true;
> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, struct
> expr_constant_set *cs,
> > > >                                  sizeof *cs->values);
> > > >      }
> > > >
> > > > -    /* Combining other values to the constant set that is tracking
> an
> > > > -     * address set, so untrack it. */
> > > > -    free(cs->as_name);
> > > > -    cs->as_name = NULL;
> > > > -
> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> > > >          return false;
> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, struct
> expr_constant_set *cs,
> > > >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
> > > >              return false;
> > > >          }
> > > > -        cs->values[cs->n_values++].string =
> xstrdup(ctx->lexer->token.s);
> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> > > > +        c->as_name = NULL;
> > > >          lexer_get(ctx->lexer);
> > > >          return true;
> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx,
> struct expr_constant_set *cs,
> > > >              return false;
> > > >          }
> > > >
> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > >          c->value = ctx->lexer->token.value;
> > > >          c->format = ctx->lexer->token.format;
> > > >          c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
> > > >          if (c->masked) {
> > > >              c->mask = ctx->lexer->token.mask;
> > > >          }
> > > > +        c->as_name = NULL;
> > > >          lexer_get(ctx->lexer);
> > > >          return true;
> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx,
> struct expr_constant_set *cs)
> > > >   * indeterminate. */
> > > >  bool
> > > >  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
> > > > -                    union expr_constant *c)
> > > > +                    struct expr_constant *c)
> > > >  {
> > > >      if (lexer->error) {
> > > >          return false;
> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const
> struct expr_field *f,
> > > >  /* Appends to 's' a re-parseable representation of constant 'c'
> with the given
> > > >   * 'type'. */
> > > >  void
> > > > -expr_constant_format(const union expr_constant *c,
> > > > +expr_constant_format(const struct expr_constant *c,
> > > >                       enum expr_constant_type type, struct ds *s)
> > > >  {
> > > >      if (type == EXPR_C_STRING) {
> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union expr_constant
> *c,
> > > >   *
> > > >   * Does not free(c). */
> > > >  void
> > > > -expr_constant_destroy(const union expr_constant *c,
> > > > +expr_constant_destroy(const struct expr_constant *c,
> > > >                        enum expr_constant_type type)
> > > >  {
> > > >      if (c && type == EXPR_C_STRING) {
> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
> expr_constant_set *cs, struct ds *s)
> > > >          ds_put_char(s, '{');
> > > >      }
> > > >
> > > > -    for (const union expr_constant *c = cs->values;
> > > > +    for (const struct expr_constant *c = cs->values;
> > > >           c < &cs->values[cs->n_values]; c++) {
> > > >          if (c != cs->values) {
> > > >              ds_put_cstr(s, ", ");
> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> expr_constant_set *cs)
> > > >              }
> > > >          }
> > > >          free(cs->values);
> > > > -        free(cs->as_name);
> > > >      }
> > > >  }
> > > >
> > > >  static int
> > > >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
> > > >  {
> > > > -    const union expr_constant *a = a_;
> > > > -    const union expr_constant *b = b_;
> > > > +    const struct expr_constant *a = a_;
> > > > +    const struct expr_constant *b = b_;
> > > >
> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> > > >      if (d) {
> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char
> *const *values, size_t n_values)
> > > >              VLOG_WARN("Invalid constant set entry: '%s', token
> type: %d",
> > > >                        values[i], lex.token.type);
> > > >          } else {
> > > > -            union expr_constant *c = &cs->values[cs->n_values++];
> > > > +            struct expr_constant *c = &cs->values[cs->n_values++];
> > > >              c->value = lex.token.value;
> > > >              c->format = lex.token.format;
> > > >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char
> *const *values, size_t n_values)
> > > >
> > > >  static void
> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> > > > -                            union expr_constant *c, size_t
> *allocated)
> > > > +                            struct expr_constant *c, size_t
> *allocated)
> > > >  {
> > > >      struct expr_constant_set *cs = *p_cs;
> > > >      if (!cs) {
> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
> *const_sets, const char *name,
> > > >                          values[i], name);
> > > >              continue;
> > > >          }
> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> > > >          c->string = xstrdup(values[i]);
> > > >      }
> > > >
> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx,
> bool *atomic)
> > > >
> > > >              *atomic = true;
> > > >
> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> > > >              cst->format = LEX_F_HEXADECIMAL;
> > > >              cst->masked = false;
> > > >
> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx,
> bool *atomic)
> > > >              c.values = cst;
> > > >              c.n_values = 1;
> > > >              c.in_curlies = false;
> > > > -            c.as_name = NULL;
> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > > >          } else if (parse_relop(ctx, &r) && parse_constant_set(ctx,
> &c)) {
> > > >              return make_cmp(ctx, &f, r, &c);
> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
> > > >  static struct expr *
> > > >  expr_clone_cmp(struct expr *expr)
> > > >  {
> > > > -    ovs_assert(!expr->as_name);
> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > >      if (!new->cmp.symbol->width) {
> > > >          new->cmp.string = xstrdup(new->cmp.string);
> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> > > >  static struct expr *
> > > >  expr_clone_condition(struct expr *expr)
> > > >  {
> > > > -    ovs_assert(!expr->as_name);
> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > >      new->cond.string = xstrdup(new->cond.string);
> > > >      return new;
> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > > >          return;
> > > >      }
> > > >
> > > > -    free(expr->as_name);
> > > > -
> > > >      struct expr *sub;
> > > >
> > > >      switch (expr->type) {
> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> > > >       * mask sizes. */
> > > >      size_t n = ovs_list_size(&expr->andor);
> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > > > -    bool modified = false;
> > > > +    bool has_addr_set = false;
> > > >      /* Linked list over the 'subs' array to quickly skip deleted
> elements,
> > > >       * i.e. the index of the next potentially non-NULL element. */
> > > >      size_t *next = xmalloc(n * sizeof *next);
> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> > > >      size_t i = 0, j, max_n_bits = 0;
> > > >      struct expr *sub;
> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > > > +        if (sub->as_name) {
> > > > +            has_addr_set = true;
> > > > +        }
> > > >          if (symbol->width) {
> > > >              const unsigned long *sub_mask;
> > > >
> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> > > >              next[last] = i;
> > > >              last = i;
> > > >          } else {
> > > > +            /* Remove address set reference from the duplicate. */
> > > > +            subs[last]->as_name = NULL;
> > > >              expr_destroy(subs[i]);
> > > >              subs[i] = NULL;
> > > > -            modified = true;
> > > >          }
> > > >      }
> > > >
> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > > > -        || (!modified && expr->as_name)) {
> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
> has_addr_set) {
> > > >          /* Not a fully maskable field or this expression is
> tracking an
> > > >           * address set.  Don't try to optimize to preserve address
> set I-P. */
> > > >          goto done;
> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> > > >              if (expr_bitmap_intersect_check(a_value, a_mask,
> b_value, b_mask,
> > > >                                              bit_width)
> > > >                  && bitmap_is_superset(b_mask, a_mask, bit_width)) {
> > > > -                /* 'a' is the same expression with a smaller mask.
> */
> > > > +                /* 'a' is the same expression with a smaller mask.
> > > > +                 * Remove address set reference from the duplicate.
> */
> > > > +                a->as_name = NULL;
> > > >                  expr_destroy(subs[j]);
> > > >                  subs[j] = NULL;
> > > > -                modified = true;
> > > >
> > > >                  /* Shorten the path for the next round. */
> > > >                  if (last) {
> > > > @@ -2685,12 +2676,6 @@ done:
> > > >          }
> > > >      }
> > > >
> > > > -    if (modified) {
> > > > -        /* Members modified, so untrack address set. */
> > > > -        free(expr->as_name);
> > > > -        expr->as_name = NULL;
> > > > -    }
> > > > -
> > > >      free(next);
> > > >      free(subs);
> > > >      return expr;
> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > > >          struct expr_match *match = expr_match_new(m, clause,
> n_clauses,
> > > >                                                    conj_id);
> > > > -        if (or->as_name) {
> > > > +        if (sub->as_name) {
> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> > > >              ovs_assert(sub->cmp.symbol->width);
> > > > -            match->as_name = xstrdup(or->as_name);
> > > > +            match->as_name = xstrdup(sub->as_name);
> > > >              match->as_ip = sub->cmp.value.ipv6;
> > > >              match->as_mask = sub->cmp.mask.ipv6;
> > > >          }
> > > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> > > > index be198e00d..27cec2aec 100644
> > > > --- a/tests/ovn-controller.at
> > > > +++ b/tests/ovn-controller.at
> > > > @@ -1625,7 +1625,9 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> actions=lo
> > > >  done
> > > >
> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [10
> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> > > > +# Last 5 is due to overlap in IP addresses between as1 and as2.
> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [7
> > > >  ])
> > > >
> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > > >  done
> > > >
> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > -# In this case the as1 and as2 are merged to a single OR expr, so
> we lose track of
> > > > -# address set information - can't handle deletion incrementally.
> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [10
> > > > +# First 5 are due to overlap in IP addresses between as1 and as2.
> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [7
> > > >  ])
> > > >
> > > >  OVN_CLEANUP([hv1])
> > > > @@ -1817,7 +1819,7 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> actions=co
> > > >
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> > > >  ])
> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [1
> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [0
> > > >  ])
> > > >
> > > >  # Delete 2 IPs
> > > > @@ -1837,7 +1839,7 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> actions=co
> > > >
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> > > >  ])
> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [1
> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [0
> > > >  ])
> > > >
> > > >  OVN_CLEANUP([hv1])
> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > > >
> > > >  AT_CLEANUP
> > > >  ])
> > > > +
> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> > > > +AT_KEYWORDS([as-i-p])
> > > > +
> > > > +ovn_start
> > > > +
> > > > +net_add n1
> > > > +sim_add hv1
> > > > +as hv1
> > > > +check ovs-vsctl add-br br-phys
> > > > +ovn_attach n1 br-phys 192.168.0.1
> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > > > +
> > > > +check ovn-nbctl ls-add ls1
> > > > +
> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > > > +
> > > > +wait_for_ports_up
> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > > > +
> > > > +# Get the OF table numbers
> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
> > > > +
> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> external_ids:name=ls1))
> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
> logical_port=ls1-lp1))
> > > > +
> > > > +ovn-nbctl create address_set name=as1
> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" &&
> ip4.src == $as1' drop
> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > > > +check ovn-nbctl --wait=hv sync
> > > > +
> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +])
> > > > +
> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > > > +check ovn-nbctl --wait=hv sync
> > > > +
> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +])
> > > > +
> > > > +
> > > > +check ovn-appctl inc-engine/recompute
> > > > +
> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > +])
> > > > +
> > > > +OVN_CLEANUP([hv1])
> > > > +AT_CLEANUP
> > > > --
> > > > 2.44.0
> > > >
> >
> >
> > Thank you Han,
> >
> > I would like to start the discussion about the possibility of
> > backporting this change. The reason for the backport being that this
> > change might be beneficial to ovn-kubernetes users, as this was
> > actually observed in ovn-kubernetes with network policies. I know that
> > strictly speaking this is not a bug fix, because this limitation was
> > well documented, however I would like to hear the opinion of others.
>
>
> I have no objections if this improvement is strongly required for released
> versions, even though it is not a bug. May I ask for which releases do you
> want this to be backported?
>

We have a request for 23.09 and 24.03.


>
> Thanks,
> Han
>
> >
> > Thanks,
> > Ales
> > --
> >
> > Ales Musil
> >
> > Senior Software Engineer - OVN Core
> >
> > Red Hat EMEA
> >
> > amusil@redhat.com
> >
>
Han Zhou May 7, 2024, 6:12 a.m. UTC | #5
On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com> wrote:
>
>
>
> On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org> wrote:
>>
>>
>>
>> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com> wrote:
>> >
>> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org> wrote:
>> > >
>> > >
>> > >
>> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
>> > > >
>> > > > Instead of tracking address set per struct expr_constant_set track
it
>> > > > per individual struct expr_constant. This allows more fine grained
>> > > > control for I-P processing of address sets in controller. It helps
with
>> > > > scenarios like matching on two address sets in one expression e.g.
>> > > > "ip4.src == {$as1, $as2}". This allows any addition or removal of
>> > > > individual adress from the set to be incrementally processed
instead
>> > > > of reprocessing all the flows.
>> > > >
>> > > > This unfortunately doesn't help with the following flows:
>> > > > "ip4.src == $as1 && ip4.dst == $as2"
>> > > > "ip4.src == $as1 || ip4.dst == $as2"
>> > > >
>> > > > The memory impact should be minimal as there is only increase of 8
bytes
>> > > > per the struct expr_constant.
>> > > >
>> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
>> > > > Signed-off-by: Ales Musil <amusil@redhat.com>
>> > > > ---
>> > > > v4: Rebase on top of current main.
>> > > >     Update the "lflow_handle_addr_set_update" comment according to
suggestion from Han.
>> > >
>> > > Thanks Ales. I updated the commit message for the same, and applied
to main branch.
>> > >
>> > > Regards,
>> > > Han
>> > >
>> > > > v3: Rebase on top of current main.
>> > > >     Address comments from Han:
>> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to
include remaning corner cases.
>> > > >     - Make sure that the flows are consistent between I-P and
recompute.
>> > > > v2: Rebase on top of current main.
>> > > >     Adjust the comment for I-P optimization.
>> > > > ---
>> > > >  controller/lflow.c      | 11 ++---
>> > > >  include/ovn/actions.h   |  2 +-
>> > > >  include/ovn/expr.h      | 46 ++++++++++---------
>> > > >  lib/actions.c           | 20 ++++-----
>> > > >  lib/expr.c              | 99
+++++++++++++++++------------------------
>> > > >  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
>> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
>> > > >
>> > > > diff --git a/controller/lflow.c b/controller/lflow.c
>> > > > index 760ec0b41..1e05665a1 100644
>> > > > --- a/controller/lflow.c
>> > > > +++ b/controller/lflow.c
>> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
*l_ctx_in,
>> > > >  }
>> > > >
>> > > >  static bool
>> > > > -as_info_from_expr_const(const char *as_name, const union
expr_constant *c,
>> > > > +as_info_from_expr_const(const char *as_name, const struct
expr_constant *c,
>> > > >                          struct addrset_info *as_info)
>> > > >  {
>> > > >      as_info->name = as_name;
>> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
*as_name, struct addr_set_diff *as_diff,
>> > > >   *        generated.
>> > > >   *
>> > > >   *      - The sub expression of the address set is combined with
other sub-
>> > > > - *        expressions/constants, usually because of disjunctions
between
>> > > > - *        sub-expressions/constants, e.g.:
>> > > > + *        expressions/constants on different fields, e.g.:
>> > > >   *
>> > > >   *          ip.src == $as1 || ip.dst == $as2
>> > > > - *          ip.src == {$as1, $as2}
>> > > > - *          ip.src == {$as1, ip1}
>> > > >   *
>> > > > - *        All these could have been split into separate lflows.
>> > > > + *        This could have been split into separate lflows.
>> > > >   *
>> > > >   *      - Conjunctions overlapping between lflows, which can be
caused by
>> > > >   *        overlapping address sets or same address set used by
multiple lflows
>> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char
*as_name,
>> > > >          if (as_diff->deleted) {
>> > > >              struct addrset_info as_info;
>> > > >              for (size_t i = 0; i < as_diff->deleted->n_values;
i++) {
>> > > > -                union expr_constant *c =
&as_diff->deleted->values[i];
>> > > > +                struct expr_constant *c =
&as_diff->deleted->values[i];
>> > > >                  if (!as_info_from_expr_const(as_name, c,
&as_info)) {
>> > > >                      continue;
>> > > >                  }
>> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
>> > > > index ae0864fdd..88cf4de79 100644
>> > > > --- a/include/ovn/actions.h
>> > > > +++ b/include/ovn/actions.h
>> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
>> > > >  struct ovnact_load {
>> > > >      struct ovnact ovnact;
>> > > >      struct expr_field dst;
>> > > > -    union expr_constant imm;
>> > > > +    struct expr_constant imm;
>> > > >  };
>> > > >
>> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
>> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
>> > > > index c48f82398..e54edb5bf 100644
>> > > > --- a/include/ovn/expr.h
>> > > > +++ b/include/ovn/expr.h
>> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type,
enum expr_relop *relop);
>> > > >  struct expr {
>> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or
EXPR_T_OR if any. */
>> > > >      enum expr_type type;        /* Expression type. */
>> > > > -    char *as_name;              /* Address set name. Null if it
is not an
>> > > > +    const char *as_name;        /* Address set name. Null if it
is not an
>> > > >                                     address set. */
>> > > >
>> > > >      union {
>> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
>> > > >  };
>> > > >
>> > > >  /* A string or integer constant (one must know which from
context). */
>> > > > -union expr_constant {
>> > > > -    /* Integer constant.
>> > > > -     *
>> > > > -     * The width of a constant isn't always clear, e.g. if you
write "1",
>> > > > -     * there's no way to tell whether you mean for that to be a
1-bit constant
>> > > > -     * or a 128-bit constant or somewhere in between. */
>> > > > -    struct {
>> > > > -        union mf_subvalue value;
>> > > > -        union mf_subvalue mask; /* Only initialized if 'masked'.
*/
>> > > > -        bool masked;
>> > > > -
>> > > > -        enum lex_format format; /* From the constant's lex_token.
*/
>> > > > -    };
>> > > > +struct expr_constant {
>> > > > +    const char *as_name;
>> > > >
>> > > > -    /* Null-terminated string constant. */
>> > > > -    char *string;
>> > > > +    union {
>> > > > +        /* Integer constant.
>> > > > +         *
>> > > > +         * The width of a constant isn't always clear, e.g. if
you write "1",
>> > > > +         * there's no way to tell whether you mean for that to be
a 1-bit
>> > > > +         * constant or a 128-bit constant or somewhere in
between. */
>> > > > +        struct {
>> > > > +            union mf_subvalue value;
>> > > > +            union mf_subvalue mask; /* Only initialized if
'masked'. */
>> > > > +            bool masked;
>> > > > +
>> > > > +            enum lex_format format; /* From the constant's
lex_token. */
>> > > > +        };
>> > > > +
>> > > > +        /* Null-terminated string constant. */
>> > > > +        char *string;
>> > > > +    };
>> > > >  };
>> > > >
>> > > >  bool expr_constant_parse(struct lexer *,
>> > > >                           const struct expr_field *,
>> > > > -                         union expr_constant *);
>> > > > -void expr_constant_format(const union expr_constant *,
>> > > > +                         struct expr_constant *);
>> > > > +void expr_constant_format(const struct expr_constant *,
>> > > >                            enum expr_constant_type, struct ds *);
>> > > > -void expr_constant_destroy(const union expr_constant *,
>> > > > +void expr_constant_destroy(const struct expr_constant *,
>> > > >                             enum expr_constant_type);
>> > > >
>> > > >  /* A collection of "union expr_constant"s of the same type. */
>> > > >  struct expr_constant_set {
>> > > > -    union expr_constant *values;  /* Constants. */
>> > > > +    struct expr_constant *values; /* Constants. */
>> > > >      size_t n_values;              /* Number of constants. */
>> > > >      enum expr_constant_type type; /* Type of the constants. */
>> > > >      bool in_curlies;              /* Whether the constants were
in {}. */
>> > > > -    char *as_name;                /* Name of an address set. It
is NULL if not
>> > > > -                                     an address set. */
>> > > >  };
>> > > >
>> > > >  bool expr_constant_set_parse(struct lexer *, struct
expr_constant_set *);
>> > > > diff --git a/lib/actions.c b/lib/actions.c
>> > > > index 94751d059..cff4f9e3c 100644
>> > > > --- a/lib/actions.c
>> > > > +++ b/lib/actions.c
>> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
>> > > >              const struct ovnact_encode_params *ep,
>> > > >              struct ofpbuf *ofpacts)
>> > > >  {
>> > > > -    const union expr_constant *c = &load->imm;
>> > > > +    const struct expr_constant *c = &load->imm;
>> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
>> > > >      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
dst.field,
>> > > >                                                         NULL,
NULL);
>> > > > @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct
ofpbuf *ofpacts,
>> > > >          /* All empty_lb_backends fields are of type 'str' */
>> > > >          ovs_assert(!strcmp(o->option->type, "str"));
>> > > >
>> > > > -        const union expr_constant *c = o->value.values;
>> > > > +        const struct expr_constant *c = o->value.values;
>> > > >          size_t size = strlen(c->string);
>> > > >          struct controller_event_opt_header hdr =
>> > > >              (struct controller_event_opt_header) {
>> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
action_context *ctx,
>> > > >  {
>> > > >      for (size_t i = 0; i < n_options; i++) {
>> > > >          const struct ovnact_gen_option *o = &options[i];
>> > > > -        const union expr_constant *c = o->value.values;
>> > > > +        const struct expr_constant *c = o->value.values;
>> > > >          struct sockaddr_storage ss;
>> > > >          struct uuid uuid;
>> > > >
>> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
ovnact_gen_option *o,
>> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>> > > >      opt_header[0] = o->option->code;
>> > > >
>> > > > -    const union expr_constant *c = o->value.values;
>> > > > +    const struct expr_constant *c = o->value.values;
>> > > >      size_t n_values = o->value.n_values;
>> > > >      if (!strcmp(o->option->type, "bool") ||
>> > > >          !strcmp(o->option->type, "uint8")) {
>> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
ovnact_gen_option *o,
>> > > >                           struct ofpbuf *ofpacts)
>> > > >  {
>> > > >      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts,
sizeof *opt);
>> > > > -    const union expr_constant *c = o->value.values;
>> > > > +    const struct expr_constant *c = o->value.values;
>> > > >      size_t n_values = o->value.n_values;
>> > > >      size_t size;
>> > > >
>> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
>> > > >          find_opt(pdo->options, pdo->n_options,
DHCP_OPT_BOOTFILE_CODE);
>> > > >      if (boot_opt) {
>> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>> > > > -        const union expr_constant *c = boot_opt->value.values;
>> > > > +        const struct expr_constant *c = boot_opt->value.values;
>> > > >          opt_header[0] = boot_opt->option->code;
>> > > >          opt_header[1] = strlen(c->string);
>> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
>> > > >          find_opt(pdo->options, pdo->n_options,
DHCP_OPT_BOOTFILE_ALT_CODE);
>> > > >      if (boot_alt_opt) {
>> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>> > > > -        const union expr_constant *c = boot_alt_opt->value.values;
>> > > > +        const struct expr_constant *c =
boot_alt_opt->value.values;
>> > > >          opt_header[0] = boot_alt_opt->option->code;
>> > > >          opt_header[1] = strlen(c->string);
>> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
ovnact_put_opts *pdo,
>> > > >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
>> > > >      if (next_server_opt) {
>> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>> > > > -        const union expr_constant *c =
next_server_opt->value.values;
>> > > > +        const struct expr_constant *c =
next_server_opt->value.values;
>> > > >          opt_header[0] = next_server_opt->option->code;
>> > > >          opt_header[1] = sizeof(ovs_be32);
>> > > >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
>> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context
*ctx, const struct expr_field *dst,
>> > > >      /* Let's validate the options. */
>> > > >      for (size_t i = 0; i < po->n_options; i++) {
>> > > >          const struct ovnact_gen_option *o = &po->options[i];
>> > > > -        const union expr_constant *c = o->value.values;
>> > > > +        const struct expr_constant *c = o->value.values;
>> > > >          if (o->value.n_values > 1) {
>> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
option",
>> > > >                          o->option->name);
>> > > > @@ -3490,7 +3490,7 @@ static void
>> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
>> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
ra_offset)
>> > > >  {
>> > > > -    const union expr_constant *c = o->value.values;
>> > > > +    const struct expr_constant *c = o->value.values;
>> > > >
>> > > >      switch (o->option->code) {
>> > > >      case ND_RA_FLAG_ADDR_MODE:
>> > > > diff --git a/lib/expr.c b/lib/expr.c
>> > > > index 0cb44e1b6..ecf8a6338 100644
>> > > > --- a/lib/expr.c
>> > > > +++ b/lib/expr.c
>> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct
expr *a, struct expr *b)
>> > > >          } else {
>> > > >              ovs_list_push_back(&a->andor, &b->node);
>> > > >          }
>> > > > -        free(a->as_name);
>> > > >          a->as_name = NULL;
>> > > >          return a;
>> > > >      } else if (b->type == type) {
>> > > >          ovs_list_push_front(&b->andor, &a->node);
>> > > > -        free(b->as_name);
>> > > >          b->as_name = NULL;
>> > > >          return b;
>> > > >      } else {
>> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context
*, struct expr_field *);
>> > > >
>> > > >  static struct expr *
>> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
>> > > > -             const union expr_constant *c)
>> > > > +             const struct expr_constant *c)
>> > > >  {
>> > > >      struct expr *e = xzalloc(sizeof *e);
>> > > >      e->type = EXPR_T_CMP;
>> > > >      e->cmp.symbol = f->symbol;
>> > > >      e->cmp.relop = r;
>> > > > +    e->as_name = c->as_name;
>> > > >      if (f->symbol->width) {
>> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
>> > > >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
>> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
expr_relop r,
>> > > >
>> > > >  /* Returns the minimum reasonable width for integer constant 'c'.
*/
>> > > >  static int
>> > > > -expr_constant_width(const union expr_constant *c)
>> > > > +expr_constant_width(const struct expr_constant *c)
>> > > >  {
>> > > >      if (c->masked) {
>> > > >          return mf_subvalue_width(&c->mask);
>> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
>> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
>> > > >                           e, make_cmp__(f, r, &cs->values[i]));
>> > > >      }
>> > > > -    /* Track address set */
>> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
>> > > > -        e->as_name = xstrdup(cs->as_name);
>> > > > -    }
>> > > > +
>> > > >  exit:
>> > > >      expr_constant_set_destroy(cs);
>> > > >      return e;
>> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >          }
>> > > >      }
>> > > >
>> > > > -    struct expr_constant_set *addr_sets
>> > > > -        = (ctx->addr_sets
>> > > > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
>> > > > -           : NULL);
>> > > > -    if (!addr_sets) {
>> > > > +    struct shash_node *node = ctx->addr_sets
>> > > > +                              ? shash_find(ctx->addr_sets,
ctx->lexer->token.s)
>> > > > +                              : NULL;
>> > > > +    if (!node) {
>> > > >          lexer_syntax_error(ctx->lexer, "expecting address set
name");
>> > > >          return false;
>> > > >      }
>> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >          return false;
>> > > >      }
>> > > >
>> > > > -    if (!cs->n_values) {
>> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
>> > > > -    }
>> > > > -
>> > > > +    struct expr_constant_set *addr_sets = node->data;
>> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
>> > > >      if (n_values >= *allocated_values) {
>> > > >          cs->values = xrealloc(cs->values, n_values * sizeof
*cs->values);
>> > > >          *allocated_values = n_values;
>> > > >      }
>> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
>> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
>> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>> > > > +        *c = addr_sets->values[i];
>> > > > +        c->as_name = node->name;
>> > > >      }
>> > > >
>> > > >      return true;
>> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >          *allocated_values = n_values;
>> > > >      }
>> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
>> > > > -        cs->values[cs->n_values++].string =
>> > > > -            xstrdup(port_group->values[i].string);
>> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>> > > > +        c->string = xstrdup(port_group->values[i].string);
>> > > > +        c->as_name = NULL;
>> > > >      }
>> > > >
>> > > >      return true;
>> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >                                  sizeof *cs->values);
>> > > >      }
>> > > >
>> > > > -    /* Combining other values to the constant set that is
tracking an
>> > > > -     * address set, so untrack it. */
>> > > > -    free(cs->as_name);
>> > > > -    cs->as_name = NULL;
>> > > > -
>> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
>> > > >          return false;
>> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
>> > > >              return false;
>> > > >          }
>> > > > -        cs->values[cs->n_values++].string =
xstrdup(ctx->lexer->token.s);
>> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>> > > > +        c->string = xstrdup(ctx->lexer->token.s);
>> > > > +        c->as_name = NULL;
>> > > >          lexer_get(ctx->lexer);
>> > > >          return true;
>> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
>> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx,
struct expr_constant_set *cs,
>> > > >              return false;
>> > > >          }
>> > > >
>> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
>> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>> > > >          c->value = ctx->lexer->token.value;
>> > > >          c->format = ctx->lexer->token.format;
>> > > >          c->masked = ctx->lexer->token.type ==
LEX_T_MASKED_INTEGER;
>> > > >          if (c->masked) {
>> > > >              c->mask = ctx->lexer->token.mask;
>> > > >          }
>> > > > +        c->as_name = NULL;
>> > > >          lexer_get(ctx->lexer);
>> > > >          return true;
>> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
>> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx,
struct expr_constant_set *cs)
>> > > >   * indeterminate. */
>> > > >  bool
>> > > >  expr_constant_parse(struct lexer *lexer, const struct expr_field
*f,
>> > > > -                    union expr_constant *c)
>> > > > +                    struct expr_constant *c)
>> > > >  {
>> > > >      if (lexer->error) {
>> > > >          return false;
>> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const
struct expr_field *f,
>> > > >  /* Appends to 's' a re-parseable representation of constant 'c'
with the given
>> > > >   * 'type'. */
>> > > >  void
>> > > > -expr_constant_format(const union expr_constant *c,
>> > > > +expr_constant_format(const struct expr_constant *c,
>> > > >                       enum expr_constant_type type, struct ds *s)
>> > > >  {
>> > > >      if (type == EXPR_C_STRING) {
>> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
expr_constant *c,
>> > > >   *
>> > > >   * Does not free(c). */
>> > > >  void
>> > > > -expr_constant_destroy(const union expr_constant *c,
>> > > > +expr_constant_destroy(const struct expr_constant *c,
>> > > >                        enum expr_constant_type type)
>> > > >  {
>> > > >      if (c && type == EXPR_C_STRING) {
>> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
expr_constant_set *cs, struct ds *s)
>> > > >          ds_put_char(s, '{');
>> > > >      }
>> > > >
>> > > > -    for (const union expr_constant *c = cs->values;
>> > > > +    for (const struct expr_constant *c = cs->values;
>> > > >           c < &cs->values[cs->n_values]; c++) {
>> > > >          if (c != cs->values) {
>> > > >              ds_put_cstr(s, ", ");
>> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
expr_constant_set *cs)
>> > > >              }
>> > > >          }
>> > > >          free(cs->values);
>> > > > -        free(cs->as_name);
>> > > >      }
>> > > >  }
>> > > >
>> > > >  static int
>> > > >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
>> > > >  {
>> > > > -    const union expr_constant *a = a_;
>> > > > -    const union expr_constant *b = b_;
>> > > > +    const struct expr_constant *a = a_;
>> > > > +    const struct expr_constant *b = b_;
>> > > >
>> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
>> > > >      if (d) {
>> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char
*const *values, size_t n_values)
>> > > >              VLOG_WARN("Invalid constant set entry: '%s', token
type: %d",
>> > > >                        values[i], lex.token.type);
>> > > >          } else {
>> > > > -            union expr_constant *c = &cs->values[cs->n_values++];
>> > > > +            struct expr_constant *c = &cs->values[cs->n_values++];
>> > > >              c->value = lex.token.value;
>> > > >              c->format = lex.token.format;
>> > > >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
>> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char
*const *values, size_t n_values)
>> > > >
>> > > >  static void
>> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
>> > > > -                            union expr_constant *c, size_t
*allocated)
>> > > > +                            struct expr_constant *c, size_t
*allocated)
>> > > >  {
>> > > >      struct expr_constant_set *cs = *p_cs;
>> > > >      if (!cs) {
>> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
*const_sets, const char *name,
>> > > >                          values[i], name);
>> > > >              continue;
>> > > >          }
>> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
>> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>> > > >          c->string = xstrdup(values[i]);
>> > > >      }
>> > > >
>> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx,
bool *atomic)
>> > > >
>> > > >              *atomic = true;
>> > > >
>> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
>> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
>> > > >              cst->format = LEX_F_HEXADECIMAL;
>> > > >              cst->masked = false;
>> > > >
>> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx,
bool *atomic)
>> > > >              c.values = cst;
>> > > >              c.n_values = 1;
>> > > >              c.in_curlies = false;
>> > > > -            c.as_name = NULL;
>> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>> > > >          } else if (parse_relop(ctx, &r) &&
parse_constant_set(ctx, &c)) {
>> > > >              return make_cmp(ctx, &f, r, &c);
>> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
>> > > >  static struct expr *
>> > > >  expr_clone_cmp(struct expr *expr)
>> > > >  {
>> > > > -    ovs_assert(!expr->as_name);
>> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>> > > >      if (!new->cmp.symbol->width) {
>> > > >          new->cmp.string = xstrdup(new->cmp.string);
>> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
>> > > >  static struct expr *
>> > > >  expr_clone_condition(struct expr *expr)
>> > > >  {
>> > > > -    ovs_assert(!expr->as_name);
>> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>> > > >      new->cond.string = xstrdup(new->cond.string);
>> > > >      return new;
>> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>> > > >          return;
>> > > >      }
>> > > >
>> > > > -    free(expr->as_name);
>> > > > -
>> > > >      struct expr *sub;
>> > > >
>> > > >      switch (expr->type) {
>> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
>> > > >       * mask sizes. */
>> > > >      size_t n = ovs_list_size(&expr->andor);
>> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
>> > > > -    bool modified = false;
>> > > > +    bool has_addr_set = false;
>> > > >      /* Linked list over the 'subs' array to quickly skip deleted
elements,
>> > > >       * i.e. the index of the next potentially non-NULL element. */
>> > > >      size_t *next = xmalloc(n * sizeof *next);
>> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const
struct expr_symbol *symbol)
>> > > >      size_t i = 0, j, max_n_bits = 0;
>> > > >      struct expr *sub;
>> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
>> > > > +        if (sub->as_name) {
>> > > > +            has_addr_set = true;
>> > > > +        }
>> > > >          if (symbol->width) {
>> > > >              const unsigned long *sub_mask;
>> > > >
>> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr,
const struct expr_symbol *symbol)
>> > > >              next[last] = i;
>> > > >              last = i;
>> > > >          } else {
>> > > > +            /* Remove address set reference from the duplicate. */
>> > > > +            subs[last]->as_name = NULL;
>> > > >              expr_destroy(subs[i]);
>> > > >              subs[i] = NULL;
>> > > > -            modified = true;
>> > > >          }
>> > > >      }
>> > > >
>> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
>> > > > -        || (!modified && expr->as_name)) {
>> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
has_addr_set) {
>> > > >          /* Not a fully maskable field or this expression is
tracking an
>> > > >           * address set.  Don't try to optimize to preserve
address set I-P. */
>> > > >          goto done;
>> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr,
const struct expr_symbol *symbol)
>> > > >              if (expr_bitmap_intersect_check(a_value, a_mask,
b_value, b_mask,
>> > > >                                              bit_width)
>> > > >                  && bitmap_is_superset(b_mask, a_mask, bit_width))
{
>> > > > -                /* 'a' is the same expression with a smaller
mask. */
>> > > > +                /* 'a' is the same expression with a smaller mask.
>> > > > +                 * Remove address set reference from the
duplicate. */
>> > > > +                a->as_name = NULL;
>> > > >                  expr_destroy(subs[j]);
>> > > >                  subs[j] = NULL;
>> > > > -                modified = true;
>> > > >
>> > > >                  /* Shorten the path for the next round. */
>> > > >                  if (last) {
>> > > > @@ -2685,12 +2676,6 @@ done:
>> > > >          }
>> > > >      }
>> > > >
>> > > > -    if (modified) {
>> > > > -        /* Members modified, so untrack address set. */
>> > > > -        free(expr->as_name);
>> > > > -        expr->as_name = NULL;
>> > > > -    }
>> > > > -
>> > > >      free(next);
>> > > >      free(subs);
>> > > >      return expr;
>> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
>> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
>> > > >          struct expr_match *match = expr_match_new(m, clause,
n_clauses,
>> > > >                                                    conj_id);
>> > > > -        if (or->as_name) {
>> > > > +        if (sub->as_name) {
>> > > >              ovs_assert(sub->type == EXPR_T_CMP);
>> > > >              ovs_assert(sub->cmp.symbol->width);
>> > > > -            match->as_name = xstrdup(or->as_name);
>> > > > +            match->as_name = xstrdup(sub->as_name);
>> > > >              match->as_ip = sub->cmp.value.ipv6;
>> > > >              match->as_mask = sub->cmp.mask.ipv6;
>> > > >          }
>> > > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
>> > > > index be198e00d..27cec2aec 100644
>> > > > --- a/tests/ovn-controller.at
>> > > > +++ b/tests/ovn-controller.at
>> > > > @@ -1625,7 +1625,9 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
actions=lo
>> > > >  done
>> > > >
>> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [10
>> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
>> > > > +# Last 5 is due to overlap in IP addresses between as1 and as2.
>> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [7
>> > > >  ])
>> > > >
>> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
>> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
>> > > >  done
>> > > >
>> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>> > > > -# In this case the as1 and as2 are merged to a single OR expr, so
we lose track of
>> > > > -# address set information - can't handle deletion incrementally.
>> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [10
>> > > > +# First 5 are due to overlap in IP addresses between as1 and as2.
>> > > > +# Last 2 are due to change from 2 IP in AS to 1.
>> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [7
>> > > >  ])
>> > > >
>> > > >  OVN_CLEANUP([hv1])
>> > > > @@ -1817,7 +1819,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
actions=co
>> > > >
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
>> > > >  ])
>> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [1
>> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [0
>> > > >  ])
>> > > >
>> > > >  # Delete 2 IPs
>> > > > @@ -1837,7 +1839,7 @@
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
actions=co
>> > > >
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
>> > > >  ])
>> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [1
>> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
[0], [0
>> > > >  ])
>> > > >
>> > > >  OVN_CLEANUP([hv1])
>> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
>> > > >
>> > > >  AT_CLEANUP
>> > > >  ])
>> > > > +
>> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
>> > > > +AT_KEYWORDS([as-i-p])
>> > > > +
>> > > > +ovn_start
>> > > > +
>> > > > +net_add n1
>> > > > +sim_add hv1
>> > > > +as hv1
>> > > > +check ovs-vsctl add-br br-phys
>> > > > +ovn_attach n1 br-phys 192.168.0.1
>> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
>> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
>> > > > +
>> > > > +check ovn-nbctl ls-add ls1
>> > > > +
>> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
>> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
>> > > > +
>> > > > +wait_for_ports_up
>> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
>> > > > +
>> > > > +# Get the OF table numbers
>> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
>> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
>> > > > +
>> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
external_ids:name=ls1))
>> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
logical_port=ls1-lp1))
>> > > > +
>> > > > +ovn-nbctl create address_set name=as1
>> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" &&
ip4.src == $as1' drop
>> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
>> > > > +check ovn-nbctl --wait=hv sync
>> > > > +
>> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +])
>> > > > +
>> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
>> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
>> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
>> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
>> > > > +check ovn-nbctl --wait=hv sync
>> > > > +
>> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +])
>> > > > +
>> > > > +
>> > > > +check ovn-appctl inc-engine/recompute
>> > > > +
>> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
sort], [0], [dnl
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > +])
>> > > > +
>> > > > +OVN_CLEANUP([hv1])
>> > > > +AT_CLEANUP
>> > > > --
>> > > > 2.44.0
>> > > >
>> >
>> >
>> > Thank you Han,
>> >
>> > I would like to start the discussion about the possibility of
>> > backporting this change. The reason for the backport being that this
>> > change might be beneficial to ovn-kubernetes users, as this was
>> > actually observed in ovn-kubernetes with network policies. I know that
>> > strictly speaking this is not a bug fix, because this limitation was
>> > well documented, however I would like to hear the opinion of others.
>>
>>
>> I have no objections if this improvement is strongly required for
released versions, even though it is not a bug. May I ask for which
releases do you want this to be backported?
>
>
> We have a request for 23.09 and 24.03.
>
Ok. I will wait for input from other maintainers. It will be a tradeoff
between risk and performance gains. Mark, Numan and Dumitru, what do you
think?

Thanks,
Han

>>
>>
>> Thanks,
>> Han
>>
>> >
>> > Thanks,
>> > Ales
>> > --
>> >
>> > Ales Musil
>> >
>> > Senior Software Engineer - OVN Core
>> >
>> > Red Hat EMEA
>> >
>> > amusil@redhat.com
>> >
>
>
>
> --
>
> Ales Musil
>
> Senior Software Engineer - OVN Core
>
> Red Hat EMEA
>
> amusil@redhat.com
Numan Siddique May 8, 2024, 4:14 p.m. UTC | #6
On Tue, May 7, 2024 at 2:13 AM Han Zhou <hzhou@ovn.org> wrote:
>
> On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com> wrote:
> >
> >
> >
> > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org> wrote:
> >>
> >>
> >>
> >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com> wrote:
> >> >
> >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org> wrote:
> >> > >
> >> > >
> >> > >
> >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com> wrote:
> >> > > >
> >> > > > Instead of tracking address set per struct expr_constant_set track
> it
> >> > > > per individual struct expr_constant. This allows more fine grained
> >> > > > control for I-P processing of address sets in controller. It helps
> with
> >> > > > scenarios like matching on two address sets in one expression e.g.
> >> > > > "ip4.src == {$as1, $as2}". This allows any addition or removal of
> >> > > > individual adress from the set to be incrementally processed
> instead
> >> > > > of reprocessing all the flows.
> >> > > >
> >> > > > This unfortunately doesn't help with the following flows:
> >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> >> > > >
> >> > > > The memory impact should be minimal as there is only increase of 8
> bytes
> >> > > > per the struct expr_constant.
> >> > > >
> >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> >> > > > Signed-off-by: Ales Musil <amusil@redhat.com>
> >> > > > ---
> >> > > > v4: Rebase on top of current main.
> >> > > >     Update the "lflow_handle_addr_set_update" comment according to
> suggestion from Han.
> >> > >
> >> > > Thanks Ales. I updated the commit message for the same, and applied
> to main branch.
> >> > >
> >> > > Regards,
> >> > > Han
> >> > >
> >> > > > v3: Rebase on top of current main.
> >> > > >     Address comments from Han:
> >> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to
> include remaning corner cases.
> >> > > >     - Make sure that the flows are consistent between I-P and
> recompute.
> >> > > > v2: Rebase on top of current main.
> >> > > >     Adjust the comment for I-P optimization.
> >> > > > ---
> >> > > >  controller/lflow.c      | 11 ++---
> >> > > >  include/ovn/actions.h   |  2 +-
> >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> >> > > >  lib/actions.c           | 20 ++++-----
> >> > > >  lib/expr.c              | 99
> +++++++++++++++++------------------------
> >> > > >  tests/ovn-controller.at | 79 +++++++++++++++++++++++++++++---
> >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> >> > > >
> >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> >> > > > index 760ec0b41..1e05665a1 100644
> >> > > > --- a/controller/lflow.c
> >> > > > +++ b/controller/lflow.c
> >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct lflow_ctx_in
> *l_ctx_in,
> >> > > >  }
> >> > > >
> >> > > >  static bool
> >> > > > -as_info_from_expr_const(const char *as_name, const union
> expr_constant *c,
> >> > > > +as_info_from_expr_const(const char *as_name, const struct
> expr_constant *c,
> >> > > >                          struct addrset_info *as_info)
> >> > > >  {
> >> > > >      as_info->name = as_name;
> >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
> *as_name, struct addr_set_diff *as_diff,
> >> > > >   *        generated.
> >> > > >   *
> >> > > >   *      - The sub expression of the address set is combined with
> other sub-
> >> > > > - *        expressions/constants, usually because of disjunctions
> between
> >> > > > - *        sub-expressions/constants, e.g.:
> >> > > > + *        expressions/constants on different fields, e.g.:
> >> > > >   *
> >> > > >   *          ip.src == $as1 || ip.dst == $as2
> >> > > > - *          ip.src == {$as1, $as2}
> >> > > > - *          ip.src == {$as1, ip1}
> >> > > >   *
> >> > > > - *        All these could have been split into separate lflows.
> >> > > > + *        This could have been split into separate lflows.
> >> > > >   *
> >> > > >   *      - Conjunctions overlapping between lflows, which can be
> caused by
> >> > > >   *        overlapping address sets or same address set used by
> multiple lflows
> >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char
> *as_name,
> >> > > >          if (as_diff->deleted) {
> >> > > >              struct addrset_info as_info;
> >> > > >              for (size_t i = 0; i < as_diff->deleted->n_values;
> i++) {
> >> > > > -                union expr_constant *c =
> &as_diff->deleted->values[i];
> >> > > > +                struct expr_constant *c =
> &as_diff->deleted->values[i];
> >> > > >                  if (!as_info_from_expr_const(as_name, c,
> &as_info)) {
> >> > > >                      continue;
> >> > > >                  }
> >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> >> > > > index ae0864fdd..88cf4de79 100644
> >> > > > --- a/include/ovn/actions.h
> >> > > > +++ b/include/ovn/actions.h
> >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> >> > > >  struct ovnact_load {
> >> > > >      struct ovnact ovnact;
> >> > > >      struct expr_field dst;
> >> > > > -    union expr_constant imm;
> >> > > > +    struct expr_constant imm;
> >> > > >  };
> >> > > >
> >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> >> > > > index c48f82398..e54edb5bf 100644
> >> > > > --- a/include/ovn/expr.h
> >> > > > +++ b/include/ovn/expr.h
> >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type type,
> enum expr_relop *relop);
> >> > > >  struct expr {
> >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or
> EXPR_T_OR if any. */
> >> > > >      enum expr_type type;        /* Expression type. */
> >> > > > -    char *as_name;              /* Address set name. Null if it
> is not an
> >> > > > +    const char *as_name;        /* Address set name. Null if it
> is not an
> >> > > >                                     address set. */
> >> > > >
> >> > > >      union {
> >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> >> > > >  };
> >> > > >
> >> > > >  /* A string or integer constant (one must know which from
> context). */
> >> > > > -union expr_constant {
> >> > > > -    /* Integer constant.
> >> > > > -     *
> >> > > > -     * The width of a constant isn't always clear, e.g. if you
> write "1",
> >> > > > -     * there's no way to tell whether you mean for that to be a
> 1-bit constant
> >> > > > -     * or a 128-bit constant or somewhere in between. */
> >> > > > -    struct {
> >> > > > -        union mf_subvalue value;
> >> > > > -        union mf_subvalue mask; /* Only initialized if 'masked'.
> */
> >> > > > -        bool masked;
> >> > > > -
> >> > > > -        enum lex_format format; /* From the constant's lex_token.
> */
> >> > > > -    };
> >> > > > +struct expr_constant {
> >> > > > +    const char *as_name;
> >> > > >
> >> > > > -    /* Null-terminated string constant. */
> >> > > > -    char *string;
> >> > > > +    union {
> >> > > > +        /* Integer constant.
> >> > > > +         *
> >> > > > +         * The width of a constant isn't always clear, e.g. if
> you write "1",
> >> > > > +         * there's no way to tell whether you mean for that to be
> a 1-bit
> >> > > > +         * constant or a 128-bit constant or somewhere in
> between. */
> >> > > > +        struct {
> >> > > > +            union mf_subvalue value;
> >> > > > +            union mf_subvalue mask; /* Only initialized if
> 'masked'. */
> >> > > > +            bool masked;
> >> > > > +
> >> > > > +            enum lex_format format; /* From the constant's
> lex_token. */
> >> > > > +        };
> >> > > > +
> >> > > > +        /* Null-terminated string constant. */
> >> > > > +        char *string;
> >> > > > +    };
> >> > > >  };
> >> > > >
> >> > > >  bool expr_constant_parse(struct lexer *,
> >> > > >                           const struct expr_field *,
> >> > > > -                         union expr_constant *);
> >> > > > -void expr_constant_format(const union expr_constant *,
> >> > > > +                         struct expr_constant *);
> >> > > > +void expr_constant_format(const struct expr_constant *,
> >> > > >                            enum expr_constant_type, struct ds *);
> >> > > > -void expr_constant_destroy(const union expr_constant *,
> >> > > > +void expr_constant_destroy(const struct expr_constant *,
> >> > > >                             enum expr_constant_type);
> >> > > >
> >> > > >  /* A collection of "union expr_constant"s of the same type. */
> >> > > >  struct expr_constant_set {
> >> > > > -    union expr_constant *values;  /* Constants. */
> >> > > > +    struct expr_constant *values; /* Constants. */
> >> > > >      size_t n_values;              /* Number of constants. */
> >> > > >      enum expr_constant_type type; /* Type of the constants. */
> >> > > >      bool in_curlies;              /* Whether the constants were
> in {}. */
> >> > > > -    char *as_name;                /* Name of an address set. It
> is NULL if not
> >> > > > -                                     an address set. */
> >> > > >  };
> >> > > >
> >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> expr_constant_set *);
> >> > > > diff --git a/lib/actions.c b/lib/actions.c
> >> > > > index 94751d059..cff4f9e3c 100644
> >> > > > --- a/lib/actions.c
> >> > > > +++ b/lib/actions.c
> >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
> >> > > >              const struct ovnact_encode_params *ep,
> >> > > >              struct ofpbuf *ofpacts)
> >> > > >  {
> >> > > > -    const union expr_constant *c = &load->imm;
> >> > > > +    const struct expr_constant *c = &load->imm;
> >> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> >> > > >      struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
> dst.field,
> >> > > >                                                         NULL,
> NULL);
> >> > > > @@ -2077,7 +2077,7 @@ encode_event_empty_lb_backends_opts(struct
> ofpbuf *ofpacts,
> >> > > >          /* All empty_lb_backends fields are of type 'str' */
> >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> >> > > >
> >> > > > -        const union expr_constant *c = o->value.values;
> >> > > > +        const struct expr_constant *c = o->value.values;
> >> > > >          size_t size = strlen(c->string);
> >> > > >          struct controller_event_opt_header hdr =
> >> > > >              (struct controller_event_opt_header) {
> >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> action_context *ctx,
> >> > > >  {
> >> > > >      for (size_t i = 0; i < n_options; i++) {
> >> > > >          const struct ovnact_gen_option *o = &options[i];
> >> > > > -        const union expr_constant *c = o->value.values;
> >> > > > +        const struct expr_constant *c = o->value.values;
> >> > > >          struct sockaddr_storage ss;
> >> > > >          struct uuid uuid;
> >> > > >
> >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
> ovnact_gen_option *o,
> >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >> > > >      opt_header[0] = o->option->code;
> >> > > >
> >> > > > -    const union expr_constant *c = o->value.values;
> >> > > > +    const struct expr_constant *c = o->value.values;
> >> > > >      size_t n_values = o->value.n_values;
> >> > > >      if (!strcmp(o->option->type, "bool") ||
> >> > > >          !strcmp(o->option->type, "uint8")) {
> >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
> ovnact_gen_option *o,
> >> > > >                           struct ofpbuf *ofpacts)
> >> > > >  {
> >> > > >      struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts,
> sizeof *opt);
> >> > > > -    const union expr_constant *c = o->value.values;
> >> > > > +    const struct expr_constant *c = o->value.values;
> >> > > >      size_t n_values = o->value.n_values;
> >> > > >      size_t size;
> >> > > >
> >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> >> > > >          find_opt(pdo->options, pdo->n_options,
> DHCP_OPT_BOOTFILE_CODE);
> >> > > >      if (boot_opt) {
> >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >> > > > -        const union expr_constant *c = boot_opt->value.values;
> >> > > > +        const struct expr_constant *c = boot_opt->value.values;
> >> > > >          opt_header[0] = boot_opt->option->code;
> >> > > >          opt_header[1] = strlen(c->string);
> >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> >> > > >          find_opt(pdo->options, pdo->n_options,
> DHCP_OPT_BOOTFILE_ALT_CODE);
> >> > > >      if (boot_alt_opt) {
> >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >> > > > -        const union expr_constant *c = boot_alt_opt->value.values;
> >> > > > +        const struct expr_constant *c =
> boot_alt_opt->value.values;
> >> > > >          opt_header[0] = boot_alt_opt->option->code;
> >> > > >          opt_header[1] = strlen(c->string);
> >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> ovnact_put_opts *pdo,
> >> > > >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
> >> > > >      if (next_server_opt) {
> >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >> > > > -        const union expr_constant *c =
> next_server_opt->value.values;
> >> > > > +        const struct expr_constant *c =
> next_server_opt->value.values;
> >> > > >          opt_header[0] = next_server_opt->option->code;
> >> > > >          opt_header[1] = sizeof(ovs_be32);
> >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
> >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct action_context
> *ctx, const struct expr_field *dst,
> >> > > >      /* Let's validate the options. */
> >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> >> > > >          const struct ovnact_gen_option *o = &po->options[i];
> >> > > > -        const union expr_constant *c = o->value.values;
> >> > > > +        const struct expr_constant *c = o->value.values;
> >> > > >          if (o->value.n_values > 1) {
> >> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
> option",
> >> > > >                          o->option->name);
> >> > > > @@ -3490,7 +3490,7 @@ static void
> >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
> ra_offset)
> >> > > >  {
> >> > > > -    const union expr_constant *c = o->value.values;
> >> > > > +    const struct expr_constant *c = o->value.values;
> >> > > >
> >> > > >      switch (o->option->code) {
> >> > > >      case ND_RA_FLAG_ADDR_MODE:
> >> > > > diff --git a/lib/expr.c b/lib/expr.c
> >> > > > index 0cb44e1b6..ecf8a6338 100644
> >> > > > --- a/lib/expr.c
> >> > > > +++ b/lib/expr.c
> >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct
> expr *a, struct expr *b)
> >> > > >          } else {
> >> > > >              ovs_list_push_back(&a->andor, &b->node);
> >> > > >          }
> >> > > > -        free(a->as_name);
> >> > > >          a->as_name = NULL;
> >> > > >          return a;
> >> > > >      } else if (b->type == type) {
> >> > > >          ovs_list_push_front(&b->andor, &a->node);
> >> > > > -        free(b->as_name);
> >> > > >          b->as_name = NULL;
> >> > > >          return b;
> >> > > >      } else {
> >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct expr_context
> *, struct expr_field *);
> >> > > >
> >> > > >  static struct expr *
> >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> >> > > > -             const union expr_constant *c)
> >> > > > +             const struct expr_constant *c)
> >> > > >  {
> >> > > >      struct expr *e = xzalloc(sizeof *e);
> >> > > >      e->type = EXPR_T_CMP;
> >> > > >      e->cmp.symbol = f->symbol;
> >> > > >      e->cmp.relop = r;
> >> > > > +    e->as_name = c->as_name;
> >> > > >      if (f->symbol->width) {
> >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> >> > > >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
> >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, enum
> expr_relop r,
> >> > > >
> >> > > >  /* Returns the minimum reasonable width for integer constant 'c'.
> */
> >> > > >  static int
> >> > > > -expr_constant_width(const union expr_constant *c)
> >> > > > +expr_constant_width(const struct expr_constant *c)
> >> > > >  {
> >> > > >      if (c->masked) {
> >> > > >          return mf_subvalue_width(&c->mask);
> >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
> >> > > >                           e, make_cmp__(f, r, &cs->values[i]));
> >> > > >      }
> >> > > > -    /* Track address set */
> >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
> >> > > > -        e->as_name = xstrdup(cs->as_name);
> >> > > > -    }
> >> > > > +
> >> > > >  exit:
> >> > > >      expr_constant_set_destroy(cs);
> >> > > >      return e;
> >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >          }
> >> > > >      }
> >> > > >
> >> > > > -    struct expr_constant_set *addr_sets
> >> > > > -        = (ctx->addr_sets
> >> > > > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
> >> > > > -           : NULL);
> >> > > > -    if (!addr_sets) {
> >> > > > +    struct shash_node *node = ctx->addr_sets
> >> > > > +                              ? shash_find(ctx->addr_sets,
> ctx->lexer->token.s)
> >> > > > +                              : NULL;
> >> > > > +    if (!node) {
> >> > > >          lexer_syntax_error(ctx->lexer, "expecting address set
> name");
> >> > > >          return false;
> >> > > >      }
> >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >          return false;
> >> > > >      }
> >> > > >
> >> > > > -    if (!cs->n_values) {
> >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> >> > > > -    }
> >> > > > -
> >> > > > +    struct expr_constant_set *addr_sets = node->data;
> >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> >> > > >      if (n_values >= *allocated_values) {
> >> > > >          cs->values = xrealloc(cs->values, n_values * sizeof
> *cs->values);
> >> > > >          *allocated_values = n_values;
> >> > > >      }
> >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> >> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
> >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +        *c = addr_sets->values[i];
> >> > > > +        c->as_name = node->name;
> >> > > >      }
> >> > > >
> >> > > >      return true;
> >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >          *allocated_values = n_values;
> >> > > >      }
> >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> >> > > > -        cs->values[cs->n_values++].string =
> >> > > > -            xstrdup(port_group->values[i].string);
> >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +        c->string = xstrdup(port_group->values[i].string);
> >> > > > +        c->as_name = NULL;
> >> > > >      }
> >> > > >
> >> > > >      return true;
> >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >                                  sizeof *cs->values);
> >> > > >      }
> >> > > >
> >> > > > -    /* Combining other values to the constant set that is
> tracking an
> >> > > > -     * address set, so untrack it. */
> >> > > > -    free(cs->as_name);
> >> > > > -    cs->as_name = NULL;
> >> > > > -
> >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> >> > > >          return false;
> >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
> >> > > >              return false;
> >> > > >          }
> >> > > > -        cs->values[cs->n_values++].string =
> xstrdup(ctx->lexer->token.s);
> >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> >> > > > +        c->as_name = NULL;
> >> > > >          lexer_get(ctx->lexer);
> >> > > >          return true;
> >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx,
> struct expr_constant_set *cs,
> >> > > >              return false;
> >> > > >          }
> >> > > >
> >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > >          c->value = ctx->lexer->token.value;
> >> > > >          c->format = ctx->lexer->token.format;
> >> > > >          c->masked = ctx->lexer->token.type ==
> LEX_T_MASKED_INTEGER;
> >> > > >          if (c->masked) {
> >> > > >              c->mask = ctx->lexer->token.mask;
> >> > > >          }
> >> > > > +        c->as_name = NULL;
> >> > > >          lexer_get(ctx->lexer);
> >> > > >          return true;
> >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context *ctx,
> struct expr_constant_set *cs)
> >> > > >   * indeterminate. */
> >> > > >  bool
> >> > > >  expr_constant_parse(struct lexer *lexer, const struct expr_field
> *f,
> >> > > > -                    union expr_constant *c)
> >> > > > +                    struct expr_constant *c)
> >> > > >  {
> >> > > >      if (lexer->error) {
> >> > > >          return false;
> >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, const
> struct expr_field *f,
> >> > > >  /* Appends to 's' a re-parseable representation of constant 'c'
> with the given
> >> > > >   * 'type'. */
> >> > > >  void
> >> > > > -expr_constant_format(const union expr_constant *c,
> >> > > > +expr_constant_format(const struct expr_constant *c,
> >> > > >                       enum expr_constant_type type, struct ds *s)
> >> > > >  {
> >> > > >      if (type == EXPR_C_STRING) {
> >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> expr_constant *c,
> >> > > >   *
> >> > > >   * Does not free(c). */
> >> > > >  void
> >> > > > -expr_constant_destroy(const union expr_constant *c,
> >> > > > +expr_constant_destroy(const struct expr_constant *c,
> >> > > >                        enum expr_constant_type type)
> >> > > >  {
> >> > > >      if (c && type == EXPR_C_STRING) {
> >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
> expr_constant_set *cs, struct ds *s)
> >> > > >          ds_put_char(s, '{');
> >> > > >      }
> >> > > >
> >> > > > -    for (const union expr_constant *c = cs->values;
> >> > > > +    for (const struct expr_constant *c = cs->values;
> >> > > >           c < &cs->values[cs->n_values]; c++) {
> >> > > >          if (c != cs->values) {
> >> > > >              ds_put_cstr(s, ", ");
> >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> expr_constant_set *cs)
> >> > > >              }
> >> > > >          }
> >> > > >          free(cs->values);
> >> > > > -        free(cs->as_name);
> >> > > >      }
> >> > > >  }
> >> > > >
> >> > > >  static int
> >> > > >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
> >> > > >  {
> >> > > > -    const union expr_constant *a = a_;
> >> > > > -    const union expr_constant *b = b_;
> >> > > > +    const struct expr_constant *a = a_;
> >> > > > +    const struct expr_constant *b = b_;
> >> > > >
> >> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> >> > > >      if (d) {
> >> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const char
> *const *values, size_t n_values)
> >> > > >              VLOG_WARN("Invalid constant set entry: '%s', token
> type: %d",
> >> > > >                        values[i], lex.token.type);
> >> > > >          } else {
> >> > > > -            union expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +            struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > >              c->value = lex.token.value;
> >> > > >              c->format = lex.token.format;
> >> > > >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
> >> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const char
> *const *values, size_t n_values)
> >> > > >
> >> > > >  static void
> >> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> >> > > > -                            union expr_constant *c, size_t
> *allocated)
> >> > > > +                            struct expr_constant *c, size_t
> *allocated)
> >> > > >  {
> >> > > >      struct expr_constant_set *cs = *p_cs;
> >> > > >      if (!cs) {
> >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
> *const_sets, const char *name,
> >> > > >                          values[i], name);
> >> > > >              continue;
> >> > > >          }
> >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
> >> > > >          c->string = xstrdup(values[i]);
> >> > > >      }
> >> > > >
> >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context *ctx,
> bool *atomic)
> >> > > >
> >> > > >              *atomic = true;
> >> > > >
> >> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
> >> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> >> > > >              cst->format = LEX_F_HEXADECIMAL;
> >> > > >              cst->masked = false;
> >> > > >
> >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context *ctx,
> bool *atomic)
> >> > > >              c.values = cst;
> >> > > >              c.n_values = 1;
> >> > > >              c.in_curlies = false;
> >> > > > -            c.as_name = NULL;
> >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> >> > > >          } else if (parse_relop(ctx, &r) &&
> parse_constant_set(ctx, &c)) {
> >> > > >              return make_cmp(ctx, &f, r, &c);
> >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
> >> > > >  static struct expr *
> >> > > >  expr_clone_cmp(struct expr *expr)
> >> > > >  {
> >> > > > -    ovs_assert(!expr->as_name);
> >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> >> > > >      if (!new->cmp.symbol->width) {
> >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> >> > > >  static struct expr *
> >> > > >  expr_clone_condition(struct expr *expr)
> >> > > >  {
> >> > > > -    ovs_assert(!expr->as_name);
> >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> >> > > >      new->cond.string = xstrdup(new->cond.string);
> >> > > >      return new;
> >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> >> > > >          return;
> >> > > >      }
> >> > > >
> >> > > > -    free(expr->as_name);
> >> > > > -
> >> > > >      struct expr *sub;
> >> > > >
> >> > > >      switch (expr->type) {
> >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> >> > > >       * mask sizes. */
> >> > > >      size_t n = ovs_list_size(&expr->andor);
> >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> >> > > > -    bool modified = false;
> >> > > > +    bool has_addr_set = false;
> >> > > >      /* Linked list over the 'subs' array to quickly skip deleted
> elements,
> >> > > >       * i.e. the index of the next potentially non-NULL element. */
> >> > > >      size_t *next = xmalloc(n * sizeof *next);
> >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, const
> struct expr_symbol *symbol)
> >> > > >      size_t i = 0, j, max_n_bits = 0;
> >> > > >      struct expr *sub;
> >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> >> > > > +        if (sub->as_name) {
> >> > > > +            has_addr_set = true;
> >> > > > +        }
> >> > > >          if (symbol->width) {
> >> > > >              const unsigned long *sub_mask;
> >> > > >
> >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr,
> const struct expr_symbol *symbol)
> >> > > >              next[last] = i;
> >> > > >              last = i;
> >> > > >          } else {
> >> > > > +            /* Remove address set reference from the duplicate. */
> >> > > > +            subs[last]->as_name = NULL;
> >> > > >              expr_destroy(subs[i]);
> >> > > >              subs[i] = NULL;
> >> > > > -            modified = true;
> >> > > >          }
> >> > > >      }
> >> > > >
> >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> >> > > > -        || (!modified && expr->as_name)) {
> >> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
> has_addr_set) {
> >> > > >          /* Not a fully maskable field or this expression is
> tracking an
> >> > > >           * address set.  Don't try to optimize to preserve
> address set I-P. */
> >> > > >          goto done;
> >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr,
> const struct expr_symbol *symbol)
> >> > > >              if (expr_bitmap_intersect_check(a_value, a_mask,
> b_value, b_mask,
> >> > > >                                              bit_width)
> >> > > >                  && bitmap_is_superset(b_mask, a_mask, bit_width))
> {
> >> > > > -                /* 'a' is the same expression with a smaller
> mask. */
> >> > > > +                /* 'a' is the same expression with a smaller mask.
> >> > > > +                 * Remove address set reference from the
> duplicate. */
> >> > > > +                a->as_name = NULL;
> >> > > >                  expr_destroy(subs[j]);
> >> > > >                  subs[j] = NULL;
> >> > > > -                modified = true;
> >> > > >
> >> > > >                  /* Shorten the path for the next round. */
> >> > > >                  if (last) {
> >> > > > @@ -2685,12 +2676,6 @@ done:
> >> > > >          }
> >> > > >      }
> >> > > >
> >> > > > -    if (modified) {
> >> > > > -        /* Members modified, so untrack address set. */
> >> > > > -        free(expr->as_name);
> >> > > > -        expr->as_name = NULL;
> >> > > > -    }
> >> > > > -
> >> > > >      free(next);
> >> > > >      free(subs);
> >> > > >      return expr;
> >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> >> > > >          struct expr_match *match = expr_match_new(m, clause,
> n_clauses,
> >> > > >                                                    conj_id);
> >> > > > -        if (or->as_name) {
> >> > > > +        if (sub->as_name) {
> >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> >> > > >              ovs_assert(sub->cmp.symbol->width);
> >> > > > -            match->as_name = xstrdup(or->as_name);
> >> > > > +            match->as_name = xstrdup(sub->as_name);
> >> > > >              match->as_ip = sub->cmp.value.ipv6;
> >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> >> > > >          }
> >> > > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> >> > > > index be198e00d..27cec2aec 100644
> >> > > > --- a/tests/ovn-controller.at
> >> > > > +++ b/tests/ovn-controller.at
> >> > > > @@ -1625,7 +1625,9 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> actions=lo
> >> > > >  done
> >> > > >
> >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [10
> >> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> >> > > > +# Last 5 is due to overlap in IP addresses between as1 and as2.
> >> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [7
> >> > > >  ])
> >> > > >
> >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> >> > > >  done
> >> > > >
> >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >> > > > -# In this case the as1 and as2 are merged to a single OR expr, so
> we lose track of
> >> > > > -# address set information - can't handle deletion incrementally.
> >> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [10
> >> > > > +# First 5 are due to overlap in IP addresses between as1 and as2.
> >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> >> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [7
> >> > > >  ])
> >> > > >
> >> > > >  OVN_CLEANUP([hv1])
> >> > > > @@ -1817,7 +1819,7 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> actions=co
> >> > > >
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> >> > > >  ])
> >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [1
> >> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [0
> >> > > >  ])
> >> > > >
> >> > > >  # Delete 2 IPs
> >> > > > @@ -1837,7 +1839,7 @@
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> actions=co
> >> > > >
>  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> >> > > >  ])
> >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >> > > > -AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [1
> >> > > > +AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))],
> [0], [0
> >> > > >  ])
> >> > > >
> >> > > >  OVN_CLEANUP([hv1])
> >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> >> > > >
> >> > > >  AT_CLEANUP
> >> > > >  ])
> >> > > > +
> >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> >> > > > +AT_KEYWORDS([as-i-p])
> >> > > > +
> >> > > > +ovn_start
> >> > > > +
> >> > > > +net_add n1
> >> > > > +sim_add hv1
> >> > > > +as hv1
> >> > > > +check ovs-vsctl add-br br-phys
> >> > > > +ovn_attach n1 br-phys 192.168.0.1
> >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> >> > > > +
> >> > > > +check ovn-nbctl ls-add ls1
> >> > > > +
> >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> >> > > > +
> >> > > > +wait_for_ports_up
> >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> >> > > > +
> >> > > > +# Get the OF table numbers
> >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
> >> > > > +
> >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> external_ids:name=ls1))
> >> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
> logical_port=ls1-lp1))
> >> > > > +
> >> > > > +ovn-nbctl create address_set name=as1
> >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" &&
> ip4.src == $as1' drop
> >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> >> > > > +check ovn-nbctl --wait=hv sync
> >> > > > +
> >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +])
> >> > > > +
> >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> >> > > > +check ovn-nbctl --wait=hv sync
> >> > > > +
> >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +])
> >> > > > +
> >> > > > +
> >> > > > +check ovn-appctl inc-engine/recompute
> >> > > > +
> >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' |
> sort], [0], [dnl
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >> > > > +])
> >> > > > +
> >> > > > +OVN_CLEANUP([hv1])
> >> > > > +AT_CLEANUP
> >> > > > --
> >> > > > 2.44.0
> >> > > >
> >> >
> >> >
> >> > Thank you Han,
> >> >
> >> > I would like to start the discussion about the possibility of
> >> > backporting this change. The reason for the backport being that this
> >> > change might be beneficial to ovn-kubernetes users, as this was
> >> > actually observed in ovn-kubernetes with network policies. I know that
> >> > strictly speaking this is not a bug fix, because this limitation was
> >> > well documented, however I would like to hear the opinion of others.
> >>
> >>
> >> I have no objections if this improvement is strongly required for
> released versions, even though it is not a bug. May I ask for which
> releases do you want this to be backported?
> >
> >
> > We have a request for 23.09 and 24.03.
> >
> Ok. I will wait for input from other maintainers. It will be a tradeoff
> between risk and performance gains. Mark, Numan and Dumitru, what do you
> think?
>

No objections from me.

Numan

> Thanks,
> Han
>
> >>
> >>
> >> Thanks,
> >> Han
> >>
> >> >
> >> > Thanks,
> >> > Ales
> >> > --
> >> >
> >> > Ales Musil
> >> >
> >> > Senior Software Engineer - OVN Core
> >> >
> >> > Red Hat EMEA
> >> >
> >> > amusil@redhat.com
> >> >
> >
> >
> >
> > --
> >
> > Ales Musil
> >
> > Senior Software Engineer - OVN Core
> >
> > Red Hat EMEA
> >
> > amusil@redhat.com
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Mark Michelson May 9, 2024, 5:32 p.m. UTC | #7
On 5/7/24 02:12, Han Zhou wrote:
> 
> 
> On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com 
> <mailto:amusil@redhat.com>> wrote:
>  >
>  >
>  >
>  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org 
> <mailto:hzhou@ovn.org>> wrote:
>  >>
>  >>
>  >>
>  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com 
> <mailto:amusil@redhat.com>> wrote:
>  >> >
>  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org 
> <mailto:hzhou@ovn.org>> wrote:
>  >> > >
>  >> > >
>  >> > >
>  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com 
> <mailto:amusil@redhat.com>> wrote:
>  >> > > >
>  >> > > > Instead of tracking address set per struct expr_constant_set 
> track it
>  >> > > > per individual struct expr_constant. This allows more fine grained
>  >> > > > control for I-P processing of address sets in controller. It 
> helps with
>  >> > > > scenarios like matching on two address sets in one expression e.g.
>  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or removal of
>  >> > > > individual adress from the set to be incrementally processed 
> instead
>  >> > > > of reprocessing all the flows.
>  >> > > >
>  >> > > > This unfortunately doesn't help with the following flows:
>  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
>  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
>  >> > > >
>  >> > > > The memory impact should be minimal as there is only increase 
> of 8 bytes
>  >> > > > per the struct expr_constant.
>  >> > > >
>  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509 
> <https://issues.redhat.com/browse/FDP-509>
>  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com 
> <mailto:amusil@redhat.com>>
>  >> > > > ---
>  >> > > > v4: Rebase on top of current main.
>  >> > > >     Update the "lflow_handle_addr_set_update" comment 
> according to suggestion from Han.
>  >> > >
>  >> > > Thanks Ales. I updated the commit message for the same, and 
> applied to main branch.
>  >> > >
>  >> > > Regards,
>  >> > > Han
>  >> > >
>  >> > > > v3: Rebase on top of current main.
>  >> > > >     Address comments from Han:
>  >> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to 
> include remaning corner cases.
>  >> > > >     - Make sure that the flows are consistent between I-P and 
> recompute.
>  >> > > > v2: Rebase on top of current main.
>  >> > > >     Adjust the comment for I-P optimization.
>  >> > > > ---
>  >> > > >  controller/lflow.c      | 11 ++---
>  >> > > >  include/ovn/actions.h   |  2 +-
>  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
>  >> > > >  lib/actions.c           | 20 ++++-----
>  >> > > >  lib/expr.c              | 99 
> +++++++++++++++++------------------------
>  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> | 79 
> +++++++++++++++++++++++++++++---
>  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
>  >> > > >
>  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
>  >> > > > index 760ec0b41..1e05665a1 100644
>  >> > > > --- a/controller/lflow.c
>  >> > > > +++ b/controller/lflow.c
>  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct 
> lflow_ctx_in *l_ctx_in,
>  >> > > >  }
>  >> > > >
>  >> > > >  static bool
>  >> > > > -as_info_from_expr_const(const char *as_name, const union 
> expr_constant *c,
>  >> > > > +as_info_from_expr_const(const char *as_name, const struct 
> expr_constant *c,
>  >> > > >                          struct addrset_info *as_info)
>  >> > > >  {
>  >> > > >      as_info->name = as_name;
>  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char 
> *as_name, struct addr_set_diff *as_diff,
>  >> > > >   *        generated.
>  >> > > >   *
>  >> > > >   *      - The sub expression of the address set is combined 
> with other sub-
>  >> > > > - *        expressions/constants, usually because of 
> disjunctions between
>  >> > > > - *        sub-expressions/constants, e.g.:
>  >> > > > + *        expressions/constants on different fields, e.g.:
>  >> > > >   *
>  >> > > >   *          ip.src == $as1 || ip.dst == $as2
>  >> > > > - *          ip.src == {$as1, $as2}
>  >> > > > - *          ip.src == {$as1, ip1}
>  >> > > >   *
>  >> > > > - *        All these could have been split into separate lflows.
>  >> > > > + *        This could have been split into separate lflows.
>  >> > > >   *
>  >> > > >   *      - Conjunctions overlapping between lflows, which can 
> be caused by
>  >> > > >   *        overlapping address sets or same address set used 
> by multiple lflows
>  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char 
> *as_name,
>  >> > > >          if (as_diff->deleted) {
>  >> > > >              struct addrset_info as_info;
>  >> > > >              for (size_t i = 0; i < 
> as_diff->deleted->n_values; i++) {
>  >> > > > -                union expr_constant *c = 
> &as_diff->deleted->values[i];
>  >> > > > +                struct expr_constant *c = 
> &as_diff->deleted->values[i];
>  >> > > >                  if (!as_info_from_expr_const(as_name, c, 
> &as_info)) {
>  >> > > >                      continue;
>  >> > > >                  }
>  >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
>  >> > > > index ae0864fdd..88cf4de79 100644
>  >> > > > --- a/include/ovn/actions.h
>  >> > > > +++ b/include/ovn/actions.h
>  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
>  >> > > >  struct ovnact_load {
>  >> > > >      struct ovnact ovnact;
>  >> > > >      struct expr_field dst;
>  >> > > > -    union expr_constant imm;
>  >> > > > +    struct expr_constant imm;
>  >> > > >  };
>  >> > > >
>  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
>  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
>  >> > > > index c48f82398..e54edb5bf 100644
>  >> > > > --- a/include/ovn/expr.h
>  >> > > > +++ b/include/ovn/expr.h
>  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type 
> type, enum expr_relop *relop);
>  >> > > >  struct expr {
>  >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or 
> EXPR_T_OR if any. */
>  >> > > >      enum expr_type type;        /* Expression type. */
>  >> > > > -    char *as_name;              /* Address set name. Null if 
> it is not an
>  >> > > > +    const char *as_name;        /* Address set name. Null if 
> it is not an
>  >> > > >                                     address set. */
>  >> > > >
>  >> > > >      union {
>  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
>  >> > > >  };
>  >> > > >
>  >> > > >  /* A string or integer constant (one must know which from 
> context). */
>  >> > > > -union expr_constant {
>  >> > > > -    /* Integer constant.
>  >> > > > -     *
>  >> > > > -     * The width of a constant isn't always clear, e.g. if 
> you write "1",
>  >> > > > -     * there's no way to tell whether you mean for that to be 
> a 1-bit constant
>  >> > > > -     * or a 128-bit constant or somewhere in between. */
>  >> > > > -    struct {
>  >> > > > -        union mf_subvalue value;
>  >> > > > -        union mf_subvalue mask; /* Only initialized if 
> 'masked'. */
>  >> > > > -        bool masked;
>  >> > > > -
>  >> > > > -        enum lex_format format; /* From the constant's 
> lex_token. */
>  >> > > > -    };
>  >> > > > +struct expr_constant {
>  >> > > > +    const char *as_name;
>  >> > > >
>  >> > > > -    /* Null-terminated string constant. */
>  >> > > > -    char *string;
>  >> > > > +    union {
>  >> > > > +        /* Integer constant.
>  >> > > > +         *
>  >> > > > +         * The width of a constant isn't always clear, e.g. 
> if you write "1",
>  >> > > > +         * there's no way to tell whether you mean for that 
> to be a 1-bit
>  >> > > > +         * constant or a 128-bit constant or somewhere in 
> between. */
>  >> > > > +        struct {
>  >> > > > +            union mf_subvalue value;
>  >> > > > +            union mf_subvalue mask; /* Only initialized if 
> 'masked'. */
>  >> > > > +            bool masked;
>  >> > > > +
>  >> > > > +            enum lex_format format; /* From the constant's 
> lex_token. */
>  >> > > > +        };
>  >> > > > +
>  >> > > > +        /* Null-terminated string constant. */
>  >> > > > +        char *string;
>  >> > > > +    };
>  >> > > >  };
>  >> > > >
>  >> > > >  bool expr_constant_parse(struct lexer *,
>  >> > > >                           const struct expr_field *,
>  >> > > > -                         union expr_constant *);
>  >> > > > -void expr_constant_format(const union expr_constant *,
>  >> > > > +                         struct expr_constant *);
>  >> > > > +void expr_constant_format(const struct expr_constant *,
>  >> > > >                            enum expr_constant_type, struct ds *);
>  >> > > > -void expr_constant_destroy(const union expr_constant *,
>  >> > > > +void expr_constant_destroy(const struct expr_constant *,
>  >> > > >                             enum expr_constant_type);
>  >> > > >
>  >> > > >  /* A collection of "union expr_constant"s of the same type. */
>  >> > > >  struct expr_constant_set {
>  >> > > > -    union expr_constant *values;  /* Constants. */
>  >> > > > +    struct expr_constant *values; /* Constants. */
>  >> > > >      size_t n_values;              /* Number of constants. */
>  >> > > >      enum expr_constant_type type; /* Type of the constants. */
>  >> > > >      bool in_curlies;              /* Whether the constants 
> were in {}. */
>  >> > > > -    char *as_name;                /* Name of an address set. 
> It is NULL if not
>  >> > > > -                                     an address set. */
>  >> > > >  };
>  >> > > >
>  >> > > >  bool expr_constant_set_parse(struct lexer *, struct 
> expr_constant_set *);
>  >> > > > diff --git a/lib/actions.c b/lib/actions.c
>  >> > > > index 94751d059..cff4f9e3c 100644
>  >> > > > --- a/lib/actions.c
>  >> > > > +++ b/lib/actions.c
>  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load *load,
>  >> > > >              const struct ovnact_encode_params *ep,
>  >> > > >              struct ofpbuf *ofpacts)
>  >> > > >  {
>  >> > > > -    const union expr_constant *c = &load->imm;
>  >> > > > +    const struct expr_constant *c = &load->imm;
>  >> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
>  >> > > >      struct ofpact_set_field *sf = 
> ofpact_put_set_field(ofpacts, dst.field,
>  >> > > >                                                         NULL, 
> NULL);
>  >> > > > @@ -2077,7 +2077,7 @@ 
> encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
>  >> > > >          /* All empty_lb_backends fields are of type 'str' */
>  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
>  >> > > >
>  >> > > > -        const union expr_constant *c = o->value.values;
>  >> > > > +        const struct expr_constant *c = o->value.values;
>  >> > > >          size_t size = strlen(c->string);
>  >> > > >          struct controller_event_opt_header hdr =
>  >> > > >              (struct controller_event_opt_header) {
>  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct 
> action_context *ctx,
>  >> > > >  {
>  >> > > >      for (size_t i = 0; i < n_options; i++) {
>  >> > > >          const struct ovnact_gen_option *o = &options[i];
>  >> > > > -        const union expr_constant *c = o->value.values;
>  >> > > > +        const struct expr_constant *c = o->value.values;
>  >> > > >          struct sockaddr_storage ss;
>  >> > > >          struct uuid uuid;
>  >> > > >
>  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct 
> ovnact_gen_option *o,
>  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>  >> > > >      opt_header[0] = o->option->code;
>  >> > > >
>  >> > > > -    const union expr_constant *c = o->value.values;
>  >> > > > +    const struct expr_constant *c = o->value.values;
>  >> > > >      size_t n_values = o->value.n_values;
>  >> > > >      if (!strcmp(o->option->type, "bool") ||
>  >> > > >          !strcmp(o->option->type, "uint8")) {
>  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct 
> ovnact_gen_option *o,
>  >> > > >                           struct ofpbuf *ofpacts)
>  >> > > >  {
>  >> > > >      struct dhcpv6_opt_header *opt = 
> ofpbuf_put_uninit(ofpacts, sizeof *opt);
>  >> > > > -    const union expr_constant *c = o->value.values;
>  >> > > > +    const struct expr_constant *c = o->value.values;
>  >> > > >      size_t n_values = o->value.n_values;
>  >> > > >      size_t size;
>  >> > > >
>  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct 
> ovnact_put_opts *pdo,
>  >> > > >          find_opt(pdo->options, pdo->n_options, 
> DHCP_OPT_BOOTFILE_CODE);
>  >> > > >      if (boot_opt) {
>  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>  >> > > > -        const union expr_constant *c = boot_opt->value.values;
>  >> > > > +        const struct expr_constant *c = boot_opt->value.values;
>  >> > > >          opt_header[0] = boot_opt->option->code;
>  >> > > >          opt_header[1] = strlen(c->string);
>  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct 
> ovnact_put_opts *pdo,
>  >> > > >          find_opt(pdo->options, pdo->n_options, 
> DHCP_OPT_BOOTFILE_ALT_CODE);
>  >> > > >      if (boot_alt_opt) {
>  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>  >> > > > -        const union expr_constant *c = 
> boot_alt_opt->value.values;
>  >> > > > +        const struct expr_constant *c = 
> boot_alt_opt->value.values;
>  >> > > >          opt_header[0] = boot_alt_opt->option->code;
>  >> > > >          opt_header[1] = strlen(c->string);
>  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct 
> ovnact_put_opts *pdo,
>  >> > > >          pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
>  >> > > >      if (next_server_opt) {
>  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>  >> > > > -        const union expr_constant *c = 
> next_server_opt->value.values;
>  >> > > > +        const struct expr_constant *c = 
> next_server_opt->value.values;
>  >> > > >          opt_header[0] = next_server_opt->option->code;
>  >> > > >          opt_header[1] = sizeof(ovs_be32);
>  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
>  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct 
> action_context *ctx, const struct expr_field *dst,
>  >> > > >      /* Let's validate the options. */
>  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
>  >> > > >          const struct ovnact_gen_option *o = &po->options[i];
>  >> > > > -        const union expr_constant *c = o->value.values;
>  >> > > > +        const struct expr_constant *c = o->value.values;
>  >> > > >          if (o->value.n_values > 1) {
>  >> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\" 
> option",
>  >> > > >                          o->option->name);
>  >> > > > @@ -3490,7 +3490,7 @@ static void
>  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
>  >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t 
> ra_offset)
>  >> > > >  {
>  >> > > > -    const union expr_constant *c = o->value.values;
>  >> > > > +    const struct expr_constant *c = o->value.values;
>  >> > > >
>  >> > > >      switch (o->option->code) {
>  >> > > >      case ND_RA_FLAG_ADDR_MODE:
>  >> > > > diff --git a/lib/expr.c b/lib/expr.c
>  >> > > > index 0cb44e1b6..ecf8a6338 100644
>  >> > > > --- a/lib/expr.c
>  >> > > > +++ b/lib/expr.c
>  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct 
> expr *a, struct expr *b)
>  >> > > >          } else {
>  >> > > >              ovs_list_push_back(&a->andor, &b->node);
>  >> > > >          }
>  >> > > > -        free(a->as_name);
>  >> > > >          a->as_name = NULL;
>  >> > > >          return a;
>  >> > > >      } else if (b->type == type) {
>  >> > > >          ovs_list_push_front(&b->andor, &a->node);
>  >> > > > -        free(b->as_name);
>  >> > > >          b->as_name = NULL;
>  >> > > >          return b;
>  >> > > >      } else {
>  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct 
> expr_context *, struct expr_field *);
>  >> > > >
>  >> > > >  static struct expr *
>  >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
>  >> > > > -             const union expr_constant *c)
>  >> > > > +             const struct expr_constant *c)
>  >> > > >  {
>  >> > > >      struct expr *e = xzalloc(sizeof *e);
>  >> > > >      e->type = EXPR_T_CMP;
>  >> > > >      e->cmp.symbol = f->symbol;
>  >> > > >      e->cmp.relop = r;
>  >> > > > +    e->as_name = c->as_name;
>  >> > > >      if (f->symbol->width) {
>  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
>  >> > > >                       &e->cmp.value, sizeof e->cmp.value, f->ofs,
>  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f, 
> enum expr_relop r,
>  >> > > >
>  >> > > >  /* Returns the minimum reasonable width for integer constant 
> 'c'. */
>  >> > > >  static int
>  >> > > > -expr_constant_width(const union expr_constant *c)
>  >> > > > +expr_constant_width(const struct expr_constant *c)
>  >> > > >  {
>  >> > > >      if (c->masked) {
>  >> > > >          return mf_subvalue_width(&c->mask);
>  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
>  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
>  >> > > >                           e, make_cmp__(f, r, &cs->values[i]));
>  >> > > >      }
>  >> > > > -    /* Track address set */
>  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
>  >> > > > -        e->as_name = xstrdup(cs->as_name);
>  >> > > > -    }
>  >> > > > +
>  >> > > >  exit:
>  >> > > >      expr_constant_set_destroy(cs);
>  >> > > >      return e;
>  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context 
> *ctx, struct expr_constant_set *cs,
>  >> > > >          }
>  >> > > >      }
>  >> > > >
>  >> > > > -    struct expr_constant_set *addr_sets
>  >> > > > -        = (ctx->addr_sets
>  >> > > > -           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
>  >> > > > -           : NULL);
>  >> > > > -    if (!addr_sets) {
>  >> > > > +    struct shash_node *node = ctx->addr_sets
>  >> > > > +                              ? shash_find(ctx->addr_sets, 
> ctx->lexer->token.s)
>  >> > > > +                              : NULL;
>  >> > > > +    if (!node) {
>  >> > > >          lexer_syntax_error(ctx->lexer, "expecting address set 
> name");
>  >> > > >          return false;
>  >> > > >      }
>  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context 
> *ctx, struct expr_constant_set *cs,
>  >> > > >          return false;
>  >> > > >      }
>  >> > > >
>  >> > > > -    if (!cs->n_values) {
>  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
>  >> > > > -    }
>  >> > > > -
>  >> > > > +    struct expr_constant_set *addr_sets = node->data;
>  >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
>  >> > > >      if (n_values >= *allocated_values) {
>  >> > > >          cs->values = xrealloc(cs->values, n_values * sizeof 
> *cs->values);
>  >> > > >          *allocated_values = n_values;
>  >> > > >      }
>  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
>  >> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
>  >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +        *c = addr_sets->values[i];
>  >> > > > +        c->as_name = node->name;
>  >> > > >      }
>  >> > > >
>  >> > > >      return true;
>  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx, 
> struct expr_constant_set *cs,
>  >> > > >          *allocated_values = n_values;
>  >> > > >      }
>  >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
>  >> > > > -        cs->values[cs->n_values++].string =
>  >> > > > -            xstrdup(port_group->values[i].string);
>  >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +        c->string = xstrdup(port_group->values[i].string);
>  >> > > > +        c->as_name = NULL;
>  >> > > >      }
>  >> > > >
>  >> > > >      return true;
>  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx, 
> struct expr_constant_set *cs,
>  >> > > >                                  sizeof *cs->values);
>  >> > > >      }
>  >> > > >
>  >> > > > -    /* Combining other values to the constant set that is 
> tracking an
>  >> > > > -     * address set, so untrack it. */
>  >> > > > -    free(cs->as_name);
>  >> > > > -    cs->as_name = NULL;
>  >> > > > -
>  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>  >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
>  >> > > >          return false;
>  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx, 
> struct expr_constant_set *cs,
>  >> > > >          if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
>  >> > > >              return false;
>  >> > > >          }
>  >> > > > -        cs->values[cs->n_values++].string = 
> xstrdup(ctx->lexer->token.s);
>  >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
>  >> > > > +        c->as_name = NULL;
>  >> > > >          lexer_get(ctx->lexer);
>  >> > > >          return true;
>  >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
>  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx, 
> struct expr_constant_set *cs,
>  >> > > >              return false;
>  >> > > >          }
>  >> > > >
>  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>  >> > > >          c->value = ctx->lexer->token.value;
>  >> > > >          c->format = ctx->lexer->token.format;
>  >> > > >          c->masked = ctx->lexer->token.type == 
> LEX_T_MASKED_INTEGER;
>  >> > > >          if (c->masked) {
>  >> > > >              c->mask = ctx->lexer->token.mask;
>  >> > > >          }
>  >> > > > +        c->as_name = NULL;
>  >> > > >          lexer_get(ctx->lexer);
>  >> > > >          return true;
>  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
>  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context 
> *ctx, struct expr_constant_set *cs)
>  >> > > >   * indeterminate. */
>  >> > > >  bool
>  >> > > >  expr_constant_parse(struct lexer *lexer, const struct 
> expr_field *f,
>  >> > > > -                    union expr_constant *c)
>  >> > > > +                    struct expr_constant *c)
>  >> > > >  {
>  >> > > >      if (lexer->error) {
>  >> > > >          return false;
>  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer, 
> const struct expr_field *f,
>  >> > > >  /* Appends to 's' a re-parseable representation of constant 
> 'c' with the given
>  >> > > >   * 'type'. */
>  >> > > >  void
>  >> > > > -expr_constant_format(const union expr_constant *c,
>  >> > > > +expr_constant_format(const struct expr_constant *c,
>  >> > > >                       enum expr_constant_type type, struct ds *s)
>  >> > > >  {
>  >> > > >      if (type == EXPR_C_STRING) {
>  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union 
> expr_constant *c,
>  >> > > >   *
>  >> > > >   * Does not free(c). */
>  >> > > >  void
>  >> > > > -expr_constant_destroy(const union expr_constant *c,
>  >> > > > +expr_constant_destroy(const struct expr_constant *c,
>  >> > > >                        enum expr_constant_type type)
>  >> > > >  {
>  >> > > >      if (c && type == EXPR_C_STRING) {
>  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct 
> expr_constant_set *cs, struct ds *s)
>  >> > > >          ds_put_char(s, '{');
>  >> > > >      }
>  >> > > >
>  >> > > > -    for (const union expr_constant *c = cs->values;
>  >> > > > +    for (const struct expr_constant *c = cs->values;
>  >> > > >           c < &cs->values[cs->n_values]; c++) {
>  >> > > >          if (c != cs->values) {
>  >> > > >              ds_put_cstr(s, ", ");
>  >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct 
> expr_constant_set *cs)
>  >> > > >              }
>  >> > > >          }
>  >> > > >          free(cs->values);
>  >> > > > -        free(cs->as_name);
>  >> > > >      }
>  >> > > >  }
>  >> > > >
>  >> > > >  static int
>  >> > > >  compare_expr_constant_integer_cb(const void *a_, const void *b_)
>  >> > > >  {
>  >> > > > -    const union expr_constant *a = a_;
>  >> > > > -    const union expr_constant *b = b_;
>  >> > > > +    const struct expr_constant *a = a_;
>  >> > > > +    const struct expr_constant *b = b_;
>  >> > > >
>  >> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
>  >> > > >      if (d) {
>  >> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const 
> char *const *values, size_t n_values)
>  >> > > >              VLOG_WARN("Invalid constant set entry: '%s', 
> token type: %d",
>  >> > > >                        values[i], lex.token.type);
>  >> > > >          } else {
>  >> > > > -            union expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +            struct expr_constant *c = 
> &cs->values[cs->n_values++];
>  >> > > >              c->value = lex.token.value;
>  >> > > >              c->format = lex.token.format;
>  >> > > >              c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
>  >> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const 
> char *const *values, size_t n_values)
>  >> > > >
>  >> > > >  static void
>  >> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
>  >> > > > -                            union expr_constant *c, size_t 
> *allocated)
>  >> > > > +                            struct expr_constant *c, size_t 
> *allocated)
>  >> > > >  {
>  >> > > >      struct expr_constant_set *cs = *p_cs;
>  >> > > >      if (!cs) {
>  >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash 
> *const_sets, const char *name,
>  >> > > >                          values[i], name);
>  >> > > >              continue;
>  >> > > >          }
>  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
>  >> > > > +        struct expr_constant *c = &cs->values[cs->n_values++];
>  >> > > >          c->string = xstrdup(values[i]);
>  >> > > >      }
>  >> > > >
>  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context 
> *ctx, bool *atomic)
>  >> > > >
>  >> > > >              *atomic = true;
>  >> > > >
>  >> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
>  >> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
>  >> > > >              cst->format = LEX_F_HEXADECIMAL;
>  >> > > >              cst->masked = false;
>  >> > > >
>  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context 
> *ctx, bool *atomic)
>  >> > > >              c.values = cst;
>  >> > > >              c.n_values = 1;
>  >> > > >              c.in_curlies = false;
>  >> > > > -            c.as_name = NULL;
>  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>  >> > > >          } else if (parse_relop(ctx, &r) && 
> parse_constant_set(ctx, &c)) {
>  >> > > >              return make_cmp(ctx, &f, r, &c);
>  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash *symtab)
>  >> > > >  static struct expr *
>  >> > > >  expr_clone_cmp(struct expr *expr)
>  >> > > >  {
>  >> > > > -    ovs_assert(!expr->as_name);
>  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>  >> > > >      if (!new->cmp.symbol->width) {
>  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
>  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
>  >> > > >  static struct expr *
>  >> > > >  expr_clone_condition(struct expr *expr)
>  >> > > >  {
>  >> > > > -    ovs_assert(!expr->as_name);
>  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>  >> > > >      new->cond.string = xstrdup(new->cond.string);
>  >> > > >      return new;
>  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>  >> > > >          return;
>  >> > > >      }
>  >> > > >
>  >> > > > -    free(expr->as_name);
>  >> > > > -
>  >> > > >      struct expr *sub;
>  >> > > >
>  >> > > >      switch (expr->type) {
>  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr, 
> const struct expr_symbol *symbol)
>  >> > > >       * mask sizes. */
>  >> > > >      size_t n = ovs_list_size(&expr->andor);
>  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
>  >> > > > -    bool modified = false;
>  >> > > > +    bool has_addr_set = false;
>  >> > > >      /* Linked list over the 'subs' array to quickly skip 
> deleted elements,
>  >> > > >       * i.e. the index of the next potentially non-NULL 
> element. */
>  >> > > >      size_t *next = xmalloc(n * sizeof *next);
>  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr, 
> const struct expr_symbol *symbol)
>  >> > > >      size_t i = 0, j, max_n_bits = 0;
>  >> > > >      struct expr *sub;
>  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
>  >> > > > +        if (sub->as_name) {
>  >> > > > +            has_addr_set = true;
>  >> > > > +        }
>  >> > > >          if (symbol->width) {
>  >> > > >              const unsigned long *sub_mask;
>  >> > > >
>  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr, 
> const struct expr_symbol *symbol)
>  >> > > >              next[last] = i;
>  >> > > >              last = i;
>  >> > > >          } else {
>  >> > > > +            /* Remove address set reference from the 
> duplicate. */
>  >> > > > +            subs[last]->as_name = NULL;
>  >> > > >              expr_destroy(subs[i]);
>  >> > > >              subs[i] = NULL;
>  >> > > > -            modified = true;
>  >> > > >          }
>  >> > > >      }
>  >> > > >
>  >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
>  >> > > > -        || (!modified && expr->as_name)) {
>  >> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL || 
> has_addr_set) {
>  >> > > >          /* Not a fully maskable field or this expression is 
> tracking an
>  >> > > >           * address set.  Don't try to optimize to preserve 
> address set I-P. */
>  >> > > >          goto done;
>  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr, 
> const struct expr_symbol *symbol)
>  >> > > >              if (expr_bitmap_intersect_check(a_value, a_mask, 
> b_value, b_mask,
>  >> > > >                                              bit_width)
>  >> > > >                  && bitmap_is_superset(b_mask, a_mask, 
> bit_width)) {
>  >> > > > -                /* 'a' is the same expression with a smaller 
> mask. */
>  >> > > > +                /* 'a' is the same expression with a smaller 
> mask.
>  >> > > > +                 * Remove address set reference from the 
> duplicate. */
>  >> > > > +                a->as_name = NULL;
>  >> > > >                  expr_destroy(subs[j]);
>  >> > > >                  subs[j] = NULL;
>  >> > > > -                modified = true;
>  >> > > >
>  >> > > >                  /* Shorten the path for the next round. */
>  >> > > >                  if (last) {
>  >> > > > @@ -2685,12 +2676,6 @@ done:
>  >> > > >          }
>  >> > > >      }
>  >> > > >
>  >> > > > -    if (modified) {
>  >> > > > -        /* Members modified, so untrack address set. */
>  >> > > > -        free(expr->as_name);
>  >> > > > -        expr->as_name = NULL;
>  >> > > > -    }
>  >> > > > -
>  >> > > >      free(next);
>  >> > > >      free(subs);
>  >> > > >      return expr;
>  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
>  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
>  >> > > >          struct expr_match *match = expr_match_new(m, clause, 
> n_clauses,
>  >> > > >                                                    conj_id);
>  >> > > > -        if (or->as_name) {
>  >> > > > +        if (sub->as_name) {
>  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
>  >> > > >              ovs_assert(sub->cmp.symbol->width);
>  >> > > > -            match->as_name = xstrdup(or->as_name);
>  >> > > > +            match->as_name = xstrdup(sub->as_name);
>  >> > > >              match->as_ip = sub->cmp.value.ipv6;
>  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
>  >> > > >          }
>  >> > > > diff --git a/tests/ovn-controller.at 
> <http://ovn-controller.at> b/tests/ovn-controller.at 
> <http://ovn-controller.at>
>  >> > > > index be198e00d..27cec2aec 100644
>  >> > > > --- a/tests/ovn-controller.at <http://ovn-controller.at>
>  >> > > > +++ b/tests/ovn-controller.at <http://ovn-controller.at>
>  >> > > > @@ -1625,7 +1625,9 @@ 
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8 
> actions=lo
>  >> > > >  done
>  >> > > >
>  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>  >> > > > -AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [10
>  >> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
>  >> > > > +# Last 5 is due to overlap in IP addresses between as1 and as2.
>  >> > > > +AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [7
>  >> > > >  ])
>  >> > > >
>  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
>  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
>  >> > > >  done
>  >> > > >
>  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>  >> > > > -# In this case the as1 and as2 are merged to a single OR 
> expr, so we lose track of
>  >> > > > -# address set information - can't handle deletion incrementally.
>  >> > > > -AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [10
>  >> > > > +# First 5 are due to overlap in IP addresses between as1 and as2.
>  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
>  >> > > > +AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [7
>  >> > > >  ])
>  >> > > >
>  >> > > >  OVN_CLEANUP([hv1])
>  >> > > > @@ -1817,7 +1819,7 @@ 
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5 
> actions=co
>  >> > > > 
>   priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
>  >> > > >  ])
>  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>  >> > > > -AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [1
>  >> > > > +AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [0
>  >> > > >  ])
>  >> > > >
>  >> > > >  # Delete 2 IPs
>  >> > > > @@ -1837,7 +1839,7 @@ 
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3 
> actions=co
>  >> > > > 
>   priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
>  >> > > >  ])
>  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
>  >> > > > -AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [1
>  >> > > > +AT_CHECK([echo $(($reprocess_count_new - 
> $reprocess_count_old))], [0], [0
>  >> > > >  ])
>  >> > > >
>  >> > > >  OVN_CLEANUP([hv1])
>  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
>  >> > > >
>  >> > > >  AT_CLEANUP
>  >> > > >  ])
>  >> > > > +
>  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
>  >> > > > +AT_KEYWORDS([as-i-p])
>  >> > > > +
>  >> > > > +ovn_start
>  >> > > > +
>  >> > > > +net_add n1
>  >> > > > +sim_add hv1
>  >> > > > +as hv1
>  >> > > > +check ovs-vsctl add-br br-phys
>  >> > > > +ovn_attach n1 br-phys 192.168.0.1
>  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
>  >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
>  >> > > > +
>  >> > > > +check ovn-nbctl ls-add ls1
>  >> > > > +
>  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
>  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
>  >> > > > +
>  >> > > > +wait_for_ports_up
>  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
>  >> > > > +
>  >> > > > +# Get the OF table numbers
>  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
>  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
>  >> > > > +
>  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key 
> external_ids:name=ls1))
>  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key 
> logical_port=ls1-lp1))
>  >> > > > +
>  >> > > > +ovn-nbctl create address_set name=as1
>  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport == 
> "ls1-lp1" && ip4.src == $as1' drop
>  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24 
> <http://10.0.0.0/24>
>  >> > > > +check ovn-nbctl --wait=hv sync
>  >> > > > +
>  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int 
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' 
> | sort], [0], [dnl
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 
> <http://10.0.0.0/24> 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +])
>  >> > > > +
>  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
>  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
>  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
>  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
>  >> > > > +check ovn-nbctl --wait=hv sync
>  >> > > > +
>  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int 
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' 
> | sort], [0], [dnl
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 
> <http://10.0.0.0/24> 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +])
>  >> > > > +
>  >> > > > +
>  >> > > > +check ovn-appctl inc-engine/recompute
>  >> > > > +
>  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int 
> table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' 
> | sort], [0], [dnl
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 
> <http://10.0.0.0/24> 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 
> actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>  >> > > > +])
>  >> > > > +
>  >> > > > +OVN_CLEANUP([hv1])
>  >> > > > +AT_CLEANUP
>  >> > > > --
>  >> > > > 2.44.0
>  >> > > >
>  >> >
>  >> >
>  >> > Thank you Han,
>  >> >
>  >> > I would like to start the discussion about the possibility of
>  >> > backporting this change. The reason for the backport being that this
>  >> > change might be beneficial to ovn-kubernetes users, as this was
>  >> > actually observed in ovn-kubernetes with network policies. I know that
>  >> > strictly speaking this is not a bug fix, because this limitation was
>  >> > well documented, however I would like to hear the opinion of others.
>  >>
>  >>
>  >> I have no objections if this improvement is strongly required for 
> released versions, even though it is not a bug. May I ask for which 
> releases do you want this to be backported?
>  >
>  >
>  > We have a request for 23.09 and 24.03.
>  >
> Ok. I will wait for input from other maintainers. It will be a tradeoff 
> between risk and performance gains. Mark, Numan and Dumitru, what do you 
> think?
> 
> Thanks,
> Han

I'm OK with the patch being backported. I think our test coverage is 
sufficient to uncover problems that may arise from the backport, so the 
risk should be minimal.

> 
>  >>
>  >>
>  >> Thanks,
>  >> Han
>  >>
>  >> >
>  >> > Thanks,
>  >> > Ales
>  >> > --
>  >> >
>  >> > Ales Musil
>  >> >
>  >> > Senior Software Engineer - OVN Core
>  >> >
>  >> > Red Hat EMEA
>  >> >
>  >> > amusil@redhat.com <mailto:amusil@redhat.com>
>  >> >
>  >
>  >
>  >
>  > --
>  >
>  > Ales Musil
>  >
>  > Senior Software Engineer - OVN Core
>  >
>  > Red Hat EMEA
>  >
>  > amusil@redhat.com <mailto:amusil@redhat.com>
Han Zhou May 10, 2024, 6:37 a.m. UTC | #8
On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com> wrote:
>
> On 5/7/24 02:12, Han Zhou wrote:
> >
> >
> > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
> > <mailto:amusil@redhat.com>> wrote:
> >  >
> >  >
> >  >
> >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
> > <mailto:hzhou@ovn.org>> wrote:
> >  >>
> >  >>
> >  >>
> >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com
> > <mailto:amusil@redhat.com>> wrote:
> >  >> >
> >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
> > <mailto:hzhou@ovn.org>> wrote:
> >  >> > >
> >  >> > >
> >  >> > >
> >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com
> > <mailto:amusil@redhat.com>> wrote:
> >  >> > > >
> >  >> > > > Instead of tracking address set per struct expr_constant_set
> > track it
> >  >> > > > per individual struct expr_constant. This allows more fine
grained
> >  >> > > > control for I-P processing of address sets in controller. It
> > helps with
> >  >> > > > scenarios like matching on two address sets in one expression
e.g.
> >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
removal of
> >  >> > > > individual adress from the set to be incrementally processed
> > instead
> >  >> > > > of reprocessing all the flows.
> >  >> > > >
> >  >> > > > This unfortunately doesn't help with the following flows:
> >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> >  >> > > >
> >  >> > > > The memory impact should be minimal as there is only increase
> > of 8 bytes
> >  >> > > > per the struct expr_constant.
> >  >> > > >
> >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > <https://issues.redhat.com/browse/FDP-509>
> >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
> > <mailto:amusil@redhat.com>>
> >  >> > > > ---
> >  >> > > > v4: Rebase on top of current main.
> >  >> > > >     Update the "lflow_handle_addr_set_update" comment
> > according to suggestion from Han.
> >  >> > >
> >  >> > > Thanks Ales. I updated the commit message for the same, and
> > applied to main branch.
> >  >> > >
> >  >> > > Regards,
> >  >> > > Han
> >  >> > >
> >  >> > > > v3: Rebase on top of current main.
> >  >> > > >     Address comments from Han:
> >  >> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to
> > include remaning corner cases.
> >  >> > > >     - Make sure that the flows are consistent between I-P and
> > recompute.
> >  >> > > > v2: Rebase on top of current main.
> >  >> > > >     Adjust the comment for I-P optimization.
> >  >> > > > ---
> >  >> > > >  controller/lflow.c      | 11 ++---
> >  >> > > >  include/ovn/actions.h   |  2 +-
> >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> >  >> > > >  lib/actions.c           | 20 ++++-----
> >  >> > > >  lib/expr.c              | 99
> > +++++++++++++++++------------------------
> >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> | 79
> > +++++++++++++++++++++++++++++---
> >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> >  >> > > >
> >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> >  >> > > > index 760ec0b41..1e05665a1 100644
> >  >> > > > --- a/controller/lflow.c
> >  >> > > > +++ b/controller/lflow.c
> >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
> > lflow_ctx_in *l_ctx_in,
> >  >> > > >  }
> >  >> > > >
> >  >> > > >  static bool
> >  >> > > > -as_info_from_expr_const(const char *as_name, const union
> > expr_constant *c,
> >  >> > > > +as_info_from_expr_const(const char *as_name, const struct
> > expr_constant *c,
> >  >> > > >                          struct addrset_info *as_info)
> >  >> > > >  {
> >  >> > > >      as_info->name = as_name;
> >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
> > *as_name, struct addr_set_diff *as_diff,
> >  >> > > >   *        generated.
> >  >> > > >   *
> >  >> > > >   *      - The sub expression of the address set is combined
> > with other sub-
> >  >> > > > - *        expressions/constants, usually because of
> > disjunctions between
> >  >> > > > - *        sub-expressions/constants, e.g.:
> >  >> > > > + *        expressions/constants on different fields, e.g.:
> >  >> > > >   *
> >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
> >  >> > > > - *          ip.src == {$as1, $as2}
> >  >> > > > - *          ip.src == {$as1, ip1}
> >  >> > > >   *
> >  >> > > > - *        All these could have been split into separate
lflows.
> >  >> > > > + *        This could have been split into separate lflows.
> >  >> > > >   *
> >  >> > > >   *      - Conjunctions overlapping between lflows, which can
> > be caused by
> >  >> > > >   *        overlapping address sets or same address set used
> > by multiple lflows
> >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char
> > *as_name,
> >  >> > > >          if (as_diff->deleted) {
> >  >> > > >              struct addrset_info as_info;
> >  >> > > >              for (size_t i = 0; i <
> > as_diff->deleted->n_values; i++) {
> >  >> > > > -                union expr_constant *c =
> > &as_diff->deleted->values[i];
> >  >> > > > +                struct expr_constant *c =
> > &as_diff->deleted->values[i];
> >  >> > > >                  if (!as_info_from_expr_const(as_name, c,
> > &as_info)) {
> >  >> > > >                      continue;
> >  >> > > >                  }
> >  >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> >  >> > > > index ae0864fdd..88cf4de79 100644
> >  >> > > > --- a/include/ovn/actions.h
> >  >> > > > +++ b/include/ovn/actions.h
> >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> >  >> > > >  struct ovnact_load {
> >  >> > > >      struct ovnact ovnact;
> >  >> > > >      struct expr_field dst;
> >  >> > > > -    union expr_constant imm;
> >  >> > > > +    struct expr_constant imm;
> >  >> > > >  };
> >  >> > > >
> >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> >  >> > > > index c48f82398..e54edb5bf 100644
> >  >> > > > --- a/include/ovn/expr.h
> >  >> > > > +++ b/include/ovn/expr.h
> >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type
> > type, enum expr_relop *relop);
> >  >> > > >  struct expr {
> >  >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or
> > EXPR_T_OR if any. */
> >  >> > > >      enum expr_type type;        /* Expression type. */
> >  >> > > > -    char *as_name;              /* Address set name. Null if
> > it is not an
> >  >> > > > +    const char *as_name;        /* Address set name. Null if
> > it is not an
> >  >> > > >                                     address set. */
> >  >> > > >
> >  >> > > >      union {
> >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> >  >> > > >  };
> >  >> > > >
> >  >> > > >  /* A string or integer constant (one must know which from
> > context). */
> >  >> > > > -union expr_constant {
> >  >> > > > -    /* Integer constant.
> >  >> > > > -     *
> >  >> > > > -     * The width of a constant isn't always clear, e.g. if
> > you write "1",
> >  >> > > > -     * there's no way to tell whether you mean for that to be
> > a 1-bit constant
> >  >> > > > -     * or a 128-bit constant or somewhere in between. */
> >  >> > > > -    struct {
> >  >> > > > -        union mf_subvalue value;
> >  >> > > > -        union mf_subvalue mask; /* Only initialized if
> > 'masked'. */
> >  >> > > > -        bool masked;
> >  >> > > > -
> >  >> > > > -        enum lex_format format; /* From the constant's
> > lex_token. */
> >  >> > > > -    };
> >  >> > > > +struct expr_constant {
> >  >> > > > +    const char *as_name;
> >  >> > > >
> >  >> > > > -    /* Null-terminated string constant. */
> >  >> > > > -    char *string;
> >  >> > > > +    union {
> >  >> > > > +        /* Integer constant.
> >  >> > > > +         *
> >  >> > > > +         * The width of a constant isn't always clear, e.g.
> > if you write "1",
> >  >> > > > +         * there's no way to tell whether you mean for that
> > to be a 1-bit
> >  >> > > > +         * constant or a 128-bit constant or somewhere in
> > between. */
> >  >> > > > +        struct {
> >  >> > > > +            union mf_subvalue value;
> >  >> > > > +            union mf_subvalue mask; /* Only initialized if
> > 'masked'. */
> >  >> > > > +            bool masked;
> >  >> > > > +
> >  >> > > > +            enum lex_format format; /* From the constant's
> > lex_token. */
> >  >> > > > +        };
> >  >> > > > +
> >  >> > > > +        /* Null-terminated string constant. */
> >  >> > > > +        char *string;
> >  >> > > > +    };
> >  >> > > >  };
> >  >> > > >
> >  >> > > >  bool expr_constant_parse(struct lexer *,
> >  >> > > >                           const struct expr_field *,
> >  >> > > > -                         union expr_constant *);
> >  >> > > > -void expr_constant_format(const union expr_constant *,
> >  >> > > > +                         struct expr_constant *);
> >  >> > > > +void expr_constant_format(const struct expr_constant *,
> >  >> > > >                            enum expr_constant_type, struct ds
*);
> >  >> > > > -void expr_constant_destroy(const union expr_constant *,
> >  >> > > > +void expr_constant_destroy(const struct expr_constant *,
> >  >> > > >                             enum expr_constant_type);
> >  >> > > >
> >  >> > > >  /* A collection of "union expr_constant"s of the same type.
*/
> >  >> > > >  struct expr_constant_set {
> >  >> > > > -    union expr_constant *values;  /* Constants. */
> >  >> > > > +    struct expr_constant *values; /* Constants. */
> >  >> > > >      size_t n_values;              /* Number of constants. */
> >  >> > > >      enum expr_constant_type type; /* Type of the constants.
*/
> >  >> > > >      bool in_curlies;              /* Whether the constants
> > were in {}. */
> >  >> > > > -    char *as_name;                /* Name of an address set.
> > It is NULL if not
> >  >> > > > -                                     an address set. */
> >  >> > > >  };
> >  >> > > >
> >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> > expr_constant_set *);
> >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
> >  >> > > > index 94751d059..cff4f9e3c 100644
> >  >> > > > --- a/lib/actions.c
> >  >> > > > +++ b/lib/actions.c
> >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load
*load,
> >  >> > > >              const struct ovnact_encode_params *ep,
> >  >> > > >              struct ofpbuf *ofpacts)
> >  >> > > >  {
> >  >> > > > -    const union expr_constant *c = &load->imm;
> >  >> > > > +    const struct expr_constant *c = &load->imm;
> >  >> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> >  >> > > >      struct ofpact_set_field *sf =
> > ofpact_put_set_field(ofpacts, dst.field,
> >  >> > > >                                                         NULL,
> > NULL);
> >  >> > > > @@ -2077,7 +2077,7 @@
> > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> >  >> > > >          /* All empty_lb_backends fields are of type 'str' */
> >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> >  >> > > >
> >  >> > > > -        const union expr_constant *c = o->value.values;
> >  >> > > > +        const struct expr_constant *c = o->value.values;
> >  >> > > >          size_t size = strlen(c->string);
> >  >> > > >          struct controller_event_opt_header hdr =
> >  >> > > >              (struct controller_event_opt_header) {
> >  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> > action_context *ctx,
> >  >> > > >  {
> >  >> > > >      for (size_t i = 0; i < n_options; i++) {
> >  >> > > >          const struct ovnact_gen_option *o = &options[i];
> >  >> > > > -        const union expr_constant *c = o->value.values;
> >  >> > > > +        const struct expr_constant *c = o->value.values;
> >  >> > > >          struct sockaddr_storage ss;
> >  >> > > >          struct uuid uuid;
> >  >> > > >
> >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
> > ovnact_gen_option *o,
> >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >  >> > > >      opt_header[0] = o->option->code;
> >  >> > > >
> >  >> > > > -    const union expr_constant *c = o->value.values;
> >  >> > > > +    const struct expr_constant *c = o->value.values;
> >  >> > > >      size_t n_values = o->value.n_values;
> >  >> > > >      if (!strcmp(o->option->type, "bool") ||
> >  >> > > >          !strcmp(o->option->type, "uint8")) {
> >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
> > ovnact_gen_option *o,
> >  >> > > >                           struct ofpbuf *ofpacts)
> >  >> > > >  {
> >  >> > > >      struct dhcpv6_opt_header *opt =
> > ofpbuf_put_uninit(ofpacts, sizeof *opt);
> >  >> > > > -    const union expr_constant *c = o->value.values;
> >  >> > > > +    const struct expr_constant *c = o->value.values;
> >  >> > > >      size_t n_values = o->value.n_values;
> >  >> > > >      size_t size;
> >  >> > > >
> >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > ovnact_put_opts *pdo,
> >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > DHCP_OPT_BOOTFILE_CODE);
> >  >> > > >      if (boot_opt) {
> >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >  >> > > > -        const union expr_constant *c =
boot_opt->value.values;
> >  >> > > > +        const struct expr_constant *c =
boot_opt->value.values;
> >  >> > > >          opt_header[0] = boot_opt->option->code;
> >  >> > > >          opt_header[1] = strlen(c->string);
> >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > ovnact_put_opts *pdo,
> >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > DHCP_OPT_BOOTFILE_ALT_CODE);
> >  >> > > >      if (boot_alt_opt) {
> >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >  >> > > > -        const union expr_constant *c =
> > boot_alt_opt->value.values;
> >  >> > > > +        const struct expr_constant *c =
> > boot_alt_opt->value.values;
> >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
> >  >> > > >          opt_header[1] = strlen(c->string);
> >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > ovnact_put_opts *pdo,
> >  >> > > >          pdo->options, pdo->n_options,
DHCP_OPT_NEXT_SERVER_CODE);
> >  >> > > >      if (next_server_opt) {
> >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> >  >> > > > -        const union expr_constant *c =
> > next_server_opt->value.values;
> >  >> > > > +        const struct expr_constant *c =
> > next_server_opt->value.values;
> >  >> > > >          opt_header[0] = next_server_opt->option->code;
> >  >> > > >          opt_header[1] = sizeof(ovs_be32);
> >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
sizeof(ovs_be32));
> >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
> > action_context *ctx, const struct expr_field *dst,
> >  >> > > >      /* Let's validate the options. */
> >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> >  >> > > >          const struct ovnact_gen_option *o = &po->options[i];
> >  >> > > > -        const union expr_constant *c = o->value.values;
> >  >> > > > +        const struct expr_constant *c = o->value.values;
> >  >> > > >          if (o->value.n_values > 1) {
> >  >> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
> > option",
> >  >> > > >                          o->option->name);
> >  >> > > > @@ -3490,7 +3490,7 @@ static void
> >  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> >  >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
> > ra_offset)
> >  >> > > >  {
> >  >> > > > -    const union expr_constant *c = o->value.values;
> >  >> > > > +    const struct expr_constant *c = o->value.values;
> >  >> > > >
> >  >> > > >      switch (o->option->code) {
> >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
> >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
> >  >> > > > index 0cb44e1b6..ecf8a6338 100644
> >  >> > > > --- a/lib/expr.c
> >  >> > > > +++ b/lib/expr.c
> >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct
> > expr *a, struct expr *b)
> >  >> > > >          } else {
> >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
> >  >> > > >          }
> >  >> > > > -        free(a->as_name);
> >  >> > > >          a->as_name = NULL;
> >  >> > > >          return a;
> >  >> > > >      } else if (b->type == type) {
> >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
> >  >> > > > -        free(b->as_name);
> >  >> > > >          b->as_name = NULL;
> >  >> > > >          return b;
> >  >> > > >      } else {
> >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
> > expr_context *, struct expr_field *);
> >  >> > > >
> >  >> > > >  static struct expr *
> >  >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> >  >> > > > -             const union expr_constant *c)
> >  >> > > > +             const struct expr_constant *c)
> >  >> > > >  {
> >  >> > > >      struct expr *e = xzalloc(sizeof *e);
> >  >> > > >      e->type = EXPR_T_CMP;
> >  >> > > >      e->cmp.symbol = f->symbol;
> >  >> > > >      e->cmp.relop = r;
> >  >> > > > +    e->as_name = c->as_name;
> >  >> > > >      if (f->symbol->width) {
> >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> >  >> > > >                       &e->cmp.value, sizeof e->cmp.value,
f->ofs,
> >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f,
> > enum expr_relop r,
> >  >> > > >
> >  >> > > >  /* Returns the minimum reasonable width for integer constant
> > 'c'. */
> >  >> > > >  static int
> >  >> > > > -expr_constant_width(const union expr_constant *c)
> >  >> > > > +expr_constant_width(const struct expr_constant *c)
> >  >> > > >  {
> >  >> > > >      if (c->masked) {
> >  >> > > >          return mf_subvalue_width(&c->mask);
> >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
EXPR_T_AND,
> >  >> > > >                           e, make_cmp__(f, r,
&cs->values[i]));
> >  >> > > >      }
> >  >> > > > -    /* Track address set */
> >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
cs->as_name) {
> >  >> > > > -        e->as_name = xstrdup(cs->as_name);
> >  >> > > > -    }
> >  >> > > > +
> >  >> > > >  exit:
> >  >> > > >      expr_constant_set_destroy(cs);
> >  >> > > >      return e;
> >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context
> > *ctx, struct expr_constant_set *cs,
> >  >> > > >          }
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    struct expr_constant_set *addr_sets
> >  >> > > > -        = (ctx->addr_sets
> >  >> > > > -           ? shash_find_data(ctx->addr_sets,
ctx->lexer->token.s)
> >  >> > > > -           : NULL);
> >  >> > > > -    if (!addr_sets) {
> >  >> > > > +    struct shash_node *node = ctx->addr_sets
> >  >> > > > +                              ? shash_find(ctx->addr_sets,
> > ctx->lexer->token.s)
> >  >> > > > +                              : NULL;
> >  >> > > > +    if (!node) {
> >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting address set
> > name");
> >  >> > > >          return false;
> >  >> > > >      }
> >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context
> > *ctx, struct expr_constant_set *cs,
> >  >> > > >          return false;
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    if (!cs->n_values) {
> >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> >  >> > > > -    }
> >  >> > > > -
> >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
> >  >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> >  >> > > >      if (n_values >= *allocated_values) {
> >  >> > > >          cs->values = xrealloc(cs->values, n_values * sizeof
> > *cs->values);
> >  >> > > >          *allocated_values = n_values;
> >  >> > > >      }
> >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> >  >> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
> >  >> > > > +        struct expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > > +        *c = addr_sets->values[i];
> >  >> > > > +        c->as_name = node->name;
> >  >> > > >      }
> >  >> > > >
> >  >> > > >      return true;
> >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx,
> > struct expr_constant_set *cs,
> >  >> > > >          *allocated_values = n_values;
> >  >> > > >      }
> >  >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> >  >> > > > -        cs->values[cs->n_values++].string =
> >  >> > > > -            xstrdup(port_group->values[i].string);
> >  >> > > > +        struct expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > > +        c->string = xstrdup(port_group->values[i].string);
> >  >> > > > +        c->as_name = NULL;
> >  >> > > >      }
> >  >> > > >
> >  >> > > >      return true;
> >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx,
> > struct expr_constant_set *cs,
> >  >> > > >                                  sizeof *cs->values);
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    /* Combining other values to the constant set that is
> > tracking an
> >  >> > > > -     * address set, so untrack it. */
> >  >> > > > -    free(cs->as_name);
> >  >> > > > -    cs->as_name = NULL;
> >  >> > > > -
> >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> >  >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> >  >> > > >          return false;
> >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx,
> > struct expr_constant_set *cs,
> >  >> > > >          if (!assign_constant_set_type(ctx, cs,
EXPR_C_STRING)) {
> >  >> > > >              return false;
> >  >> > > >          }
> >  >> > > > -        cs->values[cs->n_values++].string =
> > xstrdup(ctx->lexer->token.s);
> >  >> > > > +        struct expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> >  >> > > > +        c->as_name = NULL;
> >  >> > > >          lexer_get(ctx->lexer);
> >  >> > > >          return true;
> >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx,
> > struct expr_constant_set *cs,
> >  >> > > >              return false;
> >  >> > > >          }
> >  >> > > >
> >  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> >  >> > > > +        struct expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > >          c->value = ctx->lexer->token.value;
> >  >> > > >          c->format = ctx->lexer->token.format;
> >  >> > > >          c->masked = ctx->lexer->token.type ==
> > LEX_T_MASKED_INTEGER;
> >  >> > > >          if (c->masked) {
> >  >> > > >              c->mask = ctx->lexer->token.mask;
> >  >> > > >          }
> >  >> > > > +        c->as_name = NULL;
> >  >> > > >          lexer_get(ctx->lexer);
> >  >> > > >          return true;
> >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context
> > *ctx, struct expr_constant_set *cs)
> >  >> > > >   * indeterminate. */
> >  >> > > >  bool
> >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
> > expr_field *f,
> >  >> > > > -                    union expr_constant *c)
> >  >> > > > +                    struct expr_constant *c)
> >  >> > > >  {
> >  >> > > >      if (lexer->error) {
> >  >> > > >          return false;
> >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer,
> > const struct expr_field *f,
> >  >> > > >  /* Appends to 's' a re-parseable representation of constant
> > 'c' with the given
> >  >> > > >   * 'type'. */
> >  >> > > >  void
> >  >> > > > -expr_constant_format(const union expr_constant *c,
> >  >> > > > +expr_constant_format(const struct expr_constant *c,
> >  >> > > >                       enum expr_constant_type type, struct ds
*s)
> >  >> > > >  {
> >  >> > > >      if (type == EXPR_C_STRING) {
> >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> > expr_constant *c,
> >  >> > > >   *
> >  >> > > >   * Does not free(c). */
> >  >> > > >  void
> >  >> > > > -expr_constant_destroy(const union expr_constant *c,
> >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
> >  >> > > >                        enum expr_constant_type type)
> >  >> > > >  {
> >  >> > > >      if (c && type == EXPR_C_STRING) {
> >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
> > expr_constant_set *cs, struct ds *s)
> >  >> > > >          ds_put_char(s, '{');
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    for (const union expr_constant *c = cs->values;
> >  >> > > > +    for (const struct expr_constant *c = cs->values;
> >  >> > > >           c < &cs->values[cs->n_values]; c++) {
> >  >> > > >          if (c != cs->values) {
> >  >> > > >              ds_put_cstr(s, ", ");
> >  >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> > expr_constant_set *cs)
> >  >> > > >              }
> >  >> > > >          }
> >  >> > > >          free(cs->values);
> >  >> > > > -        free(cs->as_name);
> >  >> > > >      }
> >  >> > > >  }
> >  >> > > >
> >  >> > > >  static int
> >  >> > > >  compare_expr_constant_integer_cb(const void *a_, const void
*b_)
> >  >> > > >  {
> >  >> > > > -    const union expr_constant *a = a_;
> >  >> > > > -    const union expr_constant *b = b_;
> >  >> > > > +    const struct expr_constant *a = a_;
> >  >> > > > +    const struct expr_constant *b = b_;
> >  >> > > >
> >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> >  >> > > >      if (d) {
> >  >> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const
> > char *const *values, size_t n_values)
> >  >> > > >              VLOG_WARN("Invalid constant set entry: '%s',
> > token type: %d",
> >  >> > > >                        values[i], lex.token.type);
> >  >> > > >          } else {
> >  >> > > > -            union expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > > +            struct expr_constant *c =
> > &cs->values[cs->n_values++];
> >  >> > > >              c->value = lex.token.value;
> >  >> > > >              c->format = lex.token.format;
> >  >> > > >              c->masked = lex.token.type ==
LEX_T_MASKED_INTEGER;
> >  >> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const
> > char *const *values, size_t n_values)
> >  >> > > >
> >  >> > > >  static void
> >  >> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> >  >> > > > -                            union expr_constant *c, size_t
> > *allocated)
> >  >> > > > +                            struct expr_constant *c, size_t
> > *allocated)
> >  >> > > >  {
> >  >> > > >      struct expr_constant_set *cs = *p_cs;
> >  >> > > >      if (!cs) {
> >  >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
> > *const_sets, const char *name,
> >  >> > > >                          values[i], name);
> >  >> > > >              continue;
> >  >> > > >          }
> >  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> >  >> > > > +        struct expr_constant *c =
&cs->values[cs->n_values++];
> >  >> > > >          c->string = xstrdup(values[i]);
> >  >> > > >      }
> >  >> > > >
> >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context
> > *ctx, bool *atomic)
> >  >> > > >
> >  >> > > >              *atomic = true;
> >  >> > > >
> >  >> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
> >  >> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
> >  >> > > >              cst->masked = false;
> >  >> > > >
> >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context
> > *ctx, bool *atomic)
> >  >> > > >              c.values = cst;
> >  >> > > >              c.n_values = 1;
> >  >> > > >              c.in_curlies = false;
> >  >> > > > -            c.as_name = NULL;
> >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> >  >> > > >          } else if (parse_relop(ctx, &r) &&
> > parse_constant_set(ctx, &c)) {
> >  >> > > >              return make_cmp(ctx, &f, r, &c);
> >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
*symtab)
> >  >> > > >  static struct expr *
> >  >> > > >  expr_clone_cmp(struct expr *expr)
> >  >> > > >  {
> >  >> > > > -    ovs_assert(!expr->as_name);
> >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> >  >> > > >      if (!new->cmp.symbol->width) {
> >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> >  >> > > >  static struct expr *
> >  >> > > >  expr_clone_condition(struct expr *expr)
> >  >> > > >  {
> >  >> > > > -    ovs_assert(!expr->as_name);
> >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> >  >> > > >      new->cond.string = xstrdup(new->cond.string);
> >  >> > > >      return new;
> >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> >  >> > > >          return;
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    free(expr->as_name);
> >  >> > > > -
> >  >> > > >      struct expr *sub;
> >  >> > > >
> >  >> > > >      switch (expr->type) {
> >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr,
> > const struct expr_symbol *symbol)
> >  >> > > >       * mask sizes. */
> >  >> > > >      size_t n = ovs_list_size(&expr->andor);
> >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> >  >> > > > -    bool modified = false;
> >  >> > > > +    bool has_addr_set = false;
> >  >> > > >      /* Linked list over the 'subs' array to quickly skip
> > deleted elements,
> >  >> > > >       * i.e. the index of the next potentially non-NULL
> > element. */
> >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
> >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr,
> > const struct expr_symbol *symbol)
> >  >> > > >      size_t i = 0, j, max_n_bits = 0;
> >  >> > > >      struct expr *sub;
> >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> >  >> > > > +        if (sub->as_name) {
> >  >> > > > +            has_addr_set = true;
> >  >> > > > +        }
> >  >> > > >          if (symbol->width) {
> >  >> > > >              const unsigned long *sub_mask;
> >  >> > > >
> >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr,
> > const struct expr_symbol *symbol)
> >  >> > > >              next[last] = i;
> >  >> > > >              last = i;
> >  >> > > >          } else {
> >  >> > > > +            /* Remove address set reference from the
> > duplicate. */
> >  >> > > > +            subs[last]->as_name = NULL;
> >  >> > > >              expr_destroy(subs[i]);
> >  >> > > >              subs[i] = NULL;
> >  >> > > > -            modified = true;
> >  >> > > >          }
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> >  >> > > > -        || (!modified && expr->as_name)) {
> >  >> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
> > has_addr_set) {
> >  >> > > >          /* Not a fully maskable field or this expression is
> > tracking an
> >  >> > > >           * address set.  Don't try to optimize to preserve
> > address set I-P. */
> >  >> > > >          goto done;
> >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr,
> > const struct expr_symbol *symbol)
> >  >> > > >              if (expr_bitmap_intersect_check(a_value, a_mask,
> > b_value, b_mask,
> >  >> > > >                                              bit_width)
> >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
> > bit_width)) {
> >  >> > > > -                /* 'a' is the same expression with a smaller
> > mask. */
> >  >> > > > +                /* 'a' is the same expression with a smaller
> > mask.
> >  >> > > > +                 * Remove address set reference from the
> > duplicate. */
> >  >> > > > +                a->as_name = NULL;
> >  >> > > >                  expr_destroy(subs[j]);
> >  >> > > >                  subs[j] = NULL;
> >  >> > > > -                modified = true;
> >  >> > > >
> >  >> > > >                  /* Shorten the path for the next round. */
> >  >> > > >                  if (last) {
> >  >> > > > @@ -2685,12 +2676,6 @@ done:
> >  >> > > >          }
> >  >> > > >      }
> >  >> > > >
> >  >> > > > -    if (modified) {
> >  >> > > > -        /* Members modified, so untrack address set. */
> >  >> > > > -        free(expr->as_name);
> >  >> > > > -        expr->as_name = NULL;
> >  >> > > > -    }
> >  >> > > > -
> >  >> > > >      free(next);
> >  >> > > >      free(subs);
> >  >> > > >      return expr;
> >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> >  >> > > >          struct expr_match *match = expr_match_new(m, clause,
> > n_clauses,
> >  >> > > >                                                    conj_id);
> >  >> > > > -        if (or->as_name) {
> >  >> > > > +        if (sub->as_name) {
> >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> >  >> > > >              ovs_assert(sub->cmp.symbol->width);
> >  >> > > > -            match->as_name = xstrdup(or->as_name);
> >  >> > > > +            match->as_name = xstrdup(sub->as_name);
> >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
> >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> >  >> > > >          }
> >  >> > > > diff --git a/tests/ovn-controller.at
> > <http://ovn-controller.at> b/tests/ovn-controller.at
> > <http://ovn-controller.at>
> >  >> > > > index be198e00d..27cec2aec 100644
> >  >> > > > --- a/tests/ovn-controller.at <http://ovn-controller.at>
> >  >> > > > +++ b/tests/ovn-controller.at <http://ovn-controller.at>
> >  >> > > > @@ -1625,7 +1625,9 @@
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> > actions=lo
> >  >> > > >  done
> >  >> > > >
> >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [10
> >  >> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> >  >> > > > +# Last 5 is due to overlap in IP addresses between as1 and
as2.
> >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [7
> >  >> > > >  ])
> >  >> > > >
> >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> >  >> > > >  done
> >  >> > > >
> >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >  >> > > > -# In this case the as1 and as2 are merged to a single OR
> > expr, so we lose track of
> >  >> > > > -# address set information - can't handle deletion
incrementally.
> >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [10
> >  >> > > > +# First 5 are due to overlap in IP addresses between as1 and
as2.
> >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [7
> >  >> > > >  ])
> >  >> > > >
> >  >> > > >  OVN_CLEANUP([hv1])
> >  >> > > > @@ -1817,7 +1819,7 @@
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> > actions=co
> >  >> > > >
> >
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
> >  >> > > >  ])
> >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [1
> >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [0
> >  >> > > >  ])
> >  >> > > >
> >  >> > > >  # Delete 2 IPs
> >  >> > > > @@ -1837,7 +1839,7 @@
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> > actions=co
> >  >> > > >
> >
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
actions=conjunction,2/2)
> >  >> > > >  ])
> >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [1
> >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > $reprocess_count_old))], [0], [0
> >  >> > > >  ])
> >  >> > > >
> >  >> > > >  OVN_CLEANUP([hv1])
> >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> >  >> > > >
> >  >> > > >  AT_CLEANUP
> >  >> > > >  ])
> >  >> > > > +
> >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> >  >> > > > +AT_KEYWORDS([as-i-p])
> >  >> > > > +
> >  >> > > > +ovn_start
> >  >> > > > +
> >  >> > > > +net_add n1
> >  >> > > > +sim_add hv1
> >  >> > > > +as hv1
> >  >> > > > +check ovs-vsctl add-br br-phys
> >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
> >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> >  >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> >  >> > > > +
> >  >> > > > +check ovn-nbctl ls-add ls1
> >  >> > > > +
> >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> >  >> > > > +
> >  >> > > > +wait_for_ports_up
> >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> >  >> > > > +
> >  >> > > > +# Get the OF table numbers
> >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
ls_out_acl_action)
> >  >> > > > +
> >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> > external_ids:name=ls1))
> >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
> > logical_port=ls1-lp1))
> >  >> > > > +
> >  >> > > > +ovn-nbctl create address_set name=as1
> >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
> > "ls1-lp1" && ip4.src == $as1' drop
> >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > <http://10.0.0.0/24>
> >  >> > > > +check ovn-nbctl --wait=hv sync
> >  >> > > > +
> >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > | sort], [0], [dnl
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > <http://10.0.0.0/24>
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +])
> >  >> > > > +
> >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> >  >> > > > +check ovn-nbctl --wait=hv sync
> >  >> > > > +
> >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > | sort], [0], [dnl
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > <http://10.0.0.0/24>
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +])
> >  >> > > > +
> >  >> > > > +
> >  >> > > > +check ovn-appctl inc-engine/recompute
> >  >> > > > +
> >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > | sort], [0], [dnl
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > <http://10.0.0.0/24>
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> >  >> > > > +])
> >  >> > > > +
> >  >> > > > +OVN_CLEANUP([hv1])
> >  >> > > > +AT_CLEANUP
> >  >> > > > --
> >  >> > > > 2.44.0
> >  >> > > >
> >  >> >
> >  >> >
> >  >> > Thank you Han,
> >  >> >
> >  >> > I would like to start the discussion about the possibility of
> >  >> > backporting this change. The reason for the backport being that
this
> >  >> > change might be beneficial to ovn-kubernetes users, as this was
> >  >> > actually observed in ovn-kubernetes with network policies. I know
that
> >  >> > strictly speaking this is not a bug fix, because this limitation
was
> >  >> > well documented, however I would like to hear the opinion of
others.
> >  >>
> >  >>
> >  >> I have no objections if this improvement is strongly required for
> > released versions, even though it is not a bug. May I ask for which
> > releases do you want this to be backported?
> >  >
> >  >
> >  > We have a request for 23.09 and 24.03.
> >  >
> > Ok. I will wait for input from other maintainers. It will be a tradeoff
> > between risk and performance gains. Mark, Numan and Dumitru, what do you
> > think?
> >
> > Thanks,
> > Han
>
> I'm OK with the patch being backported. I think our test coverage is
> sufficient to uncover problems that may arise from the backport, so the
> risk should be minimal.
>

Thanks Numan and Mark for confirming, so we got a quorum. I backported the
patch to 24.03 and 23.09.

Han

> >
> >  >>
> >  >>
> >  >> Thanks,
> >  >> Han
> >  >>
> >  >> >
> >  >> > Thanks,
> >  >> > Ales
> >  >> > --
> >  >> >
> >  >> > Ales Musil
> >  >> >
> >  >> > Senior Software Engineer - OVN Core
> >  >> >
> >  >> > Red Hat EMEA
> >  >> >
> >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
> >  >> >
> >  >
> >  >
> >  >
> >  > --
> >  >
> >  > Ales Musil
> >  >
> >  > Senior Software Engineer - OVN Core
> >  >
> >  > Red Hat EMEA
> >  >
> >  > amusil@redhat.com <mailto:amusil@redhat.com>
>
Numan Siddique May 10, 2024, 4:23 p.m. UTC | #9
On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
>
> On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com> wrote:
> >
> > On 5/7/24 02:12, Han Zhou wrote:
> > >
> > >
> > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
> > > <mailto:amusil@redhat.com>> wrote:
> > >  >
> > >  >
> > >  >
> > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
> > > <mailto:hzhou@ovn.org>> wrote:
> > >  >>
> > >  >>
> > >  >>
> > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com
> > > <mailto:amusil@redhat.com>> wrote:
> > >  >> >
> > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
> > > <mailto:hzhou@ovn.org>> wrote:
> > >  >> > >
> > >  >> > >
> > >  >> > >
> > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com
> > > <mailto:amusil@redhat.com>> wrote:
> > >  >> > > >
> > >  >> > > > Instead of tracking address set per struct expr_constant_set
> > > track it
> > >  >> > > > per individual struct expr_constant. This allows more fine
> grained
> > >  >> > > > control for I-P processing of address sets in controller. It
> > > helps with
> > >  >> > > > scenarios like matching on two address sets in one expression
> e.g.
> > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
> removal of
> > >  >> > > > individual adress from the set to be incrementally processed
> > > instead
> > >  >> > > > of reprocessing all the flows.
> > >  >> > > >
> > >  >> > > > This unfortunately doesn't help with the following flows:
> > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> > >  >> > > >
> > >  >> > > > The memory impact should be minimal as there is only increase
> > > of 8 bytes
> > >  >> > > > per the struct expr_constant.
> > >  >> > > >
> > >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > <https://issues.redhat.com/browse/FDP-509>
> > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
> > > <mailto:amusil@redhat.com>>
> > >  >> > > > ---
> > >  >> > > > v4: Rebase on top of current main.
> > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
> > > according to suggestion from Han.
> > >  >> > >
> > >  >> > > Thanks Ales. I updated the commit message for the same, and
> > > applied to main branch.
> > >  >> > >
> > >  >> > > Regards,
> > >  >> > > Han
> > >  >> > >
> > >  >> > > > v3: Rebase on top of current main.
> > >  >> > > >     Address comments from Han:
> > >  >> > > >     - Adjust the comment for "lflow_handle_addr_set_update" to
> > > include remaning corner cases.
> > >  >> > > >     - Make sure that the flows are consistent between I-P and
> > > recompute.
> > >  >> > > > v2: Rebase on top of current main.
> > >  >> > > >     Adjust the comment for I-P optimization.
> > >  >> > > > ---
> > >  >> > > >  controller/lflow.c      | 11 ++---
> > >  >> > > >  include/ovn/actions.h   |  2 +-
> > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> > >  >> > > >  lib/actions.c           | 20 ++++-----
> > >  >> > > >  lib/expr.c              | 99
> > > +++++++++++++++++------------------------
> > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> | 79
> > > +++++++++++++++++++++++++++++---
> > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > >  >> > > >
> > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> > >  >> > > > index 760ec0b41..1e05665a1 100644
> > >  >> > > > --- a/controller/lflow.c
> > >  >> > > > +++ b/controller/lflow.c
> > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
> > > lflow_ctx_in *l_ctx_in,
> > >  >> > > >  }
> > >  >> > > >
> > >  >> > > >  static bool
> > >  >> > > > -as_info_from_expr_const(const char *as_name, const union
> > > expr_constant *c,
> > >  >> > > > +as_info_from_expr_const(const char *as_name, const struct
> > > expr_constant *c,
> > >  >> > > >                          struct addrset_info *as_info)
> > >  >> > > >  {
> > >  >> > > >      as_info->name = as_name;
> > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
> > > *as_name, struct addr_set_diff *as_diff,
> > >  >> > > >   *        generated.
> > >  >> > > >   *
> > >  >> > > >   *      - The sub expression of the address set is combined
> > > with other sub-
> > >  >> > > > - *        expressions/constants, usually because of
> > > disjunctions between
> > >  >> > > > - *        sub-expressions/constants, e.g.:
> > >  >> > > > + *        expressions/constants on different fields, e.g.:
> > >  >> > > >   *
> > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
> > >  >> > > > - *          ip.src == {$as1, $as2}
> > >  >> > > > - *          ip.src == {$as1, ip1}
> > >  >> > > >   *
> > >  >> > > > - *        All these could have been split into separate
> lflows.
> > >  >> > > > + *        This could have been split into separate lflows.
> > >  >> > > >   *
> > >  >> > > >   *      - Conjunctions overlapping between lflows, which can
> > > be caused by
> > >  >> > > >   *        overlapping address sets or same address set used
> > > by multiple lflows
> > >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const char
> > > *as_name,
> > >  >> > > >          if (as_diff->deleted) {
> > >  >> > > >              struct addrset_info as_info;
> > >  >> > > >              for (size_t i = 0; i <
> > > as_diff->deleted->n_values; i++) {
> > >  >> > > > -                union expr_constant *c =
> > > &as_diff->deleted->values[i];
> > >  >> > > > +                struct expr_constant *c =
> > > &as_diff->deleted->values[i];
> > >  >> > > >                  if (!as_info_from_expr_const(as_name, c,
> > > &as_info)) {
> > >  >> > > >                      continue;
> > >  >> > > >                  }
> > >  >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > >  >> > > > index ae0864fdd..88cf4de79 100644
> > >  >> > > > --- a/include/ovn/actions.h
> > >  >> > > > +++ b/include/ovn/actions.h
> > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > >  >> > > >  struct ovnact_load {
> > >  >> > > >      struct ovnact ovnact;
> > >  >> > > >      struct expr_field dst;
> > >  >> > > > -    union expr_constant imm;
> > >  >> > > > +    struct expr_constant imm;
> > >  >> > > >  };
> > >  >> > > >
> > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > >  >> > > > index c48f82398..e54edb5bf 100644
> > >  >> > > > --- a/include/ovn/expr.h
> > >  >> > > > +++ b/include/ovn/expr.h
> > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum lex_type
> > > type, enum expr_relop *relop);
> > >  >> > > >  struct expr {
> > >  >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND or
> > > EXPR_T_OR if any. */
> > >  >> > > >      enum expr_type type;        /* Expression type. */
> > >  >> > > > -    char *as_name;              /* Address set name. Null if
> > > it is not an
> > >  >> > > > +    const char *as_name;        /* Address set name. Null if
> > > it is not an
> > >  >> > > >                                     address set. */
> > >  >> > > >
> > >  >> > > >      union {
> > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > >  >> > > >  };
> > >  >> > > >
> > >  >> > > >  /* A string or integer constant (one must know which from
> > > context). */
> > >  >> > > > -union expr_constant {
> > >  >> > > > -    /* Integer constant.
> > >  >> > > > -     *
> > >  >> > > > -     * The width of a constant isn't always clear, e.g. if
> > > you write "1",
> > >  >> > > > -     * there's no way to tell whether you mean for that to be
> > > a 1-bit constant
> > >  >> > > > -     * or a 128-bit constant or somewhere in between. */
> > >  >> > > > -    struct {
> > >  >> > > > -        union mf_subvalue value;
> > >  >> > > > -        union mf_subvalue mask; /* Only initialized if
> > > 'masked'. */
> > >  >> > > > -        bool masked;
> > >  >> > > > -
> > >  >> > > > -        enum lex_format format; /* From the constant's
> > > lex_token. */
> > >  >> > > > -    };
> > >  >> > > > +struct expr_constant {
> > >  >> > > > +    const char *as_name;
> > >  >> > > >
> > >  >> > > > -    /* Null-terminated string constant. */
> > >  >> > > > -    char *string;
> > >  >> > > > +    union {
> > >  >> > > > +        /* Integer constant.
> > >  >> > > > +         *
> > >  >> > > > +         * The width of a constant isn't always clear, e.g.
> > > if you write "1",
> > >  >> > > > +         * there's no way to tell whether you mean for that
> > > to be a 1-bit
> > >  >> > > > +         * constant or a 128-bit constant or somewhere in
> > > between. */
> > >  >> > > > +        struct {
> > >  >> > > > +            union mf_subvalue value;
> > >  >> > > > +            union mf_subvalue mask; /* Only initialized if
> > > 'masked'. */
> > >  >> > > > +            bool masked;
> > >  >> > > > +
> > >  >> > > > +            enum lex_format format; /* From the constant's
> > > lex_token. */
> > >  >> > > > +        };
> > >  >> > > > +
> > >  >> > > > +        /* Null-terminated string constant. */
> > >  >> > > > +        char *string;
> > >  >> > > > +    };
> > >  >> > > >  };
> > >  >> > > >
> > >  >> > > >  bool expr_constant_parse(struct lexer *,
> > >  >> > > >                           const struct expr_field *,
> > >  >> > > > -                         union expr_constant *);
> > >  >> > > > -void expr_constant_format(const union expr_constant *,
> > >  >> > > > +                         struct expr_constant *);
> > >  >> > > > +void expr_constant_format(const struct expr_constant *,
> > >  >> > > >                            enum expr_constant_type, struct ds
> *);
> > >  >> > > > -void expr_constant_destroy(const union expr_constant *,
> > >  >> > > > +void expr_constant_destroy(const struct expr_constant *,
> > >  >> > > >                             enum expr_constant_type);
> > >  >> > > >
> > >  >> > > >  /* A collection of "union expr_constant"s of the same type.
> */
> > >  >> > > >  struct expr_constant_set {
> > >  >> > > > -    union expr_constant *values;  /* Constants. */
> > >  >> > > > +    struct expr_constant *values; /* Constants. */
> > >  >> > > >      size_t n_values;              /* Number of constants. */
> > >  >> > > >      enum expr_constant_type type; /* Type of the constants.
> */
> > >  >> > > >      bool in_curlies;              /* Whether the constants
> > > were in {}. */
> > >  >> > > > -    char *as_name;                /* Name of an address set.
> > > It is NULL if not
> > >  >> > > > -                                     an address set. */
> > >  >> > > >  };
> > >  >> > > >
> > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> > > expr_constant_set *);
> > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
> > >  >> > > > index 94751d059..cff4f9e3c 100644
> > >  >> > > > --- a/lib/actions.c
> > >  >> > > > +++ b/lib/actions.c
> > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load
> *load,
> > >  >> > > >              const struct ovnact_encode_params *ep,
> > >  >> > > >              struct ofpbuf *ofpacts)
> > >  >> > > >  {
> > >  >> > > > -    const union expr_constant *c = &load->imm;
> > >  >> > > > +    const struct expr_constant *c = &load->imm;
> > >  >> > > >      struct mf_subfield dst = expr_resolve_field(&load->dst);
> > >  >> > > >      struct ofpact_set_field *sf =
> > > ofpact_put_set_field(ofpacts, dst.field,
> > >  >> > > >                                                         NULL,
> > > NULL);
> > >  >> > > > @@ -2077,7 +2077,7 @@
> > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> > >  >> > > >          /* All empty_lb_backends fields are of type 'str' */
> > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> > >  >> > > >
> > >  >> > > > -        const union expr_constant *c = o->value.values;
> > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > >  >> > > >          size_t size = strlen(c->string);
> > >  >> > > >          struct controller_event_opt_header hdr =
> > >  >> > > >              (struct controller_event_opt_header) {
> > >  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> > > action_context *ctx,
> > >  >> > > >  {
> > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
> > >  >> > > >          const struct ovnact_gen_option *o = &options[i];
> > >  >> > > > -        const union expr_constant *c = o->value.values;
> > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > >  >> > > >          struct sockaddr_storage ss;
> > >  >> > > >          struct uuid uuid;
> > >  >> > > >
> > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const struct
> > > ovnact_gen_option *o,
> > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > >  >> > > >      opt_header[0] = o->option->code;
> > >  >> > > >
> > >  >> > > > -    const union expr_constant *c = o->value.values;
> > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > >  >> > > >      size_t n_values = o->value.n_values;
> > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
> > >  >> > > >          !strcmp(o->option->type, "uint8")) {
> > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const struct
> > > ovnact_gen_option *o,
> > >  >> > > >                           struct ofpbuf *ofpacts)
> > >  >> > > >  {
> > >  >> > > >      struct dhcpv6_opt_header *opt =
> > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
> > >  >> > > > -    const union expr_constant *c = o->value.values;
> > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > >  >> > > >      size_t n_values = o->value.n_values;
> > >  >> > > >      size_t size;
> > >  >> > > >
> > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > ovnact_put_opts *pdo,
> > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > DHCP_OPT_BOOTFILE_CODE);
> > >  >> > > >      if (boot_opt) {
> > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > >  >> > > > -        const union expr_constant *c =
> boot_opt->value.values;
> > >  >> > > > +        const struct expr_constant *c =
> boot_opt->value.values;
> > >  >> > > >          opt_header[0] = boot_opt->option->code;
> > >  >> > > >          opt_header[1] = strlen(c->string);
> > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > ovnact_put_opts *pdo,
> > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > DHCP_OPT_BOOTFILE_ALT_CODE);
> > >  >> > > >      if (boot_alt_opt) {
> > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > >  >> > > > -        const union expr_constant *c =
> > > boot_alt_opt->value.values;
> > >  >> > > > +        const struct expr_constant *c =
> > > boot_alt_opt->value.values;
> > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
> > >  >> > > >          opt_header[1] = strlen(c->string);
> > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > ovnact_put_opts *pdo,
> > >  >> > > >          pdo->options, pdo->n_options,
> DHCP_OPT_NEXT_SERVER_CODE);
> > >  >> > > >      if (next_server_opt) {
> > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > >  >> > > > -        const union expr_constant *c =
> > > next_server_opt->value.values;
> > >  >> > > > +        const struct expr_constant *c =
> > > next_server_opt->value.values;
> > >  >> > > >          opt_header[0] = next_server_opt->option->code;
> > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
> > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
> sizeof(ovs_be32));
> > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
> > > action_context *ctx, const struct expr_field *dst,
> > >  >> > > >      /* Let's validate the options. */
> > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> > >  >> > > >          const struct ovnact_gen_option *o = &po->options[i];
> > >  >> > > > -        const union expr_constant *c = o->value.values;
> > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > >  >> > > >          if (o->value.n_values > 1) {
> > >  >> > > >              lexer_error(ctx->lexer, "Invalid value for \"%s\"
> > > option",
> > >  >> > > >                          o->option->name);
> > >  >> > > > @@ -3490,7 +3490,7 @@ static void
> > >  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option *o,
> > >  >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
> > > ra_offset)
> > >  >> > > >  {
> > >  >> > > > -    const union expr_constant *c = o->value.values;
> > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > >  >> > > >
> > >  >> > > >      switch (o->option->code) {
> > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
> > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
> > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
> > >  >> > > > --- a/lib/expr.c
> > >  >> > > > +++ b/lib/expr.c
> > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type, struct
> > > expr *a, struct expr *b)
> > >  >> > > >          } else {
> > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
> > >  >> > > >          }
> > >  >> > > > -        free(a->as_name);
> > >  >> > > >          a->as_name = NULL;
> > >  >> > > >          return a;
> > >  >> > > >      } else if (b->type == type) {
> > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
> > >  >> > > > -        free(b->as_name);
> > >  >> > > >          b->as_name = NULL;
> > >  >> > > >          return b;
> > >  >> > > >      } else {
> > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
> > > expr_context *, struct expr_field *);
> > >  >> > > >
> > >  >> > > >  static struct expr *
> > >  >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > >  >> > > > -             const union expr_constant *c)
> > >  >> > > > +             const struct expr_constant *c)
> > >  >> > > >  {
> > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
> > >  >> > > >      e->type = EXPR_T_CMP;
> > >  >> > > >      e->cmp.symbol = f->symbol;
> > >  >> > > >      e->cmp.relop = r;
> > >  >> > > > +    e->as_name = c->as_name;
> > >  >> > > >      if (f->symbol->width) {
> > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > >  >> > > >                       &e->cmp.value, sizeof e->cmp.value,
> f->ofs,
> > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f,
> > > enum expr_relop r,
> > >  >> > > >
> > >  >> > > >  /* Returns the minimum reasonable width for integer constant
> > > 'c'. */
> > >  >> > > >  static int
> > >  >> > > > -expr_constant_width(const union expr_constant *c)
> > >  >> > > > +expr_constant_width(const struct expr_constant *c)
> > >  >> > > >  {
> > >  >> > > >      if (c->masked) {
> > >  >> > > >          return mf_subvalue_width(&c->mask);
> > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
> EXPR_T_AND,
> > >  >> > > >                           e, make_cmp__(f, r,
> &cs->values[i]));
> > >  >> > > >      }
> > >  >> > > > -    /* Track address set */
> > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
> cs->as_name) {
> > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
> > >  >> > > > -    }
> > >  >> > > > +
> > >  >> > > >  exit:
> > >  >> > > >      expr_constant_set_destroy(cs);
> > >  >> > > >      return e;
> > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context
> > > *ctx, struct expr_constant_set *cs,
> > >  >> > > >          }
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    struct expr_constant_set *addr_sets
> > >  >> > > > -        = (ctx->addr_sets
> > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
> ctx->lexer->token.s)
> > >  >> > > > -           : NULL);
> > >  >> > > > -    if (!addr_sets) {
> > >  >> > > > +    struct shash_node *node = ctx->addr_sets
> > >  >> > > > +                              ? shash_find(ctx->addr_sets,
> > > ctx->lexer->token.s)
> > >  >> > > > +                              : NULL;
> > >  >> > > > +    if (!node) {
> > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting address set
> > > name");
> > >  >> > > >          return false;
> > >  >> > > >      }
> > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context
> > > *ctx, struct expr_constant_set *cs,
> > >  >> > > >          return false;
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    if (!cs->n_values) {
> > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > >  >> > > > -    }
> > >  >> > > > -
> > >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
> > >  >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> > >  >> > > >      if (n_values >= *allocated_values) {
> > >  >> > > >          cs->values = xrealloc(cs->values, n_values * sizeof
> > > *cs->values);
> > >  >> > > >          *allocated_values = n_values;
> > >  >> > > >      }
> > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > >  >> > > > -        cs->values[cs->n_values++] = addr_sets->values[i];
> > >  >> > > > +        struct expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > > +        *c = addr_sets->values[i];
> > >  >> > > > +        c->as_name = node->name;
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > >      return true;
> > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context *ctx,
> > > struct expr_constant_set *cs,
> > >  >> > > >          *allocated_values = n_values;
> > >  >> > > >      }
> > >  >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> > >  >> > > > -        cs->values[cs->n_values++].string =
> > >  >> > > > -            xstrdup(port_group->values[i].string);
> > >  >> > > > +        struct expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > > +        c->string = xstrdup(port_group->values[i].string);
> > >  >> > > > +        c->as_name = NULL;
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > >      return true;
> > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context *ctx,
> > > struct expr_constant_set *cs,
> > >  >> > > >                                  sizeof *cs->values);
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    /* Combining other values to the constant set that is
> > > tracking an
> > >  >> > > > -     * address set, so untrack it. */
> > >  >> > > > -    free(cs->as_name);
> > >  >> > > > -    cs->as_name = NULL;
> > >  >> > > > -
> > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> > >  >> > > >          return false;
> > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context *ctx,
> > > struct expr_constant_set *cs,
> > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
> EXPR_C_STRING)) {
> > >  >> > > >              return false;
> > >  >> > > >          }
> > >  >> > > > -        cs->values[cs->n_values++].string =
> > > xstrdup(ctx->lexer->token.s);
> > >  >> > > > +        struct expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> > >  >> > > > +        c->as_name = NULL;
> > >  >> > > >          lexer_get(ctx->lexer);
> > >  >> > > >          return true;
> > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context *ctx,
> > > struct expr_constant_set *cs,
> > >  >> > > >              return false;
> > >  >> > > >          }
> > >  >> > > >
> > >  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > >  >> > > > +        struct expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > >          c->value = ctx->lexer->token.value;
> > >  >> > > >          c->format = ctx->lexer->token.format;
> > >  >> > > >          c->masked = ctx->lexer->token.type ==
> > > LEX_T_MASKED_INTEGER;
> > >  >> > > >          if (c->masked) {
> > >  >> > > >              c->mask = ctx->lexer->token.mask;
> > >  >> > > >          }
> > >  >> > > > +        c->as_name = NULL;
> > >  >> > > >          lexer_get(ctx->lexer);
> > >  >> > > >          return true;
> > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context
> > > *ctx, struct expr_constant_set *cs)
> > >  >> > > >   * indeterminate. */
> > >  >> > > >  bool
> > >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
> > > expr_field *f,
> > >  >> > > > -                    union expr_constant *c)
> > >  >> > > > +                    struct expr_constant *c)
> > >  >> > > >  {
> > >  >> > > >      if (lexer->error) {
> > >  >> > > >          return false;
> > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer *lexer,
> > > const struct expr_field *f,
> > >  >> > > >  /* Appends to 's' a re-parseable representation of constant
> > > 'c' with the given
> > >  >> > > >   * 'type'. */
> > >  >> > > >  void
> > >  >> > > > -expr_constant_format(const union expr_constant *c,
> > >  >> > > > +expr_constant_format(const struct expr_constant *c,
> > >  >> > > >                       enum expr_constant_type type, struct ds
> *s)
> > >  >> > > >  {
> > >  >> > > >      if (type == EXPR_C_STRING) {
> > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> > > expr_constant *c,
> > >  >> > > >   *
> > >  >> > > >   * Does not free(c). */
> > >  >> > > >  void
> > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
> > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
> > >  >> > > >                        enum expr_constant_type type)
> > >  >> > > >  {
> > >  >> > > >      if (c && type == EXPR_C_STRING) {
> > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const struct
> > > expr_constant_set *cs, struct ds *s)
> > >  >> > > >          ds_put_char(s, '{');
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    for (const union expr_constant *c = cs->values;
> > >  >> > > > +    for (const struct expr_constant *c = cs->values;
> > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
> > >  >> > > >          if (c != cs->values) {
> > >  >> > > >              ds_put_cstr(s, ", ");
> > >  >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> > > expr_constant_set *cs)
> > >  >> > > >              }
> > >  >> > > >          }
> > >  >> > > >          free(cs->values);
> > >  >> > > > -        free(cs->as_name);
> > >  >> > > >      }
> > >  >> > > >  }
> > >  >> > > >
> > >  >> > > >  static int
> > >  >> > > >  compare_expr_constant_integer_cb(const void *a_, const void
> *b_)
> > >  >> > > >  {
> > >  >> > > > -    const union expr_constant *a = a_;
> > >  >> > > > -    const union expr_constant *b = b_;
> > >  >> > > > +    const struct expr_constant *a = a_;
> > >  >> > > > +    const struct expr_constant *b = b_;
> > >  >> > > >
> > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof a->value);
> > >  >> > > >      if (d) {
> > >  >> > > > @@ -1114,7 +1106,7 @@ expr_constant_set_create_integers(const
> > > char *const *values, size_t n_values)
> > >  >> > > >              VLOG_WARN("Invalid constant set entry: '%s',
> > > token type: %d",
> > >  >> > > >                        values[i], lex.token.type);
> > >  >> > > >          } else {
> > >  >> > > > -            union expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > > +            struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > >  >> > > >              c->value = lex.token.value;
> > >  >> > > >              c->format = lex.token.format;
> > >  >> > > >              c->masked = lex.token.type ==
> LEX_T_MASKED_INTEGER;
> > >  >> > > > @@ -1135,7 +1127,7 @@ expr_constant_set_create_integers(const
> > > char *const *values, size_t n_values)
> > >  >> > > >
> > >  >> > > >  static void
> > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set **p_cs,
> > >  >> > > > -                            union expr_constant *c, size_t
> > > *allocated)
> > >  >> > > > +                            struct expr_constant *c, size_t
> > > *allocated)
> > >  >> > > >  {
> > >  >> > > >      struct expr_constant_set *cs = *p_cs;
> > >  >> > > >      if (!cs) {
> > >  >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct shash
> > > *const_sets, const char *name,
> > >  >> > > >                          values[i], name);
> > >  >> > > >              continue;
> > >  >> > > >          }
> > >  >> > > > -        union expr_constant *c = &cs->values[cs->n_values++];
> > >  >> > > > +        struct expr_constant *c =
> &cs->values[cs->n_values++];
> > >  >> > > >          c->string = xstrdup(values[i]);
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct expr_context
> > > *ctx, bool *atomic)
> > >  >> > > >
> > >  >> > > >              *atomic = true;
> > >  >> > > >
> > >  >> > > > -            union expr_constant *cst = xzalloc(sizeof *cst);
> > >  >> > > > +            struct expr_constant *cst = xzalloc(sizeof *cst);
> > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
> > >  >> > > >              cst->masked = false;
> > >  >> > > >
> > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct expr_context
> > > *ctx, bool *atomic)
> > >  >> > > >              c.values = cst;
> > >  >> > > >              c.n_values = 1;
> > >  >> > > >              c.in_curlies = false;
> > >  >> > > > -            c.as_name = NULL;
> > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > >  >> > > >          } else if (parse_relop(ctx, &r) &&
> > > parse_constant_set(ctx, &c)) {
> > >  >> > > >              return make_cmp(ctx, &f, r, &c);
> > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
> *symtab)
> > >  >> > > >  static struct expr *
> > >  >> > > >  expr_clone_cmp(struct expr *expr)
> > >  >> > > >  {
> > >  >> > > > -    ovs_assert(!expr->as_name);
> > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > >  >> > > >      if (!new->cmp.symbol->width) {
> > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> > >  >> > > >  static struct expr *
> > >  >> > > >  expr_clone_condition(struct expr *expr)
> > >  >> > > >  {
> > >  >> > > > -    ovs_assert(!expr->as_name);
> > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
> > >  >> > > >      return new;
> > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > >  >> > > >          return;
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    free(expr->as_name);
> > >  >> > > > -
> > >  >> > > >      struct expr *sub;
> > >  >> > > >
> > >  >> > > >      switch (expr->type) {
> > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr *expr,
> > > const struct expr_symbol *symbol)
> > >  >> > > >       * mask sizes. */
> > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
> > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > >  >> > > > -    bool modified = false;
> > >  >> > > > +    bool has_addr_set = false;
> > >  >> > > >      /* Linked list over the 'subs' array to quickly skip
> > > deleted elements,
> > >  >> > > >       * i.e. the index of the next potentially non-NULL
> > > element. */
> > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
> > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr *expr,
> > > const struct expr_symbol *symbol)
> > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
> > >  >> > > >      struct expr *sub;
> > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > >  >> > > > +        if (sub->as_name) {
> > >  >> > > > +            has_addr_set = true;
> > >  >> > > > +        }
> > >  >> > > >          if (symbol->width) {
> > >  >> > > >              const unsigned long *sub_mask;
> > >  >> > > >
> > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr *expr,
> > > const struct expr_symbol *symbol)
> > >  >> > > >              next[last] = i;
> > >  >> > > >              last = i;
> > >  >> > > >          } else {
> > >  >> > > > +            /* Remove address set reference from the
> > > duplicate. */
> > >  >> > > > +            subs[last]->as_name = NULL;
> > >  >> > > >              expr_destroy(subs[i]);
> > >  >> > > >              subs[i] = NULL;
> > >  >> > > > -            modified = true;
> > >  >> > > >          }
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > >  >> > > > -        || (!modified && expr->as_name)) {
> > >  >> > > > +    if (!symbol->width || symbol->level != EXPR_L_ORDINAL ||
> > > has_addr_set) {
> > >  >> > > >          /* Not a fully maskable field or this expression is
> > > tracking an
> > >  >> > > >           * address set.  Don't try to optimize to preserve
> > > address set I-P. */
> > >  >> > > >          goto done;
> > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr *expr,
> > > const struct expr_symbol *symbol)
> > >  >> > > >              if (expr_bitmap_intersect_check(a_value, a_mask,
> > > b_value, b_mask,
> > >  >> > > >                                              bit_width)
> > >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
> > > bit_width)) {
> > >  >> > > > -                /* 'a' is the same expression with a smaller
> > > mask. */
> > >  >> > > > +                /* 'a' is the same expression with a smaller
> > > mask.
> > >  >> > > > +                 * Remove address set reference from the
> > > duplicate. */
> > >  >> > > > +                a->as_name = NULL;
> > >  >> > > >                  expr_destroy(subs[j]);
> > >  >> > > >                  subs[j] = NULL;
> > >  >> > > > -                modified = true;
> > >  >> > > >
> > >  >> > > >                  /* Shorten the path for the next round. */
> > >  >> > > >                  if (last) {
> > >  >> > > > @@ -2685,12 +2676,6 @@ done:
> > >  >> > > >          }
> > >  >> > > >      }
> > >  >> > > >
> > >  >> > > > -    if (modified) {
> > >  >> > > > -        /* Members modified, so untrack address set. */
> > >  >> > > > -        free(expr->as_name);
> > >  >> > > > -        expr->as_name = NULL;
> > >  >> > > > -    }
> > >  >> > > > -
> > >  >> > > >      free(next);
> > >  >> > > >      free(subs);
> > >  >> > > >      return expr;
> > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr *or,
> > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > >  >> > > >          struct expr_match *match = expr_match_new(m, clause,
> > > n_clauses,
> > >  >> > > >                                                    conj_id);
> > >  >> > > > -        if (or->as_name) {
> > >  >> > > > +        if (sub->as_name) {
> > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
> > >  >> > > > -            match->as_name = xstrdup(or->as_name);
> > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
> > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
> > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> > >  >> > > >          }
> > >  >> > > > diff --git a/tests/ovn-controller.at
> > > <http://ovn-controller.at> b/tests/ovn-controller.at
> > > <http://ovn-controller.at>
> > >  >> > > > index be198e00d..27cec2aec 100644
> > >  >> > > > --- a/tests/ovn-controller.at <http://ovn-controller.at>
> > >  >> > > > +++ b/tests/ovn-controller.at <http://ovn-controller.at>
> > >  >> > > > @@ -1625,7 +1625,9 @@
> > > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> > > actions=lo
> > >  >> > > >  done
> > >  >> > > >
> > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [10
> > >  >> > > > +# First 2 reprocess are due to change from 1 IP in AS to 2.
> > >  >> > > > +# Last 5 is due to overlap in IP addresses between as1 and
> as2.
> > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [7
> > >  >> > > >  ])
> > >  >> > > >
> > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > >  >> > > >  done
> > >  >> > > >
> > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > >  >> > > > -# In this case the as1 and as2 are merged to a single OR
> > > expr, so we lose track of
> > >  >> > > > -# address set information - can't handle deletion
> incrementally.
> > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [10
> > >  >> > > > +# First 5 are due to overlap in IP addresses between as1 and
> as2.
> > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [7
> > >  >> > > >  ])
> > >  >> > > >
> > >  >> > > >  OVN_CLEANUP([hv1])
> > >  >> > > > @@ -1817,7 +1819,7 @@
> > > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> > > actions=co
> > >  >> > > >
> > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> > >  >> > > >  ])
> > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [1
> > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [0
> > >  >> > > >  ])
> > >  >> > > >
> > >  >> > > >  # Delete 2 IPs
> > >  >> > > > @@ -1837,7 +1839,7 @@
> > > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> > > actions=co
> > >  >> > > >
> > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> actions=conjunction,2/2)
> > >  >> > > >  ])
> > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [1
> > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > $reprocess_count_old))], [0], [0
> > >  >> > > >  ])
> > >  >> > > >
> > >  >> > > >  OVN_CLEANUP([hv1])
> > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > >  >> > > >
> > >  >> > > >  AT_CLEANUP
> > >  >> > > >  ])
> > >  >> > > > +
> > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute consistency])
> > >  >> > > > +AT_KEYWORDS([as-i-p])
> > >  >> > > > +
> > >  >> > > > +ovn_start
> > >  >> > > > +
> > >  >> > > > +net_add n1
> > >  >> > > > +sim_add hv1
> > >  >> > > > +as hv1
> > >  >> > > > +check ovs-vsctl add-br br-phys
> > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
> > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > >  >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > >  >> > > > +
> > >  >> > > > +check ovn-nbctl ls-add ls1
> > >  >> > > > +
> > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > >  >> > > > +
> > >  >> > > > +wait_for_ports_up
> > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > >  >> > > > +
> > >  >> > > > +# Get the OF table numbers
> > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
> > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
> ls_out_acl_action)
> > >  >> > > > +
> > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> > > external_ids:name=ls1))
> > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding tunnel_key
> > > logical_port=ls1-lp1))
> > >  >> > > > +
> > >  >> > > > +ovn-nbctl create address_set name=as1
> > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
> > > "ls1-lp1" && ip4.src == $as1' drop
> > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > > <http://10.0.0.0/24>
> > >  >> > > > +check ovn-nbctl --wait=hv sync
> > >  >> > > > +
> > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > > | sort], [0], [dnl
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > > <http://10.0.0.0/24>
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +])
> > >  >> > > > +
> > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > >  >> > > > +check ovn-nbctl --wait=hv sync
> > >  >> > > > +
> > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > > | sort], [0], [dnl
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > > <http://10.0.0.0/24>
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +])
> > >  >> > > > +
> > >  >> > > > +
> > >  >> > > > +check ovn-appctl inc-engine/recompute
> > >  >> > > > +
> > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}'
> > > | sort], [0], [dnl
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24
> > > <http://10.0.0.0/24>
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > >  >> > > > +])
> > >  >> > > > +
> > >  >> > > > +OVN_CLEANUP([hv1])
> > >  >> > > > +AT_CLEANUP
> > >  >> > > > --
> > >  >> > > > 2.44.0
> > >  >> > > >
> > >  >> >
> > >  >> >
> > >  >> > Thank you Han,
> > >  >> >
> > >  >> > I would like to start the discussion about the possibility of
> > >  >> > backporting this change. The reason for the backport being that
> this
> > >  >> > change might be beneficial to ovn-kubernetes users, as this was
> > >  >> > actually observed in ovn-kubernetes with network policies. I know
> that
> > >  >> > strictly speaking this is not a bug fix, because this limitation
> was
> > >  >> > well documented, however I would like to hear the opinion of
> others.
> > >  >>
> > >  >>
> > >  >> I have no objections if this improvement is strongly required for
> > > released versions, even though it is not a bug. May I ask for which
> > > releases do you want this to be backported?
> > >  >
> > >  >
> > >  > We have a request for 23.09 and 24.03.
> > >  >
> > > Ok. I will wait for input from other maintainers. It will be a tradeoff
> > > between risk and performance gains. Mark, Numan and Dumitru, what do you
> > > think?
> > >
> > > Thanks,
> > > Han
> >
> > I'm OK with the patch being backported. I think our test coverage is
> > sufficient to uncover problems that may arise from the backport, so the
> > risk should be minimal.
> >
>
> Thanks Numan and Mark for confirming, so we got a quorum. I backported the
> patch to 24.03 and 23.09.

This patch is using the "ovn-debug" binary which is missing in
branch-24.03 and branch-23.09.
Because of this, the test case "AS I-P and recompute consistency" is
failing.  I backported
the commit "117573e2d279" to these branches.

While backporting to branch-23.09,  had to move the pipeline stage macros from
northd.c to northd.h.  Running the tests in my github repo before pushing
to upstream branch-23.09.

Numan

>
> Han
>
> > >
> > >  >>
> > >  >>
> > >  >> Thanks,
> > >  >> Han
> > >  >>
> > >  >> >
> > >  >> > Thanks,
> > >  >> > Ales
> > >  >> > --
> > >  >> >
> > >  >> > Ales Musil
> > >  >> >
> > >  >> > Senior Software Engineer - OVN Core
> > >  >> >
> > >  >> > Red Hat EMEA
> > >  >> >
> > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
> > >  >> >
> > >  >
> > >  >
> > >  >
> > >  > --
> > >  >
> > >  > Ales Musil
> > >  >
> > >  > Senior Software Engineer - OVN Core
> > >  >
> > >  > Red Hat EMEA
> > >  >
> > >  > amusil@redhat.com <mailto:amusil@redhat.com>
> >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Han Zhou May 10, 2024, 4:38 p.m. UTC | #10
On Fri, May 10, 2024 at 9:23 AM Numan Siddique <numans@ovn.org> wrote:
>
> On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com>
wrote:
> > >
> > > On 5/7/24 02:12, Han Zhou wrote:
> > > >
> > > >
> > > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
> > > > <mailto:amusil@redhat.com>> wrote:
> > > >  >
> > > >  >
> > > >  >
> > > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
> > > > <mailto:hzhou@ovn.org>> wrote:
> > > >  >>
> > > >  >>
> > > >  >>
> > > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com
> > > > <mailto:amusil@redhat.com>> wrote:
> > > >  >> >
> > > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
> > > > <mailto:hzhou@ovn.org>> wrote:
> > > >  >> > >
> > > >  >> > >
> > > >  >> > >
> > > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com
> > > > <mailto:amusil@redhat.com>> wrote:
> > > >  >> > > >
> > > >  >> > > > Instead of tracking address set per struct
expr_constant_set
> > > > track it
> > > >  >> > > > per individual struct expr_constant. This allows more fine
> > grained
> > > >  >> > > > control for I-P processing of address sets in controller.
It
> > > > helps with
> > > >  >> > > > scenarios like matching on two address sets in one
expression
> > e.g.
> > > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
> > removal of
> > > >  >> > > > individual adress from the set to be incrementally
processed
> > > > instead
> > > >  >> > > > of reprocessing all the flows.
> > > >  >> > > >
> > > >  >> > > > This unfortunately doesn't help with the following flows:
> > > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> > > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> > > >  >> > > >
> > > >  >> > > > The memory impact should be minimal as there is only
increase
> > > > of 8 bytes
> > > >  >> > > > per the struct expr_constant.
> > > >  >> > > >
> > > >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > > <https://issues.redhat.com/browse/FDP-509>
> > > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
> > > > <mailto:amusil@redhat.com>>
> > > >  >> > > > ---
> > > >  >> > > > v4: Rebase on top of current main.
> > > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
> > > > according to suggestion from Han.
> > > >  >> > >
> > > >  >> > > Thanks Ales. I updated the commit message for the same, and
> > > > applied to main branch.
> > > >  >> > >
> > > >  >> > > Regards,
> > > >  >> > > Han
> > > >  >> > >
> > > >  >> > > > v3: Rebase on top of current main.
> > > >  >> > > >     Address comments from Han:
> > > >  >> > > >     - Adjust the comment for
"lflow_handle_addr_set_update" to
> > > > include remaning corner cases.
> > > >  >> > > >     - Make sure that the flows are consistent between I-P
and
> > > > recompute.
> > > >  >> > > > v2: Rebase on top of current main.
> > > >  >> > > >     Adjust the comment for I-P optimization.
> > > >  >> > > > ---
> > > >  >> > > >  controller/lflow.c      | 11 ++---
> > > >  >> > > >  include/ovn/actions.h   |  2 +-
> > > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> > > >  >> > > >  lib/actions.c           | 20 ++++-----
> > > >  >> > > >  lib/expr.c              | 99
> > > > +++++++++++++++++------------------------
> > > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> | 79
> > > > +++++++++++++++++++++++++++++---
> > > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > > >  >> > > >
> > > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> > > >  >> > > > index 760ec0b41..1e05665a1 100644
> > > >  >> > > > --- a/controller/lflow.c
> > > >  >> > > > +++ b/controller/lflow.c
> > > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
> > > > lflow_ctx_in *l_ctx_in,
> > > >  >> > > >  }
> > > >  >> > > >
> > > >  >> > > >  static bool
> > > >  >> > > > -as_info_from_expr_const(const char *as_name, const union
> > > > expr_constant *c,
> > > >  >> > > > +as_info_from_expr_const(const char *as_name, const struct
> > > > expr_constant *c,
> > > >  >> > > >                          struct addrset_info *as_info)
> > > >  >> > > >  {
> > > >  >> > > >      as_info->name = as_name;
> > > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
> > > > *as_name, struct addr_set_diff *as_diff,
> > > >  >> > > >   *        generated.
> > > >  >> > > >   *
> > > >  >> > > >   *      - The sub expression of the address set is
combined
> > > > with other sub-
> > > >  >> > > > - *        expressions/constants, usually because of
> > > > disjunctions between
> > > >  >> > > > - *        sub-expressions/constants, e.g.:
> > > >  >> > > > + *        expressions/constants on different fields,
e.g.:
> > > >  >> > > >   *
> > > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
> > > >  >> > > > - *          ip.src == {$as1, $as2}
> > > >  >> > > > - *          ip.src == {$as1, ip1}
> > > >  >> > > >   *
> > > >  >> > > > - *        All these could have been split into separate
> > lflows.
> > > >  >> > > > + *        This could have been split into separate
lflows.
> > > >  >> > > >   *
> > > >  >> > > >   *      - Conjunctions overlapping between lflows, which
can
> > > > be caused by
> > > >  >> > > >   *        overlapping address sets or same address set
used
> > > > by multiple lflows
> > > >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const
char
> > > > *as_name,
> > > >  >> > > >          if (as_diff->deleted) {
> > > >  >> > > >              struct addrset_info as_info;
> > > >  >> > > >              for (size_t i = 0; i <
> > > > as_diff->deleted->n_values; i++) {
> > > >  >> > > > -                union expr_constant *c =
> > > > &as_diff->deleted->values[i];
> > > >  >> > > > +                struct expr_constant *c =
> > > > &as_diff->deleted->values[i];
> > > >  >> > > >                  if (!as_info_from_expr_const(as_name, c,
> > > > &as_info)) {
> > > >  >> > > >                      continue;
> > > >  >> > > >                  }
> > > >  >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > > >  >> > > > index ae0864fdd..88cf4de79 100644
> > > >  >> > > > --- a/include/ovn/actions.h
> > > >  >> > > > +++ b/include/ovn/actions.h
> > > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > > >  >> > > >  struct ovnact_load {
> > > >  >> > > >      struct ovnact ovnact;
> > > >  >> > > >      struct expr_field dst;
> > > >  >> > > > -    union expr_constant imm;
> > > >  >> > > > +    struct expr_constant imm;
> > > >  >> > > >  };
> > > >  >> > > >
> > > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > > >  >> > > > index c48f82398..e54edb5bf 100644
> > > >  >> > > > --- a/include/ovn/expr.h
> > > >  >> > > > +++ b/include/ovn/expr.h
> > > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum
lex_type
> > > > type, enum expr_relop *relop);
> > > >  >> > > >  struct expr {
> > > >  >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND
or
> > > > EXPR_T_OR if any. */
> > > >  >> > > >      enum expr_type type;        /* Expression type. */
> > > >  >> > > > -    char *as_name;              /* Address set name.
Null if
> > > > it is not an
> > > >  >> > > > +    const char *as_name;        /* Address set name.
Null if
> > > > it is not an
> > > >  >> > > >                                     address set. */
> > > >  >> > > >
> > > >  >> > > >      union {
> > > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > > >  >> > > >  };
> > > >  >> > > >
> > > >  >> > > >  /* A string or integer constant (one must know which from
> > > > context). */
> > > >  >> > > > -union expr_constant {
> > > >  >> > > > -    /* Integer constant.
> > > >  >> > > > -     *
> > > >  >> > > > -     * The width of a constant isn't always clear, e.g.
if
> > > > you write "1",
> > > >  >> > > > -     * there's no way to tell whether you mean for that
to be
> > > > a 1-bit constant
> > > >  >> > > > -     * or a 128-bit constant or somewhere in between. */
> > > >  >> > > > -    struct {
> > > >  >> > > > -        union mf_subvalue value;
> > > >  >> > > > -        union mf_subvalue mask; /* Only initialized if
> > > > 'masked'. */
> > > >  >> > > > -        bool masked;
> > > >  >> > > > -
> > > >  >> > > > -        enum lex_format format; /* From the constant's
> > > > lex_token. */
> > > >  >> > > > -    };
> > > >  >> > > > +struct expr_constant {
> > > >  >> > > > +    const char *as_name;
> > > >  >> > > >
> > > >  >> > > > -    /* Null-terminated string constant. */
> > > >  >> > > > -    char *string;
> > > >  >> > > > +    union {
> > > >  >> > > > +        /* Integer constant.
> > > >  >> > > > +         *
> > > >  >> > > > +         * The width of a constant isn't always clear,
e.g.
> > > > if you write "1",
> > > >  >> > > > +         * there's no way to tell whether you mean for
that
> > > > to be a 1-bit
> > > >  >> > > > +         * constant or a 128-bit constant or somewhere in
> > > > between. */
> > > >  >> > > > +        struct {
> > > >  >> > > > +            union mf_subvalue value;
> > > >  >> > > > +            union mf_subvalue mask; /* Only initialized
if
> > > > 'masked'. */
> > > >  >> > > > +            bool masked;
> > > >  >> > > > +
> > > >  >> > > > +            enum lex_format format; /* From the
constant's
> > > > lex_token. */
> > > >  >> > > > +        };
> > > >  >> > > > +
> > > >  >> > > > +        /* Null-terminated string constant. */
> > > >  >> > > > +        char *string;
> > > >  >> > > > +    };
> > > >  >> > > >  };
> > > >  >> > > >
> > > >  >> > > >  bool expr_constant_parse(struct lexer *,
> > > >  >> > > >                           const struct expr_field *,
> > > >  >> > > > -                         union expr_constant *);
> > > >  >> > > > -void expr_constant_format(const union expr_constant *,
> > > >  >> > > > +                         struct expr_constant *);
> > > >  >> > > > +void expr_constant_format(const struct expr_constant *,
> > > >  >> > > >                            enum expr_constant_type,
struct ds
> > *);
> > > >  >> > > > -void expr_constant_destroy(const union expr_constant *,
> > > >  >> > > > +void expr_constant_destroy(const struct expr_constant *,
> > > >  >> > > >                             enum expr_constant_type);
> > > >  >> > > >
> > > >  >> > > >  /* A collection of "union expr_constant"s of the same
type.
> > */
> > > >  >> > > >  struct expr_constant_set {
> > > >  >> > > > -    union expr_constant *values;  /* Constants. */
> > > >  >> > > > +    struct expr_constant *values; /* Constants. */
> > > >  >> > > >      size_t n_values;              /* Number of
constants. */
> > > >  >> > > >      enum expr_constant_type type; /* Type of the
constants.
> > */
> > > >  >> > > >      bool in_curlies;              /* Whether the
constants
> > > > were in {}. */
> > > >  >> > > > -    char *as_name;                /* Name of an address
set.
> > > > It is NULL if not
> > > >  >> > > > -                                     an address set. */
> > > >  >> > > >  };
> > > >  >> > > >
> > > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> > > > expr_constant_set *);
> > > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
> > > >  >> > > > index 94751d059..cff4f9e3c 100644
> > > >  >> > > > --- a/lib/actions.c
> > > >  >> > > > +++ b/lib/actions.c
> > > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load
> > *load,
> > > >  >> > > >              const struct ovnact_encode_params *ep,
> > > >  >> > > >              struct ofpbuf *ofpacts)
> > > >  >> > > >  {
> > > >  >> > > > -    const union expr_constant *c = &load->imm;
> > > >  >> > > > +    const struct expr_constant *c = &load->imm;
> > > >  >> > > >      struct mf_subfield dst =
expr_resolve_field(&load->dst);
> > > >  >> > > >      struct ofpact_set_field *sf =
> > > > ofpact_put_set_field(ofpacts, dst.field,
> > > >  >> > > >
NULL,
> > > > NULL);
> > > >  >> > > > @@ -2077,7 +2077,7 @@
> > > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> > > >  >> > > >          /* All empty_lb_backends fields are of type
'str' */
> > > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> > > >  >> > > >
> > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > >  >> > > >          size_t size = strlen(c->string);
> > > >  >> > > >          struct controller_event_opt_header hdr =
> > > >  >> > > >              (struct controller_event_opt_header) {
> > > >  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> > > > action_context *ctx,
> > > >  >> > > >  {
> > > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
> > > >  >> > > >          const struct ovnact_gen_option *o = &options[i];
> > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > >  >> > > >          struct sockaddr_storage ss;
> > > >  >> > > >          struct uuid uuid;
> > > >  >> > > >
> > > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const
struct
> > > > ovnact_gen_option *o,
> > > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > >  >> > > >      opt_header[0] = o->option->code;
> > > >  >> > > >
> > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > >  >> > > >      size_t n_values = o->value.n_values;
> > > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
> > > >  >> > > >          !strcmp(o->option->type, "uint8")) {
> > > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const
struct
> > > > ovnact_gen_option *o,
> > > >  >> > > >                           struct ofpbuf *ofpacts)
> > > >  >> > > >  {
> > > >  >> > > >      struct dhcpv6_opt_header *opt =
> > > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
> > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > >  >> > > >      size_t n_values = o->value.n_values;
> > > >  >> > > >      size_t size;
> > > >  >> > > >
> > > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > ovnact_put_opts *pdo,
> > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > DHCP_OPT_BOOTFILE_CODE);
> > > >  >> > > >      if (boot_opt) {
> > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
2);
> > > >  >> > > > -        const union expr_constant *c =
> > boot_opt->value.values;
> > > >  >> > > > +        const struct expr_constant *c =
> > boot_opt->value.values;
> > > >  >> > > >          opt_header[0] = boot_opt->option->code;
> > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > ovnact_put_opts *pdo,
> > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > DHCP_OPT_BOOTFILE_ALT_CODE);
> > > >  >> > > >      if (boot_alt_opt) {
> > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
2);
> > > >  >> > > > -        const union expr_constant *c =
> > > > boot_alt_opt->value.values;
> > > >  >> > > > +        const struct expr_constant *c =
> > > > boot_alt_opt->value.values;
> > > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
> > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > ovnact_put_opts *pdo,
> > > >  >> > > >          pdo->options, pdo->n_options,
> > DHCP_OPT_NEXT_SERVER_CODE);
> > > >  >> > > >      if (next_server_opt) {
> > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
2);
> > > >  >> > > > -        const union expr_constant *c =
> > > > next_server_opt->value.values;
> > > >  >> > > > +        const struct expr_constant *c =
> > > > next_server_opt->value.values;
> > > >  >> > > >          opt_header[0] = next_server_opt->option->code;
> > > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
> > > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
> > sizeof(ovs_be32));
> > > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
> > > > action_context *ctx, const struct expr_field *dst,
> > > >  >> > > >      /* Let's validate the options. */
> > > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> > > >  >> > > >          const struct ovnact_gen_option *o =
&po->options[i];
> > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > >  >> > > >          if (o->value.n_values > 1) {
> > > >  >> > > >              lexer_error(ctx->lexer, "Invalid value for
\"%s\"
> > > > option",
> > > >  >> > > >                          o->option->name);
> > > >  >> > > > @@ -3490,7 +3490,7 @@ static void
> > > >  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option
*o,
> > > >  >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
> > > > ra_offset)
> > > >  >> > > >  {
> > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > >  >> > > >
> > > >  >> > > >      switch (o->option->code) {
> > > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
> > > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
> > > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
> > > >  >> > > > --- a/lib/expr.c
> > > >  >> > > > +++ b/lib/expr.c
> > > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type,
struct
> > > > expr *a, struct expr *b)
> > > >  >> > > >          } else {
> > > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
> > > >  >> > > >          }
> > > >  >> > > > -        free(a->as_name);
> > > >  >> > > >          a->as_name = NULL;
> > > >  >> > > >          return a;
> > > >  >> > > >      } else if (b->type == type) {
> > > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
> > > >  >> > > > -        free(b->as_name);
> > > >  >> > > >          b->as_name = NULL;
> > > >  >> > > >          return b;
> > > >  >> > > >      } else {
> > > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
> > > > expr_context *, struct expr_field *);
> > > >  >> > > >
> > > >  >> > > >  static struct expr *
> > > >  >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > > >  >> > > > -             const union expr_constant *c)
> > > >  >> > > > +             const struct expr_constant *c)
> > > >  >> > > >  {
> > > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
> > > >  >> > > >      e->type = EXPR_T_CMP;
> > > >  >> > > >      e->cmp.symbol = f->symbol;
> > > >  >> > > >      e->cmp.relop = r;
> > > >  >> > > > +    e->as_name = c->as_name;
> > > >  >> > > >      if (f->symbol->width) {
> > > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > > >  >> > > >                       &e->cmp.value, sizeof e->cmp.value,
> > f->ofs,
> > > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f,
> > > > enum expr_relop r,
> > > >  >> > > >
> > > >  >> > > >  /* Returns the minimum reasonable width for integer
constant
> > > > 'c'. */
> > > >  >> > > >  static int
> > > >  >> > > > -expr_constant_width(const union expr_constant *c)
> > > >  >> > > > +expr_constant_width(const struct expr_constant *c)
> > > >  >> > > >  {
> > > >  >> > > >      if (c->masked) {
> > > >  >> > > >          return mf_subvalue_width(&c->mask);
> > > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
> > EXPR_T_AND,
> > > >  >> > > >                           e, make_cmp__(f, r,
> > &cs->values[i]));
> > > >  >> > > >      }
> > > >  >> > > > -    /* Track address set */
> > > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
> > cs->as_name) {
> > > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
> > > >  >> > > > -    }
> > > >  >> > > > +
> > > >  >> > > >  exit:
> > > >  >> > > >      expr_constant_set_destroy(cs);
> > > >  >> > > >      return e;
> > > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context
> > > > *ctx, struct expr_constant_set *cs,
> > > >  >> > > >          }
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    struct expr_constant_set *addr_sets
> > > >  >> > > > -        = (ctx->addr_sets
> > > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
> > ctx->lexer->token.s)
> > > >  >> > > > -           : NULL);
> > > >  >> > > > -    if (!addr_sets) {
> > > >  >> > > > +    struct shash_node *node = ctx->addr_sets
> > > >  >> > > > +                              ?
shash_find(ctx->addr_sets,
> > > > ctx->lexer->token.s)
> > > >  >> > > > +                              : NULL;
> > > >  >> > > > +    if (!node) {
> > > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting
address set
> > > > name");
> > > >  >> > > >          return false;
> > > >  >> > > >      }
> > > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context
> > > > *ctx, struct expr_constant_set *cs,
> > > >  >> > > >          return false;
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    if (!cs->n_values) {
> > > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > > >  >> > > > -    }
> > > >  >> > > > -
> > > >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
> > > >  >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> > > >  >> > > >      if (n_values >= *allocated_values) {
> > > >  >> > > >          cs->values = xrealloc(cs->values, n_values *
sizeof
> > > > *cs->values);
> > > >  >> > > >          *allocated_values = n_values;
> > > >  >> > > >      }
> > > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > > >  >> > > > -        cs->values[cs->n_values++] =
addr_sets->values[i];
> > > >  >> > > > +        struct expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > > +        *c = addr_sets->values[i];
> > > >  >> > > > +        c->as_name = node->name;
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > >      return true;
> > > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context
*ctx,
> > > > struct expr_constant_set *cs,
> > > >  >> > > >          *allocated_values = n_values;
> > > >  >> > > >      }
> > > >  >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > >  >> > > > -            xstrdup(port_group->values[i].string);
> > > >  >> > > > +        struct expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > > +        c->string =
xstrdup(port_group->values[i].string);
> > > >  >> > > > +        c->as_name = NULL;
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > >      return true;
> > > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context
*ctx,
> > > > struct expr_constant_set *cs,
> > > >  >> > > >                                  sizeof *cs->values);
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    /* Combining other values to the constant set that is
> > > > tracking an
> > > >  >> > > > -     * address set, so untrack it. */
> > > >  >> > > > -    free(cs->as_name);
> > > >  >> > > > -    cs->as_name = NULL;
> > > >  >> > > > -
> > > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> > > >  >> > > >          return false;
> > > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context
*ctx,
> > > > struct expr_constant_set *cs,
> > > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
> > EXPR_C_STRING)) {
> > > >  >> > > >              return false;
> > > >  >> > > >          }
> > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > > xstrdup(ctx->lexer->token.s);
> > > >  >> > > > +        struct expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> > > >  >> > > > +        c->as_name = NULL;
> > > >  >> > > >          lexer_get(ctx->lexer);
> > > >  >> > > >          return true;
> > > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context
*ctx,
> > > > struct expr_constant_set *cs,
> > > >  >> > > >              return false;
> > > >  >> > > >          }
> > > >  >> > > >
> > > >  >> > > > -        union expr_constant *c =
&cs->values[cs->n_values++];
> > > >  >> > > > +        struct expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > >          c->value = ctx->lexer->token.value;
> > > >  >> > > >          c->format = ctx->lexer->token.format;
> > > >  >> > > >          c->masked = ctx->lexer->token.type ==
> > > > LEX_T_MASKED_INTEGER;
> > > >  >> > > >          if (c->masked) {
> > > >  >> > > >              c->mask = ctx->lexer->token.mask;
> > > >  >> > > >          }
> > > >  >> > > > +        c->as_name = NULL;
> > > >  >> > > >          lexer_get(ctx->lexer);
> > > >  >> > > >          return true;
> > > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context
> > > > *ctx, struct expr_constant_set *cs)
> > > >  >> > > >   * indeterminate. */
> > > >  >> > > >  bool
> > > >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
> > > > expr_field *f,
> > > >  >> > > > -                    union expr_constant *c)
> > > >  >> > > > +                    struct expr_constant *c)
> > > >  >> > > >  {
> > > >  >> > > >      if (lexer->error) {
> > > >  >> > > >          return false;
> > > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer
*lexer,
> > > > const struct expr_field *f,
> > > >  >> > > >  /* Appends to 's' a re-parseable representation of
constant
> > > > 'c' with the given
> > > >  >> > > >   * 'type'. */
> > > >  >> > > >  void
> > > >  >> > > > -expr_constant_format(const union expr_constant *c,
> > > >  >> > > > +expr_constant_format(const struct expr_constant *c,
> > > >  >> > > >                       enum expr_constant_type type,
struct ds
> > *s)
> > > >  >> > > >  {
> > > >  >> > > >      if (type == EXPR_C_STRING) {
> > > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> > > > expr_constant *c,
> > > >  >> > > >   *
> > > >  >> > > >   * Does not free(c). */
> > > >  >> > > >  void
> > > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
> > > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
> > > >  >> > > >                        enum expr_constant_type type)
> > > >  >> > > >  {
> > > >  >> > > >      if (c && type == EXPR_C_STRING) {
> > > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const
struct
> > > > expr_constant_set *cs, struct ds *s)
> > > >  >> > > >          ds_put_char(s, '{');
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    for (const union expr_constant *c = cs->values;
> > > >  >> > > > +    for (const struct expr_constant *c = cs->values;
> > > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
> > > >  >> > > >          if (c != cs->values) {
> > > >  >> > > >              ds_put_cstr(s, ", ");
> > > >  >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> > > > expr_constant_set *cs)
> > > >  >> > > >              }
> > > >  >> > > >          }
> > > >  >> > > >          free(cs->values);
> > > >  >> > > > -        free(cs->as_name);
> > > >  >> > > >      }
> > > >  >> > > >  }
> > > >  >> > > >
> > > >  >> > > >  static int
> > > >  >> > > >  compare_expr_constant_integer_cb(const void *a_, const
void
> > *b_)
> > > >  >> > > >  {
> > > >  >> > > > -    const union expr_constant *a = a_;
> > > >  >> > > > -    const union expr_constant *b = b_;
> > > >  >> > > > +    const struct expr_constant *a = a_;
> > > >  >> > > > +    const struct expr_constant *b = b_;
> > > >  >> > > >
> > > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof
a->value);
> > > >  >> > > >      if (d) {
> > > >  >> > > > @@ -1114,7 +1106,7 @@
expr_constant_set_create_integers(const
> > > > char *const *values, size_t n_values)
> > > >  >> > > >              VLOG_WARN("Invalid constant set entry: '%s',
> > > > token type: %d",
> > > >  >> > > >                        values[i], lex.token.type);
> > > >  >> > > >          } else {
> > > >  >> > > > -            union expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > > +            struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > >  >> > > >              c->value = lex.token.value;
> > > >  >> > > >              c->format = lex.token.format;
> > > >  >> > > >              c->masked = lex.token.type ==
> > LEX_T_MASKED_INTEGER;
> > > >  >> > > > @@ -1135,7 +1127,7 @@
expr_constant_set_create_integers(const
> > > > char *const *values, size_t n_values)
> > > >  >> > > >
> > > >  >> > > >  static void
> > > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set
**p_cs,
> > > >  >> > > > -                            union expr_constant *c,
size_t
> > > > *allocated)
> > > >  >> > > > +                            struct expr_constant *c,
size_t
> > > > *allocated)
> > > >  >> > > >  {
> > > >  >> > > >      struct expr_constant_set *cs = *p_cs;
> > > >  >> > > >      if (!cs) {
> > > >  >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct
shash
> > > > *const_sets, const char *name,
> > > >  >> > > >                          values[i], name);
> > > >  >> > > >              continue;
> > > >  >> > > >          }
> > > >  >> > > > -        union expr_constant *c =
&cs->values[cs->n_values++];
> > > >  >> > > > +        struct expr_constant *c =
> > &cs->values[cs->n_values++];
> > > >  >> > > >          c->string = xstrdup(values[i]);
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct
expr_context
> > > > *ctx, bool *atomic)
> > > >  >> > > >
> > > >  >> > > >              *atomic = true;
> > > >  >> > > >
> > > >  >> > > > -            union expr_constant *cst = xzalloc(sizeof
*cst);
> > > >  >> > > > +            struct expr_constant *cst = xzalloc(sizeof
*cst);
> > > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
> > > >  >> > > >              cst->masked = false;
> > > >  >> > > >
> > > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct
expr_context
> > > > *ctx, bool *atomic)
> > > >  >> > > >              c.values = cst;
> > > >  >> > > >              c.n_values = 1;
> > > >  >> > > >              c.in_curlies = false;
> > > >  >> > > > -            c.as_name = NULL;
> > > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > > >  >> > > >          } else if (parse_relop(ctx, &r) &&
> > > > parse_constant_set(ctx, &c)) {
> > > >  >> > > >              return make_cmp(ctx, &f, r, &c);
> > > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
> > *symtab)
> > > >  >> > > >  static struct expr *
> > > >  >> > > >  expr_clone_cmp(struct expr *expr)
> > > >  >> > > >  {
> > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > >  >> > > >      if (!new->cmp.symbol->width) {
> > > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> > > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> > > >  >> > > >  static struct expr *
> > > >  >> > > >  expr_clone_condition(struct expr *expr)
> > > >  >> > > >  {
> > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
> > > >  >> > > >      return new;
> > > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > > >  >> > > >          return;
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    free(expr->as_name);
> > > >  >> > > > -
> > > >  >> > > >      struct expr *sub;
> > > >  >> > > >
> > > >  >> > > >      switch (expr->type) {
> > > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr
*expr,
> > > > const struct expr_symbol *symbol)
> > > >  >> > > >       * mask sizes. */
> > > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
> > > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > > >  >> > > > -    bool modified = false;
> > > >  >> > > > +    bool has_addr_set = false;
> > > >  >> > > >      /* Linked list over the 'subs' array to quickly skip
> > > > deleted elements,
> > > >  >> > > >       * i.e. the index of the next potentially non-NULL
> > > > element. */
> > > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
> > > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr
*expr,
> > > > const struct expr_symbol *symbol)
> > > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
> > > >  >> > > >      struct expr *sub;
> > > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > > >  >> > > > +        if (sub->as_name) {
> > > >  >> > > > +            has_addr_set = true;
> > > >  >> > > > +        }
> > > >  >> > > >          if (symbol->width) {
> > > >  >> > > >              const unsigned long *sub_mask;
> > > >  >> > > >
> > > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr
*expr,
> > > > const struct expr_symbol *symbol)
> > > >  >> > > >              next[last] = i;
> > > >  >> > > >              last = i;
> > > >  >> > > >          } else {
> > > >  >> > > > +            /* Remove address set reference from the
> > > > duplicate. */
> > > >  >> > > > +            subs[last]->as_name = NULL;
> > > >  >> > > >              expr_destroy(subs[i]);
> > > >  >> > > >              subs[i] = NULL;
> > > >  >> > > > -            modified = true;
> > > >  >> > > >          }
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > > >  >> > > > -        || (!modified && expr->as_name)) {
> > > >  >> > > > +    if (!symbol->width || symbol->level !=
EXPR_L_ORDINAL ||
> > > > has_addr_set) {
> > > >  >> > > >          /* Not a fully maskable field or this expression
is
> > > > tracking an
> > > >  >> > > >           * address set.  Don't try to optimize to
preserve
> > > > address set I-P. */
> > > >  >> > > >          goto done;
> > > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr
*expr,
> > > > const struct expr_symbol *symbol)
> > > >  >> > > >              if (expr_bitmap_intersect_check(a_value,
a_mask,
> > > > b_value, b_mask,
> > > >  >> > > >                                              bit_width)
> > > >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
> > > > bit_width)) {
> > > >  >> > > > -                /* 'a' is the same expression with a
smaller
> > > > mask. */
> > > >  >> > > > +                /* 'a' is the same expression with a
smaller
> > > > mask.
> > > >  >> > > > +                 * Remove address set reference from the
> > > > duplicate. */
> > > >  >> > > > +                a->as_name = NULL;
> > > >  >> > > >                  expr_destroy(subs[j]);
> > > >  >> > > >                  subs[j] = NULL;
> > > >  >> > > > -                modified = true;
> > > >  >> > > >
> > > >  >> > > >                  /* Shorten the path for the next round.
*/
> > > >  >> > > >                  if (last) {
> > > >  >> > > > @@ -2685,12 +2676,6 @@ done:
> > > >  >> > > >          }
> > > >  >> > > >      }
> > > >  >> > > >
> > > >  >> > > > -    if (modified) {
> > > >  >> > > > -        /* Members modified, so untrack address set. */
> > > >  >> > > > -        free(expr->as_name);
> > > >  >> > > > -        expr->as_name = NULL;
> > > >  >> > > > -    }
> > > >  >> > > > -
> > > >  >> > > >      free(next);
> > > >  >> > > >      free(subs);
> > > >  >> > > >      return expr;
> > > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr
*or,
> > > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > > >  >> > > >          struct expr_match *match = expr_match_new(m,
clause,
> > > > n_clauses,
> > > >  >> > > >
 conj_id);
> > > >  >> > > > -        if (or->as_name) {
> > > >  >> > > > +        if (sub->as_name) {
> > > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> > > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
> > > >  >> > > > -            match->as_name = xstrdup(or->as_name);
> > > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
> > > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
> > > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> > > >  >> > > >          }
> > > >  >> > > > diff --git a/tests/ovn-controller.at
> > > > <http://ovn-controller.at> b/tests/ovn-controller.at
> > > > <http://ovn-controller.at>
> > > >  >> > > > index be198e00d..27cec2aec 100644
> > > >  >> > > > --- a/tests/ovn-controller.at <http://ovn-controller.at>
> > > >  >> > > > +++ b/tests/ovn-controller.at <http://ovn-controller.at>
> > > >  >> > > > @@ -1625,7 +1625,9 @@
> > > >
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> > > > actions=lo
> > > >  >> > > >  done
> > > >  >> > > >
> > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [10
> > > >  >> > > > +# First 2 reprocess are due to change from 1 IP in AS to
2.
> > > >  >> > > > +# Last 5 is due to overlap in IP addresses between as1
and
> > as2.
> > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [7
> > > >  >> > > >  ])
> > > >  >> > > >
> > > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > > >  >> > > >  done
> > > >  >> > > >
> > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > >  >> > > > -# In this case the as1 and as2 are merged to a single OR
> > > > expr, so we lose track of
> > > >  >> > > > -# address set information - can't handle deletion
> > incrementally.
> > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [10
> > > >  >> > > > +# First 5 are due to overlap in IP addresses between as1
and
> > as2.
> > > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [7
> > > >  >> > > >  ])
> > > >  >> > > >
> > > >  >> > > >  OVN_CLEANUP([hv1])
> > > >  >> > > > @@ -1817,7 +1819,7 @@
> > > >
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> > > > actions=co
> > > >  >> > > >
> > > >
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > actions=conjunction,2/2)
> > > >  >> > > >  ])
> > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [1
> > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [0
> > > >  >> > > >  ])
> > > >  >> > > >
> > > >  >> > > >  # Delete 2 IPs
> > > >  >> > > > @@ -1837,7 +1839,7 @@
> > > >
priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> > > > actions=co
> > > >  >> > > >
> > > >
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > actions=conjunction,2/2)
> > > >  >> > > >  ])
> > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [1
> > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > $reprocess_count_old))], [0], [0
> > > >  >> > > >  ])
> > > >  >> > > >
> > > >  >> > > >  OVN_CLEANUP([hv1])
> > > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > > >  >> > > >
> > > >  >> > > >  AT_CLEANUP
> > > >  >> > > >  ])
> > > >  >> > > > +
> > > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute
consistency])
> > > >  >> > > > +AT_KEYWORDS([as-i-p])
> > > >  >> > > > +
> > > >  >> > > > +ovn_start
> > > >  >> > > > +
> > > >  >> > > > +net_add n1
> > > >  >> > > > +sim_add hv1
> > > >  >> > > > +as hv1
> > > >  >> > > > +check ovs-vsctl add-br br-phys
> > > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
> > > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > > >  >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > > >  >> > > > +
> > > >  >> > > > +check ovn-nbctl ls-add ls1
> > > >  >> > > > +
> > > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > > >  >> > > > +
> > > >  >> > > > +wait_for_ports_up
> > > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > > >  >> > > > +
> > > >  >> > > > +# Get the OF table numbers
> > > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable
ls_out_acl_eval)
> > > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
> > ls_out_acl_action)
> > > >  >> > > > +
> > > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> > > > external_ids:name=ls1))
> > > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding
tunnel_key
> > > > logical_port=ls1-lp1))
> > > >  >> > > > +
> > > >  >> > > > +ovn-nbctl create address_set name=as1
> > > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
> > > > "ls1-lp1" && ip4.src == $as1' drop
> > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > > > <http://10.0.0.0/24>
> > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > >  >> > > > +
> > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
$8}'
> > > > | sort], [0], [dnl
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
10.0.0.0/24
> > > > <http://10.0.0.0/24>
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +])
> > > >  >> > > > +
> > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > >  >> > > > +
> > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
$8}'
> > > > | sort], [0], [dnl
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
10.0.0.0/24
> > > > <http://10.0.0.0/24>
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +])
> > > >  >> > > > +
> > > >  >> > > > +
> > > >  >> > > > +check ovn-appctl inc-engine/recompute
> > > >  >> > > > +
> > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
$8}'
> > > > | sort], [0], [dnl
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
10.0.0.0/24
> > > > <http://10.0.0.0/24>
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > >  >> > > > +])
> > > >  >> > > > +
> > > >  >> > > > +OVN_CLEANUP([hv1])
> > > >  >> > > > +AT_CLEANUP
> > > >  >> > > > --
> > > >  >> > > > 2.44.0
> > > >  >> > > >
> > > >  >> >
> > > >  >> >
> > > >  >> > Thank you Han,
> > > >  >> >
> > > >  >> > I would like to start the discussion about the possibility of
> > > >  >> > backporting this change. The reason for the backport being
that
> > this
> > > >  >> > change might be beneficial to ovn-kubernetes users, as this
was
> > > >  >> > actually observed in ovn-kubernetes with network policies. I
know
> > that
> > > >  >> > strictly speaking this is not a bug fix, because this
limitation
> > was
> > > >  >> > well documented, however I would like to hear the opinion of
> > others.
> > > >  >>
> > > >  >>
> > > >  >> I have no objections if this improvement is strongly required
for
> > > > released versions, even though it is not a bug. May I ask for which
> > > > releases do you want this to be backported?
> > > >  >
> > > >  >
> > > >  > We have a request for 23.09 and 24.03.
> > > >  >
> > > > Ok. I will wait for input from other maintainers. It will be a
tradeoff
> > > > between risk and performance gains. Mark, Numan and Dumitru, what
do you
> > > > think?
> > > >
> > > > Thanks,
> > > > Han
> > >
> > > I'm OK with the patch being backported. I think our test coverage is
> > > sufficient to uncover problems that may arise from the backport, so
the
> > > risk should be minimal.
> > >
> >
> > Thanks Numan and Mark for confirming, so we got a quorum. I backported
the
> > patch to 24.03 and 23.09.
>
> This patch is using the "ovn-debug" binary which is missing in
> branch-24.03 and branch-23.09.
> Because of this, the test case "AS I-P and recompute consistency" is
> failing.  I backported
> the commit "117573e2d279" to these branches.
>
> While backporting to branch-23.09,  had to move the pipeline stage macros
from
> northd.c to northd.h.  Running the tests in my github repo before pushing
> to upstream branch-23.09.
>

Oh, no. Thanks Numan for fixing! I ran test locally after switching
branches and all were successful, likely because the ovn-debug binary is
already in my environment, so I pushed late at night and see this just now.
Sorry for the trouble.

Regards,
Han

> Numan
>
> >
> > Han
> >
> > > >
> > > >  >>
> > > >  >>
> > > >  >> Thanks,
> > > >  >> Han
> > > >  >>
> > > >  >> >
> > > >  >> > Thanks,
> > > >  >> > Ales
> > > >  >> > --
> > > >  >> >
> > > >  >> > Ales Musil
> > > >  >> >
> > > >  >> > Senior Software Engineer - OVN Core
> > > >  >> >
> > > >  >> > Red Hat EMEA
> > > >  >> >
> > > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
> > > >  >> >
> > > >  >
> > > >  >
> > > >  >
> > > >  > --
> > > >  >
> > > >  > Ales Musil
> > > >  >
> > > >  > Senior Software Engineer - OVN Core
> > > >  >
> > > >  > Red Hat EMEA
> > > >  >
> > > >  > amusil@redhat.com <mailto:amusil@redhat.com>
> > >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Numan Siddique May 10, 2024, 4:42 p.m. UTC | #11
On Fri, May 10, 2024 at 12:39 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Fri, May 10, 2024 at 9:23 AM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > > On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com>
> wrote:
> > > >
> > > > On 5/7/24 02:12, Han Zhou wrote:
> > > > >
> > > > >
> > > > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
> > > > > <mailto:amusil@redhat.com>> wrote:
> > > > >  >
> > > > >  >
> > > > >  >
> > > > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
> > > > > <mailto:hzhou@ovn.org>> wrote:
> > > > >  >>
> > > > >  >>
> > > > >  >>
> > > > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <amusil@redhat.com
> > > > > <mailto:amusil@redhat.com>> wrote:
> > > > >  >> >
> > > > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
> > > > > <mailto:hzhou@ovn.org>> wrote:
> > > > >  >> > >
> > > > >  >> > >
> > > > >  >> > >
> > > > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <amusil@redhat.com
> > > > > <mailto:amusil@redhat.com>> wrote:
> > > > >  >> > > >
> > > > >  >> > > > Instead of tracking address set per struct
> expr_constant_set
> > > > > track it
> > > > >  >> > > > per individual struct expr_constant. This allows more fine
> > > grained
> > > > >  >> > > > control for I-P processing of address sets in controller.
> It
> > > > > helps with
> > > > >  >> > > > scenarios like matching on two address sets in one
> expression
> > > e.g.
> > > > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
> > > removal of
> > > > >  >> > > > individual adress from the set to be incrementally
> processed
> > > > > instead
> > > > >  >> > > > of reprocessing all the flows.
> > > > >  >> > > >
> > > > >  >> > > > This unfortunately doesn't help with the following flows:
> > > > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> > > > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> > > > >  >> > > >
> > > > >  >> > > > The memory impact should be minimal as there is only
> increase
> > > > > of 8 bytes
> > > > >  >> > > > per the struct expr_constant.
> > > > >  >> > > >
> > > > >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > > > <https://issues.redhat.com/browse/FDP-509>
> > > > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
> > > > > <mailto:amusil@redhat.com>>
> > > > >  >> > > > ---
> > > > >  >> > > > v4: Rebase on top of current main.
> > > > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
> > > > > according to suggestion from Han.
> > > > >  >> > >
> > > > >  >> > > Thanks Ales. I updated the commit message for the same, and
> > > > > applied to main branch.
> > > > >  >> > >
> > > > >  >> > > Regards,
> > > > >  >> > > Han
> > > > >  >> > >
> > > > >  >> > > > v3: Rebase on top of current main.
> > > > >  >> > > >     Address comments from Han:
> > > > >  >> > > >     - Adjust the comment for
> "lflow_handle_addr_set_update" to
> > > > > include remaning corner cases.
> > > > >  >> > > >     - Make sure that the flows are consistent between I-P
> and
> > > > > recompute.
> > > > >  >> > > > v2: Rebase on top of current main.
> > > > >  >> > > >     Adjust the comment for I-P optimization.
> > > > >  >> > > > ---
> > > > >  >> > > >  controller/lflow.c      | 11 ++---
> > > > >  >> > > >  include/ovn/actions.h   |  2 +-
> > > > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> > > > >  >> > > >  lib/actions.c           | 20 ++++-----
> > > > >  >> > > >  lib/expr.c              | 99
> > > > > +++++++++++++++++------------------------
> > > > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> | 79
> > > > > +++++++++++++++++++++++++++++---
> > > > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > > > >  >> > > >
> > > > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> > > > >  >> > > > index 760ec0b41..1e05665a1 100644
> > > > >  >> > > > --- a/controller/lflow.c
> > > > >  >> > > > +++ b/controller/lflow.c
> > > > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
> > > > > lflow_ctx_in *l_ctx_in,
> > > > >  >> > > >  }
> > > > >  >> > > >
> > > > >  >> > > >  static bool
> > > > >  >> > > > -as_info_from_expr_const(const char *as_name, const union
> > > > > expr_constant *c,
> > > > >  >> > > > +as_info_from_expr_const(const char *as_name, const struct
> > > > > expr_constant *c,
> > > > >  >> > > >                          struct addrset_info *as_info)
> > > > >  >> > > >  {
> > > > >  >> > > >      as_info->name = as_name;
> > > > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const char
> > > > > *as_name, struct addr_set_diff *as_diff,
> > > > >  >> > > >   *        generated.
> > > > >  >> > > >   *
> > > > >  >> > > >   *      - The sub expression of the address set is
> combined
> > > > > with other sub-
> > > > >  >> > > > - *        expressions/constants, usually because of
> > > > > disjunctions between
> > > > >  >> > > > - *        sub-expressions/constants, e.g.:
> > > > >  >> > > > + *        expressions/constants on different fields,
> e.g.:
> > > > >  >> > > >   *
> > > > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
> > > > >  >> > > > - *          ip.src == {$as1, $as2}
> > > > >  >> > > > - *          ip.src == {$as1, ip1}
> > > > >  >> > > >   *
> > > > >  >> > > > - *        All these could have been split into separate
> > > lflows.
> > > > >  >> > > > + *        This could have been split into separate
> lflows.
> > > > >  >> > > >   *
> > > > >  >> > > >   *      - Conjunctions overlapping between lflows, which
> can
> > > > > be caused by
> > > > >  >> > > >   *        overlapping address sets or same address set
> used
> > > > > by multiple lflows
> > > > >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const
> char
> > > > > *as_name,
> > > > >  >> > > >          if (as_diff->deleted) {
> > > > >  >> > > >              struct addrset_info as_info;
> > > > >  >> > > >              for (size_t i = 0; i <
> > > > > as_diff->deleted->n_values; i++) {
> > > > >  >> > > > -                union expr_constant *c =
> > > > > &as_diff->deleted->values[i];
> > > > >  >> > > > +                struct expr_constant *c =
> > > > > &as_diff->deleted->values[i];
> > > > >  >> > > >                  if (!as_info_from_expr_const(as_name, c,
> > > > > &as_info)) {
> > > > >  >> > > >                      continue;
> > > > >  >> > > >                  }
> > > > >  >> > > > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > > > >  >> > > > index ae0864fdd..88cf4de79 100644
> > > > >  >> > > > --- a/include/ovn/actions.h
> > > > >  >> > > > +++ b/include/ovn/actions.h
> > > > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > > > >  >> > > >  struct ovnact_load {
> > > > >  >> > > >      struct ovnact ovnact;
> > > > >  >> > > >      struct expr_field dst;
> > > > >  >> > > > -    union expr_constant imm;
> > > > >  >> > > > +    struct expr_constant imm;
> > > > >  >> > > >  };
> > > > >  >> > > >
> > > > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > > > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > > > >  >> > > > index c48f82398..e54edb5bf 100644
> > > > >  >> > > > --- a/include/ovn/expr.h
> > > > >  >> > > > +++ b/include/ovn/expr.h
> > > > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum
> lex_type
> > > > > type, enum expr_relop *relop);
> > > > >  >> > > >  struct expr {
> > > > >  >> > > >      struct ovs_list node;       /* In parent EXPR_T_AND
> or
> > > > > EXPR_T_OR if any. */
> > > > >  >> > > >      enum expr_type type;        /* Expression type. */
> > > > >  >> > > > -    char *as_name;              /* Address set name.
> Null if
> > > > > it is not an
> > > > >  >> > > > +    const char *as_name;        /* Address set name.
> Null if
> > > > > it is not an
> > > > >  >> > > >                                     address set. */
> > > > >  >> > > >
> > > > >  >> > > >      union {
> > > > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > > > >  >> > > >  };
> > > > >  >> > > >
> > > > >  >> > > >  /* A string or integer constant (one must know which from
> > > > > context). */
> > > > >  >> > > > -union expr_constant {
> > > > >  >> > > > -    /* Integer constant.
> > > > >  >> > > > -     *
> > > > >  >> > > > -     * The width of a constant isn't always clear, e.g.
> if
> > > > > you write "1",
> > > > >  >> > > > -     * there's no way to tell whether you mean for that
> to be
> > > > > a 1-bit constant
> > > > >  >> > > > -     * or a 128-bit constant or somewhere in between. */
> > > > >  >> > > > -    struct {
> > > > >  >> > > > -        union mf_subvalue value;
> > > > >  >> > > > -        union mf_subvalue mask; /* Only initialized if
> > > > > 'masked'. */
> > > > >  >> > > > -        bool masked;
> > > > >  >> > > > -
> > > > >  >> > > > -        enum lex_format format; /* From the constant's
> > > > > lex_token. */
> > > > >  >> > > > -    };
> > > > >  >> > > > +struct expr_constant {
> > > > >  >> > > > +    const char *as_name;
> > > > >  >> > > >
> > > > >  >> > > > -    /* Null-terminated string constant. */
> > > > >  >> > > > -    char *string;
> > > > >  >> > > > +    union {
> > > > >  >> > > > +        /* Integer constant.
> > > > >  >> > > > +         *
> > > > >  >> > > > +         * The width of a constant isn't always clear,
> e.g.
> > > > > if you write "1",
> > > > >  >> > > > +         * there's no way to tell whether you mean for
> that
> > > > > to be a 1-bit
> > > > >  >> > > > +         * constant or a 128-bit constant or somewhere in
> > > > > between. */
> > > > >  >> > > > +        struct {
> > > > >  >> > > > +            union mf_subvalue value;
> > > > >  >> > > > +            union mf_subvalue mask; /* Only initialized
> if
> > > > > 'masked'. */
> > > > >  >> > > > +            bool masked;
> > > > >  >> > > > +
> > > > >  >> > > > +            enum lex_format format; /* From the
> constant's
> > > > > lex_token. */
> > > > >  >> > > > +        };
> > > > >  >> > > > +
> > > > >  >> > > > +        /* Null-terminated string constant. */
> > > > >  >> > > > +        char *string;
> > > > >  >> > > > +    };
> > > > >  >> > > >  };
> > > > >  >> > > >
> > > > >  >> > > >  bool expr_constant_parse(struct lexer *,
> > > > >  >> > > >                           const struct expr_field *,
> > > > >  >> > > > -                         union expr_constant *);
> > > > >  >> > > > -void expr_constant_format(const union expr_constant *,
> > > > >  >> > > > +                         struct expr_constant *);
> > > > >  >> > > > +void expr_constant_format(const struct expr_constant *,
> > > > >  >> > > >                            enum expr_constant_type,
> struct ds
> > > *);
> > > > >  >> > > > -void expr_constant_destroy(const union expr_constant *,
> > > > >  >> > > > +void expr_constant_destroy(const struct expr_constant *,
> > > > >  >> > > >                             enum expr_constant_type);
> > > > >  >> > > >
> > > > >  >> > > >  /* A collection of "union expr_constant"s of the same
> type.
> > > */
> > > > >  >> > > >  struct expr_constant_set {
> > > > >  >> > > > -    union expr_constant *values;  /* Constants. */
> > > > >  >> > > > +    struct expr_constant *values; /* Constants. */
> > > > >  >> > > >      size_t n_values;              /* Number of
> constants. */
> > > > >  >> > > >      enum expr_constant_type type; /* Type of the
> constants.
> > > */
> > > > >  >> > > >      bool in_curlies;              /* Whether the
> constants
> > > > > were in {}. */
> > > > >  >> > > > -    char *as_name;                /* Name of an address
> set.
> > > > > It is NULL if not
> > > > >  >> > > > -                                     an address set. */
> > > > >  >> > > >  };
> > > > >  >> > > >
> > > > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> > > > > expr_constant_set *);
> > > > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
> > > > >  >> > > > index 94751d059..cff4f9e3c 100644
> > > > >  >> > > > --- a/lib/actions.c
> > > > >  >> > > > +++ b/lib/actions.c
> > > > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct ovnact_load
> > > *load,
> > > > >  >> > > >              const struct ovnact_encode_params *ep,
> > > > >  >> > > >              struct ofpbuf *ofpacts)
> > > > >  >> > > >  {
> > > > >  >> > > > -    const union expr_constant *c = &load->imm;
> > > > >  >> > > > +    const struct expr_constant *c = &load->imm;
> > > > >  >> > > >      struct mf_subfield dst =
> expr_resolve_field(&load->dst);
> > > > >  >> > > >      struct ofpact_set_field *sf =
> > > > > ofpact_put_set_field(ofpacts, dst.field,
> > > > >  >> > > >
> NULL,
> > > > > NULL);
> > > > >  >> > > > @@ -2077,7 +2077,7 @@
> > > > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> > > > >  >> > > >          /* All empty_lb_backends fields are of type
> 'str' */
> > > > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> > > > >  >> > > >
> > > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > > >  >> > > >          size_t size = strlen(c->string);
> > > > >  >> > > >          struct controller_event_opt_header hdr =
> > > > >  >> > > >              (struct controller_event_opt_header) {
> > > > >  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> > > > > action_context *ctx,
> > > > >  >> > > >  {
> > > > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
> > > > >  >> > > >          const struct ovnact_gen_option *o = &options[i];
> > > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > > >  >> > > >          struct sockaddr_storage ss;
> > > > >  >> > > >          struct uuid uuid;
> > > > >  >> > > >
> > > > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const
> struct
> > > > > ovnact_gen_option *o,
> > > > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > > > >  >> > > >      opt_header[0] = o->option->code;
> > > > >  >> > > >
> > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > >  >> > > >      size_t n_values = o->value.n_values;
> > > > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
> > > > >  >> > > >          !strcmp(o->option->type, "uint8")) {
> > > > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const
> struct
> > > > > ovnact_gen_option *o,
> > > > >  >> > > >                           struct ofpbuf *ofpacts)
> > > > >  >> > > >  {
> > > > >  >> > > >      struct dhcpv6_opt_header *opt =
> > > > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
> > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > >  >> > > >      size_t n_values = o->value.n_values;
> > > > >  >> > > >      size_t size;
> > > > >  >> > > >
> > > > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > > ovnact_put_opts *pdo,
> > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > > DHCP_OPT_BOOTFILE_CODE);
> > > > >  >> > > >      if (boot_opt) {
> > > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
> 2);
> > > > >  >> > > > -        const union expr_constant *c =
> > > boot_opt->value.values;
> > > > >  >> > > > +        const struct expr_constant *c =
> > > boot_opt->value.values;
> > > > >  >> > > >          opt_header[0] = boot_opt->option->code;
> > > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > > ovnact_put_opts *pdo,
> > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > > DHCP_OPT_BOOTFILE_ALT_CODE);
> > > > >  >> > > >      if (boot_alt_opt) {
> > > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
> 2);
> > > > >  >> > > > -        const union expr_constant *c =
> > > > > boot_alt_opt->value.values;
> > > > >  >> > > > +        const struct expr_constant *c =
> > > > > boot_alt_opt->value.values;
> > > > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
> > > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const struct
> > > > > ovnact_put_opts *pdo,
> > > > >  >> > > >          pdo->options, pdo->n_options,
> > > DHCP_OPT_NEXT_SERVER_CODE);
> > > > >  >> > > >      if (next_server_opt) {
> > > > >  >> > > >          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
> 2);
> > > > >  >> > > > -        const union expr_constant *c =
> > > > > next_server_opt->value.values;
> > > > >  >> > > > +        const struct expr_constant *c =
> > > > > next_server_opt->value.values;
> > > > >  >> > > >          opt_header[0] = next_server_opt->option->code;
> > > > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
> > > > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
> > > sizeof(ovs_be32));
> > > > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
> > > > > action_context *ctx, const struct expr_field *dst,
> > > > >  >> > > >      /* Let's validate the options. */
> > > > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> > > > >  >> > > >          const struct ovnact_gen_option *o =
> &po->options[i];
> > > > >  >> > > > -        const union expr_constant *c = o->value.values;
> > > > >  >> > > > +        const struct expr_constant *c = o->value.values;
> > > > >  >> > > >          if (o->value.n_values > 1) {
> > > > >  >> > > >              lexer_error(ctx->lexer, "Invalid value for
> \"%s\"
> > > > > option",
> > > > >  >> > > >                          o->option->name);
> > > > >  >> > > > @@ -3490,7 +3490,7 @@ static void
> > > > >  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option
> *o,
> > > > >  >> > > >                          struct ofpbuf *ofpacts, ptrdiff_t
> > > > > ra_offset)
> > > > >  >> > > >  {
> > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > >  >> > > >
> > > > >  >> > > >      switch (o->option->code) {
> > > > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
> > > > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
> > > > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
> > > > >  >> > > > --- a/lib/expr.c
> > > > >  >> > > > +++ b/lib/expr.c
> > > > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type,
> struct
> > > > > expr *a, struct expr *b)
> > > > >  >> > > >          } else {
> > > > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
> > > > >  >> > > >          }
> > > > >  >> > > > -        free(a->as_name);
> > > > >  >> > > >          a->as_name = NULL;
> > > > >  >> > > >          return a;
> > > > >  >> > > >      } else if (b->type == type) {
> > > > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
> > > > >  >> > > > -        free(b->as_name);
> > > > >  >> > > >          b->as_name = NULL;
> > > > >  >> > > >          return b;
> > > > >  >> > > >      } else {
> > > > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
> > > > > expr_context *, struct expr_field *);
> > > > >  >> > > >
> > > > >  >> > > >  static struct expr *
> > > > >  >> > > >  make_cmp__(const struct expr_field *f, enum expr_relop r,
> > > > >  >> > > > -             const union expr_constant *c)
> > > > >  >> > > > +             const struct expr_constant *c)
> > > > >  >> > > >  {
> > > > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
> > > > >  >> > > >      e->type = EXPR_T_CMP;
> > > > >  >> > > >      e->cmp.symbol = f->symbol;
> > > > >  >> > > >      e->cmp.relop = r;
> > > > >  >> > > > +    e->as_name = c->as_name;
> > > > >  >> > > >      if (f->symbol->width) {
> > > > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > > > >  >> > > >                       &e->cmp.value, sizeof e->cmp.value,
> > > f->ofs,
> > > > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field *f,
> > > > > enum expr_relop r,
> > > > >  >> > > >
> > > > >  >> > > >  /* Returns the minimum reasonable width for integer
> constant
> > > > > 'c'. */
> > > > >  >> > > >  static int
> > > > >  >> > > > -expr_constant_width(const union expr_constant *c)
> > > > >  >> > > > +expr_constant_width(const struct expr_constant *c)
> > > > >  >> > > >  {
> > > > >  >> > > >      if (c->masked) {
> > > > >  >> > > >          return mf_subvalue_width(&c->mask);
> > > > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > > > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
> > > EXPR_T_AND,
> > > > >  >> > > >                           e, make_cmp__(f, r,
> > > &cs->values[i]));
> > > > >  >> > > >      }
> > > > >  >> > > > -    /* Track address set */
> > > > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
> > > cs->as_name) {
> > > > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
> > > > >  >> > > > -    }
> > > > >  >> > > > +
> > > > >  >> > > >  exit:
> > > > >  >> > > >      expr_constant_set_destroy(cs);
> > > > >  >> > > >      return e;
> > > > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct expr_context
> > > > > *ctx, struct expr_constant_set *cs,
> > > > >  >> > > >          }
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    struct expr_constant_set *addr_sets
> > > > >  >> > > > -        = (ctx->addr_sets
> > > > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
> > > ctx->lexer->token.s)
> > > > >  >> > > > -           : NULL);
> > > > >  >> > > > -    if (!addr_sets) {
> > > > >  >> > > > +    struct shash_node *node = ctx->addr_sets
> > > > >  >> > > > +                              ?
> shash_find(ctx->addr_sets,
> > > > > ctx->lexer->token.s)
> > > > >  >> > > > +                              : NULL;
> > > > >  >> > > > +    if (!node) {
> > > > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting
> address set
> > > > > name");
> > > > >  >> > > >          return false;
> > > > >  >> > > >      }
> > > > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct expr_context
> > > > > *ctx, struct expr_constant_set *cs,
> > > > >  >> > > >          return false;
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    if (!cs->n_values) {
> > > > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > > > >  >> > > > -    }
> > > > >  >> > > > -
> > > > >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
> > > > >  >> > > >      size_t n_values = cs->n_values + addr_sets->n_values;
> > > > >  >> > > >      if (n_values >= *allocated_values) {
> > > > >  >> > > >          cs->values = xrealloc(cs->values, n_values *
> sizeof
> > > > > *cs->values);
> > > > >  >> > > >          *allocated_values = n_values;
> > > > >  >> > > >      }
> > > > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > > > >  >> > > > -        cs->values[cs->n_values++] =
> addr_sets->values[i];
> > > > >  >> > > > +        struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > > +        *c = addr_sets->values[i];
> > > > >  >> > > > +        c->as_name = node->name;
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > >      return true;
> > > > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct expr_context
> *ctx,
> > > > > struct expr_constant_set *cs,
> > > > >  >> > > >          *allocated_values = n_values;
> > > > >  >> > > >      }
> > > > >  >> > > >      for (size_t i = 0; i < port_group->n_values; i++) {
> > > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > > >  >> > > > -            xstrdup(port_group->values[i].string);
> > > > >  >> > > > +        struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > > +        c->string =
> xstrdup(port_group->values[i].string);
> > > > >  >> > > > +        c->as_name = NULL;
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > >      return true;
> > > > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context
> *ctx,
> > > > > struct expr_constant_set *cs,
> > > > >  >> > > >                                  sizeof *cs->values);
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    /* Combining other values to the constant set that is
> > > > > tracking an
> > > > >  >> > > > -     * address set, so untrack it. */
> > > > >  >> > > > -    free(cs->as_name);
> > > > >  >> > > > -    cs->as_name = NULL;
> > > > >  >> > > > -
> > > > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > > > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded template.");
> > > > >  >> > > >          return false;
> > > > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context
> *ctx,
> > > > > struct expr_constant_set *cs,
> > > > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
> > > EXPR_C_STRING)) {
> > > > >  >> > > >              return false;
> > > > >  >> > > >          }
> > > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > > > xstrdup(ctx->lexer->token.s);
> > > > >  >> > > > +        struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> > > > >  >> > > > +        c->as_name = NULL;
> > > > >  >> > > >          lexer_get(ctx->lexer);
> > > > >  >> > > >          return true;
> > > > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
> > > > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct expr_context
> *ctx,
> > > > > struct expr_constant_set *cs,
> > > > >  >> > > >              return false;
> > > > >  >> > > >          }
> > > > >  >> > > >
> > > > >  >> > > > -        union expr_constant *c =
> &cs->values[cs->n_values++];
> > > > >  >> > > > +        struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > >          c->value = ctx->lexer->token.value;
> > > > >  >> > > >          c->format = ctx->lexer->token.format;
> > > > >  >> > > >          c->masked = ctx->lexer->token.type ==
> > > > > LEX_T_MASKED_INTEGER;
> > > > >  >> > > >          if (c->masked) {
> > > > >  >> > > >              c->mask = ctx->lexer->token.mask;
> > > > >  >> > > >          }
> > > > >  >> > > > +        c->as_name = NULL;
> > > > >  >> > > >          lexer_get(ctx->lexer);
> > > > >  >> > > >          return true;
> > > > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO) {
> > > > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct expr_context
> > > > > *ctx, struct expr_constant_set *cs)
> > > > >  >> > > >   * indeterminate. */
> > > > >  >> > > >  bool
> > > > >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
> > > > > expr_field *f,
> > > > >  >> > > > -                    union expr_constant *c)
> > > > >  >> > > > +                    struct expr_constant *c)
> > > > >  >> > > >  {
> > > > >  >> > > >      if (lexer->error) {
> > > > >  >> > > >          return false;
> > > > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer
> *lexer,
> > > > > const struct expr_field *f,
> > > > >  >> > > >  /* Appends to 's' a re-parseable representation of
> constant
> > > > > 'c' with the given
> > > > >  >> > > >   * 'type'. */
> > > > >  >> > > >  void
> > > > >  >> > > > -expr_constant_format(const union expr_constant *c,
> > > > >  >> > > > +expr_constant_format(const struct expr_constant *c,
> > > > >  >> > > >                       enum expr_constant_type type,
> struct ds
> > > *s)
> > > > >  >> > > >  {
> > > > >  >> > > >      if (type == EXPR_C_STRING) {
> > > > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> > > > > expr_constant *c,
> > > > >  >> > > >   *
> > > > >  >> > > >   * Does not free(c). */
> > > > >  >> > > >  void
> > > > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
> > > > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
> > > > >  >> > > >                        enum expr_constant_type type)
> > > > >  >> > > >  {
> > > > >  >> > > >      if (c && type == EXPR_C_STRING) {
> > > > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const
> struct
> > > > > expr_constant_set *cs, struct ds *s)
> > > > >  >> > > >          ds_put_char(s, '{');
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    for (const union expr_constant *c = cs->values;
> > > > >  >> > > > +    for (const struct expr_constant *c = cs->values;
> > > > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
> > > > >  >> > > >          if (c != cs->values) {
> > > > >  >> > > >              ds_put_cstr(s, ", ");
> > > > >  >> > > > @@ -1067,15 +1060,14 @@ expr_constant_set_destroy(struct
> > > > > expr_constant_set *cs)
> > > > >  >> > > >              }
> > > > >  >> > > >          }
> > > > >  >> > > >          free(cs->values);
> > > > >  >> > > > -        free(cs->as_name);
> > > > >  >> > > >      }
> > > > >  >> > > >  }
> > > > >  >> > > >
> > > > >  >> > > >  static int
> > > > >  >> > > >  compare_expr_constant_integer_cb(const void *a_, const
> void
> > > *b_)
> > > > >  >> > > >  {
> > > > >  >> > > > -    const union expr_constant *a = a_;
> > > > >  >> > > > -    const union expr_constant *b = b_;
> > > > >  >> > > > +    const struct expr_constant *a = a_;
> > > > >  >> > > > +    const struct expr_constant *b = b_;
> > > > >  >> > > >
> > > > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof
> a->value);
> > > > >  >> > > >      if (d) {
> > > > >  >> > > > @@ -1114,7 +1106,7 @@
> expr_constant_set_create_integers(const
> > > > > char *const *values, size_t n_values)
> > > > >  >> > > >              VLOG_WARN("Invalid constant set entry: '%s',
> > > > > token type: %d",
> > > > >  >> > > >                        values[i], lex.token.type);
> > > > >  >> > > >          } else {
> > > > >  >> > > > -            union expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > > +            struct expr_constant *c =
> > > > > &cs->values[cs->n_values++];
> > > > >  >> > > >              c->value = lex.token.value;
> > > > >  >> > > >              c->format = lex.token.format;
> > > > >  >> > > >              c->masked = lex.token.type ==
> > > LEX_T_MASKED_INTEGER;
> > > > >  >> > > > @@ -1135,7 +1127,7 @@
> expr_constant_set_create_integers(const
> > > > > char *const *values, size_t n_values)
> > > > >  >> > > >
> > > > >  >> > > >  static void
> > > > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set
> **p_cs,
> > > > >  >> > > > -                            union expr_constant *c,
> size_t
> > > > > *allocated)
> > > > >  >> > > > +                            struct expr_constant *c,
> size_t
> > > > > *allocated)
> > > > >  >> > > >  {
> > > > >  >> > > >      struct expr_constant_set *cs = *p_cs;
> > > > >  >> > > >      if (!cs) {
> > > > >  >> > > > @@ -1246,7 +1238,7 @@ expr_const_sets_add_strings(struct
> shash
> > > > > *const_sets, const char *name,
> > > > >  >> > > >                          values[i], name);
> > > > >  >> > > >              continue;
> > > > >  >> > > >          }
> > > > >  >> > > > -        union expr_constant *c =
> &cs->values[cs->n_values++];
> > > > >  >> > > > +        struct expr_constant *c =
> > > &cs->values[cs->n_values++];
> > > > >  >> > > >          c->string = xstrdup(values[i]);
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct
> expr_context
> > > > > *ctx, bool *atomic)
> > > > >  >> > > >
> > > > >  >> > > >              *atomic = true;
> > > > >  >> > > >
> > > > >  >> > > > -            union expr_constant *cst = xzalloc(sizeof
> *cst);
> > > > >  >> > > > +            struct expr_constant *cst = xzalloc(sizeof
> *cst);
> > > > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
> > > > >  >> > > >              cst->masked = false;
> > > > >  >> > > >
> > > > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct
> expr_context
> > > > > *ctx, bool *atomic)
> > > > >  >> > > >              c.values = cst;
> > > > >  >> > > >              c.n_values = 1;
> > > > >  >> > > >              c.in_curlies = false;
> > > > >  >> > > > -            c.as_name = NULL;
> > > > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > > > >  >> > > >          } else if (parse_relop(ctx, &r) &&
> > > > > parse_constant_set(ctx, &c)) {
> > > > >  >> > > >              return make_cmp(ctx, &f, r, &c);
> > > > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
> > > *symtab)
> > > > >  >> > > >  static struct expr *
> > > > >  >> > > >  expr_clone_cmp(struct expr *expr)
> > > > >  >> > > >  {
> > > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > > >  >> > > >      if (!new->cmp.symbol->width) {
> > > > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> > > > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr *expr)
> > > > >  >> > > >  static struct expr *
> > > > >  >> > > >  expr_clone_condition(struct expr *expr)
> > > > >  >> > > >  {
> > > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
> > > > >  >> > > >      return new;
> > > > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > > > >  >> > > >          return;
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    free(expr->as_name);
> > > > >  >> > > > -
> > > > >  >> > > >      struct expr *sub;
> > > > >  >> > > >
> > > > >  >> > > >      switch (expr->type) {
> > > > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr
> *expr,
> > > > > const struct expr_symbol *symbol)
> > > > >  >> > > >       * mask sizes. */
> > > > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
> > > > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > > > >  >> > > > -    bool modified = false;
> > > > >  >> > > > +    bool has_addr_set = false;
> > > > >  >> > > >      /* Linked list over the 'subs' array to quickly skip
> > > > > deleted elements,
> > > > >  >> > > >       * i.e. the index of the next potentially non-NULL
> > > > > element. */
> > > > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
> > > > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr
> *expr,
> > > > > const struct expr_symbol *symbol)
> > > > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
> > > > >  >> > > >      struct expr *sub;
> > > > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > > > >  >> > > > +        if (sub->as_name) {
> > > > >  >> > > > +            has_addr_set = true;
> > > > >  >> > > > +        }
> > > > >  >> > > >          if (symbol->width) {
> > > > >  >> > > >              const unsigned long *sub_mask;
> > > > >  >> > > >
> > > > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr
> *expr,
> > > > > const struct expr_symbol *symbol)
> > > > >  >> > > >              next[last] = i;
> > > > >  >> > > >              last = i;
> > > > >  >> > > >          } else {
> > > > >  >> > > > +            /* Remove address set reference from the
> > > > > duplicate. */
> > > > >  >> > > > +            subs[last]->as_name = NULL;
> > > > >  >> > > >              expr_destroy(subs[i]);
> > > > >  >> > > >              subs[i] = NULL;
> > > > >  >> > > > -            modified = true;
> > > > >  >> > > >          }
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
> > > > >  >> > > > -        || (!modified && expr->as_name)) {
> > > > >  >> > > > +    if (!symbol->width || symbol->level !=
> EXPR_L_ORDINAL ||
> > > > > has_addr_set) {
> > > > >  >> > > >          /* Not a fully maskable field or this expression
> is
> > > > > tracking an
> > > > >  >> > > >           * address set.  Don't try to optimize to
> preserve
> > > > > address set I-P. */
> > > > >  >> > > >          goto done;
> > > > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr
> *expr,
> > > > > const struct expr_symbol *symbol)
> > > > >  >> > > >              if (expr_bitmap_intersect_check(a_value,
> a_mask,
> > > > > b_value, b_mask,
> > > > >  >> > > >                                              bit_width)
> > > > >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
> > > > > bit_width)) {
> > > > >  >> > > > -                /* 'a' is the same expression with a
> smaller
> > > > > mask. */
> > > > >  >> > > > +                /* 'a' is the same expression with a
> smaller
> > > > > mask.
> > > > >  >> > > > +                 * Remove address set reference from the
> > > > > duplicate. */
> > > > >  >> > > > +                a->as_name = NULL;
> > > > >  >> > > >                  expr_destroy(subs[j]);
> > > > >  >> > > >                  subs[j] = NULL;
> > > > >  >> > > > -                modified = true;
> > > > >  >> > > >
> > > > >  >> > > >                  /* Shorten the path for the next round.
> */
> > > > >  >> > > >                  if (last) {
> > > > >  >> > > > @@ -2685,12 +2676,6 @@ done:
> > > > >  >> > > >          }
> > > > >  >> > > >      }
> > > > >  >> > > >
> > > > >  >> > > > -    if (modified) {
> > > > >  >> > > > -        /* Members modified, so untrack address set. */
> > > > >  >> > > > -        free(expr->as_name);
> > > > >  >> > > > -        expr->as_name = NULL;
> > > > >  >> > > > -    }
> > > > >  >> > > > -
> > > > >  >> > > >      free(next);
> > > > >  >> > > >      free(subs);
> > > > >  >> > > >      return expr;
> > > > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct expr
> *or,
> > > > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > > > >  >> > > >          struct expr_match *match = expr_match_new(m,
> clause,
> > > > > n_clauses,
> > > > >  >> > > >
>  conj_id);
> > > > >  >> > > > -        if (or->as_name) {
> > > > >  >> > > > +        if (sub->as_name) {
> > > > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> > > > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
> > > > >  >> > > > -            match->as_name = xstrdup(or->as_name);
> > > > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
> > > > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
> > > > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> > > > >  >> > > >          }
> > > > >  >> > > > diff --git a/tests/ovn-controller.at
> > > > > <http://ovn-controller.at> b/tests/ovn-controller.at
> > > > > <http://ovn-controller.at>
> > > > >  >> > > > index be198e00d..27cec2aec 100644
> > > > >  >> > > > --- a/tests/ovn-controller.at <http://ovn-controller.at>
> > > > >  >> > > > +++ b/tests/ovn-controller.at <http://ovn-controller.at>
> > > > >  >> > > > @@ -1625,7 +1625,9 @@
> > > > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> > > > > actions=lo
> > > > >  >> > > >  done
> > > > >  >> > > >
> > > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [10
> > > > >  >> > > > +# First 2 reprocess are due to change from 1 IP in AS to
> 2.
> > > > >  >> > > > +# Last 5 is due to overlap in IP addresses between as1
> and
> > > as2.
> > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [7
> > > > >  >> > > >  ])
> > > > >  >> > > >
> > > > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > > > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > > > >  >> > > >  done
> > > > >  >> > > >
> > > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > >  >> > > > -# In this case the as1 and as2 are merged to a single OR
> > > > > expr, so we lose track of
> > > > >  >> > > > -# address set information - can't handle deletion
> > > incrementally.
> > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [10
> > > > >  >> > > > +# First 5 are due to overlap in IP addresses between as1
> and
> > > as2.
> > > > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [7
> > > > >  >> > > >  ])
> > > > >  >> > > >
> > > > >  >> > > >  OVN_CLEANUP([hv1])
> > > > >  >> > > > @@ -1817,7 +1819,7 @@
> > > > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> > > > > actions=co
> > > > >  >> > > >
> > > > >
> > > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > > actions=conjunction,2/2)
> > > > >  >> > > >  ])
> > > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [1
> > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [0
> > > > >  >> > > >  ])
> > > > >  >> > > >
> > > > >  >> > > >  # Delete 2 IPs
> > > > >  >> > > > @@ -1837,7 +1839,7 @@
> > > > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> > > > > actions=co
> > > > >  >> > > >
> > > > >
> > > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > > actions=conjunction,2/2)
> > > > >  >> > > >  ])
> > > > >  >> > > >  reprocess_count_new=$(read_counter consider_logical_flow)
> > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [1
> > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > $reprocess_count_old))], [0], [0
> > > > >  >> > > >  ])
> > > > >  >> > > >
> > > > >  >> > > >  OVN_CLEANUP([hv1])
> > > > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > > > >  >> > > >
> > > > >  >> > > >  AT_CLEANUP
> > > > >  >> > > >  ])
> > > > >  >> > > > +
> > > > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute
> consistency])
> > > > >  >> > > > +AT_KEYWORDS([as-i-p])
> > > > >  >> > > > +
> > > > >  >> > > > +ovn_start
> > > > >  >> > > > +
> > > > >  >> > > > +net_add n1
> > > > >  >> > > > +sim_add hv1
> > > > >  >> > > > +as hv1
> > > > >  >> > > > +check ovs-vsctl add-br br-phys
> > > > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
> > > > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > > > >  >> > > > +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
> > > > >  >> > > > +
> > > > >  >> > > > +check ovn-nbctl ls-add ls1
> > > > >  >> > > > +
> > > > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > > > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > > > >  >> > > > +
> > > > >  >> > > > +wait_for_ports_up
> > > > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > > > >  >> > > > +
> > > > >  >> > > > +# Get the OF table numbers
> > > > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable
> ls_out_acl_eval)
> > > > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
> > > ls_out_acl_action)
> > > > >  >> > > > +
> > > > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath tunnel_key
> > > > > external_ids:name=ls1))
> > > > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding
> tunnel_key
> > > > > logical_port=ls1-lp1))
> > > > >  >> > > > +
> > > > >  >> > > > +ovn-nbctl create address_set name=as1
> > > > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
> > > > > "ls1-lp1" && ip4.src == $as1' drop
> > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
> > > > > <http://10.0.0.0/24>
> > > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > > >  >> > > > +
> > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
> $8}'
> > > > > | sort], [0], [dnl
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> 10.0.0.0/24
> > > > > <http://10.0.0.0/24>
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +])
> > > > >  >> > > > +
> > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > > >  >> > > > +
> > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
> $8}'
> > > > > | sort], [0], [dnl
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> 10.0.0.0/24
> > > > > <http://10.0.0.0/24>
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +])
> > > > >  >> > > > +
> > > > >  >> > > > +
> > > > >  >> > > > +check ovn-appctl inc-engine/recompute
> > > > >  >> > > > +
> > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7,
> $8}'
> > > > > | sort], [0], [dnl
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> 10.0.0.0/24
> > > > > <http://10.0.0.0/24>
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > >  >> > > > +])
> > > > >  >> > > > +
> > > > >  >> > > > +OVN_CLEANUP([hv1])
> > > > >  >> > > > +AT_CLEANUP
> > > > >  >> > > > --
> > > > >  >> > > > 2.44.0
> > > > >  >> > > >
> > > > >  >> >
> > > > >  >> >
> > > > >  >> > Thank you Han,
> > > > >  >> >
> > > > >  >> > I would like to start the discussion about the possibility of
> > > > >  >> > backporting this change. The reason for the backport being
> that
> > > this
> > > > >  >> > change might be beneficial to ovn-kubernetes users, as this
> was
> > > > >  >> > actually observed in ovn-kubernetes with network policies. I
> know
> > > that
> > > > >  >> > strictly speaking this is not a bug fix, because this
> limitation
> > > was
> > > > >  >> > well documented, however I would like to hear the opinion of
> > > others.
> > > > >  >>
> > > > >  >>
> > > > >  >> I have no objections if this improvement is strongly required
> for
> > > > > released versions, even though it is not a bug. May I ask for which
> > > > > releases do you want this to be backported?
> > > > >  >
> > > > >  >
> > > > >  > We have a request for 23.09 and 24.03.
> > > > >  >
> > > > > Ok. I will wait for input from other maintainers. It will be a
> tradeoff
> > > > > between risk and performance gains. Mark, Numan and Dumitru, what
> do you
> > > > > think?
> > > > >
> > > > > Thanks,
> > > > > Han
> > > >
> > > > I'm OK with the patch being backported. I think our test coverage is
> > > > sufficient to uncover problems that may arise from the backport, so
> the
> > > > risk should be minimal.
> > > >
> > >
> > > Thanks Numan and Mark for confirming, so we got a quorum. I backported
> the
> > > patch to 24.03 and 23.09.
> >
> > This patch is using the "ovn-debug" binary which is missing in
> > branch-24.03 and branch-23.09.
> > Because of this, the test case "AS I-P and recompute consistency" is
> > failing.  I backported
> > the commit "117573e2d279" to these branches.
> >
> > While backporting to branch-23.09,  had to move the pipeline stage macros
> from
> > northd.c to northd.h.  Running the tests in my github repo before pushing
> > to upstream branch-23.09.
> >
>
> Oh, no. Thanks Numan for fixing! I ran test locally after switching
> branches and all were successful, likely because the ovn-debug binary is
> already in my environment, so I pushed late at night and see this just now.
> Sorry for the trouble.
>

No worries.  I passed for me locally too until I deleted the binary.

Numan

> Regards,
> Han
>
> > Numan
> >
> > >
> > > Han
> > >
> > > > >
> > > > >  >>
> > > > >  >>
> > > > >  >> Thanks,
> > > > >  >> Han
> > > > >  >>
> > > > >  >> >
> > > > >  >> > Thanks,
> > > > >  >> > Ales
> > > > >  >> > --
> > > > >  >> >
> > > > >  >> > Ales Musil
> > > > >  >> >
> > > > >  >> > Senior Software Engineer - OVN Core
> > > > >  >> >
> > > > >  >> > Red Hat EMEA
> > > > >  >> >
> > > > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
> > > > >  >> >
> > > > >  >
> > > > >  >
> > > > >  >
> > > > >  > --
> > > > >  >
> > > > >  > Ales Musil
> > > > >  >
> > > > >  > Senior Software Engineer - OVN Core
> > > > >  >
> > > > >  > Red Hat EMEA
> > > > >  >
> > > > >  > amusil@redhat.com <mailto:amusil@redhat.com>
> > > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Sri kor May 12, 2024, 9:17 p.m. UTC | #12
Hi @numans@ovn.org <numans@ovn.org> @Ales Musil <amusil@redhat.com> ,
    Currently we are on OVN 23.09. We are facing the following syntax error
with the address_set.From the diff, looks like you address this fix. Is
this something expected?

*2024-05-11T17:35:28.694Z|153935|lflow|WARN|error parsing match "reg0[7] ==
1 && ((ip4.src ==
$a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
&& (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
<= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
@a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c)":
Syntax error at
`$a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a'
expecting address set name.*


*Here are the transaction details .*

*[{Op:insert Table:ACL Row:map[action:allow-related direction:to-lport
external_ids:{GoMap:map[crusoe_fw_rule_id:770b6929-88d1-4659-ba3b-ed84ccfd71a0
crusoe_vpc_id:0ea55446-0df6-4358-8442-56893854004c
customer_id:781ac228-38ae-4f04-9cc1-707543df50c8]} log:false match:(ip4.src
==
$a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
&& (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
<= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
@a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c
name:{GoSet:[a_781ac228_38ae_4f04_9cc1_707543df50c8_acl_770b6929_88d1_4659_b]}
priority:2000] Rows:[] Columns:[] Mutations:[] Timeout:<nil> Where:[]
Until: Durable:<nil> Comment:<nil> Lock:<nil>
UUIDName:_641d4ca1_7596_4004_b577_0f34e0bab87d} {Op:mutate Table:Port_Group
Row:map[] Rows:[] Columns:[] Mutations:[{Column:acls Mutator:insert
Value:{GoSet:[{GoUUID:_641d4ca1_7596_4004_b577_0f34e0bab87d}]}}]
Timeout:<nil> Where:[where column _uuid ==
{00383922-81ce-4491-9115-6ea2dfc79aea}] Until: Durable:<nil> Comment:<nil>
Lock:<nil> UUIDName:} {Op:mutate Table:NB_Global Row:map[] Rows:[]
Columns:[] Mutations:[{Column:nb_cfg Mutator:+= Value:1}] Timeout:<nil>
Where:[where column _uuid == {be2921c1-99ae-423b-a69c-a43e67d8424e}] Until:
Durable:<nil> Comment:<nil> Lock:<nil> UUIDName:} {Op:select
Table:NB_Global Row:map[] Rows:[] Columns:[nb_cfg] Mutations:[]
Timeout:<nil> Where:[] Until: Durable:<nil> Comment:<nil> Lock:<nil>
UUIDName:}]*


*Thanks*

*Srini*

On Fri, May 10, 2024 at 9:43 AM Numan Siddique <numans@ovn.org> wrote:

> On Fri, May 10, 2024 at 12:39 PM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Fri, May 10, 2024 at 9:23 AM Numan Siddique <numans@ovn.org> wrote:
> > >
> > > On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
> > > >
> > > > On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com>
> > wrote:
> > > > >
> > > > > On 5/7/24 02:12, Han Zhou wrote:
> > > > > >
> > > > > >
> > > > > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
> > > > > > <mailto:amusil@redhat.com>> wrote:
> > > > > >  >
> > > > > >  >
> > > > > >  >
> > > > > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
> > > > > > <mailto:hzhou@ovn.org>> wrote:
> > > > > >  >>
> > > > > >  >>
> > > > > >  >>
> > > > > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <
> amusil@redhat.com
> > > > > > <mailto:amusil@redhat.com>> wrote:
> > > > > >  >> >
> > > > > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
> > > > > > <mailto:hzhou@ovn.org>> wrote:
> > > > > >  >> > >
> > > > > >  >> > >
> > > > > >  >> > >
> > > > > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <
> amusil@redhat.com
> > > > > > <mailto:amusil@redhat.com>> wrote:
> > > > > >  >> > > >
> > > > > >  >> > > > Instead of tracking address set per struct
> > expr_constant_set
> > > > > > track it
> > > > > >  >> > > > per individual struct expr_constant. This allows more
> fine
> > > > grained
> > > > > >  >> > > > control for I-P processing of address sets in
> controller.
> > It
> > > > > > helps with
> > > > > >  >> > > > scenarios like matching on two address sets in one
> > expression
> > > > e.g.
> > > > > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
> > > > removal of
> > > > > >  >> > > > individual adress from the set to be incrementally
> > processed
> > > > > > instead
> > > > > >  >> > > > of reprocessing all the flows.
> > > > > >  >> > > >
> > > > > >  >> > > > This unfortunately doesn't help with the following
> flows:
> > > > > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
> > > > > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
> > > > > >  >> > > >
> > > > > >  >> > > > The memory impact should be minimal as there is only
> > increase
> > > > > > of 8 bytes
> > > > > >  >> > > > per the struct expr_constant.
> > > > > >  >> > > >
> > > > > >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
> > > > > > <https://issues.redhat.com/browse/FDP-509>
> > > > > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
> > > > > > <mailto:amusil@redhat.com>>
> > > > > >  >> > > > ---
> > > > > >  >> > > > v4: Rebase on top of current main.
> > > > > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
> > > > > > according to suggestion from Han.
> > > > > >  >> > >
> > > > > >  >> > > Thanks Ales. I updated the commit message for the same,
> and
> > > > > > applied to main branch.
> > > > > >  >> > >
> > > > > >  >> > > Regards,
> > > > > >  >> > > Han
> > > > > >  >> > >
> > > > > >  >> > > > v3: Rebase on top of current main.
> > > > > >  >> > > >     Address comments from Han:
> > > > > >  >> > > >     - Adjust the comment for
> > "lflow_handle_addr_set_update" to
> > > > > > include remaning corner cases.
> > > > > >  >> > > >     - Make sure that the flows are consistent between
> I-P
> > and
> > > > > > recompute.
> > > > > >  >> > > > v2: Rebase on top of current main.
> > > > > >  >> > > >     Adjust the comment for I-P optimization.
> > > > > >  >> > > > ---
> > > > > >  >> > > >  controller/lflow.c      | 11 ++---
> > > > > >  >> > > >  include/ovn/actions.h   |  2 +-
> > > > > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
> > > > > >  >> > > >  lib/actions.c           | 20 ++++-----
> > > > > >  >> > > >  lib/expr.c              | 99
> > > > > > +++++++++++++++++------------------------
> > > > > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at> |
> 79
> > > > > > +++++++++++++++++++++++++++++---
> > > > > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
> > > > > >  >> > > >
> > > > > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
> > > > > >  >> > > > index 760ec0b41..1e05665a1 100644
> > > > > >  >> > > > --- a/controller/lflow.c
> > > > > >  >> > > > +++ b/controller/lflow.c
> > > > > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
> > > > > > lflow_ctx_in *l_ctx_in,
> > > > > >  >> > > >  }
> > > > > >  >> > > >
> > > > > >  >> > > >  static bool
> > > > > >  >> > > > -as_info_from_expr_const(const char *as_name, const
> union
> > > > > > expr_constant *c,
> > > > > >  >> > > > +as_info_from_expr_const(const char *as_name, const
> struct
> > > > > > expr_constant *c,
> > > > > >  >> > > >                          struct addrset_info *as_info)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      as_info->name = as_name;
> > > > > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const
> char
> > > > > > *as_name, struct addr_set_diff *as_diff,
> > > > > >  >> > > >   *        generated.
> > > > > >  >> > > >   *
> > > > > >  >> > > >   *      - The sub expression of the address set is
> > combined
> > > > > > with other sub-
> > > > > >  >> > > > - *        expressions/constants, usually because of
> > > > > > disjunctions between
> > > > > >  >> > > > - *        sub-expressions/constants, e.g.:
> > > > > >  >> > > > + *        expressions/constants on different fields,
> > e.g.:
> > > > > >  >> > > >   *
> > > > > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
> > > > > >  >> > > > - *          ip.src == {$as1, $as2}
> > > > > >  >> > > > - *          ip.src == {$as1, ip1}
> > > > > >  >> > > >   *
> > > > > >  >> > > > - *        All these could have been split into
> separate
> > > > lflows.
> > > > > >  >> > > > + *        This could have been split into separate
> > lflows.
> > > > > >  >> > > >   *
> > > > > >  >> > > >   *      - Conjunctions overlapping between lflows,
> which
> > can
> > > > > > be caused by
> > > > > >  >> > > >   *        overlapping address sets or same address set
> > used
> > > > > > by multiple lflows
> > > > > >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const
> > char
> > > > > > *as_name,
> > > > > >  >> > > >          if (as_diff->deleted) {
> > > > > >  >> > > >              struct addrset_info as_info;
> > > > > >  >> > > >              for (size_t i = 0; i <
> > > > > > as_diff->deleted->n_values; i++) {
> > > > > >  >> > > > -                union expr_constant *c =
> > > > > > &as_diff->deleted->values[i];
> > > > > >  >> > > > +                struct expr_constant *c =
> > > > > > &as_diff->deleted->values[i];
> > > > > >  >> > > >                  if (!as_info_from_expr_const(as_name,
> c,
> > > > > > &as_info)) {
> > > > > >  >> > > >                      continue;
> > > > > >  >> > > >                  }
> > > > > >  >> > > > diff --git a/include/ovn/actions.h
> b/include/ovn/actions.h
> > > > > >  >> > > > index ae0864fdd..88cf4de79 100644
> > > > > >  >> > > > --- a/include/ovn/actions.h
> > > > > >  >> > > > +++ b/include/ovn/actions.h
> > > > > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
> > > > > >  >> > > >  struct ovnact_load {
> > > > > >  >> > > >      struct ovnact ovnact;
> > > > > >  >> > > >      struct expr_field dst;
> > > > > >  >> > > > -    union expr_constant imm;
> > > > > >  >> > > > +    struct expr_constant imm;
> > > > > >  >> > > >  };
> > > > > >  >> > > >
> > > > > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
> > > > > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> > > > > >  >> > > > index c48f82398..e54edb5bf 100644
> > > > > >  >> > > > --- a/include/ovn/expr.h
> > > > > >  >> > > > +++ b/include/ovn/expr.h
> > > > > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum
> > lex_type
> > > > > > type, enum expr_relop *relop);
> > > > > >  >> > > >  struct expr {
> > > > > >  >> > > >      struct ovs_list node;       /* In parent
> EXPR_T_AND
> > or
> > > > > > EXPR_T_OR if any. */
> > > > > >  >> > > >      enum expr_type type;        /* Expression type. */
> > > > > >  >> > > > -    char *as_name;              /* Address set name.
> > Null if
> > > > > > it is not an
> > > > > >  >> > > > +    const char *as_name;        /* Address set name.
> > Null if
> > > > > > it is not an
> > > > > >  >> > > >                                     address set. */
> > > > > >  >> > > >
> > > > > >  >> > > >      union {
> > > > > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
> > > > > >  >> > > >  };
> > > > > >  >> > > >
> > > > > >  >> > > >  /* A string or integer constant (one must know which
> from
> > > > > > context). */
> > > > > >  >> > > > -union expr_constant {
> > > > > >  >> > > > -    /* Integer constant.
> > > > > >  >> > > > -     *
> > > > > >  >> > > > -     * The width of a constant isn't always clear,
> e.g.
> > if
> > > > > > you write "1",
> > > > > >  >> > > > -     * there's no way to tell whether you mean for
> that
> > to be
> > > > > > a 1-bit constant
> > > > > >  >> > > > -     * or a 128-bit constant or somewhere in between.
> */
> > > > > >  >> > > > -    struct {
> > > > > >  >> > > > -        union mf_subvalue value;
> > > > > >  >> > > > -        union mf_subvalue mask; /* Only initialized if
> > > > > > 'masked'. */
> > > > > >  >> > > > -        bool masked;
> > > > > >  >> > > > -
> > > > > >  >> > > > -        enum lex_format format; /* From the constant's
> > > > > > lex_token. */
> > > > > >  >> > > > -    };
> > > > > >  >> > > > +struct expr_constant {
> > > > > >  >> > > > +    const char *as_name;
> > > > > >  >> > > >
> > > > > >  >> > > > -    /* Null-terminated string constant. */
> > > > > >  >> > > > -    char *string;
> > > > > >  >> > > > +    union {
> > > > > >  >> > > > +        /* Integer constant.
> > > > > >  >> > > > +         *
> > > > > >  >> > > > +         * The width of a constant isn't always clear,
> > e.g.
> > > > > > if you write "1",
> > > > > >  >> > > > +         * there's no way to tell whether you mean for
> > that
> > > > > > to be a 1-bit
> > > > > >  >> > > > +         * constant or a 128-bit constant or
> somewhere in
> > > > > > between. */
> > > > > >  >> > > > +        struct {
> > > > > >  >> > > > +            union mf_subvalue value;
> > > > > >  >> > > > +            union mf_subvalue mask; /* Only
> initialized
> > if
> > > > > > 'masked'. */
> > > > > >  >> > > > +            bool masked;
> > > > > >  >> > > > +
> > > > > >  >> > > > +            enum lex_format format; /* From the
> > constant's
> > > > > > lex_token. */
> > > > > >  >> > > > +        };
> > > > > >  >> > > > +
> > > > > >  >> > > > +        /* Null-terminated string constant. */
> > > > > >  >> > > > +        char *string;
> > > > > >  >> > > > +    };
> > > > > >  >> > > >  };
> > > > > >  >> > > >
> > > > > >  >> > > >  bool expr_constant_parse(struct lexer *,
> > > > > >  >> > > >                           const struct expr_field *,
> > > > > >  >> > > > -                         union expr_constant *);
> > > > > >  >> > > > -void expr_constant_format(const union expr_constant *,
> > > > > >  >> > > > +                         struct expr_constant *);
> > > > > >  >> > > > +void expr_constant_format(const struct expr_constant
> *,
> > > > > >  >> > > >                            enum expr_constant_type,
> > struct ds
> > > > *);
> > > > > >  >> > > > -void expr_constant_destroy(const union expr_constant
> *,
> > > > > >  >> > > > +void expr_constant_destroy(const struct expr_constant
> *,
> > > > > >  >> > > >                             enum expr_constant_type);
> > > > > >  >> > > >
> > > > > >  >> > > >  /* A collection of "union expr_constant"s of the same
> > type.
> > > > */
> > > > > >  >> > > >  struct expr_constant_set {
> > > > > >  >> > > > -    union expr_constant *values;  /* Constants. */
> > > > > >  >> > > > +    struct expr_constant *values; /* Constants. */
> > > > > >  >> > > >      size_t n_values;              /* Number of
> > constants. */
> > > > > >  >> > > >      enum expr_constant_type type; /* Type of the
> > constants.
> > > > */
> > > > > >  >> > > >      bool in_curlies;              /* Whether the
> > constants
> > > > > > were in {}. */
> > > > > >  >> > > > -    char *as_name;                /* Name of an
> address
> > set.
> > > > > > It is NULL if not
> > > > > >  >> > > > -                                     an address set.
> */
> > > > > >  >> > > >  };
> > > > > >  >> > > >
> > > > > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
> > > > > > expr_constant_set *);
> > > > > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
> > > > > >  >> > > > index 94751d059..cff4f9e3c 100644
> > > > > >  >> > > > --- a/lib/actions.c
> > > > > >  >> > > > +++ b/lib/actions.c
> > > > > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct
> ovnact_load
> > > > *load,
> > > > > >  >> > > >              const struct ovnact_encode_params *ep,
> > > > > >  >> > > >              struct ofpbuf *ofpacts)
> > > > > >  >> > > >  {
> > > > > >  >> > > > -    const union expr_constant *c = &load->imm;
> > > > > >  >> > > > +    const struct expr_constant *c = &load->imm;
> > > > > >  >> > > >      struct mf_subfield dst =
> > expr_resolve_field(&load->dst);
> > > > > >  >> > > >      struct ofpact_set_field *sf =
> > > > > > ofpact_put_set_field(ofpacts, dst.field,
> > > > > >  >> > > >
> > NULL,
> > > > > > NULL);
> > > > > >  >> > > > @@ -2077,7 +2077,7 @@
> > > > > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
> > > > > >  >> > > >          /* All empty_lb_backends fields are of type
> > 'str' */
> > > > > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
> > > > > >  >> > > >
> > > > > >  >> > > > -        const union expr_constant *c =
> o->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> o->value.values;
> > > > > >  >> > > >          size_t size = strlen(c->string);
> > > > > >  >> > > >          struct controller_event_opt_header hdr =
> > > > > >  >> > > >              (struct controller_event_opt_header) {
> > > > > >  >> > > > @@ -2553,7 +2553,7 @@ validate_empty_lb_backends(struct
> > > > > > action_context *ctx,
> > > > > >  >> > > >  {
> > > > > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
> > > > > >  >> > > >          const struct ovnact_gen_option *o =
> &options[i];
> > > > > >  >> > > > -        const union expr_constant *c =
> o->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> o->value.values;
> > > > > >  >> > > >          struct sockaddr_storage ss;
> > > > > >  >> > > >          struct uuid uuid;
> > > > > >  >> > > >
> > > > > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const
> > struct
> > > > > > ovnact_gen_option *o,
> > > > > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
> 2);
> > > > > >  >> > > >      opt_header[0] = o->option->code;
> > > > > >  >> > > >
> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > > >  >> > > >      size_t n_values = o->value.n_values;
> > > > > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
> > > > > >  >> > > >          !strcmp(o->option->type, "uint8")) {
> > > > > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const
> > struct
> > > > > > ovnact_gen_option *o,
> > > > > >  >> > > >                           struct ofpbuf *ofpacts)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      struct dhcpv6_opt_header *opt =
> > > > > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > > >  >> > > >      size_t n_values = o->value.n_values;
> > > > > >  >> > > >      size_t size;
> > > > > >  >> > > >
> > > > > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const
> struct
> > > > > > ovnact_put_opts *pdo,
> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > > > DHCP_OPT_BOOTFILE_CODE);
> > > > > >  >> > > >      if (boot_opt) {
> > > > > >  >> > > >          uint8_t *opt_header =
> ofpbuf_put_zeros(ofpacts,
> > 2);
> > > > > >  >> > > > -        const union expr_constant *c =
> > > > boot_opt->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> > > > boot_opt->value.values;
> > > > > >  >> > > >          opt_header[0] = boot_opt->option->code;
> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const
> struct
> > > > > > ovnact_put_opts *pdo,
> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
> > > > > > DHCP_OPT_BOOTFILE_ALT_CODE);
> > > > > >  >> > > >      if (boot_alt_opt) {
> > > > > >  >> > > >          uint8_t *opt_header =
> ofpbuf_put_zeros(ofpacts,
> > 2);
> > > > > >  >> > > > -        const union expr_constant *c =
> > > > > > boot_alt_opt->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> > > > > > boot_alt_opt->value.values;
> > > > > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > > > > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const
> struct
> > > > > > ovnact_put_opts *pdo,
> > > > > >  >> > > >          pdo->options, pdo->n_options,
> > > > DHCP_OPT_NEXT_SERVER_CODE);
> > > > > >  >> > > >      if (next_server_opt) {
> > > > > >  >> > > >          uint8_t *opt_header =
> ofpbuf_put_zeros(ofpacts,
> > 2);
> > > > > >  >> > > > -        const union expr_constant *c =
> > > > > > next_server_opt->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> > > > > > next_server_opt->value.values;
> > > > > >  >> > > >          opt_header[0] = next_server_opt->option->code;
> > > > > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
> > > > > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
> > > > sizeof(ovs_be32));
> > > > > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
> > > > > > action_context *ctx, const struct expr_field *dst,
> > > > > >  >> > > >      /* Let's validate the options. */
> > > > > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
> > > > > >  >> > > >          const struct ovnact_gen_option *o =
> > &po->options[i];
> > > > > >  >> > > > -        const union expr_constant *c =
> o->value.values;
> > > > > >  >> > > > +        const struct expr_constant *c =
> o->value.values;
> > > > > >  >> > > >          if (o->value.n_values > 1) {
> > > > > >  >> > > >              lexer_error(ctx->lexer, "Invalid value for
> > \"%s\"
> > > > > > option",
> > > > > >  >> > > >                          o->option->name);
> > > > > >  >> > > > @@ -3490,7 +3490,7 @@ static void
> > > > > >  >> > > >  encode_put_nd_ra_option(const struct ovnact_gen_option
> > *o,
> > > > > >  >> > > >                          struct ofpbuf *ofpacts,
> ptrdiff_t
> > > > > > ra_offset)
> > > > > >  >> > > >  {
> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
> > > > > >  >> > > >
> > > > > >  >> > > >      switch (o->option->code) {
> > > > > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
> > > > > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
> > > > > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
> > > > > >  >> > > > --- a/lib/expr.c
> > > > > >  >> > > > +++ b/lib/expr.c
> > > > > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type type,
> > struct
> > > > > > expr *a, struct expr *b)
> > > > > >  >> > > >          } else {
> > > > > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
> > > > > >  >> > > >          }
> > > > > >  >> > > > -        free(a->as_name);
> > > > > >  >> > > >          a->as_name = NULL;
> > > > > >  >> > > >          return a;
> > > > > >  >> > > >      } else if (b->type == type) {
> > > > > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
> > > > > >  >> > > > -        free(b->as_name);
> > > > > >  >> > > >          b->as_name = NULL;
> > > > > >  >> > > >          return b;
> > > > > >  >> > > >      } else {
> > > > > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
> > > > > > expr_context *, struct expr_field *);
> > > > > >  >> > > >
> > > > > >  >> > > >  static struct expr *
> > > > > >  >> > > >  make_cmp__(const struct expr_field *f, enum
> expr_relop r,
> > > > > >  >> > > > -             const union expr_constant *c)
> > > > > >  >> > > > +             const struct expr_constant *c)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
> > > > > >  >> > > >      e->type = EXPR_T_CMP;
> > > > > >  >> > > >      e->cmp.symbol = f->symbol;
> > > > > >  >> > > >      e->cmp.relop = r;
> > > > > >  >> > > > +    e->as_name = c->as_name;
> > > > > >  >> > > >      if (f->symbol->width) {
> > > > > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
> > > > > >  >> > > >                       &e->cmp.value, sizeof
> e->cmp.value,
> > > > f->ofs,
> > > > > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct expr_field
> *f,
> > > > > > enum expr_relop r,
> > > > > >  >> > > >
> > > > > >  >> > > >  /* Returns the minimum reasonable width for integer
> > constant
> > > > > > 'c'. */
> > > > > >  >> > > >  static int
> > > > > >  >> > > > -expr_constant_width(const union expr_constant *c)
> > > > > >  >> > > > +expr_constant_width(const struct expr_constant *c)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      if (c->masked) {
> > > > > >  >> > > >          return mf_subvalue_width(&c->mask);
> > > > > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context *ctx,
> > > > > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
> > > > EXPR_T_AND,
> > > > > >  >> > > >                           e, make_cmp__(f, r,
> > > > &cs->values[i]));
> > > > > >  >> > > >      }
> > > > > >  >> > > > -    /* Track address set */
> > > > > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
> > > > cs->as_name) {
> > > > > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
> > > > > >  >> > > > -    }
> > > > > >  >> > > > +
> > > > > >  >> > > >  exit:
> > > > > >  >> > > >      expr_constant_set_destroy(cs);
> > > > > >  >> > > >      return e;
> > > > > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct
> expr_context
> > > > > > *ctx, struct expr_constant_set *cs,
> > > > > >  >> > > >          }
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    struct expr_constant_set *addr_sets
> > > > > >  >> > > > -        = (ctx->addr_sets
> > > > > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
> > > > ctx->lexer->token.s)
> > > > > >  >> > > > -           : NULL);
> > > > > >  >> > > > -    if (!addr_sets) {
> > > > > >  >> > > > +    struct shash_node *node = ctx->addr_sets
> > > > > >  >> > > > +                              ?
> > shash_find(ctx->addr_sets,
> > > > > > ctx->lexer->token.s)
> > > > > >  >> > > > +                              : NULL;
> > > > > >  >> > > > +    if (!node) {
> > > > > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting
> > address set
> > > > > > name");
> > > > > >  >> > > >          return false;
> > > > > >  >> > > >      }
> > > > > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct
> expr_context
> > > > > > *ctx, struct expr_constant_set *cs,
> > > > > >  >> > > >          return false;
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    if (!cs->n_values) {
> > > > > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
> > > > > >  >> > > > -    }
> > > > > >  >> > > > -
> > > > > >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
> > > > > >  >> > > >      size_t n_values = cs->n_values +
> addr_sets->n_values;
> > > > > >  >> > > >      if (n_values >= *allocated_values) {
> > > > > >  >> > > >          cs->values = xrealloc(cs->values, n_values *
> > sizeof
> > > > > > *cs->values);
> > > > > >  >> > > >          *allocated_values = n_values;
> > > > > >  >> > > >      }
> > > > > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++) {
> > > > > >  >> > > > -        cs->values[cs->n_values++] =
> > addr_sets->values[i];
> > > > > >  >> > > > +        struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > > +        *c = addr_sets->values[i];
> > > > > >  >> > > > +        c->as_name = node->name;
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > >      return true;
> > > > > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct
> expr_context
> > *ctx,
> > > > > > struct expr_constant_set *cs,
> > > > > >  >> > > >          *allocated_values = n_values;
> > > > > >  >> > > >      }
> > > > > >  >> > > >      for (size_t i = 0; i < port_group->n_values; i++)
> {
> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > > > >  >> > > > -            xstrdup(port_group->values[i].string);
> > > > > >  >> > > > +        struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > > +        c->string =
> > xstrdup(port_group->values[i].string);
> > > > > >  >> > > > +        c->as_name = NULL;
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > >      return true;
> > > > > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct expr_context
> > *ctx,
> > > > > > struct expr_constant_set *cs,
> > > > > >  >> > > >                                  sizeof *cs->values);
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    /* Combining other values to the constant set
> that is
> > > > > > tracking an
> > > > > >  >> > > > -     * address set, so untrack it. */
> > > > > >  >> > > > -    free(cs->as_name);
> > > > > >  >> > > > -    cs->as_name = NULL;
> > > > > >  >> > > > -
> > > > > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
> > > > > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded
> template.");
> > > > > >  >> > > >          return false;
> > > > > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context
> > *ctx,
> > > > > > struct expr_constant_set *cs,
> > > > > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
> > > > EXPR_C_STRING)) {
> > > > > >  >> > > >              return false;
> > > > > >  >> > > >          }
> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
> > > > > > xstrdup(ctx->lexer->token.s);
> > > > > >  >> > > > +        struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
> > > > > >  >> > > > +        c->as_name = NULL;
> > > > > >  >> > > >          lexer_get(ctx->lexer);
> > > > > >  >> > > >          return true;
> > > > > >  >> > > >      } else if (ctx->lexer->token.type ==
> LEX_T_INTEGER ||
> > > > > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct
> expr_context
> > *ctx,
> > > > > > struct expr_constant_set *cs,
> > > > > >  >> > > >              return false;
> > > > > >  >> > > >          }
> > > > > >  >> > > >
> > > > > >  >> > > > -        union expr_constant *c =
> > &cs->values[cs->n_values++];
> > > > > >  >> > > > +        struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > >          c->value = ctx->lexer->token.value;
> > > > > >  >> > > >          c->format = ctx->lexer->token.format;
> > > > > >  >> > > >          c->masked = ctx->lexer->token.type ==
> > > > > > LEX_T_MASKED_INTEGER;
> > > > > >  >> > > >          if (c->masked) {
> > > > > >  >> > > >              c->mask = ctx->lexer->token.mask;
> > > > > >  >> > > >          }
> > > > > >  >> > > > +        c->as_name = NULL;
> > > > > >  >> > > >          lexer_get(ctx->lexer);
> > > > > >  >> > > >          return true;
> > > > > >  >> > > >      } else if (ctx->lexer->token.type == LEX_T_MACRO)
> {
> > > > > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct
> expr_context
> > > > > > *ctx, struct expr_constant_set *cs)
> > > > > >  >> > > >   * indeterminate. */
> > > > > >  >> > > >  bool
> > > > > >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
> > > > > > expr_field *f,
> > > > > >  >> > > > -                    union expr_constant *c)
> > > > > >  >> > > > +                    struct expr_constant *c)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      if (lexer->error) {
> > > > > >  >> > > >          return false;
> > > > > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer
> > *lexer,
> > > > > > const struct expr_field *f,
> > > > > >  >> > > >  /* Appends to 's' a re-parseable representation of
> > constant
> > > > > > 'c' with the given
> > > > > >  >> > > >   * 'type'. */
> > > > > >  >> > > >  void
> > > > > >  >> > > > -expr_constant_format(const union expr_constant *c,
> > > > > >  >> > > > +expr_constant_format(const struct expr_constant *c,
> > > > > >  >> > > >                       enum expr_constant_type type,
> > struct ds
> > > > *s)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      if (type == EXPR_C_STRING) {
> > > > > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
> > > > > > expr_constant *c,
> > > > > >  >> > > >   *
> > > > > >  >> > > >   * Does not free(c). */
> > > > > >  >> > > >  void
> > > > > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
> > > > > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
> > > > > >  >> > > >                        enum expr_constant_type type)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      if (c && type == EXPR_C_STRING) {
> > > > > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const
> > struct
> > > > > > expr_constant_set *cs, struct ds *s)
> > > > > >  >> > > >          ds_put_char(s, '{');
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    for (const union expr_constant *c = cs->values;
> > > > > >  >> > > > +    for (const struct expr_constant *c = cs->values;
> > > > > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
> > > > > >  >> > > >          if (c != cs->values) {
> > > > > >  >> > > >              ds_put_cstr(s, ", ");
> > > > > >  >> > > > @@ -1067,15 +1060,14 @@
> expr_constant_set_destroy(struct
> > > > > > expr_constant_set *cs)
> > > > > >  >> > > >              }
> > > > > >  >> > > >          }
> > > > > >  >> > > >          free(cs->values);
> > > > > >  >> > > > -        free(cs->as_name);
> > > > > >  >> > > >      }
> > > > > >  >> > > >  }
> > > > > >  >> > > >
> > > > > >  >> > > >  static int
> > > > > >  >> > > >  compare_expr_constant_integer_cb(const void *a_, const
> > void
> > > > *b_)
> > > > > >  >> > > >  {
> > > > > >  >> > > > -    const union expr_constant *a = a_;
> > > > > >  >> > > > -    const union expr_constant *b = b_;
> > > > > >  >> > > > +    const struct expr_constant *a = a_;
> > > > > >  >> > > > +    const struct expr_constant *b = b_;
> > > > > >  >> > > >
> > > > > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof
> > a->value);
> > > > > >  >> > > >      if (d) {
> > > > > >  >> > > > @@ -1114,7 +1106,7 @@
> > expr_constant_set_create_integers(const
> > > > > > char *const *values, size_t n_values)
> > > > > >  >> > > >              VLOG_WARN("Invalid constant set entry:
> '%s',
> > > > > > token type: %d",
> > > > > >  >> > > >                        values[i], lex.token.type);
> > > > > >  >> > > >          } else {
> > > > > >  >> > > > -            union expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > > +            struct expr_constant *c =
> > > > > > &cs->values[cs->n_values++];
> > > > > >  >> > > >              c->value = lex.token.value;
> > > > > >  >> > > >              c->format = lex.token.format;
> > > > > >  >> > > >              c->masked = lex.token.type ==
> > > > LEX_T_MASKED_INTEGER;
> > > > > >  >> > > > @@ -1135,7 +1127,7 @@
> > expr_constant_set_create_integers(const
> > > > > > char *const *values, size_t n_values)
> > > > > >  >> > > >
> > > > > >  >> > > >  static void
> > > > > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set
> > **p_cs,
> > > > > >  >> > > > -                            union expr_constant *c,
> > size_t
> > > > > > *allocated)
> > > > > >  >> > > > +                            struct expr_constant *c,
> > size_t
> > > > > > *allocated)
> > > > > >  >> > > >  {
> > > > > >  >> > > >      struct expr_constant_set *cs = *p_cs;
> > > > > >  >> > > >      if (!cs) {
> > > > > >  >> > > > @@ -1246,7 +1238,7 @@
> expr_const_sets_add_strings(struct
> > shash
> > > > > > *const_sets, const char *name,
> > > > > >  >> > > >                          values[i], name);
> > > > > >  >> > > >              continue;
> > > > > >  >> > > >          }
> > > > > >  >> > > > -        union expr_constant *c =
> > &cs->values[cs->n_values++];
> > > > > >  >> > > > +        struct expr_constant *c =
> > > > &cs->values[cs->n_values++];
> > > > > >  >> > > >          c->string = xstrdup(values[i]);
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct
> > expr_context
> > > > > > *ctx, bool *atomic)
> > > > > >  >> > > >
> > > > > >  >> > > >              *atomic = true;
> > > > > >  >> > > >
> > > > > >  >> > > > -            union expr_constant *cst = xzalloc(sizeof
> > *cst);
> > > > > >  >> > > > +            struct expr_constant *cst = xzalloc(sizeof
> > *cst);
> > > > > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
> > > > > >  >> > > >              cst->masked = false;
> > > > > >  >> > > >
> > > > > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct
> > expr_context
> > > > > > *ctx, bool *atomic)
> > > > > >  >> > > >              c.values = cst;
> > > > > >  >> > > >              c.n_values = 1;
> > > > > >  >> > > >              c.in_curlies = false;
> > > > > >  >> > > > -            c.as_name = NULL;
> > > > > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
> > > > > >  >> > > >          } else if (parse_relop(ctx, &r) &&
> > > > > > parse_constant_set(ctx, &c)) {
> > > > > >  >> > > >              return make_cmp(ctx, &f, r, &c);
> > > > > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
> > > > *symtab)
> > > > > >  >> > > >  static struct expr *
> > > > > >  >> > > >  expr_clone_cmp(struct expr *expr)
> > > > > >  >> > > >  {
> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > > > >  >> > > >      if (!new->cmp.symbol->width) {
> > > > > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
> > > > > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr
> *expr)
> > > > > >  >> > > >  static struct expr *
> > > > > >  >> > > >  expr_clone_condition(struct expr *expr)
> > > > > >  >> > > >  {
> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
> > > > > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
> > > > > >  >> > > >      return new;
> > > > > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
> > > > > >  >> > > >          return;
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    free(expr->as_name);
> > > > > >  >> > > > -
> > > > > >  >> > > >      struct expr *sub;
> > > > > >  >> > > >
> > > > > >  >> > > >      switch (expr->type) {
> > > > > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr
> > *expr,
> > > > > > const struct expr_symbol *symbol)
> > > > > >  >> > > >       * mask sizes. */
> > > > > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
> > > > > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
> > > > > >  >> > > > -    bool modified = false;
> > > > > >  >> > > > +    bool has_addr_set = false;
> > > > > >  >> > > >      /* Linked list over the 'subs' array to quickly
> skip
> > > > > > deleted elements,
> > > > > >  >> > > >       * i.e. the index of the next potentially non-NULL
> > > > > > element. */
> > > > > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
> > > > > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr
> > *expr,
> > > > > > const struct expr_symbol *symbol)
> > > > > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
> > > > > >  >> > > >      struct expr *sub;
> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
> > > > > >  >> > > > +        if (sub->as_name) {
> > > > > >  >> > > > +            has_addr_set = true;
> > > > > >  >> > > > +        }
> > > > > >  >> > > >          if (symbol->width) {
> > > > > >  >> > > >              const unsigned long *sub_mask;
> > > > > >  >> > > >
> > > > > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr
> > *expr,
> > > > > > const struct expr_symbol *symbol)
> > > > > >  >> > > >              next[last] = i;
> > > > > >  >> > > >              last = i;
> > > > > >  >> > > >          } else {
> > > > > >  >> > > > +            /* Remove address set reference from the
> > > > > > duplicate. */
> > > > > >  >> > > > +            subs[last]->as_name = NULL;
> > > > > >  >> > > >              expr_destroy(subs[i]);
> > > > > >  >> > > >              subs[i] = NULL;
> > > > > >  >> > > > -            modified = true;
> > > > > >  >> > > >          }
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    if (!symbol->width || symbol->level !=
> EXPR_L_ORDINAL
> > > > > >  >> > > > -        || (!modified && expr->as_name)) {
> > > > > >  >> > > > +    if (!symbol->width || symbol->level !=
> > EXPR_L_ORDINAL ||
> > > > > > has_addr_set) {
> > > > > >  >> > > >          /* Not a fully maskable field or this
> expression
> > is
> > > > > > tracking an
> > > > > >  >> > > >           * address set.  Don't try to optimize to
> > preserve
> > > > > > address set I-P. */
> > > > > >  >> > > >          goto done;
> > > > > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr
> > *expr,
> > > > > > const struct expr_symbol *symbol)
> > > > > >  >> > > >              if (expr_bitmap_intersect_check(a_value,
> > a_mask,
> > > > > > b_value, b_mask,
> > > > > >  >> > > >                                              bit_width)
> > > > > >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
> > > > > > bit_width)) {
> > > > > >  >> > > > -                /* 'a' is the same expression with a
> > smaller
> > > > > > mask. */
> > > > > >  >> > > > +                /* 'a' is the same expression with a
> > smaller
> > > > > > mask.
> > > > > >  >> > > > +                 * Remove address set reference from
> the
> > > > > > duplicate. */
> > > > > >  >> > > > +                a->as_name = NULL;
> > > > > >  >> > > >                  expr_destroy(subs[j]);
> > > > > >  >> > > >                  subs[j] = NULL;
> > > > > >  >> > > > -                modified = true;
> > > > > >  >> > > >
> > > > > >  >> > > >                  /* Shorten the path for the next
> round.
> > */
> > > > > >  >> > > >                  if (last) {
> > > > > >  >> > > > @@ -2685,12 +2676,6 @@ done:
> > > > > >  >> > > >          }
> > > > > >  >> > > >      }
> > > > > >  >> > > >
> > > > > >  >> > > > -    if (modified) {
> > > > > >  >> > > > -        /* Members modified, so untrack address set.
> */
> > > > > >  >> > > > -        free(expr->as_name);
> > > > > >  >> > > > -        expr->as_name = NULL;
> > > > > >  >> > > > -    }
> > > > > >  >> > > > -
> > > > > >  >> > > >      free(next);
> > > > > >  >> > > >      free(subs);
> > > > > >  >> > > >      return expr;
> > > > > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct
> expr
> > *or,
> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
> > > > > >  >> > > >          struct expr_match *match = expr_match_new(m,
> > clause,
> > > > > > n_clauses,
> > > > > >  >> > > >
> >  conj_id);
> > > > > >  >> > > > -        if (or->as_name) {
> > > > > >  >> > > > +        if (sub->as_name) {
> > > > > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
> > > > > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
> > > > > >  >> > > > -            match->as_name = xstrdup(or->as_name);
> > > > > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
> > > > > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
> > > > > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
> > > > > >  >> > > >          }
> > > > > >  >> > > > diff --git a/tests/ovn-controller.at
> > > > > > <http://ovn-controller.at> b/tests/ovn-controller.at
> > > > > > <http://ovn-controller.at>
> > > > > >  >> > > > index be198e00d..27cec2aec 100644
> > > > > >  >> > > > --- a/tests/ovn-controller.at <
> http://ovn-controller.at>
> > > > > >  >> > > > +++ b/tests/ovn-controller.at <
> http://ovn-controller.at>
> > > > > >  >> > > > @@ -1625,7 +1625,9 @@
> > > > > >
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
> > > > > > actions=lo
> > > > > >  >> > > >  done
> > > > > >  >> > > >
> > > > > >  >> > > >  reprocess_count_new=$(read_counter
> consider_logical_flow)
> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [10
> > > > > >  >> > > > +# First 2 reprocess are due to change from 1 IP in AS
> to
> > 2.
> > > > > >  >> > > > +# Last 5 is due to overlap in IP addresses between as1
> > and
> > > > as2.
> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [7
> > > > > >  >> > > >  ])
> > > > > >  >> > > >
> > > > > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
> > > > > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
> > > > > >  >> > > >  done
> > > > > >  >> > > >
> > > > > >  >> > > >  reprocess_count_new=$(read_counter
> consider_logical_flow)
> > > > > >  >> > > > -# In this case the as1 and as2 are merged to a single
> OR
> > > > > > expr, so we lose track of
> > > > > >  >> > > > -# address set information - can't handle deletion
> > > > incrementally.
> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [10
> > > > > >  >> > > > +# First 5 are due to overlap in IP addresses between
> as1
> > and
> > > > as2.
> > > > > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [7
> > > > > >  >> > > >  ])
> > > > > >  >> > > >
> > > > > >  >> > > >  OVN_CLEANUP([hv1])
> > > > > >  >> > > > @@ -1817,7 +1819,7 @@
> > > > > >
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
> > > > > > actions=co
> > > > > >  >> > > >
> > > > > >
> > > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > > > actions=conjunction,2/2)
> > > > > >  >> > > >  ])
> > > > > >  >> > > >  reprocess_count_new=$(read_counter
> consider_logical_flow)
> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [1
> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [0
> > > > > >  >> > > >  ])
> > > > > >  >> > > >
> > > > > >  >> > > >  # Delete 2 IPs
> > > > > >  >> > > > @@ -1837,7 +1839,7 @@
> > > > > >
> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
> > > > > > actions=co
> > > > > >  >> > > >
> > > > > >
> > > >
> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
> > > > actions=conjunction,2/2)
> > > > > >  >> > > >  ])
> > > > > >  >> > > >  reprocess_count_new=$(read_counter
> consider_logical_flow)
> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [1
> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
> > > > > > $reprocess_count_old))], [0], [0
> > > > > >  >> > > >  ])
> > > > > >  >> > > >
> > > > > >  >> > > >  OVN_CLEANUP([hv1])
> > > > > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
> > > > > >  >> > > >
> > > > > >  >> > > >  AT_CLEANUP
> > > > > >  >> > > >  ])
> > > > > >  >> > > > +
> > > > > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute
> > consistency])
> > > > > >  >> > > > +AT_KEYWORDS([as-i-p])
> > > > > >  >> > > > +
> > > > > >  >> > > > +ovn_start
> > > > > >  >> > > > +
> > > > > >  >> > > > +net_add n1
> > > > > >  >> > > > +sim_add hv1
> > > > > >  >> > > > +as hv1
> > > > > >  >> > > > +check ovs-vsctl add-br br-phys
> > > > > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
> > > > > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
> > > > > >  >> > > > +    set interface hv1-vif1
> external-ids:iface-id=ls1-lp1
> > > > > >  >> > > > +
> > > > > >  >> > > > +check ovn-nbctl ls-add ls1
> > > > > >  >> > > > +
> > > > > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
> > > > > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
> > > > > >  >> > > > +
> > > > > >  >> > > > +wait_for_ports_up
> > > > > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
> > > > > >  >> > > > +
> > > > > >  >> > > > +# Get the OF table numbers
> > > > > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable
> > ls_out_acl_eval)
> > > > > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
> > > > ls_out_acl_action)
> > > > > >  >> > > > +
> > > > > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath
> tunnel_key
> > > > > > external_ids:name=ls1))
> > > > > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding
> > tunnel_key
> > > > > > logical_port=ls1-lp1))
> > > > > >  >> > > > +
> > > > > >  >> > > > +ovn-nbctl create address_set name=as1
> > > > > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
> > > > > > "ls1-lp1" && ip4.src == $as1' drop
> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
> 10.0.0.0/24
> > > > > > <http://10.0.0.0/24>
> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > > > >  >> > > > +
> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
> $7,
> > $8}'
> > > > > > | sort], [0], [dnl
> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> > 10.0.0.0/24
> > > > > > <http://10.0.0.0/24>
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > > +])
> > > > > >  >> > > > +
> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.1
> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.2
> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.3
> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses 10.0.0.4
> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
> > > > > >  >> > > > +
> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
> $7,
> > $8}'
> > > > > > | sort], [0], [dnl
> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> > 10.0.0.0/24
> > > > > > <http://10.0.0.0/24>
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > > +])
> > > > > >  >> > > > +
> > > > > >  >> > > > +
> > > > > >  >> > > > +check ovn-appctl inc-engine/recompute
> > > > > >  >> > > > +
> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
> $7,
> > $8}'
> > > > > > | sort], [0], [dnl
> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
> > 10.0.0.0/24
> > > > > > <http://10.0.0.0/24>
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > >
> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
> > > > > >  >> > > > +])
> > > > > >  >> > > > +
> > > > > >  >> > > > +OVN_CLEANUP([hv1])
> > > > > >  >> > > > +AT_CLEANUP
> > > > > >  >> > > > --
> > > > > >  >> > > > 2.44.0
> > > > > >  >> > > >
> > > > > >  >> >
> > > > > >  >> >
> > > > > >  >> > Thank you Han,
> > > > > >  >> >
> > > > > >  >> > I would like to start the discussion about the possibility
> of
> > > > > >  >> > backporting this change. The reason for the backport being
> > that
> > > > this
> > > > > >  >> > change might be beneficial to ovn-kubernetes users, as this
> > was
> > > > > >  >> > actually observed in ovn-kubernetes with network policies.
> I
> > know
> > > > that
> > > > > >  >> > strictly speaking this is not a bug fix, because this
> > limitation
> > > > was
> > > > > >  >> > well documented, however I would like to hear the opinion
> of
> > > > others.
> > > > > >  >>
> > > > > >  >>
> > > > > >  >> I have no objections if this improvement is strongly required
> > for
> > > > > > released versions, even though it is not a bug. May I ask for
> which
> > > > > > releases do you want this to be backported?
> > > > > >  >
> > > > > >  >
> > > > > >  > We have a request for 23.09 and 24.03.
> > > > > >  >
> > > > > > Ok. I will wait for input from other maintainers. It will be a
> > tradeoff
> > > > > > between risk and performance gains. Mark, Numan and Dumitru, what
> > do you
> > > > > > think?
> > > > > >
> > > > > > Thanks,
> > > > > > Han
> > > > >
> > > > > I'm OK with the patch being backported. I think our test coverage
> is
> > > > > sufficient to uncover problems that may arise from the backport, so
> > the
> > > > > risk should be minimal.
> > > > >
> > > >
> > > > Thanks Numan and Mark for confirming, so we got a quorum. I
> backported
> > the
> > > > patch to 24.03 and 23.09.
> > >
> > > This patch is using the "ovn-debug" binary which is missing in
> > > branch-24.03 and branch-23.09.
> > > Because of this, the test case "AS I-P and recompute consistency" is
> > > failing.  I backported
> > > the commit "117573e2d279" to these branches.
> > >
> > > While backporting to branch-23.09,  had to move the pipeline stage
> macros
> > from
> > > northd.c to northd.h.  Running the tests in my github repo before
> pushing
> > > to upstream branch-23.09.
> > >
> >
> > Oh, no. Thanks Numan for fixing! I ran test locally after switching
> > branches and all were successful, likely because the ovn-debug binary is
> > already in my environment, so I pushed late at night and see this just
> now.
> > Sorry for the trouble.
> >
>
> No worries.  I passed for me locally too until I deleted the binary.
>
> Numan
>
> > Regards,
> > Han
> >
> > > Numan
> > >
> > > >
> > > > Han
> > > >
> > > > > >
> > > > > >  >>
> > > > > >  >>
> > > > > >  >> Thanks,
> > > > > >  >> Han
> > > > > >  >>
> > > > > >  >> >
> > > > > >  >> > Thanks,
> > > > > >  >> > Ales
> > > > > >  >> > --
> > > > > >  >> >
> > > > > >  >> > Ales Musil
> > > > > >  >> >
> > > > > >  >> > Senior Software Engineer - OVN Core
> > > > > >  >> >
> > > > > >  >> > Red Hat EMEA
> > > > > >  >> >
> > > > > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
> > > > > >  >> >
> > > > > >  >
> > > > > >  >
> > > > > >  >
> > > > > >  > --
> > > > > >  >
> > > > > >  > Ales Musil
> > > > > >  >
> > > > > >  > Senior Software Engineer - OVN Core
> > > > > >  >
> > > > > >  > Red Hat EMEA
> > > > > >  >
> > > > > >  > amusil@redhat.com <mailto:amusil@redhat.com>
> > > > >
> > > > _______________________________________________
> > > > dev mailing list
> > > > dev@openvswitch.org
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Ales Musil May 13, 2024, 5:25 a.m. UTC | #13
On Sun, May 12, 2024 at 11:17 PM Sri kor <esrikor1@gmail.com> wrote:

> Hi @numans@ovn.org <numans@ovn.org> @Ales Musil <amusil@redhat.com> ,
>     Currently we are on OVN 23.09. We are facing the following syntax
> error with the address_set.From the diff, looks like you address this fix.
> Is this something expected?
>
> *2024-05-11T17:35:28.694Z|153935|lflow|WARN|error parsing match "reg0[7]
> == 1 && ((ip4.src ==
> $a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
> && (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
> <= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
> @a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c)":
> Syntax error at
> `$a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a'
> expecting address set name.*
>
>
> *Here are the transaction details .*
>
> *[{Op:insert Table:ACL Row:map[action:allow-related direction:to-lport
> external_ids:{GoMap:map[crusoe_fw_rule_id:770b6929-88d1-4659-ba3b-ed84ccfd71a0
> crusoe_vpc_id:0ea55446-0df6-4358-8442-56893854004c
> customer_id:781ac228-38ae-4f04-9cc1-707543df50c8]} log:false match:(ip4.src
> ==
> $a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
> && (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
> <= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
> @a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c
> name:{GoSet:[a_781ac228_38ae_4f04_9cc1_707543df50c8_acl_770b6929_88d1_4659_b]}
> priority:2000] Rows:[] Columns:[] Mutations:[] Timeout:<nil> Where:[]
> Until: Durable:<nil> Comment:<nil> Lock:<nil>
> UUIDName:_641d4ca1_7596_4004_b577_0f34e0bab87d} {Op:mutate Table:Port_Group
> Row:map[] Rows:[] Columns:[] Mutations:[{Column:acls Mutator:insert
> Value:{GoSet:[{GoUUID:_641d4ca1_7596_4004_b577_0f34e0bab87d}]}}]
> Timeout:<nil> Where:[where column _uuid ==
> {00383922-81ce-4491-9115-6ea2dfc79aea}] Until: Durable:<nil> Comment:<nil>
> Lock:<nil> UUIDName:} {Op:mutate Table:NB_Global Row:map[] Rows:[]
> Columns:[] Mutations:[{Column:nb_cfg Mutator:+= Value:1}] Timeout:<nil>
> Where:[where column _uuid == {be2921c1-99ae-423b-a69c-a43e67d8424e}] Until:
> Durable:<nil> Comment:<nil> Lock:<nil> UUIDName:} {Op:select
> Table:NB_Global Row:map[] Rows:[] Columns:[nb_cfg] Mutations:[]
> Timeout:<nil> Where:[] Until: Durable:<nil> Comment:<nil> Lock:<nil>
> UUIDName:}]*
>
>
> *Thanks*
>
> *Srini*
>
>
Hi,

this seems to be unrelated to the patch. It looks like the controller is
not able to recognize the address set name and doesn't parse it correctly.
Is this address set present in the SB DB?

Thanks,
Ales

On Fri, May 10, 2024 at 9:43 AM Numan Siddique <numans@ovn.org> wrote:
>
>> On Fri, May 10, 2024 at 12:39 PM Han Zhou <hzhou@ovn.org> wrote:
>> >
>> > On Fri, May 10, 2024 at 9:23 AM Numan Siddique <numans@ovn.org> wrote:
>> > >
>> > > On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
>> > > >
>> > > > On Thu, May 9, 2024 at 10:32 AM Mark Michelson <mmichels@redhat.com
>> >
>> > wrote:
>> > > > >
>> > > > > On 5/7/24 02:12, Han Zhou wrote:
>> > > > > >
>> > > > > >
>> > > > > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
>> > > > > > <mailto:amusil@redhat.com>> wrote:
>> > > > > >  >
>> > > > > >  >
>> > > > > >  >
>> > > > > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
>> > > > > > <mailto:hzhou@ovn.org>> wrote:
>> > > > > >  >>
>> > > > > >  >>
>> > > > > >  >>
>> > > > > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <
>> amusil@redhat.com
>> > > > > > <mailto:amusil@redhat.com>> wrote:
>> > > > > >  >> >
>> > > > > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
>> > > > > > <mailto:hzhou@ovn.org>> wrote:
>> > > > > >  >> > >
>> > > > > >  >> > >
>> > > > > >  >> > >
>> > > > > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <
>> amusil@redhat.com
>> > > > > > <mailto:amusil@redhat.com>> wrote:
>> > > > > >  >> > > >
>> > > > > >  >> > > > Instead of tracking address set per struct
>> > expr_constant_set
>> > > > > > track it
>> > > > > >  >> > > > per individual struct expr_constant. This allows more
>> fine
>> > > > grained
>> > > > > >  >> > > > control for I-P processing of address sets in
>> controller.
>> > It
>> > > > > > helps with
>> > > > > >  >> > > > scenarios like matching on two address sets in one
>> > expression
>> > > > e.g.
>> > > > > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition or
>> > > > removal of
>> > > > > >  >> > > > individual adress from the set to be incrementally
>> > processed
>> > > > > > instead
>> > > > > >  >> > > > of reprocessing all the flows.
>> > > > > >  >> > > >
>> > > > > >  >> > > > This unfortunately doesn't help with the following
>> flows:
>> > > > > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
>> > > > > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
>> > > > > >  >> > > >
>> > > > > >  >> > > > The memory impact should be minimal as there is only
>> > increase
>> > > > > > of 8 bytes
>> > > > > >  >> > > > per the struct expr_constant.
>> > > > > >  >> > > >
>> > > > > >  >> > > > Reported-at: https://issues.redhat.com/browse/FDP-509
>> > > > > > <https://issues.redhat.com/browse/FDP-509>
>> > > > > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
>> > > > > > <mailto:amusil@redhat.com>>
>> > > > > >  >> > > > ---
>> > > > > >  >> > > > v4: Rebase on top of current main.
>> > > > > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
>> > > > > > according to suggestion from Han.
>> > > > > >  >> > >
>> > > > > >  >> > > Thanks Ales. I updated the commit message for the same,
>> and
>> > > > > > applied to main branch.
>> > > > > >  >> > >
>> > > > > >  >> > > Regards,
>> > > > > >  >> > > Han
>> > > > > >  >> > >
>> > > > > >  >> > > > v3: Rebase on top of current main.
>> > > > > >  >> > > >     Address comments from Han:
>> > > > > >  >> > > >     - Adjust the comment for
>> > "lflow_handle_addr_set_update" to
>> > > > > > include remaning corner cases.
>> > > > > >  >> > > >     - Make sure that the flows are consistent between
>> I-P
>> > and
>> > > > > > recompute.
>> > > > > >  >> > > > v2: Rebase on top of current main.
>> > > > > >  >> > > >     Adjust the comment for I-P optimization.
>> > > > > >  >> > > > ---
>> > > > > >  >> > > >  controller/lflow.c      | 11 ++---
>> > > > > >  >> > > >  include/ovn/actions.h   |  2 +-
>> > > > > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
>> > > > > >  >> > > >  lib/actions.c           | 20 ++++-----
>> > > > > >  >> > > >  lib/expr.c              | 99
>> > > > > > +++++++++++++++++------------------------
>> > > > > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at>
>> | 79
>> > > > > > +++++++++++++++++++++++++++++---
>> > > > > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
>> > > > > >  >> > > >
>> > > > > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
>> > > > > >  >> > > > index 760ec0b41..1e05665a1 100644
>> > > > > >  >> > > > --- a/controller/lflow.c
>> > > > > >  >> > > > +++ b/controller/lflow.c
>> > > > > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
>> > > > > > lflow_ctx_in *l_ctx_in,
>> > > > > >  >> > > >  }
>> > > > > >  >> > > >
>> > > > > >  >> > > >  static bool
>> > > > > >  >> > > > -as_info_from_expr_const(const char *as_name, const
>> union
>> > > > > > expr_constant *c,
>> > > > > >  >> > > > +as_info_from_expr_const(const char *as_name, const
>> struct
>> > > > > > expr_constant *c,
>> > > > > >  >> > > >                          struct addrset_info *as_info)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      as_info->name = as_name;
>> > > > > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const
>> char
>> > > > > > *as_name, struct addr_set_diff *as_diff,
>> > > > > >  >> > > >   *        generated.
>> > > > > >  >> > > >   *
>> > > > > >  >> > > >   *      - The sub expression of the address set is
>> > combined
>> > > > > > with other sub-
>> > > > > >  >> > > > - *        expressions/constants, usually because of
>> > > > > > disjunctions between
>> > > > > >  >> > > > - *        sub-expressions/constants, e.g.:
>> > > > > >  >> > > > + *        expressions/constants on different fields,
>> > e.g.:
>> > > > > >  >> > > >   *
>> > > > > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
>> > > > > >  >> > > > - *          ip.src == {$as1, $as2}
>> > > > > >  >> > > > - *          ip.src == {$as1, ip1}
>> > > > > >  >> > > >   *
>> > > > > >  >> > > > - *        All these could have been split into
>> separate
>> > > > lflows.
>> > > > > >  >> > > > + *        This could have been split into separate
>> > lflows.
>> > > > > >  >> > > >   *
>> > > > > >  >> > > >   *      - Conjunctions overlapping between lflows,
>> which
>> > can
>> > > > > > be caused by
>> > > > > >  >> > > >   *        overlapping address sets or same address
>> set
>> > used
>> > > > > > by multiple lflows
>> > > > > >  >> > > > @@ -714,7 +711,7 @@ lflow_handle_addr_set_update(const
>> > char
>> > > > > > *as_name,
>> > > > > >  >> > > >          if (as_diff->deleted) {
>> > > > > >  >> > > >              struct addrset_info as_info;
>> > > > > >  >> > > >              for (size_t i = 0; i <
>> > > > > > as_diff->deleted->n_values; i++) {
>> > > > > >  >> > > > -                union expr_constant *c =
>> > > > > > &as_diff->deleted->values[i];
>> > > > > >  >> > > > +                struct expr_constant *c =
>> > > > > > &as_diff->deleted->values[i];
>> > > > > >  >> > > >                  if
>> (!as_info_from_expr_const(as_name, c,
>> > > > > > &as_info)) {
>> > > > > >  >> > > >                      continue;
>> > > > > >  >> > > >                  }
>> > > > > >  >> > > > diff --git a/include/ovn/actions.h
>> b/include/ovn/actions.h
>> > > > > >  >> > > > index ae0864fdd..88cf4de79 100644
>> > > > > >  >> > > > --- a/include/ovn/actions.h
>> > > > > >  >> > > > +++ b/include/ovn/actions.h
>> > > > > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
>> > > > > >  >> > > >  struct ovnact_load {
>> > > > > >  >> > > >      struct ovnact ovnact;
>> > > > > >  >> > > >      struct expr_field dst;
>> > > > > >  >> > > > -    union expr_constant imm;
>> > > > > >  >> > > > +    struct expr_constant imm;
>> > > > > >  >> > > >  };
>> > > > > >  >> > > >
>> > > > > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
>> > > > > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
>> > > > > >  >> > > > index c48f82398..e54edb5bf 100644
>> > > > > >  >> > > > --- a/include/ovn/expr.h
>> > > > > >  >> > > > +++ b/include/ovn/expr.h
>> > > > > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum
>> > lex_type
>> > > > > > type, enum expr_relop *relop);
>> > > > > >  >> > > >  struct expr {
>> > > > > >  >> > > >      struct ovs_list node;       /* In parent
>> EXPR_T_AND
>> > or
>> > > > > > EXPR_T_OR if any. */
>> > > > > >  >> > > >      enum expr_type type;        /* Expression type.
>> */
>> > > > > >  >> > > > -    char *as_name;              /* Address set name.
>> > Null if
>> > > > > > it is not an
>> > > > > >  >> > > > +    const char *as_name;        /* Address set name.
>> > Null if
>> > > > > > it is not an
>> > > > > >  >> > > >                                     address set. */
>> > > > > >  >> > > >
>> > > > > >  >> > > >      union {
>> > > > > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
>> > > > > >  >> > > >  };
>> > > > > >  >> > > >
>> > > > > >  >> > > >  /* A string or integer constant (one must know which
>> from
>> > > > > > context). */
>> > > > > >  >> > > > -union expr_constant {
>> > > > > >  >> > > > -    /* Integer constant.
>> > > > > >  >> > > > -     *
>> > > > > >  >> > > > -     * The width of a constant isn't always clear,
>> e.g.
>> > if
>> > > > > > you write "1",
>> > > > > >  >> > > > -     * there's no way to tell whether you mean for
>> that
>> > to be
>> > > > > > a 1-bit constant
>> > > > > >  >> > > > -     * or a 128-bit constant or somewhere in
>> between. */
>> > > > > >  >> > > > -    struct {
>> > > > > >  >> > > > -        union mf_subvalue value;
>> > > > > >  >> > > > -        union mf_subvalue mask; /* Only initialized
>> if
>> > > > > > 'masked'. */
>> > > > > >  >> > > > -        bool masked;
>> > > > > >  >> > > > -
>> > > > > >  >> > > > -        enum lex_format format; /* From the
>> constant's
>> > > > > > lex_token. */
>> > > > > >  >> > > > -    };
>> > > > > >  >> > > > +struct expr_constant {
>> > > > > >  >> > > > +    const char *as_name;
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    /* Null-terminated string constant. */
>> > > > > >  >> > > > -    char *string;
>> > > > > >  >> > > > +    union {
>> > > > > >  >> > > > +        /* Integer constant.
>> > > > > >  >> > > > +         *
>> > > > > >  >> > > > +         * The width of a constant isn't always
>> clear,
>> > e.g.
>> > > > > > if you write "1",
>> > > > > >  >> > > > +         * there's no way to tell whether you mean
>> for
>> > that
>> > > > > > to be a 1-bit
>> > > > > >  >> > > > +         * constant or a 128-bit constant or
>> somewhere in
>> > > > > > between. */
>> > > > > >  >> > > > +        struct {
>> > > > > >  >> > > > +            union mf_subvalue value;
>> > > > > >  >> > > > +            union mf_subvalue mask; /* Only
>> initialized
>> > if
>> > > > > > 'masked'. */
>> > > > > >  >> > > > +            bool masked;
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +            enum lex_format format; /* From the
>> > constant's
>> > > > > > lex_token. */
>> > > > > >  >> > > > +        };
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +        /* Null-terminated string constant. */
>> > > > > >  >> > > > +        char *string;
>> > > > > >  >> > > > +    };
>> > > > > >  >> > > >  };
>> > > > > >  >> > > >
>> > > > > >  >> > > >  bool expr_constant_parse(struct lexer *,
>> > > > > >  >> > > >                           const struct expr_field *,
>> > > > > >  >> > > > -                         union expr_constant *);
>> > > > > >  >> > > > -void expr_constant_format(const union expr_constant
>> *,
>> > > > > >  >> > > > +                         struct expr_constant *);
>> > > > > >  >> > > > +void expr_constant_format(const struct expr_constant
>> *,
>> > > > > >  >> > > >                            enum expr_constant_type,
>> > struct ds
>> > > > *);
>> > > > > >  >> > > > -void expr_constant_destroy(const union expr_constant
>> *,
>> > > > > >  >> > > > +void expr_constant_destroy(const struct
>> expr_constant *,
>> > > > > >  >> > > >                             enum expr_constant_type);
>> > > > > >  >> > > >
>> > > > > >  >> > > >  /* A collection of "union expr_constant"s of the same
>> > type.
>> > > > */
>> > > > > >  >> > > >  struct expr_constant_set {
>> > > > > >  >> > > > -    union expr_constant *values;  /* Constants. */
>> > > > > >  >> > > > +    struct expr_constant *values; /* Constants. */
>> > > > > >  >> > > >      size_t n_values;              /* Number of
>> > constants. */
>> > > > > >  >> > > >      enum expr_constant_type type; /* Type of the
>> > constants.
>> > > > */
>> > > > > >  >> > > >      bool in_curlies;              /* Whether the
>> > constants
>> > > > > > were in {}. */
>> > > > > >  >> > > > -    char *as_name;                /* Name of an
>> address
>> > set.
>> > > > > > It is NULL if not
>> > > > > >  >> > > > -                                     an address set.
>> */
>> > > > > >  >> > > >  };
>> > > > > >  >> > > >
>> > > > > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
>> > > > > > expr_constant_set *);
>> > > > > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
>> > > > > >  >> > > > index 94751d059..cff4f9e3c 100644
>> > > > > >  >> > > > --- a/lib/actions.c
>> > > > > >  >> > > > +++ b/lib/actions.c
>> > > > > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct
>> ovnact_load
>> > > > *load,
>> > > > > >  >> > > >              const struct ovnact_encode_params *ep,
>> > > > > >  >> > > >              struct ofpbuf *ofpacts)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > > -    const union expr_constant *c = &load->imm;
>> > > > > >  >> > > > +    const struct expr_constant *c = &load->imm;
>> > > > > >  >> > > >      struct mf_subfield dst =
>> > expr_resolve_field(&load->dst);
>> > > > > >  >> > > >      struct ofpact_set_field *sf =
>> > > > > > ofpact_put_set_field(ofpacts, dst.field,
>> > > > > >  >> > > >
>> > NULL,
>> > > > > > NULL);
>> > > > > >  >> > > > @@ -2077,7 +2077,7 @@
>> > > > > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
>> > > > > >  >> > > >          /* All empty_lb_backends fields are of type
>> > 'str' */
>> > > > > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
>> > > > > >  >> > > >
>> > > > > >  >> > > > -        const union expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > >          size_t size = strlen(c->string);
>> > > > > >  >> > > >          struct controller_event_opt_header hdr =
>> > > > > >  >> > > >              (struct controller_event_opt_header) {
>> > > > > >  >> > > > @@ -2553,7 +2553,7 @@
>> validate_empty_lb_backends(struct
>> > > > > > action_context *ctx,
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
>> > > > > >  >> > > >          const struct ovnact_gen_option *o =
>> &options[i];
>> > > > > >  >> > > > -        const union expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > >          struct sockaddr_storage ss;
>> > > > > >  >> > > >          struct uuid uuid;
>> > > > > >  >> > > >
>> > > > > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const
>> > struct
>> > > > > > ovnact_gen_option *o,
>> > > > > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
>> 2);
>> > > > > >  >> > > >      opt_header[0] = o->option->code;
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>> > > > > >  >> > > >      size_t n_values = o->value.n_values;
>> > > > > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
>> > > > > >  >> > > >          !strcmp(o->option->type, "uint8")) {
>> > > > > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const
>> > struct
>> > > > > > ovnact_gen_option *o,
>> > > > > >  >> > > >                           struct ofpbuf *ofpacts)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      struct dhcpv6_opt_header *opt =
>> > > > > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>> > > > > >  >> > > >      size_t n_values = o->value.n_values;
>> > > > > >  >> > > >      size_t size;
>> > > > > >  >> > > >
>> > > > > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const
>> struct
>> > > > > > ovnact_put_opts *pdo,
>> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
>> > > > > > DHCP_OPT_BOOTFILE_CODE);
>> > > > > >  >> > > >      if (boot_opt) {
>> > > > > >  >> > > >          uint8_t *opt_header =
>> ofpbuf_put_zeros(ofpacts,
>> > 2);
>> > > > > >  >> > > > -        const union expr_constant *c =
>> > > > boot_opt->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> > > > boot_opt->value.values;
>> > > > > >  >> > > >          opt_header[0] = boot_opt->option->code;
>> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
>> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string,
>> opt_header[1]);
>> > > > > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const
>> struct
>> > > > > > ovnact_put_opts *pdo,
>> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
>> > > > > > DHCP_OPT_BOOTFILE_ALT_CODE);
>> > > > > >  >> > > >      if (boot_alt_opt) {
>> > > > > >  >> > > >          uint8_t *opt_header =
>> ofpbuf_put_zeros(ofpacts,
>> > 2);
>> > > > > >  >> > > > -        const union expr_constant *c =
>> > > > > > boot_alt_opt->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> > > > > > boot_alt_opt->value.values;
>> > > > > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
>> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
>> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string,
>> opt_header[1]);
>> > > > > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const
>> struct
>> > > > > > ovnact_put_opts *pdo,
>> > > > > >  >> > > >          pdo->options, pdo->n_options,
>> > > > DHCP_OPT_NEXT_SERVER_CODE);
>> > > > > >  >> > > >      if (next_server_opt) {
>> > > > > >  >> > > >          uint8_t *opt_header =
>> ofpbuf_put_zeros(ofpacts,
>> > 2);
>> > > > > >  >> > > > -        const union expr_constant *c =
>> > > > > > next_server_opt->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> > > > > > next_server_opt->value.values;
>> > > > > >  >> > > >          opt_header[0] =
>> next_server_opt->option->code;
>> > > > > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
>> > > > > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
>> > > > sizeof(ovs_be32));
>> > > > > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
>> > > > > > action_context *ctx, const struct expr_field *dst,
>> > > > > >  >> > > >      /* Let's validate the options. */
>> > > > > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
>> > > > > >  >> > > >          const struct ovnact_gen_option *o =
>> > &po->options[i];
>> > > > > >  >> > > > -        const union expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > > +        const struct expr_constant *c =
>> o->value.values;
>> > > > > >  >> > > >          if (o->value.n_values > 1) {
>> > > > > >  >> > > >              lexer_error(ctx->lexer, "Invalid value
>> for
>> > \"%s\"
>> > > > > > option",
>> > > > > >  >> > > >                          o->option->name);
>> > > > > >  >> > > > @@ -3490,7 +3490,7 @@ static void
>> > > > > >  >> > > >  encode_put_nd_ra_option(const struct
>> ovnact_gen_option
>> > *o,
>> > > > > >  >> > > >                          struct ofpbuf *ofpacts,
>> ptrdiff_t
>> > > > > > ra_offset)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>> > > > > >  >> > > >
>> > > > > >  >> > > >      switch (o->option->code) {
>> > > > > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
>> > > > > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
>> > > > > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
>> > > > > >  >> > > > --- a/lib/expr.c
>> > > > > >  >> > > > +++ b/lib/expr.c
>> > > > > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type
>> type,
>> > struct
>> > > > > > expr *a, struct expr *b)
>> > > > > >  >> > > >          } else {
>> > > > > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
>> > > > > >  >> > > >          }
>> > > > > >  >> > > > -        free(a->as_name);
>> > > > > >  >> > > >          a->as_name = NULL;
>> > > > > >  >> > > >          return a;
>> > > > > >  >> > > >      } else if (b->type == type) {
>> > > > > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
>> > > > > >  >> > > > -        free(b->as_name);
>> > > > > >  >> > > >          b->as_name = NULL;
>> > > > > >  >> > > >          return b;
>> > > > > >  >> > > >      } else {
>> > > > > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
>> > > > > > expr_context *, struct expr_field *);
>> > > > > >  >> > > >
>> > > > > >  >> > > >  static struct expr *
>> > > > > >  >> > > >  make_cmp__(const struct expr_field *f, enum
>> expr_relop r,
>> > > > > >  >> > > > -             const union expr_constant *c)
>> > > > > >  >> > > > +             const struct expr_constant *c)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
>> > > > > >  >> > > >      e->type = EXPR_T_CMP;
>> > > > > >  >> > > >      e->cmp.symbol = f->symbol;
>> > > > > >  >> > > >      e->cmp.relop = r;
>> > > > > >  >> > > > +    e->as_name = c->as_name;
>> > > > > >  >> > > >      if (f->symbol->width) {
>> > > > > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
>> > > > > >  >> > > >                       &e->cmp.value, sizeof
>> e->cmp.value,
>> > > > f->ofs,
>> > > > > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct
>> expr_field *f,
>> > > > > > enum expr_relop r,
>> > > > > >  >> > > >
>> > > > > >  >> > > >  /* Returns the minimum reasonable width for integer
>> > constant
>> > > > > > 'c'. */
>> > > > > >  >> > > >  static int
>> > > > > >  >> > > > -expr_constant_width(const union expr_constant *c)
>> > > > > >  >> > > > +expr_constant_width(const struct expr_constant *c)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      if (c->masked) {
>> > > > > >  >> > > >          return mf_subvalue_width(&c->mask);
>> > > > > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context
>> *ctx,
>> > > > > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR :
>> > > > EXPR_T_AND,
>> > > > > >  >> > > >                           e, make_cmp__(f, r,
>> > > > &cs->values[i]));
>> > > > > >  >> > > >      }
>> > > > > >  >> > > > -    /* Track address set */
>> > > > > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
>> > > > cs->as_name) {
>> > > > > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
>> > > > > >  >> > > > -    }
>> > > > > >  >> > > > +
>> > > > > >  >> > > >  exit:
>> > > > > >  >> > > >      expr_constant_set_destroy(cs);
>> > > > > >  >> > > >      return e;
>> > > > > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct
>> expr_context
>> > > > > > *ctx, struct expr_constant_set *cs,
>> > > > > >  >> > > >          }
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    struct expr_constant_set *addr_sets
>> > > > > >  >> > > > -        = (ctx->addr_sets
>> > > > > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
>> > > > ctx->lexer->token.s)
>> > > > > >  >> > > > -           : NULL);
>> > > > > >  >> > > > -    if (!addr_sets) {
>> > > > > >  >> > > > +    struct shash_node *node = ctx->addr_sets
>> > > > > >  >> > > > +                              ?
>> > shash_find(ctx->addr_sets,
>> > > > > > ctx->lexer->token.s)
>> > > > > >  >> > > > +                              : NULL;
>> > > > > >  >> > > > +    if (!node) {
>> > > > > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting
>> > address set
>> > > > > > name");
>> > > > > >  >> > > >          return false;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct
>> expr_context
>> > > > > > *ctx, struct expr_constant_set *cs,
>> > > > > >  >> > > >          return false;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    if (!cs->n_values) {
>> > > > > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
>> > > > > >  >> > > > -    }
>> > > > > >  >> > > > -
>> > > > > >  >> > > > +    struct expr_constant_set *addr_sets = node->data;
>> > > > > >  >> > > >      size_t n_values = cs->n_values +
>> addr_sets->n_values;
>> > > > > >  >> > > >      if (n_values >= *allocated_values) {
>> > > > > >  >> > > >          cs->values = xrealloc(cs->values, n_values *
>> > sizeof
>> > > > > > *cs->values);
>> > > > > >  >> > > >          *allocated_values = n_values;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values; i++)
>> {
>> > > > > >  >> > > > -        cs->values[cs->n_values++] =
>> > addr_sets->values[i];
>> > > > > >  >> > > > +        struct expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +        *c = addr_sets->values[i];
>> > > > > >  >> > > > +        c->as_name = node->name;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > >      return true;
>> > > > > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct
>> expr_context
>> > *ctx,
>> > > > > > struct expr_constant_set *cs,
>> > > > > >  >> > > >          *allocated_values = n_values;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >      for (size_t i = 0; i < port_group->n_values;
>> i++) {
>> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
>> > > > > >  >> > > > -            xstrdup(port_group->values[i].string);
>> > > > > >  >> > > > +        struct expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +        c->string =
>> > xstrdup(port_group->values[i].string);
>> > > > > >  >> > > > +        c->as_name = NULL;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > >      return true;
>> > > > > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct
>> expr_context
>> > *ctx,
>> > > > > > struct expr_constant_set *cs,
>> > > > > >  >> > > >                                  sizeof *cs->values);
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    /* Combining other values to the constant set
>> that is
>> > > > > > tracking an
>> > > > > >  >> > > > -     * address set, so untrack it. */
>> > > > > >  >> > > > -    free(cs->as_name);
>> > > > > >  >> > > > -    cs->as_name = NULL;
>> > > > > >  >> > > > -
>> > > > > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>> > > > > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded
>> template.");
>> > > > > >  >> > > >          return false;
>> > > > > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct expr_context
>> > *ctx,
>> > > > > > struct expr_constant_set *cs,
>> > > > > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
>> > > > EXPR_C_STRING)) {
>> > > > > >  >> > > >              return false;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
>> > > > > > xstrdup(ctx->lexer->token.s);
>> > > > > >  >> > > > +        struct expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
>> > > > > >  >> > > > +        c->as_name = NULL;
>> > > > > >  >> > > >          lexer_get(ctx->lexer);
>> > > > > >  >> > > >          return true;
>> > > > > >  >> > > >      } else if (ctx->lexer->token.type ==
>> LEX_T_INTEGER ||
>> > > > > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct
>> expr_context
>> > *ctx,
>> > > > > > struct expr_constant_set *cs,
>> > > > > >  >> > > >              return false;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -        union expr_constant *c =
>> > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +        struct expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > >          c->value = ctx->lexer->token.value;
>> > > > > >  >> > > >          c->format = ctx->lexer->token.format;
>> > > > > >  >> > > >          c->masked = ctx->lexer->token.type ==
>> > > > > > LEX_T_MASKED_INTEGER;
>> > > > > >  >> > > >          if (c->masked) {
>> > > > > >  >> > > >              c->mask = ctx->lexer->token.mask;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > > +        c->as_name = NULL;
>> > > > > >  >> > > >          lexer_get(ctx->lexer);
>> > > > > >  >> > > >          return true;
>> > > > > >  >> > > >      } else if (ctx->lexer->token.type ==
>> LEX_T_MACRO) {
>> > > > > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct
>> expr_context
>> > > > > > *ctx, struct expr_constant_set *cs)
>> > > > > >  >> > > >   * indeterminate. */
>> > > > > >  >> > > >  bool
>> > > > > >  >> > > >  expr_constant_parse(struct lexer *lexer, const struct
>> > > > > > expr_field *f,
>> > > > > >  >> > > > -                    union expr_constant *c)
>> > > > > >  >> > > > +                    struct expr_constant *c)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      if (lexer->error) {
>> > > > > >  >> > > >          return false;
>> > > > > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer
>> > *lexer,
>> > > > > > const struct expr_field *f,
>> > > > > >  >> > > >  /* Appends to 's' a re-parseable representation of
>> > constant
>> > > > > > 'c' with the given
>> > > > > >  >> > > >   * 'type'. */
>> > > > > >  >> > > >  void
>> > > > > >  >> > > > -expr_constant_format(const union expr_constant *c,
>> > > > > >  >> > > > +expr_constant_format(const struct expr_constant *c,
>> > > > > >  >> > > >                       enum expr_constant_type type,
>> > struct ds
>> > > > *s)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      if (type == EXPR_C_STRING) {
>> > > > > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const union
>> > > > > > expr_constant *c,
>> > > > > >  >> > > >   *
>> > > > > >  >> > > >   * Does not free(c). */
>> > > > > >  >> > > >  void
>> > > > > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
>> > > > > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
>> > > > > >  >> > > >                        enum expr_constant_type type)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      if (c && type == EXPR_C_STRING) {
>> > > > > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const
>> > struct
>> > > > > > expr_constant_set *cs, struct ds *s)
>> > > > > >  >> > > >          ds_put_char(s, '{');
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    for (const union expr_constant *c = cs->values;
>> > > > > >  >> > > > +    for (const struct expr_constant *c = cs->values;
>> > > > > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
>> > > > > >  >> > > >          if (c != cs->values) {
>> > > > > >  >> > > >              ds_put_cstr(s, ", ");
>> > > > > >  >> > > > @@ -1067,15 +1060,14 @@
>> expr_constant_set_destroy(struct
>> > > > > > expr_constant_set *cs)
>> > > > > >  >> > > >              }
>> > > > > >  >> > > >          }
>> > > > > >  >> > > >          free(cs->values);
>> > > > > >  >> > > > -        free(cs->as_name);
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >  }
>> > > > > >  >> > > >
>> > > > > >  >> > > >  static int
>> > > > > >  >> > > >  compare_expr_constant_integer_cb(const void *a_,
>> const
>> > void
>> > > > *b_)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > > -    const union expr_constant *a = a_;
>> > > > > >  >> > > > -    const union expr_constant *b = b_;
>> > > > > >  >> > > > +    const struct expr_constant *a = a_;
>> > > > > >  >> > > > +    const struct expr_constant *b = b_;
>> > > > > >  >> > > >
>> > > > > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof
>> > a->value);
>> > > > > >  >> > > >      if (d) {
>> > > > > >  >> > > > @@ -1114,7 +1106,7 @@
>> > expr_constant_set_create_integers(const
>> > > > > > char *const *values, size_t n_values)
>> > > > > >  >> > > >              VLOG_WARN("Invalid constant set entry:
>> '%s',
>> > > > > > token type: %d",
>> > > > > >  >> > > >                        values[i], lex.token.type);
>> > > > > >  >> > > >          } else {
>> > > > > >  >> > > > -            union expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +            struct expr_constant *c =
>> > > > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > >              c->value = lex.token.value;
>> > > > > >  >> > > >              c->format = lex.token.format;
>> > > > > >  >> > > >              c->masked = lex.token.type ==
>> > > > LEX_T_MASKED_INTEGER;
>> > > > > >  >> > > > @@ -1135,7 +1127,7 @@
>> > expr_constant_set_create_integers(const
>> > > > > > char *const *values, size_t n_values)
>> > > > > >  >> > > >
>> > > > > >  >> > > >  static void
>> > > > > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set
>> > **p_cs,
>> > > > > >  >> > > > -                            union expr_constant *c,
>> > size_t
>> > > > > > *allocated)
>> > > > > >  >> > > > +                            struct expr_constant *c,
>> > size_t
>> > > > > > *allocated)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > >      struct expr_constant_set *cs = *p_cs;
>> > > > > >  >> > > >      if (!cs) {
>> > > > > >  >> > > > @@ -1246,7 +1238,7 @@
>> expr_const_sets_add_strings(struct
>> > shash
>> > > > > > *const_sets, const char *name,
>> > > > > >  >> > > >                          values[i], name);
>> > > > > >  >> > > >              continue;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > > -        union expr_constant *c =
>> > &cs->values[cs->n_values++];
>> > > > > >  >> > > > +        struct expr_constant *c =
>> > > > &cs->values[cs->n_values++];
>> > > > > >  >> > > >          c->string = xstrdup(values[i]);
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct
>> > expr_context
>> > > > > > *ctx, bool *atomic)
>> > > > > >  >> > > >
>> > > > > >  >> > > >              *atomic = true;
>> > > > > >  >> > > >
>> > > > > >  >> > > > -            union expr_constant *cst = xzalloc(sizeof
>> > *cst);
>> > > > > >  >> > > > +            struct expr_constant *cst =
>> xzalloc(sizeof
>> > *cst);
>> > > > > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
>> > > > > >  >> > > >              cst->masked = false;
>> > > > > >  >> > > >
>> > > > > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct
>> > expr_context
>> > > > > > *ctx, bool *atomic)
>> > > > > >  >> > > >              c.values = cst;
>> > > > > >  >> > > >              c.n_values = 1;
>> > > > > >  >> > > >              c.in_curlies = false;
>> > > > > >  >> > > > -            c.as_name = NULL;
>> > > > > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>> > > > > >  >> > > >          } else if (parse_relop(ctx, &r) &&
>> > > > > > parse_constant_set(ctx, &c)) {
>> > > > > >  >> > > >              return make_cmp(ctx, &f, r, &c);
>> > > > > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct shash
>> > > > *symtab)
>> > > > > >  >> > > >  static struct expr *
>> > > > > >  >> > > >  expr_clone_cmp(struct expr *expr)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
>> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>> > > > > >  >> > > >      if (!new->cmp.symbol->width) {
>> > > > > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
>> > > > > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr
>> *expr)
>> > > > > >  >> > > >  static struct expr *
>> > > > > >  >> > > >  expr_clone_condition(struct expr *expr)
>> > > > > >  >> > > >  {
>> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
>> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>> > > > > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
>> > > > > >  >> > > >      return new;
>> > > > > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>> > > > > >  >> > > >          return;
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    free(expr->as_name);
>> > > > > >  >> > > > -
>> > > > > >  >> > > >      struct expr *sub;
>> > > > > >  >> > > >
>> > > > > >  >> > > >      switch (expr->type) {
>> > > > > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr
>> > *expr,
>> > > > > > const struct expr_symbol *symbol)
>> > > > > >  >> > > >       * mask sizes. */
>> > > > > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
>> > > > > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
>> > > > > >  >> > > > -    bool modified = false;
>> > > > > >  >> > > > +    bool has_addr_set = false;
>> > > > > >  >> > > >      /* Linked list over the 'subs' array to quickly
>> skip
>> > > > > > deleted elements,
>> > > > > >  >> > > >       * i.e. the index of the next potentially
>> non-NULL
>> > > > > > element. */
>> > > > > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
>> > > > > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr
>> > *expr,
>> > > > > > const struct expr_symbol *symbol)
>> > > > > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
>> > > > > >  >> > > >      struct expr *sub;
>> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
>> > > > > >  >> > > > +        if (sub->as_name) {
>> > > > > >  >> > > > +            has_addr_set = true;
>> > > > > >  >> > > > +        }
>> > > > > >  >> > > >          if (symbol->width) {
>> > > > > >  >> > > >              const unsigned long *sub_mask;
>> > > > > >  >> > > >
>> > > > > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct expr
>> > *expr,
>> > > > > > const struct expr_symbol *symbol)
>> > > > > >  >> > > >              next[last] = i;
>> > > > > >  >> > > >              last = i;
>> > > > > >  >> > > >          } else {
>> > > > > >  >> > > > +            /* Remove address set reference from the
>> > > > > > duplicate. */
>> > > > > >  >> > > > +            subs[last]->as_name = NULL;
>> > > > > >  >> > > >              expr_destroy(subs[i]);
>> > > > > >  >> > > >              subs[i] = NULL;
>> > > > > >  >> > > > -            modified = true;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    if (!symbol->width || symbol->level !=
>> EXPR_L_ORDINAL
>> > > > > >  >> > > > -        || (!modified && expr->as_name)) {
>> > > > > >  >> > > > +    if (!symbol->width || symbol->level !=
>> > EXPR_L_ORDINAL ||
>> > > > > > has_addr_set) {
>> > > > > >  >> > > >          /* Not a fully maskable field or this
>> expression
>> > is
>> > > > > > tracking an
>> > > > > >  >> > > >           * address set.  Don't try to optimize to
>> > preserve
>> > > > > > address set I-P. */
>> > > > > >  >> > > >          goto done;
>> > > > > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct expr
>> > *expr,
>> > > > > > const struct expr_symbol *symbol)
>> > > > > >  >> > > >              if (expr_bitmap_intersect_check(a_value,
>> > a_mask,
>> > > > > > b_value, b_mask,
>> > > > > >  >> > > >
>> bit_width)
>> > > > > >  >> > > >                  && bitmap_is_superset(b_mask, a_mask,
>> > > > > > bit_width)) {
>> > > > > >  >> > > > -                /* 'a' is the same expression with a
>> > smaller
>> > > > > > mask. */
>> > > > > >  >> > > > +                /* 'a' is the same expression with a
>> > smaller
>> > > > > > mask.
>> > > > > >  >> > > > +                 * Remove address set reference from
>> the
>> > > > > > duplicate. */
>> > > > > >  >> > > > +                a->as_name = NULL;
>> > > > > >  >> > > >                  expr_destroy(subs[j]);
>> > > > > >  >> > > >                  subs[j] = NULL;
>> > > > > >  >> > > > -                modified = true;
>> > > > > >  >> > > >
>> > > > > >  >> > > >                  /* Shorten the path for the next
>> round.
>> > */
>> > > > > >  >> > > >                  if (last) {
>> > > > > >  >> > > > @@ -2685,12 +2676,6 @@ done:
>> > > > > >  >> > > >          }
>> > > > > >  >> > > >      }
>> > > > > >  >> > > >
>> > > > > >  >> > > > -    if (modified) {
>> > > > > >  >> > > > -        /* Members modified, so untrack address set.
>> */
>> > > > > >  >> > > > -        free(expr->as_name);
>> > > > > >  >> > > > -        expr->as_name = NULL;
>> > > > > >  >> > > > -    }
>> > > > > >  >> > > > -
>> > > > > >  >> > > >      free(next);
>> > > > > >  >> > > >      free(subs);
>> > > > > >  >> > > >      return expr;
>> > > > > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct
>> expr
>> > *or,
>> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
>> > > > > >  >> > > >          struct expr_match *match = expr_match_new(m,
>> > clause,
>> > > > > > n_clauses,
>> > > > > >  >> > > >
>> >  conj_id);
>> > > > > >  >> > > > -        if (or->as_name) {
>> > > > > >  >> > > > +        if (sub->as_name) {
>> > > > > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
>> > > > > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
>> > > > > >  >> > > > -            match->as_name = xstrdup(or->as_name);
>> > > > > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
>> > > > > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
>> > > > > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
>> > > > > >  >> > > >          }
>> > > > > >  >> > > > diff --git a/tests/ovn-controller.at
>> > > > > > <http://ovn-controller.at> b/tests/ovn-controller.at
>> > > > > > <http://ovn-controller.at>
>> > > > > >  >> > > > index be198e00d..27cec2aec 100644
>> > > > > >  >> > > > --- a/tests/ovn-controller.at <
>> http://ovn-controller.at>
>> > > > > >  >> > > > +++ b/tests/ovn-controller.at <
>> http://ovn-controller.at>
>> > > > > >  >> > > > @@ -1625,7 +1625,9 @@
>> > > > > >
>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
>> > > > > > actions=lo
>> > > > > >  >> > > >  done
>> > > > > >  >> > > >
>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>> consider_logical_flow)
>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [10
>> > > > > >  >> > > > +# First 2 reprocess are due to change from 1 IP in
>> AS to
>> > 2.
>> > > > > >  >> > > > +# Last 5 is due to overlap in IP addresses between
>> as1
>> > and
>> > > > as2.
>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [7
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >
>> > > > > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
>> > > > > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
>> > > > > >  >> > > >  done
>> > > > > >  >> > > >
>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>> consider_logical_flow)
>> > > > > >  >> > > > -# In this case the as1 and as2 are merged to a
>> single OR
>> > > > > > expr, so we lose track of
>> > > > > >  >> > > > -# address set information - can't handle deletion
>> > > > incrementally.
>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [10
>> > > > > >  >> > > > +# First 5 are due to overlap in IP addresses between
>> as1
>> > and
>> > > > as2.
>> > > > > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [7
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >
>> > > > > >  >> > > >  OVN_CLEANUP([hv1])
>> > > > > >  >> > > > @@ -1817,7 +1819,7 @@
>> > > > > >
>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
>> > > > > > actions=co
>> > > > > >  >> > > >
>> > > > > >
>> > > >
>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>> > > > actions=conjunction,2/2)
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>> consider_logical_flow)
>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [1
>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [0
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >
>> > > > > >  >> > > >  # Delete 2 IPs
>> > > > > >  >> > > > @@ -1837,7 +1839,7 @@
>> > > > > >
>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
>> > > > > > actions=co
>> > > > > >  >> > > >
>> > > > > >
>> > > >
>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>> > > > actions=conjunction,2/2)
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>> consider_logical_flow)
>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [1
>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>> > > > > > $reprocess_count_old))], [0], [0
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > >
>> > > > > >  >> > > >  OVN_CLEANUP([hv1])
>> > > > > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
>> > > > > >  >> > > >
>> > > > > >  >> > > >  AT_CLEANUP
>> > > > > >  >> > > >  ])
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute
>> > consistency])
>> > > > > >  >> > > > +AT_KEYWORDS([as-i-p])
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +ovn_start
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +net_add n1
>> > > > > >  >> > > > +sim_add hv1
>> > > > > >  >> > > > +as hv1
>> > > > > >  >> > > > +check ovs-vsctl add-br br-phys
>> > > > > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
>> > > > > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
>> > > > > >  >> > > > +    set interface hv1-vif1
>> external-ids:iface-id=ls1-lp1
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +check ovn-nbctl ls-add ls1
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
>> > > > > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +wait_for_ports_up
>> > > > > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +# Get the OF table numbers
>> > > > > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable
>> > ls_out_acl_eval)
>> > > > > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
>> > > > ls_out_acl_action)
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath
>> tunnel_key
>> > > > > > external_ids:name=ls1))
>> > > > > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding
>> > tunnel_key
>> > > > > > logical_port=ls1-lp1))
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +ovn-nbctl create address_set name=as1
>> > > > > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
>> > > > > > "ls1-lp1" && ip4.src == $as1' drop
>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>> 10.0.0.0/24
>> > > > > > <http://10.0.0.0/24>
>> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
>> $7,
>> > $8}'
>> > > > > > | sort], [0], [dnl
>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>> > 10.0.0.0/24
>> > > > > > <http://10.0.0.0/24>
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > > +])
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>> 10.0.0.1
>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>> 10.0.0.2
>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>> 10.0.0.3
>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>> 10.0.0.4
>> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
>> $7,
>> > $8}'
>> > > > > > | sort], [0], [dnl
>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>> > 10.0.0.0/24
>> > > > > > <http://10.0.0.0/24>
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > > +])
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +check ovn-appctl inc-engine/recompute
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print
>> $7,
>> > $8}'
>> > > > > > | sort], [0], [dnl
>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>> > 10.0.0.0/24
>> > > > > > <http://10.0.0.0/24>
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > >
>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>> > > > > >  >> > > > +])
>> > > > > >  >> > > > +
>> > > > > >  >> > > > +OVN_CLEANUP([hv1])
>> > > > > >  >> > > > +AT_CLEANUP
>> > > > > >  >> > > > --
>> > > > > >  >> > > > 2.44.0
>> > > > > >  >> > > >
>> > > > > >  >> >
>> > > > > >  >> >
>> > > > > >  >> > Thank you Han,
>> > > > > >  >> >
>> > > > > >  >> > I would like to start the discussion about the
>> possibility of
>> > > > > >  >> > backporting this change. The reason for the backport being
>> > that
>> > > > this
>> > > > > >  >> > change might be beneficial to ovn-kubernetes users, as
>> this
>> > was
>> > > > > >  >> > actually observed in ovn-kubernetes with network
>> policies. I
>> > know
>> > > > that
>> > > > > >  >> > strictly speaking this is not a bug fix, because this
>> > limitation
>> > > > was
>> > > > > >  >> > well documented, however I would like to hear the opinion
>> of
>> > > > others.
>> > > > > >  >>
>> > > > > >  >>
>> > > > > >  >> I have no objections if this improvement is strongly
>> required
>> > for
>> > > > > > released versions, even though it is not a bug. May I ask for
>> which
>> > > > > > releases do you want this to be backported?
>> > > > > >  >
>> > > > > >  >
>> > > > > >  > We have a request for 23.09 and 24.03.
>> > > > > >  >
>> > > > > > Ok. I will wait for input from other maintainers. It will be a
>> > tradeoff
>> > > > > > between risk and performance gains. Mark, Numan and Dumitru,
>> what
>> > do you
>> > > > > > think?
>> > > > > >
>> > > > > > Thanks,
>> > > > > > Han
>> > > > >
>> > > > > I'm OK with the patch being backported. I think our test coverage
>> is
>> > > > > sufficient to uncover problems that may arise from the backport,
>> so
>> > the
>> > > > > risk should be minimal.
>> > > > >
>> > > >
>> > > > Thanks Numan and Mark for confirming, so we got a quorum. I
>> backported
>> > the
>> > > > patch to 24.03 and 23.09.
>> > >
>> > > This patch is using the "ovn-debug" binary which is missing in
>> > > branch-24.03 and branch-23.09.
>> > > Because of this, the test case "AS I-P and recompute consistency" is
>> > > failing.  I backported
>> > > the commit "117573e2d279" to these branches.
>> > >
>> > > While backporting to branch-23.09,  had to move the pipeline stage
>> macros
>> > from
>> > > northd.c to northd.h.  Running the tests in my github repo before
>> pushing
>> > > to upstream branch-23.09.
>> > >
>> >
>> > Oh, no. Thanks Numan for fixing! I ran test locally after switching
>> > branches and all were successful, likely because the ovn-debug binary is
>> > already in my environment, so I pushed late at night and see this just
>> now.
>> > Sorry for the trouble.
>> >
>>
>> No worries.  I passed for me locally too until I deleted the binary.
>>
>> Numan
>>
>> > Regards,
>> > Han
>> >
>> > > Numan
>> > >
>> > > >
>> > > > Han
>> > > >
>> > > > > >
>> > > > > >  >>
>> > > > > >  >>
>> > > > > >  >> Thanks,
>> > > > > >  >> Han
>> > > > > >  >>
>> > > > > >  >> >
>> > > > > >  >> > Thanks,
>> > > > > >  >> > Ales
>> > > > > >  >> > --
>> > > > > >  >> >
>> > > > > >  >> > Ales Musil
>> > > > > >  >> >
>> > > > > >  >> > Senior Software Engineer - OVN Core
>> > > > > >  >> >
>> > > > > >  >> > Red Hat EMEA
>> > > > > >  >> >
>> > > > > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
>> > > > > >  >> >
>> > > > > >  >
>> > > > > >  >
>> > > > > >  >
>> > > > > >  > --
>> > > > > >  >
>> > > > > >  > Ales Musil
>> > > > > >  >
>> > > > > >  > Senior Software Engineer - OVN Core
>> > > > > >  >
>> > > > > >  > Red Hat EMEA
>> > > > > >  >
>> > > > > >  > amusil@redhat.com <mailto:amusil@redhat.com>
>> > > > >
>> > > > _______________________________________________
>> > > > dev mailing list
>> > > > dev@openvswitch.org
>> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>> > _______________________________________________
>> > dev mailing list
>> > dev@openvswitch.org
>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>
Sri kor May 13, 2024, 7:17 a.m. UTC | #14
Thanks Ales, This address set is not present in SB DB.

-Srini

On Sun, May 12, 2024 at 10:26 PM Ales Musil <amusil@redhat.com> wrote:

>
>
> On Sun, May 12, 2024 at 11:17 PM Sri kor <esrikor1@gmail.com> wrote:
>
>> Hi @numans@ovn.org <numans@ovn.org> @Ales Musil <amusil@redhat.com> ,
>>     Currently we are on OVN 23.09. We are facing the following syntax
>> error with the address_set.From the diff, looks like you address this fix.
>> Is this something expected?
>>
>> *2024-05-11T17:35:28.694Z|153935|lflow|WARN|error parsing match "reg0[7]
>> == 1 && ((ip4.src ==
>> $a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
>> && (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
>> <= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
>> @a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c)":
>> Syntax error at
>> `$a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a'
>> expecting address set name.*
>>
>>
>> *Here are the transaction details .*
>>
>> *[{Op:insert Table:ACL Row:map[action:allow-related direction:to-lport
>> external_ids:{GoMap:map[crusoe_fw_rule_id:770b6929-88d1-4659-ba3b-ed84ccfd71a0
>> crusoe_vpc_id:0ea55446-0df6-4358-8442-56893854004c
>> customer_id:781ac228-38ae-4f04-9cc1-707543df50c8]} log:false match:(ip4.src
>> ==
>> $a_781ac228_38ae_4f04_9cc1_707543df50c8_addr_set_b8f7bf7f_83b3_48bc_a0d5_bc5d55438e4a)
>> && (172.27.0.0 <= ip4.dst <= 172.27.255.255) && (1 <= tcp.src <= 65535 || 1
>> <= udp.src <= 65535) && (tcp.dst == 22 || udp.dst == 22) && outport ==
>> @a_781ac228_38ae_4f04_9cc1_707543df50c8_pg_0ea55446_0df6_4358_8442_56893854004c
>> name:{GoSet:[a_781ac228_38ae_4f04_9cc1_707543df50c8_acl_770b6929_88d1_4659_b]}
>> priority:2000] Rows:[] Columns:[] Mutations:[] Timeout:<nil> Where:[]
>> Until: Durable:<nil> Comment:<nil> Lock:<nil>
>> UUIDName:_641d4ca1_7596_4004_b577_0f34e0bab87d} {Op:mutate Table:Port_Group
>> Row:map[] Rows:[] Columns:[] Mutations:[{Column:acls Mutator:insert
>> Value:{GoSet:[{GoUUID:_641d4ca1_7596_4004_b577_0f34e0bab87d}]}}]
>> Timeout:<nil> Where:[where column _uuid ==
>> {00383922-81ce-4491-9115-6ea2dfc79aea}] Until: Durable:<nil> Comment:<nil>
>> Lock:<nil> UUIDName:} {Op:mutate Table:NB_Global Row:map[] Rows:[]
>> Columns:[] Mutations:[{Column:nb_cfg Mutator:+= Value:1}] Timeout:<nil>
>> Where:[where column _uuid == {be2921c1-99ae-423b-a69c-a43e67d8424e}] Until:
>> Durable:<nil> Comment:<nil> Lock:<nil> UUIDName:} {Op:select
>> Table:NB_Global Row:map[] Rows:[] Columns:[nb_cfg] Mutations:[]
>> Timeout:<nil> Where:[] Until: Durable:<nil> Comment:<nil> Lock:<nil>
>> UUIDName:}]*
>>
>>
>> *Thanks*
>>
>> *Srini*
>>
>>
> Hi,
>
> this seems to be unrelated to the patch. It looks like the controller is
> not able to recognize the address set name and doesn't parse it correctly.
> Is this address set present in the SB DB?
>
> Thanks,
> Ales
>
> On Fri, May 10, 2024 at 9:43 AM Numan Siddique <numans@ovn.org> wrote:
>>
>>> On Fri, May 10, 2024 at 12:39 PM Han Zhou <hzhou@ovn.org> wrote:
>>> >
>>> > On Fri, May 10, 2024 at 9:23 AM Numan Siddique <numans@ovn.org> wrote:
>>> > >
>>> > > On Fri, May 10, 2024 at 2:37 AM Han Zhou <hzhou@ovn.org> wrote:
>>> > > >
>>> > > > On Thu, May 9, 2024 at 10:32 AM Mark Michelson <
>>> mmichels@redhat.com>
>>> > wrote:
>>> > > > >
>>> > > > > On 5/7/24 02:12, Han Zhou wrote:
>>> > > > > >
>>> > > > > >
>>> > > > > > On Mon, May 6, 2024 at 10:37 PM Ales Musil <amusil@redhat.com
>>> > > > > > <mailto:amusil@redhat.com>> wrote:
>>> > > > > >  >
>>> > > > > >  >
>>> > > > > >  >
>>> > > > > >  > On Mon, May 6, 2024 at 8:41 PM Han Zhou <hzhou@ovn.org
>>> > > > > > <mailto:hzhou@ovn.org>> wrote:
>>> > > > > >  >>
>>> > > > > >  >>
>>> > > > > >  >>
>>> > > > > >  >> On Thu, May 2, 2024 at 10:35 PM Ales Musil <
>>> amusil@redhat.com
>>> > > > > > <mailto:amusil@redhat.com>> wrote:
>>> > > > > >  >> >
>>> > > > > >  >> > On Thu, May 2, 2024 at 6:23 PM Han Zhou <hzhou@ovn.org
>>> > > > > > <mailto:hzhou@ovn.org>> wrote:
>>> > > > > >  >> > >
>>> > > > > >  >> > >
>>> > > > > >  >> > >
>>> > > > > >  >> > > On Thu, May 2, 2024 at 6:29 AM Ales Musil <
>>> amusil@redhat.com
>>> > > > > > <mailto:amusil@redhat.com>> wrote:
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > Instead of tracking address set per struct
>>> > expr_constant_set
>>> > > > > > track it
>>> > > > > >  >> > > > per individual struct expr_constant. This allows
>>> more fine
>>> > > > grained
>>> > > > > >  >> > > > control for I-P processing of address sets in
>>> controller.
>>> > It
>>> > > > > > helps with
>>> > > > > >  >> > > > scenarios like matching on two address sets in one
>>> > expression
>>> > > > e.g.
>>> > > > > >  >> > > > "ip4.src == {$as1, $as2}". This allows any addition
>>> or
>>> > > > removal of
>>> > > > > >  >> > > > individual adress from the set to be incrementally
>>> > processed
>>> > > > > > instead
>>> > > > > >  >> > > > of reprocessing all the flows.
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > This unfortunately doesn't help with the following
>>> flows:
>>> > > > > >  >> > > > "ip4.src == $as1 && ip4.dst == $as2"
>>> > > > > >  >> > > > "ip4.src == $as1 || ip4.dst == $as2"
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > The memory impact should be minimal as there is only
>>> > increase
>>> > > > > > of 8 bytes
>>> > > > > >  >> > > > per the struct expr_constant.
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > Reported-at:
>>> https://issues.redhat.com/browse/FDP-509
>>> > > > > > <https://issues.redhat.com/browse/FDP-509>
>>> > > > > >  >> > > > Signed-off-by: Ales Musil <amusil@redhat.com
>>> > > > > > <mailto:amusil@redhat.com>>
>>> > > > > >  >> > > > ---
>>> > > > > >  >> > > > v4: Rebase on top of current main.
>>> > > > > >  >> > > >     Update the "lflow_handle_addr_set_update" comment
>>> > > > > > according to suggestion from Han.
>>> > > > > >  >> > >
>>> > > > > >  >> > > Thanks Ales. I updated the commit message for the
>>> same, and
>>> > > > > > applied to main branch.
>>> > > > > >  >> > >
>>> > > > > >  >> > > Regards,
>>> > > > > >  >> > > Han
>>> > > > > >  >> > >
>>> > > > > >  >> > > > v3: Rebase on top of current main.
>>> > > > > >  >> > > >     Address comments from Han:
>>> > > > > >  >> > > >     - Adjust the comment for
>>> > "lflow_handle_addr_set_update" to
>>> > > > > > include remaning corner cases.
>>> > > > > >  >> > > >     - Make sure that the flows are consistent
>>> between I-P
>>> > and
>>> > > > > > recompute.
>>> > > > > >  >> > > > v2: Rebase on top of current main.
>>> > > > > >  >> > > >     Adjust the comment for I-P optimization.
>>> > > > > >  >> > > > ---
>>> > > > > >  >> > > >  controller/lflow.c      | 11 ++---
>>> > > > > >  >> > > >  include/ovn/actions.h   |  2 +-
>>> > > > > >  >> > > >  include/ovn/expr.h      | 46 ++++++++++---------
>>> > > > > >  >> > > >  lib/actions.c           | 20 ++++-----
>>> > > > > >  >> > > >  lib/expr.c              | 99
>>> > > > > > +++++++++++++++++------------------------
>>> > > > > >  >> > > >  tests/ovn-controller.at <http://ovn-controller.at>
>>> | 79
>>> > > > > > +++++++++++++++++++++++++++++---
>>> > > > > >  >> > > >  6 files changed, 154 insertions(+), 103 deletions(-)
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > diff --git a/controller/lflow.c b/controller/lflow.c
>>> > > > > >  >> > > > index 760ec0b41..1e05665a1 100644
>>> > > > > >  >> > > > --- a/controller/lflow.c
>>> > > > > >  >> > > > +++ b/controller/lflow.c
>>> > > > > >  >> > > > @@ -278,7 +278,7 @@ lflow_handle_changed_flows(struct
>>> > > > > > lflow_ctx_in *l_ctx_in,
>>> > > > > >  >> > > >  }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  static bool
>>> > > > > >  >> > > > -as_info_from_expr_const(const char *as_name, const
>>> union
>>> > > > > > expr_constant *c,
>>> > > > > >  >> > > > +as_info_from_expr_const(const char *as_name, const
>>> struct
>>> > > > > > expr_constant *c,
>>> > > > > >  >> > > >                          struct addrset_info
>>> *as_info)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      as_info->name = as_name;
>>> > > > > >  >> > > > @@ -644,14 +644,11 @@ as_update_can_be_handled(const
>>> char
>>> > > > > > *as_name, struct addr_set_diff *as_diff,
>>> > > > > >  >> > > >   *        generated.
>>> > > > > >  >> > > >   *
>>> > > > > >  >> > > >   *      - The sub expression of the address set is
>>> > combined
>>> > > > > > with other sub-
>>> > > > > >  >> > > > - *        expressions/constants, usually because of
>>> > > > > > disjunctions between
>>> > > > > >  >> > > > - *        sub-expressions/constants, e.g.:
>>> > > > > >  >> > > > + *        expressions/constants on different fields,
>>> > e.g.:
>>> > > > > >  >> > > >   *
>>> > > > > >  >> > > >   *          ip.src == $as1 || ip.dst == $as2
>>> > > > > >  >> > > > - *          ip.src == {$as1, $as2}
>>> > > > > >  >> > > > - *          ip.src == {$as1, ip1}
>>> > > > > >  >> > > >   *
>>> > > > > >  >> > > > - *        All these could have been split into
>>> separate
>>> > > > lflows.
>>> > > > > >  >> > > > + *        This could have been split into separate
>>> > lflows.
>>> > > > > >  >> > > >   *
>>> > > > > >  >> > > >   *      - Conjunctions overlapping between lflows,
>>> which
>>> > can
>>> > > > > > be caused by
>>> > > > > >  >> > > >   *        overlapping address sets or same address
>>> set
>>> > used
>>> > > > > > by multiple lflows
>>> > > > > >  >> > > > @@ -714,7 +711,7 @@
>>> lflow_handle_addr_set_update(const
>>> > char
>>> > > > > > *as_name,
>>> > > > > >  >> > > >          if (as_diff->deleted) {
>>> > > > > >  >> > > >              struct addrset_info as_info;
>>> > > > > >  >> > > >              for (size_t i = 0; i <
>>> > > > > > as_diff->deleted->n_values; i++) {
>>> > > > > >  >> > > > -                union expr_constant *c =
>>> > > > > > &as_diff->deleted->values[i];
>>> > > > > >  >> > > > +                struct expr_constant *c =
>>> > > > > > &as_diff->deleted->values[i];
>>> > > > > >  >> > > >                  if
>>> (!as_info_from_expr_const(as_name, c,
>>> > > > > > &as_info)) {
>>> > > > > >  >> > > >                      continue;
>>> > > > > >  >> > > >                  }
>>> > > > > >  >> > > > diff --git a/include/ovn/actions.h
>>> b/include/ovn/actions.h
>>> > > > > >  >> > > > index ae0864fdd..88cf4de79 100644
>>> > > > > >  >> > > > --- a/include/ovn/actions.h
>>> > > > > >  >> > > > +++ b/include/ovn/actions.h
>>> > > > > >  >> > > > @@ -241,7 +241,7 @@ struct ovnact_next {
>>> > > > > >  >> > > >  struct ovnact_load {
>>> > > > > >  >> > > >      struct ovnact ovnact;
>>> > > > > >  >> > > >      struct expr_field dst;
>>> > > > > >  >> > > > -    union expr_constant imm;
>>> > > > > >  >> > > > +    struct expr_constant imm;
>>> > > > > >  >> > > >  };
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  /* OVNACT_MOVE, OVNACT_EXCHANGE. */
>>> > > > > >  >> > > > diff --git a/include/ovn/expr.h b/include/ovn/expr.h
>>> > > > > >  >> > > > index c48f82398..e54edb5bf 100644
>>> > > > > >  >> > > > --- a/include/ovn/expr.h
>>> > > > > >  >> > > > +++ b/include/ovn/expr.h
>>> > > > > >  >> > > > @@ -368,7 +368,7 @@ bool expr_relop_from_token(enum
>>> > lex_type
>>> > > > > > type, enum expr_relop *relop);
>>> > > > > >  >> > > >  struct expr {
>>> > > > > >  >> > > >      struct ovs_list node;       /* In parent
>>> EXPR_T_AND
>>> > or
>>> > > > > > EXPR_T_OR if any. */
>>> > > > > >  >> > > >      enum expr_type type;        /* Expression type.
>>> */
>>> > > > > >  >> > > > -    char *as_name;              /* Address set name.
>>> > Null if
>>> > > > > > it is not an
>>> > > > > >  >> > > > +    const char *as_name;        /* Address set name.
>>> > Null if
>>> > > > > > it is not an
>>> > > > > >  >> > > >                                     address set. */
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      union {
>>> > > > > >  >> > > > @@ -505,40 +505,42 @@ enum expr_constant_type {
>>> > > > > >  >> > > >  };
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  /* A string or integer constant (one must know
>>> which from
>>> > > > > > context). */
>>> > > > > >  >> > > > -union expr_constant {
>>> > > > > >  >> > > > -    /* Integer constant.
>>> > > > > >  >> > > > -     *
>>> > > > > >  >> > > > -     * The width of a constant isn't always clear,
>>> e.g.
>>> > if
>>> > > > > > you write "1",
>>> > > > > >  >> > > > -     * there's no way to tell whether you mean for
>>> that
>>> > to be
>>> > > > > > a 1-bit constant
>>> > > > > >  >> > > > -     * or a 128-bit constant or somewhere in
>>> between. */
>>> > > > > >  >> > > > -    struct {
>>> > > > > >  >> > > > -        union mf_subvalue value;
>>> > > > > >  >> > > > -        union mf_subvalue mask; /* Only initialized
>>> if
>>> > > > > > 'masked'. */
>>> > > > > >  >> > > > -        bool masked;
>>> > > > > >  >> > > > -
>>> > > > > >  >> > > > -        enum lex_format format; /* From the
>>> constant's
>>> > > > > > lex_token. */
>>> > > > > >  >> > > > -    };
>>> > > > > >  >> > > > +struct expr_constant {
>>> > > > > >  >> > > > +    const char *as_name;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    /* Null-terminated string constant. */
>>> > > > > >  >> > > > -    char *string;
>>> > > > > >  >> > > > +    union {
>>> > > > > >  >> > > > +        /* Integer constant.
>>> > > > > >  >> > > > +         *
>>> > > > > >  >> > > > +         * The width of a constant isn't always
>>> clear,
>>> > e.g.
>>> > > > > > if you write "1",
>>> > > > > >  >> > > > +         * there's no way to tell whether you mean
>>> for
>>> > that
>>> > > > > > to be a 1-bit
>>> > > > > >  >> > > > +         * constant or a 128-bit constant or
>>> somewhere in
>>> > > > > > between. */
>>> > > > > >  >> > > > +        struct {
>>> > > > > >  >> > > > +            union mf_subvalue value;
>>> > > > > >  >> > > > +            union mf_subvalue mask; /* Only
>>> initialized
>>> > if
>>> > > > > > 'masked'. */
>>> > > > > >  >> > > > +            bool masked;
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +            enum lex_format format; /* From the
>>> > constant's
>>> > > > > > lex_token. */
>>> > > > > >  >> > > > +        };
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +        /* Null-terminated string constant. */
>>> > > > > >  >> > > > +        char *string;
>>> > > > > >  >> > > > +    };
>>> > > > > >  >> > > >  };
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  bool expr_constant_parse(struct lexer *,
>>> > > > > >  >> > > >                           const struct expr_field *,
>>> > > > > >  >> > > > -                         union expr_constant *);
>>> > > > > >  >> > > > -void expr_constant_format(const union expr_constant
>>> *,
>>> > > > > >  >> > > > +                         struct expr_constant *);
>>> > > > > >  >> > > > +void expr_constant_format(const struct
>>> expr_constant *,
>>> > > > > >  >> > > >                            enum expr_constant_type,
>>> > struct ds
>>> > > > *);
>>> > > > > >  >> > > > -void expr_constant_destroy(const union
>>> expr_constant *,
>>> > > > > >  >> > > > +void expr_constant_destroy(const struct
>>> expr_constant *,
>>> > > > > >  >> > > >                             enum expr_constant_type);
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  /* A collection of "union expr_constant"s of the
>>> same
>>> > type.
>>> > > > */
>>> > > > > >  >> > > >  struct expr_constant_set {
>>> > > > > >  >> > > > -    union expr_constant *values;  /* Constants. */
>>> > > > > >  >> > > > +    struct expr_constant *values; /* Constants. */
>>> > > > > >  >> > > >      size_t n_values;              /* Number of
>>> > constants. */
>>> > > > > >  >> > > >      enum expr_constant_type type; /* Type of the
>>> > constants.
>>> > > > */
>>> > > > > >  >> > > >      bool in_curlies;              /* Whether the
>>> > constants
>>> > > > > > were in {}. */
>>> > > > > >  >> > > > -    char *as_name;                /* Name of an
>>> address
>>> > set.
>>> > > > > > It is NULL if not
>>> > > > > >  >> > > > -                                     an address
>>> set. */
>>> > > > > >  >> > > >  };
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  bool expr_constant_set_parse(struct lexer *, struct
>>> > > > > > expr_constant_set *);
>>> > > > > >  >> > > > diff --git a/lib/actions.c b/lib/actions.c
>>> > > > > >  >> > > > index 94751d059..cff4f9e3c 100644
>>> > > > > >  >> > > > --- a/lib/actions.c
>>> > > > > >  >> > > > +++ b/lib/actions.c
>>> > > > > >  >> > > > @@ -450,7 +450,7 @@ encode_LOAD(const struct
>>> ovnact_load
>>> > > > *load,
>>> > > > > >  >> > > >              const struct ovnact_encode_params *ep,
>>> > > > > >  >> > > >              struct ofpbuf *ofpacts)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > > -    const union expr_constant *c = &load->imm;
>>> > > > > >  >> > > > +    const struct expr_constant *c = &load->imm;
>>> > > > > >  >> > > >      struct mf_subfield dst =
>>> > expr_resolve_field(&load->dst);
>>> > > > > >  >> > > >      struct ofpact_set_field *sf =
>>> > > > > > ofpact_put_set_field(ofpacts, dst.field,
>>> > > > > >  >> > > >
>>> > NULL,
>>> > > > > > NULL);
>>> > > > > >  >> > > > @@ -2077,7 +2077,7 @@
>>> > > > > > encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
>>> > > > > >  >> > > >          /* All empty_lb_backends fields are of type
>>> > 'str' */
>>> > > > > >  >> > > >          ovs_assert(!strcmp(o->option->type, "str"));
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > >          size_t size = strlen(c->string);
>>> > > > > >  >> > > >          struct controller_event_opt_header hdr =
>>> > > > > >  >> > > >              (struct controller_event_opt_header) {
>>> > > > > >  >> > > > @@ -2553,7 +2553,7 @@
>>> validate_empty_lb_backends(struct
>>> > > > > > action_context *ctx,
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      for (size_t i = 0; i < n_options; i++) {
>>> > > > > >  >> > > >          const struct ovnact_gen_option *o =
>>> &options[i];
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > >          struct sockaddr_storage ss;
>>> > > > > >  >> > > >          struct uuid uuid;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > @@ -2861,7 +2861,7 @@ encode_put_dhcpv4_option(const
>>> > struct
>>> > > > > > ovnact_gen_option *o,
>>> > > > > >  >> > > >      uint8_t *opt_header = ofpbuf_put_zeros(ofpacts,
>>> 2);
>>> > > > > >  >> > > >      opt_header[0] = o->option->code;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>>> > > > > >  >> > > >      size_t n_values = o->value.n_values;
>>> > > > > >  >> > > >      if (!strcmp(o->option->type, "bool") ||
>>> > > > > >  >> > > >          !strcmp(o->option->type, "uint8")) {
>>> > > > > >  >> > > > @@ -3027,7 +3027,7 @@ encode_put_dhcpv6_option(const
>>> > struct
>>> > > > > > ovnact_gen_option *o,
>>> > > > > >  >> > > >                           struct ofpbuf *ofpacts)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      struct dhcpv6_opt_header *opt =
>>> > > > > > ofpbuf_put_uninit(ofpacts, sizeof *opt);
>>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>>> > > > > >  >> > > >      size_t n_values = o->value.n_values;
>>> > > > > >  >> > > >      size_t size;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > @@ -3083,7 +3083,7 @@ encode_PUT_DHCPV4_OPTS(const
>>> struct
>>> > > > > > ovnact_put_opts *pdo,
>>> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
>>> > > > > > DHCP_OPT_BOOTFILE_CODE);
>>> > > > > >  >> > > >      if (boot_opt) {
>>> > > > > >  >> > > >          uint8_t *opt_header =
>>> ofpbuf_put_zeros(ofpacts,
>>> > 2);
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> > > > boot_opt->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> > > > boot_opt->value.values;
>>> > > > > >  >> > > >          opt_header[0] = boot_opt->option->code;
>>> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
>>> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string,
>>> opt_header[1]);
>>> > > > > >  >> > > > @@ -3093,7 +3093,7 @@ encode_PUT_DHCPV4_OPTS(const
>>> struct
>>> > > > > > ovnact_put_opts *pdo,
>>> > > > > >  >> > > >          find_opt(pdo->options, pdo->n_options,
>>> > > > > > DHCP_OPT_BOOTFILE_ALT_CODE);
>>> > > > > >  >> > > >      if (boot_alt_opt) {
>>> > > > > >  >> > > >          uint8_t *opt_header =
>>> ofpbuf_put_zeros(ofpacts,
>>> > 2);
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> > > > > > boot_alt_opt->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> > > > > > boot_alt_opt->value.values;
>>> > > > > >  >> > > >          opt_header[0] = boot_alt_opt->option->code;
>>> > > > > >  >> > > >          opt_header[1] = strlen(c->string);
>>> > > > > >  >> > > >          ofpbuf_put(ofpacts, c->string,
>>> opt_header[1]);
>>> > > > > >  >> > > > @@ -3103,7 +3103,7 @@ encode_PUT_DHCPV4_OPTS(const
>>> struct
>>> > > > > > ovnact_put_opts *pdo,
>>> > > > > >  >> > > >          pdo->options, pdo->n_options,
>>> > > > DHCP_OPT_NEXT_SERVER_CODE);
>>> > > > > >  >> > > >      if (next_server_opt) {
>>> > > > > >  >> > > >          uint8_t *opt_header =
>>> ofpbuf_put_zeros(ofpacts,
>>> > 2);
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> > > > > > next_server_opt->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> > > > > > next_server_opt->value.values;
>>> > > > > >  >> > > >          opt_header[0] =
>>> next_server_opt->option->code;
>>> > > > > >  >> > > >          opt_header[1] = sizeof(ovs_be32);
>>> > > > > >  >> > > >          ofpbuf_put(ofpacts, &c->value.ipv4,
>>> > > > sizeof(ovs_be32));
>>> > > > > >  >> > > > @@ -3307,7 +3307,7 @@ parse_put_nd_ra_opts(struct
>>> > > > > > action_context *ctx, const struct expr_field *dst,
>>> > > > > >  >> > > >      /* Let's validate the options. */
>>> > > > > >  >> > > >      for (size_t i = 0; i < po->n_options; i++) {
>>> > > > > >  >> > > >          const struct ovnact_gen_option *o =
>>> > &po->options[i];
>>> > > > > >  >> > > > -        const union expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > > +        const struct expr_constant *c =
>>> o->value.values;
>>> > > > > >  >> > > >          if (o->value.n_values > 1) {
>>> > > > > >  >> > > >              lexer_error(ctx->lexer, "Invalid value
>>> for
>>> > \"%s\"
>>> > > > > > option",
>>> > > > > >  >> > > >                          o->option->name);
>>> > > > > >  >> > > > @@ -3490,7 +3490,7 @@ static void
>>> > > > > >  >> > > >  encode_put_nd_ra_option(const struct
>>> ovnact_gen_option
>>> > *o,
>>> > > > > >  >> > > >                          struct ofpbuf *ofpacts,
>>> ptrdiff_t
>>> > > > > > ra_offset)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > > -    const union expr_constant *c = o->value.values;
>>> > > > > >  >> > > > +    const struct expr_constant *c = o->value.values;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      switch (o->option->code) {
>>> > > > > >  >> > > >      case ND_RA_FLAG_ADDR_MODE:
>>> > > > > >  >> > > > diff --git a/lib/expr.c b/lib/expr.c
>>> > > > > >  >> > > > index 0cb44e1b6..ecf8a6338 100644
>>> > > > > >  >> > > > --- a/lib/expr.c
>>> > > > > >  >> > > > +++ b/lib/expr.c
>>> > > > > >  >> > > > @@ -179,12 +179,10 @@ expr_combine(enum expr_type
>>> type,
>>> > struct
>>> > > > > > expr *a, struct expr *b)
>>> > > > > >  >> > > >          } else {
>>> > > > > >  >> > > >              ovs_list_push_back(&a->andor, &b->node);
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > > -        free(a->as_name);
>>> > > > > >  >> > > >          a->as_name = NULL;
>>> > > > > >  >> > > >          return a;
>>> > > > > >  >> > > >      } else if (b->type == type) {
>>> > > > > >  >> > > >          ovs_list_push_front(&b->andor, &a->node);
>>> > > > > >  >> > > > -        free(b->as_name);
>>> > > > > >  >> > > >          b->as_name = NULL;
>>> > > > > >  >> > > >          return b;
>>> > > > > >  >> > > >      } else {
>>> > > > > >  >> > > > @@ -521,12 +519,13 @@ static bool parse_field(struct
>>> > > > > > expr_context *, struct expr_field *);
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  static struct expr *
>>> > > > > >  >> > > >  make_cmp__(const struct expr_field *f, enum
>>> expr_relop r,
>>> > > > > >  >> > > > -             const union expr_constant *c)
>>> > > > > >  >> > > > +             const struct expr_constant *c)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      struct expr *e = xzalloc(sizeof *e);
>>> > > > > >  >> > > >      e->type = EXPR_T_CMP;
>>> > > > > >  >> > > >      e->cmp.symbol = f->symbol;
>>> > > > > >  >> > > >      e->cmp.relop = r;
>>> > > > > >  >> > > > +    e->as_name = c->as_name;
>>> > > > > >  >> > > >      if (f->symbol->width) {
>>> > > > > >  >> > > >          bitwise_copy(&c->value, sizeof c->value, 0,
>>> > > > > >  >> > > >                       &e->cmp.value, sizeof
>>> e->cmp.value,
>>> > > > f->ofs,
>>> > > > > >  >> > > > @@ -547,7 +546,7 @@ make_cmp__(const struct
>>> expr_field *f,
>>> > > > > > enum expr_relop r,
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  /* Returns the minimum reasonable width for integer
>>> > constant
>>> > > > > > 'c'. */
>>> > > > > >  >> > > >  static int
>>> > > > > >  >> > > > -expr_constant_width(const union expr_constant *c)
>>> > > > > >  >> > > > +expr_constant_width(const struct expr_constant *c)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      if (c->masked) {
>>> > > > > >  >> > > >          return mf_subvalue_width(&c->mask);
>>> > > > > >  >> > > > @@ -674,10 +673,7 @@ make_cmp(struct expr_context
>>> *ctx,
>>> > > > > >  >> > > >          e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR
>>> :
>>> > > > EXPR_T_AND,
>>> > > > > >  >> > > >                           e, make_cmp__(f, r,
>>> > > > &cs->values[i]));
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > > -    /* Track address set */
>>> > > > > >  >> > > > -    if (r == EXPR_R_EQ && e->type == EXPR_T_OR &&
>>> > > > cs->as_name) {
>>> > > > > >  >> > > > -        e->as_name = xstrdup(cs->as_name);
>>> > > > > >  >> > > > -    }
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > >  exit:
>>> > > > > >  >> > > >      expr_constant_set_destroy(cs);
>>> > > > > >  >> > > >      return e;
>>> > > > > >  >> > > > @@ -797,11 +793,10 @@ parse_addr_sets(struct
>>> expr_context
>>> > > > > > *ctx, struct expr_constant_set *cs,
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    struct expr_constant_set *addr_sets
>>> > > > > >  >> > > > -        = (ctx->addr_sets
>>> > > > > >  >> > > > -           ? shash_find_data(ctx->addr_sets,
>>> > > > ctx->lexer->token.s)
>>> > > > > >  >> > > > -           : NULL);
>>> > > > > >  >> > > > -    if (!addr_sets) {
>>> > > > > >  >> > > > +    struct shash_node *node = ctx->addr_sets
>>> > > > > >  >> > > > +                              ?
>>> > shash_find(ctx->addr_sets,
>>> > > > > > ctx->lexer->token.s)
>>> > > > > >  >> > > > +                              : NULL;
>>> > > > > >  >> > > > +    if (!node) {
>>> > > > > >  >> > > >          lexer_syntax_error(ctx->lexer, "expecting
>>> > address set
>>> > > > > > name");
>>> > > > > >  >> > > >          return false;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > > @@ -810,17 +805,16 @@ parse_addr_sets(struct
>>> expr_context
>>> > > > > > *ctx, struct expr_constant_set *cs,
>>> > > > > >  >> > > >          return false;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    if (!cs->n_values) {
>>> > > > > >  >> > > > -        cs->as_name = xstrdup(ctx->lexer->token.s);
>>> > > > > >  >> > > > -    }
>>> > > > > >  >> > > > -
>>> > > > > >  >> > > > +    struct expr_constant_set *addr_sets =
>>> node->data;
>>> > > > > >  >> > > >      size_t n_values = cs->n_values +
>>> addr_sets->n_values;
>>> > > > > >  >> > > >      if (n_values >= *allocated_values) {
>>> > > > > >  >> > > >          cs->values = xrealloc(cs->values, n_values *
>>> > sizeof
>>> > > > > > *cs->values);
>>> > > > > >  >> > > >          *allocated_values = n_values;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >      for (size_t i = 0; i < addr_sets->n_values;
>>> i++) {
>>> > > > > >  >> > > > -        cs->values[cs->n_values++] =
>>> > addr_sets->values[i];
>>> > > > > >  >> > > > +        struct expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +        *c = addr_sets->values[i];
>>> > > > > >  >> > > > +        c->as_name = node->name;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      return true;
>>> > > > > >  >> > > > @@ -859,8 +853,9 @@ parse_port_group(struct
>>> expr_context
>>> > *ctx,
>>> > > > > > struct expr_constant_set *cs,
>>> > > > > >  >> > > >          *allocated_values = n_values;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >      for (size_t i = 0; i < port_group->n_values;
>>> i++) {
>>> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
>>> > > > > >  >> > > > -            xstrdup(port_group->values[i].string);
>>> > > > > >  >> > > > +        struct expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +        c->string =
>>> > xstrdup(port_group->values[i].string);
>>> > > > > >  >> > > > +        c->as_name = NULL;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      return true;
>>> > > > > >  >> > > > @@ -875,11 +870,6 @@ parse_constant(struct
>>> expr_context
>>> > *ctx,
>>> > > > > > struct expr_constant_set *cs,
>>> > > > > >  >> > > >                                  sizeof *cs->values);
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    /* Combining other values to the constant set
>>> that is
>>> > > > > > tracking an
>>> > > > > >  >> > > > -     * address set, so untrack it. */
>>> > > > > >  >> > > > -    free(cs->as_name);
>>> > > > > >  >> > > > -    cs->as_name = NULL;
>>> > > > > >  >> > > > -
>>> > > > > >  >> > > >      if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
>>> > > > > >  >> > > >          lexer_error(ctx->lexer, "Unexpanded
>>> template.");
>>> > > > > >  >> > > >          return false;
>>> > > > > >  >> > > > @@ -887,7 +877,9 @@ parse_constant(struct
>>> expr_context
>>> > *ctx,
>>> > > > > > struct expr_constant_set *cs,
>>> > > > > >  >> > > >          if (!assign_constant_set_type(ctx, cs,
>>> > > > EXPR_C_STRING)) {
>>> > > > > >  >> > > >              return false;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > > -        cs->values[cs->n_values++].string =
>>> > > > > > xstrdup(ctx->lexer->token.s);
>>> > > > > >  >> > > > +        struct expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +        c->string = xstrdup(ctx->lexer->token.s);
>>> > > > > >  >> > > > +        c->as_name = NULL;
>>> > > > > >  >> > > >          lexer_get(ctx->lexer);
>>> > > > > >  >> > > >          return true;
>>> > > > > >  >> > > >      } else if (ctx->lexer->token.type ==
>>> LEX_T_INTEGER ||
>>> > > > > >  >> > > > @@ -896,13 +888,14 @@ parse_constant(struct
>>> expr_context
>>> > *ctx,
>>> > > > > > struct expr_constant_set *cs,
>>> > > > > >  >> > > >              return false;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -        union expr_constant *c =
>>> > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +        struct expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > >          c->value = ctx->lexer->token.value;
>>> > > > > >  >> > > >          c->format = ctx->lexer->token.format;
>>> > > > > >  >> > > >          c->masked = ctx->lexer->token.type ==
>>> > > > > > LEX_T_MASKED_INTEGER;
>>> > > > > >  >> > > >          if (c->masked) {
>>> > > > > >  >> > > >              c->mask = ctx->lexer->token.mask;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > > +        c->as_name = NULL;
>>> > > > > >  >> > > >          lexer_get(ctx->lexer);
>>> > > > > >  >> > > >          return true;
>>> > > > > >  >> > > >      } else if (ctx->lexer->token.type ==
>>> LEX_T_MACRO) {
>>> > > > > >  >> > > > @@ -961,7 +954,7 @@ parse_constant_set(struct
>>> expr_context
>>> > > > > > *ctx, struct expr_constant_set *cs)
>>> > > > > >  >> > > >   * indeterminate. */
>>> > > > > >  >> > > >  bool
>>> > > > > >  >> > > >  expr_constant_parse(struct lexer *lexer, const
>>> struct
>>> > > > > > expr_field *f,
>>> > > > > >  >> > > > -                    union expr_constant *c)
>>> > > > > >  >> > > > +                    struct expr_constant *c)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      if (lexer->error) {
>>> > > > > >  >> > > >          return false;
>>> > > > > >  >> > > > @@ -987,7 +980,7 @@ expr_constant_parse(struct lexer
>>> > *lexer,
>>> > > > > > const struct expr_field *f,
>>> > > > > >  >> > > >  /* Appends to 's' a re-parseable representation of
>>> > constant
>>> > > > > > 'c' with the given
>>> > > > > >  >> > > >   * 'type'. */
>>> > > > > >  >> > > >  void
>>> > > > > >  >> > > > -expr_constant_format(const union expr_constant *c,
>>> > > > > >  >> > > > +expr_constant_format(const struct expr_constant *c,
>>> > > > > >  >> > > >                       enum expr_constant_type type,
>>> > struct ds
>>> > > > *s)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      if (type == EXPR_C_STRING) {
>>> > > > > >  >> > > > @@ -1010,7 +1003,7 @@ expr_constant_format(const
>>> union
>>> > > > > > expr_constant *c,
>>> > > > > >  >> > > >   *
>>> > > > > >  >> > > >   * Does not free(c). */
>>> > > > > >  >> > > >  void
>>> > > > > >  >> > > > -expr_constant_destroy(const union expr_constant *c,
>>> > > > > >  >> > > > +expr_constant_destroy(const struct expr_constant *c,
>>> > > > > >  >> > > >                        enum expr_constant_type type)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      if (c && type == EXPR_C_STRING) {
>>> > > > > >  >> > > > @@ -1043,7 +1036,7 @@ expr_constant_set_format(const
>>> > struct
>>> > > > > > expr_constant_set *cs, struct ds *s)
>>> > > > > >  >> > > >          ds_put_char(s, '{');
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    for (const union expr_constant *c = cs->values;
>>> > > > > >  >> > > > +    for (const struct expr_constant *c = cs->values;
>>> > > > > >  >> > > >           c < &cs->values[cs->n_values]; c++) {
>>> > > > > >  >> > > >          if (c != cs->values) {
>>> > > > > >  >> > > >              ds_put_cstr(s, ", ");
>>> > > > > >  >> > > > @@ -1067,15 +1060,14 @@
>>> expr_constant_set_destroy(struct
>>> > > > > > expr_constant_set *cs)
>>> > > > > >  >> > > >              }
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > >          free(cs->values);
>>> > > > > >  >> > > > -        free(cs->as_name);
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >  }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  static int
>>> > > > > >  >> > > >  compare_expr_constant_integer_cb(const void *a_,
>>> const
>>> > void
>>> > > > *b_)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > > -    const union expr_constant *a = a_;
>>> > > > > >  >> > > > -    const union expr_constant *b = b_;
>>> > > > > >  >> > > > +    const struct expr_constant *a = a_;
>>> > > > > >  >> > > > +    const struct expr_constant *b = b_;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      int d = memcmp(&a->value, &b->value, sizeof
>>> > a->value);
>>> > > > > >  >> > > >      if (d) {
>>> > > > > >  >> > > > @@ -1114,7 +1106,7 @@
>>> > expr_constant_set_create_integers(const
>>> > > > > > char *const *values, size_t n_values)
>>> > > > > >  >> > > >              VLOG_WARN("Invalid constant set entry:
>>> '%s',
>>> > > > > > token type: %d",
>>> > > > > >  >> > > >                        values[i], lex.token.type);
>>> > > > > >  >> > > >          } else {
>>> > > > > >  >> > > > -            union expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +            struct expr_constant *c =
>>> > > > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > >              c->value = lex.token.value;
>>> > > > > >  >> > > >              c->format = lex.token.format;
>>> > > > > >  >> > > >              c->masked = lex.token.type ==
>>> > > > LEX_T_MASKED_INTEGER;
>>> > > > > >  >> > > > @@ -1135,7 +1127,7 @@
>>> > expr_constant_set_create_integers(const
>>> > > > > > char *const *values, size_t n_values)
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  static void
>>> > > > > >  >> > > >  expr_constant_set_add_value(struct expr_constant_set
>>> > **p_cs,
>>> > > > > >  >> > > > -                            union expr_constant *c,
>>> > size_t
>>> > > > > > *allocated)
>>> > > > > >  >> > > > +                            struct expr_constant *c,
>>> > size_t
>>> > > > > > *allocated)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > >      struct expr_constant_set *cs = *p_cs;
>>> > > > > >  >> > > >      if (!cs) {
>>> > > > > >  >> > > > @@ -1246,7 +1238,7 @@
>>> expr_const_sets_add_strings(struct
>>> > shash
>>> > > > > > *const_sets, const char *name,
>>> > > > > >  >> > > >                          values[i], name);
>>> > > > > >  >> > > >              continue;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > > -        union expr_constant *c =
>>> > &cs->values[cs->n_values++];
>>> > > > > >  >> > > > +        struct expr_constant *c =
>>> > > > &cs->values[cs->n_values++];
>>> > > > > >  >> > > >          c->string = xstrdup(values[i]);
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > @@ -1359,7 +1351,7 @@ expr_parse_primary(struct
>>> > expr_context
>>> > > > > > *ctx, bool *atomic)
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >              *atomic = true;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -            union expr_constant *cst =
>>> xzalloc(sizeof
>>> > *cst);
>>> > > > > >  >> > > > +            struct expr_constant *cst =
>>> xzalloc(sizeof
>>> > *cst);
>>> > > > > >  >> > > >              cst->format = LEX_F_HEXADECIMAL;
>>> > > > > >  >> > > >              cst->masked = false;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > @@ -1367,7 +1359,6 @@ expr_parse_primary(struct
>>> > expr_context
>>> > > > > > *ctx, bool *atomic)
>>> > > > > >  >> > > >              c.values = cst;
>>> > > > > >  >> > > >              c.n_values = 1;
>>> > > > > >  >> > > >              c.in_curlies = false;
>>> > > > > >  >> > > > -            c.as_name = NULL;
>>> > > > > >  >> > > >              return make_cmp(ctx, &f, EXPR_R_NE, &c);
>>> > > > > >  >> > > >          } else if (parse_relop(ctx, &r) &&
>>> > > > > > parse_constant_set(ctx, &c)) {
>>> > > > > >  >> > > >              return make_cmp(ctx, &f, r, &c);
>>> > > > > >  >> > > > @@ -1834,7 +1825,6 @@ expr_symtab_destroy(struct
>>> shash
>>> > > > *symtab)
>>> > > > > >  >> > > >  static struct expr *
>>> > > > > >  >> > > >  expr_clone_cmp(struct expr *expr)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
>>> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>>> > > > > >  >> > > >      if (!new->cmp.symbol->width) {
>>> > > > > >  >> > > >          new->cmp.string = xstrdup(new->cmp.string);
>>> > > > > >  >> > > > @@ -1858,7 +1848,6 @@ expr_clone_andor(struct expr
>>> *expr)
>>> > > > > >  >> > > >  static struct expr *
>>> > > > > >  >> > > >  expr_clone_condition(struct expr *expr)
>>> > > > > >  >> > > >  {
>>> > > > > >  >> > > > -    ovs_assert(!expr->as_name);
>>> > > > > >  >> > > >      struct expr *new = xmemdup(expr, sizeof *expr);
>>> > > > > >  >> > > >      new->cond.string = xstrdup(new->cond.string);
>>> > > > > >  >> > > >      return new;
>>> > > > > >  >> > > > @@ -1894,8 +1883,6 @@ expr_destroy(struct expr *expr)
>>> > > > > >  >> > > >          return;
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    free(expr->as_name);
>>> > > > > >  >> > > > -
>>> > > > > >  >> > > >      struct expr *sub;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >      switch (expr->type) {
>>> > > > > >  >> > > > @@ -2567,7 +2554,7 @@ crush_or_supersets(struct expr
>>> > *expr,
>>> > > > > > const struct expr_symbol *symbol)
>>> > > > > >  >> > > >       * mask sizes. */
>>> > > > > >  >> > > >      size_t n = ovs_list_size(&expr->andor);
>>> > > > > >  >> > > >      struct expr **subs = xmalloc(n * sizeof *subs);
>>> > > > > >  >> > > > -    bool modified = false;
>>> > > > > >  >> > > > +    bool has_addr_set = false;
>>> > > > > >  >> > > >      /* Linked list over the 'subs' array to quickly
>>> skip
>>> > > > > > deleted elements,
>>> > > > > >  >> > > >       * i.e. the index of the next potentially
>>> non-NULL
>>> > > > > > element. */
>>> > > > > >  >> > > >      size_t *next = xmalloc(n * sizeof *next);
>>> > > > > >  >> > > > @@ -2575,6 +2562,9 @@ crush_or_supersets(struct expr
>>> > *expr,
>>> > > > > > const struct expr_symbol *symbol)
>>> > > > > >  >> > > >      size_t i = 0, j, max_n_bits = 0;
>>> > > > > >  >> > > >      struct expr *sub;
>>> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &expr->andor) {
>>> > > > > >  >> > > > +        if (sub->as_name) {
>>> > > > > >  >> > > > +            has_addr_set = true;
>>> > > > > >  >> > > > +        }
>>> > > > > >  >> > > >          if (symbol->width) {
>>> > > > > >  >> > > >              const unsigned long *sub_mask;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > @@ -2596,14 +2586,14 @@ crush_or_supersets(struct
>>> expr
>>> > *expr,
>>> > > > > > const struct expr_symbol *symbol)
>>> > > > > >  >> > > >              next[last] = i;
>>> > > > > >  >> > > >              last = i;
>>> > > > > >  >> > > >          } else {
>>> > > > > >  >> > > > +            /* Remove address set reference from the
>>> > > > > > duplicate. */
>>> > > > > >  >> > > > +            subs[last]->as_name = NULL;
>>> > > > > >  >> > > >              expr_destroy(subs[i]);
>>> > > > > >  >> > > >              subs[i] = NULL;
>>> > > > > >  >> > > > -            modified = true;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    if (!symbol->width || symbol->level !=
>>> EXPR_L_ORDINAL
>>> > > > > >  >> > > > -        || (!modified && expr->as_name)) {
>>> > > > > >  >> > > > +    if (!symbol->width || symbol->level !=
>>> > EXPR_L_ORDINAL ||
>>> > > > > > has_addr_set) {
>>> > > > > >  >> > > >          /* Not a fully maskable field or this
>>> expression
>>> > is
>>> > > > > > tracking an
>>> > > > > >  >> > > >           * address set.  Don't try to optimize to
>>> > preserve
>>> > > > > > address set I-P. */
>>> > > > > >  >> > > >          goto done;
>>> > > > > >  >> > > > @@ -2658,10 +2648,11 @@ crush_or_supersets(struct
>>> expr
>>> > *expr,
>>> > > > > > const struct expr_symbol *symbol)
>>> > > > > >  >> > > >              if (expr_bitmap_intersect_check(a_value,
>>> > a_mask,
>>> > > > > > b_value, b_mask,
>>> > > > > >  >> > > >
>>> bit_width)
>>> > > > > >  >> > > >                  && bitmap_is_superset(b_mask,
>>> a_mask,
>>> > > > > > bit_width)) {
>>> > > > > >  >> > > > -                /* 'a' is the same expression with a
>>> > smaller
>>> > > > > > mask. */
>>> > > > > >  >> > > > +                /* 'a' is the same expression with a
>>> > smaller
>>> > > > > > mask.
>>> > > > > >  >> > > > +                 * Remove address set reference
>>> from the
>>> > > > > > duplicate. */
>>> > > > > >  >> > > > +                a->as_name = NULL;
>>> > > > > >  >> > > >                  expr_destroy(subs[j]);
>>> > > > > >  >> > > >                  subs[j] = NULL;
>>> > > > > >  >> > > > -                modified = true;
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >                  /* Shorten the path for the next
>>> round.
>>> > */
>>> > > > > >  >> > > >                  if (last) {
>>> > > > > >  >> > > > @@ -2685,12 +2676,6 @@ done:
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > >      }
>>> > > > > >  >> > > >
>>> > > > > >  >> > > > -    if (modified) {
>>> > > > > >  >> > > > -        /* Members modified, so untrack address
>>> set. */
>>> > > > > >  >> > > > -        free(expr->as_name);
>>> > > > > >  >> > > > -        expr->as_name = NULL;
>>> > > > > >  >> > > > -    }
>>> > > > > >  >> > > > -
>>> > > > > >  >> > > >      free(next);
>>> > > > > >  >> > > >      free(subs);
>>> > > > > >  >> > > >      return expr;
>>> > > > > >  >> > > > @@ -3271,10 +3256,10 @@ add_disjunction(const struct
>>> expr
>>> > *or,
>>> > > > > >  >> > > >      LIST_FOR_EACH (sub, node, &or->andor) {
>>> > > > > >  >> > > >          struct expr_match *match = expr_match_new(m,
>>> > clause,
>>> > > > > > n_clauses,
>>> > > > > >  >> > > >
>>> >  conj_id);
>>> > > > > >  >> > > > -        if (or->as_name) {
>>> > > > > >  >> > > > +        if (sub->as_name) {
>>> > > > > >  >> > > >              ovs_assert(sub->type == EXPR_T_CMP);
>>> > > > > >  >> > > >              ovs_assert(sub->cmp.symbol->width);
>>> > > > > >  >> > > > -            match->as_name = xstrdup(or->as_name);
>>> > > > > >  >> > > > +            match->as_name = xstrdup(sub->as_name);
>>> > > > > >  >> > > >              match->as_ip = sub->cmp.value.ipv6;
>>> > > > > >  >> > > >              match->as_mask = sub->cmp.mask.ipv6;
>>> > > > > >  >> > > >          }
>>> > > > > >  >> > > > diff --git a/tests/ovn-controller.at
>>> > > > > > <http://ovn-controller.at> b/tests/ovn-controller.at
>>> > > > > > <http://ovn-controller.at>
>>> > > > > >  >> > > > index be198e00d..27cec2aec 100644
>>> > > > > >  >> > > > --- a/tests/ovn-controller.at <
>>> http://ovn-controller.at>
>>> > > > > >  >> > > > +++ b/tests/ovn-controller.at <
>>> http://ovn-controller.at>
>>> > > > > >  >> > > > @@ -1625,7 +1625,9 @@
>>> > > > > >
>>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8
>>> > > > > > actions=lo
>>> > > > > >  >> > > >  done
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>>> consider_logical_flow)
>>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [10
>>> > > > > >  >> > > > +# First 2 reprocess are due to change from 1 IP in
>>> AS to
>>> > 2.
>>> > > > > >  >> > > > +# Last 5 is due to overlap in IP addresses between
>>> as1
>>> > and
>>> > > > as2.
>>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [7
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  # Remove the IPs from as1 and as2, 1 IP each time.
>>> > > > > >  >> > > > @@ -1648,9 +1650,9 @@ for i in $(seq 10); do
>>> > > > > >  >> > > >  done
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>>> consider_logical_flow)
>>> > > > > >  >> > > > -# In this case the as1 and as2 are merged to a
>>> single OR
>>> > > > > > expr, so we lose track of
>>> > > > > >  >> > > > -# address set information - can't handle deletion
>>> > > > incrementally.
>>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [10
>>> > > > > >  >> > > > +# First 5 are due to overlap in IP addresses
>>> between as1
>>> > and
>>> > > > as2.
>>> > > > > >  >> > > > +# Last 2 are due to change from 2 IP in AS to 1.
>>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [7
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  OVN_CLEANUP([hv1])
>>> > > > > >  >> > > > @@ -1817,7 +1819,7 @@
>>> > > > > >
>>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5
>>> > > > > > actions=co
>>> > > > > >  >> > > >
>>> > > > > >
>>> > > >
>>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>>> > > > actions=conjunction,2/2)
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>>> consider_logical_flow)
>>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [1
>>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [0
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  # Delete 2 IPs
>>> > > > > >  >> > > > @@ -1837,7 +1839,7 @@
>>> > > > > >
>>> > priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3
>>> > > > > > actions=co
>>> > > > > >  >> > > >
>>> > > > > >
>>> > > >
>>> priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10
>>> > > > actions=conjunction,2/2)
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >  reprocess_count_new=$(read_counter
>>> consider_logical_flow)
>>> > > > > >  >> > > > -AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [1
>>> > > > > >  >> > > > +AT_CHECK([echo $(($reprocess_count_new -
>>> > > > > > $reprocess_count_old))], [0], [0
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  OVN_CLEANUP([hv1])
>>> > > > > >  >> > > > @@ -2906,3 +2908,68 @@ OVN_CLEANUP([hv1],[hv2])
>>> > > > > >  >> > > >
>>> > > > > >  >> > > >  AT_CLEANUP
>>> > > > > >  >> > > >  ])
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +AT_SETUP([ovn-controller - AS I-P and recompute
>>> > consistency])
>>> > > > > >  >> > > > +AT_KEYWORDS([as-i-p])
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +ovn_start
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +net_add n1
>>> > > > > >  >> > > > +sim_add hv1
>>> > > > > >  >> > > > +as hv1
>>> > > > > >  >> > > > +check ovs-vsctl add-br br-phys
>>> > > > > >  >> > > > +ovn_attach n1 br-phys 192.168.0.1
>>> > > > > >  >> > > > +check ovs-vsctl -- add-port br-int hv1-vif1 -- \
>>> > > > > >  >> > > > +    set interface hv1-vif1
>>> external-ids:iface-id=ls1-lp1
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +check ovn-nbctl ls-add ls1
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +check ovn-nbctl lsp-add ls1 ls1-lp1 \
>>> > > > > >  >> > > > +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +wait_for_ports_up
>>> > > > > >  >> > > > +ovn-appctl -t ovn-controller vlog/set file:dbg
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +# Get the OF table numbers
>>> > > > > >  >> > > > +acl_eval=$(ovn-debug lflow-stage-to-oftable
>>> > ls_out_acl_eval)
>>> > > > > >  >> > > > +acl_action=$(ovn-debug lflow-stage-to-oftable
>>> > > > ls_out_acl_action)
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +dp_key=$(printf "%x" $(fetch_column datapath
>>> tunnel_key
>>> > > > > > external_ids:name=ls1))
>>> > > > > >  >> > > > +port_key=$(printf "%x" $(fetch_column port_binding
>>> > tunnel_key
>>> > > > > > logical_port=ls1-lp1))
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +ovn-nbctl create address_set name=as1
>>> > > > > >  >> > > > +check ovn-nbctl acl-add ls1 to-lport 100 'outport ==
>>> > > > > > "ls1-lp1" && ip4.src == $as1' drop
>>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>>> 10.0.0.0/24
>>> > > > > > <http://10.0.0.0/24>
>>> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk
>>> '{print $7,
>>> > $8}'
>>> > > > > > | sort], [0], [dnl
>>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>>> > 10.0.0.0/24
>>> > > > > > <http://10.0.0.0/24>
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > > +])
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>>> 10.0.0.1
>>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>>> 10.0.0.2
>>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>>> 10.0.0.3
>>> > > > > >  >> > > > +check ovn-nbctl add address_set as1 addresses
>>> 10.0.0.4
>>> > > > > >  >> > > > +check ovn-nbctl --wait=hv sync
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk
>>> '{print $7,
>>> > $8}'
>>> > > > > > | sort], [0], [dnl
>>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>>> > 10.0.0.0/24
>>> > > > > > <http://10.0.0.0/24>
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > > +])
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +check ovn-appctl inc-engine/recompute
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int
>>> > > > > > table=$acl_eval,reg15=0x$port_key | grep -v reply | awk
>>> '{print $7,
>>> > $8}'
>>> > > > > > | sort], [0], [dnl
>>> > > > > >  >> > > > +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=
>>> > 10.0.0.0/24
>>> > > > > > <http://10.0.0.0/24>
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > >
>>> +priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4
>>> > > > > > actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
>>> > > > > >  >> > > > +])
>>> > > > > >  >> > > > +
>>> > > > > >  >> > > > +OVN_CLEANUP([hv1])
>>> > > > > >  >> > > > +AT_CLEANUP
>>> > > > > >  >> > > > --
>>> > > > > >  >> > > > 2.44.0
>>> > > > > >  >> > > >
>>> > > > > >  >> >
>>> > > > > >  >> >
>>> > > > > >  >> > Thank you Han,
>>> > > > > >  >> >
>>> > > > > >  >> > I would like to start the discussion about the
>>> possibility of
>>> > > > > >  >> > backporting this change. The reason for the backport
>>> being
>>> > that
>>> > > > this
>>> > > > > >  >> > change might be beneficial to ovn-kubernetes users, as
>>> this
>>> > was
>>> > > > > >  >> > actually observed in ovn-kubernetes with network
>>> policies. I
>>> > know
>>> > > > that
>>> > > > > >  >> > strictly speaking this is not a bug fix, because this
>>> > limitation
>>> > > > was
>>> > > > > >  >> > well documented, however I would like to hear the
>>> opinion of
>>> > > > others.
>>> > > > > >  >>
>>> > > > > >  >>
>>> > > > > >  >> I have no objections if this improvement is strongly
>>> required
>>> > for
>>> > > > > > released versions, even though it is not a bug. May I ask for
>>> which
>>> > > > > > releases do you want this to be backported?
>>> > > > > >  >
>>> > > > > >  >
>>> > > > > >  > We have a request for 23.09 and 24.03.
>>> > > > > >  >
>>> > > > > > Ok. I will wait for input from other maintainers. It will be a
>>> > tradeoff
>>> > > > > > between risk and performance gains. Mark, Numan and Dumitru,
>>> what
>>> > do you
>>> > > > > > think?
>>> > > > > >
>>> > > > > > Thanks,
>>> > > > > > Han
>>> > > > >
>>> > > > > I'm OK with the patch being backported. I think our test
>>> coverage is
>>> > > > > sufficient to uncover problems that may arise from the backport,
>>> so
>>> > the
>>> > > > > risk should be minimal.
>>> > > > >
>>> > > >
>>> > > > Thanks Numan and Mark for confirming, so we got a quorum. I
>>> backported
>>> > the
>>> > > > patch to 24.03 and 23.09.
>>> > >
>>> > > This patch is using the "ovn-debug" binary which is missing in
>>> > > branch-24.03 and branch-23.09.
>>> > > Because of this, the test case "AS I-P and recompute consistency" is
>>> > > failing.  I backported
>>> > > the commit "117573e2d279" to these branches.
>>> > >
>>> > > While backporting to branch-23.09,  had to move the pipeline stage
>>> macros
>>> > from
>>> > > northd.c to northd.h.  Running the tests in my github repo before
>>> pushing
>>> > > to upstream branch-23.09.
>>> > >
>>> >
>>> > Oh, no. Thanks Numan for fixing! I ran test locally after switching
>>> > branches and all were successful, likely because the ovn-debug binary
>>> is
>>> > already in my environment, so I pushed late at night and see this just
>>> now.
>>> > Sorry for the trouble.
>>> >
>>>
>>> No worries.  I passed for me locally too until I deleted the binary.
>>>
>>> Numan
>>>
>>> > Regards,
>>> > Han
>>> >
>>> > > Numan
>>> > >
>>> > > >
>>> > > > Han
>>> > > >
>>> > > > > >
>>> > > > > >  >>
>>> > > > > >  >>
>>> > > > > >  >> Thanks,
>>> > > > > >  >> Han
>>> > > > > >  >>
>>> > > > > >  >> >
>>> > > > > >  >> > Thanks,
>>> > > > > >  >> > Ales
>>> > > > > >  >> > --
>>> > > > > >  >> >
>>> > > > > >  >> > Ales Musil
>>> > > > > >  >> >
>>> > > > > >  >> > Senior Software Engineer - OVN Core
>>> > > > > >  >> >
>>> > > > > >  >> > Red Hat EMEA
>>> > > > > >  >> >
>>> > > > > >  >> > amusil@redhat.com <mailto:amusil@redhat.com>
>>> > > > > >  >> >
>>> > > > > >  >
>>> > > > > >  >
>>> > > > > >  >
>>> > > > > >  > --
>>> > > > > >  >
>>> > > > > >  > Ales Musil
>>> > > > > >  >
>>> > > > > >  > Senior Software Engineer - OVN Core
>>> > > > > >  >
>>> > > > > >  > Red Hat EMEA
>>> > > > > >  >
>>> > > > > >  > amusil@redhat.com <mailto:amusil@redhat.com>
>>> > > > >
>>> > > > _______________________________________________
>>> > > > dev mailing list
>>> > > > dev@openvswitch.org
>>> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>> > _______________________________________________
>>> > dev mailing list
>>> > dev@openvswitch.org
>>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>> _______________________________________________
>>> dev mailing list
>>> dev@openvswitch.org
>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>
>>
>
> --
>
> Ales Musil
>
> Senior Software Engineer - OVN Core
>
> Red Hat EMEA <https://www.redhat.com>
>
> amusil@redhat.com
> <https://red.ht/sig>
>
diff mbox series

Patch

diff --git a/controller/lflow.c b/controller/lflow.c
index 760ec0b41..1e05665a1 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -278,7 +278,7 @@  lflow_handle_changed_flows(struct lflow_ctx_in *l_ctx_in,
 }
 
 static bool
-as_info_from_expr_const(const char *as_name, const union expr_constant *c,
+as_info_from_expr_const(const char *as_name, const struct expr_constant *c,
                         struct addrset_info *as_info)
 {
     as_info->name = as_name;
@@ -644,14 +644,11 @@  as_update_can_be_handled(const char *as_name, struct addr_set_diff *as_diff,
  *        generated.
  *
  *      - The sub expression of the address set is combined with other sub-
- *        expressions/constants, usually because of disjunctions between
- *        sub-expressions/constants, e.g.:
+ *        expressions/constants on different fields, e.g.:
  *
  *          ip.src == $as1 || ip.dst == $as2
- *          ip.src == {$as1, $as2}
- *          ip.src == {$as1, ip1}
  *
- *        All these could have been split into separate lflows.
+ *        This could have been split into separate lflows.
  *
  *      - Conjunctions overlapping between lflows, which can be caused by
  *        overlapping address sets or same address set used by multiple lflows
@@ -714,7 +711,7 @@  lflow_handle_addr_set_update(const char *as_name,
         if (as_diff->deleted) {
             struct addrset_info as_info;
             for (size_t i = 0; i < as_diff->deleted->n_values; i++) {
-                union expr_constant *c = &as_diff->deleted->values[i];
+                struct expr_constant *c = &as_diff->deleted->values[i];
                 if (!as_info_from_expr_const(as_name, c, &as_info)) {
                     continue;
                 }
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index ae0864fdd..88cf4de79 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -241,7 +241,7 @@  struct ovnact_next {
 struct ovnact_load {
     struct ovnact ovnact;
     struct expr_field dst;
-    union expr_constant imm;
+    struct expr_constant imm;
 };
 
 /* OVNACT_MOVE, OVNACT_EXCHANGE. */
diff --git a/include/ovn/expr.h b/include/ovn/expr.h
index c48f82398..e54edb5bf 100644
--- a/include/ovn/expr.h
+++ b/include/ovn/expr.h
@@ -368,7 +368,7 @@  bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop);
 struct expr {
     struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR if any. */
     enum expr_type type;        /* Expression type. */
-    char *as_name;              /* Address set name. Null if it is not an
+    const char *as_name;        /* Address set name. Null if it is not an
                                    address set. */
 
     union {
@@ -505,40 +505,42 @@  enum expr_constant_type {
 };
 
 /* A string or integer constant (one must know which from context). */
-union expr_constant {
-    /* Integer constant.
-     *
-     * The width of a constant isn't always clear, e.g. if you write "1",
-     * there's no way to tell whether you mean for that to be a 1-bit constant
-     * or a 128-bit constant or somewhere in between. */
-    struct {
-        union mf_subvalue value;
-        union mf_subvalue mask; /* Only initialized if 'masked'. */
-        bool masked;
-
-        enum lex_format format; /* From the constant's lex_token. */
-    };
+struct expr_constant {
+    const char *as_name;
 
-    /* Null-terminated string constant. */
-    char *string;
+    union {
+        /* Integer constant.
+         *
+         * The width of a constant isn't always clear, e.g. if you write "1",
+         * there's no way to tell whether you mean for that to be a 1-bit
+         * constant or a 128-bit constant or somewhere in between. */
+        struct {
+            union mf_subvalue value;
+            union mf_subvalue mask; /* Only initialized if 'masked'. */
+            bool masked;
+
+            enum lex_format format; /* From the constant's lex_token. */
+        };
+
+        /* Null-terminated string constant. */
+        char *string;
+    };
 };
 
 bool expr_constant_parse(struct lexer *,
                          const struct expr_field *,
-                         union expr_constant *);
-void expr_constant_format(const union expr_constant *,
+                         struct expr_constant *);
+void expr_constant_format(const struct expr_constant *,
                           enum expr_constant_type, struct ds *);
-void expr_constant_destroy(const union expr_constant *,
+void expr_constant_destroy(const struct expr_constant *,
                            enum expr_constant_type);
 
 /* A collection of "union expr_constant"s of the same type. */
 struct expr_constant_set {
-    union expr_constant *values;  /* Constants. */
+    struct expr_constant *values; /* Constants. */
     size_t n_values;              /* Number of constants. */
     enum expr_constant_type type; /* Type of the constants. */
     bool in_curlies;              /* Whether the constants were in {}. */
-    char *as_name;                /* Name of an address set. It is NULL if not
-                                     an address set. */
 };
 
 bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
diff --git a/lib/actions.c b/lib/actions.c
index 94751d059..cff4f9e3c 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -450,7 +450,7 @@  encode_LOAD(const struct ovnact_load *load,
             const struct ovnact_encode_params *ep,
             struct ofpbuf *ofpacts)
 {
-    const union expr_constant *c = &load->imm;
+    const struct expr_constant *c = &load->imm;
     struct mf_subfield dst = expr_resolve_field(&load->dst);
     struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, dst.field,
                                                        NULL, NULL);
@@ -2077,7 +2077,7 @@  encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
         /* All empty_lb_backends fields are of type 'str' */
         ovs_assert(!strcmp(o->option->type, "str"));
 
-        const union expr_constant *c = o->value.values;
+        const struct expr_constant *c = o->value.values;
         size_t size = strlen(c->string);
         struct controller_event_opt_header hdr =
             (struct controller_event_opt_header) {
@@ -2553,7 +2553,7 @@  validate_empty_lb_backends(struct action_context *ctx,
 {
     for (size_t i = 0; i < n_options; i++) {
         const struct ovnact_gen_option *o = &options[i];
-        const union expr_constant *c = o->value.values;
+        const struct expr_constant *c = o->value.values;
         struct sockaddr_storage ss;
         struct uuid uuid;
 
@@ -2861,7 +2861,7 @@  encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
     uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
     opt_header[0] = o->option->code;
 
-    const union expr_constant *c = o->value.values;
+    const struct expr_constant *c = o->value.values;
     size_t n_values = o->value.n_values;
     if (!strcmp(o->option->type, "bool") ||
         !strcmp(o->option->type, "uint8")) {
@@ -3027,7 +3027,7 @@  encode_put_dhcpv6_option(const struct ovnact_gen_option *o,
                          struct ofpbuf *ofpacts)
 {
     struct dhcpv6_opt_header *opt = ofpbuf_put_uninit(ofpacts, sizeof *opt);
-    const union expr_constant *c = o->value.values;
+    const struct expr_constant *c = o->value.values;
     size_t n_values = o->value.n_values;
     size_t size;
 
@@ -3083,7 +3083,7 @@  encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
         find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
     if (boot_opt) {
         uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
-        const union expr_constant *c = boot_opt->value.values;
+        const struct expr_constant *c = boot_opt->value.values;
         opt_header[0] = boot_opt->option->code;
         opt_header[1] = strlen(c->string);
         ofpbuf_put(ofpacts, c->string, opt_header[1]);
@@ -3093,7 +3093,7 @@  encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
         find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_ALT_CODE);
     if (boot_alt_opt) {
         uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
-        const union expr_constant *c = boot_alt_opt->value.values;
+        const struct expr_constant *c = boot_alt_opt->value.values;
         opt_header[0] = boot_alt_opt->option->code;
         opt_header[1] = strlen(c->string);
         ofpbuf_put(ofpacts, c->string, opt_header[1]);
@@ -3103,7 +3103,7 @@  encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
         pdo->options, pdo->n_options, DHCP_OPT_NEXT_SERVER_CODE);
     if (next_server_opt) {
         uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
-        const union expr_constant *c = next_server_opt->value.values;
+        const struct expr_constant *c = next_server_opt->value.values;
         opt_header[0] = next_server_opt->option->code;
         opt_header[1] = sizeof(ovs_be32);
         ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
@@ -3307,7 +3307,7 @@  parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst,
     /* Let's validate the options. */
     for (size_t i = 0; i < po->n_options; i++) {
         const struct ovnact_gen_option *o = &po->options[i];
-        const union expr_constant *c = o->value.values;
+        const struct expr_constant *c = o->value.values;
         if (o->value.n_values > 1) {
             lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
                         o->option->name);
@@ -3490,7 +3490,7 @@  static void
 encode_put_nd_ra_option(const struct ovnact_gen_option *o,
                         struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
 {
-    const union expr_constant *c = o->value.values;
+    const struct expr_constant *c = o->value.values;
 
     switch (o->option->code) {
     case ND_RA_FLAG_ADDR_MODE:
diff --git a/lib/expr.c b/lib/expr.c
index 0cb44e1b6..ecf8a6338 100644
--- a/lib/expr.c
+++ b/lib/expr.c
@@ -179,12 +179,10 @@  expr_combine(enum expr_type type, struct expr *a, struct expr *b)
         } else {
             ovs_list_push_back(&a->andor, &b->node);
         }
-        free(a->as_name);
         a->as_name = NULL;
         return a;
     } else if (b->type == type) {
         ovs_list_push_front(&b->andor, &a->node);
-        free(b->as_name);
         b->as_name = NULL;
         return b;
     } else {
@@ -521,12 +519,13 @@  static bool parse_field(struct expr_context *, struct expr_field *);
 
 static struct expr *
 make_cmp__(const struct expr_field *f, enum expr_relop r,
-             const union expr_constant *c)
+             const struct expr_constant *c)
 {
     struct expr *e = xzalloc(sizeof *e);
     e->type = EXPR_T_CMP;
     e->cmp.symbol = f->symbol;
     e->cmp.relop = r;
+    e->as_name = c->as_name;
     if (f->symbol->width) {
         bitwise_copy(&c->value, sizeof c->value, 0,
                      &e->cmp.value, sizeof e->cmp.value, f->ofs,
@@ -547,7 +546,7 @@  make_cmp__(const struct expr_field *f, enum expr_relop r,
 
 /* Returns the minimum reasonable width for integer constant 'c'. */
 static int
-expr_constant_width(const union expr_constant *c)
+expr_constant_width(const struct expr_constant *c)
 {
     if (c->masked) {
         return mf_subvalue_width(&c->mask);
@@ -674,10 +673,7 @@  make_cmp(struct expr_context *ctx,
         e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
                          e, make_cmp__(f, r, &cs->values[i]));
     }
-    /* Track address set */
-    if (r == EXPR_R_EQ && e->type == EXPR_T_OR && cs->as_name) {
-        e->as_name = xstrdup(cs->as_name);
-    }
+
 exit:
     expr_constant_set_destroy(cs);
     return e;
@@ -797,11 +793,10 @@  parse_addr_sets(struct expr_context *ctx, struct expr_constant_set *cs,
         }
     }
 
-    struct expr_constant_set *addr_sets
-        = (ctx->addr_sets
-           ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
-           : NULL);
-    if (!addr_sets) {
+    struct shash_node *node = ctx->addr_sets
+                              ? shash_find(ctx->addr_sets, ctx->lexer->token.s)
+                              : NULL;
+    if (!node) {
         lexer_syntax_error(ctx->lexer, "expecting address set name");
         return false;
     }
@@ -810,17 +805,16 @@  parse_addr_sets(struct expr_context *ctx, struct expr_constant_set *cs,
         return false;
     }
 
-    if (!cs->n_values) {
-        cs->as_name = xstrdup(ctx->lexer->token.s);
-    }
-
+    struct expr_constant_set *addr_sets = node->data;
     size_t n_values = cs->n_values + addr_sets->n_values;
     if (n_values >= *allocated_values) {
         cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
         *allocated_values = n_values;
     }
     for (size_t i = 0; i < addr_sets->n_values; i++) {
-        cs->values[cs->n_values++] = addr_sets->values[i];
+        struct expr_constant *c = &cs->values[cs->n_values++];
+        *c = addr_sets->values[i];
+        c->as_name = node->name;
     }
 
     return true;
@@ -859,8 +853,9 @@  parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs,
         *allocated_values = n_values;
     }
     for (size_t i = 0; i < port_group->n_values; i++) {
-        cs->values[cs->n_values++].string =
-            xstrdup(port_group->values[i].string);
+        struct expr_constant *c = &cs->values[cs->n_values++];
+        c->string = xstrdup(port_group->values[i].string);
+        c->as_name = NULL;
     }
 
     return true;
@@ -875,11 +870,6 @@  parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
                                 sizeof *cs->values);
     }
 
-    /* Combining other values to the constant set that is tracking an
-     * address set, so untrack it. */
-    free(cs->as_name);
-    cs->as_name = NULL;
-
     if (ctx->lexer->token.type == LEX_T_TEMPLATE) {
         lexer_error(ctx->lexer, "Unexpanded template.");
         return false;
@@ -887,7 +877,9 @@  parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
         if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
             return false;
         }
-        cs->values[cs->n_values++].string = xstrdup(ctx->lexer->token.s);
+        struct expr_constant *c = &cs->values[cs->n_values++];
+        c->string = xstrdup(ctx->lexer->token.s);
+        c->as_name = NULL;
         lexer_get(ctx->lexer);
         return true;
     } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
@@ -896,13 +888,14 @@  parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
             return false;
         }
 
-        union expr_constant *c = &cs->values[cs->n_values++];
+        struct expr_constant *c = &cs->values[cs->n_values++];
         c->value = ctx->lexer->token.value;
         c->format = ctx->lexer->token.format;
         c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
         if (c->masked) {
             c->mask = ctx->lexer->token.mask;
         }
+        c->as_name = NULL;
         lexer_get(ctx->lexer);
         return true;
     } else if (ctx->lexer->token.type == LEX_T_MACRO) {
@@ -961,7 +954,7 @@  parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
  * indeterminate. */
 bool
 expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
-                    union expr_constant *c)
+                    struct expr_constant *c)
 {
     if (lexer->error) {
         return false;
@@ -987,7 +980,7 @@  expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
 /* Appends to 's' a re-parseable representation of constant 'c' with the given
  * 'type'. */
 void
-expr_constant_format(const union expr_constant *c,
+expr_constant_format(const struct expr_constant *c,
                      enum expr_constant_type type, struct ds *s)
 {
     if (type == EXPR_C_STRING) {
@@ -1010,7 +1003,7 @@  expr_constant_format(const union expr_constant *c,
  *
  * Does not free(c). */
 void
-expr_constant_destroy(const union expr_constant *c,
+expr_constant_destroy(const struct expr_constant *c,
                       enum expr_constant_type type)
 {
     if (c && type == EXPR_C_STRING) {
@@ -1043,7 +1036,7 @@  expr_constant_set_format(const struct expr_constant_set *cs, struct ds *s)
         ds_put_char(s, '{');
     }
 
-    for (const union expr_constant *c = cs->values;
+    for (const struct expr_constant *c = cs->values;
          c < &cs->values[cs->n_values]; c++) {
         if (c != cs->values) {
             ds_put_cstr(s, ", ");
@@ -1067,15 +1060,14 @@  expr_constant_set_destroy(struct expr_constant_set *cs)
             }
         }
         free(cs->values);
-        free(cs->as_name);
     }
 }
 
 static int
 compare_expr_constant_integer_cb(const void *a_, const void *b_)
 {
-    const union expr_constant *a = a_;
-    const union expr_constant *b = b_;
+    const struct expr_constant *a = a_;
+    const struct expr_constant *b = b_;
 
     int d = memcmp(&a->value, &b->value, sizeof a->value);
     if (d) {
@@ -1114,7 +1106,7 @@  expr_constant_set_create_integers(const char *const *values, size_t n_values)
             VLOG_WARN("Invalid constant set entry: '%s', token type: %d",
                       values[i], lex.token.type);
         } else {
-            union expr_constant *c = &cs->values[cs->n_values++];
+            struct expr_constant *c = &cs->values[cs->n_values++];
             c->value = lex.token.value;
             c->format = lex.token.format;
             c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
@@ -1135,7 +1127,7 @@  expr_constant_set_create_integers(const char *const *values, size_t n_values)
 
 static void
 expr_constant_set_add_value(struct expr_constant_set **p_cs,
-                            union expr_constant *c, size_t *allocated)
+                            struct expr_constant *c, size_t *allocated)
 {
     struct expr_constant_set *cs = *p_cs;
     if (!cs) {
@@ -1246,7 +1238,7 @@  expr_const_sets_add_strings(struct shash *const_sets, const char *name,
                         values[i], name);
             continue;
         }
-        union expr_constant *c = &cs->values[cs->n_values++];
+        struct expr_constant *c = &cs->values[cs->n_values++];
         c->string = xstrdup(values[i]);
     }
 
@@ -1359,7 +1351,7 @@  expr_parse_primary(struct expr_context *ctx, bool *atomic)
 
             *atomic = true;
 
-            union expr_constant *cst = xzalloc(sizeof *cst);
+            struct expr_constant *cst = xzalloc(sizeof *cst);
             cst->format = LEX_F_HEXADECIMAL;
             cst->masked = false;
 
@@ -1367,7 +1359,6 @@  expr_parse_primary(struct expr_context *ctx, bool *atomic)
             c.values = cst;
             c.n_values = 1;
             c.in_curlies = false;
-            c.as_name = NULL;
             return make_cmp(ctx, &f, EXPR_R_NE, &c);
         } else if (parse_relop(ctx, &r) && parse_constant_set(ctx, &c)) {
             return make_cmp(ctx, &f, r, &c);
@@ -1834,7 +1825,6 @@  expr_symtab_destroy(struct shash *symtab)
 static struct expr *
 expr_clone_cmp(struct expr *expr)
 {
-    ovs_assert(!expr->as_name);
     struct expr *new = xmemdup(expr, sizeof *expr);
     if (!new->cmp.symbol->width) {
         new->cmp.string = xstrdup(new->cmp.string);
@@ -1858,7 +1848,6 @@  expr_clone_andor(struct expr *expr)
 static struct expr *
 expr_clone_condition(struct expr *expr)
 {
-    ovs_assert(!expr->as_name);
     struct expr *new = xmemdup(expr, sizeof *expr);
     new->cond.string = xstrdup(new->cond.string);
     return new;
@@ -1894,8 +1883,6 @@  expr_destroy(struct expr *expr)
         return;
     }
 
-    free(expr->as_name);
-
     struct expr *sub;
 
     switch (expr->type) {
@@ -2567,7 +2554,7 @@  crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
      * mask sizes. */
     size_t n = ovs_list_size(&expr->andor);
     struct expr **subs = xmalloc(n * sizeof *subs);
-    bool modified = false;
+    bool has_addr_set = false;
     /* Linked list over the 'subs' array to quickly skip deleted elements,
      * i.e. the index of the next potentially non-NULL element. */
     size_t *next = xmalloc(n * sizeof *next);
@@ -2575,6 +2562,9 @@  crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
     size_t i = 0, j, max_n_bits = 0;
     struct expr *sub;
     LIST_FOR_EACH (sub, node, &expr->andor) {
+        if (sub->as_name) {
+            has_addr_set = true;
+        }
         if (symbol->width) {
             const unsigned long *sub_mask;
 
@@ -2596,14 +2586,14 @@  crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
             next[last] = i;
             last = i;
         } else {
+            /* Remove address set reference from the duplicate. */
+            subs[last]->as_name = NULL;
             expr_destroy(subs[i]);
             subs[i] = NULL;
-            modified = true;
         }
     }
 
-    if (!symbol->width || symbol->level != EXPR_L_ORDINAL
-        || (!modified && expr->as_name)) {
+    if (!symbol->width || symbol->level != EXPR_L_ORDINAL || has_addr_set) {
         /* Not a fully maskable field or this expression is tracking an
          * address set.  Don't try to optimize to preserve address set I-P. */
         goto done;
@@ -2658,10 +2648,11 @@  crush_or_supersets(struct expr *expr, const struct expr_symbol *symbol)
             if (expr_bitmap_intersect_check(a_value, a_mask, b_value, b_mask,
                                             bit_width)
                 && bitmap_is_superset(b_mask, a_mask, bit_width)) {
-                /* 'a' is the same expression with a smaller mask. */
+                /* 'a' is the same expression with a smaller mask.
+                 * Remove address set reference from the duplicate. */
+                a->as_name = NULL;
                 expr_destroy(subs[j]);
                 subs[j] = NULL;
-                modified = true;
 
                 /* Shorten the path for the next round. */
                 if (last) {
@@ -2685,12 +2676,6 @@  done:
         }
     }
 
-    if (modified) {
-        /* Members modified, so untrack address set. */
-        free(expr->as_name);
-        expr->as_name = NULL;
-    }
-
     free(next);
     free(subs);
     return expr;
@@ -3271,10 +3256,10 @@  add_disjunction(const struct expr *or,
     LIST_FOR_EACH (sub, node, &or->andor) {
         struct expr_match *match = expr_match_new(m, clause, n_clauses,
                                                   conj_id);
-        if (or->as_name) {
+        if (sub->as_name) {
             ovs_assert(sub->type == EXPR_T_CMP);
             ovs_assert(sub->cmp.symbol->width);
-            match->as_name = xstrdup(or->as_name);
+            match->as_name = xstrdup(sub->as_name);
             match->as_ip = sub->cmp.value.ipv6;
             match->as_mask = sub->cmp.mask.ipv6;
         }
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index be198e00d..27cec2aec 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -1625,7 +1625,9 @@  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.8 actions=lo
 done
 
 reprocess_count_new=$(read_counter consider_logical_flow)
-AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [10
+# First 2 reprocess are due to change from 1 IP in AS to 2.
+# Last 5 is due to overlap in IP addresses between as1 and as2.
+AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
 ])
 
 # Remove the IPs from as1 and as2, 1 IP each time.
@@ -1648,9 +1650,9 @@  for i in $(seq 10); do
 done
 
 reprocess_count_new=$(read_counter consider_logical_flow)
-# In this case the as1 and as2 are merged to a single OR expr, so we lose track of
-# address set information - can't handle deletion incrementally.
-AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [10
+# First 5 are due to overlap in IP addresses between as1 and as2.
+# Last 2 are due to change from 2 IP in AS to 1.
+AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [7
 ])
 
 OVN_CLEANUP([hv1])
@@ -1817,7 +1819,7 @@  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.5 actions=co
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
 ])
 reprocess_count_new=$(read_counter consider_logical_flow)
-AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
+AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
 ])
 
 # Delete 2 IPs
@@ -1837,7 +1839,7 @@  priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.0.0.3 actions=co
 priority=1100,ip,reg15=0x$port_key,metadata=0x$dp_key,nw_src=10.10.10.10 actions=conjunction,2/2)
 ])
 reprocess_count_new=$(read_counter consider_logical_flow)
-AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [1
+AT_CHECK([echo $(($reprocess_count_new - $reprocess_count_old))], [0], [0
 ])
 
 OVN_CLEANUP([hv1])
@@ -2906,3 +2908,68 @@  OVN_CLEANUP([hv1],[hv2])
 
 AT_CLEANUP
 ])
+
+AT_SETUP([ovn-controller - AS I-P and recompute consistency])
+AT_KEYWORDS([as-i-p])
+
+ovn_start
+
+net_add n1
+sim_add hv1
+as hv1
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+check ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=ls1-lp1
+
+check ovn-nbctl ls-add ls1
+
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
+
+wait_for_ports_up
+ovn-appctl -t ovn-controller vlog/set file:dbg
+
+# Get the OF table numbers
+acl_eval=$(ovn-debug lflow-stage-to-oftable ls_out_acl_eval)
+acl_action=$(ovn-debug lflow-stage-to-oftable ls_out_acl_action)
+
+dp_key=$(printf "%x" $(fetch_column datapath tunnel_key external_ids:name=ls1))
+port_key=$(printf "%x" $(fetch_column port_binding tunnel_key logical_port=ls1-lp1))
+
+ovn-nbctl create address_set name=as1
+check ovn-nbctl acl-add ls1 to-lport 100 'outport == "ls1-lp1" && ip4.src == $as1' drop
+check ovn-nbctl add address_set as1 addresses 10.0.0.0/24
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+])
+
+check ovn-nbctl add address_set as1 addresses 10.0.0.1
+check ovn-nbctl add address_set as1 addresses 10.0.0.2
+check ovn-nbctl add address_set as1 addresses 10.0.0.3
+check ovn-nbctl add address_set as1 addresses 10.0.0.4
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+])
+
+
+check ovn-appctl inc-engine/recompute
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=$acl_eval,reg15=0x$port_key | grep -v reply | awk '{print $7, $8}' | sort], [0], [dnl
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.0/24 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.1 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.2 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.3 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+priority=1100,ip,reg15=0x1,metadata=0x1,nw_src=10.0.0.4 actions=load:0x1->OXM_OF_PKT_REG4[[49]],resubmit(,$acl_action)
+])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP