| Message ID | 20261002142940.2556236-2-dceara@redhat.com |
|---|---|
| State | Changes Requested |
| Delegated to: | Ales Musil |
| Headers | show |
| Series | Fix FDB learning for VRRP scenarios. | expand |
| 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 |
On Fri, Oct 2, 2026 at 7:31 PM Dumitru Ceara via dev < ovs-dev@openvswitch.org> wrote: > Currently, build_lswitch_learn_fdb_op() only generates > put_fdb(inport, eth.src) flows, so only eth.src is ever learned > into the FDB. In VRRP gratuitous ARP packets, eth.src is the > physical MAC while arp.sha is the virtual MAC (VMAC). Since > put_fdb only records eth.src, the VMAC is never learned and > traffic destined to it falls through to MC_UNKNOWN flooding. > > The same gap exists for IPv6 Neighbor Discovery: unsolicited > NAs carry the VMAC in nd.tll, and NSes carry it in nd.sll. > > Add higher-priority (110) flows in both the lookup_fdb and > put_fdb stages for ARP, ND NA, and ND NS packets. The lookup > flows check both eth.src and the inner MAC field (arp.sha, > nd.tll, nd.sll) and store the inner result in a new regbit > REGBIT_LKUP_FDB_DL_INNER (reg0[22]). The put flows fire when > either MAC is unknown and learn both via two put_fdb calls. > When both MACs are already known, no put_fdb flow matches and > no packet-in is generated. > > ARP/ND packets may lack the arp.sha/SLL/TLL, in which case > arp.sha/nd.sll/nd.tll is all-zeros. Higher priority (120) > flows for ARP/ND flows prevent learning a zero MAC into the > FDB. > Hi Dumitru, thank you for the patch. Few small comments down below. > > Reported-at: https://redhat.atlassian.net/browse/FDP-4286 nit: This should only be in the 2/2 patch. > > Assisted-by: Claude Opus 4.6, opencode > Signed-off-by: Dumitru Ceara <dceara@redhat.com> > --- > NEWS | 5 ++ > northd/northd.c | 190 +++++++++++++++++++++++++++++++++++++++++--- > ovn-nb.xml | 23 ++++-- > tests/ovn-ic.at | 72 +++++++++++++++++ > tests/ovn-northd.at | 118 +++++++++++++++++++++++++++ > tests/ovn.at | 145 +++++++++++++++++++++++++++++++-- > 6 files changed, 532 insertions(+), 21 deletions(-) > > diff --git a/NEWS b/NEWS > index 7f94d0b14f..d025581c9a 100644 > --- a/NEWS > +++ b/NEWS > @@ -1,5 +1,10 @@ > Post v26.09.0 > -------------- > + - Learn ARP sender hardware address (arp.sha) and IPv6 Neighbor > + Discovery link-layer addresses (nd.tll, nd.sll) into the FDB > + table in addition to eth.src. This allows VRRP virtual MACs to > + be learned from gratuitous ARP packets and unsolicited Neighbor > + Advertisements. > - Added a new "options:ttl" key on the NB DNS table to make the TTL of > DNS replies from OVN's native DNS resolver configurable per row. > - Removed implementations of the commit_ecmp_nh, chk_ecmp_nh, and > diff --git a/northd/northd.c b/northd/northd.c > index f37040b57f..d33b60cd76 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -142,6 +142,7 @@ static bool vxlan_mode; > #define REGBIT_IP_FRAG "reg0[19]" > #define REGBIT_ACL_PERSIST_ID "reg0[20]" > #define REGBIT_ACL_HINT_ALLOW_PERSISTED "reg0[21]" > +#define REGBIT_LKUP_FDB_DL_INNER "reg0[22]" > > /* Register definitions for switches and routers. */ > > @@ -6635,7 +6636,21 @@ build_lswitch_learn_fdb_op( > return; > } > > + /* For non-ARP, non-ND Ethernet packets lookup their eth.src and learn > + * it if not already known. > + * > + * For ARP packets, also look up arp.sha in the FDB. For ND packets, > + * look up nd.tll (NA) or nd.sll (NS). They may differ from eth.src > + * in specific cases. > + * > + * ND packets may lack the optional SLLAO/TLLAO option, in which case > + * nd.sll/nd.tll is all-zeros so they need even more special > attention. > + */ > bool remote = lsp_is_remote(op->nbsp); > + const struct ovn_stage *lkup_stage = remote ? S_SWITCH_OUT_LOOKUP_FDB > + : S_SWITCH_IN_LOOKUP_FDB; > + const struct ovn_stage *put_stage = remote ? S_SWITCH_OUT_PUT_FDB > + : S_SWITCH_IN_PUT_FDB; > > if (remote || (!strcmp(op->nbsp->type, "") && > lsp_can_learn_mac(op->nbsp)) > || lsp_is_switch(op->nbsp) > @@ -6646,20 +6661,177 @@ build_lswitch_learn_fdb_op( > if (lsp_is_localnet(op->nbsp)) { > ds_put_cstr(actions, "flags.localnet = 1; "); > } > + size_t match_len = match->length; > + size_t actions_len = actions->length; > + > + /* Lookup: Prio: 120, match: arp.sha == 0, > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src); > + * REGBIT_LKUP_FDB_DL_INNER = 1; > + * Put: Prio: 120, match: arp.sha == 0 && REGBIT_LKUP_FDB == 0, > + * action: put_fdb(eth.src). > + */ > + ds_put_cstr(match, " && arp.sha == 00:00:00:00:00:00"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 120, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); > + ovn_lflow_add(lflows, op->od, put_stage, 120, > + ds_cstr(match), "put_fdb(inport, eth.src); next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 110, match: arp (i.e, arp.sha != 0) > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src); > + * REGBIT_LKUP_FDB_DL_INNER > + * = lookup_fdb(arp.sha); > + * Put: Prio: 110, match: arp && (REGBIT_LKUP_FDB == 0 > + * || REGBIT_LKUP_FDB_DL_INNER > == 0) > + * action: put_fdb(eth.src); put_fdb(arp.sha). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > + ds_put_cstr(match, " && arp"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER > + " = lookup_fdb(inport, arp.sha); next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 110, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " > + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); > + ovn_lflow_add(lflows, op->od, put_stage, 110, > + ds_cstr(match), "put_fdb(inport, eth.src); " > + "put_fdb(inport, arp.sha); " > + "next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 120, match: nd.tll == 0, > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src). > + * REGBIT_LKUP_FDB_DL_INNER = 1; > + * Put: Prio: 120, match: nd.tll == 0 && REGBIT_LKUP_FDB == 0, > + * action: put_fdb(eth.src). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > + ds_put_cstr(match, " && nd.tll == 00:00:00:00:00:00"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 120, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); > + ovn_lflow_add(lflows, op->od, put_stage, 120, > + ds_cstr(match), "put_fdb(inport, eth.src); next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 110, match: nd_na (i.e, nd_na.tll != 0) > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src); > + * REGBIT_LKUP_FDB_DL_INNER > + * = lookup_fdb(nd_na.tll); > + * Put: Prio: 110, match: nd_na > + * && (REGBIT_LKUP_FDB == 0 > + * || REGBIT_LKUP_FDB_DL_INNER > == 0) > + * action: put_fdb(eth.src); > put_fdb(nd_na.tll). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > + ds_put_cstr(match, " && nd_na"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER > + "= lookup_fdb(inport, nd.tll); next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 110, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " > + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); > + ovn_lflow_add(lflows, op->od, put_stage, 110, > + ds_cstr(match), "put_fdb(inport, eth.src); " > + "put_fdb(inport, nd.tll); " > + "next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 120, match: nd.sll == 0, > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src). > + * REGBIT_LKUP_FDB_DL_INNER = 1; > + * Put: Prio: 120, match: nd.sll == 0 && REGBIT_LKUP_FDB == 0, > + * action: put_fdb(eth.src). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > + ds_put_cstr(match, " && nd.sll == 00:00:00:00:00:00"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 120, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); > + ovn_lflow_add(lflows, op->od, put_stage, 120, > + ds_cstr(match), "put_fdb(inport, eth.src); next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 110, match: nd_ns (i.e, nd.sll != 0) > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src); > + * REGBIT_LKUP_FDB_DL_INNER > + * = lookup_fdb(nd.sll); > + * Put: Prio: 110, match: nd_ns > + * && (REGBIT_LKUP_FDB == 0 > + * || REGBIT_LKUP_FDB_DL_INNER > == 0) > + * action: put_fdb(eth.src); put_fdb(nd.sll). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > + ds_put_cstr(match, " && nd_ns"); > + ds_put_format(actions, > + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " > + REGBIT_LKUP_FDB_DL_INNER > + "= lookup_fdb(inport, nd.sll); next;"); > + ovn_lflow_add(lflows, op->od, lkup_stage, 110, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > + WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + > + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " > + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); > + ovn_lflow_add(lflows, op->od, put_stage, 110, > + ds_cstr(match), "put_fdb(inport, eth.src); " > + "put_fdb(inport, nd.sll); " > + "next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > + > + /* Lookup: Prio: 100, match: any eth packet, > + * action: REGBIT_LKUP_FDB = > lookup_fdb(eth.src). > + * Put: Prio: 100, match: any eth packet && REGBIT_LKUP_FDB == > 0, > + * action: put_fdb(eth.src). > + */ > + ds_truncate(match, match_len); > + ds_truncate(actions, actions_len); > ds_put_format(actions, REGBIT_LKUP_FDB > " = lookup_fdb(inport, eth.src); next;"); > - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_LOOKUP_FDB > - : S_SWITCH_IN_LOOKUP_FDB, > - 100, ds_cstr(match), ds_cstr(actions), > op->lflow_ref, > + ovn_lflow_add(lflows, op->od, lkup_stage, 100, > + ds_cstr(match), ds_cstr(actions), op->lflow_ref, > WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > > ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); > - ds_clear(actions); > - ds_put_cstr(actions, "put_fdb(inport, eth.src); next;"); > - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_PUT_FDB > - : S_SWITCH_IN_PUT_FDB, > - 100, ds_cstr(match), ds_cstr(actions), > op->lflow_ref, > - WITH_IO_PORT(op->key), > WITH_HINT(&op->nbsp->header_)); > + ovn_lflow_add(lflows, op->od, put_stage, 100, > + ds_cstr(match), "put_fdb(inport, eth.src); next;", > + op->lflow_ref, WITH_IO_PORT(op->key), > + WITH_HINT(&op->nbsp->header_)); > } > } > > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 003bbf4146..feea50a25f 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -1965,12 +1965,23 @@ > > <dt><code>unknown</code></dt> > <dd> > - This indicates that the logical port has an unknown set of > Ethernet > - addresses. When an OVN logical switch processes a unicast > Ethernet > - frame whose destination MAC address is not in any logical > port's > - <ref column="addresses"/> column, it delivers it to the port > (or > - ports) whose <ref column="addresses"/> columns include > - <code>unknown</code>. > + <p> > + This indicates that the logical port has an unknown set of > + Ethernet addresses. When an OVN logical switch processes a > + unicast Ethernet frame whose destination MAC address is not > in > + any logical port's <ref column="addresses"/> column, it > delivers > + it to the port (or ports) whose <ref column="addresses"/> > + columns include <code>unknown</code>. > + </p> > + <p> > + When <code>unknown</code> is present, FDB learning is > enabled > + for this port: source MACs are recorded via > <code>put_fdb</code> > + and destination MACs are resolved via <code>get_fdb</code>. > + For ARP packets, the sender hardware address is also > learned. > + For IPv6 Neighbor Advertisements, the target link-layer > address > + is learned; for Neighbor Solicitations, the source > link-layer > + address is learned. > + </p> > </dd> > > <dt><code>dynamic</code></dt> > diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at > index 638d96f795..67f2b1a65f 100644 > --- a/tests/ovn-ic.at > +++ b/tests/ovn-ic.at > @@ -4038,20 +4038,92 @@ dnl Check that FDB learning is enabled for remote > ports in the egress pipeline. > AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az1/sbflows | > ovn_strip_lflows], [0], [dnl > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > ]) > AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az2/sbflows | > ovn_strip_lflows], [0], [dnl > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > ]) > AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az3/sbflows | > ovn_strip_lflows], [0], [dnl > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == > "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > table=??(ls_out_put_fdb ), priority=100 , match=(inport == > "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_out_put_fdb ), priority=110 , match=(inport == > "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_out_put_fdb ), priority=120 , match=(inport == > "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > ]) > > check ovn-ic-nbctl --wait=sb sync > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index f8c1449182..11d181e6bb 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -8204,7 +8204,19 @@ AT_CAPTURE_FILE([S1flows]) > AT_CHECK([grep -e "ls_in_l2_lkup.*S1-vm1" S1flows | grep -e > "match=(eth.dst == 50:54:00:00:00:01)"], [1], []) > AT_CHECK([grep -e "ls_in_.*_fdb.*S1-vm1" S1flows | ovn_strip_lflows], > [0], [dnl > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "S1-vm1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "S1-vm1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "S1-vm1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "S1-vm1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "S1-vm1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "S1-vm1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "S1-vm1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == > "S1-vm1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "S1-vm1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "S1-vm1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "S1-vm1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "S1-vm1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "S1-vm1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "S1-vm1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > ]) > > #Verify the flows for a non-default port type (localnet port) > @@ -8217,6 +8229,46 @@ AT_CLEANUP > ]) > > > +OVN_FOR_EACH_NORTHD_NO_HV([ > +AT_SETUP([FDB learning includes ARP/ND inner MAC addresses]) > +AT_KEYWORDS([ovn fdb arp nd]) > +ovn_start > + > +check ovn-nbctl ls-add ls0 > +check ovn-nbctl lsp-add ls0 p1 > +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown > +check ovn-nbctl --wait=sb sync > + > +ovn-sbctl dump-flows ls0 > ls0flows > +AT_CAPTURE_FILE([ls0flows]) > + > +dnl Standard lookup/put flows at priority 100 for eth.src. > +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep -q > "priority=100"]) > +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep -q > "priority=100"]) > + > +dnl ARP-specific lookup flow at priority 110 with lookup_fdb for > +dnl both eth.src and arp.sha. > +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep > "priority=110" | grep -q "arp.sha"]) > + > +dnl ARP-specific put_fdb flow at priority 110 that learns both > +dnl eth.src and arp.sha, guarded by the OR of both regbits. > +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep > "priority=110" | grep "arp.sha" | grep -q "reg0\[[22\]]"]) > + > +dnl ND NA lookup and put flows at priority 110 for nd.tll. > +dnl The flows must include a guard against all-zero nd.tll (no TLLAO). > +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep > "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) > +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep > "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) > + > +dnl ND NS lookup and put flows at priority 110 for nd.sll. > +dnl The flows must include a guard against all-zero nd.sll (no SLLAO). > +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep > "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) > +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep > "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) > + > +OVN_CLEANUP_NORTHD > +AT_CLEANUP > +]) > + > + > OVN_FOR_EACH_NORTHD_NO_HV([ > AT_SETUP([check options:pkt_clone_type for LSP]) > ovn_start > @@ -8288,7 +8340,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" > S1flows | ovn_strip_lflows], [0], > dnl Check that FDB learning is enabled for the switch port. > AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = > lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" > && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > ]) > > ovn-sbctl dump-flows S2 > S2flows > @@ -8305,7 +8369,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S2-|unknown" > S2flows | ovn_strip_lflows], [0], > dnl Check that FDB learning is enabled for the switch port. > AT_CHECK([grep -E "ls_.*fdb.*S2-" S2flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "S2-S1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" > && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = > lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" > && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" > && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" > && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" > && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" > && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S2-S1" > && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" > && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" > && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" > && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" > && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" > && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" > && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > ]) > > dnl Add an explicit address to S1-S2 indicating that the port with > @@ -8326,7 +8402,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" > S1flows2 | ovn_strip_lflows], [0] > dnl Check that FDB learning is still enabled for the switch port. > AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = > lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= > lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" > && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" > && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, > eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" > && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > ]) > > OVN_CLEANUP_NORTHD > @@ -10681,9 +10769,21 @@ AT_CHECK([ovn-nbctl --wait=sb lsp-set-options > ln_port localnet_learn_fdb=true]) > AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' > | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "ln_port"), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, > eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ln_port" && arp), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); > next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ln_port" && nd_na), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ln_port" && nd_ns), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ln_port" && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; > reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ln_port" && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; > reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ln_port" && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; > reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == > "ln_port"), action=(flags.localnet = 1; next;) > table=??(ls_in_put_fdb ), priority=0 , match=(1), action=(next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == > "ln_port" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ln_port" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ln_port" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ln_port" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ln_port" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ln_port" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ln_port" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > ]) > > # Disable 'localnet_learn_fdb' and check the flows > @@ -11104,6 +11204,12 @@ dnl priority 50 fallback. > AT_CHECK([ovn-sbctl dump-flows ls1 | grep -e 'ls_in_lookup_fdb' | > ovn_strip_lflows], [0], [dnl > table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "ln1"), > action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" > && arp), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" > && nd_na), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" > && nd_ns), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, > eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" > && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" > && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" > && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == "ln1"), > action=(flags.localnet = 1; next;) > ]) > > @@ -23715,9 +23821,21 @@ AT_CAPTURE_FILE([ls1flows]) > > AT_CHECK([grep "ls1-to-spine" ls1flows | ovn_strip_lflows | sort], [0], > [dnl > table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == > "ls1-to-spine"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]] = lookup_fdb(inport, arp.sha); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); > reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == > "ls1-to-spine"), action=(next;) > table=??(ls_in_pre_lb ), priority=110 , match=(ip && inport == > "ls1-to-spine"), action=(next;) > table=??(ls_in_put_fdb ), priority=100 , match=(inport == > "ls1-to-spine" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , match=(inport == > "ls1-to-spine" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), > action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , match=(inport == > "ls1-to-spine" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), > action=(put_fdb(inport, eth.src); next;) > table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == > "ls1-to-spine"), action=(next;) > table=??(ls_out_pre_lb ), priority=110 , match=(ip && outport == > "ls1-to-spine"), action=(next;) > ]) > diff --git a/tests/ovn.at b/tests/ovn.at > index 136b825f98..e095e50bff 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -34090,6 +34090,30 @@ match=(1), action=(next;) > match=(inport == "sw0-p1"), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); next;) > table=??(ls_in_lookup_fdb ), priority=100 , dnl > match=(inport == "sw0-p3"), action=(reg0[[11]] = lookup_fdb(inport, > eth.src); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && arp), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); > next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && nd_na), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && nd_ns), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && arp), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); > next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && nd_na), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) > + table=??(ls_in_lookup_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && nd_ns), action=(reg0[[11]] = > lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > + table=??(ls_in_lookup_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00), > action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) > ]) > > AT_CHECK([grep "ls_in_put_fdb" sw0flows | ovn_strip_lflows], [0], [dnl > @@ -34099,6 +34123,30 @@ match=(1), action=(next;) > match=(inport == "sw0-p1" && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > table=??(ls_in_put_fdb ), priority=100 , dnl > match=(inport == "sw0-p3" && reg0[[11]] == 0), action=(put_fdb(inport, > eth.src); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && arp && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && arp && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) > + table=??(ls_in_put_fdb ), priority=110 , dnl > +match=(inport == "sw0-p3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == > 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] > == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == > 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == > 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] > == 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == > 0), action=(put_fdb(inport, eth.src); next;) > + table=??(ls_in_put_fdb ), priority=120 , dnl > +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == > 0), action=(put_fdb(inport, eth.src); next;) > ]) > > # Send a packet from sw0-p1 with a different mac not present > @@ -34440,6 +34488,88 @@ OVN_CLEANUP([hv1], [hv2], [hv3]) > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn -- FDB learning from ARP sender hardware address]) > Let's also extend this with ND? Just to make sure the flows work for IPv6 too. +AT_KEYWORDS([fdb arp]) > +CHECK_SCAPY > +ovn_start > + > +dnl Three ports on a logical switch. p1 and p2 have "unknown" to enable > +dnl FDB learning. pr is a regular client. > +check ovn-nbctl ls-add ls0 > +check ovn-nbctl lsp-add ls0 p1 > +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown > +check ovn-nbctl lsp-add ls0 p2 > +check ovn-nbctl lsp-set-addresses p2 "fa:16:3e:00:00:02 10.0.0.2" unknown > +check ovn-nbctl lsp-add ls0 p3 > +check ovn-nbctl lsp-set-addresses p3 "fa:16:3e:00:00:03 10.0.0.3" > + > +net_add n1 > +sim_add hv1 > +as hv1 > +check ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > + > +check ovs-vsctl -- add-port br-int vif1 -- \ > + set interface vif1 external-ids:iface-id=p1 \ > + options:tx_pcap=hv1/vif1-tx.pcap \ > + options:rxq_pcap=hv1/vif1-rx.pcap > +check ovs-vsctl -- add-port br-int vif2 -- \ > + set interface vif2 external-ids:iface-id=p2 \ > + options:tx_pcap=hv1/vif2-tx.pcap \ > + options:rxq_pcap=hv1/vif2-rx.pcap > +check ovs-vsctl -- add-port br-int vif3 -- \ > + set interface vif3 external-ids:iface-id=p3 \ > + options:tx_pcap=hv1/vif3-tx.pcap \ > + options:rxq_pcap=hv1/vif3-rx.pcap > + > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > + > +dnl p1 sends a GARP with eth.src=phys_mac, arp.sha=other_mac. > +dnl where phys_mac=fa:16:3e:00:00:01 and other_mac=fa:16:3e:aa:bb:cc > +dnl Both MACs should be learned into the FDB for p1. > + > +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='fa:16:3e:00:00:01')/ \ > + ARP(op=1, hwsrc='fa:16:3e:aa:bb:cc', psrc='10.0.0.100', \ > + hwdst='00:00:00:00:00:00', pdst='10.0.0.100')") > +as hv1 ovs-appctl netdev-dummy/receive vif1 $garp > + > +dnl Wait for arp.sha (other_mac) to appear in the FDB table. > +OVS_WAIT_UNTIL([ovn-sbctl find fdb mac='"fa:16:3e:aa:bb:cc"' | grep -q > port_key]) > + > +dnl Verify both MACs are in the FDB. Each put_fdb generates a separate > +dnl packet-in with an independent random delay, so both need a wait. > +wait_row_count fdb 1 mac='"fa:16:3e:00:00:01"' > +wait_row_count fdb 1 mac='"fa:16:3e:aa:bb:cc"' > + > +dnl Send unicast traffic from p2 to other_mac. The FDB should > +dnl only direct it to p1. > +as hv1 reset_pcap_file vif1 hv1/vif1 > +as hv1 reset_pcap_file vif2 hv1/vif2 > + > +packet=$(fmt_pkt "Ether(dst='fa:16:3e:aa:bb:cc', > src='fa:16:3e:00:00:03')/ \ > + IP(src='10.0.0.3', dst='10.0.0.100')/ \ > + UDP(sport=12345, dport=5678)/ \ > + Raw(b'to_other_mac')") > +echo $packet > p1.expected > +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet > + > +packet=$(fmt_pkt "Ether(dst='fa:16:3e:00:00:01', > src='fa:16:3e:00:00:03')/ \ > + IP(src='10.0.0.3', dst='10.0.0.101')/ \ > + UDP(sport=12345, dport=5678)/ \ > + Raw(b'to_other_mac')") > +echo $packet >> p1.expected > +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet > + > +> p2.expected > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [p1.expected]) > +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [p2.expected]) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > +]) > + > OVN_FOR_EACH_NORTHD([ > AT_SETUP([container port changed to normal port and then deleted]) > ovn_start > @@ -41116,12 +41246,12 @@ sleep_sb > sleep_controller hv2 > > AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 14 -> 24]) > -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', > src='00:00:00:00:10:14')/ARP(op=1, pdst='192.168.10.24', > psrc='192.168.10.14')") > +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', > src='00:00:00:00:10:14')/ARP(op=1, hwsrc='00:00:00:00:10:14', > pdst='192.168.10.24', psrc='192.168.10.14')") > as hv1 ovs-appctl netdev-dummy/receive vif14 $arp_req > ensure_controller_run hv1 > > AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 24 -> 14]) > -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', > src='00:00:00:00:10:24')/ARP(op=2, pdst='192.168.10.14', > psrc='192.168.10.24')") > +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', > src='00:00:00:00:10:24')/ARP(op=2, hwsrc='00:00:00:00:10:24', > hwdst='00:00:00:00:10:14', pdst='192.168.10.14', psrc='192.168.10.24')") > as hv2 ovs-appctl netdev-dummy/receive vif24 $arp_req > sleep 1 > > @@ -41149,12 +41279,12 @@ sleep_sb > sleep_controller hv2 > > AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 15 -> 25]) > -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', > src='00:00:00:00:10:15')/ARP(op=1, pdst='192.168.10.25', > psrc='192.168.10.15')") > +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', > src='00:00:00:00:10:15')/ARP(op=1, hwsrc='00:00:00:00:10:15', > pdst='192.168.10.25', psrc='192.168.10.15')") > as hv1 ovs-appctl netdev-dummy/receive vif15 $arp_req > ensure_controller_run hv1 > > AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 25 -> 15]) > -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', > src='00:00:00:00:10:25')/ARP(op=2, pdst='192.168.10.15', > psrc='192.168.10.25')") > +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', > src='00:00:00:00:10:25')/ARP(op=2, hwsrc='00:00:00:00:10:25', > hwdst='00:00:00:00:10:15', pdst='192.168.10.15', psrc='192.168.10.25')") > as hv2 ovs-appctl netdev-dummy/receive vif25 $arp_req > > sleep 1 > @@ -41178,7 +41308,10 @@ expected_flow_count=$(($expected_flow_count + 4)) > check_flow_count hv1 $expected_flow_count > check_flow_count hv2 $expected_flow_count > > -AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc > -l`]) > +# 5 original packet-ins + 3 extra for arp.sha learning > +# (one per ARP sent from hv1: vif14->vif24, vif15->vif25, > +# vif99->unknown). > +AT_CHECK([test 8 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc > -l`]) > # Do not check number of PACKET_IN2 on hv2, as vif14 => 99 as well as > vif15 => vif25 > # might have caused PACKET_IN2, but without any guarantee. > # e.g. (1) packet => hv1 ovs, (2) PACKET_IN2 => hv1 ovn, (3) FDB => sb, > @@ -41190,7 +41323,7 @@ AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | > grep NXT_PACKET_IN2 | wc -l`]) > # hv1 is expected to be stable. > > AS_BOX([$(date +%H:%M:%S.%03N) Packet from external]) > -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', > src='00:00:00:00:10:40')/ARP(op=2, pdst='192.168.10.50', > psrc='192.168.10.40')") > +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', > src='00:00:00:00:10:40')/ARP(op=2, hwsrc='00:00:00:00:10:40', > hwdst='00:00:00:00:10:50', pdst='192.168.10.50', psrc='192.168.10.40')") > as hv2 ovs-appctl netdev-dummy/receive ext0 $arp_req > wait_column "$ln_port_key" fdb port_key mac='"00:00:00:00:10:40"' > > -- > 2.55.0 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > Aren't we missing an update to ovn-logical-flows.7.rst? Regards, Ales
On 10/5/26 10:30 AM, Ales Musil wrote: > On Fri, Oct 2, 2026 at 7:31 PM Dumitru Ceara via dev < > ovs-dev@openvswitch.org> wrote: > >> Currently, build_lswitch_learn_fdb_op() only generates >> put_fdb(inport, eth.src) flows, so only eth.src is ever learned >> into the FDB. In VRRP gratuitous ARP packets, eth.src is the >> physical MAC while arp.sha is the virtual MAC (VMAC). Since >> put_fdb only records eth.src, the VMAC is never learned and >> traffic destined to it falls through to MC_UNKNOWN flooding. >> >> The same gap exists for IPv6 Neighbor Discovery: unsolicited >> NAs carry the VMAC in nd.tll, and NSes carry it in nd.sll. >> >> Add higher-priority (110) flows in both the lookup_fdb and >> put_fdb stages for ARP, ND NA, and ND NS packets. The lookup >> flows check both eth.src and the inner MAC field (arp.sha, >> nd.tll, nd.sll) and store the inner result in a new regbit >> REGBIT_LKUP_FDB_DL_INNER (reg0[22]). The put flows fire when >> either MAC is unknown and learn both via two put_fdb calls. >> When both MACs are already known, no put_fdb flow matches and >> no packet-in is generated. >> >> ARP/ND packets may lack the arp.sha/SLL/TLL, in which case >> arp.sha/nd.sll/nd.tll is all-zeros. Higher priority (120) >> flows for ARP/ND flows prevent learning a zero MAC into the >> FDB. >> > > Hi Dumitru, > Hi Ales, > thank you for the patch. Few small comments down below. > Thanks for the review! > >> >> Reported-at: https://redhat.atlassian.net/browse/FDP-4286 > > > nit: This should only be in the 2/2 patch. > TBH I chose myself to add it there, it wasn't AI. :) But I can remove it. > >> >> Assisted-by: Claude Opus 4.6, opencode >> Signed-off-by: Dumitru Ceara <dceara@redhat.com> >> --- >> NEWS | 5 ++ >> northd/northd.c | 190 +++++++++++++++++++++++++++++++++++++++++--- >> ovn-nb.xml | 23 ++++-- >> tests/ovn-ic.at | 72 +++++++++++++++++ >> tests/ovn-northd.at | 118 +++++++++++++++++++++++++++ >> tests/ovn.at | 145 +++++++++++++++++++++++++++++++-- >> 6 files changed, 532 insertions(+), 21 deletions(-) >> >> diff --git a/NEWS b/NEWS >> index 7f94d0b14f..d025581c9a 100644 >> --- a/NEWS >> +++ b/NEWS >> @@ -1,5 +1,10 @@ >> Post v26.09.0 >> -------------- >> + - Learn ARP sender hardware address (arp.sha) and IPv6 Neighbor >> + Discovery link-layer addresses (nd.tll, nd.sll) into the FDB >> + table in addition to eth.src. This allows VRRP virtual MACs to >> + be learned from gratuitous ARP packets and unsolicited Neighbor >> + Advertisements. >> - Added a new "options:ttl" key on the NB DNS table to make the TTL of >> DNS replies from OVN's native DNS resolver configurable per row. >> - Removed implementations of the commit_ecmp_nh, chk_ecmp_nh, and >> diff --git a/northd/northd.c b/northd/northd.c >> index f37040b57f..d33b60cd76 100644 >> --- a/northd/northd.c >> +++ b/northd/northd.c >> @@ -142,6 +142,7 @@ static bool vxlan_mode; >> #define REGBIT_IP_FRAG "reg0[19]" >> #define REGBIT_ACL_PERSIST_ID "reg0[20]" >> #define REGBIT_ACL_HINT_ALLOW_PERSISTED "reg0[21]" >> +#define REGBIT_LKUP_FDB_DL_INNER "reg0[22]" >> >> /* Register definitions for switches and routers. */ >> >> @@ -6635,7 +6636,21 @@ build_lswitch_learn_fdb_op( >> return; >> } >> >> + /* For non-ARP, non-ND Ethernet packets lookup their eth.src and learn >> + * it if not already known. >> + * >> + * For ARP packets, also look up arp.sha in the FDB. For ND packets, >> + * look up nd.tll (NA) or nd.sll (NS). They may differ from eth.src >> + * in specific cases. >> + * >> + * ND packets may lack the optional SLLAO/TLLAO option, in which case >> + * nd.sll/nd.tll is all-zeros so they need even more special >> attention. >> + */ >> bool remote = lsp_is_remote(op->nbsp); >> + const struct ovn_stage *lkup_stage = remote ? S_SWITCH_OUT_LOOKUP_FDB >> + : S_SWITCH_IN_LOOKUP_FDB; >> + const struct ovn_stage *put_stage = remote ? S_SWITCH_OUT_PUT_FDB >> + : S_SWITCH_IN_PUT_FDB; >> >> if (remote || (!strcmp(op->nbsp->type, "") && >> lsp_can_learn_mac(op->nbsp)) >> || lsp_is_switch(op->nbsp) >> @@ -6646,20 +6661,177 @@ build_lswitch_learn_fdb_op( >> if (lsp_is_localnet(op->nbsp)) { >> ds_put_cstr(actions, "flags.localnet = 1; "); >> } >> + size_t match_len = match->length; >> + size_t actions_len = actions->length; >> + >> + /* Lookup: Prio: 120, match: arp.sha == 0, >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src); >> + * REGBIT_LKUP_FDB_DL_INNER = 1; >> + * Put: Prio: 120, match: arp.sha == 0 && REGBIT_LKUP_FDB == 0, >> + * action: put_fdb(eth.src). >> + */ >> + ds_put_cstr(match, " && arp.sha == 00:00:00:00:00:00"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 120, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); >> + ovn_lflow_add(lflows, op->od, put_stage, 120, >> + ds_cstr(match), "put_fdb(inport, eth.src); next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 110, match: arp (i.e, arp.sha != 0) >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src); >> + * REGBIT_LKUP_FDB_DL_INNER >> + * = lookup_fdb(arp.sha); >> + * Put: Prio: 110, match: arp && (REGBIT_LKUP_FDB == 0 >> + * || REGBIT_LKUP_FDB_DL_INNER >> == 0) >> + * action: put_fdb(eth.src); put_fdb(arp.sha). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> + ds_put_cstr(match, " && arp"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER >> + " = lookup_fdb(inport, arp.sha); next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 110, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " >> + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); >> + ovn_lflow_add(lflows, op->od, put_stage, 110, >> + ds_cstr(match), "put_fdb(inport, eth.src); " >> + "put_fdb(inport, arp.sha); " >> + "next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 120, match: nd.tll == 0, >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src). >> + * REGBIT_LKUP_FDB_DL_INNER = 1; >> + * Put: Prio: 120, match: nd.tll == 0 && REGBIT_LKUP_FDB == 0, >> + * action: put_fdb(eth.src). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> + ds_put_cstr(match, " && nd.tll == 00:00:00:00:00:00"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 120, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); >> + ovn_lflow_add(lflows, op->od, put_stage, 120, >> + ds_cstr(match), "put_fdb(inport, eth.src); next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 110, match: nd_na (i.e, nd_na.tll != 0) >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src); >> + * REGBIT_LKUP_FDB_DL_INNER >> + * = lookup_fdb(nd_na.tll); >> + * Put: Prio: 110, match: nd_na >> + * && (REGBIT_LKUP_FDB == 0 >> + * || REGBIT_LKUP_FDB_DL_INNER >> == 0) >> + * action: put_fdb(eth.src); >> put_fdb(nd_na.tll). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> + ds_put_cstr(match, " && nd_na"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER >> + "= lookup_fdb(inport, nd.tll); next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 110, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " >> + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); >> + ovn_lflow_add(lflows, op->od, put_stage, 110, >> + ds_cstr(match), "put_fdb(inport, eth.src); " >> + "put_fdb(inport, nd.tll); " >> + "next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 120, match: nd.sll == 0, >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src). >> + * REGBIT_LKUP_FDB_DL_INNER = 1; >> + * Put: Prio: 120, match: nd.sll == 0 && REGBIT_LKUP_FDB == 0, >> + * action: put_fdb(eth.src). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> + ds_put_cstr(match, " && nd.sll == 00:00:00:00:00:00"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 120, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); >> + ovn_lflow_add(lflows, op->od, put_stage, 120, >> + ds_cstr(match), "put_fdb(inport, eth.src); next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 110, match: nd_ns (i.e, nd.sll != 0) >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src); >> + * REGBIT_LKUP_FDB_DL_INNER >> + * = lookup_fdb(nd.sll); >> + * Put: Prio: 110, match: nd_ns >> + * && (REGBIT_LKUP_FDB == 0 >> + * || REGBIT_LKUP_FDB_DL_INNER >> == 0) >> + * action: put_fdb(eth.src); put_fdb(nd.sll). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> + ds_put_cstr(match, " && nd_ns"); >> + ds_put_format(actions, >> + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " >> + REGBIT_LKUP_FDB_DL_INNER >> + "= lookup_fdb(inport, nd.sll); next;"); >> + ovn_lflow_add(lflows, op->od, lkup_stage, 110, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> + WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + >> + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " >> + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); >> + ovn_lflow_add(lflows, op->od, put_stage, 110, >> + ds_cstr(match), "put_fdb(inport, eth.src); " >> + "put_fdb(inport, nd.sll); " >> + "next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> + >> + /* Lookup: Prio: 100, match: any eth packet, >> + * action: REGBIT_LKUP_FDB = >> lookup_fdb(eth.src). >> + * Put: Prio: 100, match: any eth packet && REGBIT_LKUP_FDB == >> 0, >> + * action: put_fdb(eth.src). >> + */ >> + ds_truncate(match, match_len); >> + ds_truncate(actions, actions_len); >> ds_put_format(actions, REGBIT_LKUP_FDB >> " = lookup_fdb(inport, eth.src); next;"); >> - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_LOOKUP_FDB >> - : S_SWITCH_IN_LOOKUP_FDB, >> - 100, ds_cstr(match), ds_cstr(actions), >> op->lflow_ref, >> + ovn_lflow_add(lflows, op->od, lkup_stage, 100, >> + ds_cstr(match), ds_cstr(actions), op->lflow_ref, >> WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> >> ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); >> - ds_clear(actions); >> - ds_put_cstr(actions, "put_fdb(inport, eth.src); next;"); >> - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_PUT_FDB >> - : S_SWITCH_IN_PUT_FDB, >> - 100, ds_cstr(match), ds_cstr(actions), >> op->lflow_ref, >> - WITH_IO_PORT(op->key), >> WITH_HINT(&op->nbsp->header_)); >> + ovn_lflow_add(lflows, op->od, put_stage, 100, >> + ds_cstr(match), "put_fdb(inport, eth.src); next;", >> + op->lflow_ref, WITH_IO_PORT(op->key), >> + WITH_HINT(&op->nbsp->header_)); >> } >> } >> >> diff --git a/ovn-nb.xml b/ovn-nb.xml >> index 003bbf4146..feea50a25f 100644 >> --- a/ovn-nb.xml >> +++ b/ovn-nb.xml >> @@ -1965,12 +1965,23 @@ >> >> <dt><code>unknown</code></dt> >> <dd> >> - This indicates that the logical port has an unknown set of >> Ethernet >> - addresses. When an OVN logical switch processes a unicast >> Ethernet >> - frame whose destination MAC address is not in any logical >> port's >> - <ref column="addresses"/> column, it delivers it to the port >> (or >> - ports) whose <ref column="addresses"/> columns include >> - <code>unknown</code>. >> + <p> >> + This indicates that the logical port has an unknown set of >> + Ethernet addresses. When an OVN logical switch processes a >> + unicast Ethernet frame whose destination MAC address is not >> in >> + any logical port's <ref column="addresses"/> column, it >> delivers >> + it to the port (or ports) whose <ref column="addresses"/> >> + columns include <code>unknown</code>. >> + </p> >> + <p> >> + When <code>unknown</code> is present, FDB learning is >> enabled >> + for this port: source MACs are recorded via >> <code>put_fdb</code> >> + and destination MACs are resolved via <code>get_fdb</code>. >> + For ARP packets, the sender hardware address is also >> learned. >> + For IPv6 Neighbor Advertisements, the target link-layer >> address >> + is learned; for Neighbor Solicitations, the source >> link-layer >> + address is learned. >> + </p> >> </dd> >> >> <dt><code>dynamic</code></dt> >> diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at >> index 638d96f795..67f2b1a65f 100644 >> --- a/tests/ovn-ic.at >> +++ b/tests/ovn-ic.at >> @@ -4038,20 +4038,92 @@ dnl Check that FDB learning is enabled for remote >> ports in the egress pipeline. >> AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az1/sbflows | >> ovn_strip_lflows], [0], [dnl >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> ]) >> AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az2/sbflows | >> ovn_strip_lflows], [0], [dnl >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> ]) >> AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az3/sbflows | >> ovn_strip_lflows], [0], [dnl >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == >> "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> table=??(ls_out_put_fdb ), priority=100 , match=(inport == >> "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_out_put_fdb ), priority=110 , match=(inport == >> "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_out_put_fdb ), priority=120 , match=(inport == >> "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> ]) >> >> check ovn-ic-nbctl --wait=sb sync >> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at >> index f8c1449182..11d181e6bb 100644 >> --- a/tests/ovn-northd.at >> +++ b/tests/ovn-northd.at >> @@ -8204,7 +8204,19 @@ AT_CAPTURE_FILE([S1flows]) >> AT_CHECK([grep -e "ls_in_l2_lkup.*S1-vm1" S1flows | grep -e >> "match=(eth.dst == 50:54:00:00:00:01)"], [1], []) >> AT_CHECK([grep -e "ls_in_.*_fdb.*S1-vm1" S1flows | ovn_strip_lflows], >> [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "S1-vm1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "S1-vm1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "S1-vm1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "S1-vm1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "S1-vm1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "S1-vm1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "S1-vm1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == >> "S1-vm1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "S1-vm1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "S1-vm1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "S1-vm1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "S1-vm1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "S1-vm1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "S1-vm1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> ]) >> >> #Verify the flows for a non-default port type (localnet port) >> @@ -8217,6 +8229,46 @@ AT_CLEANUP >> ]) >> >> >> +OVN_FOR_EACH_NORTHD_NO_HV([ >> +AT_SETUP([FDB learning includes ARP/ND inner MAC addresses]) >> +AT_KEYWORDS([ovn fdb arp nd]) >> +ovn_start >> + >> +check ovn-nbctl ls-add ls0 >> +check ovn-nbctl lsp-add ls0 p1 >> +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown >> +check ovn-nbctl --wait=sb sync >> + >> +ovn-sbctl dump-flows ls0 > ls0flows >> +AT_CAPTURE_FILE([ls0flows]) >> + >> +dnl Standard lookup/put flows at priority 100 for eth.src. >> +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep -q >> "priority=100"]) >> +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep -q >> "priority=100"]) >> + >> +dnl ARP-specific lookup flow at priority 110 with lookup_fdb for >> +dnl both eth.src and arp.sha. >> +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep >> "priority=110" | grep -q "arp.sha"]) >> + >> +dnl ARP-specific put_fdb flow at priority 110 that learns both >> +dnl eth.src and arp.sha, guarded by the OR of both regbits. >> +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep >> "priority=110" | grep "arp.sha" | grep -q "reg0\[[22\]]"]) >> + >> +dnl ND NA lookup and put flows at priority 110 for nd.tll. >> +dnl The flows must include a guard against all-zero nd.tll (no TLLAO). >> +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep >> "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) >> +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep >> "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) >> + >> +dnl ND NS lookup and put flows at priority 110 for nd.sll. >> +dnl The flows must include a guard against all-zero nd.sll (no SLLAO). >> +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep >> "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) >> +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep >> "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) >> + >> +OVN_CLEANUP_NORTHD >> +AT_CLEANUP >> +]) >> + >> + >> OVN_FOR_EACH_NORTHD_NO_HV([ >> AT_SETUP([check options:pkt_clone_type for LSP]) >> ovn_start >> @@ -8288,7 +8340,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" >> S1flows | ovn_strip_lflows], [0], >> dnl Check that FDB learning is enabled for the switch port. >> AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = >> lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" >> && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> ]) >> >> ovn-sbctl dump-flows S2 > S2flows >> @@ -8305,7 +8369,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S2-|unknown" >> S2flows | ovn_strip_lflows], [0], >> dnl Check that FDB learning is enabled for the switch port. >> AT_CHECK([grep -E "ls_.*fdb.*S2-" S2flows | ovn_strip_lflows], [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "S2-S1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" >> && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = >> lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" >> && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" >> && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" >> && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" >> && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" >> && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S2-S1" >> && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" >> && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" >> && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" >> && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" >> && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" >> && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" >> && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> ]) >> >> dnl Add an explicit address to S1-S2 indicating that the port with >> @@ -8326,7 +8402,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" >> S1flows2 | ovn_strip_lflows], [0] >> dnl Check that FDB learning is still enabled for the switch port. >> AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = >> lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= >> lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" >> && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" >> && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, >> eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" >> && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> ]) >> >> OVN_CLEANUP_NORTHD >> @@ -10681,9 +10769,21 @@ AT_CHECK([ovn-nbctl --wait=sb lsp-set-options >> ln_port localnet_learn_fdb=true]) >> AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' >> | ovn_strip_lflows], [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "ln_port"), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ln_port" && arp), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); >> next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ln_port" && nd_na), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ln_port" && nd_ns), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ln_port" && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; >> reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ln_port" && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; >> reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ln_port" && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; >> reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == >> "ln_port"), action=(flags.localnet = 1; next;) >> table=??(ls_in_put_fdb ), priority=0 , match=(1), action=(next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == >> "ln_port" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ln_port" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ln_port" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ln_port" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ln_port" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ln_port" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ln_port" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> ]) >> >> # Disable 'localnet_learn_fdb' and check the flows >> @@ -11104,6 +11204,12 @@ dnl priority 50 fallback. >> AT_CHECK([ovn-sbctl dump-flows ls1 | grep -e 'ls_in_lookup_fdb' | >> ovn_strip_lflows], [0], [dnl >> table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "ln1"), >> action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" >> && arp), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" >> && nd_na), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" >> && nd_ns), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, >> eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" >> && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" >> && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" >> && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == "ln1"), >> action=(flags.localnet = 1; next;) >> ]) >> >> @@ -23715,9 +23821,21 @@ AT_CAPTURE_FILE([ls1flows]) >> >> AT_CHECK([grep "ls1-to-spine" ls1flows | ovn_strip_lflows | sort], [0], >> [dnl >> table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == >> "ls1-to-spine"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]] = lookup_fdb(inport, arp.sha); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); >> reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == >> "ls1-to-spine"), action=(next;) >> table=??(ls_in_pre_lb ), priority=110 , match=(ip && inport == >> "ls1-to-spine"), action=(next;) >> table=??(ls_in_put_fdb ), priority=100 , match=(inport == >> "ls1-to-spine" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , match=(inport == >> "ls1-to-spine" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), >> action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , match=(inport == >> "ls1-to-spine" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), >> action=(put_fdb(inport, eth.src); next;) >> table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == >> "ls1-to-spine"), action=(next;) >> table=??(ls_out_pre_lb ), priority=110 , match=(ip && outport == >> "ls1-to-spine"), action=(next;) >> ]) >> diff --git a/tests/ovn.at b/tests/ovn.at >> index 136b825f98..e095e50bff 100644 >> --- a/tests/ovn.at >> +++ b/tests/ovn.at >> @@ -34090,6 +34090,30 @@ match=(1), action=(next;) >> match=(inport == "sw0-p1"), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); next;) >> table=??(ls_in_lookup_fdb ), priority=100 , dnl >> match=(inport == "sw0-p3"), action=(reg0[[11]] = lookup_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && arp), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); >> next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && nd_na), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && nd_ns), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && arp), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); >> next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && nd_na), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) >> + table=??(ls_in_lookup_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && nd_ns), action=(reg0[[11]] = >> lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> + table=??(ls_in_lookup_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00), >> action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) >> ]) >> >> AT_CHECK([grep "ls_in_put_fdb" sw0flows | ovn_strip_lflows], [0], [dnl >> @@ -34099,6 +34123,30 @@ match=(1), action=(next;) >> match=(inport == "sw0-p1" && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> table=??(ls_in_put_fdb ), priority=100 , dnl >> match=(inport == "sw0-p3" && reg0[[11]] == 0), action=(put_fdb(inport, >> eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && arp && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && arp && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) >> + table=??(ls_in_put_fdb ), priority=110 , dnl >> +match=(inport == "sw0-p3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == >> 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] >> == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == >> 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == >> 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] >> == 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == >> 0), action=(put_fdb(inport, eth.src); next;) >> + table=??(ls_in_put_fdb ), priority=120 , dnl >> +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == >> 0), action=(put_fdb(inport, eth.src); next;) >> ]) >> >> # Send a packet from sw0-p1 with a different mac not present >> @@ -34440,6 +34488,88 @@ OVN_CLEANUP([hv1], [hv2], [hv3]) >> AT_CLEANUP >> ]) >> >> +OVN_FOR_EACH_NORTHD([ >> +AT_SETUP([ovn -- FDB learning from ARP sender hardware address]) >> > > Let's also extend this with ND? Just to make sure the > flows work for IPv6 too. > OK, I'll do that. > +AT_KEYWORDS([fdb arp]) >> +CHECK_SCAPY >> +ovn_start >> + >> +dnl Three ports on a logical switch. p1 and p2 have "unknown" to enable >> +dnl FDB learning. pr is a regular client. >> +check ovn-nbctl ls-add ls0 >> +check ovn-nbctl lsp-add ls0 p1 >> +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown >> +check ovn-nbctl lsp-add ls0 p2 >> +check ovn-nbctl lsp-set-addresses p2 "fa:16:3e:00:00:02 10.0.0.2" unknown >> +check ovn-nbctl lsp-add ls0 p3 >> +check ovn-nbctl lsp-set-addresses p3 "fa:16:3e:00:00:03 10.0.0.3" >> + >> +net_add n1 >> +sim_add hv1 >> +as hv1 >> +check ovs-vsctl add-br br-phys >> +ovn_attach n1 br-phys 192.168.0.1 >> + >> +check ovs-vsctl -- add-port br-int vif1 -- \ >> + set interface vif1 external-ids:iface-id=p1 \ >> + options:tx_pcap=hv1/vif1-tx.pcap \ >> + options:rxq_pcap=hv1/vif1-rx.pcap >> +check ovs-vsctl -- add-port br-int vif2 -- \ >> + set interface vif2 external-ids:iface-id=p2 \ >> + options:tx_pcap=hv1/vif2-tx.pcap \ >> + options:rxq_pcap=hv1/vif2-rx.pcap >> +check ovs-vsctl -- add-port br-int vif3 -- \ >> + set interface vif3 external-ids:iface-id=p3 \ >> + options:tx_pcap=hv1/vif3-tx.pcap \ >> + options:rxq_pcap=hv1/vif3-rx.pcap >> + >> +wait_for_ports_up >> +check ovn-nbctl --wait=hv sync >> + >> +dnl p1 sends a GARP with eth.src=phys_mac, arp.sha=other_mac. >> +dnl where phys_mac=fa:16:3e:00:00:01 and other_mac=fa:16:3e:aa:bb:cc >> +dnl Both MACs should be learned into the FDB for p1. >> + >> +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='fa:16:3e:00:00:01')/ \ >> + ARP(op=1, hwsrc='fa:16:3e:aa:bb:cc', psrc='10.0.0.100', \ >> + hwdst='00:00:00:00:00:00', pdst='10.0.0.100')") >> +as hv1 ovs-appctl netdev-dummy/receive vif1 $garp >> + >> +dnl Wait for arp.sha (other_mac) to appear in the FDB table. >> +OVS_WAIT_UNTIL([ovn-sbctl find fdb mac='"fa:16:3e:aa:bb:cc"' | grep -q >> port_key]) >> + >> +dnl Verify both MACs are in the FDB. Each put_fdb generates a separate >> +dnl packet-in with an independent random delay, so both need a wait. >> +wait_row_count fdb 1 mac='"fa:16:3e:00:00:01"' >> +wait_row_count fdb 1 mac='"fa:16:3e:aa:bb:cc"' >> + >> +dnl Send unicast traffic from p2 to other_mac. The FDB should >> +dnl only direct it to p1. >> +as hv1 reset_pcap_file vif1 hv1/vif1 >> +as hv1 reset_pcap_file vif2 hv1/vif2 >> + >> +packet=$(fmt_pkt "Ether(dst='fa:16:3e:aa:bb:cc', >> src='fa:16:3e:00:00:03')/ \ >> + IP(src='10.0.0.3', dst='10.0.0.100')/ \ >> + UDP(sport=12345, dport=5678)/ \ >> + Raw(b'to_other_mac')") >> +echo $packet > p1.expected >> +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet >> + >> +packet=$(fmt_pkt "Ether(dst='fa:16:3e:00:00:01', >> src='fa:16:3e:00:00:03')/ \ >> + IP(src='10.0.0.3', dst='10.0.0.101')/ \ >> + UDP(sport=12345, dport=5678)/ \ >> + Raw(b'to_other_mac')") >> +echo $packet >> p1.expected >> +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet >> + >> +> p2.expected >> +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [p1.expected]) >> +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [p2.expected]) >> + >> +OVN_CLEANUP([hv1]) >> +AT_CLEANUP >> +]) >> + >> OVN_FOR_EACH_NORTHD([ >> AT_SETUP([container port changed to normal port and then deleted]) >> ovn_start >> @@ -41116,12 +41246,12 @@ sleep_sb >> sleep_controller hv2 >> >> AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 14 -> 24]) >> -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', >> src='00:00:00:00:10:14')/ARP(op=1, pdst='192.168.10.24', >> psrc='192.168.10.14')") >> +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', >> src='00:00:00:00:10:14')/ARP(op=1, hwsrc='00:00:00:00:10:14', >> pdst='192.168.10.24', psrc='192.168.10.14')") >> as hv1 ovs-appctl netdev-dummy/receive vif14 $arp_req >> ensure_controller_run hv1 >> >> AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 24 -> 14]) >> -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', >> src='00:00:00:00:10:24')/ARP(op=2, pdst='192.168.10.14', >> psrc='192.168.10.24')") >> +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', >> src='00:00:00:00:10:24')/ARP(op=2, hwsrc='00:00:00:00:10:24', >> hwdst='00:00:00:00:10:14', pdst='192.168.10.14', psrc='192.168.10.24')") >> as hv2 ovs-appctl netdev-dummy/receive vif24 $arp_req >> sleep 1 >> >> @@ -41149,12 +41279,12 @@ sleep_sb >> sleep_controller hv2 >> >> AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 15 -> 25]) >> -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', >> src='00:00:00:00:10:15')/ARP(op=1, pdst='192.168.10.25', >> psrc='192.168.10.15')") >> +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', >> src='00:00:00:00:10:15')/ARP(op=1, hwsrc='00:00:00:00:10:15', >> pdst='192.168.10.25', psrc='192.168.10.15')") >> as hv1 ovs-appctl netdev-dummy/receive vif15 $arp_req >> ensure_controller_run hv1 >> >> AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 25 -> 15]) >> -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', >> src='00:00:00:00:10:25')/ARP(op=2, pdst='192.168.10.15', >> psrc='192.168.10.25')") >> +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', >> src='00:00:00:00:10:25')/ARP(op=2, hwsrc='00:00:00:00:10:25', >> hwdst='00:00:00:00:10:15', pdst='192.168.10.15', psrc='192.168.10.25')") >> as hv2 ovs-appctl netdev-dummy/receive vif25 $arp_req >> >> sleep 1 >> @@ -41178,7 +41308,10 @@ expected_flow_count=$(($expected_flow_count + 4)) >> check_flow_count hv1 $expected_flow_count >> check_flow_count hv2 $expected_flow_count >> >> -AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc >> -l`]) >> +# 5 original packet-ins + 3 extra for arp.sha learning >> +# (one per ARP sent from hv1: vif14->vif24, vif15->vif25, >> +# vif99->unknown). >> +AT_CHECK([test 8 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc >> -l`]) >> # Do not check number of PACKET_IN2 on hv2, as vif14 => 99 as well as >> vif15 => vif25 >> # might have caused PACKET_IN2, but without any guarantee. >> # e.g. (1) packet => hv1 ovs, (2) PACKET_IN2 => hv1 ovn, (3) FDB => sb, >> @@ -41190,7 +41323,7 @@ AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | >> grep NXT_PACKET_IN2 | wc -l`]) >> # hv1 is expected to be stable. >> >> AS_BOX([$(date +%H:%M:%S.%03N) Packet from external]) >> -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', >> src='00:00:00:00:10:40')/ARP(op=2, pdst='192.168.10.50', >> psrc='192.168.10.40')") >> +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', >> src='00:00:00:00:10:40')/ARP(op=2, hwsrc='00:00:00:00:10:40', >> hwdst='00:00:00:00:10:50', pdst='192.168.10.50', psrc='192.168.10.40')") >> as hv2 ovs-appctl netdev-dummy/receive ext0 $arp_req >> wait_column "$ln_port_key" fdb port_key mac='"00:00:00:00:10:40"' >> >> -- >> 2.55.0 >> >> _______________________________________________ >> dev mailing list >> dev@openvswitch.org >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >> >> > Aren't we missing an update to ovn-logical-flows.7.rst? > We are, I still dislike having to do that TBH but I will. Thanks again for the review! Regards, Dumitru > Regards, > Ales >
diff --git a/NEWS b/NEWS index 7f94d0b14f..d025581c9a 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ Post v26.09.0 -------------- + - Learn ARP sender hardware address (arp.sha) and IPv6 Neighbor + Discovery link-layer addresses (nd.tll, nd.sll) into the FDB + table in addition to eth.src. This allows VRRP virtual MACs to + be learned from gratuitous ARP packets and unsolicited Neighbor + Advertisements. - Added a new "options:ttl" key on the NB DNS table to make the TTL of DNS replies from OVN's native DNS resolver configurable per row. - Removed implementations of the commit_ecmp_nh, chk_ecmp_nh, and diff --git a/northd/northd.c b/northd/northd.c index f37040b57f..d33b60cd76 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -142,6 +142,7 @@ static bool vxlan_mode; #define REGBIT_IP_FRAG "reg0[19]" #define REGBIT_ACL_PERSIST_ID "reg0[20]" #define REGBIT_ACL_HINT_ALLOW_PERSISTED "reg0[21]" +#define REGBIT_LKUP_FDB_DL_INNER "reg0[22]" /* Register definitions for switches and routers. */ @@ -6635,7 +6636,21 @@ build_lswitch_learn_fdb_op( return; } + /* For non-ARP, non-ND Ethernet packets lookup their eth.src and learn + * it if not already known. + * + * For ARP packets, also look up arp.sha in the FDB. For ND packets, + * look up nd.tll (NA) or nd.sll (NS). They may differ from eth.src + * in specific cases. + * + * ND packets may lack the optional SLLAO/TLLAO option, in which case + * nd.sll/nd.tll is all-zeros so they need even more special attention. + */ bool remote = lsp_is_remote(op->nbsp); + const struct ovn_stage *lkup_stage = remote ? S_SWITCH_OUT_LOOKUP_FDB + : S_SWITCH_IN_LOOKUP_FDB; + const struct ovn_stage *put_stage = remote ? S_SWITCH_OUT_PUT_FDB + : S_SWITCH_IN_PUT_FDB; if (remote || (!strcmp(op->nbsp->type, "") && lsp_can_learn_mac(op->nbsp)) || lsp_is_switch(op->nbsp) @@ -6646,20 +6661,177 @@ build_lswitch_learn_fdb_op( if (lsp_is_localnet(op->nbsp)) { ds_put_cstr(actions, "flags.localnet = 1; "); } + size_t match_len = match->length; + size_t actions_len = actions->length; + + /* Lookup: Prio: 120, match: arp.sha == 0, + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src); + * REGBIT_LKUP_FDB_DL_INNER = 1; + * Put: Prio: 120, match: arp.sha == 0 && REGBIT_LKUP_FDB == 0, + * action: put_fdb(eth.src). + */ + ds_put_cstr(match, " && arp.sha == 00:00:00:00:00:00"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 120, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); + ovn_lflow_add(lflows, op->od, put_stage, 120, + ds_cstr(match), "put_fdb(inport, eth.src); next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 110, match: arp (i.e, arp.sha != 0) + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src); + * REGBIT_LKUP_FDB_DL_INNER + * = lookup_fdb(arp.sha); + * Put: Prio: 110, match: arp && (REGBIT_LKUP_FDB == 0 + * || REGBIT_LKUP_FDB_DL_INNER == 0) + * action: put_fdb(eth.src); put_fdb(arp.sha). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); + ds_put_cstr(match, " && arp"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER + " = lookup_fdb(inport, arp.sha); next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 110, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); + ovn_lflow_add(lflows, op->od, put_stage, 110, + ds_cstr(match), "put_fdb(inport, eth.src); " + "put_fdb(inport, arp.sha); " + "next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 120, match: nd.tll == 0, + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src). + * REGBIT_LKUP_FDB_DL_INNER = 1; + * Put: Prio: 120, match: nd.tll == 0 && REGBIT_LKUP_FDB == 0, + * action: put_fdb(eth.src). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); + ds_put_cstr(match, " && nd.tll == 00:00:00:00:00:00"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 120, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); + ovn_lflow_add(lflows, op->od, put_stage, 120, + ds_cstr(match), "put_fdb(inport, eth.src); next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 110, match: nd_na (i.e, nd_na.tll != 0) + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src); + * REGBIT_LKUP_FDB_DL_INNER + * = lookup_fdb(nd_na.tll); + * Put: Prio: 110, match: nd_na + * && (REGBIT_LKUP_FDB == 0 + * || REGBIT_LKUP_FDB_DL_INNER == 0) + * action: put_fdb(eth.src); put_fdb(nd_na.tll). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); + ds_put_cstr(match, " && nd_na"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER + "= lookup_fdb(inport, nd.tll); next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 110, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); + ovn_lflow_add(lflows, op->od, put_stage, 110, + ds_cstr(match), "put_fdb(inport, eth.src); " + "put_fdb(inport, nd.tll); " + "next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 120, match: nd.sll == 0, + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src). + * REGBIT_LKUP_FDB_DL_INNER = 1; + * Put: Prio: 120, match: nd.sll == 0 && REGBIT_LKUP_FDB == 0, + * action: put_fdb(eth.src). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); + ds_put_cstr(match, " && nd.sll == 00:00:00:00:00:00"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER " = 1; next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 120, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); + ovn_lflow_add(lflows, op->od, put_stage, 120, + ds_cstr(match), "put_fdb(inport, eth.src); next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 110, match: nd_ns (i.e, nd.sll != 0) + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src); + * REGBIT_LKUP_FDB_DL_INNER + * = lookup_fdb(nd.sll); + * Put: Prio: 110, match: nd_ns + * && (REGBIT_LKUP_FDB == 0 + * || REGBIT_LKUP_FDB_DL_INNER == 0) + * action: put_fdb(eth.src); put_fdb(nd.sll). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); + ds_put_cstr(match, " && nd_ns"); + ds_put_format(actions, + REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); " + REGBIT_LKUP_FDB_DL_INNER + "= lookup_fdb(inport, nd.sll); next;"); + ovn_lflow_add(lflows, op->od, lkup_stage, 110, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, + WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + + ds_put_cstr(match, " && ("REGBIT_LKUP_FDB" == 0 " + "|| " REGBIT_LKUP_FDB_DL_INNER " == 0)"); + ovn_lflow_add(lflows, op->od, put_stage, 110, + ds_cstr(match), "put_fdb(inport, eth.src); " + "put_fdb(inport, nd.sll); " + "next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + /* Lookup: Prio: 100, match: any eth packet, + * action: REGBIT_LKUP_FDB = lookup_fdb(eth.src). + * Put: Prio: 100, match: any eth packet && REGBIT_LKUP_FDB == 0, + * action: put_fdb(eth.src). + */ + ds_truncate(match, match_len); + ds_truncate(actions, actions_len); ds_put_format(actions, REGBIT_LKUP_FDB " = lookup_fdb(inport, eth.src); next;"); - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_LOOKUP_FDB - : S_SWITCH_IN_LOOKUP_FDB, - 100, ds_cstr(match), ds_cstr(actions), op->lflow_ref, + ovn_lflow_add(lflows, op->od, lkup_stage, 100, + ds_cstr(match), ds_cstr(actions), op->lflow_ref, WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); ds_put_cstr(match, " && "REGBIT_LKUP_FDB" == 0"); - ds_clear(actions); - ds_put_cstr(actions, "put_fdb(inport, eth.src); next;"); - ovn_lflow_add(lflows, op->od, remote ? S_SWITCH_OUT_PUT_FDB - : S_SWITCH_IN_PUT_FDB, - 100, ds_cstr(match), ds_cstr(actions), op->lflow_ref, - WITH_IO_PORT(op->key), WITH_HINT(&op->nbsp->header_)); + ovn_lflow_add(lflows, op->od, put_stage, 100, + ds_cstr(match), "put_fdb(inport, eth.src); next;", + op->lflow_ref, WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); } } diff --git a/ovn-nb.xml b/ovn-nb.xml index 003bbf4146..feea50a25f 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1965,12 +1965,23 @@ <dt><code>unknown</code></dt> <dd> - This indicates that the logical port has an unknown set of Ethernet - addresses. When an OVN logical switch processes a unicast Ethernet - frame whose destination MAC address is not in any logical port's - <ref column="addresses"/> column, it delivers it to the port (or - ports) whose <ref column="addresses"/> columns include - <code>unknown</code>. + <p> + This indicates that the logical port has an unknown set of + Ethernet addresses. When an OVN logical switch processes a + unicast Ethernet frame whose destination MAC address is not in + any logical port's <ref column="addresses"/> column, it delivers + it to the port (or ports) whose <ref column="addresses"/> + columns include <code>unknown</code>. + </p> + <p> + When <code>unknown</code> is present, FDB learning is enabled + for this port: source MACs are recorded via <code>put_fdb</code> + and destination MACs are resolved via <code>get_fdb</code>. + For ARP packets, the sender hardware address is also learned. + For IPv6 Neighbor Advertisements, the target link-layer address + is learned; for Neighbor Solicitations, the source link-layer + address is learned. + </p> </dd> <dt><code>dynamic</code></dt> diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at index 638d96f795..67f2b1a65f 100644 --- a/tests/ovn-ic.at +++ b/tests/ovn-ic.at @@ -4038,20 +4038,92 @@ dnl Check that FDB learning is enabled for remote ports in the egress pipeline. AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az1/sbflows | ovn_strip_lflows], [0], [dnl table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az2/sbflows | ovn_strip_lflows], [0], [dnl table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) AT_CHECK([grep -E "ls_out.*fdb.*spine-to-" az3/sbflows | ovn_strip_lflows], [0], [dnl table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) table=??(ls_out_lookup_fdb ), priority=100 , match=(inport == "spine-to-ls2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_out_lookup_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_out_lookup_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) table=??(ls_out_put_fdb ), priority=100 , match=(inport == "spine-to-ls2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_out_put_fdb ), priority=110 , match=(inport == "spine-to-ls2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_out_put_fdb ), priority=120 , match=(inport == "spine-to-ls2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) check ovn-ic-nbctl --wait=sb sync diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index f8c1449182..11d181e6bb 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -8204,7 +8204,19 @@ AT_CAPTURE_FILE([S1flows]) AT_CHECK([grep -e "ls_in_l2_lkup.*S1-vm1" S1flows | grep -e "match=(eth.dst == 50:54:00:00:00:01)"], [1], []) AT_CHECK([grep -e "ls_in_.*_fdb.*S1-vm1" S1flows | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "S1-vm1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-vm1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-vm1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-vm1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-vm1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-vm1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-vm1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-vm1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-vm1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-vm1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-vm1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-vm1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-vm1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-vm1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) #Verify the flows for a non-default port type (localnet port) @@ -8217,6 +8229,46 @@ AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([FDB learning includes ARP/ND inner MAC addresses]) +AT_KEYWORDS([ovn fdb arp nd]) +ovn_start + +check ovn-nbctl ls-add ls0 +check ovn-nbctl lsp-add ls0 p1 +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown +check ovn-nbctl --wait=sb sync + +ovn-sbctl dump-flows ls0 > ls0flows +AT_CAPTURE_FILE([ls0flows]) + +dnl Standard lookup/put flows at priority 100 for eth.src. +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep -q "priority=100"]) +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep -q "priority=100"]) + +dnl ARP-specific lookup flow at priority 110 with lookup_fdb for +dnl both eth.src and arp.sha. +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep "priority=110" | grep -q "arp.sha"]) + +dnl ARP-specific put_fdb flow at priority 110 that learns both +dnl eth.src and arp.sha, guarded by the OR of both regbits. +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep "priority=110" | grep "arp.sha" | grep -q "reg0\[[22\]]"]) + +dnl ND NA lookup and put flows at priority 110 for nd.tll. +dnl The flows must include a guard against all-zero nd.tll (no TLLAO). +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep "priority=120" | grep -q "nd.tll == 00:00:00:00:00:00"]) + +dnl ND NS lookup and put flows at priority 110 for nd.sll. +dnl The flows must include a guard against all-zero nd.sll (no SLLAO). +AT_CHECK([grep -e "ls_in_lookup_fdb" ls0flows | grep "p1" | grep "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) +AT_CHECK([grep -e "ls_in_put_fdb" ls0flows | grep "p1" | grep "priority=120" | grep -q "nd.sll == 00:00:00:00:00:00"]) + +OVN_CLEANUP_NORTHD +AT_CLEANUP +]) + + OVN_FOR_EACH_NORTHD_NO_HV([ AT_SETUP([check options:pkt_clone_type for LSP]) ovn_start @@ -8288,7 +8340,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" S1flows | ovn_strip_lflows], [0], dnl Check that FDB learning is enabled for the switch port. AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) ovn-sbctl dump-flows S2 > S2flows @@ -8305,7 +8369,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S2-|unknown" S2flows | ovn_strip_lflows], [0], dnl Check that FDB learning is enabled for the switch port. AT_CHECK([grep -E "ls_.*fdb.*S2-" S2flows | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "S2-S1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S2-S1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S2-S1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S2-S1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S2-S1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S2-S1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) dnl Add an explicit address to S1-S2 indicating that the port with @@ -8326,7 +8402,19 @@ AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" S1flows2 | ovn_strip_lflows], [0] dnl Check that FDB learning is still enabled for the switch port. AT_CHECK([grep -E "ls_.*fdb.*S1-" S1flows | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "S1-S2"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "S1-S2" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "S1-S2" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "S1-S2" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "S1-S2" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "S1-S2" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) OVN_CLEANUP_NORTHD @@ -10681,9 +10769,21 @@ AT_CHECK([ovn-nbctl --wait=sb lsp-set-options ln_port localnet_learn_fdb=true]) AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "ln_port"), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln_port" && arp), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln_port" && nd_na), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln_port" && nd_ns), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln_port" && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln_port" && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln_port" && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == "ln_port"), action=(flags.localnet = 1; next;) table=??(ls_in_put_fdb ), priority=0 , match=(1), action=(next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "ln_port" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ln_port" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ln_port" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ln_port" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ln_port" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ln_port" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ln_port" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) # Disable 'localnet_learn_fdb' and check the flows @@ -11104,6 +11204,12 @@ dnl priority 50 fallback. AT_CHECK([ovn-sbctl dump-flows ls1 | grep -e 'ls_in_lookup_fdb' | ovn_strip_lflows], [0], [dnl table=??(ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;) table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "ln1"), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" && arp), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" && nd_na), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ln1" && nd_ns), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" && arp.sha == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" && nd.sll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ln1" && nd.tll == 00:00:00:00:00:00), action=(flags.localnet = 1; reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_lookup_fdb ), priority=50 , match=(inport == "ln1"), action=(flags.localnet = 1; next;) ]) @@ -23715,9 +23821,21 @@ AT_CAPTURE_FILE([ls1flows]) AT_CHECK([grep "ls1-to-spine" ls1flows | ovn_strip_lflows | sort], [0], [dnl table=??(ls_in_lookup_fdb ), priority=100 , match=(inport == "ls1-to-spine"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ls1-to-spine" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ls1-to-spine" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , match=(inport == "ls1-to-spine" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ls1-to-spine" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ls1-to-spine" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , match=(inport == "ls1-to-spine" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == "ls1-to-spine"), action=(next;) table=??(ls_in_pre_lb ), priority=110 , match=(ip && inport == "ls1-to-spine"), action=(next;) table=??(ls_in_put_fdb ), priority=100 , match=(inport == "ls1-to-spine" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ls1-to-spine" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ls1-to-spine" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , match=(inport == "ls1-to-spine" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ls1-to-spine" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ls1-to-spine" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , match=(inport == "ls1-to-spine" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == "ls1-to-spine"), action=(next;) table=??(ls_out_pre_lb ), priority=110 , match=(ip && outport == "ls1-to-spine"), action=(next;) ]) diff --git a/tests/ovn.at b/tests/ovn.at index 136b825f98..e095e50bff 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -34090,6 +34090,30 @@ match=(1), action=(next;) match=(inport == "sw0-p1"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) table=??(ls_in_lookup_fdb ), priority=100 , dnl match=(inport == "sw0-p3"), action=(reg0[[11]] = lookup_fdb(inport, eth.src); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && arp), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = lookup_fdb(inport, arp.sha); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && nd_na), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.tll); next;) + table=??(ls_in_lookup_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && nd_ns), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]]= lookup_fdb(inport, nd.sll); next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) + table=??(ls_in_lookup_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00), action=(reg0[[11]] = lookup_fdb(inport, eth.src); reg0[[22]] = 1; next;) ]) AT_CHECK([grep "ls_in_put_fdb" sw0flows | ovn_strip_lflows], [0], [dnl @@ -34099,6 +34123,30 @@ match=(1), action=(next;) match=(inport == "sw0-p1" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) table=??(ls_in_put_fdb ), priority=100 , dnl match=(inport == "sw0-p3" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p1" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && arp && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, arp.sha); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && nd_na && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.tll); next;) + table=??(ls_in_put_fdb ), priority=110 , dnl +match=(inport == "sw0-p3" && nd_ns && (reg0[[11]] == 0 || reg0[[22]] == 0)), action=(put_fdb(inport, eth.src); put_fdb(inport, nd.sll); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p1" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && arp.sha == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && nd.sll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) + table=??(ls_in_put_fdb ), priority=120 , dnl +match=(inport == "sw0-p3" && nd.tll == 00:00:00:00:00:00 && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;) ]) # Send a packet from sw0-p1 with a different mac not present @@ -34440,6 +34488,88 @@ OVN_CLEANUP([hv1], [hv2], [hv3]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn -- FDB learning from ARP sender hardware address]) +AT_KEYWORDS([fdb arp]) +CHECK_SCAPY +ovn_start + +dnl Three ports on a logical switch. p1 and p2 have "unknown" to enable +dnl FDB learning. pr is a regular client. +check ovn-nbctl ls-add ls0 +check ovn-nbctl lsp-add ls0 p1 +check ovn-nbctl lsp-set-addresses p1 "fa:16:3e:00:00:01 10.0.0.1" unknown +check ovn-nbctl lsp-add ls0 p2 +check ovn-nbctl lsp-set-addresses p2 "fa:16:3e:00:00:02 10.0.0.2" unknown +check ovn-nbctl lsp-add ls0 p3 +check ovn-nbctl lsp-set-addresses p3 "fa:16:3e:00:00:03 10.0.0.3" + +net_add n1 +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 + +check ovs-vsctl -- add-port br-int vif1 -- \ + set interface vif1 external-ids:iface-id=p1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap +check ovs-vsctl -- add-port br-int vif2 -- \ + set interface vif2 external-ids:iface-id=p2 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap +check ovs-vsctl -- add-port br-int vif3 -- \ + set interface vif3 external-ids:iface-id=p3 \ + options:tx_pcap=hv1/vif3-tx.pcap \ + options:rxq_pcap=hv1/vif3-rx.pcap + +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +dnl p1 sends a GARP with eth.src=phys_mac, arp.sha=other_mac. +dnl where phys_mac=fa:16:3e:00:00:01 and other_mac=fa:16:3e:aa:bb:cc +dnl Both MACs should be learned into the FDB for p1. + +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='fa:16:3e:00:00:01')/ \ + ARP(op=1, hwsrc='fa:16:3e:aa:bb:cc', psrc='10.0.0.100', \ + hwdst='00:00:00:00:00:00', pdst='10.0.0.100')") +as hv1 ovs-appctl netdev-dummy/receive vif1 $garp + +dnl Wait for arp.sha (other_mac) to appear in the FDB table. +OVS_WAIT_UNTIL([ovn-sbctl find fdb mac='"fa:16:3e:aa:bb:cc"' | grep -q port_key]) + +dnl Verify both MACs are in the FDB. Each put_fdb generates a separate +dnl packet-in with an independent random delay, so both need a wait. +wait_row_count fdb 1 mac='"fa:16:3e:00:00:01"' +wait_row_count fdb 1 mac='"fa:16:3e:aa:bb:cc"' + +dnl Send unicast traffic from p2 to other_mac. The FDB should +dnl only direct it to p1. +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file vif2 hv1/vif2 + +packet=$(fmt_pkt "Ether(dst='fa:16:3e:aa:bb:cc', src='fa:16:3e:00:00:03')/ \ + IP(src='10.0.0.3', dst='10.0.0.100')/ \ + UDP(sport=12345, dport=5678)/ \ + Raw(b'to_other_mac')") +echo $packet > p1.expected +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet + +packet=$(fmt_pkt "Ether(dst='fa:16:3e:00:00:01', src='fa:16:3e:00:00:03')/ \ + IP(src='10.0.0.3', dst='10.0.0.101')/ \ + UDP(sport=12345, dport=5678)/ \ + Raw(b'to_other_mac')") +echo $packet >> p1.expected +as hv1 ovs-appctl netdev-dummy/receive vif3 $packet + +> p2.expected +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [p1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [p2.expected]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD([ AT_SETUP([container port changed to normal port and then deleted]) ovn_start @@ -41116,12 +41246,12 @@ sleep_sb sleep_controller hv2 AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 14 -> 24]) -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:14')/ARP(op=1, pdst='192.168.10.24', psrc='192.168.10.14')") +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:14')/ARP(op=1, hwsrc='00:00:00:00:10:14', pdst='192.168.10.24', psrc='192.168.10.14')") as hv1 ovs-appctl netdev-dummy/receive vif14 $arp_req ensure_controller_run hv1 AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 24 -> 14]) -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', src='00:00:00:00:10:24')/ARP(op=2, pdst='192.168.10.14', psrc='192.168.10.24')") +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:14', src='00:00:00:00:10:24')/ARP(op=2, hwsrc='00:00:00:00:10:24', hwdst='00:00:00:00:10:14', pdst='192.168.10.14', psrc='192.168.10.24')") as hv2 ovs-appctl netdev-dummy/receive vif24 $arp_req sleep 1 @@ -41149,12 +41279,12 @@ sleep_sb sleep_controller hv2 AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 15 -> 25]) -arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:15')/ARP(op=1, pdst='192.168.10.25', psrc='192.168.10.15')") +arp_req=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:15')/ARP(op=1, hwsrc='00:00:00:00:10:15', pdst='192.168.10.25', psrc='192.168.10.15')") as hv1 ovs-appctl netdev-dummy/receive vif15 $arp_req ensure_controller_run hv1 AS_BOX([$(date +%H:%M:%S.%03N) Sending ARP 25 -> 15]) -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', src='00:00:00:00:10:25')/ARP(op=2, pdst='192.168.10.15', psrc='192.168.10.25')") +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:15', src='00:00:00:00:10:25')/ARP(op=2, hwsrc='00:00:00:00:10:25', hwdst='00:00:00:00:10:15', pdst='192.168.10.15', psrc='192.168.10.25')") as hv2 ovs-appctl netdev-dummy/receive vif25 $arp_req sleep 1 @@ -41178,7 +41308,10 @@ expected_flow_count=$(($expected_flow_count + 4)) check_flow_count hv1 $expected_flow_count check_flow_count hv2 $expected_flow_count -AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc -l`]) +# 5 original packet-ins + 3 extra for arp.sha learning +# (one per ARP sent from hv1: vif14->vif24, vif15->vif25, +# vif99->unknown). +AT_CHECK([test 8 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc -l`]) # Do not check number of PACKET_IN2 on hv2, as vif14 => 99 as well as vif15 => vif25 # might have caused PACKET_IN2, but without any guarantee. # e.g. (1) packet => hv1 ovs, (2) PACKET_IN2 => hv1 ovn, (3) FDB => sb, @@ -41190,7 +41323,7 @@ AT_CHECK([test 5 = `cat hv1/ovs-vswitchd.log | grep NXT_PACKET_IN2 | wc -l`]) # hv1 is expected to be stable. AS_BOX([$(date +%H:%M:%S.%03N) Packet from external]) -arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', src='00:00:00:00:10:40')/ARP(op=2, pdst='192.168.10.50', psrc='192.168.10.40')") +arp_req=$(fmt_pkt "Ether(dst='00:00:00:00:10:50', src='00:00:00:00:10:40')/ARP(op=2, hwsrc='00:00:00:00:10:40', hwdst='00:00:00:00:10:50', pdst='192.168.10.50', psrc='192.168.10.40')") as hv2 ovs-appctl netdev-dummy/receive ext0 $arp_req wait_column "$ln_port_key" fdb port_key mac='"00:00:00:00:10:40"'
Currently, build_lswitch_learn_fdb_op() only generates put_fdb(inport, eth.src) flows, so only eth.src is ever learned into the FDB. In VRRP gratuitous ARP packets, eth.src is the physical MAC while arp.sha is the virtual MAC (VMAC). Since put_fdb only records eth.src, the VMAC is never learned and traffic destined to it falls through to MC_UNKNOWN flooding. The same gap exists for IPv6 Neighbor Discovery: unsolicited NAs carry the VMAC in nd.tll, and NSes carry it in nd.sll. Add higher-priority (110) flows in both the lookup_fdb and put_fdb stages for ARP, ND NA, and ND NS packets. The lookup flows check both eth.src and the inner MAC field (arp.sha, nd.tll, nd.sll) and store the inner result in a new regbit REGBIT_LKUP_FDB_DL_INNER (reg0[22]). The put flows fire when either MAC is unknown and learn both via two put_fdb calls. When both MACs are already known, no put_fdb flow matches and no packet-in is generated. ARP/ND packets may lack the arp.sha/SLL/TLL, in which case arp.sha/nd.sll/nd.tll is all-zeros. Higher priority (120) flows for ARP/ND flows prevent learning a zero MAC into the FDB. Reported-at: https://redhat.atlassian.net/browse/FDP-4286 Assisted-by: Claude Opus 4.6, opencode Signed-off-by: Dumitru Ceara <dceara@redhat.com> --- NEWS | 5 ++ northd/northd.c | 190 +++++++++++++++++++++++++++++++++++++++++--- ovn-nb.xml | 23 ++++-- tests/ovn-ic.at | 72 +++++++++++++++++ tests/ovn-northd.at | 118 +++++++++++++++++++++++++++ tests/ovn.at | 145 +++++++++++++++++++++++++++++++-- 6 files changed, 532 insertions(+), 21 deletions(-)