On 7/23/25 3:12 PM, Dima Chumak via dev wrote:
> Add two new commands `ovs-appctl ovs/router/rule/add` and
> `ovs-appctl ovs/router/rule/del` for adding and deleting user routing

s/router/route/  here, in the subject line and in a few places in the patch.

> rules in OVS:
> 
>   ovs/route/rule/add      [not] from=<all|IP/PLEN> [prio=NUM] 
> table=<local|main|default|ID>
>   ovs/route/rule/del      [not] from=<all|IP/PLEN> [prio=NUM] 
> table=<local|main|default|ID>

Same comemnts for consistency, should probably keep formatting
for all the commands more or less the same.

> 
> Examples for adding user rules:
> 
>   ovs-appctl ovs/route/rule/add from=all table=10
>   ovs-appctl ovs/route/rule/add from=all prio=10 table=10
>   ovs-appctl ovs/route/rule/add not from=10.0.0.0/24 table=10
> 
> Examples for deleting user rules:
> 
>   ovs-appctl ovs/route/rule/del from=all prio=10 table=10
>   ovs-appctl ovs/route/rule/del from=all table=10
>   ovs-appctl ovs/route/rule/del not from=10.0.0.0/24 table=10
> 
> When adding a new rule, if priority is not specified, the lowest unused
> priority will be selected automatically. Multiple rules with the same
> priority may exist. User added rules and kernel cached rules co-exist
> together.
> 
> Only user added rules can be deleted with 'ovs/route/rule/del' command.
> For kernel cached rules they must be deleted using `ip rule del` system
> command. If priority is not specified when deleting a rule, the first
> matching rule will be selected for deletion.
> 
> Signed-off-by: Dima Chumak <[email protected]>
> ---
>  Documentation/howto/userspace-tunneling.rst |   5 +
>  NEWS                                        |   2 +
>  lib/ovs-router.c                            | 212 +++++++++++++++++++-
>  lib/ovs-router.h                            |   9 +-
>  lib/route-table.c                           |   6 +-
>  ofproto/ofproto-tnl-unixctl.man             |  14 ++
>  tests/ovs-router.at                         |  72 ++++++-
>  tests/tunnel-push-pop.at                    | 119 +++++++++++
>  8 files changed, 422 insertions(+), 17 deletions(-)
> 
> diff --git a/Documentation/howto/userspace-tunneling.rst 
> b/Documentation/howto/userspace-tunneling.rst
> index 5bebbce69e4a..62f87c426b16 100644
> --- a/Documentation/howto/userspace-tunneling.rst
> +++ b/Documentation/howto/userspace-tunneling.rst
> @@ -207,6 +207,11 @@ To see all routes configured::
>  
>      $ ovs-appctl ovs/route/show [table=ID|all]
>  
> +To add/delete router rule::
> +
> +    $ ovs-appctl ovs/route/rule/add [not] from=<all|IP/PLEN> [prio=NUM] 
> table=<local|main|default|ID>
> +    $ ovs-appctl ovs/route/rule/del [not] from=<all|IP/PLEN> [prio=NUM] 
> table=<local|main|default|ID>
> +
>  To see all router rules configured::
>  
>      $ ovs-appctl ovs/router/rule/show
> diff --git a/NEWS b/NEWS
> index a58baa0a8365..440c4cfc5d49 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -31,6 +31,8 @@ v3.6.0 - xx xxx xxxx
>         add/delete a route from a specific OVS table.
>       * 'ovs/route/lookup': added new option, src=IP, to perform lookup with
>         a specific source IP address.
> +     * Added new sub-commands, ovs/router/rule/{add,del}, to add and remove
> +       user routing rules in OVS.
>     - ovs-vsctl:
>       * Now exits with error code 160 (ERROR_BAD_ARGUMENTS) on Windows and
>         65 (EX_DATAERR) on other platforms if it fails while waiting for
> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
> index a3df5c6fc2cc..11d1e7b2d4e1 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -60,6 +60,7 @@ struct clsmap_node {
>  struct router_rule {
>      uint32_t prio;
>      bool invert;
> +    bool user;
>      uint8_t src_len;
>      struct in6_addr from_addr;
>      uint32_t lookup_table;
> @@ -893,6 +894,7 @@ ovs_router_rules_show_json(struct json *rule_entries)
>          struct json *entry = json_object_create();
>  
>          json_object_put(entry, "priority", json_integer_create(rule->prio));
> +        json_object_put(entry, "user", json_integer_create(rule->user));
>          json_object_put(entry, "invert", json_boolean_create(rule->invert));
>          json_object_put(entry, "src_len", 
> json_integer_create(rule->src_len));
>          json_object_put(entry, "lookup",
> @@ -932,6 +934,11 @@ ovs_router_rules_show_text(struct ds *ds)
>      struct router_rule *rule;
>  
>      PVECTOR_FOR_EACH (rule, &rules) {
> +        if (rule->user) {
> +            ds_put_format(ds, "User: ");
> +        } else {
> +            ds_put_format(ds, "Cached: ");
> +        }
>          ds_put_format(ds, "%u: ", rule->prio);
>          if (rule->invert) {
>              ds_put_format(ds, "not ");
> @@ -974,6 +981,156 @@ ovs_router_rules_show(struct unixctl_conn *conn, int 
> argc OVS_UNUSED,
>      }
>  }
>  
> +static void
> +ovs_router_rule_add_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
> +                        const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
> +{
> +    unsigned int src_len = 0;
> +    struct in6_addr from;
> +    bool invert = false;
> +    uint32_t prio = 0;
> +    uint32_t table;
> +    ovs_be32 ip;
> +    int i = 1;
> +
> +    if (ovs_scan(argv[i], "not")) {
> +        invert = true;
> +        i++;
> +    }
> +
> +    if (ovs_scan(argv[i], "from=all")) {
> +        from = in6addr_any;
> +    } else if (ovs_scan(argv[i], "from=")) {
> +        const char *arg = &argv[i][strlen("from=")];
> +
> +        if (scan_ipv4_route(arg, &ip, &src_len)) {
> +            in6_addr_set_mapped_ipv4(&from, ip);
> +            src_len += 96;
> +        } else if (!scan_ipv6_route(arg, &from, &src_len)) {
> +            unixctl_command_reply_error(conn, "Invalid from=ip/plen");
> +            return;
> +        }
> +    } else {
> +        unixctl_command_reply_error(conn, "Invalid 'from' parameter");
> +        return;
> +    }
> +    if (argc <= ++i) {
> +        unixctl_command_reply_error(conn, "Not enough arguments");
> +        return;
> +    }
> +
> +    if (ovs_scan(argv[i], "prio=%"SCNi32, &prio)) {
> +        if (argc <= ++i) {
> +            unixctl_command_reply_error(conn, "Not enough arguments");
> +            return;
> +        }
> +    }
> +
> +    if (ovs_scan(argv[i], "table=local")) {
> +        table = CLS_LOCAL;
> +    } else if (ovs_scan(argv[i], "table=main")) {
> +        table = CLS_MAIN;
> +    } else if (ovs_scan(argv[i], "table=default")) {
> +        table = CLS_DEFAULT;
> +    } else if (!ovs_scan(argv[i], "table=%"SCNi32, &table)) {

Same SCNu/PRIu comments here and below.

> +        unixctl_command_reply_error(conn, "Invalid 'table' format");
> +        return;
> +    }
> +
> +    if (!prio) {
> +        struct router_rule *rule;
> +        uint32_t prev_prio = 0;
> +
> +        PVECTOR_FOR_EACH (rule, &rules) {
> +            if ((!prio && rule->prio) ||
> +                (rule->prio - prev_prio > 1)) {
> +                prio = rule->prio - 1;
> +            }
> +            prev_prio = rule->prio;
> +        }
> +    }
> +
> +    ovs_mutex_lock(&mutex);
> +    ovs_router_rule_add(prio, invert, true, src_len, &from, table);
> +    ovs_mutex_unlock(&mutex);
> +    unixctl_command_reply(conn, "OK");
> +}
> +
> +static void
> +ovs_router_rule_del_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
> +                        const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
> +{
> +    unsigned int src_len = 0;
> +    struct in6_addr from;
> +    bool invert = false;
> +    uint32_t prio = 0;
> +    uint32_t table;
> +    ovs_be32 ip;
> +    int i = 1;
> +    int err;
> +
> +    if (ovs_scan(argv[i], "not")) {
> +        invert = true;
> +        i++;
> +    }
> +
> +    if (ovs_scan(argv[i], "from=all")) {
> +        from = in6addr_any;
> +    } else if (ovs_scan(argv[i], "from=")) {
> +        const char *arg = &argv[i][strlen("from=")];
> +
> +        if (scan_ipv4_route(arg, &ip, &src_len)) {
> +            in6_addr_set_mapped_ipv4(&from, ip);
> +            src_len += 96;
> +        } else if (!scan_ipv6_route(arg, &from, &src_len)) {
> +            unixctl_command_reply_error(conn, "Invalid from=ip/plen");
> +            return;
> +        }
> +    } else {
> +        unixctl_command_reply_error(conn, "Invalid 'from' parameter");
> +        return;
> +    }
> +    if (argc <= ++i) {
> +        unixctl_command_reply_error(conn, "Not enough arguments");
> +        return;
> +    }
> +
> +    if (ovs_scan(argv[i], "prio=%"SCNi32, &prio)) {
> +        if (argc <= ++i) {
> +            unixctl_command_reply_error(conn, "Not enough arguments");
> +            return;
> +        }
> +    }
> +
> +    if (ovs_scan(argv[i], "table=local")) {
> +        table = CLS_LOCAL;
> +    } else if (ovs_scan(argv[i], "table=main")) {
> +        table = CLS_MAIN;
> +    } else if (ovs_scan(argv[i], "table=default")) {
> +        table = CLS_DEFAULT;
> +    } else if (!ovs_scan(argv[i], "table=%"SCNi32, &table)) {
> +        unixctl_command_reply_error(conn, "Invalid 'table' format");
> +        return;
> +    }
> +
> +    ovs_mutex_lock(&mutex);
> +    err = ovs_router_rule_del(prio, invert, src_len, &from, table);
> +    ovs_mutex_unlock(&mutex);
> +
> +    if (err) {
> +        struct ds ds = DS_EMPTY_INITIALIZER;
> +
> +        ds_put_format(&ds, "Failed to delete router rule: %d", err);
> +        if (err == -ENOENT) {
> +            ds_put_format(&ds, " (No such entry)");
> +        }

Should we just use ovs_strerror in case of any error?

> +        unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
> +        ds_destroy(&ds);
> +    } else {
> +        unixctl_command_reply(conn, "OK");
> +    }
> +}
> +
>  static void
>  ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
>                        const char *argv[], void *aux OVS_UNUSED)
> @@ -1063,9 +1220,9 @@ static void
>  init_standard_rules(void)
>  {
>      /* Add default rules using same priorities as Linux kernel does. */
> -    ovs_router_rule_add(0, false, 0, &in6addr_any, CLS_LOCAL);
> -    ovs_router_rule_add(0x7FFE, false, 0, &in6addr_any, CLS_MAIN);
> -    ovs_router_rule_add(0x7FFF, false, 0, &in6addr_any, CLS_DEFAULT);
> +    ovs_router_rule_add(0, false, false, 0, &in6addr_any, CLS_LOCAL);
> +    ovs_router_rule_add(0x7FFE, false, false, 0, &in6addr_any, CLS_MAIN);
> +    ovs_router_rule_add(0x7FFF, false, false, 0, &in6addr_any, CLS_DEFAULT);
>  }
>  
>  static void
> @@ -1075,22 +1232,26 @@ rule_destroy_cb(struct router_rule *rule)
>  }
>  
>  void
> -ovs_router_rules_flush(void)
> +ovs_router_rules_flush(bool flush_all)
>  {
>      struct router_rule *rule;
>  
> +    ovs_mutex_lock(&mutex);
>      PVECTOR_FOR_EACH (rule, &rules) {
> -        pvector_remove(&rules, rule);
> -        ovsrcu_postpone(rule_destroy_cb, rule);
> +        if (flush_all || !rule->user) {
> +            pvector_remove(&rules, rule);
> +            ovsrcu_postpone(rule_destroy_cb, rule);
> +        }
>      }
>      pvector_publish(&rules);
>      init_standard_rules();
> +    ovs_mutex_unlock(&mutex);
>  }
>  
>  static void
>  ovs_router_flush_handler(void *aux OVS_UNUSED)
>  {
> -    ovs_router_rules_flush();
> +    ovs_router_rules_flush(true);
>      ovs_router_flush(true);
>  
>      ovs_mutex_lock(&mutex);
> @@ -1137,13 +1298,14 @@ rule_pvec_prio(uint32_t prio)
>  }
>  
>  void
> -ovs_router_rule_add(uint32_t prio, bool invert, uint8_t src_len,
> +ovs_router_rule_add(uint32_t prio, bool invert, bool user, uint8_t src_len,
>                      const struct in6_addr *from, uint32_t lookup_table)
>  {
>      struct router_rule *rule = xzalloc(sizeof *rule);
>  
>      rule->prio = prio;
>      rule->invert = invert;
> +    rule->user = user;
>      rule->src_len = src_len;
>      rule->from_addr = *from;
>      rule->lookup_table = lookup_table;
> @@ -1152,6 +1314,32 @@ ovs_router_rule_add(uint32_t prio, bool invert, 
> uint8_t src_len,
>      pvector_publish(&rules);
>  }
>  
> +int
> +ovs_router_rule_del(uint32_t prio, bool invert, uint8_t src_len,
> +                    const struct in6_addr *from, uint32_t lookup_table)
> +{
> +    struct router_rule *rule;
> +
> +    PVECTOR_FOR_EACH (rule, &rules) {
> +        if (prio && rule->prio > prio) {
> +            break;
> +        }
> +        if (rule->user &&
> +            rule->invert == invert &&
> +            (!prio || rule->prio == prio) &&
> +            rule->src_len == src_len &&
> +            ipv6_addr_equals(&rule->from_addr, from) &&
> +            rule->lookup_table == lookup_table) {

This is a little hard to read, I'd suggest moving && to the next line,
to at least visually split the conditions from the body.

> +            pvector_remove(&rules, rule);
> +            ovsrcu_postpone(rule_destroy_cb, rule);
> +            pvector_publish(&rules);
> +            return 0;
> +        }
> +    }
> +
> +    return -ENOENT;
> +}
> +
>  void
>  ovs_router_init(void)
>  {
> @@ -1181,6 +1369,14 @@ ovs_router_init(void)
>                                   ovs_router_lookup_cmd, NULL);
>          unixctl_command_register("ovs/route/rule/show", "", 0, 0,
>                                   ovs_router_rules_show, NULL);
> +        unixctl_command_register("ovs/route/rule/add",
> +                                 "[not] from=<all|IP/PLEN> [prio=NUM] "
> +                                 "table=<local|main|default|ID>",
> +                                 2, 5, ovs_router_rule_add_cmd, NULL);
> +        unixctl_command_register("ovs/route/rule/del",
> +                                 "[not] from=<all|IP/PLEN> [prio=NUM] "
> +                                 "table=<local|main|default|ID>",
> +                                 2, 5, ovs_router_rule_del_cmd, NULL);
>          ovsthread_once_done(&once);
>      }
>  }
> diff --git a/lib/ovs-router.h b/lib/ovs-router.h
> index a6e100e2f79d..c7d55f30554e 100644
> --- a/lib/ovs-router.h
> +++ b/lib/ovs-router.h
> @@ -50,10 +50,13 @@ void ovs_router_force_insert(uint32_t table, uint32_t 
> mark,
>                               const char output_netdev[],
>                               const struct in6_addr *gw,
>                               const struct in6_addr *prefsrc);
> -void ovs_router_rule_add(uint32_t prio, bool invert, uint8_t src_len,
> -                         const struct in6_addr *from, uint32_t lookup_table);
> +void ovs_router_rule_add(uint32_t prio, bool invert, bool user,
> +                         uint8_t src_len, const struct in6_addr *from,
> +                         uint32_t lookup_table);
> +int ovs_router_rule_del(uint32_t prio, bool invert, uint8_t src_len,
> +                        const struct in6_addr *from, uint32_t lookup_table);
>  void ovs_router_flush(bool flush_all);
> -void ovs_router_rules_flush(void);
> +void ovs_router_rules_flush(bool flush_all);
>  
>  void ovs_router_disable_system_routing_table(void);
>  
> diff --git a/lib/route-table.c b/lib/route-table.c
> index 85ded4716d7c..688229a72f6c 100644
> --- a/lib/route-table.c
> +++ b/lib/route-table.c
> @@ -288,8 +288,8 @@ rule_handle_msg(const struct route_table_msg *change)
>              route_table_dump_one_table(rd->lookup_table,
>                                         route_table_handle_msg, NULL);
>          }
> -        ovs_router_rule_add(rd->prio, rd->invert, rd->src_len, 
> &rd->from_addr,
> -                            rd->lookup_table);
> +        ovs_router_rule_add(rd->prio, rd->invert, false, rd->src_len,
> +                            &rd->from_addr, rd->lookup_table);
>      }
>  }
>  
> @@ -777,7 +777,7 @@ rules_change(const struct route_table_msg *change 
> OVS_UNUSED,
>  static void
>  route_map_clear(void)
>  {
> -    ovs_router_rules_flush();
> +    ovs_router_rules_flush(false);
>      ovs_router_flush(false);
>  }
>  
> diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man
> index 8e48c0e809ac..0ebbe33db2cf 100644
> --- a/ofproto/ofproto-tnl-unixctl.man
> +++ b/ofproto/ofproto-tnl-unixctl.man
> @@ -30,6 +30,20 @@ additional parameters are used: \fIpkt_mark\fR and 
> \fIsrc\fR.
>  Print routing rules in OVS. This includes routing rules cached from the 
> system
>  routing policy database and user configured routing rules.
>  .
> +.IP "\fBovs/route/rule/add [not] from=<\fIall|IP/PLEN\fB> [prio=\fINUM\fB] \
> +table=<\fIlocal|main|default|ID\fB>\fR"
> +Add user-configured routing rule to vswitchd. It can be useful to reference
> +non-standard routing tables for configuring advanced routing policies, for
> +example matching on the source IP address. If priority is not specified, the
> +lowest unused priority will be selected automatically. Multiple rules with 
> the
> +same priority may exist. User-configured rules and system cached rules 
> co-exist
> +together.
> +.
> +.IP "\fBovs/route/rule/del [not] from=<\fIall|IP/PLEN\fB> [prio=\fINUM\fB] \
> +table=<\fIlocal|main|default|ID\fB>\fR"
> +Delete user-configured routing rule from vswitchd. If priority is not 
> specified
> +the first matching rule will be selected for deletion.
> +.

Same comments for consistency and fonts.  Angle brackets should not be needed.

>  .IP "\fBtnl/neigh/show\fR"
>  .IP "\fBtnl/arp/show\fR"
>  OVS builds ARP cache by snooping are messages. This command shows
> diff --git a/tests/ovs-router.at b/tests/ovs-router.at
> index 450eebb28fb0..aa2f5f1ffc53 100644
> --- a/tests/ovs-router.at
> +++ b/tests/ovs-router.at
> @@ -238,9 +238,75 @@ AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 
> 192.0.2.1/24], [0], [OK
>  
>  dnl Check standard rules exist.
>  AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> -0: from all lookup local
> -32766: from all lookup main
> -32767: from all lookup default
> +Cached: 0: from all lookup local
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
> +])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +AT_SETUP([appctl - route/rule/{add,del}])
> +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.0.2.1/24], [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
> +])
> +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 local
> +User: 10.1.1.0/24 dev br0 GW 192.0.2.2 SRC 192.0.2.1 table 11
> +User: 10.2.2.0/24 dev br0 GW 192.0.2.2 SRC 192.0.2.1 table 12
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], 
> [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.20/32 table=12], [0], 
> [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> +Cached: 0: from all lookup local
> +User: 32764: from 192.0.2.20 lookup 12
> +User: 32765: from 192.0.2.10 lookup 11
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], 
> [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], 
> [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> +Cached: 0: from all lookup local
> +User: 32762: from 192.0.2.10 lookup 11
> +User: 32763: from 192.0.2.10 lookup 11
> +User: 32764: from 192.0.2.20 lookup 12
> +User: 32765: from 192.0.2.10 lookup 11
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/rule/del from=192.0.2.10/32 table=11], [0], 
> [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> +Cached: 0: from all lookup local
> +User: 32763: from 192.0.2.10 lookup 11
> +User: 32764: from 192.0.2.20 lookup 12
> +User: 32765: from 192.0.2.10 lookup 11
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/rule/del from=192.0.2.10/32 prio=32765 
> table=11], [0], [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> +Cached: 0: from all lookup local
> +User: 32763: from 192.0.2.10 lookup 11
> +User: 32764: from 192.0.2.20 lookup 12
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
>  ])
>  
>  OVS_VSWITCHD_STOP
> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> index 64c41460bbb2..065a1fcacba6 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -1440,3 +1440,122 @@ AT_CHECK([tail -1 stdout], [0],
>  
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
> +
> +AT_SETUP([tunnel_push_pop - use two VTEPs in same subnet with rules])
> +
> +dnl Create bridge that has a MAC address.
> +OVS_VSWITCHD_START([set bridge br0 datapath_type=dummy dnl
> +                    -- set Interface br0 
> other-config:hwaddr=aa:55:aa:55:00:00])
> +
> +AT_CHECK([ovs-vsctl add-port br0 p0 \
> +          -- set Interface p0 type=dummy ofport_request=1])
> +
> +AT_CHECK([ovs-vsctl add-br br1 \
> +          -- set bridge br1 datapath_type=dummy \
> +          -- set Interface br1 other-config:hwaddr=aa:66:aa:66:00:00])
> +AT_CHECK([ovs-vsctl add-port br1 p1 \
> +          -- set interface p1 type=dummy ofport_request=2])
> +
> +AT_CHECK([ovs-vsctl \
> +          -- add-br int-br \
> +          -- set bridge int-br datapath_type=dummy \
> +          -- set Interface int-br other-config:hwaddr=aa:77:aa:77:00:00])
> +AT_CHECK([ovs-vsctl \
> +          -- add-port int-br t0 \
> +          -- set Interface t0 type=gre ofport_request=3 \
> +                              options:local_ip=1.1.2.80 \
> +                              options:remote_ip=1.1.2.90
> +])
> +AT_CHECK([ovs-vsctl \
> +          -- add-port int-br t1 \
> +          -- set Interface t1 type=gre ofport_request=4 \
> +                              options:local_ip=1.1.2.81 \
> +                              options:remote_ip=1.1.2.91
> +])
> +
> +AT_CHECK([ovs-appctl dpif/show], [0], [dnl
> +dummy@ovs-dummy: hit:0 missed:0
> +  br0:
> +    br0 65534/100: (dummy-internal)
> +    p0 1/1: (dummy)
> +  br1:
> +    br1 65534/101: (dummy-internal)
> +    p1 2/2: (dummy)
> +  int-br:
> +    int-br 65534/3: (dummy-internal)
> +    t0 3/4: (gre: local_ip=1.1.2.80, remote_ip=1.1.2.90)
> +    t1 4/4: (gre: local_ip=1.1.2.81, remote_ip=1.1.2.91)
> +])
> +
> +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.80/24], [0], [OK
> +])
> +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br1 1.1.2.81/24], [0], [OK
> +])
> +dnl Checking that a local route for added IP was successfully installed
> +dnl on p1 only.

br1?

> +AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
> +Cached: 1.1.2.0/24 dev br1 SRC 1.1.2.81 local
> +])
> +
> +AT_CHECK([ovs-ofctl add-flow br0 action=normal])
> +AT_CHECK([ovs-ofctl add-flow br1 action=normal])
> +AT_CHECK([ovs-ofctl add-flow int-br action=normal])
> +
> +dnl Use arp reply to achieve tunnel next hop mac binding

Period at the end of the sentence.

> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl
> + 'recirc_id(0),in_port(1),dnl
> +  eth(src=f8:bc:12:44:34:b0,dst=aa:55:aa:55:00:00),eth_type(0x0806),dnl
> +  
> arp(sip=1.1.2.90,tip=1.1.2.80,op=2,sha=f8:bc:12:44:34:b0,tha=00:00:00:00:00:00)'
> +])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 dnl
> + 'recirc_id(0),in_port(2),dnl
> +  eth(src=f8:bc:12:44:34:b1,dst=aa:66:aa:66:00:00),eth_type(0x0806),dnl
> +  
> arp(sip=1.1.2.91,tip=1.1.2.81,op=2,sha=f8:bc:12:44:34:b1,tha=00:00:00:00:00:00)'
> +])
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
> +1.1.2.90                                      f8:bc:12:44:34:b0   br0
> +1.1.2.91                                      f8:bc:12:44:34:b1   br1
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/add 1.1.2.0/24 br0 src=1.1.2.80 table=10], 
> [0], [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/add 1.1.2.0/24 br1 src=1.1.2.81 table=11], 
> [0], [OK
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl
> +Cached: 1.1.2.0/24 dev br1 SRC 1.1.2.81 local
> +User: 1.1.2.0/24 dev br0 SRC 1.1.2.80 table 10
> +User: 1.1.2.0/24 dev br1 SRC 1.1.2.81 table 11
> +])
> +
> +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(int-br),dnl
> +          
> eth(src=00:00:00:00:00:00,dst=f8:bc:12:44:34:b0),eth_type(0x0800),dnl
> +          ipv4(src=1.1.3.100,dst=1.1.3.200,proto=1,tos=0,ttl=64,frag=no),dnl
> +          icmp(type=8,code=0)' | grep 'tunneling to.*via'], [0], [dnl
> +     -> tunneling to 1.1.2.90 via br1
> +     -> tunneling to 1.1.2.91 via br1
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=1.1.2.80/32 table=10], [0], [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/add from=1.1.2.81/32 table=11], [0], [OK
> +])
> +AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
> +Cached: 0: from all lookup local
> +User: 32764: from 1.1.2.81 lookup 11
> +User: 32765: from 1.1.2.80 lookup 10
> +Cached: 32766: from all lookup main
> +Cached: 32767: from all lookup default
> +])
> +
> +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(int-br),dnl
> +          
> eth(src=00:00:00:00:00:00,dst=f8:bc:12:44:34:b0),eth_type(0x0800),dnl
> +          ipv4(src=1.1.3.100,dst=1.1.3.200,proto=1,tos=0,ttl=64,frag=no),dnl
> +          icmp(type=8,code=0)' | grep 'tunneling to.*via'], [0], [dnl
> +     -> tunneling to 1.1.2.90 via br0
> +     -> tunneling to 1.1.2.91 via br1
> +])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to