On 7 Feb 2023, at 7:48, Nobuhiro MIKI wrote:

> 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.

Thanks for this patch! Can you also update the man pages for routes, i.e. 
ofproto-tnl-unixctl.man?

See some more comments inline below.

Cheers,

Eelco

> Signed-off-by: Nobuhiro MIKI <nm...@yahoo-corp.jp>
> ---
>  NEWS                |  2 ++
>  lib/ovs-router.c    | 79 ++++++++++++++++++++++++++++++++++-----------
>  tests/ovs-router.at | 46 ++++++++++++++++++++++++++
>  3 files changed, 109 insertions(+), 18 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index fe6055a2700b..929437733e3d 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -4,6 +4,8 @@ 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:
> +     * "ovs-appctl ovs/route/add" command can now support "src" option.


* Add support for selecting the source address with the “ovs-appctl 
ovs/route/add" command.


>
>  v3.1.0 - xx xxx xxxx
> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
> index 5d0fbd503e9e..fc95a91012f9 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -217,12 +217,13 @@ 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)
>  {
>      const struct cls_rule *cr;
>      struct ovs_router_entry *p;
>      struct match match;
> -    int err;
> +    int err = 0;
>
>      rt_init_match(&match, mark, ip6_dst, plen);
>
> @@ -236,8 +237,14 @@ 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;

We need some verification, as any IP is accepted now. See the test cases below.

> +    } else {
> +        err = ovs_router_get_netdev_source_address(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);
> @@ -274,7 +281,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);
>      }
>  }
>
> @@ -342,6 +350,7 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
>                const char *argv[], void *aux OVS_UNUSED)
>  {
>      struct in6_addr gw6 = in6addr_any;
> +    struct in6_addr src6 = in6addr_any;
>      struct in6_addr ip6;
>      uint32_t mark = 0;
>      unsigned int plen;
> @@ -350,11 +359,27 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
>
>      if (scan_ipv4_route(argv[1], &ip, &plen)) {
>          ovs_be32 gw = 0;
> +        ovs_be32 src = 0;
>
>          if (argc > 3) {
> -            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
> +            if (!ovs_scan(argv[3], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src)) &&
> +                !ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
>                  !ip_parse(argv[3], &gw)) {
> -                unixctl_command_reply_error(conn, "Invalid pkt_mark or 
> gateway");
> +                unixctl_command_reply_error(
> +                    conn, "Invalid src, pkt_mark or gateway");
> +                return;
> +            }
> +        }
> +        if (argc > 4) {
> +            if (!ovs_scan(argv[4], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src)) &&
> +                !ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
> +                unixctl_command_reply_error(conn, "Invalid src or pkt_mark");
> +                return;
> +            }
> +        }
> +        if (argc > 5) {
> +            if (!ovs_scan(argv[5], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
> +                unixctl_command_reply_error(conn, "Invalid src");
>                  return;
>              }
>          }
> @@ -362,12 +387,35 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
>          if (gw) {
>              in6_addr_set_mapped_ipv4(&gw6, gw);
>          }
> +        if (src) {
> +            in6_addr_set_mapped_ipv4(&src6, src);
> +        }
>          plen += 96;
>      } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
> +        char src6_s[IPV6_SCAN_LEN + 1];
> +
>          if (argc > 3) {
> -            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
> +            if (!(ovs_scan(argv[3], "src="IPV6_SCAN_FMT, src6_s) &&
> +                  ipv6_parse(src6_s, &src6)) &&
> +                !ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
>                  !ipv6_parse(argv[3], &gw6)) {
> -                unixctl_command_reply_error(conn, "Invalid pkt_mark or IPv6 
> gateway");
> +                unixctl_command_reply_error(
> +                    conn, "Invalid src, pkt_mark or IPv6 gateway");
> +                return;
> +            }
> +        }
> +        if (argc > 4) {
> +            if (!(ovs_scan(argv[4], "src="IPV6_SCAN_FMT, src6_s) &&
> +                  ipv6_parse(src6_s, &src6)) &&
> +                !ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
> +                unixctl_command_reply_error(conn, "Invalid src or pkt_mark");
> +                return;
> +            }
> +        }
> +        if (argc > 5) {
> +            if (!(ovs_scan(argv[5], "src="IPV6_SCAN_FMT, src6_s) &&
> +                  ipv6_parse(src6_s, &src6))) {
> +                unixctl_command_reply_error(conn, "Invalid src");
>                  return;
>              }
>          }

I think this function needs some proper refactoring, as it has become very 
messy.
Guess we should first parse all arguments, and then do the verification.

> @@ -375,14 +423,9 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
>          unixctl_command_reply_error(conn, "Invalid parameters");
>          return;
>      }
> -    if (argc > 4) {
> -        if (!ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
> -            unixctl_command_reply_error(conn, "Invalid pkt_mark");
> -            return;
> -        }
> -    }
>
> -    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 {
> @@ -533,8 +576,8 @@ ovs_router_init(void)
>          classifier_init(&cls, NULL);
>          unixctl_command_register("ovs/route/add",
>                                   "ip_addr/prefix_len out_br_name [gw] "
> -                                 "[pkt_mark=mark]",
> -                                 2, 4, ovs_router_add, NULL);
> +                                 "[pkt_mark=mark] [src=src_ip_addr]",
> +                                 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_addr/prefix_len "
> diff --git a/tests/ovs-router.at b/tests/ovs-router.at
> index 6dacc2954bc6..d4e39b4dd995 100644
> --- a/tests/ovs-router.at
> +++ b/tests/ovs-router.at
> @@ -12,6 +12,52 @@ AT_CHECK([ovs-appctl ovs/route/add 1.1.1.0/24 br0 
> 2.2.2.10], [0], [OK
>  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.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
> +])

I’ve added the below, and the test case is also passing (for this AT_CHECK).
We might need to add a check, see the earlier comment.


AT_CHECK([ovs-appctl ovs/route/add 192.168.10.14/32 br0 192.168.9.1 pkt_mark=14 
src=192.168.9.20], [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 src or pkt_mark
> +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: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
> +])

Do we need an invalid and unknown source IP added to this test case also?

> +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])
> -- 
> 2.31.1
>
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to