diff --git a/lib/nexthop.c b/lib/nexthop.c index 465cc94851..8e775b68be 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -129,6 +129,9 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh) nexthop->ifindex = nh->ifindex; memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + if (nh->nh_label) + nexthop_add_labels (nexthop, nh->nh_label->num_labels, + &nh->nh_label->label[0]); nexthop_add(tnh, nexthop); if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 8d19cbb5dc..3b564f2b15 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -77,12 +77,28 @@ static const struct message nlmsg_str[] = { #endif #ifndef RTA_VIA -#define RTA_VIA 16 +#define RTA_VIA 18 #endif #ifndef RTA_NEWDST #define RTA_NEWDST 19 #endif + +#ifndef RTA_ENCAP_TYPE +#define RTA_ENCAP_TYPE 21 +#endif + +#ifndef RTA_ENCAP +#define RTA_ENCAP 22 +#endif + +#ifndef LWTUNNEL_ENCAP_MPLS +#define LWTUNNEL_ENCAP_MPLS 1 +#endif + +#ifndef MPLS_IPTUNNEL_DST +#define MPLS_IPTUNNEL_DST 1 +#endif /* End of temporary definitions */ struct gw_family_t @@ -1720,6 +1736,39 @@ addattr32 (struct nlmsghdr *n, unsigned int maxlen, int type, int data) return addattr_l(n, maxlen, type, &data, sizeof(u_int32_t)); } +/* Some more utility functions from iproute2 */ +static struct rtattr * +addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +static int +addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = NLMSG_TAIL(n) - nest; + return n->nlmsg_len; +} + +static struct rtattr * +rta_nest(struct rtattr *rta, int maxlen, int type) +{ + struct rtattr *nest = RTA_TAIL(rta); + + rta_addattr_l(rta, maxlen, type, NULL, 0); + return nest; +} + +static int +rta_nest_end(struct rtattr *rta, struct rtattr *nest) +{ + nest->rta_len = RTA_TAIL(rta) - nest; + return rta->rta_len; +} + static int netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id) @@ -1865,7 +1914,8 @@ _netlink_route_build_singlepath( size_t req_size, int cmd) { - mpls_lse_t out_lse; + struct nexthop_label *nh_label; + mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[100]; if (rtmsg->rtm_family == AF_INET && @@ -1895,16 +1945,55 @@ _netlink_route_build_singlepath( } label_buf[0] = '\0'; + /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP + * (in the case of LER) + */ + nh_label = nexthop->nh_label; if (rtmsg->rtm_family == AF_MPLS) { - assert (nexthop->nh_label); + assert (nh_label); + assert (nh_label->num_labels == 1); + } - /* Fill out label, if present. */ - if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + if (nh_label && nh_label->num_labels) + { + int i, num_labels = 0; + u_int32_t bos; + char label_buf1[20]; + + for (i = 0; i < nh_label->num_labels; i++) { - 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 (nh_label->label[i] != MPLS_IMP_NULL_LABEL) + { + bos = ((i == (nh_label->num_labels - 1)) ? 1 : 0); + out_lse[i] = mpls_lse_encode (nh_label->label[i], 0, 0, bos); + if (!num_labels) + sprintf (label_buf, "label %d", nh_label->label[i]); + else + { + sprintf (label_buf1, "/%d", nh_label->label[i]); + strcat (label_buf, label_buf1); + } + num_labels++; + } + } + if (num_labels) + { + if (rtmsg->rtm_family == AF_MPLS) + addattr_l (nlmsg, req_size, RTA_NEWDST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + else + { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + + addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, + &encap, sizeof (u_int16_t)); + nest = addattr_nest(nlmsg, req_size, RTA_ENCAP); + addattr_l (nlmsg, req_size, MPLS_IPTUNNEL_DST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + addattr_nest_end(nlmsg, nest); + } } } @@ -2023,7 +2112,8 @@ _netlink_route_build_multipath( struct rtmsg *rtmsg, union g_addr **src) { - mpls_lse_t out_lse; + struct nexthop_label *nh_label; + mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[100]; rtnh->rtnh_len = sizeof (*rtnh); @@ -2059,18 +2149,60 @@ _netlink_route_build_multipath( } label_buf[0] = '\0'; + /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP + * (in the case of LER) + */ + nh_label = nexthop->nh_label; if (rtmsg->rtm_family == AF_MPLS) { - assert (nexthop->nh_label); + assert (nh_label); + assert (nh_label->num_labels == 1); + } - /* Fill out label, if present. */ - if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL) + if (nh_label && nh_label->num_labels) + { + int i, num_labels = 0; + u_int32_t bos; + char label_buf1[20]; + + for (i = 0; i < nh_label->num_labels; i++) { - 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 (nh_label->label[i] != MPLS_IMP_NULL_LABEL) + { + bos = ((i == (nh_label->num_labels - 1)) ? 1 : 0); + out_lse[i] = mpls_lse_encode (nh_label->label[i], 0, 0, bos); + if (!num_labels) + sprintf (label_buf, "label %d", nh_label->label[i]); + else + { + sprintf (label_buf1, "/%d", nh_label->label[i]); + strcat (label_buf, label_buf1); + } + num_labels++; + } + } + if (num_labels) + { + if (rtmsg->rtm_family == AF_MPLS) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + rtnh->rtnh_len += RTA_LENGTH (num_labels * sizeof(mpls_lse_t)); + } + else + { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + int len = rta->rta_len; + + rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE, + &encap, sizeof (u_int16_t)); + nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP); + rta_addattr_l (rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST, + &out_lse, num_labels * sizeof(mpls_lse_t)); + rta_nest_end(rta, nest); + rtnh->rtnh_len += rta->rta_len - len; + } } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 601721c6d4..f79dfafa5e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -204,6 +204,9 @@ rib_copy_nexthops (struct rib *rib, struct nexthop *nh) nexthop->ifindex = nh->ifindex; memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + if (nh->nh_label) + nexthop_add_labels (nexthop, nh->nh_label->num_labels, + &nh->nh_label->label[0]); rib_nexthop_add(rib, nexthop); if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) copy_nexthops(&nexthop->resolved, nh->resolved); diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 4acb77497d..fddc9d54c8 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -42,6 +42,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro struct route_node *rn; struct route_table *table; struct prefix nh_p; + struct nexthop *nexthop = NULL; /* Lookup table. */ table = zebra_vrf_table (afi, safi, si->vrf_id); @@ -71,29 +72,34 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro switch (si->type) { case STATIC_IPV4_GATEWAY: - rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nexthop = rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IFINDEX: - rib_nexthop_ifindex_add (rib, si->ifindex); + nexthop = rib_nexthop_ifindex_add (rib, si->ifindex); break; case STATIC_IPV4_BLACKHOLE: - rib_nexthop_blackhole_add (rib); + nexthop = rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nexthop = rib_nexthop_ipv6_add (rib, &si->addr.ipv6); nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFINDEX: - rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, si->ifindex); + nexthop = rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, + si->ifindex); break; } + /* Update label(s), if present. */ + if (si->snh_label.num_labels) + nexthop_add_labels (nexthop, si->snh_label.num_labels, + &si->snh_label.label[0]); if (IS_ZEBRA_DEBUG_RIB) { @@ -130,29 +136,34 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro switch (si->type) { case STATIC_IPV4_GATEWAY: - rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nexthop = rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IFINDEX: - rib_nexthop_ifindex_add (rib, si->ifindex); + nexthop = rib_nexthop_ifindex_add (rib, si->ifindex); break; case STATIC_IPV4_BLACKHOLE: - rib_nexthop_blackhole_add (rib); + nexthop = rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nexthop = rib_nexthop_ipv6_add (rib, &si->addr.ipv6); nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFINDEX: - rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, si->ifindex); + nexthop = rib_nexthop_ipv6_ifindex_add (rib, &si->addr.ipv6, + si->ifindex); break; } + /* Update label(s), if present. */ + if (si->snh_label.num_labels) + nexthop_add_labels (nexthop, si->snh_label.num_labels, + &si->snh_label.label[0]); /* Save the flags of this static routes (reject, blackhole) */ rib->flags = si->flags; @@ -181,30 +192,59 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro } } +static int +static_nexthop_label_same (struct nexthop *nexthop, + struct static_nh_label *snh_label) +{ + int i; + + if ((snh_label->num_labels == 0 && nexthop->nh_label) || + (snh_label->num_labels != 0 && !nexthop->nh_label)) + return 0; + + if (snh_label->num_labels != 0) + if (snh_label->num_labels != nexthop->nh_label->num_labels) + return 0; + + for (i = 0; i < snh_label->num_labels; i++) + if (snh_label->label[i] != nexthop->nh_label->label[i]) + return 0; + + return 1; +} + static int static_nexthop_same (struct nexthop *nexthop, struct static_route *si) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 - && si->type == STATIC_IPV4_GATEWAY - && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4)) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - && si->type == STATIC_IFINDEX - && nexthop->ifindex == si->ifindex) - return 1; + int gw_match = 0; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE && si->type == STATIC_IPV4_BLACKHOLE) return 1; - if (nexthop->type == NEXTHOP_TYPE_IPV6 + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + && si->type == STATIC_IPV4_GATEWAY + && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4)) + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IFINDEX + && si->type == STATIC_IFINDEX + && nexthop->ifindex == si->ifindex) + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IPV6 && si->type == STATIC_IPV6_GATEWAY && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6)) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + gw_match = 1; + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX && si->type == STATIC_IPV6_GATEWAY_IFINDEX && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6) && nexthop->ifindex == si->ifindex) - return 1; - return 0; + gw_match = 1; + + if (!gw_match) + return 0; + + /* Check match on label(s), if any */ + return static_nexthop_label_same (nexthop, &si->snh_label); } /* Uninstall static route from RIB. */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index fa2a270889..1dfa40145b 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2318,6 +2318,15 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) default: break; } + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + vty_out (vty, " label %s", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); @@ -2558,6 +2567,14 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, break; } + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + vty_out (vty, " label %s", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) vty_out (vty, ", bh"); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT))