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]>
---
v2:
- Split src parameter into in and out versions in lookup function.
- Big refactor of lookup function.
- Added comment to explain their use.
- Changed unit test formatting.
- Added address family to router classifier.
---
lib/flow.c | 2 +-
lib/netdev-vport.c | 2 +-
lib/ovs-router.c | 115 +++++++++++++++++++----------------
lib/ovs-router.h | 2 +-
ofproto/ofproto-dpif-sflow.c | 2 +-
ofproto/ofproto-dpif-xlate.c | 30 +++++----
tests/ovs-router.at | 22 +++++++
7 files changed, 108 insertions(+), 67 deletions(-)
diff --git a/lib/flow.c b/lib/flow.c
index a59a25c46..d14603fe3 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -3731,7 +3731,7 @@ flow_get_tunnel_netdev(struct flow_tnl *tunnel)
return NULL;
}
- if (!ovs_router_lookup(0, &ip6, iface, NULL, &gw)) {
+ if (!ovs_router_lookup(0, &ip6, NULL, iface, NULL, &gw)) {
return NULL;
}
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index d11269d00..aa5223128 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -300,7 +300,7 @@ tunnel_check_status_change__(struct netdev_vport *netdev)
iface[0] = '\0';
route = &tnl_cfg->ipv6_dst;
mark = tnl_cfg->egress_pkt_mark;
- if (ovs_router_lookup(mark, route, iface, NULL, &gw)) {
+ if (ovs_router_lookup(mark, route, NULL, iface, NULL, &gw)) {
struct netdev *egress_netdev;
if (!netdev_open(iface, NULL, &egress_netdev)) {
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index c11a52764..30113592f 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -169,90 +169,94 @@ ovs_router_lookup_fallback(const struct in6_addr *ip6_dst,
return true;
}
+/* If the caller does not want to match on source prefix, src_in must be NULL.
+ * If src_in is not NULL, src_out will be set based on it instead of the route
+ * entry. */
bool
ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst,
+ const struct in6_addr *src_in,
char output_netdev[],
- struct in6_addr *src, struct in6_addr *gw)
+ struct in6_addr *src_out, struct in6_addr *gw)
{
- struct flow flow = {.ipv6_dst = *ip6_dst, .pkt_mark = mark};
- const struct in6_addr *from_src = src;
- const struct cls_rule *cr = NULL;
+ bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(ip6_dst);
+ ovs_be16 dl_type = is_ipv4 ? htons(ETH_TYPE_IP) : htons(ETH_TYPE_IPV6);
+ const struct cls_rule *cr;
struct router_rule *rule;
+ struct classifier *cls;
+ struct flow flow;
- 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_in) {
+ flow = (struct flow) {.ipv6_dst = *src_in, .pkt_mark = mark,
+ .dl_type = dl_type};
+ ovs_assert(is_ipv4 == IN6_IS_ADDR_V4MAPPED(src_in));
- if (!cls_local) {
+ cls = cls_find(CLS_LOCAL);
+
+ if (!cls) {
return false;
}
- cr_src = classifier_lookup(cls_local, OVS_VERSION_MAX, &flow_src,
+ cr = classifier_lookup(cls, OVS_VERSION_MAX, &flow,
NULL, NULL);
- if (!cr_src) {
+ if (!cr) {
return false;
}
- }
-
- if (!from_src) {
- if (IN6_IS_ADDR_V4MAPPED(ip6_dst)) {
- from_src = &in6addr_v4mapped_any;
+ if (src_out) {
+ *src_out = *src_in;
+ src_out = NULL;
+ }
+ } else {
+ if (is_ipv4) {
+ src_in = &in6addr_v4mapped_any;
} else {
- from_src = &in6addr_any;
+ src_in = &in6addr_any;
}
}
+ flow = (struct flow){.ipv6_dst = *ip6_dst, .pkt_mark = mark,
+ .dl_type = dl_type};
+
PVECTOR_FOR_EACH (rule, &rules) {
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;
}
matched = (!rule->src_prefix ||
- ipv6_addr_equals_masked(&rule->from_addr, from_src, plen));
+ ipv6_addr_equals_masked(&rule->from_addr, src_in, plen));
if (rule->invert) {
matched = !matched;
}
- if (matched) {
- struct classifier *cls = cls_find(rule->lookup_table);
+ if (!matched) {
+ continue;
+ }
+ cls = cls_find(rule->lookup_table);
- if (!cls) {
- /* A rule can be added before the table is created. */
- continue;
- }
- cr = classifier_lookup(cls, OVS_VERSION_MAX, &flow, NULL,
- NULL);
- 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.
- */
- if (!IN6_IS_ADDR_V4MAPPED(ip6_dst) ||
- IN6_IS_ADDR_V4MAPPED(&p->nw_addr)) {
- break;
- }
- }
+ if (!cls) {
+ /* A rule can be added before the table is created. */
+ continue;
+ }
+ cr = classifier_lookup(cls, OVS_VERSION_MAX, &flow, NULL,
+ NULL);
+ if (!cr) {
+ continue;
}
- }
- if (cr) {
struct ovs_router_entry *p = ovs_router_entry_cast(cr);
ovs_strlcpy(output_netdev, p->output_netdev, IFNAMSIZ);
*gw = p->gw;
- if (src && !ipv6_addr_is_set(src)) {
- *src = p->src_addr;
+ if (src_out) {
+ *src_out = p->src_addr;
}
return true;
}
- return ovs_router_lookup_fallback(ip6_dst, output_netdev, src, gw);
+
+ return ovs_router_lookup_fallback(ip6_dst, output_netdev, src_out, gw);
}
static void
@@ -268,14 +272,23 @@ static void rt_init_match(struct match *match, uint32_t
mark,
{
struct in6_addr dst;
struct in6_addr mask;
+ ovs_be16 dl_type;
+
+ if (IN6_IS_ADDR_V4MAPPED(ip6_dst)) {
+ dl_type = htons(ETH_TYPE_IP);
+ } else {
+ dl_type = htons(ETH_TYPE_IPV6);
+ }
mask = ipv6_create_mask(plen);
dst = ipv6_addr_bitand(ip6_dst, &mask);
memset(match, 0, sizeof *match);
match->flow.ipv6_dst = dst;
+ match->flow.dl_type = dl_type;
match->wc.masks.ipv6_dst = mask;
match->wc.masks.pkt_mark = UINT32_MAX;
+ match->wc.masks.dl_type = htons(UINT16_MAX);
match->flow.pkt_mark = mark;
}
@@ -1126,7 +1139,8 @@ static void
ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux OVS_UNUSED)
{
- struct in6_addr gw, src6 = in6addr_any;
+ struct in6_addr gw, src_out, src6;
+ struct in6_addr *src_in = NULL;
char src6_s[IPV6_SCAN_LEN + 1];
char iface[IFNAMSIZ];
struct in6_addr ip6;
@@ -1156,10 +1170,13 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int
argc,
if (is_ipv6) {
if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
ipv6_parse(src6_s, &src6)) {
+ src_in = &src6;
continue;
}
} else {
if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
+ in6_addr_set_mapped_ipv4(&src6, src);
+ src_in = &src6;
continue;
}
}
@@ -1168,15 +1185,11 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int
argc,
return;
}
- if (src) {
- in6_addr_set_mapped_ipv4(&src6, src);
- }
-
- if (ovs_router_lookup(mark, &ip6, iface, &src6, &gw)) {
+ if (ovs_router_lookup(mark, &ip6, src_in, iface, &src_out, &gw)) {
struct ds ds = DS_EMPTY_INITIALIZER;
ds_put_format(&ds, "src ");
- ipv6_format_mapped(&src6, &ds);
+ ipv6_format_mapped(&src_out, &ds);
ds_put_format(&ds, "\ngateway ");
ipv6_format_mapped(&gw, &ds);
ds_put_format(&ds, "\ndev %s\n", iface);
diff --git a/lib/ovs-router.h b/lib/ovs-router.h
index bd1ab7a9a..ff465a7c7 100644
--- a/lib/ovs-router.h
+++ b/lib/ovs-router.h
@@ -34,7 +34,7 @@ enum {
};
bool ovs_router_lookup(uint32_t mark, const struct in6_addr *ip_dst,
- char output_netdev[],
+ const struct in6_addr *src_in, char output_netdev[],
struct in6_addr *src, struct in6_addr *gw);
void ovs_router_init(void);
bool ovs_router_is_referenced(uint32_t table);
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index e043d7cbc..27e5955a6 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -482,7 +482,7 @@ sflow_choose_agent_address(const char *agent_device,
struct in6_addr gw, src = in6addr_any;
char name[IFNAMSIZ];
- if (ovs_router_lookup(0, &target_ip, name, &src, &gw)) {
+ if (ovs_router_lookup(0, &target_ip, NULL, name, &src, &gw)) {
ip = src;
goto success;
}
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index f2db4723b..cfeca24a3 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3679,16 +3679,28 @@ process_special(struct xlate_ctx *ctx, const struct
xport *xport)
static int
tnl_route_lookup_flow(const struct xlate_ctx *ctx,
const struct flow *oflow,
- struct in6_addr *ip, struct in6_addr *src,
+ struct in6_addr *ip, struct in6_addr *out_src,
struct xport **out_port)
{
- char out_dev[IFNAMSIZ];
struct xbridge *xbridge;
- struct in6_addr gw;
+ char out_dev[IFNAMSIZ];
+ struct in6_addr in_src;
+ struct in6_addr *_in_src = &in_src;
struct in6_addr dst;
+ struct in6_addr gw;
+
+ /* If tunnel src address is set, it overrides route lookup. */
+ if (oflow->tunnel.ip_src) {
+ in6_addr_set_mapped_ipv4(&in_src, oflow->tunnel.ip_src);
+ } else if (ipv6_addr_is_set(&oflow->tunnel.ipv6_src)) {
+ in_src = oflow->tunnel.ipv6_src;
+ } else {
+ _in_src = NULL;
+ }
dst = flow_tnl_dst(&oflow->tunnel);
- if (!ovs_router_lookup(oflow->pkt_mark, &dst, out_dev, src, &gw)) {
+ if (!ovs_router_lookup(oflow->pkt_mark, &dst, _in_src, out_dev, out_src,
+ &gw)) {
return -ENOENT;
}
@@ -3878,8 +3890,8 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct
xport *xport,
struct ovs_action_push_tnl tnl_push_data;
struct xport *out_dev = NULL;
ovs_be32 s_ip = 0, d_ip = 0;
- struct in6_addr s_ip6 = in6addr_any;
- struct in6_addr d_ip6 = in6addr_any;
+ struct in6_addr s_ip6;
+ struct in6_addr d_ip6;
struct eth_addr smac;
struct eth_addr dmac;
int err;
@@ -3897,12 +3909,6 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct
xport *xport,
memcpy(&old_base_flow, &ctx->base_flow, sizeof old_base_flow);
memcpy(&old_flow, &ctx->xin->flow, sizeof old_flow);
- if (flow->tunnel.ip_src) {
- in6_addr_set_mapped_ipv4(&s_ip6, flow->tunnel.ip_src);
- } else if (ipv6_addr_is_set(&flow->tunnel.ipv6_src)) {
- s_ip6 = flow->tunnel.ipv6_src;
- }
-
err = tnl_route_lookup_flow(ctx, flow, &d_ip6, &s_ip6, &out_dev);
if (err) {
put_cloned_drop_action(ctx->xbridge->ofproto, ctx->odp_actions,
diff --git a/tests/ovs-router.at b/tests/ovs-router.at
index b5282fd19..5f37e6c26 100644
--- a/tests/ovs-router.at
+++ b/tests/ovs-router.at
@@ -315,6 +315,28 @@ 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], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2.2.2.3/32 br0 192.0.2.1 table=15], [0], [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], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/rule/del from=all table=15], [0], [OK
+])
+
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.53.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev