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 | |
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>
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | lib/ovs-router.c | 86 | ||||
-rw-r--r-- | ofproto/ofproto-tnl-unixctl.man | 5 | ||||
-rw-r--r-- | tests/ovs-router.at | 80 |
4 files changed, 161 insertions, 13 deletions
@@ -6,6 +6,9 @@ Post-v3.1.0 * OVS now collects per-interface upcall statistics that can be obtained via 'ovs-appctl dpctl/show -s' or the interface's statistics column in OVSDB. Available with upstream kernel 6.2+. + - ovs-appctl: + * Add support for selecting the source address with the + 'ovs-appctl ovs/route/add' command. - ovs-ctl: * Added new options --[ovsdb-server|ovs-vswitchd]-umask=MODE to set umask value when starting OVS daemons. E.g., use --ovsdb-server-umask=0002 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 " diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man index 6ed7e7fce..a801cfdcc 100644 --- a/ofproto/ofproto-tnl-unixctl.man +++ b/ofproto/ofproto-tnl-unixctl.man @@ -1,8 +1,9 @@ .SS "OPENVSWITCH TUNNELING COMMANDS" These commands query and modify OVS tunnel components. . -.IP "\fBovs/route/add ip/plen output_bridge [gw] [pkt_mark=mark]\fR" -Adds ip/plen route to vswitchd routing table. output_bridge +.IP "\fBovs/route/add \fIip\fB/\fIplen\fB \fIoutput_bridge\fB \ +[\fIgw\fB] [pkt_mark=\fImark\fB] [src=\fIsrc_ip\fB]\fR" +Adds \fIip\fR/\fIplen\fR route to vswitchd routing table. \fIoutput_bridge\fR needs to be OVS bridge name. This command is useful if OVS cached routes does not look right. . diff --git a/tests/ovs-router.at b/tests/ovs-router.at index ec3b1dffe..b3314b3df 100644 --- a/tests/ovs-router.at +++ b/tests/ovs-router.at @@ -20,7 +20,7 @@ Invalid 'ip/plen' parameter ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 2.2.2.4/24 br0 pkt_mark=baz], [2], [], [dnl -Invalid pkt_mark or IP gateway +Invalid pkt_mark, IP gateway or src_ip ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [dnl @@ -31,6 +31,84 @@ User: 2.2.2.3/32 MARK 1 dev br0 SRC 2.2.2.2 OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([appctl - route/add with src - ipv4]) +AT_KEYWORDS([ovs_router]) +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.2/24], [0], [OK +]) +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.3/24], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.9.11/32 br0 src=192.168.9.3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.12/32 br0 192.168.9.1 src=192.168.9.3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.13/32 br0 192.168.9.1 pkt_mark=13 src=192.168.9.3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.14/32 br0 192.168.9.1 pkt_mark=14 src=192.168.9.2], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.15/32 br0 192.168.9.1 src=foo.bar.9.200], [2], [], [dnl +Invalid pkt_mark, IP gateway or src_ip +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.16/32 br0 192.168.9.1 src=192.168.9.200], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.17/32 br0 192.168.11.1 src=192.168.9.3], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.18/32 br0 src=192.168.9.3], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 192.168.10 | sort], [0], [dnl +User: 192.168.10.12/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3 +User: 192.168.10.13/32 MARK 13 dev br0 GW 192.168.9.1 SRC 192.168.9.3 +User: 192.168.10.14/32 MARK 14 dev br0 GW 192.168.9.1 SRC 192.168.9.2 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([appctl - route/add with src - ipv6]) +AT_KEYWORDS([ovs_router]) +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) +AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::2/64], [0], [OK +]) +AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::3/64], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:cafe::11/128 br0 src=2001:db8:cafe::3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::12/128 br0 2001:db8:cafe::1 src=2001:db8:cafe::3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::13/128 br0 2001:db8:cafe::1 pkt_mark=13 src=2001:db8:cafe::3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::14/128 br0 2001:db8:cafe::1 pkt_mark=14 src=2001:db8:cafe::2], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::15/128 br0 2001:db8:cafe::1 src=foo:bar:2001:db8:cafe], [2], [], [dnl +Invalid pkt_mark, IP gateway or src_ip +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::16/128 br0 2001:db8:cafe::1 src=2001:db8:cafe::200], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::17/128 br0 2001:db8:face::1 src=2001:db8:cafe::3], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::18/128 br0 src=2001:db8:cafe::3], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 2001:db8:beef | sort], [0], [dnl +User: 2001:db8:beef::12/128 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3 +User: 2001:db8:beef::13/128 MARK 13 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3 +User: 2001:db8:beef::14/128 MARK 14 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::2 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([appctl - route/lookup]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) |