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;