On Mon, Sep 22, 2025 at 4:27 PM Dumitru Ceara via dev < [email protected]> wrote:
> This closes the gap with the static routes support and allows users to > explicitly break the tie for cases that are ambiguous, e.g., when the > reroute next hop IP could be theoretically reachable on multiple > connected router ports. An initial proposal tried to solve this problem > in a different way [0] but upon further discussions [1] we agreed to match > the support and static routes currently offer. > > The NB router policy schema is extended to include an "output_port" > optional field which will be used by the CMS to explicitly determine > which router port to be used when the reroute policy is applied. This > overrides any automatic port detection that happens for policies without > an "output_port" explicitly specified. > > As mentioned in the documentation and in the TODO.rst file, the support > is limited to non-ECMP policies. That's mainly because the current > router policy schema makes it extremely cumbersome to attach different > ports to different next hops that may be defined for the policy. Unlike > NB static routes, the router policies store the ECMP nexthops as > comma-separated strings in the NB policy record. > > [0] https://mail.openvswitch.org/pipermail/ovs-dev/2025-July/424894.html > [1] > https://mail.openvswitch.org/pipermail/ovs-dev/2025-September/426231.html > > Reported-at: https://issues.redhat.com/browse/FDP-1554 > Reported-by: Jakub Libosvar <[email protected]> > Signed-off-by: Dumitru Ceara <[email protected]> > --- > Hi Dumitru, thank you for the patch. I have two small nits below. NEWS | 1 + > TODO.rst | 6 ++ > northd/en-learned-route-sync.c | 2 +- > northd/northd.c | 137 +++++++++++++++++++++------------ > northd/northd.h | 3 +- > ovn-nb.ovsschema | 10 ++- > ovn-nb.xml | 17 ++++ > tests/ovn-nbctl.at | 17 ++++ > tests/ovn-northd.at | 45 +++++++++++ > tests/system-ovn.at | 98 +++++++++++++++++++++++ > utilities/ovn-nbctl.c | 33 +++++++- > 11 files changed, 313 insertions(+), 56 deletions(-) > > diff --git a/NEWS b/NEWS > index 4d0c45e4dc..ede89aea35 100644 > --- a/NEWS > +++ b/NEWS > @@ -2,6 +2,7 @@ Post v25.09.0 > ------------- > - Added disable_garp_rarp option to logical_router table in order to > disable > GARP/RARP announcements by all the peer ports of this logical router. > + - Support for specifying output_port for logical router reroute > policies. > > OVN v25.09.0 - xxx xx xxxx > -------------------------- > diff --git a/TODO.rst b/TODO.rst > index cda5f0d996..b992b8e5ae 100644 > --- a/TODO.rst > +++ b/TODO.rst > @@ -171,6 +171,12 @@ OVN To-do List > allow for the eventual removal of the ovn\_datapath structure from the > codebase. > > +* Logical Router Policies > + > + * Add support for configuring output\_port for reroute router policies > that > + have more than one nexthop (ECMP). This probably requires a redesign > of > + the Northbound Logical_Router_Policy database schema. > + > * CI > > * ovn-kubernetes: Only a subset of the ovn-kubernetes features is > currently > diff --git a/northd/en-learned-route-sync.c > b/northd/en-learned-route-sync.c > index 7ba43ec10a..c8e5ade0b4 100644 > --- a/northd/en-learned-route-sync.c > +++ b/northd/en-learned-route-sync.c > @@ -192,7 +192,7 @@ parse_route_from_sbrec_route(struct hmap > *parsed_routes_out, > const char *lrp_addr_s = NULL; > struct ovn_port *out_port = NULL; > if (!find_route_outport(lr_ports, route->logical_port->logical_port, > - route->ip_prefix, route->nexthop, > + "static route", route->ip_prefix, > route->nexthop, > IN6_IS_ADDR_V4MAPPED(&prefix), > false, > &out_port, &lrp_addr_s)) { > diff --git a/northd/northd.c b/northd/northd.c > index 23a4c0784f..e83aacc57a 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -10754,29 +10754,59 @@ lrp_find_member_ip(const struct ovn_port *op, > const char *ip_s) > return find_lport_address(&op->lrp_networks, ip_s); > } > > -static struct ovn_port* > -get_outport_for_routing_policy_nexthop(struct ovn_datapath *od, > - const struct hmap *lr_ports, > - int priority, const char *nexthop) > +/* Returns true if the output port to be used for forwarding traffic > through > + * this policy could be determined. Stores a pointer to the output port > + * in 'p_output_port' and a pointer to the router IP address to be used > for > + * this policy, in 'p_lrp_addr_s'. */ > +static bool > +find_policy_outport(struct ovn_datapath *od, const struct hmap *lr_ports, > + const struct nbrec_logical_router_policy *policy, > + const char *nexthop, bool is_ipv4, > + const char **p_lrp_addr_s, struct ovn_port > **p_out_port) > { > if (nexthop == NULL) { > return NULL; > nit: The signature changed to bool, it should return false. > } > > - /* Find the router port matching the next hop. */ > - for (int i = 0; i < od->nbr->n_ports; i++) { > - struct nbrec_logical_router_port *lrp = od->nbr->ports[i]; > + struct ovn_port *out_port = NULL; > + const char *lrp_addr_s = NULL; > > - struct ovn_port *out_port = ovn_port_find(lr_ports, lrp->name); > - if (out_port && lrp_find_member_ip(out_port, nexthop)) { > - return out_port; > - } > + if (policy->output_port) { > + if (!find_route_outport(lr_ports, policy->output_port->name, > + "policy", policy->match, > + nexthop, is_ipv4, true, &out_port, > + &lrp_addr_s)) { > + return NULL; > nit: Same here it should return false. > + } > + } else { > + /* If output_port is not specified, find the router port matching > + * the next hop. */ > + HMAP_FOR_EACH (out_port, dp_node, &od->ports) { > + lrp_addr_s = lrp_find_member_ip(out_port, nexthop); > + if (lrp_addr_s) { > + break; > + } > + } > } > > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "No path for routing policy priority %d on router > %s; " > - "next hop %s", priority, od->nbr->name, nexthop); > - return NULL; > + if (!out_port || !lrp_addr_s) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "Logical Router: %s, policy " > + "(chain: '%s', match: '%s', priority > %"PRId64"): " > + "no path for next hop %s", > + od->nbr->name, > + policy->chain ? policy->chain : "<Default>", > + policy->match, policy->priority, nexthop); > + return false; > + } > + if (p_out_port) { > + *p_out_port = out_port; > + } > + if (p_lrp_addr_s) { > + *p_lrp_addr_s = lrp_addr_s; > + } > + > + return true; > } > > static bool check_bfd_state(const struct nbrec_logical_router_policy > *rule, > @@ -10852,18 +10882,12 @@ build_routing_policy_flow(struct lflow_table > *lflows, struct ovn_datapath *od, > } > > char *nexthop = rp->valid_nexthops[0]; > - struct ovn_port *out_port = > get_outport_for_routing_policy_nexthop( > - od, lr_ports, rule->priority, nexthop); > - if (!out_port) { > - return; > - } > + bool is_ipv4 = strchr(nexthop, '.') ? true : false; > + const char *lrp_addr_s = NULL; > + struct ovn_port *out_port = NULL; > > - const char *lrp_addr_s = lrp_find_member_ip(out_port, nexthop); > - if (!lrp_addr_s) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy " > - " priority %"PRId64" nexthop %s", > - rule->priority, nexthop); > + if (!find_policy_outport(od, lr_ports, rule, nexthop, is_ipv4, > + &lrp_addr_s, &out_port)) { > return; > } > > @@ -10872,7 +10896,6 @@ build_routing_policy_flow(struct lflow_table > *lflows, struct ovn_datapath *od, > ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark); > } > > - bool is_ipv4 = strchr(nexthop, '.') ? true : false; > ds_put_format(&actions, "%s = %s; " > "%s = %s; " > "eth.src = %s; " > @@ -10952,19 +10975,12 @@ build_ecmp_routing_policy_flows(struct > lflow_table *lflows, > struct ds actions = DS_EMPTY_INITIALIZER; > > for (size_t i = 0; i < rp->n_valid_nexthops; i++) { > - struct ovn_port *out_port = > get_outport_for_routing_policy_nexthop( > - od, lr_ports, rule->priority, rp->valid_nexthops[i]); > - if (!out_port) { > - goto cleanup; > - } > + bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false; > + const char *lrp_addr_s = NULL; > + struct ovn_port *out_port = NULL; > > - const char *lrp_addr_s = > - lrp_find_member_ip(out_port, rp->valid_nexthops[i]); > - if (!lrp_addr_s) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy " > - " priority %"PRId64" nexthop %s", > - rule->priority, rp->valid_nexthops[i]); > + if (!find_policy_outport(od, lr_ports, rule, > rp->valid_nexthops[i], > + is_ipv4, &lrp_addr_s, &out_port)) { > goto cleanup; > } > > @@ -10974,7 +10990,6 @@ build_ecmp_routing_policy_flows(struct lflow_table > *lflows, > ds_put_format(&actions, "pkt.mark = %u; ", pkt_mark); > } > > - bool is_ipv4 = strchr(rp->valid_nexthops[i], '.') ? true : false; > > ds_put_format(&actions, "%s = %s; " > "%s = %s; " > @@ -11571,15 +11586,16 @@ build_route_match(const struct ovn_port > *op_inport, uint32_t rtb_id, > > bool > find_route_outport(const struct hmap *lr_ports, const char *output_port, > - const char *ip_prefix, const char *nexthop, bool > is_ipv4, > + const char *route_type, const char *route_desc, > + const char *nexthop, bool is_ipv4, > bool force_out_port, > struct ovn_port **out_port, const char **lrp_addr_s) > { > *out_port = ovn_port_find(lr_ports, output_port); > if (!*out_port) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "Bad out port %s for static route %s", > - output_port, ip_prefix); > + VLOG_WARN_RL(&rl, "Bad out port %s for %s %s", > + output_port, route_type, route_desc); > return false; > } > if (nexthop[0]) { > @@ -11619,8 +11635,10 @@ find_static_route_outport(const struct > ovn_datapath *od, > struct ovn_port *out_port = NULL; > if (route->output_port) { > /* XXX: we should be able to use &od->ports instead of lr_ports. > */ > - if (!find_route_outport(lr_ports, route->output_port, > route->ip_prefix, > - route->nexthop, is_ipv4, true, &out_port, &lrp_addr_s)) { > + if (!find_route_outport(lr_ports, route->output_port, > + "static route", route->ip_prefix, > + route->nexthop, is_ipv4, true, &out_port, > + &lrp_addr_s)) { > return false; > } > } else { > @@ -14217,16 +14235,33 @@ build_route_policies(struct ovn_datapath *od, > const struct hmap *lr_ports, > if (!strcmp(rule->action, "reroute")) { > size_t n_nexthops = rule->n_nexthops ? rule->n_nexthops : 1; > > + if (rule->output_port && n_nexthops != 1) { > + static struct vlog_rate_limit rl = > VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, > + "Logical router: %s, policy " > + "(chain: '%s', match: '%s', priority > %"PRId64"): " > + "output_port only supported on non-ECMP " > + "reroute policies", > + od->nbr->name, > + rule->chain ? rule->chain : "<Default>", > + rule->match, rule->priority); > + continue; > + } > + > valid_nexthops = xcalloc(n_nexthops, sizeof *valid_nexthops); > for (size_t j = 0; j < n_nexthops; j++) { > char *nexthop = rule->n_nexthops > ? rule->nexthops[j] : rule->nexthop; > - struct ovn_port *out_port = > - get_outport_for_routing_policy_nexthop( > - od, lr_ports, rule->priority, nexthop); > - if (!out_port || !check_bfd_state(rule, out_port, nexthop, > - bfd_connections, > - > bfd_active_connections)) { > + struct ovn_port *out_port = NULL; > + bool is_ipv4 = strchr(nexthop, '.') ? true : false; > + > + if (!find_policy_outport(od, lr_ports, rule, nexthop, > is_ipv4, > + NULL, &out_port)) { > + continue; > + } > + if (!check_bfd_state(rule, out_port, nexthop, > + bfd_connections, > + bfd_active_connections)) { > continue; > } > valid_nexthops[n_valid_nexthops++] = nexthop; > diff --git a/northd/northd.h b/northd/northd.h > index ca35eb91e4..0348a3234f 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -855,7 +855,8 @@ struct svc_monitors_map_data { > > bool > find_route_outport(const struct hmap *lr_ports, const char *output_port, > - const char *ip_prefix, const char *nexthop, bool > is_ipv4, > + const char *route_type, const char *route_desc, > + const char *nexthop, bool is_ipv4, > bool force_out_port, > struct ovn_port **out_port, const char **lrp_addr_s); > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema > index f55930a2e5..cfb1f5f7dc 100644 > --- a/ovn-nb.ovsschema > +++ b/ovn-nb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Northbound", > - "version": "7.12.0", > - "cksum": "2749576410 39903", > + "version": "7.13.0", > + "cksum": "296941325 40198", > "tables": { > "NB_Global": { > "columns": { > @@ -555,6 +555,12 @@ > "nexthop": {"type": {"key": "string", "min": 0, "max": > 1}}, > "nexthops": {"type": { > "key": "string", "min": 0, "max": "unlimited"}}, > + "output_port": { > + "type": {"key": {"type": "uuid", > + "refTable": "Logical_Router_Port", > + "refType": "weak"}, > + "min": 0, > + "max": 1}}, > "bfd_sessions": {"type": {"key": {"type": "uuid", > "refTable": "BFD", > "refType": "weak"}, > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 1f5c58490c..c83ec1b7f4 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -4725,6 +4725,23 @@ or > </p> > </column> > > + <column name="output_port"> > + <p> > + The <ref table="Logical_Router_Port"/> via which the packet > + needs to be sent out. This is optional and when not specified, > + OVN will automatically figure this out based on the > + <ref column="nexthops"/> column. When this is specified and > there are > + multiple IP addresses on the router port and none of them are in > the > + same subnet of <ref column="nexthops"/>, OVN chooses the first IP > + address as the one via which the <ref column="nexthops"/> are > + reachable. > + > + NOTE: for now OVN does not support configuring the output port on > + ECMP reroute policies (with more than one value in the > + <ref column="nexthops"/> column). > + </p> > + </column> > + > <column name="bfd_sessions"> > <p> > Reference to <ref table="BFD"/> row if the route policy has > associated > diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at > index fe4a984d07..c6edd0f17b 100644 > --- a/tests/ovn-nbctl.at > +++ b/tests/ovn-nbctl.at > @@ -2512,6 +2512,21 @@ AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src > == 2.1.1.0/24" jump], [1], [] > [ovn-nbctl: Chain name is required when action is jump. > ]) > > +AT_CHECK([ovn-nbctl --output-port=lrp0 lr-policy-add lr0 101 "ip4.src == > 3.1.1.0/24" drop], [1], [], > + [ovn-nbctl: Specifying output-port is only allowed for reroute policies. > +]) > + > +AT_CHECK([ovn-nbctl --output-port=lrp0 lr-policy-add lr0 101 "ip4.src == > 3.1.1.0/24" reroute 192.168.0.10,192.168.0.11], [1], [], > + [ovn-nbctl: output-port option is supported only for policies with a > single reroute next hop > +]) > + > +AT_CHECK([ovn-nbctl --output-port=lrp0 lr-policy-add lr0 101 "ip4.src == > 3.1.1.0/24" reroute 192.168.0.10], [1], [], > + [ovn-nbctl: unknown output-port: lrp0 > +]) > + > +check ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:00:01 192.168.0.1/24 > +check ovn-nbctl --output-port=lrp0 lr-policy-add lr0 101 "ip4.src == > 3.1.1.0/24" reroute 192.168.0.10 > + > dnl Add duplicated policy > AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], > [1], [], > [ovn-nbctl: Same routing policy already existed on the logical router > lr0. > @@ -2532,6 +2547,7 @@ Routing Policies > Chain <Default>: > 101 ip4.src == 2.1.1.0/24 > allow > 101 ip4.src == 2.1.2.0/24 > drop > + 101 ip4.src == 3.1.1.0/24 > reroute 192.168.0.10 lrp0 > 101 ip6.src == 2002::/64 > drop > 100 ip4.src == 1.1.2.0/24 > allow pkt_mark=100,foo=bar > > @@ -2549,6 +2565,7 @@ Routing Policies > Chain <Default>: > 101 ip4.src == 2.1.1.0/24 > allow > 101 ip4.src == 2.1.2.0/24 > drop > + 101 ip4.src == 3.1.1.0/24 > reroute 192.168.0.10 lrp0 > 101 ip6.src == 2002::/64 > drop > 100 ip4.src == 1.1.2.0/24 > allow pkt_mark=100,foo=bar > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index 6298222247..83a142d20f 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -3643,6 +3643,39 @@ AT_CHECK([grep "lr_in_policy[[^_]]" lr0flows2 | > ovn_strip_lflows | sort], [0], [ > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD_NO_HV([ > +AT_SETUP([Router policies - output_port]) > +ovn_start > + > +dnl Test with a simple non-ECMP reroute policy with an explicit > output-port set. > +check ovn-nbctl \ > + -- lr-add lr0 \ > + -- lrp-add lr0 lrp1 00:00:00:00:00:01 1.1.1.1/24 > \ > + -- lrp-add lr0 lrp2 00:00:00:00:00:02 2.2.2.1/24 > \ > + -- --output-port=lrp2 lr-policy-add lr0 100 "ip4.src == 42.42.42.42" \ > + reroute 3.3.3.3 > + > +dnl Also add an ECMP reroute policy and forcefully set output-port; > ovn-northd > +dnl should warn and ignore this second policy. > +check ovn-nbctl lr-policy-add lr0 200 "ip4.src == 43.43.43.43" reroute > 3.3.3.4,3.3.3.5 > +lrpol=$(fetch_column nb:logical_router_policy _uuid priority=200) > +lrp2=$(fetch_column nb:logical_router_port _uuid name=lrp2) > +check ovn-nbctl set logical_router_policy $lrpol output_port=$lrp2 > +check ovn-nbctl --wait=sb sync > + > +check grep -qE "output_port only supported on non-ECMP reroute policies" > northd/ovn-northd.log > + > +ovn-sbctl dump-flows lr0 > lr0flows > +AT_CAPTURE_FILE([lr0flows]) > + > +AT_CHECK([grep "lr_in_policy[[^_]]" lr0flows | ovn_strip_lflows | sort], > [0], [dnl > + table=??(lr_in_policy ), priority=0 , match=(1), > action=(reg8[[0..15]] = 0; next;) > + table=??(lr_in_policy ), priority=100 , match=(ip4.src == > 42.42.42.42), action=(reg0 = 3.3.3.3; reg5 = 2.2.2.1; eth.src = > 00:00:00:00:00:02; outport = "lrp2"; flags.loopback = 1; reg8[[0..15]] = 0; > reg9[[9]] = 1; next;) > +]) > + > +AT_CLEANUP > +]) > + > OVN_FOR_EACH_NORTHD_NO_HV([ > AT_SETUP([ACL allow-stateless omit conntrack - Logical_Switch]) > ovn_start > @@ -12739,6 +12772,18 @@ check_engine_stats sync_to_sb_lb recompute > nocompute > check_engine_stats lflow recompute nocompute > CHECK_NO_CHANGE_AFTER_RECOMPUTE > > +# Change router Policy to use explicit output port. > +lrp_lr0_sw0=$(fetch_column nb:logical_router_port _uuid name=lr0-sw0) > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > +check ovn-nbctl --wait=sb set logical_router_policy . > output_port=$lrp_lr0_sw0 > +check_engine_stats northd recompute nocompute > +check_engine_stats lr_nat recompute nocompute > +check_engine_stats lr_stateful recompute nocompute > +check_engine_stats sync_to_sb_pb recompute nocompute > +check_engine_stats sync_to_sb_lb recompute nocompute > +check_engine_stats lflow recompute nocompute > +CHECK_NO_CHANGE_AFTER_RECOMPUTE > + > check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats > check ovn-nbctl --wait=sb lr-policy-del lr0 10 "ip4.src == 10.0.0.3" > check_engine_stats northd recompute nocompute > diff --git a/tests/system-ovn.at b/tests/system-ovn.at > index b2319b1801..e5d97bf152 100644 > --- a/tests/system-ovn.at > +++ b/tests/system-ovn.at > @@ -18485,3 +18485,101 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query > port patch-.*/d > /connection dropped.*/d"]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Router reroute policies - output port]) > +AT_SKIP_IF([test $HAVE_NC = no]) > + > +ovn_start > +OVS_TRAFFIC_VSWITCHD_START() > +ADD_BR([br-int]) > + > +check ovs-vsctl \ > + -- set Open_vSwitch . external-ids:system-id=hv1 \ > + -- set Open_vSwitch . > external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ > + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ > + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ > + -- set bridge br-int fail-mode=secure > other-config:disable-in-band=true > +start_daemon ovn-controller > + > +dnl Logical network: > +dnl +--- LR2 --- LS2 --- VM2 (20.0.0.1) > +dnl | > +dnl vm1 -- LS1 --- LR1 --- + > +dnl +--- LR3 --- LS3 --- VM3 > +dnl > +dnl - Link-Local only connections between LR1 and LR2 and between LR1 and > LR3. > +dnl - LR1 with a default route via LR2's LLA > +dnl - LR1 with a routing policy to reroute traffic from vm1 via LR3's LLA > +dnl and, to avoid ambiguity, with output-port set to be the port > towards LR3. > +dnl > +check ovn-nbctl \ > + -- lr-add lr1 \ > + -- lrp-add lr1 lr1-ls1 00:00:00:00:00:01 10.0.0.1/24 \ > + -- lrp-add lr1 lr1-lr2 00:00:00:00:00:02 peer=lr2-lr1 \ > + -- lrp-add lr1 lr1-lr3 00:00:00:00:00:03 peer=lr3-lr1 \ > + -- lr-route-add lr1 0.0.0.0/0 fe80::200:ff:fe00:200 \ > + -- --output-port=lr1-lr3 lr-policy-add lr1 100 \ > + "ip4.src == 10.0.0.2" \ > + reroute fe80::200:ff:fe00:300 \ > + -- lr-add lr2 \ > + -- lrp-add lr2 lr2-lr1 00:00:00:00:02:00 peer=lr1-lr2 \ > + -- lrp-add lr2 lr2-ls2 00:00:00:00:00:12 20.0.0.1/24 \ > + -- lr-route-add lr2 10.0.0.0/24 fe80::200:ff:fe00:2 lr2-lr1 \ > + -- lr-add lr3 \ > + -- lrp-add lr3 lr3-lr1 00:00:00:00:03:00 peer=lr1-lr3 \ > + -- lrp-add lr3 lr3-ls3 00:00:00:00:00:13 30.0.0.1/24 \ > + -- lr-route-add lr3 10.0.0.0/24 fe80::200:ff:fe00:3 lr3-lr1 \ > + -- ls-add ls1 \ > + -- lsp-add ls1 ls1-lr1 \ > + -- lsp-set-type ls1-lr1 router \ > + -- lsp-set-addresses ls1-lr1 router \ > + -- lsp-set-options ls1-lr1 router-port=lr1-ls1 \ > + -- ls-add ls2 \ > + -- lsp-add ls2 ls2-lr2 \ > + -- lsp-set-type ls2-lr2 router \ > + -- lsp-set-addresses ls2-lr2 router \ > + -- lsp-set-options ls2-lr2 router-port=lr2-ls2 \ > + -- ls-add ls3 \ > + -- lsp-add ls3 ls3-lr3 \ > + -- lsp-set-type ls3-lr3 router \ > + -- lsp-set-addresses ls3-lr3 router \ > + -- lsp-set-options ls3-lr3 router-port=lr3-ls3 \ > + -- lsp-add ls1 vm1 \ > + -- lsp-set-addresses vm1 "00:00:00:00:01:00 10.0.0.2" \ > + -- lsp-add ls2 vm2 \ > + -- lsp-set-addresses vm2 "00:00:00:00:02:00 20.0.0.2" \ > + -- lsp-add ls3 vm3 \ > + -- lsp-set-addresses vm3 "00:00:00:00:03:00 30.0.0.2" > + > +ADD_NAMESPACES(vm1) > +ADD_VETH(vm1, vm1, br-int, "10.0.0.2/24", "00:00:00:00:01:00", > "10.0.0.1") > +ADD_NAMESPACES(vm2) > +ADD_VETH(vm2, vm2, br-int, "20.0.0.2/24", "00:00:00:00:02:00", > "20.0.0.1") > +ADD_NAMESPACES(vm3) > +ADD_VETH(vm3, vm3, br-int, "30.0.0.2/24", "00:00:00:00:03:00", > "30.0.0.1") > + > +OVN_POPULATE_ARP > +check ovn-nbctl --wait=hv sync > +wait_for_ports_up > + > +NETNS_DAEMONIZE([vm3], [nc -l -k 80], [vm3.pid]) > +NS_CHECK_EXEC([vm1], [nc 30.0.0.2 80 -z]) > + > +OVN_CLEANUP_CONTROLLER([hv1]) > + > +as ovn-sb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as ovn-nb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as northd > +OVS_APP_EXIT_AND_WAIT([ovn-northd]) > + > +as > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d > +/Failed to acquire.*/d > +/connection dropped.*/d"]) > +AT_CLEANUP > +]) > diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c > index 58517f9668..9d58094e98 100644 > --- a/utilities/ovn-nbctl.c > +++ b/utilities/ovn-nbctl.c > @@ -4189,6 +4189,9 @@ nbctl_lr_policy_add(struct ctl_context *ctx) > const struct nbrec_bfd **bfd_sessions = NULL; > char *chain_s = shash_find_data(&ctx->options, "--chain"); > > + const char *output_port = shash_find_data(&ctx->options, > "--output-port"); > + const struct nbrec_logical_router_port *out_lrp = NULL; > + > bool reroute = false; > bool jump = false; > > @@ -4205,6 +4208,10 @@ nbctl_lr_policy_add(struct ctl_context *ctx) > return; > } > reroute = true; > + } else if (output_port) { > + ctl_error(ctx, "Specifying output-port is only allowed for " > + "reroute policies."); > + return; > } > > if (!strcmp(action, "jump")) { > @@ -4264,6 +4271,21 @@ nbctl_lr_policy_add(struct ctl_context *ctx) > } > > free(nexthops_arg); > + > + if (output_port) { > + if (vector_len(&nexthops) != 1) { > + ctl_error(ctx, "output-port option is supported only for " > + "policies with a single reroute next hop"); > + goto free_nexthops; > + } > + > + error = lrp_by_name_or_uuid(ctx, output_port, true, &out_lrp); > + if (error) { > + ctl_error(ctx, "unknown output-port: %s", output_port); > + free(error); > + goto free_nexthops; > + } > + } > } > > struct nbrec_logical_router_policy *policy; > @@ -4283,6 +4305,7 @@ nbctl_lr_policy_add(struct ctl_context *ctx) > if (reroute) { > nbrec_logical_router_policy_set_nexthops( > policy, vector_get_array(&nexthops), vector_len(&nexthops)); > + nbrec_logical_router_policy_set_output_port(policy, out_lrp); > } > > /* Parse the options. */ > @@ -4505,6 +4528,10 @@ print_routing_policy(const struct > nbrec_logical_router_policy *policy, > free(next_hop); > } > > + if (policy->output_port) { > + ds_put_format(s, " %s", policy->output_port->name); > + } > + > if (!smap_is_empty(&policy->options) || policy->n_bfd_sessions) { > ds_put_format(s, "%15s", ""); > if (policy->n_bfd_sessions) { > @@ -4526,10 +4553,14 @@ nbctl_pre_lr_policy_list(struct ctl_context *ctx) > ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_name); > ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_policies); > > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name); > + > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_priority); > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_match); > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_nexthops); > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_action); > + ovsdb_idl_add_column(ctx->idl, > + &nbrec_logical_router_policy_col_output_port); > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_options); > ovsdb_idl_add_column(ctx->idl, > &nbrec_logical_router_policy_col_bfd_sessions); > @@ -8418,7 +8449,7 @@ static const struct ctl_command_syntax > nbctl_commands[] = { > { "lr-policy-add", 4, INT_MAX, > "ROUTER PRIORITY MATCH ACTION [NEXTHOP] [OPTIONS - KEY=VALUE ...]", > nbctl_pre_lr_policy_add, nbctl_lr_policy_add, NULL, > - "--may-exist,--bfd?,--chain=", RW }, > + "--may-exist,--bfd?,--chain=,--output-port=", RW }, > { "lr-policy-del", 1, 3, "ROUTER [{PRIORITY | UUID} [MATCH]]", > nbctl_pre_lr_policy_del, nbctl_lr_policy_del, NULL, > "--if-exists,--chain=", RW }, > -- > 2.51.0 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > With that addressed: Acked-by: Ales Musil <[email protected]> Regards, Ales _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
