On Fri, Feb 06, 2026 at 12:05:16PM -0500, Mike Pattrick via dev wrote:
> The source address parameter in ovs_router_lookup is both an input and
> an output. However, the interface was complex. The caller could
> inadvertently set a search over v4 or v6 rules based on if the source
> address was initialized to in6addr_any or in6addr_v4mapped_any. The
> lookup function even used these two values interchangeably.
>
> This patch uses dst address to determine if the lookup is v4 or v6, and
> considers both v6_any and v4mapped_any to be the null value equally. Now
> if the caller just wants src as output, they can initialize it to v6_any
> and lookup will still work correctly.
>
> Fixes: dc14e92bcc25 ("route-table: Introduce multi-table route lookup.")
> Signed-off-by: Mike Pattrick <[email protected]>
I was reading this same function yesterday and was scratching my head
for the same reason, thanks for posting this patch.
I think it improves clarity and reduces possible pitfals. Only have some
nits and unrelated comments.
> ---
> lib/ovs-router.c | 47 ++++++++++++++++++++++++---------------------
> lib/packets.h | 5 +++++
> tests/ovs-router.at | 26 +++++++++++++++++++++++++
> 3 files changed, 56 insertions(+), 22 deletions(-)
>
> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
> index c11a52764..3ac421915 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -175,28 +175,31 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr
> *ip6_dst,
> struct in6_addr *src, struct in6_addr *gw)
Because of the complexity of the API, it might be worth considering some
ways to make it clearer, maybe a function-level comment would be a good
first start. This is unrelated to your patch though.
> {
> struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
> + bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
> const struct in6_addr *from_src = src;
> const struct cls_rule *cr = NULL;
> struct router_rule *rule;
> + bool is_any = true;
>
> - if (src && ipv6_addr_is_set(src)) {
> - struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
> - struct classifier *cls_local = cls_find(CLS_LOCAL);
> - const struct cls_rule *cr_src;
> + if (src) {
> + if (ipv4_addr_is_set(src) || ipv6_addr_is_set(src)) {
> + struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark};
> + struct classifier *cls_local = cls_find(CLS_LOCAL);
> + const struct cls_rule *cr_src;
>
> - if (!cls_local) {
> - return false;
> - }
> + if (!cls_local) {
> + return false;
> + }
>
> - cr_src = classifier_lookup(cls_local, OVS_VERSION_MAX, &flow_src,
> - NULL, NULL);
> - if (!cr_src) {
> - return false;
> + cr_src = classifier_lookup(cls_local, OVS_VERSION_MAX, &flow_src,
> + NULL, NULL);
> + if (!cr_src) {
> + return false;
> + }
> + is_any = false;
> }
> - }
Also unrelated to your patch but moving this block to an independent
function called "check_addr_is_local" or something like that could help
reduce the length and improve readability.
> -
> - if (!from_src) {
> - if (IN6_IS_ADDR_V4MAPPED(ip6_dst)) {
> + } else {
> + if (is_ipv4) {
> from_src = &in6addr_v4mapped_any;
> } else {
> from_src = &in6addr_any;
> @@ -207,8 +210,7 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr
> *ip6_dst,
> uint8_t plen = rule->ipv4 ? rule->src_prefix + 96 : rule->src_prefix;
> bool matched;
>
> - if ((IN6_IS_ADDR_V4MAPPED(from_src) && !rule->ipv4) ||
> - (!IN6_IS_ADDR_V4MAPPED(from_src) && rule->ipv4)) {
> + if (is_ipv4 != rule->ipv4) {
> continue;
> }
>
> @@ -231,12 +233,13 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr
> *ip6_dst,
> if (cr) {
> struct ovs_router_entry *p = ovs_router_entry_cast(cr);
> /* Avoid matching mapped IPv4 of a packet against default
> IPv6
> - * route entry. Either packet dst is IPv6 or both packet and
> - * route entry dst are mapped IPv4.
> + * route entry. IPv6 status of the packet dst and route dst
> + * must be the same.
> */
> - if (!IN6_IS_ADDR_V4MAPPED(ip6_dst) ||
> - IN6_IS_ADDR_V4MAPPED(&p->nw_addr)) {
> + if (is_ipv4 == IN6_IS_ADDR_V4MAPPED(&p->nw_addr)) {
> break;
> + } else {
> + cr = NULL;
> }
> }
> }
> @@ -247,7 +250,7 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr
> *ip6_dst,
>
> ovs_strlcpy(output_netdev, p->output_netdev, IFNAMSIZ);
> *gw = p->gw;
> - if (src && !ipv6_addr_is_set(src)) {
> + if (src && is_any) {
> *src = p->src_addr;
> }
> return true;
> diff --git a/lib/packets.h b/lib/packets.h
> index 61666f3ad..7e2e6be72 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -1202,6 +1202,11 @@ static inline bool ipv6_is_all_hosts(const struct
> in6_addr *addr) {
> return ipv6_addr_equals(addr, &in6addr_all_hosts);
> }
>
> +static inline bool ipv4_addr_is_set(const struct in6_addr *addr) {
> + return IN6_IS_ADDR_V4MAPPED(addr) &&
> + !ipv6_addr_equals(addr, &in6addr_v4mapped_any);
> +}
> +
> static inline bool ipv6_addr_is_set(const struct in6_addr *addr) {
> return !ipv6_addr_equals(addr, &in6addr_any);
> }
> diff --git a/tests/ovs-router.at b/tests/ovs-router.at
> index b5282fd19..258ff5c80 100644
> --- a/tests/ovs-router.at
> +++ b/tests/ovs-router.at
> @@ -315,6 +315,32 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1
> type=dummy])
> AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK
> ])
>
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=all table=15], [0], [dnl
> +OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/add 2.2.2.3/32 br0 192.0.2.1 table=15], [0],
> [dnl
> +OK
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl
> +Cached: 192.0.2.0/24 dev br0 SRC 192.0.2.1
> +Cached: 192.0.2.1/32 dev br0 SRC 192.0.2.1 local
> +User: 2.2.2.3/32 dev br0 GW 192.0.2.1 SRC 192.0.2.1 table 15
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/lookup 2.2.2.3], [0], [dnl
> +src 192.0.2.1
> +gateway 192.0.2.1
> +dev br0
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/del 2.2.2.3/32 table=15], [0], [dnl
> +OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/del from=all table=15], [0], [dnl
> +OK
> +])
> +
nit: the rest of this test puts "OK" in the same line, maybe we could
keep it that way of coherence?
> AT_CHECK([ovs-appctl ovs/route/add 10.1.1.0/24 br0 192.0.2.2 table=11], [0],
> [OK
> ])
> AT_CHECK([ovs-appctl ovs/route/add 10.2.2.0/24 br0 192.0.2.2 table=12], [0],
> [OK
> --
> 2.52.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev