From: vivek Date: Sat, 16 Apr 2016 02:19:37 +0000 (-0700) Subject: Quagga: Install label forwarding entries for statically configured LSPs X-Git-Tag: frr-2.0-rc1~276 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=40c7bdb0c9ba746d1f1bdfe1cb4d03aa5f407661;p=matthieu%2Ffrr.git Quagga: Install label forwarding entries for statically configured LSPs Install the statically configured LSPs into the FIB (kernel). This is done using the new attributes and definitions for MPLS in the kernel - RTA_VIA, RTA_NEWDST and AF_MPLS. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-4804 Reviewed By: CCR-3088 Testing Done: Manual in SE-1 --- diff --git a/lib/nexthop.c b/lib/nexthop.c index 427f77f87a..465cc94851 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -32,8 +32,10 @@ #include "thread.h" #include "prefix.h" #include "nexthop.h" +#include "mpls.h" -DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") +DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") +DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") /* check if nexthops are same, non-recursive */ int @@ -138,6 +140,7 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh) void nexthop_free (struct nexthop *nexthop) { + nexthop_del_labels (nexthop); if (nexthop->resolved) nexthops_free(nexthop->resolved); XFREE (MTYPE_NEXTHOP, nexthop); @@ -156,6 +159,29 @@ nexthops_free (struct nexthop *nexthop) } } +/* Update nexthop with label information. */ +void +nexthop_add_labels (struct nexthop *nexthop, u_int8_t num_labels, + mpls_label_t *label) +{ + struct nexthop_label *nh_label; + int i; + + nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label)); + nh_label->num_labels = num_labels; + for (i = 0; i < num_labels; i++) + nh_label->label[i] = *(label + i); + nexthop->nh_label = nh_label; +} + +/* Free label information of nexthop, if present. */ +void +nexthop_del_labels (struct nexthop *nexthop) +{ + if (nexthop->nh_label) + XFREE (MTYPE_NH_LABEL, nexthop->nh_label); +} + const char * nexthop2str (struct nexthop *nexthop, char *str, int size) { diff --git a/lib/nexthop.h b/lib/nexthop.h index 801904306e..c06dfe0e25 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -84,6 +84,9 @@ struct nexthop * obtained by recursive resolution will be added to `resolved'. * Only one level of recursive resolution is currently supported. */ struct nexthop *resolved; + + /* Label(s) associated with this nexthop. */ + struct nexthop_label *nh_label; }; extern int zebra_rnh_ip_default_route; @@ -106,6 +109,9 @@ void copy_nexthops (struct nexthop **tnh, struct nexthop *nh); void nexthop_free (struct nexthop *nexthop); void nexthops_free (struct nexthop *nexthop); +void nexthop_add_labels (struct nexthop *, u_int8_t, mpls_label_t *); +void nexthop_del_labels (struct nexthop *); + extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern int nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2); diff --git a/zebra/connected.c b/zebra/connected.c index 290973a5cb..9737f8deba 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -38,6 +38,7 @@ #include "zebra/interface.h" #include "zebra/connected.h" #include "zebra/rtadv.h" +#include "zebra/zebra_mpls.h" /* communicate the withdrawal of a connected address */ static void @@ -213,6 +214,15 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc) zlog_debug ("%u: IF %s IPv4 address add/up, scheduling RIB processing", ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Add connected IPv4 route to the interface. */ @@ -333,6 +343,15 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Delete connected IPv4 route to the interface. */ @@ -359,6 +378,15 @@ connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } void @@ -389,6 +417,15 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } /* Add connected IPv6 route to the interface. */ @@ -479,6 +516,15 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } void @@ -504,6 +550,15 @@ connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, ifp->vrf_id, ifp->name); rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + + /* Schedule LSP forwarding entries for processing, if appropriate. */ + if (ifp->vrf_id == VRF_DEFAULT) + { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing", + ifp->vrf_id, ifp->name); + zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id)); + } } int diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 17b3c7bc8d..0802570e60 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -30,6 +30,7 @@ #include "zebra/connected.h" #include "zebra/rt_netlink.h" #include "zebra/rib.h" +#include "zebra/zebra_mpls.h" int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } int kernel_update_ipv4 (struct prefix *a, struct rib *b) { return 0; } @@ -65,3 +66,9 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e) void kernel_init (struct zebra_ns *zns) { return; } void kernel_terminate (struct zebra_ns *zns) { return; } void route_read (struct zebra_ns *zns) { return; } + +int kernel_add_lsp (zebra_lsp_t *l) { return 0; } + +int kernel_del_lsp (zebra_lsp_t *l) { return 0; } + +int kernel_upd_lsp (zebra_lsp_t *l) { return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 9867323e6e..0f7f70ada7 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -30,6 +30,7 @@ #include "nexthop.h" #include "vrf.h" #include "if.h" +#include "mpls.h" #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ @@ -373,6 +374,8 @@ extern struct route_table *rib_table_ipv6; extern int rib_gc_dest (struct route_node *rn); extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter); +extern u_char route_distance(int type); + /* * Inline functions. */ diff --git a/zebra/rt.h b/zebra/rt.h index 46e71fa46e..1137f880f0 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -26,6 +26,8 @@ #include "prefix.h" #include "if.h" #include "zebra/rib.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_mpls.h" extern int kernel_add_ipv4 (struct prefix *, struct rib *); extern int kernel_update_ipv4 (struct prefix *, struct rib *); @@ -39,4 +41,8 @@ extern int kernel_add_ipv6 (struct prefix *, struct rib *); extern int kernel_update_ipv6 (struct prefix *, struct rib *); extern int kernel_delete_ipv6 (struct prefix *, struct rib *); +extern int kernel_add_lsp (zebra_lsp_t *); +extern int kernel_upd_lsp (zebra_lsp_t *); +extern int kernel_del_lsp (zebra_lsp_t *); + #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 41ecb1d0c4..8d19cbb5dc 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -40,6 +40,7 @@ #include "privs.h" #include "nexthop.h" #include "vrf.h" +#include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_ns.h" @@ -50,6 +51,7 @@ #include "zebra/debug.h" #include "zebra/rtadv.h" #include "zebra/zebra_ptm.h" +#include "zebra/zebra_mpls.h" #include "rt_netlink.h" @@ -69,6 +71,27 @@ static const struct message nlmsg_str[] = { {0, NULL} }; +/* TODO - Temporary definitions, need to refine. */ +#ifndef AF_MPLS +#define AF_MPLS 28 +#endif + +#ifndef RTA_VIA +#define RTA_VIA 16 +#endif + +#ifndef RTA_NEWDST +#define RTA_NEWDST 19 +#endif +/* End of temporary definitions */ + +struct gw_family_t +{ + u_int16_t filler; + u_int16_t family; + union g_addr gate; +}; + extern struct zebra_privs_t zserv_privs; extern u_int32_t nl_rcvbufsize; @@ -439,10 +462,10 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *, /* OK we got netlink message. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u", + zlog_debug ("netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u", nl->name, lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, - h->nlmsg_seq, h->nlmsg_pid); + h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid); /* skip unsolicited messages originating from command socket * linux sets the originators port-id for {NEW|DEL}ADDR messages, @@ -961,6 +984,11 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h, if (rtm->rtm_src_len != 0) return 0; + /* We don't care about change notifications for the MPLS table. */ + /* TODO: Revisit this. */ + if (rtm->rtm_family == AF_MPLS) + return 0; + /* Table corresponding to route. */ if (tb[RTA_TABLE]) table = *(int *) RTA_DATA (tb[RTA_TABLE]); @@ -1161,6 +1189,11 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; } + /* We don't care about change notifications for the MPLS table. */ + /* TODO: Revisit this. */ + if (rtm->rtm_family == AF_MPLS) + return 0; + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg)); if (len < 0) return -1; @@ -1723,10 +1756,10 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns) n->nlmsg_flags |= NLM_F_ACK; if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_talk: %s type %s(%u), seq=%u flags 0x%x", + zlog_debug ("netlink_talk: %s type %s(%u), len=%d seq=%u flags 0x%x", nl->name, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, - n->nlmsg_seq, n->nlmsg_flags); + n->nlmsg_len, n->nlmsg_seq, n->nlmsg_flags); /* Send message to netlink interface. */ if (zserv_privs.change (ZPRIVS_RAISE)) @@ -1757,6 +1790,60 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns) return netlink_parse_info (netlink_talk_filter, nl, zns, 0); } +static void +_netlink_route_nl_add_gateway_info (u_char route_family, u_char gw_family, + struct nlmsghdr *nlmsg, + size_t req_size, int bytelen, + struct nexthop *nexthop) +{ + if (route_family == AF_MPLS) + { + struct gw_family_t gw_fam; + + gw_fam.family = gw_family; + if (gw_family == AF_INET) + memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); + else + memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); + addattr_l (nlmsg, req_size, RTA_VIA, &gw_fam.family, bytelen+2); + } + else + { + if (gw_family == AF_INET) + addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); + else + addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); + } +} + +static void +_netlink_route_rta_add_gateway_info (u_char route_family, u_char gw_family, + struct rtattr *rta, struct rtnexthop *rtnh, + size_t req_size, int bytelen, + struct nexthop *nexthop) +{ + if (route_family == AF_MPLS) + { + struct gw_family_t gw_fam; + + gw_fam.family = gw_family; + if (gw_family == AF_INET) + memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); + else + memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); + rta_addattr_l (rta, req_size, RTA_VIA, &gw_fam.family, bytelen+2); + rtnh->rtnh_len += RTA_LENGTH (bytelen + 2); + } + else + { + if (gw_family == AF_INET) + rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); + else + rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + } +} + /* This function takes a nexthop as argument and adds * the appropriate netlink attributes to an existing * netlink message. @@ -1778,6 +1865,8 @@ _netlink_route_build_singlepath( size_t req_size, int cmd) { + mpls_lse_t out_lse; + char label_buf[100]; if (rtmsg->rtm_family == AF_INET && (nexthop->type == NEXTHOP_TYPE_IPV6 @@ -1805,14 +1894,28 @@ _netlink_route_build_singlepath( return; } + label_buf[0] = '\0'; + if (rtmsg->rtm_family == AF_MPLS) + { + assert (nexthop->nh_label); + + /* Fill out label, if present. */ + if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + { + out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); + addattr_l (nlmsg, req_size, RTA_NEWDST, &out_lse, sizeof(mpls_lse_t)); + sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + } + } + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtmsg->rtm_flags |= RTNH_F_ONLINK; if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - addattr_l (nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); + _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET, nlmsg, + req_size, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { @@ -1826,16 +1929,16 @@ _netlink_route_build_singlepath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - addattr_l (nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); + _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET6, nlmsg, + req_size, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { @@ -1849,10 +1952,10 @@ _netlink_route_build_singlepath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -1920,6 +2023,9 @@ _netlink_route_build_multipath( struct rtmsg *rtmsg, union g_addr **src) { + mpls_lse_t out_lse; + char label_buf[100]; + rtnh->rtnh_len = sizeof (*rtnh); rtnh->rtnh_flags = 0; rtnh->rtnh_hops = 0; @@ -1952,6 +2058,21 @@ _netlink_route_build_multipath( return; } + label_buf[0] = '\0'; + if (rtmsg->rtm_family == AF_MPLS) + { + assert (nexthop->nh_label); + + /* Fill out label, if present. */ + if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + { + out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1); + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST, + &out_lse, sizeof(mpls_lse_t)); + rtnh->rtnh_len += RTA_LENGTH (sizeof(mpls_lse_t)); + sprintf (label_buf, "label %d", nexthop->nh_label->label[0]); + } + } if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtnh->rtnh_flags |= RTNH_F_ONLINK; @@ -1959,10 +2080,8 @@ _netlink_route_build_multipath( if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; - + _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET, rta, + rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop); if (nexthop->rmap_src.ipv4.s_addr) *src = &nexthop->rmap_src; else if (nexthop->src.ipv4.s_addr) @@ -1970,17 +2089,16 @@ _netlink_route_build_multipath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); + label_buf, nexthop->ifindex); } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET6, rta, + rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop); if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) *src = &nexthop->rmap_src; @@ -1989,10 +2107,10 @@ _netlink_route_build_multipath( if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " - "nexthop via %s if %u", + "nexthop via %s %s if %u", routedesc, inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); + label_buf, nexthop->ifindex); } /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX @@ -2023,6 +2141,44 @@ _netlink_route_build_multipath( } } +static inline void +_netlink_mpls_build_singlepath( + const char *routedesc, + zebra_nhlfe_t *nhlfe, + struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, + size_t req_size, + int cmd) +{ + int bytelen; + u_char family; + + family = NHLFE_FAMILY (nhlfe); + bytelen = (family == AF_INET ? 4 : 16); + _netlink_route_build_singlepath(routedesc, bytelen, nhlfe->nexthop, + nlmsg, rtmsg, req_size, cmd); +} + + +static inline void +_netlink_mpls_build_multipath( + const char *routedesc, + zebra_nhlfe_t *nhlfe, + struct rtattr *rta, + struct rtnexthop *rtnh, + struct rtmsg *rtmsg, + union g_addr **src) +{ + int bytelen; + u_char family; + + family = NHLFE_FAMILY (nhlfe); + bytelen = (family == AF_INET ? 4 : 16); + _netlink_route_build_multipath(routedesc, bytelen, nhlfe->nexthop, + rta, rtnh, rtmsg, src); +} + + /* Log debug information for netlink_route_multipath * if debug logging is enabled. * @@ -2048,11 +2204,22 @@ _netlink_route_debug( zlog_debug ("netlink_route_multipath() (%s): %s %s vrf %u type %s", routedesc, lookup (nlmsg_str, cmd), - prefix2str (p, buf, sizeof(buf)), - zvrf->vrf_id, nexthop_type_to_str (nexthop->type)); + prefix2str (p, buf, sizeof(buf)), zvrf->vrf_id, + (nexthop) ? nexthop_type_to_str (nexthop->type) : "UNK"); } } +static void +_netlink_mpls_debug( + int cmd, + u_int32_t label, + const char *routedesc) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("netlink_mpls_multipath() (%s): %s %u/20", + routedesc, lookup (nlmsg_str, cmd), label); +} + static int netlink_neigh_update (int cmd, int ifindex, uint32_t addr, char *lla, int llalen) { @@ -2493,6 +2660,272 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) lla, llalen); } +/* + * MPLS label forwarding table change via netlink interface. + */ +static int +netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp) +{ + mpls_lse_t lse; + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop = NULL; + int nexthop_num; + const char *routedesc; + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + + struct + { + struct nlmsghdr n; + struct rtmsg r; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE); + + + /* + * Count # nexthops so we can decide whether to use singlepath + * or multipath case. + */ + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + if (cmd == RTM_NEWROUTE) + { + /* Count all selected NHLFEs */ + if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + nexthop_num++; + } + else /* DEL */ + { + /* Count all installed NHLFEs */ + if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + nexthop_num++; + } + } + + if (nexthop_num == 0) // unexpected + return 0; + + req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); + req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.r.rtm_family = AF_MPLS; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_dst_len = MPLS_LABEL_LEN_BITS; + req.r.rtm_protocol = RTPROT_ZEBRA; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + + if (cmd == RTM_NEWROUTE) + /* We do a replace to handle update. */ + req.n.nlmsg_flags |= NLM_F_REPLACE; + + /* Fill destination */ + lse = mpls_lse_encode (lsp->ile.in_label, 0, 0, 1); + addattr_l (&req.n, sizeof req, RTA_DST, &lse, sizeof(mpls_lse_t)); + + /* Fill nexthops (paths) based on single-path or multipath. The paths + * chosen depend on the operation. + */ + if (nexthop_num == 1 || MULTIPATH_NUM == 1) + { + routedesc = "single hop"; + _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc); + + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if ((cmd == RTM_NEWROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (cmd == RTM_DELROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + /* Add the gateway */ + _netlink_mpls_build_singlepath(routedesc, nhlfe, + &req.n, &req.r, sizeof req, cmd); + if (cmd == RTM_NEWROUTE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + nexthop_num++; + break; + } + } + } + else /* Multipath case */ + { + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + struct rtnexthop *rtnh; + union g_addr *src1 = NULL; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH (0); + rtnh = RTA_DATA (rta); + + routedesc = "multihop"; + _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc); + + nexthop_num = 0; + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + if ((cmd == RTM_NEWROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (cmd == RTM_DELROUTE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + nexthop_num++; + + /* Build the multipath */ + _netlink_mpls_build_multipath(routedesc, nhlfe, rta, + rtnh, &req.r, &src1); + rtnh = RTNH_NEXT (rtnh); + + if (cmd == RTM_NEWROUTE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + + } + } + + /* Add the multipath */ + if (rta->rta_len > RTA_LENGTH (0)) + addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + } + + /* Talk to netlink socket. */ + return netlink_talk (&req.n, &zns->netlink_cmd, zns); +} + +/* + * Handle failure in LSP install, clear flags for NHLFE. + */ +static inline void +clear_nhlfe_installed (zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } +} + +/* + * Install Label Forwarding entry into the kernel. + */ +int +kernel_add_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Update Label Forwarding entry in the kernel. This means that the Label + * forwarding entry is already installed and needs an update - either a new + * path is to be added, an installed path has changed (e.g., outgoing label) + * or an installed path (but not all paths) has to be removed. + * TODO: Performs a DEL followed by ADD now, need to change to REPLACE. Note + * that REPLACE was originally implemented for IPv4 nexthops but removed as + * it was not functioning when moving from swap to PHP as that was signaled + * through the metric field (before kernel-MPLS). This shouldn't be an issue + * any longer, so REPLACE can be reintroduced. + */ +int +kernel_upd_lsp (zebra_lsp_t *lsp) +{ + int ret; + + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + + /* First issue a DEL and clear the installed flag. */ + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + + /* Then issue an ADD. */ + ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp); + if (!ret) + SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + else + clear_nhlfe_installed (lsp); + + return ret; +} + +/* + * Delete Label Forwarding entry from the kernel. + */ +int +kernel_del_lsp (zebra_lsp_t *lsp) +{ + if (!lsp) // unexpected + return -1; + + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + { + netlink_mpls_multipath (RTM_DELROUTE, lsp); + UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED); + } + + return 0; +} + extern struct thread_master *master; /* Kernel route reflection. */ diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index db1f318595..892aec965a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -48,6 +48,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") DEFINE_MTYPE_STATIC(ZEBRA, SLSP, "MPLS static LSP config") +DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE, "MPLS static nexthop object") DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME, "MPLS static nexthop ifname") @@ -59,6 +60,53 @@ static unsigned int label_hash (void *p); static int label_cmp (const void *p1, const void *p2); +static int +nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); +static int +nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); +static int +nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe); +static void +lsp_select_best_nhlfe (zebra_lsp_t *lsp); +static void +lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt); +static void +lsp_schedule (struct hash_backet *backet, void *ctxt); +static wq_item_status +lsp_process (struct work_queue *wq, void *data); +static void +lsp_processq_del (struct work_queue *wq, void *data); +static void +lsp_processq_complete (struct work_queue *wq); +static int +lsp_processq_add (zebra_lsp_t *lsp); +static void * +lsp_alloc (void *p); +static char * +nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size); +static int +nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static zebra_nhlfe_t * +nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); +static zebra_nhlfe_t * +nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex, mpls_label_t out_label); +static int +nhlfe_del (zebra_nhlfe_t *snhlfe); +static int +static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex); +static int +static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex); +static int +static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label); static void lsp_config_write (struct hash_backet *backet, void *ctxt); static void * @@ -79,6 +127,8 @@ static int snhlfe_del_all (zebra_slsp_t *slsp); static char * snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size); +static void +mpls_processq_init (struct zebra_t *zebra); @@ -108,6 +158,810 @@ label_cmp (const void *p1, const void *p2) return (ile1->in_label == ile2->in_label); } +/* + * Check if an IPv4 nexthop for a NHLFE is active. Update nexthop based on + * the passed flag. + * NOTE: Looking only for connected routes right now. + */ +static int +nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop) +{ + struct route_table *table; + struct prefix_ipv4 p; + struct route_node *rn; + struct rib *match; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + /* Lookup nexthop in IPv4 routing table. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv4; + + rn = route_node_match (table, (struct prefix *) &p); + if (!rn) + return 0; + + route_unlock_node (rn); + + /* Locate a valid connected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if ((match->type == ZEBRA_ROUTE_CONNECT) && + !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) && + CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (!match || !match->nexthop) + return 0; + + nexthop->ifindex = match->nexthop->ifindex; + return 1; +} + + +/* + * Check if an IPv6 nexthop for a NHLFE is active. Update nexthop based on + * the passed flag. + * NOTE: Looking only for connected routes right now. + */ +static int +nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop) +{ + struct route_table *table; + struct prefix_ipv6 p; + struct route_node *rn; + struct rib *match; + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + /* Lookup nexthop in IPv6 routing table. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv6; + + rn = route_node_match (table, (struct prefix *) &p); + if (!rn) + return 0; + + route_unlock_node (rn); + + /* Locate a valid connected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if ((match->type == ZEBRA_ROUTE_CONNECT) && + !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) && + CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (!match || !match->nexthop) + return 0; + + nexthop->ifindex = match->nexthop->ifindex; + return 1; +} + + +/* + * Check the nexthop reachability for a NHLFE and return if valid (reachable) + * or not. + * NOTE: Each NHLFE points to only 1 nexthop. + */ +static int +nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe) +{ + struct nexthop *nexthop; + struct interface *ifp; + + nexthop = nhlfe->nexthop; + if (!nexthop) // unexpected + return 0; + + /* Check on nexthop based on type. */ + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + if (nhlfe_nexthop_active_ipv4 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + + case NEXTHOP_TYPE_IPV6: + if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6)) + { + ifp = if_lookup_by_index (nexthop->ifindex); + if (ifp && if_is_operative(ifp)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + else + { + if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + + default: + break; + } + + return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* + * Walk through NHLFEs for a LSP forwarding entry, verify nexthop + * reachability and select the best. Multipath entries are also + * marked. This is invoked when an LSP scheduled for processing (due + * to some change) is examined. + */ +static void +lsp_select_best_nhlfe (zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + zebra_nhlfe_t *best; + struct nexthop *nexthop; + int changed = 0; + + if (!lsp) + return; + + best = NULL; + lsp->num_ecmp = 0; + UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED); + + /* + * First compute the best path, after checking nexthop status. We are only + * concerned with non-deleted NHLFEs. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + /* Clear selection flags. */ + UNSET_FLAG (nhlfe->flags, + (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH)); + + if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) && + nhlfe_nexthop_active (nhlfe)) + { + if (!best || (nhlfe->distance < best->distance)) + best = nhlfe; + } + } + + lsp->best_nhlfe = best; + if (!lsp->best_nhlfe) + return; + + /* Mark best NHLFE as selected. */ + SET_FLAG (lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); + + /* + * If best path exists, see if there is ECMP. While doing this, note if a + * new (uninstalled) NHLFE has been selected, an installed entry that is + * still selected has a change or an installed entry is to be removed. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + int nh_chg, nh_sel, nh_inst; + + nexthop = nhlfe->nexthop; + if (!nexthop) // unexpected + continue; + + if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + (nhlfe->distance == lsp->best_nhlfe->distance)) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_MULTIPATH); + lsp->num_ecmp++; + } + + if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED) && + !changed) + { + nh_chg = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + nh_sel = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED); + nh_inst = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + + if ((nh_sel && !nh_inst) || + (nh_sel && nh_inst && nh_chg) || + (nh_inst && !nh_sel)) + changed = 1; + } + + /* We have finished examining, clear changed flag. */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + } + + if (changed) + SET_FLAG (lsp->flags, LSP_FLAG_CHANGED); +} + +/* + * Delete LSP forwarding entry from kernel, if installed. Called upon + * process exit. + */ +static void +lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + + lsp = (zebra_lsp_t *) backet->data; + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) + kernel_del_lsp (lsp); +} + +/* + * Schedule LSP forwarding entry for processing. Called upon changes + * that may impact LSPs such as nexthop / connected route changes. + */ +static void +lsp_schedule (struct hash_backet *backet, void *ctxt) +{ + zebra_lsp_t *lsp; + + lsp = (zebra_lsp_t *) backet->data; + lsp_processq_add (lsp); +} + +/* + * Process a LSP entry that is in the queue. Recalculate best NHLFE and + * any multipaths and update or delete from the kernel, as needed. + */ +static wq_item_status +lsp_process (struct work_queue *wq, void *data) +{ + zebra_lsp_t *lsp; + zebra_nhlfe_t *oldbest, *newbest; + char buf[BUFSIZ], buf2[BUFSIZ]; + + lsp = (zebra_lsp_t *)data; + if (!lsp) // unexpected + return WQ_SUCCESS; + + oldbest = lsp->best_nhlfe; + + /* Select best NHLFE(s) */ + lsp_select_best_nhlfe (lsp); + + newbest = lsp->best_nhlfe; + + if (IS_ZEBRA_DEBUG_MPLS) + { + if (oldbest) + nhlfe2str (oldbest, buf, BUFSIZ); + if (newbest) + nhlfe2str (newbest, buf2, BUFSIZ); + zlog_debug ("Process LSP in-label %u oldbest %s newbest %s " + "flags 0x%x ecmp# %d", + lsp->ile.in_label, oldbest ? buf : "NULL", + newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp); + } + + if (!CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED)) + { + /* Not already installed */ + if (newbest) + kernel_add_lsp (lsp); + } + else + { + /* Installed, may need an update and/or delete. */ + if (!newbest) + kernel_del_lsp (lsp); + else if (CHECK_FLAG (lsp->flags, LSP_FLAG_CHANGED)) + kernel_upd_lsp (lsp); + } + + return WQ_SUCCESS; +} + + +/* + * Callback upon processing completion of a LSP forwarding entry. + */ +static void +lsp_processq_del (struct work_queue *wq, void *data) +{ + struct zebra_vrf *zvrf; + zebra_lsp_t *lsp; + struct hash *lsp_table; + zebra_nhlfe_t *nhlfe, *nhlfe_next; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + assert (zvrf); + + lsp_table = zvrf->lsp_table; + if (!lsp_table) // unexpected + return; + + lsp = (zebra_lsp_t *)data; + if (!lsp) // unexpected + return; + + /* Clear flag, remove any NHLFEs marked for deletion. If no NHLFEs exist, + * delete LSP entry also. + */ + UNSET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED); + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) + { + nhlfe_next = nhlfe->next; + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + nhlfe_del (nhlfe); + } + + if (!lsp->nhlfe_list) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } +} + +/* + * Callback upon finishing the processing of all scheduled + * LSP forwarding entries. + */ +static void +lsp_processq_complete (struct work_queue *wq) +{ + /* Nothing to do for now. */ +} + +/* + * Add LSP forwarding entry to queue for subsequent processing. + */ +static int +lsp_processq_add (zebra_lsp_t *lsp) +{ + /* If already scheduled, exit. */ + if (CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + return 0; + + work_queue_add (zebrad.lsp_process_q, lsp); + SET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED); + return 0; +} + +/* + * Callback to allocate LSP forwarding table entry. + */ +static void * +lsp_alloc (void *p) +{ + const zebra_ile_t *ile = p; + zebra_lsp_t *lsp; + + lsp = XCALLOC (MTYPE_LSP, sizeof(zebra_lsp_t)); + lsp->ile = *ile; + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Alloc LSP in-label %u", lsp->ile.in_label); + + return ((void *)lsp); +} + +/* + * Create printable string for NHLFE entry. + */ +static char * +nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size) +{ + struct nexthop *nexthop; + + buf[0] = '\0'; + nexthop = nhlfe->nexthop; + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + inet_ntop (AF_INET, &nexthop->gate.ipv4, buf, size); + break; + case NEXTHOP_TYPE_IPV6: + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, size); + break; + default: + break; + } + + return buf; +} + +/* + * Check if NHLFE matches with search info passed. + */ +static int +nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct nexthop *nhop; + u_char cmp = -1; + + nhop = nhlfe->nexthop; + if (!nhop) + return -1; + + if (nhop->type != gtype) + return -1; + + switch (nhop->type) + { + case NEXTHOP_TYPE_IPV4: + cmp = memcmp(&(nhop->gate.ipv4), &(gate->ipv4), + sizeof(struct in_addr)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + cmp = memcmp(&(nhop->gate.ipv6), &(gate->ipv6), + sizeof(struct in6_addr)); + if (!cmp && nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + cmp = !(nhop->ifindex == ifindex); + break; + default: + break; + } + + return cmp; +} + + +/* + * Locate NHLFE that matches with passed info. + */ +static zebra_nhlfe_t * +nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return NULL; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + if (nhlfe->type != lsp_type) + continue; + if (!nhlfe_nhop_match (nhlfe, gtype, gate, ifname, ifindex)) + break; + } + + return nhlfe; +} + +/* + * Add NHLFE. Base entry must have been created and duplicate + * check done. + */ +static zebra_nhlfe_t * +nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex, mpls_label_t out_label) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; + + if (!lsp) + return NULL; + + nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t)); + if (!nhlfe) + return NULL; + + nhlfe->lsp = lsp; + nhlfe->type = lsp_type; + nhlfe->distance = lsp_distance (lsp_type); + + nexthop = nexthop_new(); + if (!nexthop) + { + XFREE (MTYPE_NHLFE, nhlfe); + return NULL; + } + nexthop_add_labels (nexthop, 1, &out_label); + + nexthop->type = gtype; + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + nexthop->gate.ipv4 = gate->ipv4; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + nexthop->gate.ipv6 = gate->ipv6; + if (ifindex) + nexthop->ifindex = ifindex; + break; + default: + nexthop_free(nexthop); + XFREE (MTYPE_NHLFE, nhlfe); + return NULL; + break; + } + + nhlfe->nexthop = nexthop; + if (lsp->nhlfe_list) + lsp->nhlfe_list->prev = nhlfe; + nhlfe->next = lsp->nhlfe_list; + lsp->nhlfe_list = nhlfe; + + return nhlfe; +} + +/* + * Delete NHLFE. Entry must be present on list. + */ +static int +nhlfe_del (zebra_nhlfe_t *nhlfe) +{ + zebra_lsp_t *lsp; + + if (!nhlfe) + return -1; + + lsp = nhlfe->lsp; + if (!lsp) + return -1; + + /* Free nexthop. */ + if (nhlfe->nexthop) + nexthop_free(nhlfe->nexthop); + + /* Unlink from LSP */ + if (nhlfe->next) + nhlfe->next->prev = nhlfe->prev; + if (nhlfe->prev) + nhlfe->prev->next = nhlfe->next; + else + lsp->nhlfe_list = nhlfe->next; + + XFREE (MTYPE_NHLFE, nhlfe); + + return 0; +} + + +/* + * Install/update a static NHLFE for an LSP in the forwarding table. This may + * be a new LSP entry or a new NHLFE for an existing in-label or an update of + * the out-label for an existing NHLFE (update case). + */ +static int +static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); + if (nhlfe) + { + struct nexthop *nh = nhlfe->nexthop; + + assert (nh); + assert (nh->nh_label); + + /* Clear deleted flag (in case it was set) */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (nh->nh_label->label[0] == out_label) + /* No change */ + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("LSP in-label %u type %d nexthop %s " + "out-label changed to %u (old %u)", + in_label, ZEBRA_LSP_STATIC, buf, + out_label, nh->nh_label->label[0]); + } + + /* Update out label, trigger processing. */ + nh->nh_label->label[0] = out_label; + } + else + { + /* Add LSP entry to this nexthop */ + nhlfe = nhlfe_add (lsp, ZEBRA_LSP_STATIC, gtype, gate, + ifname, ifindex, out_label); + if (!nhlfe) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Add LSP in-label %u type %d nexthop %s " + "out-label %u", + in_label, ZEBRA_LSP_STATIC, buf, + out_label); + } + + lsp->addr_family = NHLFE_FAMILY (nhlfe); + } + + /* Mark NHLFE, queue LSP for processing. */ + SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + if (lsp_processq_add (lsp)) + return -1; + + return 0; +} + +/* + * Uninstall a particular static NHLFE in the forwarding table. If this is + * the only NHLFE, the entire LSP forwarding entry has to be deleted. + */ +static int +static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + char *ifname, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp) + return 0; + nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex); + if (!nhlfe) + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); + } + + /* Mark NHLFE for delete or directly delete, as appropriate. */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (lsp_processq_add (lsp)) + return -1; + } + else + { + nhlfe_del (nhlfe); + + /* Free LSP entry if no other NHLFEs and not scheduled. */ + if (!lsp->nhlfe_list && + !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } + } + return 0; +} + +/* + * Uninstall all static NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +static int +static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe, *nhlfe_next; + int schedule_lsp = 0; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup (lsp_table, &tmp_ile); + if (!lsp || !lsp->nhlfe_list) + return 0; + + /* Mark NHLFEs for delete or directly delete, as appropriate. */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) + { + nhlfe_next = nhlfe->next; + + /* Skip non-static NHLFEs */ + if (nhlfe->type != ZEBRA_LSP_STATIC) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags); + } + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + schedule_lsp = 1; + } + else + { + nhlfe_del (nhlfe); + } + } + + /* Queue LSP for processing, if needed, else delete. */ + if (schedule_lsp) + { + if (lsp_processq_add (lsp)) + return -1; + } + else if (!lsp->nhlfe_list && + !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + lsp = hash_release(lsp_table, &lsp->ile); + if (lsp) + XFREE(MTYPE_LSP, lsp); + } + + return 0; +} + /* * Write out static LSP configuration. */ @@ -318,6 +1172,27 @@ snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size) return buf; } +/* + * Initialize work queue for processing changed LSPs. + */ +static void +mpls_processq_init (struct zebra_t *zebra) +{ + zebra->lsp_process_q = work_queue_new (zebra->master, "LSP processing"); + if (!zebra->lsp_process_q) + { + zlog_err ("%s: could not initialise work queue!", __func__); + return; + } + + zebra->lsp_process_q->spec.workfunc = &lsp_process; + zebra->lsp_process_q->spec.del_item_data = &lsp_processq_del; + zebra->lsp_process_q->spec.errorfunc = NULL; + zebra->lsp_process_q->spec.completion_func = &lsp_processq_complete; + zebra->lsp_process_q->spec.max_retries = 0; + zebra->lsp_process_q->spec.hold = 10; +} + /* Public functions */ @@ -438,6 +1313,11 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, } } + /* (Re)Install LSP in the main table. */ + if (static_lsp_install (zvrf, in_label, out_label, gtype, + gate, ifname, ifindex)) + return -1; + return 0; } @@ -475,6 +1355,9 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("Del static LSP in-label %u", in_label); + /* Uninstall entire LSP from the main table. */ + static_lsp_uninstall_all (zvrf, in_label); + /* Delete all static NHLFEs */ snhlfe_del_all (slsp); } @@ -493,6 +1376,10 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, in_label, buf); } + /* Uninstall LSP from the main table. */ + static_lsp_uninstall (zvrf, in_label, gtype, + gate, ifname, ifindex); + /* Delete static LSP NHLFE */ snhlfe_del (snhlfe); } @@ -508,6 +1395,19 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; } +/* + * Schedule all MPLS label forwarding entries for processing. + * Called upon changes that may affect one or more of them such as + * interface or nexthop state changes. + */ +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + hash_iterate(zvrf->lsp_table, lsp_schedule, NULL); +} + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ @@ -518,6 +1418,19 @@ zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) return (zvrf->slsp_table->count ? 1 : 0); } +/* + * Called upon process exiting, need to delete LSP forwarding + * entries from the kernel. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); +} + /* * Allocate MPLS tables for this VRF and do other initialization. * NOTE: Currently supported only for default VRF. @@ -528,6 +1441,7 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) if (!zvrf) return; zvrf->slsp_table = hash_create(label_hash, label_cmp); + zvrf->lsp_table = hash_create(label_hash, label_cmp); } /* @@ -536,5 +1450,5 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) void zebra_mpls_init (void) { - /* Filler for subsequent use. */ + mpls_processq_init (&zebrad); } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index b62230ead6..5fb481781a 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -189,12 +189,28 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, enum nexthop_types_t gtype, union g_addr *gate, char *ifname, ifindex_t ifindex); +/* + * Schedule all MPLS label forwarding entries for processing. + * Called upon changes that may affect one or more of them such as + * interface or nexthop state changes. + */ +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf); + /* * Display MPLS LSP configuration of all static LSPs (VTY command handler). */ int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf); +/* + * Called upon process exiting, need to delete LSP forwarding + * entries from the kernel. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf); + /* * Allocate MPLS tables for this VRF. * NOTE: Currently supported only for default VRF. @@ -208,4 +224,34 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf); void zebra_mpls_init (void); +/* Inline functions. */ + +/* + * Distance (priority) definition for LSP NHLFE. + */ +static inline u_char +lsp_distance (enum lsp_types_t type) +{ + if (type == ZEBRA_LSP_STATIC) + return (route_distance (ZEBRA_ROUTE_STATIC)); + + return 150; +} + +/* + * Map RIB type to LSP type. Used when labeled-routes from BGP + * are converted into LSPs. + */ +static inline enum lsp_types_t +lsp_type_from_rib_type (int rib_type) +{ + switch (rib_type) + { + case ZEBRA_ROUTE_STATIC: + return ZEBRA_LSP_STATIC; + default: + return ZEBRA_LSP_INVALID; + } +} + #endif /*_ZEBRA_MPLS_H */ diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index a8c9aa3322..15169f0f25 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -28,12 +28,22 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; } +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) +{ +} + int zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) { return 0; } +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf) +{ +} + void zebra_mpls_init_tables (struct zebra_vrf *zvrf) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e238f8e8eb..910610fc60 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -37,6 +37,7 @@ #include "routemap.h" #include "nexthop.h" #include "vrf.h" +#include "mpls.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -117,6 +118,19 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int prior #define rnode_info(node, ...) \ _rnode_zlog(__func__, vrf_id, node, LOG_INFO, __VA_ARGS__) +u_char +route_distance (int type) +{ + u_char distance; + + if ((unsigned)type >= array_size(route_info)) + distance = 150; + else + distance = route_info[type].distance; + + return distance; +} + int is_zebra_valid_kernel_table(u_int32_t table_id) { @@ -3123,6 +3137,9 @@ rib_close (void) if (zvrf->other_table[AFI_IP6][table_id]) rib_close_table (zvrf->other_table[AFI_IP6][table_id]); } + + zebra_mpls_close_tables(zvrf); + } /* Routing information base initialize. */ diff --git a/zebra/zserv.h b/zebra/zserv.h index 3667f5b2b6..ce243dd6ac 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -130,6 +130,9 @@ struct zebra_t /* rib work queue */ struct work_queue *ribq; struct meta_queue *mq; + + /* LSP work queue */ + struct work_queue *lsp_process_q; }; extern struct zebra_t zebrad;