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
