On Thu, Nov 14, 2024 at 03:12:46PM +0100, Lorenzo Bianconi wrote: > > here we expand the previous routes-sync engine node to not only > > advertise routes to the southbound table, but also learn received routes > > from this table. > > > > These routes are then passed to the same logic that connected and static > > routes are using for flow generation. > > However we prioritize these routes lower than connected or static routes > > as information in cluster (for the same prefix length) should always be > > more correct then learned routes. > > This is also consistent with the behaviour of phyiscal routers.
Hi Lorenzo, > > Ok, now it is more clear. This node must be processed before lflow generation > but IIRC we use the "en_xxxxx_sync" naming convention just for nodes syncing > info to sb db. Right? Anyway I do not have a strong opinion about it. The closest thing i could find for this is the "bfd_sync" engine node which mostly writes to the southbound db, but also at least syncs the status from there to northbound. But i honestly just did not have another name idea :) > Moreover, this patch needs a repsin and I guess we need to add some unit-tests > for the feature you are adding. All of these patches here will get tests and documentation in the coming v3 (hopefully this week). Thanks for the review Felix > > Regards, > Lorenzo > > > > > Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud> > > --- > > northd/en-lflow.c | 4 +- > > northd/en-routes-sync.c | 156 ++++++++++++++++++++++++++++++-- > > northd/en-routes-sync.h | 6 +- > > northd/inc-proc-northd.c | 2 + > > northd/northd.c | 186 ++++++++++++++++++++++++--------------- > > northd/northd.h | 30 ++++++- > > ovn-sb.ovsschema | 5 +- > > ovn-sb.xml | 11 +++ > > tests/ovn-northd.at | 42 ++++----- > > 9 files changed, 335 insertions(+), 107 deletions(-) > > > > diff --git a/northd/en-lflow.c b/northd/en-lflow.c > > index fa1f0236d..8995f0300 100644 > > --- a/northd/en-lflow.c > > +++ b/northd/en-lflow.c > > @@ -46,6 +46,8 @@ lflow_get_input_data(struct engine_node *node, > > engine_get_input_data("bfd_sync", node); > > struct routes_data *routes_data = > > engine_get_input_data("routes", node); > > + struct routes_sync_data *routes_sync_data = > > + engine_get_input_data("routes_sync", node); > > struct route_policies_data *route_policies_data = > > engine_get_input_data("route_policies", node); > > struct port_group_data *pg_data = > > @@ -82,7 +84,7 @@ lflow_get_input_data(struct engine_node *node, > > lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map; > > lflow_input->svc_monitor_map = &northd_data->svc_monitor_map; > > lflow_input->bfd_ports = &bfd_sync_data->bfd_ports; > > - lflow_input->parsed_routes = &routes_data->parsed_routes; > > + lflow_input->parsed_routes = &routes_sync_data->parsed_routes; > > lflow_input->route_tables = &routes_data->route_tables; > > lflow_input->route_policies = &route_policies_data->route_policies; > > > > diff --git a/northd/en-routes-sync.c b/northd/en-routes-sync.c > > index 581f21b8e..c932cc34e 100644 > > --- a/northd/en-routes-sync.c > > +++ b/northd/en-routes-sync.c > > @@ -29,33 +29,86 @@ VLOG_DEFINE_THIS_MODULE(en_routes_sync); > > static void > > routes_table_sync(struct ovsdb_idl_txn *ovnsb_txn, > > const struct sbrec_route_table *sbrec_route_table, > > - const struct hmap *parsed_routes); > > + const struct hmap *parsed_routes, > > + const struct hmap *lr_ports, > > + const struct ovn_datapaths *lr_datapaths, > > + struct hmap *parsed_routes_out); > > + > > +static void > > +routes_sync_init(struct routes_sync_data *data) > > +{ > > + hmap_init(&data->parsed_routes); > > +} > > + > > +static void > > +routes_sync_destroy(struct routes_sync_data *data) > > +{ > > + struct parsed_route *r; > > + HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) { > > + parsed_route_free(r); > > + } > > + hmap_destroy(&data->parsed_routes); > > +} > > + > > +bool > > +routes_sync_northd_change_handler(struct engine_node *node, > > + void *data OVS_UNUSED) > > +{ > > + struct northd_data *northd_data = engine_get_input_data("northd", > > node); > > + if (!northd_has_tracked_data(&northd_data->trk_data)) { > > + return false; > > + } > > + > > + /* This node uses the below data from the en_northd engine node. > > + * See (lr_stateful_get_input_data()) > > + * 1. northd_data->lr_datapaths > > + * 2. northd_data->lr_ports > > + * This data gets updated when a logical router or logical router > > port > > + * is created or deleted. > > + * Northd engine node presently falls back to full recompute when > > + * this happens and so does this node. > > + * Note: When we add I-P to the created/deleted logical routers or > > + * logical router ports, we need to revisit this handler. > > + */ > > + return true; > > +} > > > > void > > *en_routes_sync_init(struct engine_node *node OVS_UNUSED, > > struct engine_arg *arg OVS_UNUSED) > > { > > - return NULL; > > + struct routes_sync_data *data = xzalloc(sizeof *data); > > + routes_sync_init(data); > > + return data; > > } > > > > void > > -en_routes_sync_cleanup(void *data_ OVS_UNUSED) > > +en_routes_sync_cleanup(void *data) > > { > > + routes_sync_destroy(data); > > } > > > > void > > -en_routes_sync_run(struct engine_node *node, void *data_ OVS_UNUSED) > > +en_routes_sync_run(struct engine_node *node, void *data) > > { > > + routes_sync_destroy(data); > > + routes_sync_init(data); > > + > > + struct routes_sync_data *routes_sync_data = data; > > struct routes_data *routes_data > > = engine_get_input_data("routes", node); > > const struct engine_context *eng_ctx = engine_get_context(); > > const struct sbrec_route_table *sbrec_route_table = > > EN_OVSDB_GET(engine_get_input("SB_route", node)); > > + struct northd_data *northd_data = engine_get_input_data("northd", > > node); > > > > stopwatch_start(ROUTES_SYNC_RUN_STOPWATCH_NAME, time_msec()); > > > > routes_table_sync(eng_ctx->ovnsb_idl_txn, sbrec_route_table, > > - &routes_data->parsed_routes); > > + &routes_data->parsed_routes, > > + &northd_data->lr_ports, > > + &northd_data->lr_datapaths, > > + &routes_sync_data->parsed_routes); > > > > stopwatch_stop(ROUTES_SYNC_RUN_STOPWATCH_NAME, time_msec()); > > engine_set_node_state(node, EN_UPDATED); > > @@ -136,10 +189,93 @@ get_nbrp_or_nbr_option(const struct ovn_port *op, > > const char *key) > > smap_get_bool(&op->od->nbr->options, key, false)); > > } > > > > +static void > > +parse_route_from_sbrec_route(struct hmap *parsed_routes_out, > > + const struct hmap *lr_ports, > > + const struct hmap *lr_datapaths, > > + const struct sbrec_route *route) > > +{ > > + const struct ovn_datapath *od = ovn_datapath_from_sbrec( > > + NULL, lr_datapaths, route->datapath); > > + > > + /* Verify that the next hop is an IP address with an all-ones mask. */ > > + struct in6_addr *nexthop = xmalloc(sizeof(*nexthop)); > > + unsigned int plen; > > + if (!ip46_parse_cidr(route->nexthop, nexthop, &plen)) { > > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > > + VLOG_WARN_RL(&rl, "bad 'nexthop' %s in learned route " > > + UUID_FMT, route->nexthop, > > + UUID_ARGS(&route->header_.uuid)); > > + free(nexthop); > > + return; > > + } > > + if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) || > > + (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) { > > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > > + VLOG_WARN_RL(&rl, "bad next hop mask %s in learned route " > > + UUID_FMT, route->nexthop, > > + UUID_ARGS(&route->header_.uuid)); > > + free(nexthop); > > + return; > > + } > > + > > + /* Parse ip_prefix */ > > + struct in6_addr prefix; > > + if (!ip46_parse_cidr(route->ip_prefix, &prefix, &plen)) { > > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > > + VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in learned route " > > + UUID_FMT, route->ip_prefix, > > + UUID_ARGS(&route->header_.uuid)); > > + free(nexthop); > > + return; > > + } > > + > > + /* Verify that ip_prefix and nexthop have same address familiy. */ > > + 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 learned 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; > > + if (!find_route_outport(lr_ports, route->logical_port, > > + route->ip_prefix, route->nexthop, > > + IN6_IS_ADDR_V4MAPPED(&prefix), > > + &out_port, &lrp_addr_s)) { > > + free(nexthop); > > + return; > > + } > > + > > + parsed_route_add( > > + od, > > + nexthop, > > + prefix, > > + plen, > > + false, > > + lrp_addr_s, > > + out_port, > > + 0, > > + false, > > + false, > > + ROUTE_SOURCE_LEARNED, > > + &route->header_, > > + parsed_routes_out > > + ); > > +} > > + > > static void > > routes_table_sync(struct ovsdb_idl_txn *ovnsb_txn, > > const struct sbrec_route_table *sbrec_route_table, > > - const struct hmap *parsed_routes) > > + const struct hmap *parsed_routes, > > + const struct hmap *lr_ports, > > + const struct ovn_datapaths *lr_datapaths, > > + struct hmap *parsed_routes_out) > > { > > if (!ovnsb_txn) { > > return; > > @@ -159,9 +295,17 @@ routes_table_sync(struct ovsdb_idl_txn *ovnsb_txn, > > sb_route->type); > > route_e->stale = true; > > route_e->sb_route = sb_route; > > + > > + if (!strcmp(route_e->type, "receive")) { > > + parse_route_from_sbrec_route(parsed_routes_out, lr_ports, > > + &lr_datapaths->datapaths, > > + sb_route); > > + } > > } > > > > HMAP_FOR_EACH (route, key_node, parsed_routes) { > > + hmap_insert(parsed_routes_out, > > &parsed_route_clone(route)->key_node, > > + parsed_route_hash(route)); > > if (route->is_discard_route) { > > continue; > > } > > diff --git a/northd/en-routes-sync.h b/northd/en-routes-sync.h > > index ecd41b0b9..391f17452 100644 > > --- a/northd/en-routes-sync.h > > +++ b/northd/en-routes-sync.h > > @@ -16,10 +16,8 @@ > > > > #include "lib/inc-proc-eng.h" > > > > -/*struct routes_sync_data { > > - struct sset routes; > > -};*/ > > - > > +bool routes_sync_northd_change_handler(struct engine_node *node, > > + void *data OVS_UNUSED); > > void *en_routes_sync_init(struct engine_node *, struct engine_arg *); > > void en_routes_sync_cleanup(void *data); > > void en_routes_sync_run(struct engine_node *, void *data); > > diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c > > index bc361ce72..741295709 100644 > > --- a/northd/inc-proc-northd.c > > +++ b/northd/inc-proc-northd.c > > @@ -269,6 +269,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, > > > > engine_add_input(&en_routes_sync, &en_routes, NULL); > > engine_add_input(&en_routes_sync, &en_sb_route, NULL); > > + engine_add_input(&en_routes_sync, &en_northd, > > + routes_sync_northd_change_handler); > > > > engine_add_input(&en_sync_meters, &en_nb_acl, NULL); > > engine_add_input(&en_sync_meters, &en_nb_meter, NULL); > > diff --git a/northd/northd.c b/northd/northd.c > > index 4ad760025..b4412e70c 100644 > > --- a/northd/northd.c > > +++ b/northd/northd.c > > @@ -300,11 +300,14 @@ BUILD_ASSERT_DECL(ACL_OBS_STAGE_MAX < (1 << 2)); > > /* > > * Route offsets implement logic to prioritize traffic for routes with > > * same ip_prefix values: > > - * - connected route overrides static one; > > - * - static route overrides src-ip route. */ > > -#define ROUTE_PRIO_OFFSET_MULTIPLIER 3 > > -#define ROUTE_PRIO_OFFSET_STATIC 1 > > -#define ROUTE_PRIO_OFFSET_CONNECTED 2 > > + * 1. (highest priority) connected routes > > + * 2. static routes > > + * 3. routes learned from the outside via ovn-controller (e.g. bgp) > > + * 4. (lowest priority) src-ip routes */ > > +#define ROUTE_PRIO_OFFSET_MULTIPLIER 4 > > +#define ROUTE_PRIO_OFFSET_LEARNED 1 > > +#define ROUTE_PRIO_OFFSET_STATIC 2 > > +#define ROUTE_PRIO_OFFSET_CONNECTED 3 > > > > /* Returns the type of the datapath to which a flow with the given 'stage' > > may > > * be added. */ > > @@ -11046,7 +11049,7 @@ build_route_table_lflow(struct ovn_datapath *od, > > struct lflow_table *lflows, > > } > > > > static uint32_t > > -route_hash(struct parsed_route *route) > > +route_hash(const struct parsed_route *route) > > { > > return hash_bytes(&route->prefix, sizeof route->prefix, > > (uint32_t)route->plen); > > @@ -11097,7 +11100,7 @@ parsed_route_lookup(struct hmap *routes, size_t > > hash, > > continue; > > } > > > > - if (pr->route != new_pr->route) { > > + if (pr->source_hint != new_pr->source_hint) { > > continue; > > } > > > > @@ -11123,7 +11126,36 @@ parsed_route_lookup(struct hmap *routes, size_t > > hash, > > return NULL; > > } > > > > -static void > > +struct parsed_route * parsed_route_clone(const struct parsed_route *pr) { > > + struct parsed_route *new_pr = xzalloc(sizeof *new_pr); > > + new_pr->prefix = pr->prefix; > > + new_pr->plen = pr->plen; > > + if (pr->nexthop) { > > + new_pr->nexthop = xmemdup(pr->nexthop, sizeof(*pr->nexthop)); > > + } > > + new_pr->route_table_id = pr->route_table_id; > > + new_pr->is_src_route = pr->is_src_route; > > + new_pr->hash = route_hash(pr); > > + new_pr->ecmp_symmetric_reply = pr->ecmp_symmetric_reply; > > + new_pr->is_discard_route = pr->is_discard_route; > > + new_pr->od = pr->od; > > + new_pr->stale = pr->stale; > > + new_pr->source = pr->source; > > + new_pr->source_hint = pr->source_hint; > > + if (pr->lrp_addr_s) { > > + new_pr->lrp_addr_s = xstrdup(pr->lrp_addr_s); > > + } > > + if (pr->out_port) { > > + new_pr->out_port = pr->out_port; > > + } > > + return new_pr; > > +} > > + > > +size_t parsed_route_hash(const struct parsed_route *pr) { > > + return uuid_hash(&pr->od->key); > > +} > > + > > +void > > parsed_route_free(struct parsed_route *pr) { > > if (pr->nexthop) { > > free(pr->nexthop); > > @@ -11135,7 +11167,7 @@ parsed_route_free(struct parsed_route *pr) { > > free(pr); > > } > > > > -static void > > +void > > parsed_route_add(const struct ovn_datapath *od, > > struct in6_addr *nexthop, > > const struct in6_addr prefix, > > @@ -11143,11 +11175,11 @@ parsed_route_add(const struct ovn_datapath *od, > > bool is_discard_route, > > const char *lrp_addr_s, > > const struct ovn_port *out_port, > > - const struct nbrec_logical_router_static_route *route, > > uint32_t route_table_id, > > bool is_src_route, > > bool ecmp_symmetric_reply, > > enum route_source source, > > + const struct ovsdb_idl_row *source_hint, > > struct hmap *routes) > > { > > > > @@ -11166,9 +11198,9 @@ parsed_route_add(const struct ovn_datapath *od, > > } > > new_pr->out_port = out_port; > > new_pr->source = source; > > - new_pr->route = route; > > + new_pr->source_hint = source_hint; > > > > - size_t hash = uuid_hash(&od->key); > > + size_t hash = parsed_route_hash(new_pr); > > struct parsed_route *pr = parsed_route_lookup(routes, hash, new_pr); > > if (!pr) { > > hmap_insert(routes, &new_pr->key_node, hash); > > @@ -11294,8 +11326,8 @@ parsed_routes_add_static(const struct ovn_datapath > > *od, > > } > > > > parsed_route_add(od, nexthop, prefix, plen, is_discard_route, > > lrp_addr_s, > > - out_port, route, route_table_id, is_src_route, > > - ecmp_symmetric_reply, source, > > + out_port, route_table_id, is_src_route, > > + ecmp_symmetric_reply, source, &route->header_, > > routes); > > } > > > > @@ -11311,9 +11343,9 @@ parsed_routes_add_connected(const struct > > ovn_datapath *od, > > > > parsed_route_add(od, NULL, prefix, addr->plen, > > false, addr->addr_s, op, > > - NULL, 0, false, > > + 0, false, > > false, ROUTE_SOURCE_CONNECTED, > > - routes); > > + &op->nbrp->header_, routes); > > } > > > > for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { > > @@ -11323,9 +11355,9 @@ parsed_routes_add_connected(const struct > > ovn_datapath *od, > > > > parsed_route_add(od, NULL, prefix, addr->plen, > > false, addr->addr_s, op, > > - NULL, 0, false, > > + 0, false, > > false, ROUTE_SOURCE_CONNECTED, > > - routes); > > + &op->nbrp->header_, routes); > > } > > } > > > > @@ -11543,6 +11575,41 @@ build_route_match(const struct ovn_port > > *op_inport, uint32_t rtb_id, > > network_s, plen); > > } > > > > +bool > > +find_route_outport(const struct hmap *lr_ports, const char *output_port, > > + const char *ip_prefix, const char *nexthop, bool > > is_ipv4, > > + 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); > > + return false; > > + } > > + if (nexthop[0]) { > > + *lrp_addr_s = find_lrp_member_ip(*out_port, nexthop); > > + } > > + if (!*lrp_addr_s) { > > + /* There are no IP networks configured on the router's port via > > + * which 'route->nexthop' is theoretically reachable. But since > > + * 'out_port' has been specified, we honor it by trying to reach > > + * 'route->nexthop' via the first IP address of 'out_port'. > > + * (There are cases, e.g in GCE, where each VM gets a /32 IP > > + * address and the default gateway is still reachable from it.) */ > > + if (is_ipv4) { > > + if ((*out_port)->lrp_networks.n_ipv4_addrs) { > > + *lrp_addr_s = > > (*out_port)->lrp_networks.ipv4_addrs[0].addr_s; > > + } > > + } else { > > + if ((*out_port)->lrp_networks.n_ipv6_addrs) { > > + *lrp_addr_s = > > (*out_port)->lrp_networks.ipv6_addrs[0].addr_s; > > + } > > + } > > + } > > + return true; > > +} > > + > > /* Output: p_lrp_addr_s and p_out_port. */ > > static bool > > find_static_route_outport(const struct ovn_datapath *od, > > @@ -11553,33 +11620,10 @@ find_static_route_outport(const struct > > ovn_datapath *od, > > const char *lrp_addr_s = NULL; > > struct ovn_port *out_port = NULL; > > if (route->output_port) { > > - out_port = ovn_port_find(lr_ports, route->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", > > - route->output_port, route->ip_prefix); > > + if (!find_route_outport(lr_ports, route->output_port, > > route->ip_prefix, > > + route->nexthop, is_ipv4, &out_port, &lrp_addr_s)) { > > return false; > > } > > - if (route->nexthop[0]) { > > - lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop); > > - } > > - if (!lrp_addr_s) { > > - /* There are no IP networks configured on the router's port via > > - * which 'route->nexthop' is theoretically reachable. But > > since > > - * 'out_port' has been specified, we honor it by trying to > > reach > > - * 'route->nexthop' via the first IP address of 'out_port'. > > - * (There are cases, e.g in GCE, where each VM gets a /32 IP > > - * address and the default gateway is still reachable from > > it.) */ > > - if (is_ipv4) { > > - if (out_port->lrp_networks.n_ipv4_addrs) { > > - lrp_addr_s = > > out_port->lrp_networks.ipv4_addrs[0].addr_s; > > - } > > - } else { > > - if (out_port->lrp_networks.n_ipv6_addrs) { > > - lrp_addr_s = > > out_port->lrp_networks.ipv6_addrs[0].addr_s; > > - } > > - } > > - } > > } else { > > /* output_port is not specified, find the > > * router port matching the next hop. */ > > @@ -11618,7 +11662,6 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > > *lflows, > > struct ds *route_match, > > struct lflow_ref *lflow_ref) > > { > > - const struct nbrec_logical_router_static_route *st_route = > > route->route; > > struct ds match = DS_EMPTY_INITIALIZER; > > struct ds actions = DS_EMPTY_INITIALIZER; > > struct ds ecmp_reply = DS_EMPTY_INITIALIZER; > > @@ -11635,12 +11678,12 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > > *lflows, > > free(cidr); > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100, > > ds_cstr(&match), "ct_next;", > > - &st_route->header_, lflow_ref); > > + route->source_hint, lflow_ref); > > > > /* And packets that go out over an ECMP route need conntrack */ > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100, > > ds_cstr(route_match), "ct_next;", > > - &st_route->header_, lflow_ref); > > + route->source_hint, lflow_ref); > > > > /* Save src eth and inport in ct_label for packets that arrive over > > * an ECMP route. > > @@ -11656,7 +11699,7 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > > *lflows, > > out_port->sb->tunnel_key); > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100, > > ds_cstr(&match), ds_cstr(&actions), > > - &st_route->header_, > > + route->source_hint, > > lflow_ref); > > > > /* Bypass ECMP selection if we already have ct_label information > > @@ -11676,13 +11719,13 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > > *lflows, > > port_ip, out_port->json_key); > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, 10300, > > ds_cstr(&match), ds_cstr(&actions), > > - &st_route->header_, > > + route->source_hint, > > lflow_ref); > > > > /* Egress reply traffic for symmetric ECMP routes skips router > > policies. */ > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, 65535, > > ds_cstr(&ecmp_reply), "next;", > > - &st_route->header_, > > + route->source_hint, > > lflow_ref); > > > > /* Use REG_ECMP_ETH_FULL to pass the eth field from ct_label to > > eth.dst to > > @@ -11699,7 +11742,7 @@ add_ecmp_symmetric_reply_flows(struct lflow_table > > *lflows, > > " pop(" REG_ECMP_ETH_FULL "); next;"; > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_RESOLVE, > > 200, ds_cstr(&ecmp_reply), > > - action, &st_route->header_, > > + action, route->source_hint, > > lflow_ref); > > > > ds_destroy(&match); > > @@ -11715,6 +11758,8 @@ route_source_to_offset(enum route_source source) > > return ROUTE_PRIO_OFFSET_CONNECTED; > > case ROUTE_SOURCE_STATIC: > > return ROUTE_PRIO_OFFSET_STATIC; > > + case ROUTE_SOURCE_LEARNED: > > + return ROUTE_PRIO_OFFSET_LEARNED; > > default: > > OVS_NOT_REACHED(); > > } > > @@ -11767,17 +11812,16 @@ build_ecmp_route_flow(struct lflow_table *lflows, > > struct ovn_datapath *od, > > struct ds match = DS_EMPTY_INITIALIZER; > > struct sset visited_ports = SSET_INITIALIZER(&visited_ports); > > 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; > > + const struct parsed_route *route = er->route; > > /* Symmetric ECMP reply is only usable on gateway routers. > > * It is NOT usable on distributed routers with a gateway port. > > */ > > if (smap_get(&od->nbr->options, "chassis") && > > - route_->ecmp_symmetric_reply && sset_add(&visited_ports, > > - > > route_->out_port->key)) { > > - add_ecmp_symmetric_reply_flows(lflows, od, route_->lrp_addr_s, > > - route_->out_port, > > - route_, &route_match, > > + route->ecmp_symmetric_reply && sset_add(&visited_ports, > > + > > route->out_port->key)) { > > + add_ecmp_symmetric_reply_flows(lflows, od, route->lrp_addr_s, > > + route->out_port, > > + route, &route_match, > > lflow_ref); > > } > > ds_clear(&match); > > @@ -11787,19 +11831,19 @@ build_ecmp_route_flow(struct lflow_table *lflows, > > struct ovn_datapath *od, > > ds_clear(&actions); > > ds_put_format(&actions, "%s = ", > > is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); > > - ipv6_format_mapped(route_->nexthop, &actions); > > + ipv6_format_mapped(route->nexthop, &actions); > > ds_put_format(&actions, "; " > > "%s = %s; " > > "eth.src = %s; " > > "outport = %s; " > > "next;", > > is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, > > - route_->lrp_addr_s, > > - route_->out_port->lrp_networks.ea_s, > > - route_->out_port->json_key); > > + route->lrp_addr_s, > > + route->out_port->lrp_networks.ea_s, > > + route->out_port->json_key); > > ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, > > 100, > > ds_cstr(&match), ds_cstr(&actions), > > - &route->header_, lflow_ref); > > + route->source_hint, lflow_ref); > > } > > sset_destroy(&visited_ports); > > ds_destroy(&match); > > @@ -11878,19 +11922,17 @@ add_route(struct lflow_table *lflows, struct > > ovn_datapath *od, > > > > static void > > build_route_flow(struct lflow_table *lflows, struct ovn_datapath *od, > > - const struct parsed_route *route_, > > + const struct parsed_route *route, > > const struct sset *bfd_ports, > > struct lflow_ref *lflow_ref) > > { > > - const struct nbrec_logical_router_static_route *route = route_->route; > > - > > - char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen); > > - add_route(lflows, route_->is_discard_route ? od : route_->out_port->od, > > - route_->out_port, route_->lrp_addr_s, prefix_s, > > - 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); > > + char *prefix_s = build_route_prefix_s(&route->prefix, route->plen); > > + add_route(lflows, route->is_discard_route ? od : route->out_port->od, > > + route->out_port, route->lrp_addr_s, prefix_s, > > + route->plen, route->nexthop, route->is_src_route, > > + route->route_table_id, bfd_ports, > > + route->source_hint, > > + route->is_discard_route, route->source, lflow_ref); > > > > free(prefix_s); > > } > > diff --git a/northd/northd.h b/northd/northd.h > > index 77faab65d..126d58626 100644 > > --- a/northd/northd.h > > +++ b/northd/northd.h > > @@ -186,6 +186,10 @@ struct routes_data { > > struct hmap bfd_active_connections; > > }; > > > > +struct routes_sync_data { > > + struct hmap parsed_routes; > > +}; > > + > > struct route_policies_data { > > struct hmap route_policies; > > struct hmap bfd_active_connections; > > @@ -701,6 +705,8 @@ enum route_source { > > ROUTE_SOURCE_CONNECTED, > > /* the route is derived from a northbound static route entry */ > > ROUTE_SOURCE_STATIC, > > + /* the route is learned by an ovn-controller */ > > + ROUTE_SOURCE_LEARNED, > > }; > > > > struct parsed_route { > > @@ -711,16 +717,38 @@ struct parsed_route { > > bool is_src_route; > > uint32_t route_table_id; > > uint32_t hash; > > - const struct nbrec_logical_router_static_route *route; > > bool ecmp_symmetric_reply; > > bool is_discard_route; > > const struct ovn_datapath *od; > > bool stale; > > enum route_source source; > > + const struct ovsdb_idl_row *source_hint; > > char *lrp_addr_s; > > const struct ovn_port *out_port; > > }; > > > > +struct parsed_route * parsed_route_clone(const struct parsed_route *pr); > > +size_t parsed_route_hash(const struct parsed_route *pr); > > +void parsed_route_free(struct parsed_route *pr); > > +void parsed_route_add(const struct ovn_datapath *od, > > + struct in6_addr *nexthop, > > + const struct in6_addr prefix, > > + unsigned int plen, > > + bool is_discard_route, > > + const char *lrp_addr_s, > > + const struct ovn_port *out_port, > > + uint32_t route_table_id, > > + bool is_src_route, > > + bool ecmp_symmetric_reply, > > + enum route_source source, > > + const struct ovsdb_idl_row *source_hint, > > + struct hmap *routes); > > + > > +bool > > +find_route_outport(const struct hmap *lr_ports, const char *output_port, > > + const char *ip_prefix, const char *nexthop, bool > > is_ipv4, > > + struct ovn_port **out_port, const char **lrp_addr_s); > > + > > void ovnnb_db_run(struct northd_input *input_data, > > struct northd_data *data, > > struct ovsdb_idl_txn *ovnnb_txn, > > diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema > > index 22e43dc8a..74540782e 100644 > > --- a/ovn-sb.ovsschema > > +++ b/ovn-sb.ovsschema > > @@ -1,7 +1,7 @@ > > { > > "name": "OVN_Southbound", > > "version": "20.38.0", > > - "cksum": "956398967 32154", > > + "cksum": "1944407838 32212", > > "tables": { > > "SB_Global": { > > "columns": { > > @@ -625,11 +625,12 @@ > > "refTable": "Datapath_Binding"}}}, > > "logical_port": {"type": "string"}, > > "ip_prefix": {"type": "string"}, > > + "nexthop": {"type": "string"}, > > "type": {"type": {"key": {"type": "string", > > "enum": ["set", ["advertise", > > "receive"]]}, > > "min": 1, "max": 1}}}, > > - "indexes": [["datapath", "logical_port", "ip_prefix"]], > > + "indexes": [["datapath", "logical_port", "ip_prefix", > > "nexthop"]], > > "isRoot": true} > > } > > } > > diff --git a/ovn-sb.xml b/ovn-sb.xml > > index a65bd2cbb..493b7e839 100644 > > --- a/ovn-sb.xml > > +++ b/ovn-sb.xml > > @@ -5213,6 +5213,17 @@ tcp.flags = RST; > > </p> > > </column> > > > > + <column name="nexthop"> > > + <p> > > + If the type is <code>advertise</code> then this is empty. > > + </p> > > + > > + <p> > > + If the type is <code>receive</code> then this is the nexthop ip we > > + from the outside. > > + </p> > > + </column> > > + > > <column name="type"> > > <p> > > If the route is to be exported from OVN to the outside network or > > if > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > > index 4588a65c6..9583df998 100644 > > --- a/tests/ovn-northd.at > > +++ b/tests/ovn-northd.at > > @@ -6810,9 +6810,9 @@ 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; reg1 = > > 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=194 , 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=74 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 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=97 , 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=130 , 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=259 , 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=99 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; > > flags.loopback = 1; next;) > > ]) > > > > AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | ovn_strip_lflows], > > [0], [dnl > > @@ -6828,9 +6828,9 @@ 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; reg1 = > > 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=194 , 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=74 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 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=97 , 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=130 , 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=259 , 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=99 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; > > flags.loopback = 1; 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;) > > @@ -6857,9 +6857,9 @@ 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; reg1 = > > 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=194 , 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=74 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 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=97 , 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=130 , 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=259 , 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=99 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; > > flags.loopback = 1; 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;) > > @@ -6875,14 +6875,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=73 , match=(reg7 == 0 && > > ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.10; reg1 = 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=98 , match=(reg7 == 0 && > > ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = > > "lr0-public"; flags.loopback = 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=73 , match=(reg7 == 0 && > > ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > ip4.dst; reg1 = 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=98 , match=(reg7 == 0 && > > ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > ip4.dst; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = > > "lr0-public"; flags.loopback = 1; next;) > > ]) > > > > AT_CLEANUP > > @@ -7306,16 +7306,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=1 , match=(reg7 == 0 && > > ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = > > "lrp0"; flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=1 , match=(reg7 == 2 && > > ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = > > "lrp0"; flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=194 , 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=194 , 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=194 , 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=73 , match=(reg7 == 1 && > > ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.1.10; reg1 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = > > "lrp1"; flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; > > flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == > > 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; > > flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == > > 192.168.2.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.2.1; eth.src = 00:00:00:00:02:01; outport = "lrp2"; > > flags.loopback = 1; next;) > > - table=??(lr_in_ip_routing ), priority=97 , match=(reg7 == 2 && > > ip4.dst == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.20; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = > > "lrp0"; flags.loopback = 1; next;) > > + table=??(lr_in_ip_routing ), priority=130 , match=(reg7 == 2 && > > ip4.dst == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.0.20; reg1 = 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; reg1 = 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; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = > > "lrp0"; flags.loopback = 1; next;) > > + table=??(lr_in_ip_routing ), priority=259 , 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=259 , 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=259 , 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=98 , match=(reg7 == 1 && > > ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = > > 192.168.1.10; reg1 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = > > "lrp1"; flags.loopback = 1; next;) > > + table=??(lr_in_ip_routing ), priority=99 , match=(ip4.dst == > > 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; > > flags.loopback = 1; next;) > > + table=??(lr_in_ip_routing ), priority=99 , match=(ip4.dst == > > 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; > > flags.loopback = 1; next;) > > + table=??(lr_in_ip_routing ), priority=99 , match=(ip4.dst == > > 192.168.2.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 > > = 192.168.2.1; eth.src = 00:00:00:00:02:01; outport = "lrp2"; > > flags.loopback = 1; next;) > > ]) > > > > AT_CLEANUP > > -- > > 2.47.0 > > > > > > _______________________________________________ > > dev mailing list > > d...@openvswitch.org > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev