diff options
author | Nobuhiro MIKI <nmiki@yahoo-corp.jp> | 2023-03-06 11:49:18 +0900 |
---|---|---|
committer | Ilya Maximets <i.maximets@ovn.org> | 2023-03-07 18:29:16 +0100 |
commit | b801f1aa001cf0537cc64b268a49c7988b78cbf5 (patch) | |
tree | 265f3e1b4aee4c97e1f497aa55156728a14d5b4c /lib | |
parent | 01acf09f746e4678e81b545b38ca682171628d02 (diff) | |
download | openvswitch-b801f1aa001cf0537cc64b268a49c7988b78cbf5.tar.gz |
ovs-router: Introduce src option in ovs/route/add command.
When adding a route with ovs/route/add command, the source address
in "ovs_router_entry" structure is always the FIRST address that the
interface has. See "ovs_router_get_netdev_source_address"
function for more information.
If an interface has multiple ipv4 and/or ipv6 addresses, there are use
cases where the user wants to control the source address. This patch
therefore addresses this issue by adding a src parameter.
Note that same constraints also exist when caching routes from
Kernel FIB with Netlink, but are not dealt with in this patch.
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Nobuhiro MIKI <nmiki@yahoo-corp.jp>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ovs-router.c | 86 |
1 files changed, 76 insertions, 10 deletions
diff --git a/lib/ovs-router.c b/lib/ovs-router.c index 02fce9095..3107f2d56 100644 --- a/lib/ovs-router.c +++ b/lib/ovs-router.c @@ -164,6 +164,46 @@ static void rt_init_match(struct match *match, uint32_t mark, match->flow.pkt_mark = mark; } +static int +verify_prefsrc(const struct in6_addr *ip6_dst, + const char output_bridge[], + struct in6_addr *prefsrc) +{ + struct in6_addr *mask, *addr6; + struct netdev *dev; + int err, n_in6, i; + + err = netdev_open(output_bridge, NULL, &dev); + if (err) { + return err; + } + + err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6); + if (err) { + goto out; + } + + for (i = 0; i < n_in6; i++) { + struct in6_addr a1, a2; + a1 = ipv6_addr_bitand(ip6_dst, &mask[i]); + a2 = ipv6_addr_bitand(prefsrc, &mask[i]); + + /* Check that the interface has "prefsrc" and + * it is same broadcast domain with "ip6_dst". */ + if (IN6_ARE_ADDR_EQUAL(prefsrc, &addr6[i]) && + IN6_ARE_ADDR_EQUAL(&a1, &a2)) { + goto out; + } + } + err = ENOENT; + +out: + free(addr6); + free(mask); + netdev_close(dev); + return err; +} + int ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst, const char output_bridge[], @@ -217,8 +257,12 @@ static int ovs_router_insert__(uint32_t mark, uint8_t priority, bool local, const struct in6_addr *ip6_dst, uint8_t plen, const char output_bridge[], - const struct in6_addr *gw) + const struct in6_addr *gw, + const struct in6_addr *ip6_src) { + int (*get_src_addr)(const struct in6_addr *ip6_dst, + const char output_bridge[], + struct in6_addr *prefsrc); const struct cls_rule *cr; struct ovs_router_entry *p; struct match match; @@ -236,11 +280,17 @@ ovs_router_insert__(uint32_t mark, uint8_t priority, bool local, p->plen = plen; p->local = local; p->priority = priority; - err = ovs_router_get_netdev_source_address(ip6_dst, output_bridge, - &p->src_addr); + + if (ipv6_addr_is_set(ip6_src)) { + p->src_addr = *ip6_src; + get_src_addr = verify_prefsrc; + } else { + get_src_addr = ovs_router_get_netdev_source_address; + } + + err = get_src_addr(ip6_dst, output_bridge, &p->src_addr); if (err && ipv6_addr_is_set(gw)) { - err = ovs_router_get_netdev_source_address(gw, output_bridge, - &p->src_addr); + err = get_src_addr(gw, output_bridge, &p->src_addr); } if (err) { struct ds ds = DS_EMPTY_INITIALIZER; @@ -274,7 +324,8 @@ ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen, { if (use_system_routing_table) { uint8_t priority = local ? plen + 64 : plen; - ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw); + ovs_router_insert__(mark, priority, local, ip_dst, plen, + output_bridge, gw, &in6addr_any); } } @@ -341,10 +392,13 @@ static void ovs_router_add(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { + struct in6_addr src6 = in6addr_any; struct in6_addr gw6 = in6addr_any; + char src6_s[IPV6_SCAN_LEN + 1]; struct in6_addr ip6; uint32_t mark = 0; unsigned int plen; + ovs_be32 src = 0; ovs_be32 gw = 0; bool is_ipv6; ovs_be32 ip; @@ -370,24 +424,36 @@ ovs_router_add(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)) { + continue; + } if (ipv6_parse(argv[i], &gw6)) { continue; } } else { + if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) { + continue; + } if (ip_parse(argv[i], &gw)) { continue; } } - unixctl_command_reply_error(conn, "Invalid pkt_mark or IP gateway"); + unixctl_command_reply_error(conn, + "Invalid pkt_mark, IP gateway or src_ip"); return; } if (gw) { in6_addr_set_mapped_ipv4(&gw6, gw); } + if (src) { + in6_addr_set_mapped_ipv4(&src6, src); + } - err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6); + err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], + &gw6, &src6); if (err) { unixctl_command_reply_error(conn, "Error while inserting route."); } else { @@ -538,8 +604,8 @@ ovs_router_init(void) classifier_init(&cls, NULL); unixctl_command_register("ovs/route/add", "ip/plen output_bridge [gw] " - "[pkt_mark=mark]", - 2, 4, ovs_router_add, NULL); + "[pkt_mark=mark] [src=src_ip]", + 2, 5, ovs_router_add, NULL); unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL); unixctl_command_register("ovs/route/del", "ip/plen " |