diff mbox series

[ovs-dev,v3] northd: drop traffic to disabled LSPs in ingress pipeline

Message ID 20220916011459.2683707-1-odivlad@gmail.com
State Accepted
Headers show
Series [ovs-dev,v3] northd: drop traffic to disabled LSPs in ingress pipeline | 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 success github build: passed

Commit Message

Vladislav Odintsov Sept. 16, 2022, 1:14 a.m. UTC
Prior to this patch traffic to LSPs, which are disabled with
`ovn-nbctl lsp-set-enabled <LSP> disabled` was dropped in the end of
lswitch egress pipeline.  This means that traffic is processed in vain:
- traffic, which should be dropped, first travels from one chassis to
  another (if source/dest LSPs reside on different nodes) and dropped on
  the destination chassis;
- when such traffic reaches destination chassis, if stateful services are
  enabled within logical switch, first traffic is sent to conntrack and
  is dropped after that.

So it is costly to drop traffic in such manner especially in case LSP is
disabled to prevent chassis and/or VM attack by any harmful traffic.

This patch changes "to-lport" drop behaviour.  Now it is dropped in
lswitch ingress pipeline to avoid sending traffic to disabled LSP from
one chassis to another.
Traffic doesn't reach conntrack in destination LSP's zone now as well.

Signed-off-by: Vladislav Odintsov <odivlad@gmail.com>
---
v3: Addressed Numan's review comments:
       turned back drop lflow in egress pipeline
---
 northd/northd.c         |  17 ++--
 northd/ovn-northd.8.xml |  14 ++-
 tests/ovn-northd.at     | 188 +++++++++++++++++++++++++---------------
 3 files changed, 140 insertions(+), 79 deletions(-)

Comments

Mark Michelson Sept. 16, 2022, 3:19 p.m. UTC | #1
Hi,

Acked-by: Mark Michelson <mmichels@redhat.com>

In the interest of getting this merged into 22.09 before its impending 
release, I have gone ahead and backported this to branch-22.09 and 
branch-22.06. I attempted to backport this to branch-22.03 but there 
were conflicts. If you could post a branch-22.03 version of this patch, 
that would be wonderful. Thanks!

On 9/15/22 21:14, Vladislav Odintsov wrote:
> Prior to this patch traffic to LSPs, which are disabled with
> `ovn-nbctl lsp-set-enabled <LSP> disabled` was dropped in the end of
> lswitch egress pipeline.  This means that traffic is processed in vain:
> - traffic, which should be dropped, first travels from one chassis to
>    another (if source/dest LSPs reside on different nodes) and dropped on
>    the destination chassis;
> - when such traffic reaches destination chassis, if stateful services are
>    enabled within logical switch, first traffic is sent to conntrack and
>    is dropped after that.
> 
> So it is costly to drop traffic in such manner especially in case LSP is
> disabled to prevent chassis and/or VM attack by any harmful traffic.
> 
> This patch changes "to-lport" drop behaviour.  Now it is dropped in
> lswitch ingress pipeline to avoid sending traffic to disabled LSP from
> one chassis to another.
> Traffic doesn't reach conntrack in destination LSP's zone now as well.
> 
> Signed-off-by: Vladislav Odintsov <odivlad@gmail.com>
> ---
> v3: Addressed Numan's review comments:
>         turned back drop lflow in egress pipeline
> ---
>   northd/northd.c         |  17 ++--
>   northd/ovn-northd.8.xml |  14 ++-
>   tests/ovn-northd.at     | 188 +++++++++++++++++++++++++---------------
>   3 files changed, 140 insertions(+), 79 deletions(-)
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index 4a40ec9b0..ef93500c5 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -5478,6 +5478,10 @@ build_lswitch_port_sec_op(struct ovn_port *op, struct hmap *lflows,
>               lflows, op->od, S_SWITCH_OUT_CHECK_PORT_SEC, 150,
>               ds_cstr(match), REGBIT_PORT_SEC_DROP" = 1; next;",
>               op->key, &op->nbsp->header_);
> +
> +        ovn_lflow_add_with_lport_and_hint(
> +            lflows, op->od, S_SWITCH_IN_L2_UNKNOWN, 50, ds_cstr(match),
> +            "drop;", op->key, &op->nbsp->header_);
>           return;
>       }
>   
> @@ -8466,6 +8470,8 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op,
>                * Ethernet address followed by zero or more IPv4
>                * or IPv6 addresses (or both). */
>               struct eth_addr mac;
> +            bool lsp_enabled = lsp_is_enabled(op->nbsp);
> +            char *action = lsp_enabled ? "outport = %s; output;" : "drop;";
>               if (ovs_scan(op->nbsp->addresses[i],
>                           ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
>                   ds_clear(match);
> @@ -8473,13 +8479,13 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op,
>                                 ETH_ADDR_ARGS(mac));
>   
>                   ds_clear(actions);
> -                ds_put_format(actions, "outport = %s; output;", op->json_key);
> +                ds_put_format(actions, action, op->json_key);
>                   ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
>                                           50, ds_cstr(match),
>                                           ds_cstr(actions),
>                                           &op->nbsp->header_);
>               } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
> -                if (lsp_is_enabled(op->nbsp)) {
> +                if (lsp_enabled) {
>                       ovs_mutex_lock(&mcgroup_mutex);
>                       ovn_multicast_add(mcgroups, &mc_unknown, op);
>                       ovs_mutex_unlock(&mcgroup_mutex);
> @@ -8496,7 +8502,7 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op,
>                                 ETH_ADDR_ARGS(mac));
>   
>                   ds_clear(actions);
> -                ds_put_format(actions, "outport = %s; output;", op->json_key);
> +                ds_put_format(actions, action, op->json_key);
>                   ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
>                                           50, ds_cstr(match),
>                                           ds_cstr(actions),
> @@ -8544,7 +8550,7 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op,
>                   }
>   
>                   ds_clear(actions);
> -                ds_put_format(actions, "outport = %s; output;", op->json_key);
> +                ds_put_format(actions, action, op->json_key);
>                   ovn_lflow_add_with_hint(lflows, op->od,
>                                           S_SWITCH_IN_L2_LKUP, 50,
>                                           ds_cstr(match), ds_cstr(actions),
> @@ -8567,8 +8573,7 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op,
>                                             nat->logical_port);
>   
>                               ds_clear(actions);
> -                            ds_put_format(actions, "outport = %s; output;",
> -                                          op->json_key);
> +                            ds_put_format(actions, action, op->json_key);
>                               ovn_lflow_add_with_hint(lflows, op->od,
>                                                       S_SWITCH_IN_L2_LKUP, 50,
>                                                       ds_cstr(match),
> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> index f4eceb0ec..fc9b2e222 100644
> --- a/northd/ovn-northd.8.xml
> +++ b/northd/ovn-northd.8.xml
> @@ -1737,8 +1737,9 @@ output;
>         <li>
>           <p>
>             One priority-50 flow that matches each known Ethernet address against
> -          <code>eth.dst</code> and outputs the packet to the single associated
> -          output port.
> +          <code>eth.dst</code>.  Action of this flow outputs the packet to the
> +          single associated output port if it is enabled. <code>drop;</code>
> +          action is applied if LSP is disabled.
>           </p>
>   
>           <p>
> @@ -1814,6 +1815,13 @@ output;
>       </p>
>   
>       <ul>
> +      <li>
> +        <p>
> +          Priority 50 flow with the match <code>outport == <var>P</var></code>
> +          is added for each disabled Logical Switch Port <code>P</code>.  This
> +          flow has action <code>drop;</code>.
> +        </p>
> +      </li>
>         <li>
>           <p>
>             If the logical switch has logical ports with 'unknown' addresses set,
> @@ -1822,7 +1830,7 @@ output;
>   
>           <ul>
>             <li>
> -            Priority 50 flow with the match <code>outport == none</code> then
> +            Priority 50 flow with the match <code>outport == "none"</code> then
>               outputs them to the <code>MC_UNKNOWN</code> multicast group, which
>               <code>ovn-northd</code> populates with all enabled logical ports
>               that accept unknown destination packets.  As a small optimization,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index d5136ac6d..66cbbc3c6 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -5461,7 +5461,7 @@ AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl
>   check ovn-nbctl set logical_switch_port lsp0 enabled=false
>   check ovn-nbctl --wait=sb sync
>   AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl
> -3
> +4
>   ])
>   
>   AT_CLEANUP
> @@ -7425,16 +7425,22 @@ check ovn-nbctl --wait=sb ls-add sw0
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   check ovn-nbctl lsp-add sw0 sw0p1 -- lsp-set-addresses sw0p1 "00:00:00:00:00:01"
> @@ -7444,16 +7450,24 @@ check ovn-nbctl --wait=sb lsp-add sw0 localnetport -- lsp-set-type localnetport
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   check ovn-nbctl lsp-set-port-security sw0p1 "00:00:00:00:00:01 10.0.0.3 1000::3"
> @@ -7462,16 +7476,24 @@ check ovn-nbctl --wait=sb lsp-set-port-security sw0p2 "00:00:00:00:00:02 10.0.0.
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   # Disable sw0p1
> @@ -7480,37 +7502,55 @@ check ovn-nbctl --wait=sb set logical_switch_port sw0p1 enabled=false
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   check ovn-nbctl --wait=sb lsp-set-options sw0p2 qdisc_queue_id=10
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   check ovn-nbctl set logical_switch_port sw0p1 enabled=true
> @@ -7519,20 +7559,28 @@ check ovn-nbctl --wait=sb lsp-set-options localnetport qdisc_queue_id=10
>   ovn-sbctl dump-flows sw0 > sw0flows
>   AT_CAPTURE_FILE([sw0flows])
>   
> -AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
> -  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> -  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=16);)
> -  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> -  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> -  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> -  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> -  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> -  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> -  table=? (ls_out_apply_port_sec), priority=100  , match=(outport == "localnetport"), action=(set_queue(10); output;)
> -  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
> +sort | sed 's/table=../table=??/' ], [0], [dnl
> +  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
> +  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=16);)
> +  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> +  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
> +  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
> +  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
> +  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
> +  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
> +  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
> +  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
> +  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
> +  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
> +  table=??(ls_out_apply_port_sec), priority=100  , match=(outport == "localnetport"), action=(set_queue(10); output;)
> +  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
>   ])
>   
>   AT_CLEANUP
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index 4a40ec9b0..ef93500c5 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -5478,6 +5478,10 @@  build_lswitch_port_sec_op(struct ovn_port *op, struct hmap *lflows,
             lflows, op->od, S_SWITCH_OUT_CHECK_PORT_SEC, 150,
             ds_cstr(match), REGBIT_PORT_SEC_DROP" = 1; next;",
             op->key, &op->nbsp->header_);
+
+        ovn_lflow_add_with_lport_and_hint(
+            lflows, op->od, S_SWITCH_IN_L2_UNKNOWN, 50, ds_cstr(match),
+            "drop;", op->key, &op->nbsp->header_);
         return;
     }
 
@@ -8466,6 +8470,8 @@  build_lswitch_ip_unicast_lookup(struct ovn_port *op,
              * Ethernet address followed by zero or more IPv4
              * or IPv6 addresses (or both). */
             struct eth_addr mac;
+            bool lsp_enabled = lsp_is_enabled(op->nbsp);
+            char *action = lsp_enabled ? "outport = %s; output;" : "drop;";
             if (ovs_scan(op->nbsp->addresses[i],
                         ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
                 ds_clear(match);
@@ -8473,13 +8479,13 @@  build_lswitch_ip_unicast_lookup(struct ovn_port *op,
                               ETH_ADDR_ARGS(mac));
 
                 ds_clear(actions);
-                ds_put_format(actions, "outport = %s; output;", op->json_key);
+                ds_put_format(actions, action, op->json_key);
                 ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
                                         50, ds_cstr(match),
                                         ds_cstr(actions),
                                         &op->nbsp->header_);
             } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
-                if (lsp_is_enabled(op->nbsp)) {
+                if (lsp_enabled) {
                     ovs_mutex_lock(&mcgroup_mutex);
                     ovn_multicast_add(mcgroups, &mc_unknown, op);
                     ovs_mutex_unlock(&mcgroup_mutex);
@@ -8496,7 +8502,7 @@  build_lswitch_ip_unicast_lookup(struct ovn_port *op,
                               ETH_ADDR_ARGS(mac));
 
                 ds_clear(actions);
-                ds_put_format(actions, "outport = %s; output;", op->json_key);
+                ds_put_format(actions, action, op->json_key);
                 ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
                                         50, ds_cstr(match),
                                         ds_cstr(actions),
@@ -8544,7 +8550,7 @@  build_lswitch_ip_unicast_lookup(struct ovn_port *op,
                 }
 
                 ds_clear(actions);
-                ds_put_format(actions, "outport = %s; output;", op->json_key);
+                ds_put_format(actions, action, op->json_key);
                 ovn_lflow_add_with_hint(lflows, op->od,
                                         S_SWITCH_IN_L2_LKUP, 50,
                                         ds_cstr(match), ds_cstr(actions),
@@ -8567,8 +8573,7 @@  build_lswitch_ip_unicast_lookup(struct ovn_port *op,
                                           nat->logical_port);
 
                             ds_clear(actions);
-                            ds_put_format(actions, "outport = %s; output;",
-                                          op->json_key);
+                            ds_put_format(actions, action, op->json_key);
                             ovn_lflow_add_with_hint(lflows, op->od,
                                                     S_SWITCH_IN_L2_LKUP, 50,
                                                     ds_cstr(match),
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index f4eceb0ec..fc9b2e222 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1737,8 +1737,9 @@  output;
       <li>
         <p>
           One priority-50 flow that matches each known Ethernet address against
-          <code>eth.dst</code> and outputs the packet to the single associated
-          output port.
+          <code>eth.dst</code>.  Action of this flow outputs the packet to the
+          single associated output port if it is enabled. <code>drop;</code>
+          action is applied if LSP is disabled.
         </p>
 
         <p>
@@ -1814,6 +1815,13 @@  output;
     </p>
 
     <ul>
+      <li>
+        <p>
+          Priority 50 flow with the match <code>outport == <var>P</var></code>
+          is added for each disabled Logical Switch Port <code>P</code>.  This
+          flow has action <code>drop;</code>.
+        </p>
+      </li>
       <li>
         <p>
           If the logical switch has logical ports with 'unknown' addresses set,
@@ -1822,7 +1830,7 @@  output;
 
         <ul>
           <li>
-            Priority 50 flow with the match <code>outport == none</code> then
+            Priority 50 flow with the match <code>outport == "none"</code> then
             outputs them to the <code>MC_UNKNOWN</code> multicast group, which
             <code>ovn-northd</code> populates with all enabled logical ports
             that accept unknown destination packets.  As a small optimization,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index d5136ac6d..66cbbc3c6 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5461,7 +5461,7 @@  AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl
 check ovn-nbctl set logical_switch_port lsp0 enabled=false
 check ovn-nbctl --wait=sb sync
 AT_CHECK([ovn-sbctl --columns=tags list logical_flow | grep lsp0 -c], [0], [dnl
-3
+4
 ])
 
 AT_CLEANUP
@@ -7425,16 +7425,22 @@  check ovn-nbctl --wait=sb ls-add sw0
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 check ovn-nbctl lsp-add sw0 sw0p1 -- lsp-set-addresses sw0p1 "00:00:00:00:00:01"
@@ -7444,16 +7450,24 @@  check ovn-nbctl --wait=sb lsp-add sw0 localnetport -- lsp-set-type localnetport
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 check ovn-nbctl lsp-set-port-security sw0p1 "00:00:00:00:00:01 10.0.0.3 1000::3"
@@ -7462,16 +7476,24 @@  check ovn-nbctl --wait=sb lsp-set-port-security sw0p2 "00:00:00:00:00:02 10.0.0.
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 # Disable sw0p1
@@ -7480,37 +7502,55 @@  check ovn-nbctl --wait=sb set logical_switch_port sw0p1 enabled=false
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 check ovn-nbctl --wait=sb lsp-set-options sw0p2 qdisc_queue_id=10
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_check_port_sec), priority=150  , match=(outport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 check ovn-nbctl set logical_switch_port sw0p1 enabled=true
@@ -7519,20 +7559,28 @@  check ovn-nbctl --wait=sb lsp-set-options localnetport qdisc_queue_id=10
 ovn-sbctl dump-flows sw0 > sw0flows
 AT_CAPTURE_FILE([sw0flows])
 
-AT_CHECK([cat sw0flows | grep -e port_sec | sort | sed 's/table=./table=?/' ], [0], [dnl
-  table=? (ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
-  table=? (ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=16);)
-  table=? (ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
-  table=? (ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
-  table=? (ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
-  table=? (ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
-  table=? (ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
-  table=? (ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
-  table=? (ls_out_apply_port_sec), priority=100  , match=(outport == "localnetport"), action=(set_queue(10); output;)
-  table=? (ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown | \
+sort | sed 's/table=../table=??/' ], [0], [dnl
+  table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
+  table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=16);)
+  table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
+  table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+  table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+  table=??(ls_in_l2_lkup      ), priority=0    , match=(1), action=(outport = get_fdb(eth.dst); next;)
+  table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(handle_svc_check(inport);)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0p1"; output;)
+  table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0p2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
+  table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), action=(drop;)
+  table=??(ls_out_check_port_sec), priority=0    , match=(1), action=(reg0[[15]] = check_out_port_sec(); next;)
+  table=??(ls_out_check_port_sec), priority=100  , match=(eth.mcast), action=(reg0[[15]] = 0; next;)
+  table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
+  table=??(ls_out_apply_port_sec), priority=100  , match=(outport == "localnetport"), action=(set_queue(10); output;)
+  table=??(ls_out_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
 ])
 
 AT_CLEANUP