diff --git a/NEWS b/NEWS index dfdb9343c4..b83c4548a8 100644 --- a/NEWS +++ b/NEWS @@ -81,6 +81,10 @@ OVN v24.09.0 - 13 Sep 2024 "routing-protocol-redirect" and "routing-protocols", that allow redirection of routing protocol traffic received by a router port to a different logical switch port. + - Allow Static Routes where the address families of ip_prefix and nexthop + diverge (e.g. IPv4 packets over IPv6 links). This is currently limited to + nexthops that have their mac addresses prepopulated (so + dynamic_neigh_routers must be false). OVN v24.03.0 - 01 Mar 2024 -------------------------- diff --git a/northd/northd.c b/northd/northd.c index 8184b1eddc..7a1ac62c04 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -173,6 +173,7 @@ BUILD_ASSERT_DECL(ACL_OBS_STAGE_MAX < (1 << 2)); #define REGBIT_KNOWN_LB_SESSION "reg9[6]" #define REGBIT_DHCP_RELAY_REQ_CHK "reg9[7]" #define REGBIT_DHCP_RELAY_RESP_CHK "reg9[8]" +#define REGBIT_NEXTHOP_IS_IPV4 "reg9[9]" /* Register to store the eth address associated to a router port for packets * received in S_ROUTER_IN_ADMISSION. @@ -289,7 +290,8 @@ BUILD_ASSERT_DECL(ACL_OBS_STAGE_MAX < (1 << 2)); * | | LOOKUP_NEIGHBOR_RESULT/ | | | * | | SKIP_LOOKUP_NEIGHBOR/ | | | * | |REGBIT_DHCP_RELAY_REQ_CHK/ | | | - * | |REGBIT_DHCP_RELAY_RESP_CHK}| | | + * | |REGBIT_DHCP_RELAY_RESP_CHK | | | + * | |REGBIT_NEXTHOP_IS_IPV4} | | | * | | | | | * | | REG_ORIG_TP_DPORT_ROUTER | | | * | | | | | @@ -10916,13 +10918,15 @@ build_routing_policy_flow(struct lflow_table *lflows, struct ovn_datapath *od, "outport = %s; " "flags.loopback = 1; " REG_ECMP_GROUP_ID" = 0; " + REGBIT_NEXTHOP_IS_IPV4" = %d; " "next;", is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6, nexthop, is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, out_port->lrp_networks.ea_s, - out_port->json_key); + out_port->json_key, + is_ipv4); } else if (!strcmp(rule->action, "drop")) { ds_put_cstr(&actions, debug_drop_action()); @@ -11006,13 +11010,15 @@ build_ecmp_routing_policy_flows(struct lflow_table *lflows, "eth.src = %s; " "outport = %s; " "flags.loopback = 1; " + REGBIT_NEXTHOP_IS_IPV4" = %d; " "next;", is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6, rp->valid_nexthops[i], is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, out_port->lrp_networks.ea_s, - out_port->json_key); + out_port->json_key, + is_ipv4); ds_clear(&match); ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && " @@ -11292,19 +11298,6 @@ parsed_routes_add_static(struct ovn_datapath *od, const struct hmap *lr_ports, return; } - /* Verify that ip_prefix and nexthop have same address familiy. */ - if (valid_nexthop) { - if (IN6_IS_ADDR_V4MAPPED(&prefix) != IN6_IS_ADDR_V4MAPPED(nexthop)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "Address family doesn't match between 'ip_prefix'" - " %s and 'nexthop' %s in static route "UUID_FMT, - route->ip_prefix, route->nexthop, - UUID_ARGS(&route->header_.uuid)); - free(nexthop); - return; - } - } - /* Verify that ip_prefix and nexthop are on the same network. */ const char *lrp_addr_s = NULL; struct ovn_port *out_port = NULL; @@ -11829,7 +11822,7 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, const char *protocol) { - bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(&eg->prefix); + bool is_ipv4_prefix = IN6_IS_ADDR_V4MAPPED(&eg->prefix); uint16_t priority; struct ecmp_route_list_node *er; struct ds route_match = DS_EMPTY_INITIALIZER; @@ -11837,7 +11830,8 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, char *prefix_s = build_route_prefix_s(&eg->prefix, eg->plen); int ofs = route_source_to_offset(eg->source); build_route_match(NULL, eg->route_table_id, prefix_s, eg->plen, - eg->is_src_route, is_ipv4, &route_match, &priority, ofs, + eg->is_src_route, is_ipv4_prefix, &route_match, + &priority, ofs, protocol != NULL); free(prefix_s); @@ -11902,6 +11896,7 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, LIST_FOR_EACH (er, list_node, &eg->route_list) { const struct parsed_route *route_ = er->route; const struct nbrec_logical_router_static_route *route = route_->route; + bool is_ipv4_nexthop = IN6_IS_ADDR_V4MAPPED(route_->nexthop); /* Symmetric ECMP reply is only usable on gateway routers. * It is NOT usable on distributed routers with a gateway port. */ @@ -11919,17 +11914,19 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, eg->id, er->id); ds_clear(&actions); ds_put_format(&actions, "%s = ", - is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); + is_ipv4_nexthop ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); ipv6_format_mapped(route_->nexthop, &actions); ds_put_format(&actions, "; " "%s = %s; " "eth.src = %s; " "outport = %s; " + REGBIT_NEXTHOP_IS_IPV4" = %d; " "next;", - is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, + is_ipv4_nexthop ? REG_SRC_IPV4 : REG_SRC_IPV6, route_->lrp_addr_s, route_->out_port->lrp_networks.ea_s, - route_->out_port->json_key); + route_->out_port->json_key, + is_ipv4_nexthop); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, 100, ds_cstr(&match), ds_cstr(&actions), &route->header_, lflow_ref); @@ -11947,9 +11944,9 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od, bool is_src_route, const uint32_t rtb_id, const struct sset *bfd_ports, const struct ovsdb_idl_row *stage_hint, bool is_discard_route, - enum route_source source, struct lflow_ref *lflow_ref) + enum route_source source, struct lflow_ref *lflow_ref, + bool is_ipv4_prefix, bool is_ipv4_nexthop) { - bool is_ipv4 = strchr(network_s, '.') ? true : false; struct ds match = DS_EMPTY_INITIALIZER; uint16_t priority; const struct ovn_port *op_inport = NULL; @@ -11957,7 +11954,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od, int ofs = route_source_to_offset(source); /* IPv6 link-local addresses must be scoped to the local router port. */ - if (!is_ipv4) { + if (!is_ipv4_prefix) { struct in6_addr network; ovs_assert(ipv6_parse(network_s, &network)); if (in6_is_lla(&network)) { @@ -11965,7 +11962,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od, } } build_route_match(op_inport, rtb_id, network_s, plen, is_src_route, - is_ipv4, &match, &priority, ofs, false); + is_ipv4_prefix, &match, &priority, ofs, false); struct ds common_actions = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; @@ -11973,22 +11970,25 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od, ds_put_cstr(&actions, debug_drop_action()); } else { ds_put_format(&common_actions, REG_ECMP_GROUP_ID" = 0; %s = ", - is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); + is_ipv4_nexthop ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); if (gateway) { ipv6_format_mapped(gateway, &common_actions); } else { - ds_put_format(&common_actions, "ip%s.dst", is_ipv4 ? "4" : "6"); + ds_put_format(&common_actions, "ip%s.dst", + is_ipv4_prefix ? "4" : "6"); } ds_put_format(&common_actions, "; " "%s = %s; " "eth.src = %s; " "outport = %s; " "flags.loopback = 1; " + REGBIT_NEXTHOP_IS_IPV4" = %d; " "next;", - is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, + is_ipv4_nexthop ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, op->lrp_networks.ea_s, - op->json_key); + op->json_key, + is_ipv4_nexthop); ds_put_format(&actions, "ip.ttl--; %s", ds_cstr(&common_actions)); } @@ -12016,6 +12016,10 @@ build_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, struct lflow_ref *lflow_ref) { const struct nbrec_logical_router_static_route *route = route_->route; + bool is_ipv4_prefix = IN6_IS_ADDR_V4MAPPED(&route_->prefix); + bool is_ipv4_nexthop = route_->nexthop + ? IN6_IS_ADDR_V4MAPPED(route_->nexthop) + : is_ipv4_prefix; char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen); add_route(lflows, route_->is_discard_route ? od : route_->out_port->od, @@ -12023,7 +12027,8 @@ build_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, route_->plen, route_->nexthop, route_->is_src_route, route_->route_table_id, bfd_ports, route ? &route->header_ : &route_->out_port->nbrp->header_, - route_->is_discard_route, route_->source, lflow_ref); + route_->is_discard_route, route_->source, lflow_ref, + is_ipv4_prefix, is_ipv4_nexthop); free(prefix_s); } @@ -14189,11 +14194,13 @@ build_arp_resolve_flows_for_lrouter( "ip4.mcast || ip6.mcast", "next;", lflow_ref); - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 1, "ip4", + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 1, + REGBIT_NEXTHOP_IS_IPV4 " == 1", "get_arp(outport, " REG_NEXT_HOP_IPV4 "); next;", lflow_ref); - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 1, "ip6", + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 1, + REGBIT_NEXTHOP_IS_IPV4 " == 0", "get_nd(outport, " REG_NEXT_HOP_IPV6 "); next;", lflow_ref); @@ -17145,7 +17152,8 @@ build_routable_flows_for_router_port( laddrs->ipv4_addrs[k].plen, NULL, false, 0, bfd_ports, &router_port->nbrp->header_, false, ROUTE_SOURCE_CONNECTED, - lrp->stateful_lflow_ref); + lrp->stateful_lflow_ref, + true, true); } } } diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 2efa13b938..addcc45933 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -1857,7 +1857,7 @@ AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.2]) AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.10.0/24 lp0]) AT_CHECK([ovn-nbctl --bfd lr-route-add lr0 10.0.20.0/24 11.0.2.1 lp0]) AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.10.0/24 lp1], [1], [], - [ovn-nbctl: bad IPv4 nexthop argument: lp1 + [ovn-nbctl: bad nexthop argument: lp1 ]) dnl Add overlapping route with 10.0.0.1/24 @@ -1871,13 +1871,13 @@ AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24a 11.0.0.1], [1], [], [ovn-nbctl: bad prefix argument: 10.0.0.111/24a ]) AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1a], [1], [], - [ovn-nbctl: bad IPv4 nexthop argument: 11.0.0.1a + [ovn-nbctl: bad nexthop argument: 11.0.0.1a ]) AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1/24], [1], [], - [ovn-nbctl: bad IPv4 nexthop argument: 11.0.0.1/24 + [ovn-nbctl: bad nexthop argument: 11.0.0.1/24 ]) AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1/64], [1], [], - [ovn-nbctl: bad IPv6 nexthop argument: 2001:0db8:0:f103::1/64 + [ovn-nbctl: bad nexthop argument: 2001:0db8:0:f103::1/64 ]) AT_CHECK([ovn-nbctl --ecmp lr-route-add lr0 20.0.0.0/24 discard], [1], [], [ovn-nbctl: ecmp is not valid for discard routes. @@ -2105,6 +2105,24 @@ check ovn-nbctl lr-route-del lr0 AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl ]) +dnl Check IPv4 over v6 and IPv6 over v4 routes +AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 2001:0db8:0:f103::10]) +AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 11.0.1.10]) + +AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl +IPv4 Routes +Route Table
: + 10.0.0.0/24 2001:db8:0:f103::10 dst-ip + +IPv6 Routes +Route Table
: + 2001:db8::/64 11.0.1.10 dst-ip +]) + +check ovn-nbctl lr-route-del lr0 +AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl +]) + dnl Check IPv4 routes in route table check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 0.0.0.0/0 192.168.0.1 check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0 diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index bb0a2cf9f8..bdab8933d4 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -3441,8 +3441,8 @@ AT_CHECK([grep "lr_in_policy" lr0flows3 | ovn_strip_lflows], [0], [dnl table=??(lr_in_policy ), priority=0 , match=(1), action=(reg8[[0..15]] = 0; next;) table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.3), action=(reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);) table=??(lr_in_policy_ecmp ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=150 , match=(reg8[[0..15]] == 0), action=(next;) ]) @@ -3457,11 +3457,11 @@ sed 's/reg8\[[0..15\]] == [[0-9]]*/reg8\[[0..15\]] == /' | ovn_strip_lf table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.3), action=(reg8[[0..15]] = ; reg8[[16..31]] = select(1, 2);) table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.4), action=(reg8[[0..15]] = ; reg8[[16..31]] = select(1, 2, 3);) table=??(lr_in_policy_ecmp ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=150 , match=(reg8[[0..15]] == ), action=(next;) ]) @@ -3475,13 +3475,13 @@ sed 's/reg8\[[0..15\]] == [[0-9]]*/reg8\[[0..15\]] == /' | ovn_strip_lf table=??(lr_in_policy ), priority=0 , match=(1), action=(reg8[[0..15]] = ; next;) table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.3), action=(reg8[[0..15]] = ; reg8[[16..31]] = select(1, 2);) table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.4), action=(reg8[[0..15]] = ; reg8[[16..31]] = select(1, 2, 3);) - table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; next;) + table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=150 , match=(reg8[[0..15]] == ), action=(next;) ]) @@ -3494,11 +3494,11 @@ sed 's/reg8\[[0..15\]] = [[0-9]]*/reg8\[[0..15\]] = /' | \ sed 's/reg8\[[0..15\]] == [[0-9]]*/reg8\[[0..15\]] == /' | ovn_strip_lflows], [0], [dnl table=??(lr_in_policy ), priority=0 , match=(1), action=(reg8[[0..15]] = ; next;) table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.4), action=(reg8[[0..15]] = ; reg8[[16..31]] = select(1, 2, 3);) - table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; next;) + table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) - table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 1), action=(reg0 = 172.168.0.101; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 2), action=(reg0 = 172.168.0.102; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_policy_ecmp ), priority=100 , match=(reg8[[0..15]] == && reg8[[16..31]] == 3), action=(reg0 = 172.168.0.103; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=150 , match=(reg8[[0..15]] == ), action=(next;) ]) @@ -3510,7 +3510,7 @@ AT_CHECK([grep "lr_in_policy" lr0flows3 | \ sed 's/reg8\[[0..15\]] = [[0-9]]*/reg8\[[0..15\]] = /' | \ sed 's/reg8\[[0..15\]] == [[0-9]]*/reg8\[[0..15\]] == /' | ovn_strip_lflows], [0], [dnl table=??(lr_in_policy ), priority=0 , match=(1), action=(reg8[[0..15]] = ; next;) - table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; next;) + table=??(lr_in_policy ), priority=10 , match=(ip4.src == 10.0.0.5), action=(reg0 = 172.168.0.110; reg5 = 172.168.0.100; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg8[[0..15]] = ; reg9[[9]] = 1; next;) table=??(lr_in_policy_ecmp ), priority=0 , match=(1), action=(drop;) table=??(lr_in_policy_ecmp ), priority=150 , match=(reg8[[0..15]] == ), action=(next;) ]) @@ -6823,14 +6823,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;) table=??(lr_in_ip_routing ), priority=10300, match=(ct_mark.ecmp_reply_port == 1 && reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; eth.src = 00:00:20:20:12:13; reg5 = 192.168.0.1; outport = "lr0-public"; next;) table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra), action=(drop;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing ), priority=162 , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = 1; next;) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 0; next;) ]) AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;) - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;) + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0), action=(next;) ]) @@ -6841,14 +6841,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;) table=??(lr_in_ip_routing ), priority=10300, match=(ct_mark.ecmp_reply_port == 1 && reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; eth.src = 00:00:20:20:12:13; reg5 = 192.168.0.1; outport = "lr0-public"; next;) table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra), action=(drop;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing ), priority=162 , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 0; next;) ]) AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 's/192\.168\.0\..0/192.168.0.??/' | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;) - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;) - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;) + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0), action=(next;) ]) @@ -6870,14 +6870,14 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;) table=??(lr_in_ip_routing ), priority=10300, match=(ct_mark.ecmp_reply_port == 1 && reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; eth.src = 00:00:20:20:12:13; reg5 = 192.168.0.1; outport = "lr0-public"; next;) table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra), action=(drop;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing ), priority=162 , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 0; next;) ]) AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 's/192\.168\.0\..0/192.168.0.??/' | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;) - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;) - table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;) + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 = 192.168.0.??; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; reg9[[9]] = 1; next;) table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0), action=(next;) ]) @@ -6888,14 +6888,14 @@ check ovn-nbctl --wait=sb lr-route-add lr0 1.0.0.0/24 192.168.0.10 ovn-sbctl dump-flows lr0 > lr0flows AT_CHECK([grep -e "lr_in_ip_routing.*192.168.0.10" lr0flows | ovn_strip_lflows], [0], [dnl - table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) ]) check ovn-nbctl --wait=sb lr-route-add lr0 2.0.0.0/24 lr0-public ovn-sbctl dump-flows lr0 > lr0flows AT_CHECK([grep -e "lr_in_ip_routing.*2.0.0.0" lr0flows | ovn_strip_lflows], [0], [dnl - table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) ]) check ovn-nbctl lr-route-add lr0 3.3.0.0/16 192.168.0.11 @@ -6928,6 +6928,66 @@ AT_CHECK([grep -e "(lr_in_ip_routing ).*3.3.0.0" lr0flows | sed 's/table=../ta AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([ovn -- static routes multiple address families flows]) +AT_KEYWORDS([static-routes-flows]) +ovn_start + +check ovn-sbctl chassis-add ch1 geneve 127.0.0.1 + +check ovn-nbctl lr-add lr0 +check ovn-nbctl set logical_router lr0 options:chassis=ch1 +check ovn-nbctl ls-add public +check ovn-nbctl ls-add private +check ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 192.168.0.1/24 +check ovn-nbctl lsp-add public public-lr0 +check ovn-nbctl lsp-set-type public-lr0 router +check ovn-nbctl lsp-set-addresses public-lr0 router +check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public + +check ovn-nbctl lrp-add lr0 lr0-private 00:00:20:20:12:14 2001:db8::1/64 +check ovn-nbctl lsp-add private private-lr0 +check ovn-nbctl lsp-set-type private-lr0 router +check ovn-nbctl lsp-set-addresses private-lr0 router +check ovn-nbctl lsp-set-options private-lr0 router-port=lr0-private + +check ovn-nbctl --wait=sb lr-route-add lr0 10.0.0.0/24 192.168.0.10 +check ovn-nbctl --wait=sb lr-route-add lr0 11.0.0.0/24 2001:db8::10 +check ovn-nbctl --wait=sb lr-route-add lr0 2001:db8:1::/64 192.168.0.20 +check ovn-nbctl --wait=sb lr-route-add lr0 2001:db8:2::/64 2001:db8::20 + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CHECK([grep -e "lr_in_ip_routing " lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra), action=(drop;) + table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 0 && ip4.dst == 11.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = 2001:db8::10; xxreg1 = 2001:db8::1; eth.src = 00:00:20:20:12:14; outport = "lr0-private"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=322 , match=(reg7 == 0 && ip6.dst == 2001:db8:1::/64), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.20; reg5 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=322 , match=(reg7 == 0 && ip6.dst == 2001:db8:2::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = 2001:db8::20; xxreg1 = 2001:db8::1; eth.src = 00:00:20:20:12:14; outport = "lr0-private"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-private" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1214; eth.src = 00:00:20:20:12:14; outport = "lr0-private"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(ip6.dst == 2001:db8::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = 2001:db8::1; eth.src = 00:00:20:20:12:14; outport = "lr0-private"; flags.loopback = 1; reg9[[9]] = 0; next;) +]) + +AT_CHECK([grep -e "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_arp_resolve ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 0), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 1), action=(get_arp(outport, reg0); next;) + table=??(lr_in_arp_resolve ), priority=500 , match=(ip4.mcast || ip6.mcast), action=(next;) +]) + +AT_CHECK([grep -e "lr_in_arp_request" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_arp_request ), priority=0 , match=(1), action=(output;) + table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && ip4), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg5; arp.tpa = reg0; arp.op = 1; output; }; output;) + table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && ip6), action=(nd_ns { nd.target = xxreg0; output; }; output;) + table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::10), action=(nd_ns { eth.dst = 33:33:ff:00:00:10; ip6.dst = ff02::1:ff00:10; nd.target = 2001:db8::10; output; }; output;) + table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::20), action=(nd_ns { eth.dst = 33:33:ff:00:00:20; ip6.dst = ff02::1:ff00:20; nd.target = 2001:db8::20; output; }; output;) +]) + +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD_NO_HV([ AT_SETUP([ovn-northd -- lr multiple gw ports]) AT_KEYWORDS([multiple-l3dgw-ports]) @@ -7346,16 +7406,16 @@ AT_CHECK([grep "lr_in_ip_routing_pre" lr0flows | ovn_strip_lflows], [0], [dnl grep -e "(lr_in_ip_routing ).*outport" lr0flows AT_CHECK([grep -e "(lr_in_ip_routing ).*outport" lr0flows | ovn_strip_lflows], [0], [dnl - table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 1 && ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.1.10; reg5 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.2.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.2.1; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=162 , match=(reg7 == 2 && ip4.dst == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.20; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=2 , match=(reg7 == 0 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=2 , match=(reg7 == 2 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp0" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp1" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:101; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;) - table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp2" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:201; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; next;) + table=??(lr_in_ip_routing ), priority=122 , match=(reg7 == 1 && ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.1.10; reg5 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=124 , match=(ip4.dst == 192.168.2.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 = 192.168.2.1; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=162 , match=(reg7 == 2 && ip4.dst == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.20; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=2 , match=(reg7 == 0 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=2 , match=(reg7 == 2 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg5 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; reg9[[9]] = 1; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp0" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp1" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:101; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; reg9[[9]] = 0; next;) + table=??(lr_in_ip_routing ), priority=324 , match=(inport == "lrp2" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:201; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; reg9[[9]] = 0; next;) ]) AT_CLEANUP @@ -13469,8 +13529,8 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl AT_CHECK([grep "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_arp_resolve ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_arp_resolve ), priority=1 , match=(ip4), action=(get_arp(outport, reg0); next;) - table=??(lr_in_arp_resolve ), priority=1 , match=(ip6), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 0), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 1), action=(get_arp(outport, reg0); next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.100), action=(eth.dst = 00:00:00:00:ff:02; next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 30:54:00:00:00:03; next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) @@ -13639,8 +13699,8 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl AT_CHECK([grep "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_in_arp_resolve ), priority=0 , match=(1), action=(drop;) - table=??(lr_in_arp_resolve ), priority=1 , match=(ip4), action=(get_arp(outport, reg0); next;) - table=??(lr_in_arp_resolve ), priority=1 , match=(ip6), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 0), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(reg9[[9]] == 1), action=(get_arp(outport, reg0); next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.100), action=(eth.dst = 00:00:00:00:ff:02; next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 00:00:00:00:ff:02; next;) table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) diff --git a/tests/ovn.at b/tests/ovn.at index 1d13c5323c..b871891deb 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -39760,3 +39760,648 @@ OVN_CLEANUP([hv1]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([2 HVs, 2 LS, 1 lport/LS, 2 peer LRs, IPv4 over IPv6]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to each other as peers in 2001:db8::/64 +# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it. +# R2 has ls2 (172.16.1.0/24) connected to it. + +ls1_lp1_mac="f0:00:00:01:02:03" +rp_ls1_mac="00:00:00:01:02:03" +rp_ls2_mac="00:00:00:01:02:04" +ls2_lp1_mac="f0:00:00:01:02:04" + +ls1_lp1_ip="192.168.1.2" +ls2_lp1_ip="172.16.1.2" + +check ovn-nbctl lr-add R1 +check ovn-nbctl lr-add R2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24 + +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ + options:router-port=ls1 addresses=\"$rp_ls1_mac\" + +# Connect ls2 to R2 +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24 + +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ + options:router-port=ls2 addresses=\"$rp_ls2_mac\" + +# Connect R1 to R2 +check ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 2001:db8::1/64 peer=R2_R1 +check ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 2001:db8::2/64 peer=R1_R2 + +AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8::2]) +AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8::1]) + +# Create logical port ls1-lp1 in ls1 +check ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" + +# Create logical port ls2-lp1 in ls2 +check ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +check ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Packet to send. +packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \ + UDP(sport=53, dport=4369)") +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# Packet to Expect +# The TTL should be decremented by 2. +expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \ + UDP(sport=53, dport=4369)") +echo ${expected} > expected +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [1 +]) + +# Disable the ls2-lp1 port. +check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [0 +]) + +# Send the same packet again and it should not be delivered +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# The 2nd packet sent shound not be received. +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to ls-transfer in 2001:db8::/64 +# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it. +# R2 has ls2 (172.16.1.0/24) connected to it. + +ls1_lp1_mac="f0:00:00:01:02:03" +rp_ls1_mac="00:00:00:01:02:03" +rp_ls2_mac="00:00:00:01:02:04" +ls2_lp1_mac="f0:00:00:01:02:04" + +ls1_lp1_ip="192.168.1.2" +ls2_lp1_ip="172.16.1.2" + +check ovn-nbctl lr-add R1 +check ovn-nbctl lr-add R2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add ls2 +check ovn-nbctl ls-add ls-transfer + +# Connect ls1 to R1 +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24 + +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ + options:router-port=ls1 addresses=\"$rp_ls1_mac\" + +# Connect ls2 to R2 +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24 + +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ + options:router-port=ls2 addresses=\"$rp_ls2_mac\" + +# Connect R1 to R2 +check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 2001:db8::1/64 +check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 2001:db8::2/64 + +check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \ + set Logical_Switch_Port ls-transfer_r1 type=router \ + options:router-port=R1_ls-transfer addresses=\"router\" +check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \ + set Logical_Switch_Port ls-transfer_r2 type=router \ + options:router-port=R2_ls-transfer addresses=\"router\" + +AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8::2]) +AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8::1]) + +# Create logical port ls1-lp1 in ls1 +check ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" + +# Create logical port ls2-lp1 in ls2 +check ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +check ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Packet to send. +packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \ + UDP(sport=53, dport=4369)") +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# Packet to Expect +# The TTL should be decremented by 2. +expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \ + UDP(sport=53, dport=4369)") +echo ${expected} > expected +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [1 +]) + +# Disable the ls2-lp1 port. +check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [0 +]) + +# Send the same packet again and it should not be delivered +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# The 2nd packet sent shound not be received. +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, static mac]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to ls-transfer in 2001:db8::/64 +# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it. +# R2 has ls2 (172.16.1.0/24) connected to it. + +ls1_lp1_mac="f0:00:00:01:02:03" +rp_ls1_mac="00:00:00:01:02:03" +rp_ls2_mac="00:00:00:01:02:04" +ls2_lp1_mac="f0:00:00:01:02:04" + +ls1_lp1_ip="192.168.1.2" +ls2_lp1_ip="172.16.1.2" + +check ovn-nbctl lr-add R1 +check ovn-nbctl lr-add R2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add ls2 +check ovn-nbctl ls-add ls-transfer + +# Connect ls1 to R1 +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24 +check ovn-nbctl set Logical_Router R1 options:dynamic_neigh_routers=true + +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ + options:router-port=ls1 addresses=\"$rp_ls1_mac\" + +# Connect ls2 to R2 +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24 +check ovn-nbctl set Logical_Router R2 options:dynamic_neigh_routers=true + +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ + options:router-port=ls2 addresses=\"$rp_ls2_mac\" + +# Connect R1 to R2 +check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 2001:db8::1/64 +check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 2001:db8::2/64 + +check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \ + set Logical_Switch_Port ls-transfer_r1 type=router \ + options:router-port=R1_ls-transfer addresses=\"router\" +check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \ + set Logical_Switch_Port ls-transfer_r2 type=router \ + options:router-port=R2_ls-transfer addresses=\"router\" + +# Static mac binding entries +check ovn-nbctl static-mac-binding-add R1_ls-transfer 2001:db8::2 00:00:00:02:03:05 +check ovn-nbctl static-mac-binding-add R2_ls-transfer 2001:db8::1 00:00:00:02:03:04 + +AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8::2]) +AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8::1]) + +# Create logical port ls1-lp1 in ls1 +check ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" + +# Create logical port ls2-lp1 in ls2 +check ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +check ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Packet to send. +packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \ + UDP(sport=53, dport=4369)") +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# Packet to Expect +# The TTL should be decremented by 2. +expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \ + UDP(sport=53, dport=4369)") +echo ${expected} > expected +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [1 +]) + +# Disable the ls2-lp1 port. +check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [0 +]) + +# Send the same packet again and it should not be delivered +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# The 2nd packet sent shound not be received. +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, ECMP]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to ls-transfer1 and lr-transfer2 in +# 2001:db8:1::/64 and 2001:db8:2::/64 +# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it. +# R2 has ls2 (172.16.1.0/24) connected to it. + +ls1_lp1_mac="f0:00:00:01:02:03" +rp_ls1_mac="00:00:00:01:02:03" +rp_ls2_mac="00:00:00:01:02:04" +ls2_lp1_mac="f0:00:00:01:02:04" + +ls1_lp1_ip="192.168.1.2" +ls2_lp1_ip="172.16.1.2" + +check ovn-nbctl lr-add R1 +check ovn-nbctl lr-add R2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add ls2 +check ovn-nbctl ls-add ls-transfer1 +check ovn-nbctl ls-add ls-transfer2 + +# Connect ls1 to R1 +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24 + +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ + options:router-port=ls1 addresses=\"$rp_ls1_mac\" + +# Connect ls2 to R2 +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24 + +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ + options:router-port=ls2 addresses=\"$rp_ls2_mac\" + +# Connect R1 to R2 (ls-transfer1) +check ovn-nbctl lrp-add R1 R1_ls-transfer1 00:00:00:02:03:04 2001:db8:1::1/64 +check ovn-nbctl lrp-add R2 R2_ls-transfer1 00:00:00:02:03:05 2001:db8:1::2/64 + +check ovn-nbctl lsp-add ls-transfer1 ls-transfer1_r1 -- \ + set Logical_Switch_Port ls-transfer1_r1 type=router \ + options:router-port=R1_ls-transfer1 addresses=\"router\" +check ovn-nbctl lsp-add ls-transfer1 ls-transfer1_r2 -- \ + set Logical_Switch_Port ls-transfer1_r2 type=router \ + options:router-port=R2_ls-transfer1 addresses=\"router\" + +# Connect R1 to R2 (ls-transfer2) +check ovn-nbctl lrp-add R1 R1_ls-transfer2 00:00:00:02:03:14 2001:db8:2::1/64 +check ovn-nbctl lrp-add R2 R2_ls-transfer2 00:00:00:02:03:15 2001:db8:2::2/64 + +check ovn-nbctl lsp-add ls-transfer2 ls-transfer2_r1 -- \ + set Logical_Switch_Port ls-transfer2_r1 type=router \ + options:router-port=R1_ls-transfer2 addresses=\"router\" +check ovn-nbctl lsp-add ls-transfer2 ls-transfer2_r2 -- \ + set Logical_Switch_Port ls-transfer2_r2 type=router \ + options:router-port=R2_ls-transfer2 addresses=\"router\" + +AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8:1::2]) +AT_CHECK([ovn-nbctl --ecmp lr-route-add R1 "0.0.0.0/0" 2001:db8:2::2]) +AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8:1::1]) +AT_CHECK([ovn-nbctl --ecmp lr-route-add R2 "0.0.0.0/0" 2001:db8:2::1]) + +# Create logical port ls1-lp1 in ls1 +check ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" + +# Create logical port ls2-lp1 in ls2 +check ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +check ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Packet to send. +packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \ + UDP(sport=53, dport=4369)") +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# Packet to Expect +# The TTL should be decremented by 2. +expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \ + IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \ + UDP(sport=53, dport=4369)") +echo ${expected} > expected +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [1 +]) + +# Disable the ls2-lp1 port. +check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "reg0 == 172.16.1.2" | wc -l], [0], [0 +]) + +# Send the same packet again and it should not be delivered +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# The 2nd packet sent shound not be received. +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([2 HVs, 2 LS, 1 lport/LS, 2 peer LRs, IPv6 over IPv4]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +# Logical network: +# Two LRs - R1 and R2 that are connected to each other as peers in 10.0.0.0/24 +# network. R1 has a switchs ls1 (2001:db8:1::/64) connected to it. +# R2 has ls2 (2001:db8:2::/64) connected to it. + +ls1_lp1_mac="f0:00:00:01:02:03" +rp_ls1_mac="00:00:00:01:02:03" +rp_ls2_mac="00:00:00:01:02:04" +ls2_lp1_mac="f0:00:00:01:02:04" + +ls1_lp1_ip="2001:db8:1::2" +ls2_lp1_ip="2001:db8:2::2" + +check ovn-nbctl lr-add R1 +check ovn-nbctl lr-add R2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 2001:db8:1::1/64 + +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \ + options:router-port=ls1 addresses=\"$rp_ls1_mac\" + +# Connect ls2 to R2 +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 2001:db8:2::1/64 + +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \ + options:router-port=ls2 addresses=\"$rp_ls2_mac\" + +# Connect R1 to R2 +check ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 10.0.0.1/24 peer=R2_R1 +check ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 10.0.0.2/24 peer=R1_R2 + +AT_CHECK([ovn-nbctl lr-route-add R1 "::/0" 10.0.0.2]) +AT_CHECK([ovn-nbctl lr-route-add R2 "::/0" 10.0.0.1]) + +# Create logical port ls1-lp1 in ls1 +check ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip" + +# Create logical port ls2-lp1 in ls2 +check ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip" + +# Create two hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +sim_add hv2 +as hv2 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +check ovs-vsctl -- add-port br-int hv2-vif1 -- \ + set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ + ofport-request=1 + + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Packet to send. +packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \ + IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', hlim=64)/ \ + UDP(sport=53, dport=4369)") +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# Packet to Expect +# The TTL should be decremented by 2. +expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \ + IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', hlim=62)/ \ + UDP(sport=53, dport=4369)") +echo ${expected} > expected +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "xxreg0 == 2001:db8:2::2" | wc -l], [0], [1 +]) + +# Disable the ls2-lp1 port. +check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false + +AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \ +grep "xxreg0 == 2001:db8:2::2" | wc -l], [0], [0 +]) + +# Send the same packet again and it should not be delivered +check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet" + +# The 2nd packet sent shound not be received. +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index d45be75c78..86a3b5966d 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -4591,11 +4591,9 @@ nbctl_lr_route_add(struct ctl_context *ctx) } char *route_table = shash_find_data(&ctx->options, "--route-table"); - bool v6_prefix = false; prefix = normalize_ipv4_prefix_str(ctx->argv[2]); if (!prefix) { prefix = normalize_ipv6_prefix_str(ctx->argv[2]); - v6_prefix = true; } if (!prefix) { ctl_error(ctx, "bad prefix argument: %s", ctx->argv[2]); @@ -4606,15 +4604,15 @@ nbctl_lr_route_add(struct ctl_context *ctx) if (is_discard_route) { next_hop = xasprintf("discard"); } else { - next_hop = v6_prefix - ? normalize_ipv6_addr_str(ctx->argv[3]) - : normalize_ipv4_addr_str(ctx->argv[3]); + next_hop = normalize_ipv4_addr_str(ctx->argv[3]); + if (!next_hop) { + next_hop = normalize_ipv6_addr_str(ctx->argv[3]); + } if (!next_hop) { /* check if it is a output port. */ error = lrp_by_name_or_uuid(ctx, ctx->argv[3], true, &out_lrp); if (error) { - ctl_error(ctx, "bad %s nexthop argument: %s", - v6_prefix ? "IPv6" : "IPv4", ctx->argv[3]); + ctl_error(ctx, "bad nexthop argument: %s", ctx->argv[3]); free(error); goto cleanup; }