diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/main.c | 6 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 9 | ||||
| -rw-r--r-- | zebra/rt_netlink.h | 1 | ||||
| -rw-r--r-- | zebra/subdir.am | 2 | ||||
| -rw-r--r-- | zebra/zapi_msg.c | 114 | ||||
| -rw-r--r-- | zebra/zapi_msg.h | 4 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 43 | ||||
| -rw-r--r-- | zebra/zebra_mpls.h | 13 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 83 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 3 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 32 | ||||
| -rw-r--r-- | zebra/zebra_rnh.h | 3 | ||||
| -rw-r--r-- | zebra/zebra_srte.c | 378 | ||||
| -rw-r--r-- | zebra/zebra_srte.h | 74 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 7 |
15 files changed, 742 insertions, 30 deletions
diff --git a/zebra/main.c b/zebra/main.c index 92e94c2a2a..64746f7166 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -57,6 +57,7 @@ #include "zebra/zebra_routemap.h" #include "zebra/zebra_nb.h" #include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" #if defined(HANDLE_NETLINK_FUZZING) #include "zebra/kernel_netlink.h" @@ -438,9 +439,10 @@ int main(int argc, char **argv) zebra_pw_vty_init(); zebra_pbr_init(); zebra_opaque_init(); + zebra_srte_init(); -/* For debug purpose. */ -/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ + /* For debug purpose. */ + /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 4daef42d7a..ea15de6bdb 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -179,7 +179,8 @@ static inline bool is_selfroute(int proto) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) - || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) { + || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC) + || (proto == RTPROT_SRTE)) { return true; } @@ -229,6 +230,9 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_OPENFABRIC: proto = RTPROT_OPENFABRIC; break; + case ZEBRA_ROUTE_SRTE: + proto = RTPROT_SRTE; + break; case ZEBRA_ROUTE_TABLE: case ZEBRA_ROUTE_NHG: proto = RTPROT_ZEBRA; @@ -294,6 +298,9 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) case RTPROT_OPENFABRIC: proto = ZEBRA_ROUTE_OPENFABRIC; break; + case RTPROT_SRTE: + proto = ZEBRA_ROUTE_SRTE; + break; case RTPROT_ZEBRA: if (is_nexthop) { proto = ZEBRA_ROUTE_NHG; diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 429ff0bf85..05c0d76427 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -60,6 +60,7 @@ extern "C" { #define RTPROT_PBR 195 #define RTPROT_ZSTATIC 196 #define RTPROT_OPENFABRIC 197 +#define RTPROT_SRTE 198 void rt_netlink_init(void); diff --git a/zebra/subdir.am b/zebra/subdir.am index c552ca513e..1cea1a9f40 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -106,6 +106,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_router.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ + zebra/zebra_srte.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ @@ -167,6 +168,7 @@ noinst_HEADERS += \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ zebra/zebra_router.h \ + zebra/zebra_srte.h \ zebra/zebra_vrf.h \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 0a459b4d0a..e0c582779d 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -60,6 +60,7 @@ #include "zebra/zebra_mlag.h" #include "zebra/connected.h" #include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" /* Encoding helpers -------------------------------------------------------- */ @@ -1650,6 +1651,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } + /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -1729,6 +1735,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) nexthop->backup_num = 0; } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } + /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -2180,6 +2191,107 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) mpls_zapi_labels_process(true, zvrf, &zl); } +static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zapi_srte_tunnel *zt; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + zt = &zp.segment_list; + if (zt->label_num < 1) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "%s: SR-TE tunnel must contain at least one label", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) + policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + /* TODO: per-VRF list of SR-TE policies. */ + policy->zvrf = zvrf; + + zebra_sr_policy_validate(policy, &zp.segment_list); +} + +static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to find SR-TE policy", + __PRETTY_FUNCTION__); + return; + } + + zebra_sr_policy_del(policy); +} + +int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, + char *name, int status) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0); + if (!client) { + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Not notifying pathd about policy %s" + " status change to %d", + name, status); + return 0; + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Notifying pathd about policy %s status change" + " to %d", + name, status); + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT); + stream_putl(s, color); + stream_put_ipaddr(s, endpoint); + stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH); + stream_putl(s, status); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Send response to a table manager connect request to client */ static void zread_table_manager_connect(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) @@ -2879,6 +2991,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register, [ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable, [ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable, + [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set, + [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete, [ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add, [ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete, [ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace, diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index eb50e3c410..29818059b1 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -100,7 +100,9 @@ extern int zsend_assign_label_chunk_response(struct zserv *client, extern int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, unsigned short result); - +extern int zsend_sr_policy_notify_status(uint32_t color, + struct ipaddr *endpoint, char *name, + int status); #ifdef __cplusplus } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 1ac7bc7a2a..a30cea6a1e 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -47,6 +47,7 @@ #include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_srte.h" #include "zebra/zebra_errors.h" DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") @@ -1505,6 +1506,7 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) char buf[BUFSIZ]; json_object *json_nhlfe = NULL; json_object *json_backups = NULL; + json_object *json_label_stack; struct nexthop *nexthop = nhlfe->nexthop; int i; @@ -1512,6 +1514,14 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); json_object_int_add(json_nhlfe, "outLabel", nexthop->nh_label->label[0]); + + json_label_stack = json_object_new_array(); + json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack); + for (i = 0; i < nexthop->nh_label->num_labels; i++) + json_object_array_add( + json_label_stack, + json_object_new_int(nexthop->nh_label->label[i])); + json_object_int_add(json_nhlfe, "distance", nhlfe->distance); if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) @@ -1916,14 +1926,13 @@ static int mpls_processq_init(void) } -/* Public functions */ - /* * Process LSP update results from zebra dataplane. */ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) { struct zebra_vrf *zvrf; + mpls_label_t label; zebra_ile_t tmp_ile; struct hash *lsp_table; zebra_lsp_t *lsp; @@ -1931,6 +1940,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) struct nexthop *nexthop; enum dplane_op_e op; enum zebra_dplane_result status; + enum zebra_sr_policy_update_label_mode update_mode; op = dplane_ctx_get_op(ctx); status = dplane_ctx_get_status(ctx); @@ -1941,6 +1951,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) dplane_ctx_get_in_label(ctx), dplane_res2str(status)); + label = dplane_ctx_get_in_label(ctx); + switch (op) { case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: @@ -1951,7 +1963,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) lsp_table = zvrf->lsp_table; - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); if (lsp == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) @@ -1985,13 +1997,21 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } } + update_mode = (op == DPLANE_OP_LSP_INSTALL) + ? ZEBRA_SR_POLICY_LABEL_CREATED + : ZEBRA_SR_POLICY_LABEL_UPDATED; + zebra_sr_policy_label_update(label, update_mode); break; case DPLANE_OP_LSP_DELETE: - if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE, "LSP Deletion Failure: in-label %u", dplane_ctx_get_in_label(ctx)); + break; + } + zebra_sr_policy_label_update(label, + ZEBRA_SR_POLICY_LABEL_REMOVED); break; default: @@ -3419,6 +3439,21 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, return 0; } +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return NULL; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + return hash_lookup(lsp_table, &tmp_ile); +} + /* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 07a8efeb8b..c0e58c44e3 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -33,6 +33,7 @@ #include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_vrf.h" +#include "hook.h" #ifdef __cplusplus extern "C" { @@ -326,6 +327,11 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, const union g_addr *gate, ifindex_t ifindex); /* + * Lookup LSP by its input label. + */ +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label); + +/* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. */ @@ -461,6 +467,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type) case ZEBRA_LSP_SHARP: case ZEBRA_LSP_OSPF_SR: case ZEBRA_LSP_ISIS_SR: + case ZEBRA_LSP_SRTE: return 150; } @@ -492,6 +499,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type) return ZEBRA_LSP_ISIS_SR; case ZEBRA_ROUTE_SHARP: return ZEBRA_LSP_SHARP; + case ZEBRA_ROUTE_SRTE: + return ZEBRA_LSP_SRTE; default: return ZEBRA_LSP_NONE; } @@ -517,6 +526,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) return ZEBRA_ROUTE_KERNEL; case ZEBRA_LSP_SHARP: return ZEBRA_ROUTE_SHARP; + case ZEBRA_LSP_SRTE: + return ZEBRA_ROUTE_SRTE; } /* @@ -544,6 +555,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "SR (IS-IS)"; case ZEBRA_LSP_SHARP: return "SHARP"; + case ZEBRA_LSP_SRTE: + return "SR-TE"; case ZEBRA_LSP_NONE: return "Unknown"; } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 43bf745896..1c5b843539 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -28,6 +28,7 @@ #include "lib/mpls.h" #include "lib/jhash.h" #include "lib/debug.h" +#include "lib/lib_errors.h" #include "zebra/connected.h" #include "zebra/debug.h" @@ -36,6 +37,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" #include "zebra/zebra_memory.h" +#include "zebra/zebra_srte.h" #include "zebra/zserv.h" #include "zebra/rt.h" #include "zebra_errors.h" @@ -1620,7 +1622,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) } static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) + struct nexthop *nexthop, + struct zebra_sr_policy *policy) { struct nexthop *resolved_hop; uint8_t num_labels = 0; @@ -1684,7 +1687,21 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; /* Copy labels of the resolved route and the parent resolving to it */ - if (newhop->nh_label) { + if (policy) { + int i = 0; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!newhop->nh_label || !newhop->nh_label->num_labels + || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL) + i = 1; + + for (; i < policy->segment_list.label_num; i++) + labels[num_labels++] = policy->segment_list.labels[i]; + label_type = policy->segment_list.type; + } else if (newhop->nh_label) { for (i = 0; i < newhop->nh_label->num_labels; i++) { /* Be a bit picky about overrunning the local array */ if (num_labels >= MPLS_MAX_LABELS) { @@ -1771,11 +1788,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re, struct route_node *rn; struct route_entry *match = NULL; int resolved; + zebra_nhlfe_t *nhlfe; struct nexthop *newhop; struct interface *ifp; rib_dest_t *dest; struct zebra_vrf *zvrf; - struct in_addr ipv4; + struct in_addr local_ipv4; + struct in_addr *ipv4; if ((nexthop->type == NEXTHOP_TYPE_IPV4) || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -1839,6 +1858,51 @@ static int nexthop_active(afi_t afi, struct route_entry *re, /* Validation for ipv4 mapped ipv6 nexthop. */ if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { afi = AFI_IP; + ipv4 = &local_ipv4; + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4); + } else { + ipv4 = &nexthop->gate.ipv4; + } + + if (nexthop->srte_color) { + struct ipaddr endpoint = {0}; + struct zebra_sr_policy *policy; + + switch (afi) { + case AFI_IP: + endpoint.ipa_type = IPADDR_V4; + endpoint.ipaddr_v4 = *ipv4; + break; + case AFI_IP6: + endpoint.ipa_type = IPADDR_V6; + endpoint.ipaddr_v6 = nexthop->gate.ipv6; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address-family: %u", __func__, + afi); + exit(1); + } + + policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint); + if (policy && policy->status == ZEBRA_SR_POLICY_UP) { + resolved = 0; + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, + nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_DELETED)) + continue; + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, nhlfe->nexthop, + nexthop, policy); + resolved = 1; + } + if (resolved) + return 1; + } } /* Make lookup prefix. */ @@ -1847,12 +1911,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, case AFI_IP: p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; - if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { - ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4); - p.u.prefix4 = ipv4; - } else { - p.u.prefix4 = nexthop->gate.ipv4; - } + p.u.prefix4 = *ipv4; break; case AFI_IP6: p.family = AF_INET6; @@ -1978,7 +2037,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } @@ -2001,7 +2061,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } done_with_match: diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ee9d5d76e0..afae449e7c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -106,7 +106,8 @@ static const struct { [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5}, [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3}, - [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5} + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}, + [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 5}, /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index b151e90a92..cdc00f6026 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -46,6 +46,7 @@ #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" +#include "zebra/zebra_srte.h" #include "zebra/interface.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" @@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn); static int compare_state(struct route_entry *r1, struct route_entry *r2); -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id); static void print_rnh(struct route_node *rn, struct vty *vty); static int zebra_client_cleanup_rnh(struct zserv *client); @@ -305,7 +304,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, * We always need to respond with known information, * currently multiple daemons expect this behavior */ - send_client(rnh, client, type, vrf_id); + zebra_send_rnh_update(rnh, client, type, vrf_id, 0); } void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, @@ -531,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi, } /* state changed, notify clients */ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - send_client(rnh, client, - RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, + RNH_IMPORT_CHECK_TYPE, + zvrf->vrf->vrf_id, 0); } } } @@ -594,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, zebra_route_string(client->proto)); } - send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf->vrf->vrf_id, 0); } if (re) @@ -1107,8 +1108,9 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) return 0; } -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id) +int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color) { struct stream *s = NULL; struct route_entry *re; @@ -1117,6 +1119,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, struct nexthop *nh; struct route_node *rn; int ret; + uint32_t message = 0; int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE : ZEBRA_NEXTHOP_UPDATE; @@ -1128,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, zclient_create_header(s, cmd, vrf_id); + /* Message flags. */ + if (srte_color) + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + stream_putw(s, rn->p.family); switch (rn->p.family) { case AF_INET: @@ -1144,6 +1152,9 @@ static int send_client(struct rnh *rnh, struct zserv *client, __func__, rn->p.family); goto failure; } + if (srte_color) + stream_putl(s, srte_color); + if (re) { struct zapi_nexthop znh; struct nexthop_group *nhg; @@ -1160,7 +1171,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); - ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); + ret = zapi_nexthop_encode(s, &znh, 0, message); if (ret < 0) goto failure; @@ -1173,7 +1184,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); ret = zapi_nexthop_encode( - s, &znh, 0 /* flags */); + s, &znh, 0 /* flags */, + 0 /* message */); if (ret < 0) goto failure; diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index e744504920..ba12b1738f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -50,6 +50,9 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, enum rnh_type type, vrf_id_t vrfid); +extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color); extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c new file mode 100644 index 0000000000..d6043534e3 --- /dev/null +++ b/zebra/zebra_srte.c @@ -0,0 +1,378 @@ +/* Zebra SR-TE code + * Copyright (C) 2020 NetDEF, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "lib/zclient.h" +#include "lib/lib_errors.h" + +#include "zebra/zebra_srte.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_mpls.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zapi_msg.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy") + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy); + +/* Generate rb-tree of SR Policy instances. */ +static inline int +zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a, + const struct zebra_sr_policy *b) +{ + return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, + b->color); +} +RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +struct zebra_sr_policy_instance_head zebra_sr_policy_instances = + RB_INITIALIZER(&zebra_sr_policy_instances); + +struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color, + struct ipaddr *endpoint, char *name) +{ + struct zebra_sr_policy *policy; + + policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy)); + policy->color = color; + policy->endpoint = *endpoint; + strlcpy(policy->name, name, sizeof(policy->name)); + policy->status = ZEBRA_SR_POLICY_DOWN; + RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + + return policy; +} + +void zebra_sr_policy_del(struct zebra_sr_policy *policy) +{ + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + XFREE(MTYPE_ZEBRA_SR_POLICY, policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint) +{ + struct zebra_sr_policy policy = {}; + + policy.color = color; + policy.endpoint = *endpoint; + return RB_FIND(zebra_sr_policy_instance_head, + &zebra_sr_policy_instances, &policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name) +{ + struct zebra_sr_policy *policy; + + // TODO: create index for policy names + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + if (strcmp(policy->name, name) == 0) + return policy; + } + + return NULL; +} + +static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, + struct zserv *client) +{ + const zebra_nhlfe_t *nhlfe; + struct stream *s; + uint32_t message = 0; + unsigned long nump = 0; + uint8_t num; + struct zapi_nexthop znh; + int ret; + + /* Get output stream. */ + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf)); + + /* Message flags. */ + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); + break; + case IPADDR_V6: + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + stream_putl(s, policy->color); + + num = 0; + frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + if (num == 0) { + stream_putc(s, re_type_from_lsp_type(nhlfe->type)); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, nhlfe->distance); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putc(s, 0); + } + + zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + + num++; + } + stream_putc_at(s, nump, num); + stream_putw_at(s, 0, stream_get_endp(s)); + + client->nh_last_upd_time = monotime(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; +} + +static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) +{ + struct rnh *rnh; + struct prefix p = {}; + struct zebra_vrf *zvrf; + struct listnode *node; + struct zserv *client; + + zvrf = policy->zvrf; + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = policy->endpoint.ipaddr_v4; + break; + case IPADDR_V6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = policy->endpoint.ipaddr_v6; + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + + rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE); + if (!rnh) + return; + + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_notify_update_client(policy, client); + else + /* Fallback to the IGP shortest path. */ + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf_id(zvrf), policy->color); + } +} + +static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp) +{ + policy->status = ZEBRA_SR_POLICY_UP; + policy->lsp = lsp; + (void)zebra_sr_policy_bsid_install(policy); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_update(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp, + struct zapi_srte_tunnel *old_tunnel) +{ + bool bsid_changed; + bool segment_list_changed; + + policy->lsp = lsp; + + bsid_changed = + policy->segment_list.local_label != old_tunnel->local_label; + segment_list_changed = + policy->segment_list.label_num != old_tunnel->label_num + || memcmp(policy->segment_list.labels, old_tunnel->labels, + sizeof(mpls_label_t) + * policy->segment_list.label_num); + + /* Re-install label stack if necessary. */ + if (bsid_changed || segment_list_changed) { + zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label); + (void)zebra_sr_policy_bsid_install(policy); + } + + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + + /* Handle segment-list update. */ + if (segment_list_changed) + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) +{ + policy->status = ZEBRA_SR_POLICY_DOWN; + policy->lsp = NULL; + zebra_sr_policy_bsid_uninstall(policy, + policy->segment_list.local_label); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_DOWN); + zebra_sr_policy_notify_update(policy); +} + +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel) +{ + struct zapi_srte_tunnel old_tunnel = policy->segment_list; + zebra_lsp_t *lsp; + + if (new_tunnel) + policy->segment_list = *new_tunnel; + + /* Try to resolve the Binding-SID nexthops. */ + lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]); + if (!lsp || !lsp->best_nhlfe + || lsp->addr_family != ipaddr_family(&policy->endpoint)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + return -1; + } + + /* First label was resolved successfully. */ + if (policy->status == ZEBRA_SR_POLICY_DOWN) + zebra_sr_policy_activate(policy, lsp); + else + zebra_sr_policy_update(policy, lsp, &old_tunnel); + + return 0; +} + +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + zebra_nhlfe_t *nhlfe; + + if (zt->local_label == MPLS_LABEL_NONE) + return 0; + + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) { + uint8_t num_out_labels; + mpls_label_t *out_labels; + mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL; + + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!nhlfe->nexthop->nh_label + || !nhlfe->nexthop->nh_label->num_labels + || nhlfe->nexthop->nh_label->label[0] + == MPLS_LABEL_IMPLICIT_NULL) { + if (zt->label_num > 1) { + num_out_labels = zt->label_num - 1; + out_labels = &zt->labels[1]; + } else { + num_out_labels = 1; + out_labels = &null_label; + } + } else { + num_out_labels = zt->label_num; + out_labels = zt->labels; + } + + if (mpls_lsp_install( + policy->zvrf, zt->type, zt->local_label, + num_out_labels, out_labels, nhlfe->nexthop->type, + &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex) + < 0) + return -1; + } + + return 0; +} + +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + + mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid); +} + +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode) +{ + struct zebra_sr_policy *policy; + + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + mpls_label_t next_hop_label; + + next_hop_label = policy->segment_list.labels[0]; + if (next_hop_label != label) + continue; + + switch (mode) { + case ZEBRA_SR_POLICY_LABEL_CREATED: + case ZEBRA_SR_POLICY_LABEL_UPDATED: + case ZEBRA_SR_POLICY_LABEL_REMOVED: + zebra_sr_policy_validate(policy, NULL); + break; + } + } + + return 0; +} + +void zebra_srte_init(void) +{ +} diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h new file mode 100644 index 0000000000..e5239b7b7b --- /dev/null +++ b/zebra/zebra_srte.h @@ -0,0 +1,74 @@ +/* Zebra's client header. + * Copyright (C) 2020 Netdef, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_SRTE_H +#define _ZEBRA_SRTE_H + +#include "zebra/zebra_mpls.h" + +#include "lib/zclient.h" +#include "lib/srte.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum zebra_sr_policy_update_label_mode { + ZEBRA_SR_POLICY_LABEL_CREATED = 1, + ZEBRA_SR_POLICY_LABEL_UPDATED = 2, + ZEBRA_SR_POLICY_LABEL_REMOVED = 3, +}; + +struct zebra_sr_policy { + RB_ENTRY(zebra_sr_policy) entry; + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + enum zebra_sr_policy_status status; + struct zapi_srte_tunnel segment_list; + zebra_lsp_t *lsp; + struct zebra_vrf *zvrf; +}; +RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy); +RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances; + +struct zebra_sr_policy * +zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name); +void zebra_sr_policy_del(struct zebra_sr_policy *policy); +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint); +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name); +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel); +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy); +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid); +void zebra_srte_init(void); +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_SRTE_H */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 2ea04eee2e..fc1eb6223d 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -562,6 +562,10 @@ static void show_route_nexthop_helper(struct vty *vty, if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof(buf))) vty_out(vty, ", src %s", buf); + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", + nexthop->srte_color); } break; case NEXTHOP_TYPE_IPV6: @@ -781,6 +785,9 @@ static void show_nexthop_json_helper(json_object *json_nexthop, json_object_int_add(json_nexthop, "weight", nexthop->weight); + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, |
