On 12/10/25 11:18 AM, Ales Musil wrote:
> The advertised routes would always be specified as blackhole
> without any nexthop. Allow the route to be configured with
> specific nexthop even from different IP family to allow
> routing of IPv4 over IPv6 nexthop.
>
> At the same time also extend the netlink testing with
> route-sync so we can test this out independently of
> the rest of route advertisement.
>
> Signed-off-by: Ales Musil <[email protected]>
> ---
Hi Ales,
Thanks for the patch!
> controller/route-exchange-netlink.c | 126 ++++++++++++------
> controller/route-exchange-netlink.h | 9 +-
> controller/route.c | 16 ++-
> controller/route.h | 5 +-
> tests/automake.mk | 2 +
> tests/system-ovn-netlink.at | 200 ++++++++++++++++++++++++++++
> tests/test-ovn-netlink.c | 68 ++++++++++
> tests/test-utils.c | 18 +++
> tests/test-utils.h | 4 +
> 9 files changed, 397 insertions(+), 51 deletions(-)
>
> diff --git a/controller/route-exchange-netlink.c
> b/controller/route-exchange-netlink.c
> index 60bae4781..55670729f 100644
> --- a/controller/route-exchange-netlink.c
> +++ b/controller/route-exchange-netlink.c
> @@ -38,6 +38,9 @@ VLOG_DEFINE_THIS_MODULE(route_exchange_netlink);
>
> #define NETNL_REQ_BUFFER_SIZE 128
>
> +static void re_nl_encode_nexthop(struct ofpbuf *, bool dst_is_ipv4,
> + const struct in6_addr *);
> +
> int
> re_nl_create_vrf(const char *ifname, uint32_t table_id)
> {
> @@ -95,13 +98,32 @@ re_nl_delete_vrf(const char *ifname)
> return err;
> }
>
> +void
> +re_route_format(struct ds *ds, uint32_t table_id, const struct in6_addr *dst,
> + unsigned int plen, const struct in6_addr *nexthop, int err)
> +{
> + ds_put_format(ds, "table_id=%"PRIu32" dst=", table_id);
> + ipv6_format_mapped(dst, ds);
> + ds_put_format(ds, " plen=%u nexthop=", plen);
> + if (ipv6_is_zero(nexthop)) {
> + ds_put_cstr(ds, "(blackhole)");
> + } else {
> + ipv6_format_mapped(nexthop, ds);
> + }
> +
> + if (err) {
> + ds_put_format(ds, " failed: %s", ovs_strerror(err));
> + }
> +}
> +
> static int
> modify_route(uint32_t type, uint32_t flags_arg, uint32_t table_id,
> const struct in6_addr *dst, unsigned int plen,
> - unsigned int priority)
> + const struct in6_addr *nexhhop, unsigned int priority)
Typo: "nexhhop"
> {
> uint32_t flags = NLM_F_REQUEST | NLM_F_ACK;
> bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(dst);
> + bool nexthop_unspec = ipv6_is_zero(nexhhop);
> struct rtmsg *rt;
> int err;
>
> @@ -117,7 +139,7 @@ modify_route(uint32_t type, uint32_t flags_arg, uint32_t
> table_id,
> rt->rtm_table = RT_TABLE_UNSPEC; /* RTA_TABLE attribute allows id > 256
> */
> /* Manage only OVN routes */
> rt->rtm_protocol = RTPROT_OVN;
> - rt->rtm_type = RTN_BLACKHOLE;
> + rt->rtm_type = nexthop_unspec ? RTN_BLACKHOLE : RTN_UNICAST;
> rt->rtm_scope = RT_SCOPE_UNIVERSE;
> rt->rtm_dst_len = plen;
>
> @@ -130,25 +152,16 @@ modify_route(uint32_t type, uint32_t flags_arg,
> uint32_t table_id,
> nl_msg_put_in6_addr(&request, RTA_DST, dst);
> }
>
> + if (!nexthop_unspec) {
> + re_nl_encode_nexthop(&request, is_ipv4, nexhhop);
> + }
> +
> if (VLOG_IS_DBG_ENABLED()) {
> struct ds msg = DS_EMPTY_INITIALIZER;
>
> - if (type == RTM_DELROUTE) {
> - ds_put_cstr(&msg, "Removing blackhole route from ");
> - } else {
> - ds_put_cstr(&msg, "Adding blackhole route to ");
> - }
> -
> - ds_put_format(&msg, "table %"PRIu32 " for prefix ", table_id);
> - if (IN6_IS_ADDR_V4MAPPED(dst)) {
> - ds_put_format(&msg, IP_FMT,
> - IP_ARGS(in6_addr_get_mapped_ipv4(dst)));
> - } else {
> - ipv6_format_addr(dst, &msg);
> - }
> - ds_put_format(&msg, "/%u", plen);
> -
> - VLOG_DBG("%s", ds_cstr(&msg));
> + re_route_format(&msg, table_id, dst, plen, nexhhop, 0);
> + VLOG_DBG("%s route %s", type == RTM_DELROUTE ? "Removing" : "Adding",
> + ds_cstr(&msg));
> ds_destroy(&msg);
> }
>
> @@ -160,7 +173,8 @@ modify_route(uint32_t type, uint32_t flags_arg, uint32_t
> table_id,
>
> int
> re_nl_add_route(uint32_t table_id, const struct in6_addr *dst,
> - unsigned int plen, unsigned int priority)
> + unsigned int plen, const struct in6_addr *nexthop,
> + unsigned int priority)
> {
> if (!TABLE_ID_VALID(table_id)) {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> @@ -171,12 +185,13 @@ re_nl_add_route(uint32_t table_id, const struct
> in6_addr *dst,
> }
>
> return modify_route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, table_id,
> - dst, plen, priority);
> + dst, plen, nexthop, priority);
> }
>
> int
> re_nl_delete_route(uint32_t table_id, const struct in6_addr *dst,
> - unsigned int plen, unsigned int priority)
> + unsigned int plen, const struct in6_addr *nexthop,
> + unsigned int priority)
> {
> if (!TABLE_ID_VALID(table_id)) {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> @@ -186,7 +201,8 @@ re_nl_delete_route(uint32_t table_id, const struct
> in6_addr *dst,
> return EINVAL;
> }
>
> - return modify_route(RTM_DELROUTE, 0, table_id, dst, plen, priority);
> + return modify_route(RTM_DELROUTE, 0, table_id, dst, plen,
> + nexthop, priority);
> }
>
> struct route_msg_handle_data {
> @@ -249,10 +265,14 @@ handle_route_msg(const struct route_table_msg *msg,
> void *data)
> }
>
> if (handle_data->routes_to_advertise) {
> - uint32_t hash = advertise_route_hash(&rd->rta_dst, rd->rtm_dst_len);
> + uint32_t hash = advertise_route_hash(&rd->rta_dst,
> + &rd->primary_next_hop__.addr,
> + rd->rtm_dst_len);
> HMAP_FOR_EACH_WITH_HASH (ar, node, hash, handle_data->routes) {
> if (ipv6_addr_equals(&ar->addr, &rd->rta_dst)
> && ar->plen == rd->rtm_dst_len
> + && ipv6_addr_equals(&ar->nexthop,
> + &rd->primary_next_hop__.addr)
I wonder if the reason behind OVS naming this field with trailing
underscores is because it should be "private". Looking at the
definition in 'struct route_data':
* For the common case of one next hop, the nexthops list will contain a
* single entry pointing to the struct route_data primary_next_hop__
* element.
I think it would make sense to use:
CONTAINER_OF(ovs_list_front(&rd->nexthops),
const struct route_data_nexthop,
nexthop_node);
instead.
Applies to the two occurences below too.
> && ar->priority == rd->rta_priority) {
> hmapx_find_and_delete(handle_data->routes_to_advertise, ar);
> return;
> @@ -268,30 +288,52 @@ handle_route_msg(const struct route_table_msg *msg,
> void *data)
> static int
> re_nl_delete_stale_routes(const struct vector *stale_routes)
> {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> + struct ds ds = DS_EMPTY_INITIALIZER;
> int ret = 0;
>
> const struct route_data *rd;
> VECTOR_FOR_EACH_PTR (stale_routes, rd) {
> int err = re_nl_delete_route(rd->rta_table_id, &rd->rta_dst,
> - rd->rtm_dst_len, rd->rta_priority);
> + rd->rtm_dst_len,
> + &rd->primary_next_hop__.addr,
> + rd->rta_priority);
> if (err) {
> - char addr_s[INET6_ADDRSTRLEN + 1];
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> - VLOG_WARN_RL(&rl, "Delete route table_id=%"PRIu32" dst=%s
> plen=%d "
> - "failed: %s", rd->rta_table_id,
> - ipv6_string_mapped(
> - addr_s, &rd->rta_dst) ? addr_s : "(invalid)",
> - rd->rtm_dst_len,
> - ovs_strerror(err));
> + re_route_format(&ds, rd->rta_table_id, &rd->rta_dst,
> + rd->rtm_dst_len, &rd->primary_next_hop__.addr,
> + err);
> + VLOG_WARN_RL(&rl, "Delete route %s", ds_cstr(&ds));
> + ds_clear(&ds);
> if (!ret) {
> ret = err;
> }
> }
> }
>
> + ds_destroy(&ds);
> return ret;
> }
>
> +static void
> +re_nl_encode_nexthop(struct ofpbuf *request, bool dst_is_ipv4,
> + const struct in6_addr *nexthop)
> +{
> + bool nh_is_ipv4 = IN6_IS_ADDR_V4MAPPED(nexthop);
> + size_t len = nh_is_ipv4 ? sizeof(ovs_be32) : sizeof(struct in6_addr);
> +
> + ovs_be32 nexthop4 = in6_addr_get_mapped_ipv4(nexthop);
> + void *nl_attr_dst = nh_is_ipv4 ? (void *) &nexthop4 : (void *) nexthop;
> +
> + if (dst_is_ipv4 != nh_is_ipv4) {
> + struct rtvia *via = nl_msg_put_unspec_uninit(request, RTA_VIA,
> + sizeof *via + len);
> + via->rtvia_family = nh_is_ipv4 ? AF_INET : AF_INET6;
> + memcpy(via->rtvia_addr, nl_attr_dst, len);
> + } else {
> + nl_msg_put_unspec(request, RTA_GATEWAY, nl_attr_dst, len);
> + }
> +}
> +
> int
> re_nl_sync_routes(uint32_t table_id, const struct hmap *routes,
> struct vector *learned_routes,
> @@ -320,22 +362,21 @@ re_nl_sync_routes(uint32_t table_id, const struct hmap
> *routes,
>
> int ret = re_nl_delete_stale_routes(&stale_routes);
>
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> + struct ds ds = DS_EMPTY_INITIALIZER;
> +
> /* Add any remaining routes in the routes_to_advertise hmapx to the
> * system routing table. */
> struct hmapx_node *hn;
> HMAPX_FOR_EACH (hn, &routes_to_advertise) {
> ar = hn->data;
> - int err = re_nl_add_route(table_id, &ar->addr, ar->plen,
> ar->priority);
> + int err = re_nl_add_route(table_id, &ar->addr, ar->plen,
> + &ar->nexthop, ar->priority);
> if (err) {
> - char addr_s[INET6_ADDRSTRLEN + 1];
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> - VLOG_WARN_RL(&rl, "Add route table_id=%"PRIu32" dst=%s "
> - "plen=%d: %s",
> - table_id,
> - ipv6_string_mapped(
> - addr_s, &ar->addr) ? addr_s : "(invalid)",
> - ar->plen,
> - ovs_strerror(err));
> + re_route_format(&ds, table_id, &ar->addr, ar->plen,
> + &ar->nexthop, err);
> + VLOG_WARN_RL(&rl, "Add route %s", ds_cstr(&ds));
> + ds_clear(&ds);
> if (!ret) {
> /* Report the first error value to the caller. */
> ret = err;
> @@ -345,6 +386,7 @@ re_nl_sync_routes(uint32_t table_id, const struct hmap
> *routes,
>
> hmapx_destroy(&routes_to_advertise);
> vector_destroy(&stale_routes);
> + ds_destroy(&ds);
>
> return ret;
> }
> diff --git a/controller/route-exchange-netlink.h
> b/controller/route-exchange-netlink.h
> index 1741f761d..4ee513d40 100644
> --- a/controller/route-exchange-netlink.h
> +++ b/controller/route-exchange-netlink.h
> @@ -52,10 +52,15 @@ struct re_nl_received_route_node {
> int re_nl_create_vrf(const char *ifname, uint32_t table_id);
> int re_nl_delete_vrf(const char *ifname);
>
> +void re_route_format(struct ds *, uint32_t table_id,
> + const struct in6_addr *dst, unsigned int plen,
> + const struct in6_addr *nexthop, int err);
> int re_nl_add_route(uint32_t table_id, const struct in6_addr *dst,
> - unsigned int plen, unsigned int priority);
> + unsigned int plen, const struct in6_addr *nexthop,
> + unsigned int priority);
> int re_nl_delete_route(uint32_t table_id, const struct in6_addr *dst,
> - unsigned int plen, unsigned int priority);
> + unsigned int plen, const struct in6_addr *nexthop,
> + unsigned int priority);
>
> int re_nl_sync_routes(uint32_t table_id, const struct hmap *routes,
> struct vector *learned_routes,
> diff --git a/controller/route.c b/controller/route.c
> index 093306c5b..082a4630d 100644
> --- a/controller/route.c
> +++ b/controller/route.c
> @@ -43,9 +43,11 @@ route_exchange_relevant_port(const struct
> sbrec_port_binding *pb)
> }
>
> uint32_t
> -advertise_route_hash(const struct in6_addr *dst, unsigned int plen)
> +advertise_route_hash(const struct in6_addr *dst,
> + const struct in6_addr *nexthop, unsigned int plen)
> {
> - uint32_t hash = hash_bytes(dst->s6_addr, 16, 0);
> + uint32_t hash = hash_add_in6_addr(0, dst);
> + hash = hash_add_in6_addr(hash, nexthop);
> return hash_int(plen, hash);
> }
>
> @@ -318,11 +320,13 @@ route_run(struct route_ctx_in *r_ctx_in,
> }
>
> struct advertise_route_entry *ar = xmalloc(sizeof(*ar));
> - ar->addr = prefix;
> - ar->plen = plen;
> - ar->priority = priority;
> + *ar = (struct advertise_route_entry) {
> + .addr = prefix,
> + .plen = plen,
> + .priority = priority,
> + };
> hmap_insert(&ad->routes, &ar->node,
> - advertise_route_hash(&prefix, plen));
> + advertise_route_hash(&ar->addr, &ar->nexthop, plen));
> }
>
> smap_destroy(&port_mapping);
> diff --git a/controller/route.h b/controller/route.h
> index 5c800f4ab..cf84a3079 100644
> --- a/controller/route.h
> +++ b/controller/route.h
> @@ -79,6 +79,7 @@ struct advertise_datapath_entry {
> struct advertise_route_entry {
> struct hmap_node node;
> struct in6_addr addr;
> + struct in6_addr nexthop;
> unsigned int plen;
> unsigned int priority;
> };
> @@ -88,7 +89,9 @@ const struct sbrec_port_binding *route_exchange_find_port(
> const struct sbrec_chassis *chassis,
> const struct sbrec_port_binding *pb,
> const char **dynamic_routing_port_name);
> -uint32_t advertise_route_hash(const struct in6_addr *dst, unsigned int plen);
> +uint32_t advertise_route_hash(const struct in6_addr *dst,
> + const struct in6_addr *nexthop,
> + unsigned int plen);
> void route_run(struct route_ctx_in *, struct route_ctx_out *);
> void route_cleanup(struct hmap *announce_routes);
> uint32_t route_get_table_id(const struct sbrec_datapath_binding *);
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 18472d9cc..b037a3634 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -306,6 +306,8 @@ tests_ovstest_SOURCES += \
> controller/neighbor-table-notify.h \
> controller/neighbor.c \
> controller/neighbor.h \
> + controller/route-exchange-netlink.c \
> + controller/route-exchange-netlink.h \
> tests/test-ovn-netlink.c
> endif
>
> diff --git a/tests/system-ovn-netlink.at b/tests/system-ovn-netlink.at
> index 392973144..79988534f 100644
> --- a/tests/system-ovn-netlink.at
> +++ b/tests/system-ovn-netlink.at
> @@ -299,3 +299,203 @@ AT_CHECK([ovstest test-ovn-netlink host-if-monitor
> lo-test \
> 0
> ])
> AT_CLEANUP
> +
> +AT_SETUP([sync netlink routes - Blackhole])
> +AT_KEYWORDS([netlink-routes])
> +
> +table_id=100
> +
> +check ip link add vrf-$table_id type vrf table $table_id
> +on_exit 'ip link del vrf-$table_id'
> +check ip link set dev vrf-$table_id up
> +
> +check ip link add lo-test type dummy
> +on_exit 'ip link del lo-test'
> +check ip link set lo-test master vrf-$table_id
> +check ip link set lo-test address 00:00:00:00:00:10
> +check ip addr add 20.0.0.10/24 dev lo-test
> +check ip addr add fd20::10/64 dev lo-test
> +check ip link set up lo-test
> +
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id | sort], [0], [dnl
> +])
> +
> +check ip route add 10.10.10.1 via 20.0.0.1 vrf vrf-$table_id proto zebra
> +check ip route add fd20:100::10 via fd20::1 vrf vrf-$table_id proto zebra
> +check ip route add 10.10.20.0/24 via 20.0.0.1 vrf vrf-$table_id
> +
> +AS_BOX([No advertisement])
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id | sort], [0], [dnl
> +Route table_id=$table_id dst=10.10.10.1 plen=32 nexthop=20.0.0.1
> +Route table_id=$table_id dst=fd20:100::10 plen=128 nexthop=fd20::1
> +])
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +10.10.10.1 via 20.0.0.1 dev lo-test proto zebra
> +10.10.20.0/24 via 20.0.0.1 dev lo-test
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::10 via fd20::1 dev lo-test proto zebra metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Advertise just IPv4])
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id 192.168.100.0/24 | sort], [0], [dnl
> +Route table_id=$table_id dst=10.10.10.1 plen=32 nexthop=20.0.0.1
> +Route table_id=$table_id dst=fd20:100::10 plen=128 nexthop=fd20::1
> +])
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +10.10.10.1 via 20.0.0.1 dev lo-test proto zebra
> +10.10.20.0/24 via 20.0.0.1 dev lo-test
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +blackhole 192.168.100.0/24 proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::10 via fd20::1 dev lo-test proto zebra metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Advertise both IPv4 and IPv6])
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id 192.168.100.0/24 fd20:100::/64 | sort], [0], [dnl
> +Route table_id=$table_id dst=10.10.10.1 plen=32 nexthop=20.0.0.1
> +Route table_id=$table_id dst=fd20:100::10 plen=128 nexthop=fd20::1
> +])
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +10.10.10.1 via 20.0.0.1 dev lo-test proto zebra
> +10.10.20.0/24 via 20.0.0.1 dev lo-test
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +blackhole 192.168.100.0/24 proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::10 via fd20::1 dev lo-test proto zebra metric 1024 pref medium
> +blackhole fd20:100::/64 dev lo proto ovn metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Advertise just IPv4, should remove the IPv6])
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id 192.168.100.0/24 | sort], [0], [dnl
> +Route table_id=$table_id dst=10.10.10.1 plen=32 nexthop=20.0.0.1
> +Route table_id=$table_id dst=fd20:100::10 plen=128 nexthop=fd20::1
> +])
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +10.10.10.1 via 20.0.0.1 dev lo-test proto zebra
> +10.10.20.0/24 via 20.0.0.1 dev lo-test
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +blackhole 192.168.100.0/24 proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::10 via fd20::1 dev lo-test proto zebra metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([No advertisement should remove IPv4])
> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovstest test-ovn-netlink route-sync \
> + $table_id | sort], [0], [dnl
> +Route table_id=$table_id dst=10.10.10.1 plen=32 nexthop=20.0.0.1
> +Route table_id=$table_id dst=fd20:100::10 plen=128 nexthop=fd20::1
> +])
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +10.10.10.1 via 20.0.0.1 dev lo-test proto zebra
> +10.10.20.0/24 via 20.0.0.1 dev lo-test
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::10 via fd20::1 dev lo-test proto zebra metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AT_CLEANUP
> +
> +AT_SETUP([sync netlink routes - Nexthop])
> +AT_KEYWORDS([netlink-routes])
> +
> +table_id=100
> +
> +check ip link add vrf-$table_id type vrf table $table_id
> +on_exit 'ip link del vrf-$table_id'
> +check ip link set dev vrf-$table_id up
> +
> +check ip link add lo-test type dummy
> +on_exit 'ip link del lo-test'
> +check ip link set lo-test master vrf-$table_id
> +check ip link set lo-test address 00:00:00:00:00:10
> +check ip addr add 20.0.0.10/24 dev lo-test
> +check ip addr add fd20::10/64 dev lo-test
> +check ip link set up lo-test
> +
> +check ovstest test-ovn-netlink route-sync $table_id
> +
> +AS_BOX([Advertise IPv4 via IPv4])
> +check ovstest test-ovn-netlink route-sync $table_id \
> + 192.168.100.0/24 via 20.0.0.1
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +192.168.100.0/24 via 20.0.0.1 dev lo-test proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Advertise IPv4 via IPv4 and IPv6 via IPv6])
> +check ovstest test-ovn-netlink route-sync $table_id \
> + 192.168.100.0/24 via 20.0.0.1 \
> + fd20:100::/64 via fd20::1
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +192.168.100.0/24 via 20.0.0.1 dev lo-test proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::/64 via fd20::1 dev lo-test proto ovn metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Advertise IPv4 via IPv6 and IPv6 via IPv6])
> +check ovstest test-ovn-netlink route-sync $table_id \
> + 192.168.100.0/24 via fd20::1 \
> + fd20:100::/64 via fd20::1
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +192.168.100.0/24 via inet6 fd20::1 dev lo-test proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +fd20:100::/64 via fd20::1 dev lo-test proto ovn metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AS_BOX([Replace both with blackhole])
> +check ovstest test-ovn-netlink route-sync $table_id \
> + 192.168.100.0/24 \
> + fd20:100::/64
> +
> +OVN_ROUTE_EQUAL([vrf-$table_id], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +blackhole 192.168.100.0/24 proto ovn])
> +
> +OVN_ROUTE_V6_EQUAL([vrf-$table_id], [dnl
> +fd20::/64 dev lo-test proto kernel metric 256 pref medium
> +blackhole fd20:100::/64 dev lo proto ovn metric 1024 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +AT_CLEANUP
> diff --git a/tests/test-ovn-netlink.c b/tests/test-ovn-netlink.c
> index 84e24d0dd..701692fa6 100644
> --- a/tests/test-ovn-netlink.c
> +++ b/tests/test-ovn-netlink.c
> @@ -16,6 +16,7 @@
> #include <config.h>
>
> #include "openvswitch/hmap.h"
> +#include "route-table.h"
> #include "packets.h"
> #include "tests/ovstest.h"
> #include "tests/test-utils.h"
> @@ -24,6 +25,8 @@
> #include "controller/neighbor-exchange-netlink.h"
> #include "controller/neighbor-table-notify.h"
> #include "controller/neighbor.h"
> +#include "controller/route.h"
> +#include "controller/route-exchange-netlink.h"
>
> static void
> test_neighbor_sync(struct ovs_cmdl_context *ctx)
> @@ -177,6 +180,70 @@ test_host_if_monitor(struct ovs_cmdl_context *ctx)
> sset_destroy(&if_names);
> }
>
> +static void
> +test_route_sync(struct ovs_cmdl_context *ctx)
> +{
> + struct advertise_route_entry *e;
> + unsigned int shift = 1;
> +
> + unsigned int table_id;
> + if (!test_read_uint_value(ctx, shift++, "table id", &table_id)) {
> + return;
> + }
> +
> + struct hmap routes_to_advertise = HMAP_INITIALIZER(&routes_to_advertise);
> + struct vector received_routes =
> + VECTOR_EMPTY_INITIALIZER(struct re_nl_received_route_node);
> +
> + while (shift < ctx->argc) {
> + struct advertise_route_entry *ar = xzalloc(sizeof *ar);
> + if (!test_read_ipv6_cidr_mapped_value(ctx, shift++, "IP address",
> + &ar->addr, &ar->plen)) {
> + free(ar);
> + goto done;
> + }
> +
> + /* Check if we are adding only blackhole route. */
> + if (shift + 1 < ctx->argc) {
> + const char *via = test_read_value(ctx, shift++, "via");
> + if (strcmp(via, "via")) {
> + shift--;
> + continue;
> + }
> +
> + if (!test_read_ipv6_mapped_value(ctx, shift++, "IP address",
> + &ar->nexthop)) {
> + free(ar);
> + goto done;
> + }
> + }
> + hmap_insert(&routes_to_advertise, &ar->node,
> + advertise_route_hash(&ar->addr, &ar->nexthop, ar->plen));
> + }
> +
> + ovs_assert(re_nl_sync_routes(table_id, &routes_to_advertise,
> + &received_routes, NULL) == 0);
> +
> +
Nit: too many empty lines. One is probably enough.
> + struct ds msg = DS_EMPTY_INITIALIZER;
> +
> + struct re_nl_received_route_node *rr;
> + VECTOR_FOR_EACH_PTR (&received_routes, rr) {
> + re_route_format(&msg, table_id, &rr->prefix,
> + rr->plen, &rr->nexthop, 0);
> + printf("Route %s\n", ds_cstr(&msg));
> + ds_clear(&msg);
> + }
> +
> +done:
> + HMAP_FOR_EACH_POP (e, node, &routes_to_advertise) {
> + free(e);
> + }
> + hmap_destroy(&routes_to_advertise);
> + vector_destroy(&received_routes);
> + ds_destroy(&msg);
> +}
> +
> static void
> test_ovn_netlink(int argc, char *argv[])
> {
> @@ -186,6 +253,7 @@ test_ovn_netlink(int argc, char *argv[])
> {"neighbor-table-notify", NULL, 3, 4,
> test_neighbor_table_notify, OVS_RO},
> {"host-if-monitor", NULL, 2, 3, test_host_if_monitor, OVS_RO},
> + {"route-sync", NULL, 1, INT_MAX, test_route_sync, OVS_RO},
> {NULL, NULL, 0, 0, NULL, OVS_RO},
> };
> struct ovs_cmdl_context ctx;
> diff --git a/tests/test-utils.c b/tests/test-utils.c
> index e55557066..8edd521d0 100644
> --- a/tests/test-utils.c
> +++ b/tests/test-utils.c
> @@ -114,3 +114,21 @@ test_read_ipv6_mapped_value(struct ovs_cmdl_context
> *ctx, unsigned int index,
> }
> return true;
> }
> +
> +bool
> +test_read_ipv6_cidr_mapped_value(struct ovs_cmdl_context *ctx,
> + unsigned int index, const char *descr,
> + struct in6_addr *result, unsigned int *plen)
> +{
> + if (index >= ctx->argc) {
> + fprintf(stderr, "Missing %s argument\n", descr);
> + return false;
> + }
> +
> + const char *arg = ctx->argv[index];
> + if (!ip46_parse_cidr(arg, result, plen)) {
> + fprintf(stderr, "Invalid %s: %s\n", descr, arg);
> + return false;
> + }
> + return true;
> +}
> diff --git a/tests/test-utils.h b/tests/test-utils.h
> index fef67e799..e8176ce44 100644
> --- a/tests/test-utils.h
> +++ b/tests/test-utils.h
> @@ -35,4 +35,8 @@ bool test_read_eth_addr_value(struct ovs_cmdl_context *ctx,
> unsigned int index,
> bool test_read_ipv6_mapped_value(struct ovs_cmdl_context *ctx,
> unsigned int index, const char *descr,
> struct in6_addr *result);
> +bool test_read_ipv6_cidr_mapped_value(struct ovs_cmdl_context *ctx,
> + unsigned int index, const char *descr,
> + struct in6_addr *result,
> + unsigned int *plen);
> #endif /* tests/test-utils.h */
With the small issues I mentioned above addressed the patch looks good
to me. Feel free to add my ack if you're just changing what I listed above:
Acked-by: Dumitru Ceara <[email protected]>
Regards,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev