FDB entries using a nexthop ID instead of a direct destination address are not parsed. Add NDA_NH_ID support so they can be used by the EVPN FDBs.
Assisted-by: Claude Opus 4.6, Claude Code Signed-off-by: Ales Musil <[email protected]> --- controller/neighbor-exchange-netlink.c | 13 +++ controller/neighbor-exchange-netlink.h | 1 + tests/ovn-macros.at | 6 +- tests/system-ovn-netlink.at | 121 ++++++++++++++++++++----- tests/test-ovn-netlink.c | 10 +- 5 files changed, 121 insertions(+), 30 deletions(-) diff --git a/controller/neighbor-exchange-netlink.c b/controller/neighbor-exchange-netlink.c index 812c146f4..9eecf9f5b 100644 --- a/controller/neighbor-exchange-netlink.c +++ b/controller/neighbor-exchange-netlink.c @@ -266,12 +266,18 @@ ne_table_parse__(struct ofpbuf *buf, size_t ofs, const struct nlmsghdr *nlmsg, [NDA_DST] = { .type = NL_A_U32, .optional = true }, [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, [NDA_PORT] = { .type = NL_A_U16, .optional = true }, + /* NDA_NH_ID is only used with AF_BRIDGE messages. + * This entry aligns the policy array sizes. */ + [NDA_NH_ID] = { .type = NL_A_U32, .optional = true }, }; static const struct nl_policy policy6[] = { [NDA_DST] = { .type = NL_A_IPV6, .optional = true }, [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, [NDA_PORT] = { .type = NL_A_U16, .optional = true }, + /* NDA_NH_ID is only used with AF_BRIDGE messages. + * This entry aligns the policy array sizes. */ + [NDA_NH_ID] = { .type = NL_A_U32, .optional = true }, }; static const struct nl_policy policy_bridge[] = { @@ -281,6 +287,7 @@ ne_table_parse__(struct ofpbuf *buf, size_t ofs, const struct nlmsghdr *nlmsg, [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, [NDA_PORT] = { .type = NL_A_U16, .optional = true }, [NDA_VLAN] = { .type = NL_A_U16, .optional = true }, + [NDA_NH_ID] = { .type = NL_A_U32, .optional = true }, }; BUILD_ASSERT(ARRAY_SIZE(policy) == ARRAY_SIZE(policy6)); @@ -345,6 +352,12 @@ ne_table_parse__(struct ofpbuf *buf, size_t ofs, const struct nlmsghdr *nlmsg, if (attrs[NDA_VLAN]) { change->nd.vlan = nl_attr_get_u16(attrs[NDA_VLAN]); } + + /* NDA_NH_ID is only present in AF_BRIDGE messages in practice, + * but reading it unconditionally is harmless. */ + if (attrs[NDA_NH_ID]) { + change->nd.nh_id = nl_attr_get_u32(attrs[NDA_NH_ID]); + } } else { VLOG_DBG_RL(&rl, "received unparseable rtnetlink neigh message"); return 0; diff --git a/controller/neighbor-exchange-netlink.h b/controller/neighbor-exchange-netlink.h index 6d907938e..659fe72d8 100644 --- a/controller/neighbor-exchange-netlink.h +++ b/controller/neighbor-exchange-netlink.h @@ -40,6 +40,7 @@ struct ne_nl_received_neigh { * from linux/neighbour.h. */ uint8_t type; /* A value out of 'rtm_type' from linux/rtnetlink.h * e.g., RTN_UNICAST, RTN_MULTICAST. */ + uint32_t nh_id; /* ID of nexthop group if present, 0 otherwise. */ }; /* A digested version of a neigh message sent down by the kernel to indicate diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at index 39f03ba62..241453df9 100644 --- a/tests/ovn-macros.at +++ b/tests/ovn-macros.at @@ -1465,7 +1465,11 @@ ovn_strip_collector_set() { } netlink_if_index() { - ip -o link show dev $1 | awk -F: '{print $1}' + local ns_prefix= + if test -n "$2"; then + ns_prefix="ip netns exec $2" + fi + $ns_prefix ip -o link show dev $1 | awk -F: '{print $1}' } OVS_END_SHELL_HELPERS diff --git a/tests/system-ovn-netlink.at b/tests/system-ovn-netlink.at index 738800ea7..6bcad017f 100644 --- a/tests/system-ovn-netlink.at +++ b/tests/system-ovn-netlink.at @@ -24,9 +24,9 @@ check bridge fdb append 00:00:00:00:00:00 dev vxlan-test \ if_index=$(netlink_if_index vxlan-test) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ bridge $if_index 0 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:00 dst=42.42.42.3 port=0 -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:00 dst=42.42.42.4 port=4790 -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:00 dst=42.42.42.3 port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:00 dst=42.42.42.4 port=4790 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 ]) AT_CLEANUP @@ -52,8 +52,44 @@ check bridge fdb add 00:00:00:00:00:03 dev vxlan-test \ if_index=$(netlink_if_index vxlan-test) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ bridge $if_index 0 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:03 dst=42.42.42.3 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:03 dst=42.42.42.3 port=0 nh_id=0 +]) + +dnl Test FDB entries with nexthop group ID (NDA_NH_ID). +dnl Use a separate namespace to avoid nexthop ID conflicts. +ADD_NAMESPACES(nhid) + +dnl Bridge setup. +NS_EXEC([nhid], [ip link add br-nhid type bridge]) +NS_EXEC([nhid], [ip link set br-nhid address 00:00:00:00:00:01]) +NS_EXEC([nhid], [ip link set dev br-nhid up]) + +dnl VXLAN setup. +NS_EXEC([nhid], [ip link add vxlan-nhid type vxlan id 42 \ + dstport 4789 local 42.42.42.2 nolearning]) +NS_EXEC([nhid], [ip link set vxlan-nhid master br-nhid]) +NS_EXEC([nhid], [ip link set vxlan-nhid address 00:00:00:00:00:02]) +NS_EXEC([nhid], [ip link set dev vxlan-nhid up]) + +dnl Nexthop setup. +NS_EXEC([nhid], [ip nexthop add id 1 via 192.168.1.1 fdb]) +NS_EXEC([nhid], [ip nexthop add id 10 group 1 fdb]) + +dnl FDB entries. +NS_EXEC([nhid], [bridge fdb add 00:00:00:00:00:03 dev vxlan-nhid \ + dst 42.42.42.3 static extern_learn]) +NS_EXEC([nhid], [bridge fdb add 00:00:00:00:00:04 dev vxlan-nhid nhid 10]) + +nhid_if_index=$(netlink_if_index vxlan-nhid nhid) + +dnl Verify that nh_id is parsed and reported. +OVS_WAIT_FOR_OUTPUT_UNQUOTED( + [ip netns exec nhid ovstest test-ovn-netlink neighbor-sync \ + bridge $nhid_if_index 0 | sort], [0], [dnl +Neighbor ifindex=$nhid_if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 +Neighbor ifindex=$nhid_if_index vlan=0 eth=00:00:00:00:00:03 dst=42.42.42.3 port=0 nh_id=0 +Neighbor ifindex=$nhid_if_index vlan=0 eth=00:00:00:00:00:04 dst=:: port=0 nh_id=10 ]) AT_CLEANUP @@ -76,9 +112,9 @@ dnl the L2 multicast ones. if_index=$(netlink_if_index lo-test) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ bridge $if_index 2 00:00:00:00:01:00 00:00:00:00:02:00 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 nh_id=0 ]) dnl Check that OVN installed its entries (these are always installed @@ -98,9 +134,9 @@ check bridge fdb del 00:00:00:00:02:00 dev lo-test master static OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ bridge $if_index 2 00:00:00:00:01:00 00:00:00:00:02:00 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 nh_id=0 ]) OVS_WAIT_FOR_OUTPUT([bridge fdb show dev lo-test | grep static | sort], [0], @@ -118,9 +154,9 @@ check bridge fdb add 00:00:00:00:04:00 dev lo-test master static OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ bridge $if_index 2 00:00:00:00:01:00 00:00:00:00:02:00 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 -Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:00:02 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=01:00:5e:00:00:01 dst=:: port=0 nh_id=0 +Neighbor ifindex=$if_index vlan=0 eth=33:33:00:00:00:01 dst=:: port=0 nh_id=0 ]) OVS_WAIT_FOR_OUTPUT([bridge fdb show dev lo-test | grep static | sort], [0], @@ -160,7 +196,7 @@ dnl external ones. if_index=$(netlink_if_index br-test) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet $if_index 1 00:00:00:00:20:00 20.20.20.20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 nh_id=0 ]) dnl Check that OVN installed its entries (these are always installed @@ -172,7 +208,7 @@ dnl Let OVN inject some IPv6 neighbors too and make sure it learnt the dnl external ones. OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet6 $if_index 1 00:00:00:00:20:00 20::20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 nh_id=0 ]) dnl Check that OVN installed its entries (these are always installed @@ -186,11 +222,11 @@ check ip neigh del dev br-test 20.20.20.20 check ip -6 neigh del dev br-test 20::20 OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet $if_index 1 00:00:00:00:20:00 20.20.20.20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 nh_id=0 ]) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet6 $if_index 1 00:00:00:00:20:00 20::20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 nh_id=0 ]) OVN_NEIGH_EQUAL([br-test], [nud noarp], [20.20.20], [dnl @@ -207,11 +243,11 @@ check ip -6 neigh add 20::40 \ OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet $if_index 1 00:00:00:00:20:00 20.20.20.20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 nh_id=0 ]) OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink neighbor-sync \ inet6 $if_index 1 00:00:00:00:20:00 20::20 | sort], [0], [dnl -Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 +Neighbor ifindex=$if_index vlan=0 eth=00:00:00:00:10:00 dst=10::10 port=0 nh_id=0 ]) OVN_NEIGH_EQUAL([br-test], [nud noarp], [20.20.20], [dnl @@ -241,13 +277,13 @@ lo_if_index=$(netlink_if_index lo-test) dnl Should notify if an entry is added to a bridge port monitored by OVN. AT_CHECK_UNQUOTED([ovstest test-ovn-netlink neighbor-table-notify \ 'bridge fdb add 00:00:00:00:00:05 dev lo-test'], [0], [dnl -Add neighbor ifindex=$lo_if_index vlan=0 eth=00:00:00:00:00:05 dst=:: port=0 +Add neighbor ifindex=$lo_if_index vlan=0 eth=00:00:00:00:00:05 dst=:: port=0 nh_id=0 ]) dnl Should notify if an entry is removed from a bridge port monitored by OVN. AT_CHECK_UNQUOTED([ovstest test-ovn-netlink neighbor-table-notify \ 'bridge fdb del 00:00:00:00:00:05 dev lo-test'], [0], [dnl -Delete neighbor ifindex=$lo_if_index vlan=0 eth=00:00:00:00:00:05 dst=:: port=0 +Delete neighbor ifindex=$lo_if_index vlan=0 eth=00:00:00:00:00:05 dst=:: port=0 nh_id=0 ]) dnl Should NOT notify if a static entry is added to a bridge port @@ -261,7 +297,7 @@ dnl OVN. AT_CHECK_UNQUOTED([ovstest test-ovn-netlink neighbor-table-notify \ 'ip neigh add 10.10.10.10 lladdr 00:00:00:00:10:00 \ dev br-test extern_learn'], [0], [dnl -Add neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 +Add neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:10:00 dst=10.10.10.10 port=0 nh_id=0 ]) dnl Should notify if an entry is removed from a bridge that's monitored by @@ -269,8 +305,8 @@ dnl OVN. AT_CHECK_UNQUOTED([ovstest test-ovn-netlink neighbor-table-notify \ 'ip neigh del 10.10.10.10 lladdr 00:00:00:00:10:00 \ dev br-test' | sort], [0], [dnl -Add neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:00:00 dst=10.10.10.10 port=0 -Delete neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:00:00 dst=10.10.10.10 port=0 +Add neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:00:00 dst=10.10.10.10 port=0 nh_id=0 +Delete neighbor ifindex=$br_if_index vlan=0 eth=00:00:00:00:00:00 dst=10.10.10.10 port=0 nh_id=0 ]) dnl Should NOT notify if a noarp entry is added to a bridge port @@ -279,6 +315,41 @@ AT_CHECK_UNQUOTED([ovstest test-ovn-netlink neighbor-table-notify \ 'ip neigh add 20.20.20.20 lladdr 00:00:00:00:20:00 \ dev br-test nud noarp'], [0], [dnl ]) + +dnl Test NDA_NH_ID in notifications. +dnl Use a separate namespace to avoid nexthop ID conflicts. +ADD_NAMESPACES(nhid) + +dnl Bridge setup. +NS_EXEC([nhid], [ip link add br-nhid type bridge]) +NS_EXEC([nhid], [ip link set br-nhid address 00:00:00:00:00:01]) +NS_EXEC([nhid], [ip link set dev br-nhid up]) + +dnl VXLAN setup. +NS_EXEC([nhid], [ip link add vxlan-nhid type vxlan id 42 \ + dstport 4789 local 42.42.42.2 nolearning]) +NS_EXEC([nhid], [ip link set vxlan-nhid master br-nhid]) +NS_EXEC([nhid], [ip link set dev vxlan-nhid up]) + +dnl Nexthop setup. +NS_EXEC([nhid], [ip nexthop add id 1 via 192.168.1.1 fdb]) +NS_EXEC([nhid], [ip nexthop add id 10 group 1 fdb]) + +nhid_if_index=$(netlink_if_index vxlan-nhid nhid) + +dnl Should notify with nh_id when FDB entry with nhid is added. +AT_CHECK_UNQUOTED( + [ip netns exec nhid ovstest test-ovn-netlink neighbor-table-notify \ + 'bridge fdb add 00:00:00:00:00:07 dev vxlan-nhid nhid 10'], [0], [dnl +Add neighbor ifindex=$nhid_if_index vlan=0 eth=00:00:00:00:00:07 dst=:: port=0 nh_id=10 +]) + +dnl Should notify with nh_id when FDB entry with nhid is deleted. +AT_CHECK_UNQUOTED( + [ip netns exec nhid ovstest test-ovn-netlink neighbor-table-notify \ + 'bridge fdb del 00:00:00:00:00:07 dev vxlan-nhid nhid 10'], [0], [dnl +Delete neighbor ifindex=$nhid_if_index vlan=0 eth=00:00:00:00:00:07 dst=:: port=0 nh_id=10 +]) AT_CLEANUP AT_SETUP([netlink - host-if-monitor]) diff --git a/tests/test-ovn-netlink.c b/tests/test-ovn-netlink.c index 45880e3b4..bea269b5e 100644 --- a/tests/test-ovn-netlink.c +++ b/tests/test-ovn-netlink.c @@ -95,10 +95,11 @@ test_neighbor_sync(struct ovs_cmdl_context *ctx) VECTOR_FOR_EACH_PTR (&received_neighbors, ne) { char addr_s[INET6_ADDRSTRLEN + 1]; printf("Neighbor ifindex=%"PRId32" vlan=%"PRIu16" " - "eth=" ETH_ADDR_FMT " dst=%s port=%"PRIu16"\n", + "eth=" ETH_ADDR_FMT " dst=%s port=%"PRIu16" " + "nh_id=%"PRIu32"\n", ne->if_index, ne->vlan, ETH_ADDR_ARGS(ne->lladdr), ipv6_string_mapped(addr_s, &ne->addr) ? addr_s : "(invalid)", - ne->port); + ne->port, ne->nh_id); } done: @@ -142,13 +143,14 @@ test_neighbor_table_notify(struct ovs_cmdl_context *ctx) VECTOR_FOR_EACH_PTR (msgs, msg) { char addr_s[INET6_ADDRSTRLEN + 1]; printf("%s neighbor ifindex=%"PRId32" vlan=%"PRIu16" " - "eth=" ETH_ADDR_FMT " dst=%s port=%"PRIu16"\n", + "eth=" ETH_ADDR_FMT " dst=%s port=%"PRIu16" " + "nh_id=%"PRIu32"\n", msg->nlmsg_type == RTM_NEWNEIGH ? "Add" : "Delete", msg->nd.if_index, msg->nd.vlan, ETH_ADDR_ARGS(msg->nd.lladdr), ipv6_string_mapped(addr_s, &msg->nd.addr) ? addr_s : "(invalid)", - msg->nd.port); + msg->nd.port, msg->nd.nh_id); } ovn_netlink_notifiers_destroy(); -- 2.53.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
