#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
void
nexthop_free (struct nexthop *nexthop)
{
+ nexthop_del_labels (nexthop);
if (nexthop->resolved)
nexthops_free(nexthop->resolved);
XFREE (MTYPE_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)
{
* 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;
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);
#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
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. */
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. */
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
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. */
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
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
#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; }
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; }
#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 */
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.
*/
#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 *);
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 */
#include "privs.h"
#include "nexthop.h"
#include "vrf.h"
+#include "mpls.h"
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
#include "zebra/debug.h"
#include "zebra/rtadv.h"
#include "zebra/zebra_ptm.h"
+#include "zebra/zebra_mpls.h"
#include "rt_netlink.h"
{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;
/* 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,
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]);
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;
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))
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.
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
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)
{
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)
{
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)
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;
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;
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)
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;
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
}
}
+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.
*
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)
{
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. */
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")
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 *
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);
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.
*/
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 */
}
}
+ /* (Re)Install LSP in the main table. */
+ if (static_lsp_install (zvrf, in_label, out_label, gtype,
+ gate, ifname, ifindex))
+ return -1;
+
return 0;
}
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);
}
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);
}
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).
*/
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.
if (!zvrf)
return;
zvrf->slsp_table = hash_create(label_hash, label_cmp);
+ zvrf->lsp_table = hash_create(label_hash, label_cmp);
}
/*
void
zebra_mpls_init (void)
{
- /* Filler for subsequent use. */
+ mpls_processq_init (&zebrad);
}
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.
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 */
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)
{
#include "routemap.h"
#include "nexthop.h"
#include "vrf.h"
+#include "mpls.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#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)
{
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. */
/* 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;