From ab7980c8d46ca3fb76b68f48ce487860796a6b1b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 29 Jun 2021 14:10:35 +0200 Subject: [PATCH 01/10] lib: remove unused connected_add prototype This function is not implemented anywhere. Not sure it ever existed. Signed-off-by: David Lamparter --- lib/if.h | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/if.h b/lib/if.h index a653246ccb2e..da67e6d94c15 100644 --- a/lib/if.h +++ b/lib/if.h @@ -576,7 +576,6 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id); /* Connected address functions. */ extern struct connected *connected_new(void); extern void connected_free(struct connected **connected); -extern void connected_add(struct interface *, struct connected *); extern struct connected * connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *); extern struct connected *connected_delete_by_prefix(struct interface *, From 866bb23cd96058442234d56c06075a7b2eb9142e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 15:08:01 +0200 Subject: [PATCH 02/10] ospf6d: advertise local addresses with LA bit Both for virtual links and correct PtMP operation, advertising local addresses as Intra-Prefix with LA set is a prerequisite. Add the appropriate entries. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 22 +++++++++++++++++++++- ospf6d/ospf6_route.c | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index d3dd5501a43c..fb3881c8187f 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -407,7 +407,6 @@ void ospf6_interface_state_update(struct interface *ifp) void ospf6_interface_connected_route_update(struct interface *ifp) { struct ospf6_interface *oi; - struct ospf6_route *route; struct connected *c; struct listnode *node, *nnode; struct in6_addr nh_addr; @@ -461,6 +460,27 @@ void ospf6_interface_connected_route_update(struct interface *ifp) } } + if (oi->state == OSPF6_INTERFACE_LOOPBACK + || oi->state == OSPF6_INTERFACE_POINTTOPOINT) { + struct ospf6_route *la_route; + + la_route = ospf6_route_create(oi->area->ospf6); + la_route->prefix = *c->address; + la_route->prefix.prefixlen = 128; + la_route->prefix_options |= OSPF6_PREFIX_OPTION_LA; + + la_route->type = OSPF6_DEST_TYPE_NETWORK; + la_route->path.area_id = oi->area->area_id; + la_route->path.type = OSPF6_PATH_TYPE_INTRA; + la_route->path.cost = 0; + inet_pton(AF_INET6, "::1", &nh_addr); + ospf6_route_add_nexthop( + la_route, oi->interface->ifindex, &nh_addr); + ospf6_route_add(la_route, oi->route_connected); + } + + struct ospf6_route *route; + route = ospf6_route_create(oi->area->ospf6); memcpy(&route->prefix, c->address, sizeof(struct prefix)); apply_mask(&route->prefix); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 9603c91a9ad7..4fdebe0c26cb 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -554,6 +554,10 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb) if (ra->path.area_id != rb->path.area_id) return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id)); + if ((ra->prefix_options & OSPF6_PREFIX_OPTION_LA) + != (rb->prefix_options & OSPF6_PREFIX_OPTION_LA)) + return ra->prefix_options & OSPF6_PREFIX_OPTION_LA ? -1 : 1; + return 0; } From 708a603616cc4076d2dd7f865282b92bd9cf36ac Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 20 Jul 2021 17:13:22 +0200 Subject: [PATCH 03/10] ospf6d: factor out link-local addr change For PtMP the cost may need to be recalculated when the LL addr changes (since neighbors are configured by LL addr and a different entry with a different cost may match.) Signed-off-by: David Lamparter --- ospf6d/ospf6_message.c | 2 +- ospf6d/ospf6_neighbor.c | 9 +++++++++ ospf6d/ospf6_neighbor.h | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 3d29a65d1b15..9a50c73960a2 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -491,7 +491,7 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, on->hello_in++; /* Always override neighbor's source address */ - memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr)); + ospf6_neighbor_lladdr_set(on, src); /* Neighbor ifindex check */ if (on->ifindex != (ifindex_t)ntohl(hello->interface_id)) { diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index a4ba1fb5696b..8edf1cead92c 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -195,6 +195,15 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) XFREE(MTYPE_OSPF6_NEIGHBOR, on); } +void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, + const struct in6_addr *addr) +{ + if (IPV6_ADDR_SAME(addr, &on->linklocal_addr)) + return; + + memcpy(&on->linklocal_addr, addr, sizeof(struct in6_addr)); +} + static void ospf6_neighbor_state_change(uint8_t next_state, struct ospf6_neighbor *on, int event) { diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index ea4d4241528f..dcfbe184b020 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -204,6 +204,9 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, struct ospf6_interface *oi); void ospf6_neighbor_delete(struct ospf6_neighbor *on); +void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, + const struct in6_addr *addr); + /* Neighbor event */ extern void hello_received(struct thread *thread); extern void twoway_received(struct thread *thread); From 4ab2de83dd88e8f2f3242dc7ff2fc640a81b258e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 20 Jul 2021 15:57:18 +0200 Subject: [PATCH 04/10] ospf6d: allow configuring PtP neighbors & cost Add a list of configured neighbors for each interface. Only stores cost (and "existence") for now. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.h | 6 ++ ospf6d/ospf6_intra.c | 2 +- ospf6d/ospf6_neighbor.c | 198 +++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_neighbor.h | 30 ++++++ ospf6d/subdir.am | 1 + 5 files changed, 236 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 9a57a7f4dec6..f867b1193078 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -28,6 +28,8 @@ DECLARE_MTYPE(OSPF6_AUTH_MANUAL_KEY); +#include "ospf6_neighbor.h" + /* Debug option */ extern unsigned char conf_debug_ospf6_interface; #define OSPF6_DEBUG_INTERFACE_ON() (conf_debug_ospf6_interface = 1) @@ -81,6 +83,10 @@ struct ospf6_interface { uint8_t type; bool type_cfg; + /* P2P/P2MP behavior: */ + + struct ospf6_if_p2xp_neighcfgs_head p2xp_neighs; + /* Router Priority */ uint8_t priority; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 52bb745d74cf..03ebfc6f9625 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -342,7 +342,7 @@ void ospf6_router_lsa_originate(struct thread *thread) continue; lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT; - lsdesc->metric = htons(oi->cost); + lsdesc->metric = htons(ospf6_neighbor_cost(on)); lsdesc->interface_id = htonl(oi->interface->ifindex); lsdesc->neighbor_interface_id = diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 8edf1cead92c..44f1e8632ecf 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -49,6 +49,18 @@ #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR_P2XP_CFG, + "OSPF6 PtP/PtMP neighbor config"); + +static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, + const struct ospf6_if_p2xp_neighcfg *b); +static struct ospf6_if_p2xp_neighcfg * +ospf6_if_p2xp_find(struct ospf6_interface *oi, const struct in6_addr *addr); + +DECLARE_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs, struct ospf6_if_p2xp_neighcfg, + item, ospf6_if_p2xp_neighcfg_cmp); + +static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost); DEFINE_HOOK(ospf6_neighbor_change, (struct ospf6_neighbor * on, int state, int next_state), @@ -164,6 +176,9 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, void ospf6_neighbor_delete(struct ospf6_neighbor *on) { + if (on->p2xp_cfg) + on->p2xp_cfg->active = NULL; + ospf6_neighbor_clear_ls_lists(on); ospf6_lsdb_remove_all(on->dbdesc_list); @@ -202,6 +217,12 @@ void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, return; memcpy(&on->linklocal_addr, addr, sizeof(struct in6_addr)); + + if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) { + uint32_t prev_cost = ospf6_neighbor_cost(on); + + p2xp_neigh_refresh(on, prev_cost); + } } static void ospf6_neighbor_state_change(uint8_t next_state, @@ -625,8 +646,167 @@ void inactivity_timer(struct thread *thread) } } +/* P2P/P2MP stuff */ + +uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on) +{ + if (on->p2xp_cfg && on->p2xp_cfg->cfg_cost) + return on->p2xp_cfg->cost; + return on->ospf6_if->cost; +} + +static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, + const struct ospf6_if_p2xp_neighcfg *b) +{ + return IPV6_ADDR_CMP(&a->addr, &b->addr); +} + +static struct ospf6_if_p2xp_neighcfg * +ospf6_if_p2xp_find(struct ospf6_interface *oi, const struct in6_addr *addr) +{ + struct ospf6_if_p2xp_neighcfg ref; + + if (!oi) + return NULL; + + ref.addr = *addr; + return ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref); +} + +static struct ospf6_if_p2xp_neighcfg * +ospf6_if_p2xp_get(struct ospf6_interface *oi, const struct in6_addr *addr) +{ + struct ospf6_if_p2xp_neighcfg ref, *ret; + + if (!oi) + return NULL; + + ref.addr = *addr; + ret = ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref); + if (!ret) { + ret = XCALLOC(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, sizeof(*ret)); + ret->addr = *addr; + ret->ospf6_if = oi; + + ospf6_if_p2xp_neighcfgs_add(&oi->p2xp_neighs, ret); + } + + return ret; +} + +static void ospf6_if_p2xp_destroy(struct ospf6_if_p2xp_neighcfg *p2xp_cfg) +{ + THREAD_OFF(p2xp_cfg->t_unicast_hello); + ospf6_if_p2xp_neighcfgs_del(&p2xp_cfg->ospf6_if->p2xp_neighs, p2xp_cfg); + + XFREE(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, p2xp_cfg); +} + +static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost) +{ + if (on->p2xp_cfg) + on->p2xp_cfg->active = NULL; + on->p2xp_cfg = ospf6_if_p2xp_find(on->ospf6_if, &on->linklocal_addr); + if (on->p2xp_cfg) + on->p2xp_cfg->active = on; + + if (ospf6_neighbor_cost(on) != prev_cost) + OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); +} /* vty functions */ + +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_neighbor_clippy.c" +#endif + +DEFPY (ipv6_ospf6_p2xp_neigh, + ipv6_ospf6_p2xp_neigh_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X", + NO_STR + IP6_STR + OSPF6_STR + "Configure static neighbor\n" + "Neighbor link-local address\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + + if (no) { + struct ospf6_neighbor *on; + uint32_t prev_cost = 0; + + p2xp_cfg = ospf6_if_p2xp_find(oi, &neighbor); + if (!p2xp_cfg) + return CMD_SUCCESS; + + on = p2xp_cfg->active; + if (on) + prev_cost = ospf6_neighbor_cost(on); + + p2xp_cfg->active = NULL; + ospf6_if_p2xp_destroy(p2xp_cfg); + + if (on) { + on->p2xp_cfg = NULL; + p2xp_neigh_refresh(on, prev_cost); + } + return CMD_SUCCESS; + } + + p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor); + return CMD_SUCCESS; +} + +DEFPY (ipv6_ospf6_p2xp_neigh_cost, + ipv6_ospf6_p2xp_neigh_cost_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X cost (1-65535)", + NO_STR + IP6_STR + OSPF6_STR + "Configure static neighbor\n" + "Neighbor link-local address\n" + "Outgoing metric for this neighbor\n" + "Outgoing metric for this neighbor\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + + p2xp_cfg = ospf6_if_p2xp_find(oi, &neighbor); + if (!p2xp_cfg) + return CMD_SUCCESS; + + uint32_t prev_cost; + if (p2xp_cfg->active) + prev_cost = ospf6_neighbor_cost(p2xp_cfg->active); + + if (no) { + p2xp_cfg->cfg_cost = false; + p2xp_cfg->cost = 0; + } else { + p2xp_cfg->cfg_cost = true; + p2xp_cfg->cost = cost; + } + + if (p2xp_cfg->active) + p2xp_neigh_refresh(p2xp_cfg->active, prev_cost); + return CMD_SUCCESS; +} + /* show neighbor structure */ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, json_object *json_array, bool use_json) @@ -1234,6 +1414,9 @@ void ospf6_neighbor_init(void) { install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_one_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cost_cmd); } DEFUN (debug_ospf6_neighbor, @@ -1336,6 +1519,21 @@ int config_write_ospf6_debug_neighbor(struct vty *vty) return 0; } +int config_write_ospf6_p2xp_neighbor(struct vty *vty, + struct ospf6_interface *oi) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) { + vty_out(vty, " ipv6 ospf6 neighbor %pI6\n", &p2xp_cfg->addr); + + if (p2xp_cfg->cfg_cost) + vty_out(vty, " ipv6 ospf6 neighbor %pI6 cost %u\n", + &p2xp_cfg->addr, p2xp_cfg->cost); + } + return 0; +} + void install_element_ospf6_debug_neighbor(void) { install_element(ENABLE_NODE, &debug_ospf6_neighbor_cmd); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index dcfbe184b020..2b6da5b367ff 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -21,8 +21,11 @@ #ifndef OSPF6_NEIGHBOR_H #define OSPF6_NEIGHBOR_H +#include "typesafe.h" #include "hook.h" +#include "ospf6_message.h" + /* Forward declaration(s). */ struct ospf6_area; @@ -67,6 +70,8 @@ struct ospf6_helper_info { uint32_t rejected_reason; }; +struct ospf6_if_p2xp_neighcfg; + /* Neighbor structure */ struct ospf6_neighbor { /* Neighbor Router ID String */ @@ -75,6 +80,11 @@ struct ospf6_neighbor { /* OSPFv3 Interface this neighbor belongs to */ struct ospf6_interface *ospf6_if; + /* P2P/P2MP config for this neighbor. + * can be NULL if not explicitly configured! + */ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + /* Neighbor state */ uint8_t state; @@ -154,6 +164,22 @@ struct ospf6_neighbor { bool lls_present; }; +PREDECL_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs); + +struct ospf6_if_p2xp_neighcfg { + struct ospf6_if_p2xp_neighcfgs_item item; + + struct ospf6_interface *ospf6_if; + struct in6_addr addr; + + bool cfg_cost : 1; + + uint32_t cost; + + /* NULL if down */ + struct ospf6_neighbor *active; +}; + /* Neighbor state */ #define OSPF6_NEIGHBOR_DOWN 1 #define OSPF6_NEIGHBOR_ATTEMPT 2 @@ -207,6 +233,8 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on); void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, const struct in6_addr *addr); +uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on); + /* Neighbor event */ extern void hello_received(struct thread *thread); extern void twoway_received(struct thread *thread); @@ -222,6 +250,8 @@ extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); extern void ospf6_neighbor_init(void); extern int config_write_ospf6_debug_neighbor(struct vty *vty); +extern int config_write_ospf6_p2xp_neighbor(struct vty *vty, + struct ospf6_interface *oi); extern void install_element_ospf6_debug_neighbor(void); DECLARE_HOOK(ospf6_neighbor_change, diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 3dff03956cea..9ee17e91d363 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -83,6 +83,7 @@ clippy_scan += \ ospf6d/ospf6_gr.c \ ospf6d/ospf6_nssa.c \ ospf6d/ospf6_route.c \ + ospf6d/ospf6_neighbor.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ From 9dd56e732ec6e90652fb3ca5173ce0309869cd48 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 16:10:23 +0200 Subject: [PATCH 05/10] ospf6d: option to restrict PtP neighbor list This adds a knob to refuse forming adjacencies with neighbors not listed in the config. Only works on PtP/PtMP of course, otherwise the DR/BDR machinery gets broken. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 34 ++++++++++++++++++++++++++++++++++ ospf6d/ospf6_interface.h | 3 +++ ospf6d/ospf6_message.c | 18 ++++++++++++++++++ ospf6d/ospf6_neighbor.c | 6 ++---- ospf6d/ospf6_neighbor.h | 2 ++ 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index fb3881c8187f..64249504851c 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -2596,6 +2596,34 @@ DEFUN (no_ipv6_ospf6_network, return CMD_SUCCESS; } +DEFPY (ipv6_ospf6_p2xp_only_cfg_neigh, + ipv6_ospf6_p2xp_only_cfg_neigh_cmd, + "[no] ipv6 ospf6 p2p-p2mp config-neighbors-only", + NO_STR + IP6_STR + OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Only form adjacencies with explicitly configured neighbors\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + + if (no) { + if (!oi) + return CMD_SUCCESS; + + oi->p2xp_only_cfg_neigh = false; + return CMD_SUCCESS; + } + + if (!oi) + oi = ospf6_interface_create(ifp); + + oi->p2xp_only_cfg_neigh = true; + return CMD_SUCCESS; +} + + static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { struct ospf6_interface *oi; @@ -2660,6 +2688,10 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) vty_out(vty, " ipv6 ospf6 network broadcast\n"); + if (oi->p2xp_only_cfg_neigh) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp config-neighbors-only\n"); + ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); @@ -2782,6 +2814,8 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_network_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_only_cfg_neigh_cmd); + /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index f867b1193078..1d4d6a4a8f7e 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -85,6 +85,9 @@ struct ospf6_interface { /* P2P/P2MP behavior: */ + /* only allow explicitly configured neighbors? */ + bool p2xp_only_cfg_neigh; + struct ospf6_if_p2xp_neighcfgs_head p2xp_neighs; /* Router Priority */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 9a50c73960a2..84865bb3591e 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -422,6 +422,24 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, hello = (struct ospf6_hello *)((caddr_t)oh + sizeof(struct ospf6_header)); + if (oi->state == OSPF6_INTERFACE_POINTTOPOINT + && oi->p2xp_only_cfg_neigh) { + /* NEVER, never, ever, do this on broadcast (or NBMA)! + * DR/BDR election requires everyone to talk to everyone else + * only for PtP/PtMP we can be selective in adjacencies! + */ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + p2xp_cfg = ospf6_if_p2xp_find(oi, src); + if (!p2xp_cfg) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "ignoring PtP/PtMP hello from %pI6, neighbor not configured", + src); + return; + } + } + /* HelloInterval check */ if (ntohs(hello->hello_interval) != oi->hello_interval) { zlog_warn( diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 44f1e8632ecf..558776276b66 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -54,8 +54,6 @@ DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR_P2XP_CFG, static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, const struct ospf6_if_p2xp_neighcfg *b); -static struct ospf6_if_p2xp_neighcfg * -ospf6_if_p2xp_find(struct ospf6_interface *oi, const struct in6_addr *addr); DECLARE_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs, struct ospf6_if_p2xp_neighcfg, item, ospf6_if_p2xp_neighcfg_cmp); @@ -661,8 +659,8 @@ static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a, return IPV6_ADDR_CMP(&a->addr, &b->addr); } -static struct ospf6_if_p2xp_neighcfg * -ospf6_if_p2xp_find(struct ospf6_interface *oi, const struct in6_addr *addr) +struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi, + const struct in6_addr *addr) { struct ospf6_if_p2xp_neighcfg ref; diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 2b6da5b367ff..7998fb370367 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -232,6 +232,8 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on); void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, const struct in6_addr *addr); +struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi, + const struct in6_addr *addr); uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on); From 0565a2a47feea89d820d8cd539be79cdc8e4be72 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 16:12:04 +0200 Subject: [PATCH 06/10] ospf6d: option to disable multicast hellos With the configured neighbor list, unicast hellos can be sent. Allow disabling multicast hellos for that scenario. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 33 +++++++++++++++++++++++++++++++++ ospf6d/ospf6_interface.h | 2 ++ ospf6d/ospf6_message.c | 11 +++++++---- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 64249504851c..2d564131fa32 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -2623,6 +2623,33 @@ DEFPY (ipv6_ospf6_p2xp_only_cfg_neigh, return CMD_SUCCESS; } +DEFPY (ipv6_ospf6_p2xp_no_multicast_hello, + ipv6_ospf6_p2xp_no_multicast_hello_cmd, + "[no] ipv6 ospf6 p2p-p2mp disable-multicast-hello", + NO_STR + IP6_STR + OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Do not send multicast hellos\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + + if (no) { + if (!oi) + return CMD_SUCCESS; + + oi->p2xp_no_multicast_hello = false; + return CMD_SUCCESS; + } + + if (!oi) + oi = ospf6_interface_create(ifp); + + oi->p2xp_no_multicast_hello = true; + return CMD_SUCCESS; +} + static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { @@ -2692,6 +2719,10 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) vty_out(vty, " ipv6 ospf6 p2p-p2mp config-neighbors-only\n"); + if (oi->p2xp_no_multicast_hello) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp disable-multicast-hello\n"); + ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); @@ -2815,6 +2846,8 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_only_cfg_neigh_cmd); + install_element(INTERFACE_NODE, + &ipv6_ospf6_p2xp_no_multicast_hello_cmd); /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 1d4d6a4a8f7e..c67913dbf690 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -85,6 +85,8 @@ struct ospf6_interface { /* P2P/P2MP behavior: */ + /* disable hellos on standard multicast? */ + bool p2xp_no_multicast_hello; /* only allow explicitly configured neighbors? */ bool p2xp_only_cfg_neigh; diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 84865bb3591e..7dc2720ae933 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -2273,6 +2273,13 @@ void ospf6_hello_send(struct thread *thread) return; } + thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, + &oi->thread_send_hello); + + if (oi->state == OSPF6_INTERFACE_POINTTOPOINT + && oi->p2xp_no_multicast_hello) + return 0; + op = ospf6_packet_new(oi->ifmtu); ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s); @@ -2300,10 +2307,6 @@ void ospf6_hello_send(struct thread *thread) */ ospf6_packet_add_top(oi, op); - /* set next thread */ - thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, - &oi->thread_send_hello); - OSPF6_MESSAGE_WRITE_ON(oi); } From 86a1004e2c0f62fa1a306f36f84afd18cae398f1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 17:31:34 +0200 Subject: [PATCH 07/10] ospf6d: support unicast hellos on PtP/PtMP Some lower layers still don't handle multicast correctly (or efficiently.) Add option to send unicast hellos on explicitly configured neighbors for PtP/PtMP. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 4 ++ ospf6d/ospf6_message.c | 62 +++++++++++++++++++++++++------ ospf6d/ospf6_message.h | 5 +++ ospf6d/ospf6_neighbor.c | 79 ++++++++++++++++++++++++++++++++++++++-- ospf6d/ospf6_neighbor.h | 4 ++ 5 files changed, 139 insertions(+), 15 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 2d564131fa32..efb8e846af5a 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -554,6 +554,9 @@ static int ospf6_interface_state_change(uint8_t next_state, OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); } + if (next_state == OSPF6_INTERFACE_POINTTOPOINT) + ospf6_if_p2xp_up(oi); + hook_call(ospf6_interface_change, oi, next_state, prev_state); return 0; @@ -2723,6 +2726,7 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) vty_out(vty, " ipv6 ospf6 p2p-p2mp disable-multicast-hello\n"); + config_write_ospf6_p2xp_neighbor(vty, oi); ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 7dc2720ae933..f2558de12632 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -283,6 +283,18 @@ static struct ospf6_packet *ospf6_packet_new(size_t size) return new; } +static struct ospf6_packet *ospf6_packet_dup(struct ospf6_packet *old) +{ + struct ospf6_packet *new; + + new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet)); + new->s = stream_dup(old->s); + new->dst = old->dst; + new->length = old->length; + + return new; +} + static void ospf6_packet_free(struct ospf6_packet *op) { if (op->s) @@ -2261,8 +2273,6 @@ static void ospf6_write(struct thread *thread) void ospf6_hello_send(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_packet *op; - uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)THREAD_ARG(thread); @@ -2276,9 +2286,16 @@ void ospf6_hello_send(struct thread *thread) thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, &oi->thread_send_hello); - if (oi->state == OSPF6_INTERFACE_POINTTOPOINT - && oi->p2xp_no_multicast_hello) - return 0; + ospf6_hello_send_addr(oi, NULL); +} + +/* used to send polls for PtP/PtMP too */ +void ospf6_hello_send_addr(struct ospf6_interface *oi, + const struct in6_addr *addr) +{ + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + bool anything = false; op = ospf6_packet_new(oi->ifmtu); @@ -2298,16 +2315,37 @@ void ospf6_hello_send(struct thread *thread) /* Set packet length. */ op->length = length; - op->dst = allspfrouters6; + if (!addr && oi->state == OSPF6_INTERFACE_POINTTOPOINT + && oi->p2xp_no_multicast_hello) { + struct listnode *node; + struct ospf6_neighbor *on; + struct ospf6_packet *opdup; - ospf6_fill_hdr_checksum(oi, op); + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) { + if (on->state < OSPF6_NEIGHBOR_INIT) + /* poll-interval for these */ + continue; - /* Add packet to the top of the interface output queue, so that they - * can't get delayed by things like long queues of LS Update packets - */ - ospf6_packet_add_top(oi, op); + opdup = ospf6_packet_dup(op); + opdup->dst = on->linklocal_addr; + ospf6_packet_add_top(oi, opdup); + anything = true; + } - OSPF6_MESSAGE_WRITE_ON(oi); + ospf6_packet_free(op); + } else { + op->dst = addr ? *addr : allspfrouters6; + + /* Add packet to the top of the interface output queue, so that + * they can't get delayed by things like long queues of LS + * Update packets + */ + ospf6_packet_add_top(oi, op); + anything = true; + } + + if (anything) + OSPF6_MESSAGE_WRITE_ON(oi); } static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index daab98640efe..a8f3c4ca8f76 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -65,6 +65,8 @@ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ #define OSPF6_MESSAGE_TYPE_MAX 0x6 /* same as OSPF6_MESSAGE_TYPE_ALL */ +struct ospf6_interface; + struct ospf6_packet { struct ospf6_packet *next; @@ -184,6 +186,9 @@ extern void ospf6_lsupdate_send_neighbor(struct thread *thread); extern void ospf6_lsack_send_interface(struct thread *thread); extern void ospf6_lsack_send_neighbor(struct thread *thread); +extern void ospf6_hello_send_addr(struct ospf6_interface *oi, + const struct in6_addr *addr); + extern int config_write_ospf6_debug_message(struct vty *); extern void install_element_ospf6_debug_message(void); extern const char *ospf6_message_type(int type); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 558776276b66..f61c75a581e5 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -784,9 +784,7 @@ DEFPY (ipv6_ospf6_p2xp_neigh_cost, oi = ospf6_interface_create(ifp); } - p2xp_cfg = ospf6_if_p2xp_find(oi, &neighbor); - if (!p2xp_cfg) - return CMD_SUCCESS; + p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor); uint32_t prev_cost; if (p2xp_cfg->active) @@ -805,6 +803,74 @@ DEFPY (ipv6_ospf6_p2xp_neigh_cost, return CMD_SUCCESS; } +static void p2xp_unicast_hello_send(struct thread *thread); + +static void p2xp_unicast_hello_sched(struct ospf6_if_p2xp_neighcfg *p2xp_cfg) +{ + if (!p2xp_cfg->poll_interval + || p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOPOINT) + /* state check covers DOWN state too */ + THREAD_OFF(p2xp_cfg->t_unicast_hello); + else + thread_add_timer(master, p2xp_unicast_hello_send, p2xp_cfg, + p2xp_cfg->poll_interval, + &p2xp_cfg->t_unicast_hello); +} + +void ospf6_if_p2xp_up(struct ospf6_interface *oi) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) + p2xp_unicast_hello_sched(p2xp_cfg); +} + +static void p2xp_unicast_hello_send(struct thread *thread) +{ + struct ospf6_if_p2xp_neighcfg *p2xp_cfg = THREAD_ARG(thread); + struct ospf6_interface *oi = p2xp_cfg->ospf6_if; + + if (oi->state != OSPF6_INTERFACE_POINTTOPOINT) + return; + + p2xp_unicast_hello_sched(p2xp_cfg); + + if (p2xp_cfg->active && p2xp_cfg->active->state >= OSPF6_NEIGHBOR_INIT) + return; + + ospf6_hello_send_addr(oi, &p2xp_cfg->addr); +} + +DEFPY (ipv6_ospf6_p2xp_neigh_poll_interval, + ipv6_ospf6_p2xp_neigh_poll_interval_cmd, + "[no] ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)", + NO_STR + IP6_STR + OSPF6_STR + "Configure static neighbor\n" + "Neighbor link-local address\n" + "Send unicast hellos to neighbor when down\n" + "Unicast hello interval when down (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + struct ospf6_if_p2xp_neighcfg *p2xp_cfg; + + if (!oi) { + if (no) + return CMD_SUCCESS; + oi = ospf6_interface_create(ifp); + } + if (no) + poll_interval = 0; + + p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor); + p2xp_cfg->poll_interval = poll_interval; + + p2xp_unicast_hello_sched(p2xp_cfg); + return CMD_SUCCESS; +} + /* show neighbor structure */ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, json_object *json_array, bool use_json) @@ -1415,6 +1481,8 @@ void ospf6_neighbor_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cost_cmd); + install_element(INTERFACE_NODE, + &ipv6_ospf6_p2xp_neigh_poll_interval_cmd); } DEFUN (debug_ospf6_neighbor, @@ -1525,6 +1593,11 @@ int config_write_ospf6_p2xp_neighbor(struct vty *vty, frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) { vty_out(vty, " ipv6 ospf6 neighbor %pI6\n", &p2xp_cfg->addr); + if (p2xp_cfg->poll_interval) + vty_out(vty, + " ipv6 ospf6 neighbor %pI6 poll-interval %u\n", + &p2xp_cfg->addr, p2xp_cfg->poll_interval); + if (p2xp_cfg->cfg_cost) vty_out(vty, " ipv6 ospf6 neighbor %pI6 cost %u\n", &p2xp_cfg->addr, p2xp_cfg->cost); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 7998fb370367..a18e710a5320 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -175,9 +175,12 @@ struct ospf6_if_p2xp_neighcfg { bool cfg_cost : 1; uint32_t cost; + uint16_t poll_interval; /* NULL if down */ struct ospf6_neighbor *active; + + struct thread *t_unicast_hello; }; /* Neighbor state */ @@ -234,6 +237,7 @@ void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, const struct in6_addr *addr); struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi, const struct in6_addr *addr); +void ospf6_if_p2xp_up(struct ospf6_interface *oi); uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on); From 49bef9a454befe1bc720d6d33c35722546446cda Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 10:05:36 +0200 Subject: [PATCH 08/10] ospf6d: add point-to-multipoint interface mode This adds the PtMP interface type, which is effectively identical to PtP except that all the database flooding & updates are unicast. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 26 +++++++++++++++++++++----- ospf6d/ospf6_interface.h | 19 ++++++++++--------- ospf6d/ospf6_intra.c | 4 +++- ospf6d/ospf6_message.c | 8 +++++--- ospf6d/ospf6_neighbor.c | 12 +++++++++--- ospf6d/ospf6_snmp.c | 3 +++ 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index efb8e846af5a..335ff2561d6c 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -63,8 +63,8 @@ DEFINE_HOOK(ospf6_interface_change, unsigned char conf_debug_ospf6_interface = 0; const char *const ospf6_interface_state_str[] = { - "None", "Down", "Loopback", "Waiting", "PointToPoint", - "DROther", "BDR", "DR", NULL}; + "None", "Down", "Loopback", "Waiting", "PointToPoint", + "PtMultipoint", "DROther", "BDR", "DR", NULL}; int ospf6_interface_neighbor_count(struct ospf6_interface *oi) { @@ -461,6 +461,7 @@ void ospf6_interface_connected_route_update(struct interface *ifp) } if (oi->state == OSPF6_INTERFACE_LOOPBACK + || oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT || oi->state == OSPF6_INTERFACE_POINTTOPOINT) { struct ospf6_route *la_route; @@ -554,7 +555,8 @@ static int ospf6_interface_state_change(uint8_t next_state, OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); } - if (next_state == OSPF6_INTERFACE_POINTTOPOINT) + if (next_state == OSPF6_INTERFACE_POINTTOPOINT + || next_state == OSPF6_INTERFACE_POINTTOMULTIPOINT) ospf6_if_p2xp_up(oi); hook_call(ospf6_interface_change, oi, next_state, prev_state); @@ -861,6 +863,9 @@ void interface_up(struct thread *thread) ospf6_interface_state_change(OSPF6_INTERFACE_LOOPBACK, oi); } else if (oi->type == OSPF_IFTYPE_POINTOPOINT) { ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi); + } else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOMULTIPOINT, + oi); } else if (oi->priority == 0) ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi); else { @@ -989,6 +994,8 @@ static const char *ospf6_iftype_str(uint8_t iftype) return "BROADCAST"; case OSPF_IFTYPE_POINTOPOINT: return "POINTOPOINT"; + case OSPF_IFTYPE_POINTOMULTIPOINT: + return "POINTOMULTIPOINT"; } return "UNKNOWN"; } @@ -2523,12 +2530,13 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_list, DEFUN (ipv6_ospf6_network, ipv6_ospf6_network_cmd, - "ipv6 ospf6 network ", + "ipv6 ospf6 network ", IP6_STR OSPF6_STR "Network type\n" "Specify OSPF6 broadcast network\n" "Specify OSPF6 point-to-point network\n" + "Specify OSPF6 point-to-multipoint network\n" ) { VTY_DECLVAR_CONTEXT(interface, ifp); @@ -2554,6 +2562,11 @@ DEFUN (ipv6_ospf6_network, return CMD_SUCCESS; } oi->type = OSPF_IFTYPE_POINTOPOINT; + } else if (strncmp(argv[idx_network]->arg, "point-to-m", 10) == 0) { + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + return CMD_SUCCESS; + } + oi->type = OSPF_IFTYPE_POINTOMULTIPOINT; } /* Reset the interface */ @@ -2713,7 +2726,10 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) if (oi->mtu_ignore) vty_out(vty, " ipv6 ospf6 mtu-ignore\n"); - if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) + if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + vty_out(vty, + " ipv6 ospf6 network point-to-multipoint\n"); + else if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) vty_out(vty, " ipv6 ospf6 network point-to-point\n"); else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) vty_out(vty, " ipv6 ospf6 network broadcast\n"); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index c67913dbf690..c778ba14a514 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -188,15 +188,16 @@ struct ospf6_interface { DECLARE_QOBJ_TYPE(ospf6_interface); /* interface state */ -#define OSPF6_INTERFACE_NONE 0 -#define OSPF6_INTERFACE_DOWN 1 -#define OSPF6_INTERFACE_LOOPBACK 2 -#define OSPF6_INTERFACE_WAITING 3 -#define OSPF6_INTERFACE_POINTTOPOINT 4 -#define OSPF6_INTERFACE_DROTHER 5 -#define OSPF6_INTERFACE_BDR 6 -#define OSPF6_INTERFACE_DR 7 -#define OSPF6_INTERFACE_MAX 8 +#define OSPF6_INTERFACE_NONE 0 +#define OSPF6_INTERFACE_DOWN 1 +#define OSPF6_INTERFACE_LOOPBACK 2 +#define OSPF6_INTERFACE_WAITING 3 +#define OSPF6_INTERFACE_POINTTOPOINT 4 +#define OSPF6_INTERFACE_POINTTOMULTIPOINT 5 +#define OSPF6_INTERFACE_DROTHER 6 +#define OSPF6_INTERFACE_BDR 7 +#define OSPF6_INTERFACE_DR 8 +#define OSPF6_INTERFACE_MAX 9 extern const char *const ospf6_interface_state_str[]; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 03ebfc6f9625..f8107e0b16f4 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -336,7 +336,8 @@ void ospf6_router_lsa_originate(struct thread *thread) } /* Point-to-Point interfaces */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { if (on->state != OSPF6_NEIGHBOR_FULL) continue; @@ -1083,6 +1084,7 @@ void ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) if (oi->state != OSPF6_INTERFACE_LOOPBACK && oi->state != OSPF6_INTERFACE_POINTTOPOINT + && oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT && full_count != 0) { if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) zlog_debug(" Interface %s is not stub, ignore", diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index f2558de12632..b753713401ee 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -434,7 +434,8 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, hello = (struct ospf6_hello *)((caddr_t)oh + sizeof(struct ospf6_header)); - if (oi->state == OSPF6_INTERFACE_POINTTOPOINT + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT + || oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT) && oi->p2xp_only_cfg_neigh) { /* NEVER, never, ever, do this on broadcast (or NBMA)! * DR/BDR election requires everyone to talk to everyone else @@ -2315,8 +2316,9 @@ void ospf6_hello_send_addr(struct ospf6_interface *oi, /* Set packet length. */ op->length = length; - if (!addr && oi->state == OSPF6_INTERFACE_POINTTOPOINT - && oi->p2xp_no_multicast_hello) { + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT + || oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT) + && !addr && oi->p2xp_no_multicast_hello) { struct listnode *node; struct ospf6_neighbor *on; struct ospf6_packet *opdup; diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index f61c75a581e5..2b052299aae9 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -216,7 +216,8 @@ void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on, memcpy(&on->linklocal_addr, addr, sizeof(struct in6_addr)); - if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) { + if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT + || on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT) { uint32_t prev_cost = ospf6_neighbor_cost(on); p2xp_neigh_refresh(on, prev_cost); @@ -299,6 +300,7 @@ static void ospf6_neighbor_state_change(uint8_t next_state, static int need_adjacency(struct ospf6_neighbor *on) { if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT + || on->ospf6_if->state == OSPF6_INTERFACE_POINTTOMULTIPOINT || on->ospf6_if->state == OSPF6_INTERFACE_DR || on->ospf6_if->state == OSPF6_INTERFACE_BDR) return 1; @@ -808,7 +810,8 @@ static void p2xp_unicast_hello_send(struct thread *thread); static void p2xp_unicast_hello_sched(struct ospf6_if_p2xp_neighcfg *p2xp_cfg) { if (!p2xp_cfg->poll_interval - || p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOPOINT) + || (p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOMULTIPOINT + && p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOPOINT)) /* state check covers DOWN state too */ THREAD_OFF(p2xp_cfg->t_unicast_hello); else @@ -830,7 +833,8 @@ static void p2xp_unicast_hello_send(struct thread *thread) struct ospf6_if_p2xp_neighcfg *p2xp_cfg = THREAD_ARG(thread); struct ospf6_interface *oi = p2xp_cfg->ospf6_if; - if (oi->state != OSPF6_INTERFACE_POINTTOPOINT) + if (oi->state != OSPF6_INTERFACE_POINTTOPOINT + && oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT) return; p2xp_unicast_hello_sched(p2xp_cfg); @@ -905,6 +909,8 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, /* Neighbor State */ if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) snprintf(nstate, sizeof(nstate), "PointToPoint"); + else if (on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT) + snprintf(nstate, sizeof(nstate), "PtMultipoint"); else { if (on->router_id == on->drouter) snprintf(nstate, sizeof(nstate), "DR"); diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 1070474d0f59..f666638c568a 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -1141,6 +1141,8 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); else if (oi->type == OSPF_IFTYPE_POINTOPOINT) return SNMP_INTEGER(3); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + return SNMP_INTEGER(5); else break; /* Unknown, don't put anything */ case OSPFv3IFADMINSTATUS: @@ -1382,6 +1384,7 @@ static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state, /* Terminal state or regression */ if ((next_state != OSPF6_INTERFACE_POINTTOPOINT) + && (next_state != OSPF6_INTERFACE_POINTTOMULTIPOINT) && (next_state != OSPF6_INTERFACE_DROTHER) && (next_state != OSPF6_INTERFACE_BDR) && (next_state != OSPF6_INTERFACE_DR) && (next_state >= prev_state)) From 636b7202737a8a43702e7cfccef97225be1b86aa Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jul 2021 16:13:08 +0200 Subject: [PATCH 09/10] ospf6d: connected prefix toggle for PtP/PtMP To announce connected prefixes, or not to announce connected prefixes, that is the question... Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 66 ++++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_interface.h | 5 +++ ospf6d/subdir.am | 1 + 3 files changed, 72 insertions(+) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 335ff2561d6c..6f1972bb4bf1 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -480,6 +480,13 @@ void ospf6_interface_connected_route_update(struct interface *ifp) ospf6_route_add(la_route, oi->route_connected); } + if (oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT + && !oi->p2xp_connected_pfx_include) + continue; + if (oi->state == OSPF6_INTERFACE_POINTTOPOINT + && oi->p2xp_connected_pfx_exclude) + continue; + struct ospf6_route *route; route = ospf6_route_create(oi->area->ospf6); @@ -1392,6 +1399,10 @@ static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id, return CMD_SUCCESS; } +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_interface_clippy.c" +#endif + /* show interface */ DEFUN(show_ipv6_ospf6_interface, show_ipv6_ospf6_interface_ifname_cmd, "show ipv6 ospf6 [vrf ] interface [IFNAME] [json]", @@ -2666,6 +2677,52 @@ DEFPY (ipv6_ospf6_p2xp_no_multicast_hello, return CMD_SUCCESS; } +DEFPY (ipv6_ospf6_p2xp_connected_pfx, + ipv6_ospf6_p2xp_connected_pfx_cmd, + "[no] ipv6 ospf6 p2p-p2mp connected-prefixes ", + NO_STR + IP6_STR + OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Adjust handling of directly connected prefixes\n" + "Advertise prefixes and own /128 (default for PtP)\n" + "Ignore, only advertise own /128 (default for PtMP)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi = ifp->info; + bool old_incl, old_excl; + + if (no && !oi) + return CMD_SUCCESS; + + if (!oi) + oi = ospf6_interface_create(ifp); + + old_incl = oi->p2xp_connected_pfx_include; + old_excl = oi->p2xp_connected_pfx_exclude; + oi->p2xp_connected_pfx_include = false; + oi->p2xp_connected_pfx_exclude = false; + + if (incl && !no) + oi->p2xp_connected_pfx_include = true; + if (excl && !no) + oi->p2xp_connected_pfx_exclude = true; + + if (oi->p2xp_connected_pfx_include != old_incl + || oi->p2xp_connected_pfx_exclude != old_excl) + ospf6_interface_connected_route_update(ifp); + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_p2xp_connected_pfx, + no_ipv6_ospf6_p2xp_connected_pfx_cmd, + "no ipv6 ospf6 p2p-p2mp connected-prefixes", + NO_STR + IP6_STR + OSPF6_STR + "Point-to-point and Point-to-Multipoint parameters\n" + "Adjust handling of directly connected prefixes\n") + static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { @@ -2742,6 +2799,13 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) vty_out(vty, " ipv6 ospf6 p2p-p2mp disable-multicast-hello\n"); + if (oi->p2xp_connected_pfx_include) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp connected-prefixes include\n"); + else if (oi->p2xp_connected_pfx_exclude) + vty_out(vty, + " ipv6 ospf6 p2p-p2mp connected-prefixes exclude\n"); + config_write_ospf6_p2xp_neighbor(vty, oi); ospf6_bfd_write_config(vty, oi); @@ -2868,6 +2932,8 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_only_cfg_neigh_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_no_multicast_hello_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_connected_pfx_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_p2xp_connected_pfx_cmd); /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index c778ba14a514..ef4c7935bb0b 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -89,6 +89,11 @@ struct ospf6_interface { bool p2xp_no_multicast_hello; /* only allow explicitly configured neighbors? */ bool p2xp_only_cfg_neigh; + /* override mode default for advertising connected prefixes. + * both false by default (= do include for PtP, exclude for PtMP) + */ + bool p2xp_connected_pfx_include; + bool p2xp_connected_pfx_exclude; struct ospf6_if_p2xp_neighcfgs_head p2xp_neighs; diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 9ee17e91d363..d5e1b26219ff 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -81,6 +81,7 @@ clippy_scan += \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_gr.c \ + ospf6d/ospf6_interface.c \ ospf6d/ospf6_nssa.c \ ospf6d/ospf6_route.c \ ospf6d/ospf6_neighbor.c \ From af9ddb7308a2aa6c93866fed3d8a2d485660173e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 21 Jul 2021 09:07:26 +0200 Subject: [PATCH 10/10] doc: update user docs for OSPFv3 PtMP changes Update & add docs for all the stuff in the previous 10-ish commits. Signed-off-by: David Lamparter --- doc/user/ospf6d.rst | 127 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 8dacb9c9dc25..a7692452ade5 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -299,10 +299,135 @@ OSPF6 interface Sets interface's Inf-Trans-Delay. Default value is 1. -.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point) +.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point|point-to-multipoint) Set explicitly network type for specified interface. + The only functional difference between ``point-to-point`` (PtP) and + ``point-to-multipoint`` (PtMP) mode is the packet addressing for database + flooding and updates. PtP will use multicast packets while PtMP will + unicast them. Apart from this, + :clicmd:`ipv6 ospf6 p2p-p2mp connected-prefixes ` has a + different default for PtP and PtMP. There are no other differences, in + particular FRR does not impose a limit of one neighbor in PtP mode. + + FRR does not support NBMA mode for IPv6 and likely never will, as NBMA is + considered deprecated for IPv6. Refer to `this IETF OSPF working group + discussion + `_ + for context. + +OSPF6 point-to-point and point-to-multipoint operation +====================================================== + +OSPFv3, by default, operates in broadcast mode where it elects a DR and BDR +for each network segment. This can be changed to point-to-point (PtP) / +point-to-multipoint (PtMP) mode by configuration. The actual physical +interface characteristics do not matter for this setting, all interfaces can +be configured for all modes. However, routers must be configured for the same +mode to form adjacencies. + +The main advantages of PtP/PtMP mode are: + +- no DR/BDR election +- adjacencies can be suppressed in a pairwise manner for any two routers, e.g. + to represent the underlying topology if it isn't a true full mesh +- distinct costs can be set for each pair of routers and direction + +The main downside is less efficient flooding on networks with a large number +of OSPFv3 routers. + +.. warning:: + + All options in this section should be considered "advanced" configuration + options. Inconsistent or nonsensical combinations can easily result in a + non-functional setup. + +.. clicmd:: ipv6 ospf6 p2p-p2mp disable-multicast-hello + + Disables sending normal multicast hellos when in PtP/PtMP mode. Some + vendors do this automatically for PtMP mode while others have a separate + ``no-broadcast`` option matching this. + + If this setting is used, you must issue + :clicmd:`ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)` for each + neighbor to send unicast hello packets. + +.. clicmd:: ipv6 ospf6 p2p-p2mp config-neighbors-only + + Only form adjacencies with neighbors that are explicitly configured with + the :clicmd:`ipv6 ospf6 neighbor X:X::X:X` command. Hellos from other + routers are ignored. + + .. warning:: + + This setting is not intended to provide any security benefit. Do not + run OSPFv3 over untrusted links without additional security measures + (e.g. IPsec.) + +.. clicmd:: ipv6 ospf6 p2p-p2mp connected-prefixes + + For global/ULA prefixes configured on this interfaces, do (not) advertise + the full prefix to the area. Regardless of this setting, the router's own + address, as a /128 host route with the "LA" (Local Address) bit set, will + always be advertised. + + The default is to include connected prefixes for PtP mode and exclude them + for PtMP mode. Since these prefixes will cover other router's addresses, + these addresses can become unreachable if the link is partitioned if the + other router does not advertise the address as a /128. However, conversely, + if all routers have this flag set, the overall prefix will not be advertised + anywhere. End hosts on this link will therefore be unreachable (and + blackholing best-practices for non-existing prefixes apply.) It may be + preferable to have only one router announce the connected prefix. + + The Link LSA (which is not propagated into the area) always includes all + prefixes on the interface. This setting only affects the Router LSA that + is visible to all routers in the area. + + .. note:: + + Before interacting with this setting, consider either not configuring + any global/ULA IPv6 address on the interface, or directly configuring a + /128 if needed. OSPFv3 relies exclusively on link-local addresses to do + its signaling and there is absolutely no reason to configure global/ULA + addresses as far as OSPFv3 is concerned. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X + + Explicitly configure a neighbor by its link-local address on this interface. + This statement has no effect other than allowing an adjacency when + :clicmd:`ipv6 ospf6 p2p-p2mp config-neighbors-only` is set. This command + does **not** cause unicast hellos to be sent. + + Only link-local addresses can be used to establish explicit neighbors. + When using this command, you should probably assign static IPv6 link-local + addresses to all routers on this link. It would technically be possible to + use the neighbor's Router ID (IPv4 address) here to ease working with + changing link-local addresses but this is not planned as a feature at the + time of writing. Global/ULA IPv6 addresses cannot be supported here due to + the way OSPFv3 works. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535) + + Send unicast hellos to this neighbor at the specified interval (in seconds.) + The interval is only used while there is no adjacency with this neighbor. + As soon as an adjacency is formed, the interface's + :clicmd:`ipv6 ospf6 hello-interval HELLOINTERVAL` value is used. + (``hello-interval`` must be the same on all routers on this link.) + + :rfc:`2328` recommends a "much larger" value than ``hello-interval`` for + this setting, but this is a legacy of ATM and X.25 networks and nowadays you + should probably just use the same value as for ``hello-interval``. + +.. clicmd:: ipv6 ospf6 neighbor X:X::X:X cost (1-65535) + + Use a distinct cost for paths traversing this neighbor. The default is + to use the interface's cost value (which may be automatically calculated + based on link bandwidth.) Note that costs are directional in OSPF and the + reverse direction must be set on the other router. + + OSPF6 route-map ===============