diff mbox series

[ovs-dev,2/5] northd, controller: Use the MAC cache table

Message ID 20230710110517.128560-3-amusil@redhat.com
State Superseded
Headers show
Series Add MAC binding aging timestamp refresh mechanism | expand

Checks

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

Commit Message

Ales Musil July 10, 2023, 11:05 a.m. UTC
Populate and use the MAC cache table. The MAC cache has
the following entries:
ip,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,nw_src=<MB_IP> actions=drop
ipv6,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,ipv6_src=<MB_IP> actions=drop

The "mac_cache_use" action will resubmit packets from
"lr_in_learn_neighbor" table for MAC bindings that we already
know.

This is a preparation for the MAC binding refresh mechanism.

Signed-off-by: Ales Musil <amusil@redhat.com>
---
 controller/lflow.c  | 22 ++++++++++++++++++++++
 controller/lflow.h  |  1 +
 northd/northd.c     |  2 +-
 tests/ovn-northd.at |  2 +-
 tests/ovn.at        | 23 ++++++++++++++++++++---
 5 files changed, 45 insertions(+), 5 deletions(-)

Comments

Mark Michelson July 20, 2023, 8:58 p.m. UTC | #1
Hi Ales,

Can you please add a test to tests/ovn-controller.at that ensures that 
flows in table 79 exist when we expect them to and that they are removed 
when we expect them to be removed?

On 7/10/23 07:05, Ales Musil wrote:
> Populate and use the MAC cache table. The MAC cache has
> the following entries:
> ip,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,nw_src=<MB_IP> actions=drop
> ipv6,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,ipv6_src=<MB_IP> actions=drop
> 
> The "mac_cache_use" action will resubmit packets from
> "lr_in_learn_neighbor" table for MAC bindings that we already
> know.
> 
> This is a preparation for the MAC binding refresh mechanism.
> 
> Signed-off-by: Ales Musil <amusil@redhat.com>
> ---
>   controller/lflow.c  | 22 ++++++++++++++++++++++
>   controller/lflow.h  |  1 +
>   northd/northd.c     |  2 +-
>   tests/ovn-northd.at |  2 +-
>   tests/ovn.at        | 23 ++++++++++++++++++++---
>   5 files changed, 45 insertions(+), 5 deletions(-)
> 
> diff --git a/controller/lflow.c b/controller/lflow.c
> index 22faaf013..bc5f73279 100644
> --- a/controller/lflow.c
> +++ b/controller/lflow.c
> @@ -887,6 +887,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
>           .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
>           .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
>           .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
> +        .mac_cache_use_table = OFTABLE_MAC_CACHE_USE,
>           .ctrl_meter_id = ctrl_meter_id,
>           .common_nat_ct_zone = get_common_nat_zone(ldp),
>       };
> @@ -1337,6 +1338,7 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>   
>       struct match get_arp_match = MATCH_CATCHALL_INITIALIZER;
>       struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER;
> +    struct match mb_cache_use_match = MATCH_CATCHALL_INITIALIZER;
>   
>       if (strchr(ip, '.')) {
>           ovs_be32 ip_addr;
> @@ -1345,9 +1347,14 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>               VLOG_WARN_RL(&rl, "bad 'ip' %s", ip);
>               return;
>           }
> +
>           match_set_reg(&get_arp_match, 0, ntohl(ip_addr));
> +
>           match_set_reg(&lookup_arp_match, 0, ntohl(ip_addr));
>           match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP));
> +
> +        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IP));
> +        match_set_nw_src(&mb_cache_use_match, ip_addr);
>       } else {
>           struct in6_addr ip6;
>           if (!ipv6_parse(ip, &ip6)) {
> @@ -1363,6 +1370,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>           match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6));
>           match_set_nw_proto(&lookup_arp_match, 58);
>           match_set_icmp_code(&lookup_arp_match, 0);
> +
> +        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IPV6));
> +        match_set_ipv6_src(&mb_cache_use_match, &ip6);
>       }
>   
>       match_set_metadata(&get_arp_match, htonll(pb->datapath->tunnel_key));
> @@ -1372,6 +1382,11 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>       match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - MFF_REG0,
>                     pb->tunnel_key);
>   
> +    match_set_dl_src(&mb_cache_use_match, mac_addr);
> +    match_set_reg(&mb_cache_use_match, MFF_LOG_INPORT - MFF_REG0,
> +                  pb->tunnel_key);
> +    match_set_metadata(&mb_cache_use_match, htonll(pb->datapath->tunnel_key));
> +
>       uint64_t stub[1024 / 8];
>       struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
>       uint8_t value = 1;
> @@ -1392,6 +1407,13 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>                       &lookup_arp_match, &ofpacts,
>                       b ? &b->header_.uuid : &smb->header_.uuid);
>   
> +    if (b) {
> +        ofpbuf_clear(&ofpacts);
> +        ofctrl_add_flow(flow_table, OFTABLE_MAC_CACHE_USE, priority,
> +                        b->header_.uuid.parts[0], &mb_cache_use_match,
> +                        &ofpacts, &b->header_.uuid);
> +    }
> +
>       ofpbuf_uninit(&ofpacts);
>   }
>   
> diff --git a/controller/lflow.h b/controller/lflow.h
> index 2472dec29..5da4385e4 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -93,6 +93,7 @@ struct uuid;
>   #define OFTABLE_ECMP_NH_MAC              76
>   #define OFTABLE_ECMP_NH                  77
>   #define OFTABLE_CHK_LB_AFFINITY          78
> +#define OFTABLE_MAC_CACHE_USE            79
>   
>   struct lflow_ctx_in {
>       struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
> diff --git a/northd/northd.c b/northd/northd.c
> index b9605862e..4e3c5d02a 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -12615,7 +12615,7 @@ build_neigh_learning_flows_for_lrouter(
>                     learn_from_arp_request ? "" :
>                     " || "REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" == 0");
>       ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
> -                  ds_cstr(match), "next;");
> +                  ds_cstr(match), "mac_cache_use; next;");
>   
>       ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
>                         "arp", "put_arp(inport, arp.spa, arp.sha); next;",
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 3e06f14c9..6298d4cec 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -7432,7 +7432,7 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
>     table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
>     table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
>     table=2 (lr_in_learn_neighbor), priority=0    , match=(1), action=(drop;)
> -  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] == 1), action=(next;)
> +  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] == 1), action=(mac_cache_use; next;)
>     table=2 (lr_in_learn_neighbor), priority=90   , match=(arp), action=(put_arp(inport, arp.spa, arp.sha); next;)
>     table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_na), action=(put_nd(inport, nd.target, nd.tll); next;)
>     table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_ns), action=(put_nd(inport, ip6.src, nd.sll); next;)
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 7fee42acf..7cee8a175 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -34532,12 +34532,23 @@ send_garp() {
>       mac_byte=$3
>       ip_byte=${4-$3}
>   
> -    mac="0000000010$mac_byte"
> -    ip=`ip_to_hex 192 168 10 $ip_byte`
> -    packet=ffffffffffff${mac}08060001080006040002${mac}${ip}${mac}${ip}
> +    mac="00:00:00:00:10:${mac_byte}"
> +    ip="192.168.10.${ip_byte}"
> +    packet=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${mac}')/ \
> +                      ARP(op=2, hwsrc='${mac}', hwdst='${mac}', \
> +                      psrc='${ip}', pdst='${ip}')")
>       as $hv ovs-appctl netdev-dummy/receive $dev $packet
>   }
>   
> +send_udp() {
> +    hv=$1
> +    dev=$2
> +    byte=$3
> +
> +    packet=$(fmt_pkt "Ether(dst='00:00:00:00:10:00', src='00:00:00:00:10:${byte}')/ \
> +                      IP(dst='192.168.20.${byte}', src='192.168.10.${byte}')/UDP()")
> +    as $hv ovs-appctl netdev-dummy/receive $dev $packet
> +}
>   # Check if the option is not present by default
>   AT_CHECK([fetch_column nb:logical_router options name="gw" | grep -q mac_binding_age_threshold], [1])
>   
> @@ -34548,6 +34559,12 @@ send_garp hv2 ext2 20
>   OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.10"])
>   OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.20"])
>   
> +send_udp hv1 ext1 10
> +send_udp hv2 ext2 20
> +
> +OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=79 | grep "192.168.10.10" | grep -q "n_packets=1"])
> +OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=79 | grep "192.168.10.20" | grep -q "n_packets=1"])
> +
>   # Set the MAC binding aging threshold
>   AT_CHECK([ovn-nbctl set logical_router gw options:mac_binding_age_threshold=1])
>   AT_CHECK([fetch_column nb:logical_router options | grep -q mac_binding_age_threshold=1])
Ales Musil July 28, 2023, 10:09 a.m. UTC | #2
On Thu, Jul 20, 2023 at 10:58 PM Mark Michelson <mmichels@redhat.com> wrote:

> Hi Ales,
>
> Can you please add a test to tests/ovn-controller.at that ensures that
> flows in table 79 exist when we expect them to and that they are removed
> when we expect them to be removed?
>

Hi Mark,

I did adjust in v2 the MAC binding test to also check if the flows are
added/removed.


>
> On 7/10/23 07:05, Ales Musil wrote:
> > Populate and use the MAC cache table. The MAC cache has
> > the following entries:
> > ip,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,nw_src=<MB_IP>
> actions=drop
> >
> ipv6,reg14=<MB_Port>,metadata=<MB_Datapath>,dl_src=<MB_MAC>,ipv6_src=<MB_IP>
> actions=drop
> >
> > The "mac_cache_use" action will resubmit packets from
> > "lr_in_learn_neighbor" table for MAC bindings that we already
> > know.
> >
> > This is a preparation for the MAC binding refresh mechanism.
> >
> > Signed-off-by: Ales Musil <amusil@redhat.com>
> > ---
> >   controller/lflow.c  | 22 ++++++++++++++++++++++
> >   controller/lflow.h  |  1 +
> >   northd/northd.c     |  2 +-
> >   tests/ovn-northd.at |  2 +-
> >   tests/ovn.at        | 23 ++++++++++++++++++++---
> >   5 files changed, 45 insertions(+), 5 deletions(-)
> >
> > diff --git a/controller/lflow.c b/controller/lflow.c
> > index 22faaf013..bc5f73279 100644
> > --- a/controller/lflow.c
> > +++ b/controller/lflow.c
> > @@ -887,6 +887,7 @@ add_matches_to_flow_table(const struct
> sbrec_logical_flow *lflow,
> >           .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
> >           .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
> >           .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
> > +        .mac_cache_use_table = OFTABLE_MAC_CACHE_USE,
> >           .ctrl_meter_id = ctrl_meter_id,
> >           .common_nat_ct_zone = get_common_nat_zone(ldp),
> >       };
> > @@ -1337,6 +1338,7 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >
> >       struct match get_arp_match = MATCH_CATCHALL_INITIALIZER;
> >       struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER;
> > +    struct match mb_cache_use_match = MATCH_CATCHALL_INITIALIZER;
> >
> >       if (strchr(ip, '.')) {
> >           ovs_be32 ip_addr;
> > @@ -1345,9 +1347,14 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >               VLOG_WARN_RL(&rl, "bad 'ip' %s", ip);
> >               return;
> >           }
> > +
> >           match_set_reg(&get_arp_match, 0, ntohl(ip_addr));
> > +
> >           match_set_reg(&lookup_arp_match, 0, ntohl(ip_addr));
> >           match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP));
> > +
> > +        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IP));
> > +        match_set_nw_src(&mb_cache_use_match, ip_addr);
> >       } else {
> >           struct in6_addr ip6;
> >           if (!ipv6_parse(ip, &ip6)) {
> > @@ -1363,6 +1370,9 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >           match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6));
> >           match_set_nw_proto(&lookup_arp_match, 58);
> >           match_set_icmp_code(&lookup_arp_match, 0);
> > +
> > +        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IPV6));
> > +        match_set_ipv6_src(&mb_cache_use_match, &ip6);
> >       }
> >
> >       match_set_metadata(&get_arp_match,
> htonll(pb->datapath->tunnel_key));
> > @@ -1372,6 +1382,11 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >       match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - MFF_REG0,
> >                     pb->tunnel_key);
> >
> > +    match_set_dl_src(&mb_cache_use_match, mac_addr);
> > +    match_set_reg(&mb_cache_use_match, MFF_LOG_INPORT - MFF_REG0,
> > +                  pb->tunnel_key);
> > +    match_set_metadata(&mb_cache_use_match,
> htonll(pb->datapath->tunnel_key));
> > +
> >       uint64_t stub[1024 / 8];
> >       struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
> >       uint8_t value = 1;
> > @@ -1392,6 +1407,13 @@ consider_neighbor_flow(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> >                       &lookup_arp_match, &ofpacts,
> >                       b ? &b->header_.uuid : &smb->header_.uuid);
> >
> > +    if (b) {
> > +        ofpbuf_clear(&ofpacts);
> > +        ofctrl_add_flow(flow_table, OFTABLE_MAC_CACHE_USE, priority,
> > +                        b->header_.uuid.parts[0], &mb_cache_use_match,
> > +                        &ofpacts, &b->header_.uuid);
> > +    }
> > +
> >       ofpbuf_uninit(&ofpacts);
> >   }
> >
> > diff --git a/controller/lflow.h b/controller/lflow.h
> > index 2472dec29..5da4385e4 100644
> > --- a/controller/lflow.h
> > +++ b/controller/lflow.h
> > @@ -93,6 +93,7 @@ struct uuid;
> >   #define OFTABLE_ECMP_NH_MAC              76
> >   #define OFTABLE_ECMP_NH                  77
> >   #define OFTABLE_CHK_LB_AFFINITY          78
> > +#define OFTABLE_MAC_CACHE_USE            79
> >
> >   struct lflow_ctx_in {
> >       struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
> > diff --git a/northd/northd.c b/northd/northd.c
> > index b9605862e..4e3c5d02a 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -12615,7 +12615,7 @@ build_neigh_learning_flows_for_lrouter(
> >                     learn_from_arp_request ? "" :
> >                     " || "REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" == 0");
> >       ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
> > -                  ds_cstr(match), "next;");
> > +                  ds_cstr(match), "mac_cache_use; next;");
> >
> >       ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
> >                         "arp", "put_arp(inport, arp.spa, arp.sha);
> next;",
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 3e06f14c9..6298d4cec 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -7432,7 +7432,7 @@ AT_CHECK([cat lrflows | grep -e
> lr_in_lookup_neighbor -e lr_in_learn_neighbor |
> >     table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_na),
> action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
> >     table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_ns),
> action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
> >     table=2 (lr_in_learn_neighbor), priority=0    , match=(1),
> action=(drop;)
> > -  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] ==
> 1), action=(next;)
> > +  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] ==
> 1), action=(mac_cache_use; next;)
> >     table=2 (lr_in_learn_neighbor), priority=90   , match=(arp),
> action=(put_arp(inport, arp.spa, arp.sha); next;)
> >     table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_na),
> action=(put_nd(inport, nd.target, nd.tll); next;)
> >     table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_ns),
> action=(put_nd(inport, ip6.src, nd.sll); next;)
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index 7fee42acf..7cee8a175 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -34532,12 +34532,23 @@ send_garp() {
> >       mac_byte=$3
> >       ip_byte=${4-$3}
> >
> > -    mac="0000000010$mac_byte"
> > -    ip=`ip_to_hex 192 168 10 $ip_byte`
> > -    packet=ffffffffffff${mac}08060001080006040002${mac}${ip}${mac}${ip}
> > +    mac="00:00:00:00:10:${mac_byte}"
> > +    ip="192.168.10.${ip_byte}"
> > +    packet=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${mac}')/ \
> > +                      ARP(op=2, hwsrc='${mac}', hwdst='${mac}', \
> > +                      psrc='${ip}', pdst='${ip}')")
> >       as $hv ovs-appctl netdev-dummy/receive $dev $packet
> >   }
> >
> > +send_udp() {
> > +    hv=$1
> > +    dev=$2
> > +    byte=$3
> > +
> > +    packet=$(fmt_pkt "Ether(dst='00:00:00:00:10:00',
> src='00:00:00:00:10:${byte}')/ \
> > +                      IP(dst='192.168.20.${byte}',
> src='192.168.10.${byte}')/UDP()")
> > +    as $hv ovs-appctl netdev-dummy/receive $dev $packet
> > +}
> >   # Check if the option is not present by default
> >   AT_CHECK([fetch_column nb:logical_router options name="gw" | grep -q
> mac_binding_age_threshold], [1])
> >
> > @@ -34548,6 +34559,12 @@ send_garp hv2 ext2 20
> >   OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.10"])
> >   OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.20"])
> >
> > +send_udp hv1 ext1 10
> > +send_udp hv2 ext2 20
> > +
> > +OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=79 | grep
> "192.168.10.10" | grep -q "n_packets=1"])
> > +OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=79 | grep
> "192.168.10.20" | grep -q "n_packets=1"])
> > +
> >   # Set the MAC binding aging threshold
> >   AT_CHECK([ovn-nbctl set logical_router gw
> options:mac_binding_age_threshold=1])
> >   AT_CHECK([fetch_column nb:logical_router options | grep -q
> mac_binding_age_threshold=1])
>
>
Thanks,
Ales
diff mbox series

Patch

diff --git a/controller/lflow.c b/controller/lflow.c
index 22faaf013..bc5f73279 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -887,6 +887,7 @@  add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
         .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
         .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC,
         .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC,
+        .mac_cache_use_table = OFTABLE_MAC_CACHE_USE,
         .ctrl_meter_id = ctrl_meter_id,
         .common_nat_ct_zone = get_common_nat_zone(ldp),
     };
@@ -1337,6 +1338,7 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
 
     struct match get_arp_match = MATCH_CATCHALL_INITIALIZER;
     struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER;
+    struct match mb_cache_use_match = MATCH_CATCHALL_INITIALIZER;
 
     if (strchr(ip, '.')) {
         ovs_be32 ip_addr;
@@ -1345,9 +1347,14 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             VLOG_WARN_RL(&rl, "bad 'ip' %s", ip);
             return;
         }
+
         match_set_reg(&get_arp_match, 0, ntohl(ip_addr));
+
         match_set_reg(&lookup_arp_match, 0, ntohl(ip_addr));
         match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP));
+
+        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IP));
+        match_set_nw_src(&mb_cache_use_match, ip_addr);
     } else {
         struct in6_addr ip6;
         if (!ipv6_parse(ip, &ip6)) {
@@ -1363,6 +1370,9 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
         match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6));
         match_set_nw_proto(&lookup_arp_match, 58);
         match_set_icmp_code(&lookup_arp_match, 0);
+
+        match_set_dl_type(&mb_cache_use_match, htons(ETH_TYPE_IPV6));
+        match_set_ipv6_src(&mb_cache_use_match, &ip6);
     }
 
     match_set_metadata(&get_arp_match, htonll(pb->datapath->tunnel_key));
@@ -1372,6 +1382,11 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
     match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - MFF_REG0,
                   pb->tunnel_key);
 
+    match_set_dl_src(&mb_cache_use_match, mac_addr);
+    match_set_reg(&mb_cache_use_match, MFF_LOG_INPORT - MFF_REG0,
+                  pb->tunnel_key);
+    match_set_metadata(&mb_cache_use_match, htonll(pb->datapath->tunnel_key));
+
     uint64_t stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
     uint8_t value = 1;
@@ -1392,6 +1407,13 @@  consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                     &lookup_arp_match, &ofpacts,
                     b ? &b->header_.uuid : &smb->header_.uuid);
 
+    if (b) {
+        ofpbuf_clear(&ofpacts);
+        ofctrl_add_flow(flow_table, OFTABLE_MAC_CACHE_USE, priority,
+                        b->header_.uuid.parts[0], &mb_cache_use_match,
+                        &ofpacts, &b->header_.uuid);
+    }
+
     ofpbuf_uninit(&ofpacts);
 }
 
diff --git a/controller/lflow.h b/controller/lflow.h
index 2472dec29..5da4385e4 100644
--- a/controller/lflow.h
+++ b/controller/lflow.h
@@ -93,6 +93,7 @@  struct uuid;
 #define OFTABLE_ECMP_NH_MAC              76
 #define OFTABLE_ECMP_NH                  77
 #define OFTABLE_CHK_LB_AFFINITY          78
+#define OFTABLE_MAC_CACHE_USE            79
 
 struct lflow_ctx_in {
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
diff --git a/northd/northd.c b/northd/northd.c
index b9605862e..4e3c5d02a 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -12615,7 +12615,7 @@  build_neigh_learning_flows_for_lrouter(
                   learn_from_arp_request ? "" :
                   " || "REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" == 0");
     ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
-                  ds_cstr(match), "next;");
+                  ds_cstr(match), "mac_cache_use; next;");
 
     ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
                       "arp", "put_arp(inport, arp.spa, arp.sha); next;",
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 3e06f14c9..6298d4cec 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -7432,7 +7432,7 @@  AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
   table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
   table=1 (lr_in_lookup_neighbor), priority=100  , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
   table=2 (lr_in_learn_neighbor), priority=0    , match=(1), action=(drop;)
-  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] == 1), action=(next;)
+  table=2 (lr_in_learn_neighbor), priority=100  , match=(reg9[[2]] == 1), action=(mac_cache_use; next;)
   table=2 (lr_in_learn_neighbor), priority=90   , match=(arp), action=(put_arp(inport, arp.spa, arp.sha); next;)
   table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_na), action=(put_nd(inport, nd.target, nd.tll); next;)
   table=2 (lr_in_learn_neighbor), priority=90   , match=(nd_ns), action=(put_nd(inport, ip6.src, nd.sll); next;)
diff --git a/tests/ovn.at b/tests/ovn.at
index 7fee42acf..7cee8a175 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -34532,12 +34532,23 @@  send_garp() {
     mac_byte=$3
     ip_byte=${4-$3}
 
-    mac="0000000010$mac_byte"
-    ip=`ip_to_hex 192 168 10 $ip_byte`
-    packet=ffffffffffff${mac}08060001080006040002${mac}${ip}${mac}${ip}
+    mac="00:00:00:00:10:${mac_byte}"
+    ip="192.168.10.${ip_byte}"
+    packet=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${mac}')/ \
+                      ARP(op=2, hwsrc='${mac}', hwdst='${mac}', \
+                      psrc='${ip}', pdst='${ip}')")
     as $hv ovs-appctl netdev-dummy/receive $dev $packet
 }
 
+send_udp() {
+    hv=$1
+    dev=$2
+    byte=$3
+
+    packet=$(fmt_pkt "Ether(dst='00:00:00:00:10:00', src='00:00:00:00:10:${byte}')/ \
+                      IP(dst='192.168.20.${byte}', src='192.168.10.${byte}')/UDP()")
+    as $hv ovs-appctl netdev-dummy/receive $dev $packet
+}
 # Check if the option is not present by default
 AT_CHECK([fetch_column nb:logical_router options name="gw" | grep -q mac_binding_age_threshold], [1])
 
@@ -34548,6 +34559,12 @@  send_garp hv2 ext2 20
 OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.10"])
 OVS_WAIT_UNTIL([ovn-sbctl list mac_binding | grep -q "192.168.10.20"])
 
+send_udp hv1 ext1 10
+send_udp hv2 ext2 20
+
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=79 | grep "192.168.10.10" | grep -q "n_packets=1"])
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=79 | grep "192.168.10.20" | grep -q "n_packets=1"])
+
 # Set the MAC binding aging threshold
 AT_CHECK([ovn-nbctl set logical_router gw options:mac_binding_age_threshold=1])
 AT_CHECK([fetch_column nb:logical_router options | grep -q mac_binding_age_threshold=1])