diff mbox series

[ovs-dev,RFC,ovn,4/6] actions: Implement new actions lookup_arp_ip and lookup_nd_ip.

Message ID 1591815613-49682-5-git-send-email-hzhou@ovn.org
State Superseded
Headers show
Series Avoid ARP flow explosion. | expand

Commit Message

Han Zhou June 10, 2020, 7 p.m. UTC
lookup_arp_ip and lookup_nd_ip are added to lookup if an entry exists
in MAC bindings for a given IP address, for IPv4 and IPv6 respectively.

Signed-off-by: Han Zhou <hzhou@ovn.org>
---
 controller/lflow.c    |   4 +-
 include/ovn/actions.h |  10 +++++
 lib/actions.c         | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/ovn.at          |  50 ++++++++++++++++++++++
 utilities/ovn-trace.c |  54 ++++++++++++++++++++++--
 5 files changed, 226 insertions(+), 4 deletions(-)

Comments

Numan Siddique June 11, 2020, 4:59 p.m. UTC | #1
On Thu, Jun 11, 2020 at 12:31 AM Han Zhou <hzhou@ovn.org> wrote:

> lookup_arp_ip and lookup_nd_ip are added to lookup if an entry exists
> in MAC bindings for a given IP address, for IPv4 and IPv6 respectively.
>
> Signed-off-by: Han Zhou <hzhou@ovn.org>
>

Hi Han,

I know this is RFC. I was just going through it and noticed that this patch
needs to document the new actions in ovn-sb.xml.

Thanks
Numan

---
>  controller/lflow.c    |   4 +-
>  include/ovn/actions.h |  10 +++++
>  lib/actions.c         | 112
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/ovn.at          |  50 ++++++++++++++++++++++
>  utilities/ovn-trace.c |  54 ++++++++++++++++++++++--
>  5 files changed, 226 insertions(+), 4 deletions(-)
>
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 01214a3..e45ed33 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -761,13 +761,15 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
>
>      uint64_t stub[1024 / 8];
>      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
> +    uint8_t value = 1;
>      put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
> +    put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
> +             &ofpacts);
>      ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100,
>                      b->header_.uuid.parts[0], &get_arp_match,
>                      &ofpacts, &b->header_.uuid);
>
>      ofpbuf_clear(&ofpacts);
> -    uint8_t value = 1;
>      put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
>               &ofpacts);
>      match_set_dl_src(&lookup_arp_match, mac);
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index 4a54abe..2b5a63a 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -75,9 +75,11 @@ struct ovn_extend_table;
>      OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
>      OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
>      OVNACT(LOOKUP_ARP,        ovnact_lookup_mac_bind) \
> +    OVNACT(LOOKUP_ARP_IP,     ovnact_lookup_mac_bind_ip) \
>      OVNACT(GET_ND,            ovnact_get_mac_bind)    \
>      OVNACT(PUT_ND,            ovnact_put_mac_bind)    \
>      OVNACT(LOOKUP_ND,         ovnact_lookup_mac_bind) \
> +    OVNACT(LOOKUP_ND_IP,      ovnact_lookup_mac_bind_ip) \
>      OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
>      OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
>      OVNACT(SET_QUEUE,         ovnact_set_queue)       \
> @@ -307,6 +309,14 @@ struct ovnact_lookup_mac_bind {
>      struct expr_field mac;      /* 48-bit Ethernet address. */
>  };
>
> +/* OVNACT_LOOKUP_ARP_IP, OVNACT_LOOKUP_ND_IP. */
> +struct ovnact_lookup_mac_bind_ip {
> +    struct ovnact ovnact;
> +    struct expr_field dst;      /* 1-bit destination field. */
> +    struct expr_field port;     /* Logical port name. */
> +    struct expr_field ip;       /* 32-bit or 128-bit IP address. */
> +};
> +
>  struct ovnact_gen_option {
>      const struct gen_opts_map *option;
>      struct expr_constant_set value;
> diff --git a/lib/actions.c b/lib/actions.c
> index 22c0e76..41301c8 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -1953,6 +1953,110 @@ ovnact_lookup_mac_bind_free(
>
>  }
>
> +
> +static void format_lookup_mac_bind_ip(
> +    const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> +    struct ds *s, const char *name)
> +{
> +    expr_field_format(&lookup_mac->dst, s);
> +    ds_put_format(s, " = %s(", name);
> +    expr_field_format(&lookup_mac->port, s);
> +    ds_put_cstr(s, ", ");
> +    expr_field_format(&lookup_mac->ip, s);
> +    ds_put_cstr(s, ");");
> +}
> +
> +static void
> +format_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> +                     struct ds *s)
> +{
> +    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_arp_ip");
> +}
> +
> +static void
> +format_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> +                    struct ds *s)
> +{
> +    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_nd_ip");
> +}
> +
> +static void
> +encode_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip
> *lookup_mac,
> +                          enum mf_field_id ip_field,
> +                          const struct ovnact_encode_params *ep,
> +                          struct ofpbuf *ofpacts)
> +{
> +    const struct arg args[] = {
> +        { expr_resolve_field(&lookup_mac->port), MFF_LOG_OUTPORT },
> +        { expr_resolve_field(&lookup_mac->ip), ip_field },
> +    };
> +
> +    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
> +    init_stack(ofpact_put_STACK_PUSH(ofpacts), MFF_ETH_DST);
> +
> +    struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst);
> +    ovs_assert(dst.field);
> +
> +    put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts);
> +    emit_resubmit(ofpacts, ep->mac_bind_ptable);
> +
> +    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
> +    orm->dst = dst;
> +    orm->src.field = mf_from_id(MFF_LOG_FLAGS);
> +    orm->src.ofs = MLF_LOOKUP_MAC_BIT;
> +    orm->src.n_bits = 1;
> +
> +    init_stack(ofpact_put_STACK_POP(ofpacts), MFF_ETH_DST);
> +    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
> +}
> +
> +static void
> +encode_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> +                     const struct ovnact_encode_params *ep,
> +                     struct ofpbuf *ofpacts)
> +{
> +    encode_lookup_mac_bind_ip(lookup_mac, MFF_REG0, ep, ofpacts);
> +}
> +
> +static void
> +encode_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> +                    const struct ovnact_encode_params *ep,
> +                    struct ofpbuf *ofpacts)
> +{
> +    encode_lookup_mac_bind_ip(lookup_mac, MFF_XXREG0, ep, ofpacts);
> +}
> +
> +static void
> +parse_lookup_mac_bind_ip(struct action_context *ctx,
> +                         const struct expr_field *dst,
> +                         int width,
> +                         struct ovnact_lookup_mac_bind_ip *lookup_mac)
> +{
> +    /* Validate that the destination is a 1-bit, modifiable field. */
> +    char *error = expr_type_check(dst, 1, true);
> +    if (error) {
> +        lexer_error(ctx->lexer, "%s", error);
> +        free(error);
> +        return;
> +    }
> +
> +    lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */
> +    lexer_get(ctx->lexer); /* Skip '('. * */
> +
> +    action_parse_field(ctx, 0, false, &lookup_mac->port);
> +    lexer_force_match(ctx->lexer, LEX_T_COMMA);
> +    action_parse_field(ctx, width, false, &lookup_mac->ip);
> +    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> +    lookup_mac->dst = *dst;
> +}
> +
> +static void
> +ovnact_lookup_mac_bind_ip_free(
> +    struct ovnact_lookup_mac_bind_ip *lookup_mac OVS_UNUSED)
> +{
> +
> +}
> +
>
>  static void
>  parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
> @@ -3278,6 +3382,14 @@ parse_set_action(struct action_context *ctx)
>                  && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
>              parse_lookup_mac_bind(ctx, &lhs, 128,
>                                    ovnact_put_LOOKUP_ND(ctx->ovnacts));
> +        } else if (!strcmp(ctx->lexer->token.s, "lookup_arp_ip")
> +                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> +            parse_lookup_mac_bind_ip(ctx, &lhs, 32,
> +
>  ovnact_put_LOOKUP_ARP_IP(ctx->ovnacts));
> +        } else if (!strcmp(ctx->lexer->token.s, "lookup_nd_ip")
> +                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> +            parse_lookup_mac_bind_ip(ctx, &lhs, 128,
> +
>  ovnact_put_LOOKUP_ND_IP(ctx->ovnacts));
>          } else {
>              parse_assignment_action(ctx, false, &lhs);
>          }
> diff --git a/tests/ovn.at b/tests/ovn.at
> index b7976c6..12849b4 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -1203,6 +1203,31 @@ reg0[0] = lookup_arp(inport, eth.dst);
>  reg0[0] = lookup_arp(inport, ip4.src, ip4.dst);
>      Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
>
> +# lookup_arp_ip
> +reg0[0] = lookup_arp_ip(inport, ip4.dst);
> +    encodes as
> push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
> +    has prereqs eth.type == 0x800
> +reg1[1] = lookup_arp_ip(inport, arp.spa);
> +    encodes as
> push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_ARP_SPA[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
> +    has prereqs eth.type == 0x806
> +
> +lookup_arp_ip;
> +    Syntax error at `lookup_arp_ip' expecting action.
> +reg0[0] = lookup_arp_ip;
> +    Syntax error at `lookup_arp_ip' expecting field name.
> +reg0[0] = lookup_arp_ip();
> +    Syntax error at `)' expecting field name.
> +reg0[0] = lookup_arp_ip(inport);
> +    Syntax error at `)' expecting `,'.
> +reg0[0] = lookup_arp_ip(inport ip4.dst);
> +    Syntax error at `ip4.dst' expecting `,'.
> +reg0[0] = lookup_arp_ip(inport, ip4.dst;
> +    Syntax error at `;' expecting `)'.
> +reg0[0] = lookup_arp_ip(inport, ip4.dst, eth.src;
> +    Syntax error at `,' expecting `)'.
> +reg0[0] = lookup_arp_ip(inport, eth.dst);
> +    Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required.
> +
>  # put_dhcp_opts
>  reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
>      encodes as
> controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
> @@ -1332,6 +1357,31 @@ reg0[0] = lookup_nd(inport, ip4.src, ip4.dst);
>  reg0[0] = lookup_nd(inport, ip6.src, ip6.dst);
>      Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is
> required.
>
> +# lookup_nd_ip
> +reg2[0] = lookup_nd_ip(inport, ip6.dst);
> +    encodes as
> push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
> +    has prereqs eth.type == 0x86dd
> +reg3[0] = lookup_nd_ip(inport, nd.target);
> +    encodes as
> push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_ND_TARGET[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
> +    has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type ==
> 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) &&
> icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type ==
> 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 ||
> eth.type == 0x86dd)
> +
> +lookup_nd_ip;
> +    Syntax error at `lookup_nd_ip' expecting action.
> +reg0[0] = lookup_nd_ip;
> +    Syntax error at `lookup_nd_ip' expecting field name.
> +reg0[0] = lookup_nd_ip();
> +    Syntax error at `)' expecting field name.
> +reg0[0] = lookup_nd_ip(inport);
> +    Syntax error at `)' expecting `,'.
> +reg0[0] = lookup_nd_ip(inport ip6.dst);
> +    Syntax error at `ip6.dst' expecting `,'.
> +reg0[0] = lookup_nd_ip(inport, ip6.dst;
> +    Syntax error at `;' expecting `)'.
> +reg0[0] = lookup_nd_ip(inport, eth.dst);
> +    Cannot use 48-bit field eth.dst[0..47] where 128-bit field is
> required.
> +reg0[0] = lookup_nd_ip(inport, ip4.src);
> +    Cannot use 32-bit field ip4.src[0..31] where 128-bit field is
> required.
> +
>  # set_queue
>  set_queue(0);
>      encodes as set_queue:0
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index 146560c..ae0a59e 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -582,13 +582,13 @@ ovntrace_mac_binding_find(const struct
> ovntrace_datapath *dp,
>  static const struct ovntrace_mac_binding *
>  ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
>                                   uint16_t port_key, const struct in6_addr
> *ip,
> -                                 struct eth_addr mac)
> +                                 struct eth_addr *mac)
>  {
>      const struct ovntrace_mac_binding *bind;
>      HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
>                               &dp->mac_bindings) {
>          if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)
> -            && eth_addr_equals(bind->mac, mac)) {
> +            && (!mac || eth_addr_equals(bind->mac, *mac))) {
>              return bind;
>          }
>      }
> @@ -1771,7 +1771,7 @@ execute_lookup_mac_bind(const struct
> ovnact_lookup_mac_bind *bind,
>      mf_read_subfield(&mac_sf, uflow, &mac_sv);
>
>      const struct ovntrace_mac_binding *binding
> -        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, mac_sv.mac);
> +        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip,
> &mac_sv.mac);
>
>      struct mf_subfield dst = expr_resolve_field(&bind->dst);
>      uint8_t val = 0;
> @@ -1790,6 +1790,44 @@ execute_lookup_mac_bind(const struct
> ovnact_lookup_mac_bind *bind,
>  }
>
>  static void
> +execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *bind,
> +                           const struct ovntrace_datapath *dp,
> +                           struct flow *uflow,
> +                           struct ovs_list *super)
> +{
> +    /* Get logical port number.*/
> +    struct mf_subfield port_sf = expr_resolve_field(&bind->port);
> +    ovs_assert(port_sf.n_bits == 32);
> +    uint32_t port_key = mf_get_subfield(&port_sf, uflow);
> +
> +    /* Get IP address. */
> +    struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
> +    ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
> +    union mf_subvalue ip_sv;
> +    mf_read_subfield(&ip_sf, uflow, &ip_sv);
> +    struct in6_addr ip = (ip_sf.n_bits == 32
> +                          ? in6_addr_mapped_ipv4(ip_sv.ipv4)
> +                          : ip_sv.ipv6);
> +
> +    const struct ovntrace_mac_binding *binding
> +        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, NULL);
> +
> +    struct mf_subfield dst = expr_resolve_field(&bind->dst);
> +    uint8_t val = 0;
> +
> +    if (binding) {
> +        val = 1;
> +        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
> +                             "/* MAC binding for <TODO: ipv4/v6> found.
> */");
> +    } else {
> +        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
> +                             "/* lookup failed - No MAC binding. */");
> +    }
> +    union mf_subvalue sv = { .u8_val = val };
> +    mf_write_subfield_flow(&dst, &sv, uflow);
> +}
> +
> +static void
>  execute_put_opts(const struct ovnact_put_opts *po,
>                   const char *name, struct flow *uflow,
>                   struct ovs_list *super)
> @@ -2216,6 +2254,16 @@ trace_actions(const struct ovnact *ovnacts, size_t
> ovnacts_len,
>              execute_lookup_mac_bind(ovnact_get_LOOKUP_ND(a), dp, uflow,
> super);
>              break;
>
> +        case OVNACT_LOOKUP_ARP_IP:
> +            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ARP_IP(a), dp,
> uflow,
> +                                       super);
> +            break;
> +
> +        case OVNACT_LOOKUP_ND_IP:
> +            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ND_IP(a), dp,
> uflow,
> +                                       super);
> +            break;
> +
>          case OVNACT_PUT_DHCPV4_OPTS:
>              execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
>                                    "put_dhcp_opts", uflow, super);
> --
> 2.1.0
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Han Zhou July 19, 2020, 10:30 p.m. UTC | #2
On Thu, Jun 11, 2020 at 9:59 AM Numan Siddique <nusiddiq@redhat.com> wrote:
>
> On Thu, Jun 11, 2020 at 12:31 AM Han Zhou <hzhou@ovn.org> wrote:
>
> > lookup_arp_ip and lookup_nd_ip are added to lookup if an entry exists
> > in MAC bindings for a given IP address, for IPv4 and IPv6 respectively.
> >
> > Signed-off-by: Han Zhou <hzhou@ovn.org>
> >
>
> Hi Han,
>
> I know this is RFC. I was just going through it and noticed that this
patch
> needs to document the new actions in ovn-sb.xml.

Sure, I will add.

>
> Thanks
> Numan
>
> ---
> >  controller/lflow.c    |   4 +-
> >  include/ovn/actions.h |  10 +++++
> >  lib/actions.c         | 112
> > ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  tests/ovn.at          |  50 ++++++++++++++++++++++
> >  utilities/ovn-trace.c |  54 ++++++++++++++++++++++--
> >  5 files changed, 226 insertions(+), 4 deletions(-)
> >
> > diff --git a/controller/lflow.c b/controller/lflow.c
> > index 01214a3..e45ed33 100644
> > --- a/controller/lflow.c
> > +++ b/controller/lflow.c
> > @@ -761,13 +761,15 @@ consider_neighbor_flow(struct ovsdb_idl_index
> > *sbrec_port_binding_by_name,
> >
> >      uint64_t stub[1024 / 8];
> >      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
> > +    uint8_t value = 1;
> >      put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
> > +    put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT,
1,
> > +             &ofpacts);
> >      ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100,
> >                      b->header_.uuid.parts[0], &get_arp_match,
> >                      &ofpacts, &b->header_.uuid);
> >
> >      ofpbuf_clear(&ofpacts);
> > -    uint8_t value = 1;
> >      put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT,
1,
> >               &ofpacts);
> >      match_set_dl_src(&lookup_arp_match, mac);
> > diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> > index 4a54abe..2b5a63a 100644
> > --- a/include/ovn/actions.h
> > +++ b/include/ovn/actions.h
> > @@ -75,9 +75,11 @@ struct ovn_extend_table;
> >      OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
> >      OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
> >      OVNACT(LOOKUP_ARP,        ovnact_lookup_mac_bind) \
> > +    OVNACT(LOOKUP_ARP_IP,     ovnact_lookup_mac_bind_ip) \
> >      OVNACT(GET_ND,            ovnact_get_mac_bind)    \
> >      OVNACT(PUT_ND,            ovnact_put_mac_bind)    \
> >      OVNACT(LOOKUP_ND,         ovnact_lookup_mac_bind) \
> > +    OVNACT(LOOKUP_ND_IP,      ovnact_lookup_mac_bind_ip) \
> >      OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
> >      OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
> >      OVNACT(SET_QUEUE,         ovnact_set_queue)       \
> > @@ -307,6 +309,14 @@ struct ovnact_lookup_mac_bind {
> >      struct expr_field mac;      /* 48-bit Ethernet address. */
> >  };
> >
> > +/* OVNACT_LOOKUP_ARP_IP, OVNACT_LOOKUP_ND_IP. */
> > +struct ovnact_lookup_mac_bind_ip {
> > +    struct ovnact ovnact;
> > +    struct expr_field dst;      /* 1-bit destination field. */
> > +    struct expr_field port;     /* Logical port name. */
> > +    struct expr_field ip;       /* 32-bit or 128-bit IP address. */
> > +};
> > +
> >  struct ovnact_gen_option {
> >      const struct gen_opts_map *option;
> >      struct expr_constant_set value;
> > diff --git a/lib/actions.c b/lib/actions.c
> > index 22c0e76..41301c8 100644
> > --- a/lib/actions.c
> > +++ b/lib/actions.c
> > @@ -1953,6 +1953,110 @@ ovnact_lookup_mac_bind_free(
> >
> >  }
> >
> > +
> > +static void format_lookup_mac_bind_ip(
> > +    const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> > +    struct ds *s, const char *name)
> > +{
> > +    expr_field_format(&lookup_mac->dst, s);
> > +    ds_put_format(s, " = %s(", name);
> > +    expr_field_format(&lookup_mac->port, s);
> > +    ds_put_cstr(s, ", ");
> > +    expr_field_format(&lookup_mac->ip, s);
> > +    ds_put_cstr(s, ");");
> > +}
> > +
> > +static void
> > +format_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip
*lookup_mac,
> > +                     struct ds *s)
> > +{
> > +    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_arp_ip");
> > +}
> > +
> > +static void
> > +format_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> > +                    struct ds *s)
> > +{
> > +    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_nd_ip");
> > +}
> > +
> > +static void
> > +encode_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip
> > *lookup_mac,
> > +                          enum mf_field_id ip_field,
> > +                          const struct ovnact_encode_params *ep,
> > +                          struct ofpbuf *ofpacts)
> > +{
> > +    const struct arg args[] = {
> > +        { expr_resolve_field(&lookup_mac->port), MFF_LOG_OUTPORT },
> > +        { expr_resolve_field(&lookup_mac->ip), ip_field },
> > +    };
> > +
> > +    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
> > +    init_stack(ofpact_put_STACK_PUSH(ofpacts), MFF_ETH_DST);
> > +
> > +    struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst);
> > +    ovs_assert(dst.field);
> > +
> > +    put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts);
> > +    emit_resubmit(ofpacts, ep->mac_bind_ptable);
> > +
> > +    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
> > +    orm->dst = dst;
> > +    orm->src.field = mf_from_id(MFF_LOG_FLAGS);
> > +    orm->src.ofs = MLF_LOOKUP_MAC_BIT;
> > +    orm->src.n_bits = 1;
> > +
> > +    init_stack(ofpact_put_STACK_POP(ofpacts), MFF_ETH_DST);
> > +    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
> > +}
> > +
> > +static void
> > +encode_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip
*lookup_mac,
> > +                     const struct ovnact_encode_params *ep,
> > +                     struct ofpbuf *ofpacts)
> > +{
> > +    encode_lookup_mac_bind_ip(lookup_mac, MFF_REG0, ep, ofpacts);
> > +}
> > +
> > +static void
> > +encode_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
> > +                    const struct ovnact_encode_params *ep,
> > +                    struct ofpbuf *ofpacts)
> > +{
> > +    encode_lookup_mac_bind_ip(lookup_mac, MFF_XXREG0, ep, ofpacts);
> > +}
> > +
> > +static void
> > +parse_lookup_mac_bind_ip(struct action_context *ctx,
> > +                         const struct expr_field *dst,
> > +                         int width,
> > +                         struct ovnact_lookup_mac_bind_ip *lookup_mac)
> > +{
> > +    /* Validate that the destination is a 1-bit, modifiable field. */
> > +    char *error = expr_type_check(dst, 1, true);
> > +    if (error) {
> > +        lexer_error(ctx->lexer, "%s", error);
> > +        free(error);
> > +        return;
> > +    }
> > +
> > +    lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */
> > +    lexer_get(ctx->lexer); /* Skip '('. * */
> > +
> > +    action_parse_field(ctx, 0, false, &lookup_mac->port);
> > +    lexer_force_match(ctx->lexer, LEX_T_COMMA);
> > +    action_parse_field(ctx, width, false, &lookup_mac->ip);
> > +    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> > +    lookup_mac->dst = *dst;
> > +}
> > +
> > +static void
> > +ovnact_lookup_mac_bind_ip_free(
> > +    struct ovnact_lookup_mac_bind_ip *lookup_mac OVS_UNUSED)
> > +{
> > +
> > +}
> > +
> >
> >  static void
> >  parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
> > @@ -3278,6 +3382,14 @@ parse_set_action(struct action_context *ctx)
> >                  && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> >              parse_lookup_mac_bind(ctx, &lhs, 128,
> >                                    ovnact_put_LOOKUP_ND(ctx->ovnacts));
> > +        } else if (!strcmp(ctx->lexer->token.s, "lookup_arp_ip")
> > +                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> > +            parse_lookup_mac_bind_ip(ctx, &lhs, 32,
> > +
> >  ovnact_put_LOOKUP_ARP_IP(ctx->ovnacts));
> > +        } else if (!strcmp(ctx->lexer->token.s, "lookup_nd_ip")
> > +                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
> > +            parse_lookup_mac_bind_ip(ctx, &lhs, 128,
> > +
> >  ovnact_put_LOOKUP_ND_IP(ctx->ovnacts));
> >          } else {
> >              parse_assignment_action(ctx, false, &lhs);
> >          }
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index b7976c6..12849b4 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -1203,6 +1203,31 @@ reg0[0] = lookup_arp(inport, eth.dst);
> >  reg0[0] = lookup_arp(inport, ip4.src, ip4.dst);
> >      Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is
required.
> >
> > +# lookup_arp_ip
> > +reg0[0] = lookup_arp_ip(inport, ip4.dst);
> > +    encodes as
> >
push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
> > +    has prereqs eth.type == 0x800
> > +reg1[1] = lookup_arp_ip(inport, arp.spa);
> > +    encodes as
> >
push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_ARP_SPA[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
> > +    has prereqs eth.type == 0x806
> > +
> > +lookup_arp_ip;
> > +    Syntax error at `lookup_arp_ip' expecting action.
> > +reg0[0] = lookup_arp_ip;
> > +    Syntax error at `lookup_arp_ip' expecting field name.
> > +reg0[0] = lookup_arp_ip();
> > +    Syntax error at `)' expecting field name.
> > +reg0[0] = lookup_arp_ip(inport);
> > +    Syntax error at `)' expecting `,'.
> > +reg0[0] = lookup_arp_ip(inport ip4.dst);
> > +    Syntax error at `ip4.dst' expecting `,'.
> > +reg0[0] = lookup_arp_ip(inport, ip4.dst;
> > +    Syntax error at `;' expecting `)'.
> > +reg0[0] = lookup_arp_ip(inport, ip4.dst, eth.src;
> > +    Syntax error at `,' expecting `)'.
> > +reg0[0] = lookup_arp_ip(inport, eth.dst);
> > +    Cannot use 48-bit field eth.dst[0..47] where 32-bit field is
required.
> > +
> >  # put_dhcp_opts
> >  reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
> >      encodes as
> >
controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
> > @@ -1332,6 +1357,31 @@ reg0[0] = lookup_nd(inport, ip4.src, ip4.dst);
> >  reg0[0] = lookup_nd(inport, ip6.src, ip6.dst);
> >      Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is
> > required.
> >
> > +# lookup_nd_ip
> > +reg2[0] = lookup_nd_ip(inport, ip6.dst);
> > +    encodes as
> >
push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
> > +    has prereqs eth.type == 0x86dd
> > +reg3[0] = lookup_nd_ip(inport, nd.target);
> > +    encodes as
> >
push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_ND_TARGET[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
> > +    has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type
==
> > 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)
&&
> > icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type
==
> > 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 ||
> > eth.type == 0x86dd)
> > +
> > +lookup_nd_ip;
> > +    Syntax error at `lookup_nd_ip' expecting action.
> > +reg0[0] = lookup_nd_ip;
> > +    Syntax error at `lookup_nd_ip' expecting field name.
> > +reg0[0] = lookup_nd_ip();
> > +    Syntax error at `)' expecting field name.
> > +reg0[0] = lookup_nd_ip(inport);
> > +    Syntax error at `)' expecting `,'.
> > +reg0[0] = lookup_nd_ip(inport ip6.dst);
> > +    Syntax error at `ip6.dst' expecting `,'.
> > +reg0[0] = lookup_nd_ip(inport, ip6.dst;
> > +    Syntax error at `;' expecting `)'.
> > +reg0[0] = lookup_nd_ip(inport, eth.dst);
> > +    Cannot use 48-bit field eth.dst[0..47] where 128-bit field is
> > required.
> > +reg0[0] = lookup_nd_ip(inport, ip4.src);
> > +    Cannot use 32-bit field ip4.src[0..31] where 128-bit field is
> > required.
> > +
> >  # set_queue
> >  set_queue(0);
> >      encodes as set_queue:0
> > diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> > index 146560c..ae0a59e 100644
> > --- a/utilities/ovn-trace.c
> > +++ b/utilities/ovn-trace.c
> > @@ -582,13 +582,13 @@ ovntrace_mac_binding_find(const struct
> > ovntrace_datapath *dp,
> >  static const struct ovntrace_mac_binding *
> >  ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
> >                                   uint16_t port_key, const struct
in6_addr
> > *ip,
> > -                                 struct eth_addr mac)
> > +                                 struct eth_addr *mac)
> >  {
> >      const struct ovntrace_mac_binding *bind;
> >      HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key,
ip),
> >                               &dp->mac_bindings) {
> >          if (bind->port_key == port_key && ipv6_addr_equals(ip,
&bind->ip)
> > -            && eth_addr_equals(bind->mac, mac)) {
> > +            && (!mac || eth_addr_equals(bind->mac, *mac))) {
> >              return bind;
> >          }
> >      }
> > @@ -1771,7 +1771,7 @@ execute_lookup_mac_bind(const struct
> > ovnact_lookup_mac_bind *bind,
> >      mf_read_subfield(&mac_sf, uflow, &mac_sv);
> >
> >      const struct ovntrace_mac_binding *binding
> > -        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip,
mac_sv.mac);
> > +        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip,
> > &mac_sv.mac);
> >
> >      struct mf_subfield dst = expr_resolve_field(&bind->dst);
> >      uint8_t val = 0;
> > @@ -1790,6 +1790,44 @@ execute_lookup_mac_bind(const struct
> > ovnact_lookup_mac_bind *bind,
> >  }
> >
> >  static void
> > +execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip
*bind,
> > +                           const struct ovntrace_datapath *dp,
> > +                           struct flow *uflow,
> > +                           struct ovs_list *super)
> > +{
> > +    /* Get logical port number.*/
> > +    struct mf_subfield port_sf = expr_resolve_field(&bind->port);
> > +    ovs_assert(port_sf.n_bits == 32);
> > +    uint32_t port_key = mf_get_subfield(&port_sf, uflow);
> > +
> > +    /* Get IP address. */
> > +    struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
> > +    ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
> > +    union mf_subvalue ip_sv;
> > +    mf_read_subfield(&ip_sf, uflow, &ip_sv);
> > +    struct in6_addr ip = (ip_sf.n_bits == 32
> > +                          ? in6_addr_mapped_ipv4(ip_sv.ipv4)
> > +                          : ip_sv.ipv6);
> > +
> > +    const struct ovntrace_mac_binding *binding
> > +        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, NULL);
> > +
> > +    struct mf_subfield dst = expr_resolve_field(&bind->dst);
> > +    uint8_t val = 0;
> > +
> > +    if (binding) {
> > +        val = 1;
> > +        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
> > +                             "/* MAC binding for <TODO: ipv4/v6> found.
> > */");
> > +    } else {
> > +        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
> > +                             "/* lookup failed - No MAC binding. */");
> > +    }
> > +    union mf_subvalue sv = { .u8_val = val };
> > +    mf_write_subfield_flow(&dst, &sv, uflow);
> > +}
> > +
> > +static void
> >  execute_put_opts(const struct ovnact_put_opts *po,
> >                   const char *name, struct flow *uflow,
> >                   struct ovs_list *super)
> > @@ -2216,6 +2254,16 @@ trace_actions(const struct ovnact *ovnacts,
size_t
> > ovnacts_len,
> >              execute_lookup_mac_bind(ovnact_get_LOOKUP_ND(a), dp, uflow,
> > super);
> >              break;
> >
> > +        case OVNACT_LOOKUP_ARP_IP:
> > +            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ARP_IP(a), dp,
> > uflow,
> > +                                       super);
> > +            break;
> > +
> > +        case OVNACT_LOOKUP_ND_IP:
> > +            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ND_IP(a), dp,
> > uflow,
> > +                                       super);
> > +            break;
> > +
> >          case OVNACT_PUT_DHCPV4_OPTS:
> >              execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
> >                                    "put_dhcp_opts", uflow, super);
> > --
> > 2.1.0
> >
> > _______________________________________________
> > 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
diff mbox series

Patch

diff --git a/controller/lflow.c b/controller/lflow.c
index 01214a3..e45ed33 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -761,13 +761,15 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
 
     uint64_t stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
+    uint8_t value = 1;
     put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
+    put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
+             &ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100,
                     b->header_.uuid.parts[0], &get_arp_match,
                     &ofpacts, &b->header_.uuid);
 
     ofpbuf_clear(&ofpacts);
-    uint8_t value = 1;
     put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
              &ofpacts);
     match_set_dl_src(&lookup_arp_match, mac);
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 4a54abe..2b5a63a 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -75,9 +75,11 @@  struct ovn_extend_table;
     OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
     OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
     OVNACT(LOOKUP_ARP,        ovnact_lookup_mac_bind) \
+    OVNACT(LOOKUP_ARP_IP,     ovnact_lookup_mac_bind_ip) \
     OVNACT(GET_ND,            ovnact_get_mac_bind)    \
     OVNACT(PUT_ND,            ovnact_put_mac_bind)    \
     OVNACT(LOOKUP_ND,         ovnact_lookup_mac_bind) \
+    OVNACT(LOOKUP_ND_IP,      ovnact_lookup_mac_bind_ip) \
     OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
     OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
     OVNACT(SET_QUEUE,         ovnact_set_queue)       \
@@ -307,6 +309,14 @@  struct ovnact_lookup_mac_bind {
     struct expr_field mac;      /* 48-bit Ethernet address. */
 };
 
+/* OVNACT_LOOKUP_ARP_IP, OVNACT_LOOKUP_ND_IP. */
+struct ovnact_lookup_mac_bind_ip {
+    struct ovnact ovnact;
+    struct expr_field dst;      /* 1-bit destination field. */
+    struct expr_field port;     /* Logical port name. */
+    struct expr_field ip;       /* 32-bit or 128-bit IP address. */
+};
+
 struct ovnact_gen_option {
     const struct gen_opts_map *option;
     struct expr_constant_set value;
diff --git a/lib/actions.c b/lib/actions.c
index 22c0e76..41301c8 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1953,6 +1953,110 @@  ovnact_lookup_mac_bind_free(
 
 }
 
+
+static void format_lookup_mac_bind_ip(
+    const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+    struct ds *s, const char *name)
+{
+    expr_field_format(&lookup_mac->dst, s);
+    ds_put_format(s, " = %s(", name);
+    expr_field_format(&lookup_mac->port, s);
+    ds_put_cstr(s, ", ");
+    expr_field_format(&lookup_mac->ip, s);
+    ds_put_cstr(s, ");");
+}
+
+static void
+format_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+                     struct ds *s)
+{
+    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_arp_ip");
+}
+
+static void
+format_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+                    struct ds *s)
+{
+    format_lookup_mac_bind_ip(lookup_mac, s, "lookup_nd_ip");
+}
+
+static void
+encode_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+                          enum mf_field_id ip_field,
+                          const struct ovnact_encode_params *ep,
+                          struct ofpbuf *ofpacts)
+{
+    const struct arg args[] = {
+        { expr_resolve_field(&lookup_mac->port), MFF_LOG_OUTPORT },
+        { expr_resolve_field(&lookup_mac->ip), ip_field },
+    };
+
+    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
+    init_stack(ofpact_put_STACK_PUSH(ofpacts), MFF_ETH_DST);
+
+    struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst);
+    ovs_assert(dst.field);
+
+    put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts);
+    emit_resubmit(ofpacts, ep->mac_bind_ptable);
+
+    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
+    orm->dst = dst;
+    orm->src.field = mf_from_id(MFF_LOG_FLAGS);
+    orm->src.ofs = MLF_LOOKUP_MAC_BIT;
+    orm->src.n_bits = 1;
+
+    init_stack(ofpact_put_STACK_POP(ofpacts), MFF_ETH_DST);
+    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
+}
+
+static void
+encode_LOOKUP_ARP_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+                     const struct ovnact_encode_params *ep,
+                     struct ofpbuf *ofpacts)
+{
+    encode_lookup_mac_bind_ip(lookup_mac, MFF_REG0, ep, ofpacts);
+}
+
+static void
+encode_LOOKUP_ND_IP(const struct ovnact_lookup_mac_bind_ip *lookup_mac,
+                    const struct ovnact_encode_params *ep,
+                    struct ofpbuf *ofpacts)
+{
+    encode_lookup_mac_bind_ip(lookup_mac, MFF_XXREG0, ep, ofpacts);
+}
+
+static void
+parse_lookup_mac_bind_ip(struct action_context *ctx,
+                         const struct expr_field *dst,
+                         int width,
+                         struct ovnact_lookup_mac_bind_ip *lookup_mac)
+{
+    /* Validate that the destination is a 1-bit, modifiable field. */
+    char *error = expr_type_check(dst, 1, true);
+    if (error) {
+        lexer_error(ctx->lexer, "%s", error);
+        free(error);
+        return;
+    }
+
+    lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */
+    lexer_get(ctx->lexer); /* Skip '('. * */
+
+    action_parse_field(ctx, 0, false, &lookup_mac->port);
+    lexer_force_match(ctx->lexer, LEX_T_COMMA);
+    action_parse_field(ctx, width, false, &lookup_mac->ip);
+    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+    lookup_mac->dst = *dst;
+}
+
+static void
+ovnact_lookup_mac_bind_ip_free(
+    struct ovnact_lookup_mac_bind_ip *lookup_mac OVS_UNUSED)
+{
+
+}
+
 
 static void
 parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
@@ -3278,6 +3382,14 @@  parse_set_action(struct action_context *ctx)
                 && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
             parse_lookup_mac_bind(ctx, &lhs, 128,
                                   ovnact_put_LOOKUP_ND(ctx->ovnacts));
+        } else if (!strcmp(ctx->lexer->token.s, "lookup_arp_ip")
+                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+            parse_lookup_mac_bind_ip(ctx, &lhs, 32,
+                                     ovnact_put_LOOKUP_ARP_IP(ctx->ovnacts));
+        } else if (!strcmp(ctx->lexer->token.s, "lookup_nd_ip")
+                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+            parse_lookup_mac_bind_ip(ctx, &lhs, 128,
+                                     ovnact_put_LOOKUP_ND_IP(ctx->ovnacts));
         } else {
             parse_assignment_action(ctx, false, &lhs);
         }
diff --git a/tests/ovn.at b/tests/ovn.at
index b7976c6..12849b4 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1203,6 +1203,31 @@  reg0[0] = lookup_arp(inport, eth.dst);
 reg0[0] = lookup_arp(inport, ip4.src, ip4.dst);
     Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
 
+# lookup_arp_ip
+reg0[0] = lookup_arp_ip(inport, ip4.dst);
+    encodes as push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
+    has prereqs eth.type == 0x800
+reg1[1] = lookup_arp_ip(inport, arp.spa);
+    encodes as push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_OF_ARP_SPA[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_DST[],pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
+    has prereqs eth.type == 0x806
+
+lookup_arp_ip;
+    Syntax error at `lookup_arp_ip' expecting action.
+reg0[0] = lookup_arp_ip;
+    Syntax error at `lookup_arp_ip' expecting field name.
+reg0[0] = lookup_arp_ip();
+    Syntax error at `)' expecting field name.
+reg0[0] = lookup_arp_ip(inport);
+    Syntax error at `)' expecting `,'.
+reg0[0] = lookup_arp_ip(inport ip4.dst);
+    Syntax error at `ip4.dst' expecting `,'.
+reg0[0] = lookup_arp_ip(inport, ip4.dst;
+    Syntax error at `;' expecting `)'.
+reg0[0] = lookup_arp_ip(inport, ip4.dst, eth.src;
+    Syntax error at `,' expecting `)'.
+reg0[0] = lookup_arp_ip(inport, eth.dst);
+    Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required.
+
 # put_dhcp_opts
 reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
@@ -1332,6 +1357,31 @@  reg0[0] = lookup_nd(inport, ip4.src, ip4.dst);
 reg0[0] = lookup_nd(inport, ip6.src, ip6.dst);
     Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is required.
 
+# lookup_nd_ip
+reg2[0] = lookup_nd_ip(inport, ip6.dst);
+    encodes as push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
+    has prereqs eth.type == 0x86dd
+reg3[0] = lookup_nd_ip(inport, nd.target);
+    encodes as push:NXM_NX_REG15[],push:NXM_NX_XXREG0[],push:NXM_NX_ND_TARGET[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_XXREG0[],push:NXM_OF_ETH_DST[],set_field:0/0x40->reg10,resubmit(,66),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_DST[],pop:NXM_NX_XXREG0[],pop:NXM_NX_REG15[]
+    has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
+
+lookup_nd_ip;
+    Syntax error at `lookup_nd_ip' expecting action.
+reg0[0] = lookup_nd_ip;
+    Syntax error at `lookup_nd_ip' expecting field name.
+reg0[0] = lookup_nd_ip();
+    Syntax error at `)' expecting field name.
+reg0[0] = lookup_nd_ip(inport);
+    Syntax error at `)' expecting `,'.
+reg0[0] = lookup_nd_ip(inport ip6.dst);
+    Syntax error at `ip6.dst' expecting `,'.
+reg0[0] = lookup_nd_ip(inport, ip6.dst;
+    Syntax error at `;' expecting `)'.
+reg0[0] = lookup_nd_ip(inport, eth.dst);
+    Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required.
+reg0[0] = lookup_nd_ip(inport, ip4.src);
+    Cannot use 32-bit field ip4.src[0..31] where 128-bit field is required.
+
 # set_queue
 set_queue(0);
     encodes as set_queue:0
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index 146560c..ae0a59e 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -582,13 +582,13 @@  ovntrace_mac_binding_find(const struct ovntrace_datapath *dp,
 static const struct ovntrace_mac_binding *
 ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
                                  uint16_t port_key, const struct in6_addr *ip,
-                                 struct eth_addr mac)
+                                 struct eth_addr *mac)
 {
     const struct ovntrace_mac_binding *bind;
     HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
                              &dp->mac_bindings) {
         if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)
-            && eth_addr_equals(bind->mac, mac)) {
+            && (!mac || eth_addr_equals(bind->mac, *mac))) {
             return bind;
         }
     }
@@ -1771,7 +1771,7 @@  execute_lookup_mac_bind(const struct ovnact_lookup_mac_bind *bind,
     mf_read_subfield(&mac_sf, uflow, &mac_sv);
 
     const struct ovntrace_mac_binding *binding
-        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, mac_sv.mac);
+        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, &mac_sv.mac);
 
     struct mf_subfield dst = expr_resolve_field(&bind->dst);
     uint8_t val = 0;
@@ -1790,6 +1790,44 @@  execute_lookup_mac_bind(const struct ovnact_lookup_mac_bind *bind,
 }
 
 static void
+execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *bind,
+                           const struct ovntrace_datapath *dp,
+                           struct flow *uflow,
+                           struct ovs_list *super)
+{
+    /* Get logical port number.*/
+    struct mf_subfield port_sf = expr_resolve_field(&bind->port);
+    ovs_assert(port_sf.n_bits == 32);
+    uint32_t port_key = mf_get_subfield(&port_sf, uflow);
+
+    /* Get IP address. */
+    struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
+    ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
+    union mf_subvalue ip_sv;
+    mf_read_subfield(&ip_sf, uflow, &ip_sv);
+    struct in6_addr ip = (ip_sf.n_bits == 32
+                          ? in6_addr_mapped_ipv4(ip_sv.ipv4)
+                          : ip_sv.ipv6);
+
+    const struct ovntrace_mac_binding *binding
+        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, NULL);
+
+    struct mf_subfield dst = expr_resolve_field(&bind->dst);
+    uint8_t val = 0;
+
+    if (binding) {
+        val = 1;
+        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+                             "/* MAC binding for <TODO: ipv4/v6> found. */");
+    } else {
+        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+                             "/* lookup failed - No MAC binding. */");
+    }
+    union mf_subvalue sv = { .u8_val = val };
+    mf_write_subfield_flow(&dst, &sv, uflow);
+}
+
+static void
 execute_put_opts(const struct ovnact_put_opts *po,
                  const char *name, struct flow *uflow,
                  struct ovs_list *super)
@@ -2216,6 +2254,16 @@  trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
             execute_lookup_mac_bind(ovnact_get_LOOKUP_ND(a), dp, uflow, super);
             break;
 
+        case OVNACT_LOOKUP_ARP_IP:
+            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ARP_IP(a), dp, uflow,
+                                       super);
+            break;
+
+        case OVNACT_LOOKUP_ND_IP:
+            execute_lookup_mac_bind_ip(ovnact_get_LOOKUP_ND_IP(a), dp, uflow,
+                                       super);
+            break;
+
         case OVNACT_PUT_DHCPV4_OPTS:
             execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
                                   "put_dhcp_opts", uflow, super);