diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index f2330d6bdd..5cc05e6727 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -82,6 +82,7 @@ static struct ospf_sr_db OspfSR; static void ospf_sr_register_vty(void); static inline void del_adj_sid(struct sr_nhlfe nhlfe); +static int ospf_sr_start(struct ospf *ospf); /* * Segment Routing Data Base functions @@ -221,6 +222,26 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, * Segment Routing Initialization functions */ +/** + * Thread function to re-attempt connection to the Label Manager and thus be + * able to start Segment Routing. + * + * @param start Thread structure that contains area as argument + * + * @return 1 on success + */ +static int sr_start_label_manager(struct thread *start) +{ + struct ospf *ospf; + + ospf = THREAD_ARG(start); + + /* re-attempt to start SR & Label Manager connection */ + ospf_sr_start(ospf); + + return 1; +} + /* Segment Routing starter function */ static int ospf_sr_start(struct ospf *ospf) { @@ -231,16 +252,56 @@ static int ospf_sr_start(struct ospf *ospf) osr_debug("SR (%s): Start Segment Routing", __func__); - /* Initialize self SR Node */ - srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), - (void *)sr_node_new); + /* Initialize self SR Node if not already done */ + if (OspfSR.self == NULL) { + srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), + (void *)sr_node_new); - /* Complete & Store self SR Node */ - srn->srgb.range_size = OspfSR.srgb.range_size; - srn->srgb.lower_bound = OspfSR.srgb.lower_bound; - srn->algo[0] = OspfSR.algo[0]; - srn->msd = OspfSR.msd; - OspfSR.self = srn; + /* Complete & Store self SR Node */ + srn->srgb.range_size = OspfSR.srgb.range_size; + srn->srgb.lower_bound = OspfSR.srgb.lower_bound; + srn->algo[0] = OspfSR.algo[0]; + srn->msd = OspfSR.msd; + OspfSR.self = srn; + } + + /* Then, start Label Manager if not ready */ + if (!ospf_zebra_label_manager_ready()) + if (ospf_zebra_label_manager_connect() < 0) { + /* Re-attempt to connect to Label Manager in 1 sec. */ + thread_add_timer(master, sr_start_label_manager, ospf, + 1, &OspfSR.t_start_lm); + osr_debug(" |- Failed to start the Label Manager"); + return -1; + } + + /* + * Request SGRB to the label manager if not already active. If the + * allocation fails, return an error to disable SR until a new SRGB + * is successfully allocated. + */ + if (!OspfSR.srgb_reserved) { + if (ospf_zebra_request_label_range(OspfSR.srgb.lower_bound, + OspfSR.srgb.range_size) + < 0) { + OspfSR.srgb_reserved = false; + return -1; + } else + OspfSR.srgb_reserved = true; + } + + /* SR is UP and ready to flood LSA */ + OspfSR.status = SR_UP; + + /* Set Router Information SR parameters */ + osr_debug("SR: Activate SR for Router Information LSA"); + + ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + + /* Update Ext LSA */ + osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); + + ospf_ext_update_sr(true); osr_debug("SR (%s): Update SR-DB from LSDB", __func__); @@ -277,13 +338,24 @@ static void ospf_sr_stop(void) osr_debug("SR (%s): Stop Segment Routing", __func__); + /* Disable any re-attempt to connect to Label Manager */ + THREAD_TIMER_OFF(OspfSR.t_start_lm); + + /* Release SRGB if active. */ + if (OspfSR.srgb_reserved) { + ospf_zebra_release_label_range( + OspfSR.srgb.lower_bound, + OspfSR.srgb.lower_bound + OspfSR.srgb.range_size - 1); + OspfSR.srgb_reserved = false; + } + /* * Remove all SR Nodes from the Hash table. Prefix and Link SID will * be remove though list_delete() call. See sr_node_del() */ hash_clean(OspfSR.neighbors, (void *)sr_node_del); OspfSR.self = NULL; - OspfSR.enabled = false; + OspfSR.status = SR_OFF; } /* @@ -300,7 +372,7 @@ int ospf_sr_init(void) osr_debug("SR (%s): Initialize SR Data Base", __func__); memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); - OspfSR.enabled = false; + OspfSR.status = SR_OFF; /* Only AREA flooding is supported in this release */ OspfSR.scope = OSPF_OPAQUE_AREA_LSA; @@ -312,6 +384,7 @@ int ospf_sr_init(void) OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE; OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; + OspfSR.srgb_reserved = false; OspfSR.msd = 0; /* Initialize Hash table for neighbor SR nodes */ @@ -1612,7 +1685,7 @@ void ospf_sr_config_write_router(struct vty *vty) struct listnode *node; struct sr_prefix *srp; - if (OspfSR.enabled) { + if (OspfSR.status != SR_OFF) { vty_out(vty, " segment-routing on\n"); if ((OspfSR.srgb.lower_bound != MPLS_DEFAULT_MIN_SRGB_LABEL) @@ -1651,7 +1724,7 @@ DEFUN(ospf_sr_enable, VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return CMD_SUCCESS; if (ospf->vrf_id != VRF_DEFAULT) { @@ -1663,19 +1736,9 @@ DEFUN(ospf_sr_enable, osr_debug("SR: Segment Routing: OFF -> ON"); /* Start Segment Routing */ - OspfSR.enabled = true; + OspfSR.status = SR_ON; ospf_sr_start(ospf); - /* Set Router Information SR parameters */ - osr_debug("SR: Activate SR for Router Information LSA"); - - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); - - /* Update Ext LSA */ - osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); - - ospf_ext_update_sr(true); - return CMD_SUCCESS; } @@ -1687,7 +1750,7 @@ DEFUN (no_ospf_sr_enable, "Disable Segment Routing\n") { - if (!OspfSR.enabled) + if (OspfSR.status == SR_OFF) return CMD_SUCCESS; osr_debug("SR: Segment Routing: ON -> OFF"); @@ -1706,7 +1769,7 @@ DEFUN (no_ospf_sr_enable, static int ospf_sr_enabled(struct vty *vty) { - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return 1; if (vty) @@ -1715,13 +1778,71 @@ static int ospf_sr_enabled(struct vty *vty) return 0; } +/** + * Update SRGB following new CLI value. + * + * @param lower Lower bound of the SRGB + * @param size Size of the SRGB + * + * @return 0 on success, -1 otherwise + */ +static int update_srgb(uint32_t lower, uint32_t size) +{ + + /* Check if values have changed */ + if ((OspfSR.srgb.range_size == size) + && (OspfSR.srgb.lower_bound == lower)) + return 0; + + /* Release old SRGB if active. */ + if (OspfSR.srgb_reserved) { + ospf_zebra_release_label_range( + OspfSR.srgb.lower_bound, + OspfSR.srgb.lower_bound + OspfSR.srgb.range_size - 1); + OspfSR.srgb_reserved = false; + } + + /* Set new SRGB values */ + OspfSR.srgb.range_size = size; + OspfSR.srgb.lower_bound = lower; + if (OspfSR.self != NULL) { + OspfSR.self->srgb.range_size = size; + OspfSR.self->srgb.lower_bound = lower; + } + + /* Check if SR is correctly started i.e. Label Manager connected */ + if (OspfSR.status != SR_UP) + return 0; + + /* + * Try to reserve the new block from the Label Manger. If the allocation + * fails, disable SR until a new SRGB is successfully allocated. + */ + if (ospf_zebra_request_label_range(OspfSR.srgb.lower_bound, + OspfSR.srgb.range_size) < 0) { + OspfSR.srgb_reserved = false; + ospf_sr_stop(); + return -1; + } + + /* SRGB is reserved, set Router Information parameters */ + ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + + /* and update NHLFE entries */ + hash_iterate(OspfSR.neighbors, + (void (*)(struct hash_bucket *, void *))update_in_nhlfe, + NULL); + + return 0; +} + DEFUN (sr_sid_label_range, sr_sid_label_range_cmd, - "segment-routing global-block (0-1048575) (0-1048575)", + "segment-routing global-block (16-1048575) (16-1048575)", SR_STR "Segment Routing Global Block label range\n" - "Lower-bound range in decimal (0-1048575)\n" - "Upper-bound range in decimal (0-1048575)\n") + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") { uint32_t upper; uint32_t lower; @@ -1737,47 +1858,10 @@ DEFUN (sr_sid_label_range, upper = strtoul(argv[idx_up]->arg, NULL, 10); size = upper - lower + 1; - if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) { - vty_out(vty, - "Range size cannot be less than 0 or more than %u\n", - MPLS_DEFAULT_MAX_SRGB_SIZE); + if (update_srgb(lower, size) < 0) return CMD_WARNING_CONFIG_FAILED; - } - - if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot exceed %u\n", - MPLS_DEFAULT_MAX_SRGB_LABEL); - return CMD_WARNING_CONFIG_FAILED; - } - - if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot be lower than %u\n", - MPLS_DEFAULT_MIN_SRGB_LABEL); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check if values have changed */ - if ((OspfSR.srgb.range_size == size) - && (OspfSR.srgb.lower_bound == lower)) + else return CMD_SUCCESS; - - /* Set SID/Label range SRGB */ - OspfSR.srgb.range_size = size; - OspfSR.srgb.lower_bound = lower; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = size; - OspfSR.self->srgb.lower_bound = lower; - } - - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); - - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); - - return CMD_SUCCESS; } DEFUN (no_sr_sid_label_range, @@ -1793,23 +1877,11 @@ DEFUN (no_sr_sid_label_range, if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - /* Revert to default SRGB value */ - OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE; - OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = OspfSR.srgb.range_size; - OspfSR.self->srgb.lower_bound = OspfSR.srgb.lower_bound; - } - - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); - - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); - - return CMD_SUCCESS; + if (update_srgb(MPLS_DEFAULT_MIN_SRGB_SIZE, + MPLS_DEFAULT_MIN_SRGB_LABEL) < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; } DEFUN (sr_node_msd, @@ -1843,8 +1915,9 @@ DEFUN (sr_node_msd, if (OspfSR.self != NULL) OspfSR.self->msd = msd; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); return CMD_SUCCESS; } @@ -1866,8 +1939,9 @@ DEFUN (no_sr_node_msd, if (OspfSR.self != NULL) OspfSR.self->msd = 0; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, 0); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.srgb, 0); return CMD_SUCCESS; } @@ -1972,9 +2046,14 @@ DEFUN (sr_prefix_sid, } else { listnode_add(OspfSR.self->ext_prefix, new); } - ospf_zebra_update_prefix_sid(new); - /* Finally, update Extended Prefix LSA */ + /* Install Prefix SID if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_zebra_update_prefix_sid(new); + else + return CMD_SUCCESS; + + /* Finally, update Extended Prefix LSA id SR is UP */ new->instance = ospf_ext_schedule_prefix_index( ifp, new->sid, &new->prefv4, new->flags); if (new->instance == 0) { @@ -2008,6 +2087,9 @@ DEFUN (no_sr_prefix_sid, if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; + if (OspfSR.status != SR_UP) + return CMD_SUCCESS; + /* Get network prefix */ argv_find(argv, argc, "A.B.C.D/M", &idx); rc = str2prefix(argv[idx]->arg, &p); @@ -2352,7 +2434,7 @@ DEFUN (show_ip_opsf_srdb, bool uj = use_json(argc, argv); json_object *json = NULL, *json_node_array = NULL; - if (!OspfSR.enabled) { + if (OspfSR.status == SR_OFF) { vty_out(vty, "Segment Routing is disabled on this router\n"); return CMD_WARNING; } diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index 3621ea53de..f30d7da395 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -193,10 +193,13 @@ struct sr_srgb { /* SID type to make difference between loopback interfaces and others */ enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID }; +/* Status of Segment Routing: Off (Disable), On (Enable), (Up) Started */ +enum sr_status { SR_OFF, SR_ON, SR_UP, SR_DOWN }; + /* Structure aggregating all OSPF Segment Routing information for the node */ struct ospf_sr_db { - /* Status of Segment Routing: enable or disable */ - bool enabled; + /* Status of Segment Routing */ + enum sr_status status; /* Flooding Scope: Area = 10 or AS = 11 */ uint8_t scope; @@ -219,6 +222,13 @@ struct ospf_sr_db { * Only one range supported in this code */ struct sr_srgb srgb; + + /* Thread timer to start Label Manager */ + struct thread *t_start_lm; + + /* Status of SRGB: reserved within Label Manager or not */ + bool srgb_reserved; + /* Maximum SID Depth supported by the node */ uint8_t msd; }; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 2b8769a4a8..eb125394b8 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -59,6 +59,8 @@ DEFINE_MTYPE_STATIC(OSPFD, OSPF_DIST_ARGS, "OSPF Distribute arguments") /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; +/* and for the Synchronous connection to the Label Manager */ +static struct zclient *zclient_sync; /* For registering threads. */ extern struct thread_master *master; @@ -1713,6 +1715,109 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf) } } +/* Label Manager Functions */ + +/** + * Check if Label Manager is Ready or not. + * + * @return True if Label Manager is ready, False otherwise + */ +bool ospf_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +/** + * Request Label Range to the Label Manager. + * + * @param base base label of the label range to request + * @param chunk_size size of the label range to request + * + * @return 0 on success, -1 on failure + */ +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start, + &end); + if (ret < 0) { + zlog_warn("%s: error getting label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Release Label Range to the Label Manager. + * + * @param start start of label range to release + * @param end end of label range to release + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + zlog_warn("%s: error releasing label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Connect to the Label Manager. + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_label_manager_connect(void) +{ + /* Connect to label manager. */ + if (zclient_socket_connect(zclient_sync) < 0) { + zlog_warn("%s: failed connecting synchronous zclient!", + __func__); + return -1; + } + /* make socket non-blocking */ + set_nonblocking(zclient_sync->sock); + + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) < 0) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return -1; + } + + /* Connect to label manager */ + if (lm_label_manager_connect(zclient_sync, 0) != 0) { + zlog_warn("%s: failed connecting to label manager!", __func__); + if (zclient_sync->sock > 0) { + close(zclient_sync->sock); + zclient_sync->sock = -1; + } + return -1; + } + + osr_debug("SR (%s): Successfully connected to the Label Manager", + __func__); + + return 0; +} + static void ospf_zebra_connected(struct zclient *zclient) { /* Send the client registration */ @@ -1736,6 +1841,20 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) zclient->redistribute_route_add = ospf_zebra_read_route; zclient->redistribute_route_del = ospf_zebra_read_route; + /* Initialize special zclient for synchronous message exchanges. */ + struct zclient_options options = zclient_options_default; + options.synchronous = true; + zclient_sync = zclient_new(master, &options); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_OSPF; + zclient_sync->instance = instance; + /* + * session_id must be different from default value (0) to distinguish + * the asynchronous socket from the synchronous one + */ + zclient_sync->session_id = 1; + zclient_sync->privs = &ospfd_privs; + access_list_add_hook(ospf_filter_update); access_list_delete_hook(ospf_filter_update); prefix_list_add_hook(ospf_prefix_list_update); diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index 6a79f39fa4..bdc8af0402 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -102,4 +102,8 @@ int ospf_external_info_apply_default_routemap(struct ospf *ospf, extern void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p); +bool ospf_zebra_label_manager_ready(void); +int ospf_zebra_label_manager_connect(void); +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +int ospf_zebra_release_label_range(uint32_t start, uint32_t end); #endif /* _ZEBRA_OSPF_ZEBRA_H */