diff options
author | Donald Sharp <sharpd@cumulusnetworks.com> | 2017-05-02 17:38:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-02 17:38:06 +0200 |
commit | b46be72b4aff1d413e89bc26a70a29934afe9d85 (patch) | |
tree | cd556a135211559b7e636db619dbf5257f874e86 | |
parent | vtysh: remove unused compilation file (diff) | |
parent | Merge pull request #377 from qlyoung/frr-pthreads (diff) | |
download | frr-b46be72b4aff1d413e89bc26a70a29934afe9d85.tar.xz frr-b46be72b4aff1d413e89bc26a70a29934afe9d85.zip |
Merge branch 'master' into EIGRP
81 files changed, 6415 insertions, 1157 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index b6ed9a4d6..4ea045552 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -81,7 +81,7 @@ libbgp_a_SOURCES = \ bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ - bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c + bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c noinst_HEADERS = \ bgp_memory.h \ @@ -92,7 +92,8 @@ noinst_HEADERS = \ bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ bgp_advertise.h bgp_vty.h bgp_mpath.h bgp_nht.h \ bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \ - $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h bgp_vpn.h + $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ + bgp_vpn.h bgp_label.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 1fccd25c8..a25ebf477 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -78,7 +78,8 @@ static const struct message attr_str [] = #if ENABLE_BGP_VNC { BGP_ATTR_VNC, "VNC" }, #endif - { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" } + { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" }, + { BGP_ATTR_LABEL_INDEX, "LABEL_INDEX" } }; static const int attr_str_max = array_size(attr_str); @@ -676,6 +677,7 @@ attrhash_key_make (void *p) MIX(extra->mp_nexthop_global_in.s_addr); MIX(extra->originator_id.s_addr); MIX(extra->tag); + MIX(extra->label_index); } if (attr->aspath) @@ -730,6 +732,7 @@ attrhash_cmp (const void *p1, const void *p2) && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr && ae1->weight == ae2->weight && ae1->tag == ae2->tag + && ae1->label_index == ae2->label_index && ae1->mp_nexthop_len == ae2->mp_nexthop_len && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global) && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local) @@ -1287,6 +1290,7 @@ const u_int8_t attr_flags_values [] = { [BGP_ATTR_AS4_PATH] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_AS4_AGGREGATOR] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_LARGE_COMMUNITIES]= BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_LABEL_INDEX] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -2274,6 +2278,52 @@ bgp_attr_encap( return 0; } +/* Label index attribute */ +static bgp_attr_parse_ret_t +bgp_attr_label_index (struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + u_int32_t label_index; + + /* Length check. */ + if (length != 8) + { + zlog_err ("Bad label index length %d", length); + + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* First u32 is currently unused - reserved and flags (undefined) */ + stream_getl (peer->ibuf); + + /* Fetch the label index and see if it is valid. */ + label_index = stream_getl (peer->ibuf); + if (label_index == BGP_INVALID_LABEL_INDEX) + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + /* Store label index; subsequently, we'll check on address-family */ + (bgp_attr_extra_get (attr))->label_index = label_index; + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX); + + /* + * Ignore the Label index attribute unless received for labeled-unicast + * SAFI. We reset the flag, though it is probably unnecesary. + */ + if (!mp_update->length || mp_update->safi != SAFI_LABELED_UNICAST) + { + attr->extra->label_index = BGP_INVALID_LABEL_INDEX; + attr->flag &= ~ATTR_FLAG_BIT(BGP_ATTR_LABEL_INDEX); + } + return BGP_ATTR_PARSE_PROCEED; +} + /* BGP unknown attribute treatment. */ static bgp_attr_parse_ret_t bgp_attr_unknown (struct bgp_attr_parser_args *args) @@ -2572,6 +2622,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_ENCAP: ret = bgp_attr_encap (type, peer, length, attr, flag, startp); break; + case BGP_ATTR_LABEL_INDEX: + ret = bgp_attr_label_index (&attr_args, mp_update); + break; default: ret = bgp_attr_unknown (&attr_args); break; @@ -2740,6 +2793,7 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, if (nh_afi == AFI_MAX) nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->extra->mp_nexthop_len); + /* Nexthop */ switch (nh_afi) { @@ -2748,6 +2802,7 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, { case SAFI_UNICAST: case SAFI_MULTICAST: + case SAFI_LABELED_UNICAST: bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr); stream_putc (s, 4); stream_put_ipv4 (s, attr->nexthop.s_addr); @@ -2772,6 +2827,7 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, { case SAFI_UNICAST: case SAFI_MULTICAST: + case SAFI_LABELED_UNICAST: { struct attr_extra *attre = attr->extra; @@ -2875,6 +2931,11 @@ bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, { bgp_packet_mpattr_route_type_5(s, p, prd, tag, attr); } + else if (safi == SAFI_LABELED_UNICAST) + { + /* Prefix write with label. */ + stream_put_labeled_prefix(s, p, tag); + } else stream_put_prefix_addpath (s, p, addpath_encode, addpath_tx_id); } @@ -3112,7 +3173,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, stream_putc (s, 4); stream_put_ipv4 (s, attr->nexthop.s_addr); } - else if (safi == SAFI_UNICAST && peer_cap_enhe(from)) + else if (peer_cap_enhe(from)) { /* * Likely this is the case when an IPv4 prefix was received with @@ -3348,6 +3409,23 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } } + /* Label index attribute. */ + if (safi == SAFI_LABELED_UNICAST) + { + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + u_int32_t label_index; + + assert (attr->extra); + label_index = attr->extra->label_index; + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_LABEL_INDEX); + stream_putc (s, 8); + stream_putl (s, 0); + stream_putl (s, label_index); + } + } + if ( send_as4_path ) { /* If the peer is NOT As4 capable, AND */ @@ -3439,6 +3517,11 @@ bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, u_char *tag, int addpath_encode, u_int32_t addpath_tx_id, struct attr *attr) { + u_char wlabel[3] = {0x80, 0x00, 0x00}; + + if (safi == SAFI_LABELED_UNICAST) + tag = wlabel; + return bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag, addpath_encode, addpath_tx_id, attr); } @@ -3626,6 +3709,17 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); } + /* Label index */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + assert (attr->extra); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_LABEL_INDEX); + stream_putc (s, 8); + stream_putl (s, 0); + stream_putl (s, attr->extra->label_index); + } + /* Return total size of attribute. */ len = stream_get_endp (s) - cp - 2; stream_putw_at (s, cp, len); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 015039c6c..d57e944aa 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -134,6 +134,9 @@ struct attr_extra /* route tag */ route_tag_t tag; + /* Label index */ + u_int32_t label_index; + uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ @@ -160,7 +163,7 @@ struct attr unsigned long refcnt; /* Flag of attribute is set or not. */ - u_int32_t flag; + uint64_t flag; /* Apart from in6_addr, the remaining static attributes */ struct in_addr nexthop; @@ -201,7 +204,7 @@ struct transit u_char *val; }; -#define ATTR_FLAG_BIT(X) (1 << ((X) - 1)) +#define ATTR_FLAG_BIT(X) (1ULL << ((X) - 1)) #define BGP_CLUSTER_LIST_LENGTH(attr) \ (((attr)->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) ? \ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 232f53c77..8e4d8bf4f 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -450,6 +450,10 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size) snprintf (buf + strlen (buf), size - strlen (buf), ", path %s", aspath_print (attr->aspath)); + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX))) + snprintf (buf + strlen (buf), size - strlen (buf), ", label-index %u", + attr->extra->label_index); + if (strlen (buf) > 1) return 1; else diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 2bbdca595..b17888482 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1142,9 +1142,11 @@ bgp_stop (struct peer *peer) /* Reset prefix count */ peer->pcount[AFI_IP][SAFI_UNICAST] = 0; peer->pcount[AFI_IP][SAFI_MULTICAST] = 0; + peer->pcount[AFI_IP][SAFI_LABELED_UNICAST] = 0; peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0; peer->pcount[AFI_IP6][SAFI_UNICAST] = 0; peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0; + peer->pcount[AFI_IP6][SAFI_LABELED_UNICAST] = 0; #endif /* 0 */ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE) && diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c new file mode 100644 index 000000000..615eca588 --- /dev/null +++ b/bgpd/bgp_label.c @@ -0,0 +1,338 @@ +/* BGP carrying label information + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "command.h" +#include "thread.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" +#include "log.h" +#include "memory.h" +#include "nexthop.h" +#include "mpls.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_label.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_debug.h" + +extern struct zclient *zclient; + +int +bgp_parse_fec_update (void) +{ + struct stream *s; + struct bgp_node *rn; + struct bgp *bgp; + struct bgp_table *table; + struct prefix p; + u_int32_t label; + afi_t afi; + safi_t safi; + + s = zclient->ibuf; + + memset(&p, 0, sizeof(struct prefix)); + p.family = stream_getw(s); + p.prefixlen = stream_getc(s); + stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); + label = stream_getl(s); + + /* hack for the bgp instance & SAFI = have to send/receive it */ + afi = family2afi(p.family); + safi = SAFI_LABELED_UNICAST; + bgp = bgp_get_default(); + if (!bgp) + { + zlog_debug("no default bgp instance"); + return -1; + } + + table = bgp->rib[afi][safi]; + if (!table) + { + zlog_debug("no %u labeled-unicast table", p.family); + return -1; + } + rn = bgp_node_lookup(table, &p); + if (!rn) + { + zlog_debug("no node for the prefix"); + return -1; + } + + /* treat it as implicit withdraw - the label is invalid */ + if (label == MPLS_INVALID_LABEL) + bgp_unset_valid_label(rn->local_label); + else + { + label_ntop(label, 1, rn->local_label); + bgp_set_valid_label(rn->local_label); + } + SET_FLAG(rn->flags, BGP_NODE_LABEL_CHANGED); + bgp_unlock_node (rn); + bgp_process (bgp, rn, afi, safi); + return 1; +} + +u_char * +bgp_adv_label (struct bgp_node *rn, struct bgp_info *ri, struct peer *to, + afi_t afi, safi_t safi) +{ + struct peer *from; + u_char *remote_label; + int reflect; + + if (!rn || !ri || !to) + return NULL; + + remote_label = ri->extra ? ri->extra->tag : NULL; + from = ri->peer; + reflect = ((from->sort == BGP_PEER_IBGP) && (to->sort == BGP_PEER_IBGP)); + + if (reflect && !CHECK_FLAG(to->af_flags[afi][safi], + PEER_FLAG_FORCE_NEXTHOP_SELF)) + return remote_label; + + if (CHECK_FLAG(to->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) + return remote_label; + + return rn->local_label; +} + +void +bgp_reg_dereg_for_label (struct bgp_node *rn, struct bgp_info *ri, + int reg) +{ + struct stream *s; + struct prefix *p; + int command; + u_int16_t flags = 0; + size_t flags_pos = 0; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return; + + p = &(rn->p); + s = zclient->obuf; + stream_reset (s); + command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER; + zclient_create_header (s, command, VRF_DEFAULT); + flags_pos = stream_get_endp (s); /* save position of 'flags' */ + stream_putw(s, flags); /* initial flags */ + stream_putw(s, PREFIX_FAMILY(p)); + stream_put_prefix(s, p); + if (reg) + { + assert (ri); + if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + assert (ri->attr->extra); + flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX; + stream_putl (s, ri->attr->extra->label_index); + } + SET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + } + else + UNSET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + + /* Set length and flags */ + stream_putw_at (s, 0, stream_get_endp (s)); + stream_putw_at (s, flags_pos, flags); + + zclient_send_message(zclient); +} + +static int +bgp_nlri_get_labels (struct peer *peer, u_char *pnt, u_char plen, + u_char label[]) +{ + u_char *data = pnt; + u_char *lim = pnt + plen; + u_char llen = 0; + + for (; data < lim; data += BGP_LABEL_BYTES) + { + memcpy(label, data, BGP_LABEL_BYTES); + llen += 3; + if (bgp_is_withdraw_label(label) || label_bos(label)) + break; + } + if (!(bgp_is_withdraw_label(label) || label_bos(label))) + zlog_warn("%s: [Update:RCVD] invalid label - no bottom of stack", + peer->host); + + return llen; +} + +int +bgp_nlri_parse_label (struct peer *peer, struct attr *attr, + struct bgp_nlri *packet) +{ + u_char *pnt; + u_char *lim; + struct prefix p; + int psize = 0; + int prefixlen; + afi_t afi; + safi_t safi; + int addpath_encoded; + u_int32_t addpath_id; + u_char label[3]; + u_char llen; + + /* Check peer status. */ + if (peer->status != Established) + return 0; + + pnt = packet->nlri; + lim = pnt + packet->length; + afi = packet->afi; + safi = packet->safi; + addpath_id = 0; + + addpath_encoded = (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) && + CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV)); + + for (; pnt < lim; pnt += psize) + { + /* Clear prefix structure. */ + memset (&p, 0, sizeof (struct prefix)); + llen = 0; + + if (addpath_encoded) + { + + /* When packet overflow occurs return immediately. */ + if (pnt + BGP_ADDPATH_ID_LEN > lim) + return -1; + + addpath_id = ntohl(*((uint32_t*) pnt)); + pnt += BGP_ADDPATH_ID_LEN; + } + + /* Fetch prefix length. */ + prefixlen = *pnt++; + p.family = afi2family (packet->afi); + psize = PSIZE (prefixlen); + + /* sanity check against packet data */ + if ((pnt + psize) > lim) + { + zlog_err ("%s [Error] Update packet error / L-U (prefix length %d exceeds packet size %u)", + peer->host, + prefixlen, (uint)(lim-pnt)); + return -1; + } + + /* Fill in the labels */ + llen = bgp_nlri_get_labels(peer, pnt, psize, label); + // zlog_debug("rcvd label [%x/%x/%x], llen=%d\n", label[0], label[1], label[2], llen); + p.prefixlen = prefixlen - BSIZE(llen); + + /* There needs to be at least one label */ + if (prefixlen < 24) + { + zlog_err ("%s [Error] Update packet error" + " (wrong label length %d)", + peer->host, prefixlen); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NETWORK); + return -1; + } + + if ((afi == AFI_IP && p.prefixlen > 32) + || (afi == AFI_IP6 && p.prefixlen > 128)) + return -1; + + /* Fetch prefix from NLRI packet */ + memcpy (&p.u.prefix, pnt + llen, psize - llen); + + /* Check address. */ + if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + { + if (IN_CLASSD (ntohl (p.u.prefix4.s_addr))) + { + /* From RFC4271 Section 6.3: + * + * If a prefix in the NLRI field is semantically incorrect + * (e.g., an unexpected multicast IP address), an error SHOULD + * be logged locally, and the prefix SHOULD be ignored. + */ + zlog_err ("%s: IPv4 labeled-unicast NLRI is multicast address %s, ignoring", + peer->host, inet_ntoa (p.u.prefix4)); + continue; + } + } + + /* Check address. */ + if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + { + if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) + { + char buf[BUFSIZ]; + + zlog_err ("%s: IPv6 labeled-unicast NLRI is link-local address %s, ignoring", + peer->host, inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ)); + + continue; + } + + if (IN6_IS_ADDR_MULTICAST (&p.u.prefix6)) + { + char buf[BUFSIZ]; + + zlog_err ("%s: IPv6 unicast NLRI is multicast address %s, ignoring", + peer->host, inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ)); + + continue; + } + } + + if (attr) + { + bgp_update (peer, &p, addpath_id, attr, packet->afi, SAFI_LABELED_UNICAST, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label, 0, NULL); + } + else + { + bgp_withdraw (peer, &p, addpath_id, attr, packet->afi, SAFI_LABELED_UNICAST, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label, NULL); + } + } + + /* Packet length consistency check. */ + if (pnt != lim) + { + zlog_err ("%s [Error] Update packet error / L-U (%zu data remaining after parsing)", + peer->host, lim - pnt); + return -1; + } + + return 0; +} diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h new file mode 100644 index 000000000..49a7b945a --- /dev/null +++ b/bgpd/bgp_label.h @@ -0,0 +1,125 @@ +/* BGP carrying Label information + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _BGP_LABEL_H +#define _BGP_LABEL_H + +#define BGP_LABEL_BYTES 3 +#define BGP_LABEL_BITS 24 +#define BGP_WITHDRAW_LABEL 0x800000 + +struct bgp_node; +struct bgp_info; +struct peer; + +extern void bgp_reg_dereg_for_label (struct bgp_node *rn, struct bgp_info *ri, + int reg); +extern int bgp_parse_fec_update(void); +extern u_char * bgp_adv_label(struct bgp_node *rn, struct bgp_info *ri, + struct peer *to, afi_t afi, safi_t safi); + +extern int bgp_nlri_parse_label (struct peer *peer, struct attr *attr, + struct bgp_nlri *packet); + +static inline int +bgp_labeled_safi (safi_t safi) +{ + if ((safi == SAFI_LABELED_UNICAST) || (safi == SAFI_MPLS_VPN)) + return 1; + return 0; +} + +static inline int +bgp_is_withdraw_label (u_char *pkt) +{ + if ((pkt[0] == 0x80) && (pkt[1] == 0x00) && (pkt[2] == 0x00)) + return 1; + return 0; +} + +static inline u_char * +bgp_encode_withdraw_label (u_char *pkt) +{ + *pkt++ = 0x80; *pkt++ = 0x00; *pkt++ = 0x00; + return pkt; +} + +static inline int +bgp_is_valid_label (u_char *t) +{ + if (!t) + return 0; + return (t[2] & 0x02); +} + +static inline void +bgp_set_valid_label (u_char *t) +{ + if (t) + t[2] |= 0x02; +} + +static inline void +bgp_unset_valid_label (u_char *t) +{ + if (t) + t[2] &= ~0x02; +} + +static inline void +bgp_register_for_label (struct bgp_node *rn, struct bgp_info *ri) +{ + bgp_reg_dereg_for_label (rn, ri, 1); +} + +static inline void +bgp_unregister_for_label (struct bgp_node *rn) +{ + bgp_reg_dereg_for_label (rn, NULL, 0); +} + +/* Label stream to value */ +static inline u_int32_t +label_pton (u_char t[]) +{ + return ((((unsigned int) t[0]) << 12) | (((unsigned int) t[1]) << 4) | + ((unsigned int) ((t[2] & 0xF0) >> 4))); +} + +/* Encode label values */ +static inline void +label_ntop (u_int32_t l, int bos, u_char t[]) +{ + t[0] = ((l & 0x000FF000) >> 12); + t[1] = ((l & 0x00000FF0) >> 4); + t[2] = ((l & 0x0000000F) << 4); + if (bos) + t[2] |= 0x01; +} + +/* Return BOS value of label stream */ +static inline u_char +label_bos (u_char t[]) +{ + return (t[2] & 0x01); +}; + +#endif /* _BGP_LABEL_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1773070fe..3b844cf70 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -237,6 +237,8 @@ bgp_exit (int status) stream_free (bgp_nexthop_buf); if (bgp_ifindices_buf) stream_free (bgp_ifindices_buf); + if (bgp_label_buf) + stream_free (bgp_label_buf); /* reverse bgp_master_init */ if (bm->master) diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index b0362b553..1e8dc5d97 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -404,8 +404,9 @@ bgp_parse_nexthop_update (int command, vrf_id_t vrf_id) { char buf[PREFIX2STR_BUFFER]; prefix2str(&p, buf, sizeof (buf)); - zlog_debug("%d: NH update for %s - metric %d (cur %d) #nhops %d (cur %d)", - vrf_id, buf, metric, bnc->metric, nexthop_num, bnc->nexthop_num); + zlog_debug("%d: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", + vrf_id, buf, metric, bnc->metric, nexthop_num, bnc->nexthop_num, + bnc->flags); } if (metric != bnc->metric) @@ -678,6 +679,8 @@ evaluate_paths (struct bgp_nexthop_cache *bnc) struct bgp *bgp = bnc->bgp; int afi; struct peer *peer = (struct peer *)bnc->nht_info; + struct bgp_table *table; + safi_t safi; if (BGP_DEBUG(nht, NHT)) { @@ -695,7 +698,10 @@ evaluate_paths (struct bgp_nexthop_cache *bnc) continue; rn = path->net; + assert (rn && bgp_node_table (rn)); afi = family2afi(rn->p.family); + table = bgp_node_table (rn); + safi = table->safi; /* Path becomes valid/invalid depending on whether the nexthop * reachable/unreachable. @@ -705,15 +711,13 @@ evaluate_paths (struct bgp_nexthop_cache *bnc) { if (CHECK_FLAG (path->flags, BGP_INFO_VALID)) { - bgp_aggregate_decrement (bgp, &rn->p, path, - afi, SAFI_UNICAST); + bgp_aggregate_decrement (bgp, &rn->p, path, afi, safi); bgp_info_unset_flag (rn, path, BGP_INFO_VALID); } else { bgp_info_set_flag (rn, path, BGP_INFO_VALID); - bgp_aggregate_increment (bgp, &rn->p, path, - afi, SAFI_UNICAST); + bgp_aggregate_increment (bgp, &rn->p, path, afi, safi); } } @@ -727,7 +731,7 @@ evaluate_paths (struct bgp_nexthop_cache *bnc) CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)) SET_FLAG(path->flags, BGP_INFO_IGP_CHANGED); - bgp_process(bgp, rn, afi, SAFI_UNICAST); + bgp_process(bgp, rn, afi, safi); } if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 51079f31e..83fc3fe97 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -108,6 +108,9 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer, u_char use_json, jso case SAFI_MULTICAST: json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "multicast"); break; + case SAFI_LABELED_UNICAST: + json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "labeled-unicast"); + break; case SAFI_MPLS_VPN: json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "MPLS-labeled VPN"); break; @@ -148,6 +151,9 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer, u_char use_json, jso case SAFI_MULTICAST: vty_out (vty, "SAFI Multicast"); break; + case SAFI_LABELED_UNICAST: + vty_out (vty, "SAFI Labeled-unicast"); + break; case SAFI_MPLS_VPN: vty_out (vty, "SAFI MPLS-labeled VPN"); break; @@ -1143,10 +1149,12 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability) { if (! peer->afc_nego[AFI_IP][SAFI_UNICAST] && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST] + && ! peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] && ! peer->afc_nego[AFI_IP][SAFI_ENCAP] && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST] && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST] + && ! peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] && ! peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] && ! peer->afc_nego[AFI_IP6][SAFI_ENCAP] && ! peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 853fcc869..3b1305560 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -55,6 +55,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_updgrp.h" +#include "bgpd/bgp_label.h" /* Set up BGP packet marker and packet type. */ int @@ -1153,8 +1154,10 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) { peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; + peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] = peer->afc[AFI_IP][SAFI_LABELED_UNICAST]; peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST]; + peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] = peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]; } /* When collision is detected and this peer is closed. Retrun @@ -1342,6 +1345,8 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet, i case SAFI_UNICAST: case SAFI_MULTICAST: return bgp_nlri_parse_ip (peer, mp_withdraw?NULL:attr, packet); + case SAFI_LABELED_UNICAST: + return bgp_nlri_parse_label (peer, mp_withdraw?NULL:attr, packet); case SAFI_MPLS_VPN: return bgp_nlri_parse_vpn (peer, mp_withdraw?NULL:attr, packet); case SAFI_ENCAP: diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 32cf0bcb8..88147e2b6 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -35,6 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "thread.h" #include "workqueue.h" #include "queue.h" +#include "mpls.h" #include "memory.h" #include "lib/json.h" @@ -62,6 +63,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_mpath.h" #include "bgpd/bgp_nht.h" #include "bgpd/bgp_updgrp.h" +#include "bgpd/bgp_label.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -296,6 +298,20 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri) } } +static int +bgp_label_index_differs (struct bgp_info *ri1, struct bgp_info *ri2) +{ + u_int32_t ri1_label_index = BGP_INVALID_LABEL_INDEX; + u_int32_t ri2_label_index = BGP_INVALID_LABEL_INDEX; + + if (ri1->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + ri1_label_index = ri1->attr->extra->label_index; + + if (ri2->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + ri2_label_index = ri2->attr->extra->label_index; + + return (!(ri1_label_index == ri2_label_index)); +} /* Set/unset bgp_info flags, adjusting any other state as needed. * This is here primarily to keep prefix-count in check. @@ -1187,7 +1203,8 @@ subgroup_announce_reset_nhop (u_char family, struct attr *attr) } int -subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, +subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri, + struct update_subgroup *subgrp, struct prefix *p, struct attr *attr) { struct bgp_filter *filter; @@ -1261,6 +1278,21 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, return 0; } + /* If it's labeled safi, make sure the route has a valid label. */ + if (bgp_labeled_safi(safi)) + { + u_char *tag = bgp_adv_label(rn, ri, peer, afi, safi); + if (!bgp_is_valid_label(tag)) + { + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s/%d is filtered - no label (%p)", + subgrp->update_group->id, subgrp->id, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, tag); + return 0; + } + } + /* Do not send back route to sender. */ if (onlypeer && from == onlypeer) { @@ -1804,7 +1836,7 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp, /* Announcement to the subgroup. If the route is filtered withdraw it. */ if (selected) { - if (subgroup_announce_check(selected, subgrp, p, &attr)) + if (subgroup_announce_check(rn, selected, subgrp, p, &attr)) bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected); else bgp_adj_out_unset_subgroup(rn, subgrp, 1, selected->addpath_tx_id); @@ -1914,7 +1946,39 @@ bgp_process_main (struct work_queue *wq, void *data) old_select = old_and_new.old; new_select = old_and_new.new; - /* Nothing to do. */ + /* Do we need to allocate or free labels? + * Right now, since we only deal with per-prefix labels, it is not necessary + * to do this upon changes to best path except of the label index changes. + */ + bgp_table_lock (bgp_node_table (rn)); + if (bgp_labeled_safi (safi)) + { + if (new_select) + { + if (!old_select || + bgp_label_index_differs (new_select, old_select) || + new_select->sub_type != old_select->sub_type) + { + if (new_select->sub_type == BGP_ROUTE_STATIC && + new_select->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) + bgp_unregister_for_label (rn); + label_ntop (MPLS_IMP_NULL_LABEL, 1, rn->local_label); + bgp_set_valid_label(rn->local_label); + } + else + bgp_register_for_label (rn, new_select); + } + } + else if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) + bgp_unregister_for_label (rn); + } + + /* If best route remains the same and this is not due to user-initiated + * clear, see exactly what needs to be done. + */ + if (old_select && old_select == new_select && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) && @@ -1926,10 +1990,26 @@ bgp_process_main (struct work_queue *wq, void *data) vnc_import_bgp_add_route(bgp, p, old_select); vnc_import_bgp_exterior_add_route(bgp, p, old_select); #endif - bgp_zebra_announce (p, old_select, bgp, afi, safi); + if (bgp_fibupd_safi(safi) && + !bgp->name && + !bgp_option_check (BGP_OPT_NO_FIB) && + new_select->type == ZEBRA_ROUTE_BGP && + new_select->sub_type == BGP_ROUTE_NORMAL) + bgp_zebra_announce (rn, p, old_select, bgp, afi, safi); } UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); bgp_zebra_clear_route_change_flags (rn); + + /* If there is a change of interest to peers, reannounce the route. */ + if (CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED) || + CHECK_FLAG (rn->flags, BGP_NODE_LABEL_CHANGED)) + { + group_announce_route(bgp, afi, safi, rn, new_select); + + UNSET_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED); + UNSET_FLAG (rn->flags, BGP_NODE_LABEL_CHANGED); + } + UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); return WQ_SUCCESS; } @@ -1978,7 +2058,7 @@ bgp_process_main (struct work_queue *wq, void *data) group_announce_route(bgp, afi, safi, rn, new_select); /* FIB update. */ - if ((safi == SAFI_UNICAST || safi == SAFI_MULTICAST) && + if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) && !bgp_option_check (BGP_OPT_NO_FIB)) { @@ -1986,7 +2066,7 @@ bgp_process_main (struct work_queue *wq, void *data) && new_select->type == ZEBRA_ROUTE_BGP && (new_select->sub_type == BGP_ROUTE_NORMAL || new_select->sub_type == BGP_ROUTE_AGGREGATE)) - bgp_zebra_announce (p, new_select, bgp, afi, safi); + bgp_zebra_announce (rn, p, new_select, bgp, afi, safi); else { /* Withdraw the route from the kernel. */ @@ -2406,6 +2486,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, struct bgp_info *new; const char *reason; char pfx_buf[BGP_PRD_PATH_STRLEN]; + char label_buf[20]; int connected = 0; int do_loop_check = 1; #if ENABLE_BGP_VNC @@ -2417,6 +2498,9 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, bgp = peer->bgp; rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + label_buf[0] = '\0'; + if (bgp_labeled_safi(safi)) + sprintf (label_buf, "label %u", label_pton(tag)); /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ @@ -2516,6 +2600,8 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, /* Same attribute comes in. */ if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) && attrhash_cmp (ri->attr, attr_new) + && (!bgp_labeled_safi(safi) || + memcmp ((bgp_info_extra_get (ri))->tag, tag, 3) == 0) && (overlay_index_equal(afi, ri, evpn==NULL?NULL:&evpn->eth_s_id, evpn==NULL?NULL:&evpn->gw_ip))) { @@ -2524,9 +2610,9 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, && CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) { if (bgp_debug_update(peer, p, NULL, 1)) - zlog_debug ("%s rcvd %s", peer->host, + zlog_debug ("%s rcvd %s %s", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0, - addpath_id, pfx_buf, sizeof (pfx_buf))); + addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf); if (bgp_damp_update (ri, rn, afi, safi) != BGP_DAMP_SUPPRESSED) { @@ -2544,10 +2630,10 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, peer->rcvd_attr_printed = 1; } - zlog_debug ("%s rcvd %s...duplicate ignored", + zlog_debug ("%s rcvd %s %s...duplicate ignored", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? - 1 : 0, addpath_id, pfx_buf, sizeof (pfx_buf))); + 1 : 0, addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf); } /* graceful restart STALE flag unset. */ @@ -2568,18 +2654,18 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { if (bgp_debug_update(peer, p, NULL, 1)) - zlog_debug ("%s rcvd %s, flapped quicker than processing", + zlog_debug ("%s rcvd %s %s, flapped quicker than processing", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0, - addpath_id, pfx_buf, sizeof (pfx_buf))); + addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf); bgp_info_restore (rn, ri); } /* Received Logging. */ if (bgp_debug_update(peer, p, NULL, 1)) - zlog_debug ("%s rcvd %s", peer->host, + zlog_debug ("%s rcvd %s %s", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0, - addpath_id, pfx_buf, sizeof (pfx_buf))); + addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf); /* graceful restart STALE flag unset. */ if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) @@ -2637,7 +2723,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, ri->attr = attr_new; /* Update MPLS tag. */ - if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) + if (bgp_labeled_safi(safi)) memcpy ((bgp_info_extra_get (ri))->tag, tag, 3); #if ENABLE_BGP_VNC @@ -2678,8 +2764,9 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, } } - /* Nexthop reachability check. */ - if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST) + /* Nexthop reachability check - for unicast and labeled-unicast.. */ + if ((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) @@ -2759,16 +2846,16 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, peer->rcvd_attr_printed = 1; } - zlog_debug ("%s rcvd %s", peer->host, + zlog_debug ("%s rcvd %s%s ", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0, - addpath_id, pfx_buf, sizeof (pfx_buf))); + addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf); } /* Make new BGP info. */ new = info_make(type, sub_type, 0, peer, attr_new, rn); /* Update MPLS tag. */ - if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) + if (bgp_labeled_safi(safi) || safi == SAFI_EVPN) memcpy ((bgp_info_extra_get (new))->tag, tag, 3); /* Update Overlay Index */ @@ -2778,7 +2865,8 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, evpn==NULL?NULL:&evpn->gw_ip); } /* Nexthop reachability check. */ - if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST) + if ((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) @@ -2873,10 +2961,10 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id, peer->rcvd_attr_printed = 1; } - zlog_debug ("%s rcvd UPDATE about %s -- DENIED due to: %s", + zlog_debug ("%s rcvd UPDATE about %s %s -- DENIED due to: %s", peer->host, bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0, - addpath_id, pfx_buf, sizeof (pfx_buf)), reason); + addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf, reason); } if (ri) @@ -3702,9 +3790,9 @@ bgp_static_free (struct bgp_static *bgp_static) XFREE (MTYPE_BGP_STATIC, bgp_static); } -static void -bgp_static_update_main (struct bgp *bgp, struct prefix *p, - struct bgp_static *bgp_static, afi_t afi, safi_t safi) +void +bgp_static_update (struct bgp *bgp, struct prefix *p, + struct bgp_static *bgp_static, afi_t afi, safi_t safi) { struct bgp_node *rn; struct bgp_info *ri; @@ -3732,6 +3820,13 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, if (bgp_static->atomic) attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); + /* Store label index, if required. */ + if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) + { + (bgp_attr_extra_get (&attr))->label_index = bgp_static->label_index; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX); + } + /* Apply route-map. */ if (bgp_static->rmap.name) { @@ -3818,9 +3913,11 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, #endif /* Nexthop reachability check. */ - if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK) && + safi == SAFI_UNICAST) { - if (bgp_find_or_add_nexthop (bgp, afi, ri, NULL, 0)) + if (bgp_find_or_add_nexthop (bgp, afi, ri, NULL, 0) && + safi == SAFI_UNICAST) bgp_info_set_flag (rn, ri, BGP_INFO_VALID); else { @@ -3904,13 +4001,6 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, } void -bgp_static_update (struct bgp *bgp, struct prefix *p, - struct bgp_static *bgp_static, afi_t afi, safi_t safi) -{ - bgp_static_update_main (bgp, p, bgp_static, afi, safi); -} - -void bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi) { @@ -4158,7 +4248,8 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p, route should be installed as valid. */ static int bgp_static_set (struct vty *vty, const char *ip_str, - afi_t afi, safi_t safi, const char *rmap, int backdoor) + afi_t afi, safi_t safi, const char *rmap, int backdoor, + u_int32_t label_index) { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; @@ -4191,6 +4282,13 @@ bgp_static_set (struct vty *vty, const char *ip_str, /* Configuration change. */ bgp_static = rn->info; + /* Label index cannot be changed. */ + if (bgp_static->label_index != label_index) + { + vty_out (vty, "%% Label index cannot be changed%s", VTY_NEWLINE); + return CMD_WARNING; + } + /* Check previous routes are installed into BGP. */ if (bgp_static->valid && bgp_static->backdoor != backdoor) need_update = 1; @@ -4222,6 +4320,7 @@ bgp_static_set (struct vty *vty, const char *ip_str, bgp_static->valid = 0; bgp_static->igpmetric = 0; bgp_static->igpnexthop.s_addr = 0; + bgp_static->label_index = label_index; if (rmap) { @@ -4748,7 +4847,8 @@ DEFUN (bgp_network, { int idx_ipv4_prefixlen = 1; return bgp_static_set (vty, argv[idx_ipv4_prefixlen]->arg, - AFI_IP, bgp_node_safi (vty), NULL, 0); + AFI_IP, bgp_node_safi (vty), NULL, 0, + BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_route_map, @@ -4762,7 +4862,8 @@ DEFUN (bgp_network_route_map, int idx_ipv4_prefixlen = 1; int idx_word = 3; return bgp_static_set (vty, argv[idx_ipv4_prefixlen]->arg, - AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0); + AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0, + BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_backdoor, @@ -4774,7 +4875,7 @@ DEFUN (bgp_network_backdoor, { int idx_ipv4_prefixlen = 1; return bgp_static_set (vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP, SAFI_UNICAST, - NULL, 1); + NULL, 1, BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask, @@ -4798,7 +4899,7 @@ DEFUN (bgp_network_mask, } return bgp_static_set (vty, prefix_str, - AFI_IP, bgp_node_safi (vty), NULL, 0); + AFI_IP, bgp_node_safi (vty), NULL, 0, BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask_route_map, @@ -4825,7 +4926,7 @@ DEFUN (bgp_network_mask_route_map, } return bgp_static_set (vty, prefix_str, - AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0); + AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0, BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask_backdoor, @@ -4850,7 +4951,8 @@ DEFUN (bgp_network_mask_backdoor, } return bgp_static_set (vty, prefix_str, AFI_IP, SAFI_UNICAST, - NULL, 1); + NULL, 1, + BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask_natural, @@ -4871,7 +4973,8 @@ DEFUN (bgp_network_mask_natural, } return bgp_static_set (vty, prefix_str, - AFI_IP, bgp_node_safi (vty), NULL, 0); + AFI_IP, bgp_node_safi (vty), NULL, 0, + BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask_natural_route_map, @@ -4895,7 +4998,8 @@ DEFUN (bgp_network_mask_natural_route_map, } return bgp_static_set (vty, prefix_str, - AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0); + AFI_IP, bgp_node_safi (vty), argv[idx_word]->arg, 0, + BGP_INVALID_LABEL_INDEX); } DEFUN (bgp_network_mask_natural_backdoor, @@ -4917,7 +5021,39 @@ DEFUN (bgp_network_mask_natural_backdoor, } return bgp_static_set (vty, prefix_str, AFI_IP, SAFI_UNICAST, - NULL, 1); + NULL, 1, BGP_INVALID_LABEL_INDEX); +} + +DEFUN (bgp_network_label_index, + bgp_network_label_index_cmd, + "network A.B.C.D/M label-index (0-4294967294)", + "Specify a network to announce via BGP\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Label index to associate with the prefix\n" + "Label index value\n") +{ + u_int32_t label_index; + + VTY_GET_INTEGER ("label-index", label_index, argv[3]->arg); + return bgp_static_set (vty, argv[1]->arg, + AFI_IP, bgp_node_safi (vty), NULL, 0, label_index); +} + +DEFUN (bgp_network_label_index_route_map, + bgp_network_label_index_route_map_cmd, + "network A.B.C.D/M label-index (0-4294967294) route-map WORD", + "Specify a network to announce via BGP\n" + "IP prefix\n" + "Label index to associate with the prefix\n" + "Label index value\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + u_int32_t label_index; + + VTY_GET_INTEGER ("label-index", label_index, argv[3]->arg); + return bgp_static_set (vty, argv[1]->arg, + AFI_IP, bgp_node_safi (vty), argv[5]->arg, 0, label_index); } DEFUN (no_bgp_network, @@ -4988,6 +5124,26 @@ DEFUN (no_bgp_network_mask_natural, bgp_node_safi (vty)); } +ALIAS (no_bgp_network, + no_bgp_network_label_index_cmd, + "no network A.B.C.D/M label-index (0-4294967294)", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Label index to associate with the prefix\n" + "Label index value\n") + +ALIAS (no_bgp_network, + no_bgp_network_label_index_route_map_cmd, + "no network A.B.C.D/M label-index (0-4294967294) route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix\n" + "Label index to associate with the prefix\n" + "Label index value\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + DEFUN (ipv6_bgp_network, ipv6_bgp_network_cmd, "network X:X::X:X/M", @@ -4996,7 +5152,8 @@ DEFUN (ipv6_bgp_network, { int idx_ipv6_prefixlen = 1; return bgp_static_set (vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, bgp_node_safi(vty), - NULL, 0); + NULL, 0, + BGP_INVALID_LABEL_INDEX); } DEFUN (ipv6_bgp_network_route_map, @@ -5010,7 +5167,40 @@ DEFUN (ipv6_bgp_network_route_map, int idx_ipv6_prefixlen = 1; int idx_word = 3; return bgp_static_set (vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, - bgp_node_safi (vty), argv[idx_word]->arg, 0); + bgp_node_safi (vty), argv[idx_word]->arg, 0, + BGP_INVALID_LABEL_INDEX); +} + +DEFUN (ipv6_bgp_network_label_index, + ipv6_bgp_network_label_index_cmd, + "network X:X::X:X/M label-index (0-4294967294)", + "Specify a network to announce via BGP\n" + "IPv6 prefix <network>/<length>\n" + "Label index to associate with the prefix\n" + "Label index value\n") +{ + u_int32_t label_index; + + VTY_GET_INTEGER ("label-index", label_index, argv[3]->arg); + return bgp_static_set (vty, argv[1]->arg, + AFI_IP6, bgp_node_safi (vty), NULL, 0, label_index); +} + +DEFUN (ipv6_bgp_network_label_index_route_map, + ipv6_bgp_network_label_index_route_map_cmd, + "network X:X::X:X/M label-index (0-4294967294) route-map WORD", + "Specify a network to announce via BGP\n" + "IPv6 prefix\n" + "Label index to associate with the prefix\n" + "Label index value\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + u_int32_t label_index; + + VTY_GET_INTEGER ("label-index", label_index, argv[3]->arg); + return bgp_static_set (vty, argv[1]->arg, + AFI_IP6, bgp_node_safi (vty), argv[5]->arg, 0, label_index); } DEFUN (no_ipv6_bgp_network, @@ -5026,6 +5216,26 @@ DEFUN (no_ipv6_bgp_network, return bgp_static_unset (vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, bgp_node_safi(vty)); } +ALIAS (no_ipv6_bgp_network, + no_ipv6_bgp_network_label_index_cmd, + "no network X:X::X:X/M label-index (0-4294967294)", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix <network>/<length>\n" + "Label index to associate with the prefix\n" + "Label index value\n") + +ALIAS (no_ipv6_bgp_network, + no_ipv6_bgp_network_label_index_route_map_cmd, + "no network X:X::X:X/M label-index (0-4294967294) route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix\n" + "Label index to associate with the prefix\n" + "Label index value\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + /* Aggreagete address: advertise-map Set condition to advertise attribute @@ -5557,6 +5767,8 @@ bgp_aggregate_unset (struct vty *vty, const char *prefix_str, aggregate = rn->info; if (aggregate->safi & SAFI_UNICAST) bgp_aggregate_delete (bgp, &p, afi, SAFI_UNICAST, aggregate); + if (aggregate->safi & SAFI_LABELED_UNICAST) + bgp_aggregate_delete (bgp, &p, afi, SAFI_LABELED_UNICAST, aggregate); if (aggregate->safi & SAFI_MULTICAST) bgp_aggregate_delete (bgp, &p, afi, SAFI_MULTICAST, aggregate); @@ -5615,6 +5827,8 @@ bgp_aggregate_set (struct vty *vty, const char *prefix_str, /* Aggregate address insert into BGP routing table. */ if (safi & SAFI_UNICAST) bgp_aggregate_add (bgp, &p, afi, SAFI_UNICAST, aggregate); + if (safi & SAFI_LABELED_UNICAST) + bgp_aggregate_add (bgp, &p, afi, SAFI_LABELED_UNICAST, aggregate); if (safi & SAFI_MULTICAST) bgp_aggregate_add (bgp, &p, afi, SAFI_MULTICAST, aggregate); @@ -7439,6 +7653,28 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (binfo->extra && binfo->extra->damp_info) bgp_damp_info_vty (vty, binfo, json_path); + /* Label information */ + if ((bgp_labeled_safi(safi) && binfo->extra) || + (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)))) + { + if (bgp_labeled_safi(safi) && binfo->extra) + { + uint32_t label = label_pton(binfo->extra->tag); + if (json_paths) + json_object_int_add(json_path, "remoteLabel", label); + else + vty_out(vty, " Remote label: %d%s", label, VTY_NEWLINE); + } + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX))) + { + if (json_paths) + json_object_int_add(json_path, "labelIndex", attr->extra->label_index); + else + vty_out(vty, " Label Index: %d%s", attr->extra->label_index, VTY_NEWLINE); + } + } + /* Line 8 display Addpath IDs */ if (binfo->addpath_rx_id || binfo->addpath_tx_id) { @@ -7932,6 +8168,18 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":" : "", buf2, p->prefixlen, VTY_NEWLINE); + + if (bgp_labeled_safi(safi)) + { + vty_out(vty, "Local label: "); + if (!bgp_is_valid_label(rn->local_label)) + vty_out(vty, "not allocated%s", VTY_NEWLINE); + else + { + uint32_t label = label_pton(rn->local_label); + vty_out(vty, "%d%s", label, VTY_NEWLINE); + } + } } for (ri = rn->info; ri; ri = ri->next) @@ -8213,7 +8461,7 @@ bgp_show_lcommunity_list (struct vty *vty, struct bgp *bgp, const char *lcom, DEFUN (show_ip_bgp_large_community_list, show_ip_bgp_large_community_list_cmd, - "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] large-community-list <(1-500)|WORD> [json]", + "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap|labeled-unicast>]] large-community-list <(1-500)|WORD> [json]", SHOW_STR IP_STR BGP_STR @@ -8224,6 +8472,7 @@ DEFUN (show_ip_bgp_large_community_list, "Address Family modifier\n" "Address Family modifier\n" "Address Family modifier\n" + "Address Family modifier\n" "Display routes matching the large-community-list\n" "large-community-list number\n" "large-community-list name\n" @@ -8259,7 +8508,7 @@ DEFUN (show_ip_bgp_large_community_list, } DEFUN (show_ip_bgp_large_community, show_ip_bgp_large_community_cmd, - "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] large-community [AA:BB:CC] [json]", + "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap|labeled-unicast>]] large-community [AA:BB:CC] [json]", SHOW_STR IP_STR BGP_STR @@ -8270,6 +8519,7 @@ DEFUN (show_ip_bgp_large_community, "Address Family modifier\n" "Address Family modifier\n" "Address Family modifier\n" + "Address Family modifier\n" "Display routes matching the large-communities\n" "List of large-community numbers\n" JSON_STR) @@ -9210,7 +9460,7 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, u_c DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, show_ip_bgp_instance_neighbor_prefix_counts_cmd, - "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] " + "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap|labeled-unicast>]] " "neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]", SHOW_STR IP_STR @@ -9222,6 +9472,7 @@ DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, "Address Family modifier\n" "Address Family modifier\n" "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -10528,6 +10779,9 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp, p->prefixlen); } + if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) + vty_out (vty, " label-index %u", bgp_static->label_index); + if (bgp_static->rmap.name) vty_out (vty, " route-map %s", bgp_static->rmap.name); else @@ -10654,6 +10908,8 @@ bgp_route_init (void) install_element (BGP_IPV4_NODE, &bgp_network_route_map_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_network_label_index_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_network_label_index_route_map_cmd); install_element (BGP_IPV4_NODE, &no_bgp_table_map_cmd); install_element (BGP_IPV4_NODE, &no_bgp_network_cmd); install_element (BGP_IPV4_NODE, &no_bgp_network_mask_cmd); @@ -10681,6 +10937,21 @@ bgp_route_init (void) install_element (BGP_IPV4M_NODE, &no_aggregate_address_cmd); install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd); + /* IPv4 labeled-unicast configuration. */ + install_element (BGP_IPV4L_NODE, &bgp_table_map_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_mask_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_mask_natural_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_route_map_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_mask_route_map_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_label_index_cmd); + install_element (BGP_IPV4L_NODE, &bgp_network_label_index_route_map_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_table_map_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_network_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_network_mask_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_network_mask_natural_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_all_cmd); install_element (VIEW_NODE, &show_ip_bgp_cmd); install_element (VIEW_NODE, &show_ip_bgp_route_cmd); @@ -10714,6 +10985,10 @@ bgp_route_init (void) install_element (BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd); install_element (BGP_IPV6_NODE, &no_bgp_table_map_cmd); install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd); + install_element (BGP_IPV6_NODE, &ipv6_bgp_network_label_index_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_label_index_cmd); + install_element (BGP_IPV6_NODE, &ipv6_bgp_network_label_index_route_map_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_label_index_route_map_cmd); install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_cmd); install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd); @@ -10721,6 +10996,12 @@ bgp_route_init (void) install_element (BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); install_element (BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd); + install_element (BGP_IPV6L_NODE, &bgp_table_map_cmd); + install_element (BGP_IPV6L_NODE, &ipv6_bgp_network_cmd); + install_element (BGP_IPV6L_NODE, &ipv6_bgp_network_route_map_cmd); + install_element (BGP_IPV6L_NODE, &no_bgp_table_map_cmd); + install_element (BGP_IPV6L_NODE, &no_ipv6_bgp_network_cmd); + install_element (BGP_NODE, &bgp_distance_cmd); install_element (BGP_NODE, &no_bgp_distance_cmd); install_element (BGP_NODE, &bgp_distance_source_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index e75978d00..35994d4f7 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -150,6 +150,7 @@ struct bgp_info #define BGP_INFO_COUNTED (1 << 10) #define BGP_INFO_MULTIPATH (1 << 11) #define BGP_INFO_MULTIPATH_CHG (1 << 12) +#define BGP_INFO_RIB_ATTR_CHG (1 << 13) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ u_char type; @@ -179,6 +180,10 @@ struct bgp_static /* Backdoor configuration. */ int backdoor; + /* Label index configuration; applies to LU prefixes. */ + u_int32_t label_index; +#define BGP_INVALID_LABEL_INDEX 0xFFFFFFFF + /* Import check status. */ u_char valid; @@ -273,6 +278,16 @@ bgp_bump_version (struct bgp_node *node) node->version = bgp_table_next_version(bgp_node_table(node)); } +static inline int +bgp_fibupd_safi (safi_t safi) +{ + if (safi == SAFI_UNICAST || + safi == SAFI_MULTICAST || + safi == SAFI_LABELED_UNICAST) + return 1; + return 0; +} + /* Prototypes. */ extern void bgp_process_queue_init (void); extern void bgp_route_init (void); @@ -370,7 +385,7 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp, struct bgp_node *rn, u_int32_t addpath_tx_id); -extern int subgroup_announce_check(struct bgp_info *ri, +extern int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri, struct update_subgroup *subgrp, struct prefix *p, struct attr *attr); diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 3c96dac61..a6b99a53d 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -56,10 +56,14 @@ struct bgp_node struct bgp_node *prn; + u_char local_label[3]; + uint64_t version; u_char flags; #define BGP_NODE_PROCESS_SCHEDULED (1 << 0) #define BGP_NODE_USER_CLEAR (1 << 1) +#define BGP_NODE_LABEL_CHANGED (1 << 2) +#define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3) }; /* diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index efb2046e1..ac5f77474 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -618,7 +618,7 @@ subgroup_announce_table (struct update_subgroup *subgrp, if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) || (addpath_capable && bgp_addpath_tx_path(peer, afi, safi, ri))) { - if (subgroup_announce_check (ri, subgrp, &rn->p, &attr)) + if (subgroup_announce_check (rn, ri, subgrp, &rn->p, &attr)) bgp_adj_out_set_subgroup (rn, subgrp, &attr, ri); else bgp_adj_out_unset_subgroup (rn, subgrp, 1, ri->addpath_tx_id); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 8839de391..9be1a50db 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -43,6 +43,7 @@ #include "workqueue.h" #include "hash.h" #include "queue.h" +#include "mpls.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_debug.h" @@ -54,6 +55,7 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nht.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_label.h" /******************** * PRIVATE FUNCTIONS @@ -653,6 +655,7 @@ subgroup_update_packet (struct update_subgroup *subgrp) int addpath_encode = 0; u_int32_t addpath_tx_id = 0; struct prefix_rd *prd = NULL; + char label_buf[20]; if (!subgrp) return NULL; @@ -660,7 +663,6 @@ subgroup_update_packet (struct update_subgroup *subgrp) if (bpacket_queue_is_full (SUBGRP_INST (subgrp), SUBGRP_PKTQ (subgrp))) return NULL; - peer = SUBGRP_PEER (subgrp); afi = SUBGRP_AFI (subgrp); safi = SUBGRP_SAFI (subgrp); @@ -668,6 +670,7 @@ subgroup_update_packet (struct update_subgroup *subgrp) stream_reset (s); snlri = subgrp->scratch; stream_reset (snlri); + label_buf[0] = '\0'; bpacket_attr_vec_arr_reset (&vecarr); @@ -760,8 +763,9 @@ subgroup_update_packet (struct update_subgroup *subgrp) if (rn->prn) prd = (struct prefix_rd *) &rn->prn->p; - if (binfo && binfo->extra) - tag = binfo->extra->tag; + tag = bgp_adv_label(rn, binfo, peer, afi, safi); + if (bgp_labeled_safi(safi)) + sprintf (label_buf, "label %u", label_pton(tag)); if (stream_empty (snlri)) mpattrlen_pos = bgp_packet_mpattr_start (snlri, afi, safi, @@ -783,14 +787,26 @@ subgroup_update_packet (struct update_subgroup *subgrp) { zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE w/ attr: %s", subgrp->update_group->id, subgrp->id, send_attr_str); + if (!stream_empty (snlri)) + { + iana_afi_t pkt_afi; + safi_t pkt_safi; + + pkt_afi = afi_int2iana (afi); + pkt_safi = safi_int2iana (safi); + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send MP_REACH for afi/safi %d/%d", + subgrp->update_group->id, subgrp->id, pkt_afi, pkt_safi); + } + send_attr_printed = 1; } - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s", + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s %s", subgrp->update_group->id, subgrp->id, bgp_debug_rdpfxpath2str (prd, &rn->p, addpath_encode, addpath_tx_id, - pfx_buf, sizeof (pfx_buf))); + pfx_buf, sizeof (pfx_buf)), + label_buf); } /* Synchnorize attribute. */ @@ -824,7 +840,7 @@ subgroup_update_packet (struct update_subgroup *subgrp) packet = stream_dup (s); bgp_packet_set_size (packet); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " UPDATE len %zd numpfx %d", + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE len %zd numpfx %d", subgrp->update_group->id, subgrp->id, (stream_get_endp(packet) - stream_get_getp(packet)), num_pfx); pkt = bpacket_queue_add (SUBGRP_PKTQ (subgrp), packet, &vecarr); @@ -917,11 +933,20 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) /* If first time, format the MP_UNREACH header */ if (first_time) { + iana_afi_t pkt_afi; + safi_t pkt_safi; + + pkt_afi = afi_int2iana (afi); + pkt_safi = safi_int2iana (safi); + attrlen_pos = stream_get_endp (s); /* total attr length = 0 for now. reevaluate later */ stream_putw (s, 0); mp_start = stream_get_endp (s); mplen_pos = bgp_packet_mpunreach_start (s, afi, safi); + if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send MP_UNREACH for afi/safi %d/%d", + subgrp->update_group->id, subgrp->id, pkt_afi, pkt_safi); } bgp_packet_mpunreach_prefix (s, &rn->p, afi, safi, prd, NULL, @@ -968,7 +993,7 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) } bgp_packet_set_size (s); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " UPDATE (withdraw) len %zd numpfx %d", + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE (withdraw) len %zd numpfx %d", subgrp->update_group->id, subgrp->id, (stream_get_endp(s) - stream_get_getp(s)), num_pfx); pkt = bpacket_queue_add (SUBGRP_PKTQ (subgrp), stream_dup (s), NULL); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index e94de682d..44389b99c 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -60,6 +60,73 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA static struct peer_group * listen_range_exists (struct bgp *bgp, struct prefix *range, int exact); +#if 0 +#define INSTALL_CMD_ON_AF_NODES(cmd) \ + install_element(BGP_IPV4_NODE, cmd); \ + install_element(BGP_IPV4M_NODE, cmd); \ + install_element(BGP_IPV4L_NODE, cmd); \ + install_element(BGP_IPV6_NODE, cmd); \ + install_element(BGP_IPV6M_NODE, cmd); \ + install_element(BGP_IPV6L_NODE, cmd); \ + install_element(BGP_VPNV4_NODE, cmd); +#endif +static enum node_type +bgp_node_type (afi_t afi, safi_t safi) +{ + switch (afi) + { + case AFI_IP: + switch (safi) + { + case SAFI_UNICAST: + return BGP_IPV4_NODE; + break; + case SAFI_MULTICAST: + return BGP_IPV4M_NODE; + break; + case SAFI_LABELED_UNICAST: + return BGP_IPV4L_NODE; + break; + case SAFI_MPLS_VPN: + return BGP_VPNV4_NODE; + break; + case SAFI_ENCAP: + return BGP_ENCAP_NODE; + break; + } + break; + case AFI_IP6: + switch (safi) + { + case SAFI_UNICAST: + return BGP_IPV6_NODE; + break; + case SAFI_MULTICAST: + return BGP_IPV6M_NODE; + break; + case SAFI_LABELED_UNICAST: + return BGP_IPV6L_NODE; + break; + case SAFI_MPLS_VPN: + return BGP_VPNV6_NODE; + break; + case SAFI_ENCAP: + return BGP_ENCAP_NODE; + break; + } + break; + case AFI_L2VPN: + return BGP_EVPN_NODE; + break; + case AFI_MAX: + // We should never be here but to clarify the switch statement.. + return BGP_IPV4_NODE; + break; + } + + // Impossible to happen + return BGP_IPV4_NODE; +} /* Utility function to get address family from current node. */ afi_t @@ -70,6 +137,7 @@ bgp_node_afi (struct vty *vty) { case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_IPV6L_NODE: case BGP_VPNV6_NODE: case BGP_ENCAPV6_NODE: afi = AFI_IP6; @@ -107,6 +175,10 @@ bgp_node_safi (struct vty *vty) case BGP_EVPN_NODE: safi = SAFI_EVPN; break; + case BGP_IPV4L_NODE: + case BGP_IPV6L_NODE: + safi = SAFI_LABELED_UNICAST; + break; default: safi = SAFI_UNICAST; break; @@ -160,7 +232,7 @@ argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index, afi_t *af return ret; } -/* supports <unicast|multicast|vpn|encap> */ +/* supports <unicast|multicast|vpn|encap|labeled-unicast> */ safi_t bgp_vty_safi_from_arg(const char *safi_str) { @@ -173,6 +245,8 @@ bgp_vty_safi_from_arg(const char *safi_str) safi = SAFI_ENCAP; else if (strncmp (safi_str, "v", 1) == 0) safi = SAFI_MPLS_VPN; + else if (strncmp (safi_str, "l", 1) == 0) + safi = SAFI_LABELED_UNICAST; return safi; } @@ -192,6 +266,12 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t if (safi) *safi = SAFI_MULTICAST; } + else if (argv_find (argv, argc, "labeled-unicast", index)) + { + ret = 1; + if (safi) + *safi = SAFI_LABELED_UNICAST; + } else if (argv_find (argv, argc, "vpn", index)) { ret = 1; @@ -223,12 +303,12 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t * that is being parsed. * * The show commands are generally of the form: - * "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] ..." + * "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap|labeled-unicast>]] ..." * * Since we use argv_find if the show command in particular doesn't have: * [ip] * [<view|vrf> WORD] - * [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] + * [<ipv4|ipv6> [<unicast|multicast|vpn|encap|labeled-unicast>]] * The command parsing should still be ok. * * vty -> The vty for the command so we can output some useful data in @@ -5649,30 +5729,16 @@ DEFUN (no_neighbor_addpath_tx_bestpath_per_as, DEFUN_NOSH (address_family_ipv4_safi, address_family_ipv4_safi_cmd, - "address-family ipv4 [<unicast|multicast|vpn|encap>]", + "address-family ipv4 [<unicast|multicast|vpn|encap|labeled-unicast>]", "Enter Address Family command mode\n" "Address Family\n" BGP_SAFI_HELP_STR) { - int idx_safi = 2; - if (argc == (idx_safi + 1)) + + if (argc == 3) { - switch (bgp_vty_safi_from_arg(argv[idx_safi]->arg)) - { - case SAFI_MULTICAST: - vty->node = BGP_IPV4M_NODE; - break; - case SAFI_ENCAP: - vty->node = BGP_ENCAP_NODE; - break; - case SAFI_MPLS_VPN: - vty->node = BGP_VPNV4_NODE; - break; - case SAFI_UNICAST: - default: - vty->node = BGP_IPV4_NODE; - break; - } + safi_t safi = bgp_vty_safi_from_arg(argv[2]->arg); + vty->node = bgp_node_type(AFI_IP, safi); } else vty->node = BGP_IPV4_NODE; @@ -5682,30 +5748,15 @@ DEFUN_NOSH (address_family_ipv4_safi, DEFUN_NOSH (address_family_ipv6_safi, address_family_ipv6_safi_cmd, - "address-family ipv6 [<unicast|multicast|vpn|encap>]", + "address-family ipv6 [<unicast|multicast|vpn|encap|labeled-unicast>]", "Enter Address Family command mode\n" "Address Family\n" BGP_SAFI_HELP_STR) { - int idx_safi = 2; - if (argc == (idx_safi + 1)) + if (argc == 3) { - switch (bgp_vty_safi_from_arg(argv[idx_safi]->arg)) - { - case SAFI_MULTICAST: - vty->node = BGP_IPV6M_NODE; - break; - case SAFI_ENCAP: - vty->node = BGP_ENCAPV6_NODE; - break; - case SAFI_MPLS_VPN: - vty->node = BGP_VPNV6_NODE; - break; - case SAFI_UNICAST: - default: - vty->node = BGP_IPV6_NODE; - break; - } + safi_t safi = bgp_vty_safi_from_arg(argv[2]->arg); + vty->node = bgp_node_type(AFI_IP6, safi); } else vty->node = BGP_IPV6_NODE; @@ -5778,9 +5829,11 @@ DEFUN_NOSH (exit_address_family, { if (vty->node == BGP_IPV4_NODE || vty->node == BGP_IPV4M_NODE + || vty->node == BGP_IPV4L_NODE || vty->node == BGP_VPNV4_NODE || vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE + || vty->node == BGP_IPV6L_NODE || vty->node == BGP_VPNV6_NODE || vty->node == BGP_ENCAP_NODE || vty->node == BGP_ENCAPV6_NODE @@ -6871,6 +6924,8 @@ afi_safi_print (afi_t afi, safi_t safi) return "IPv4 Unicast"; else if (afi == AFI_IP && safi == SAFI_MULTICAST) return "IPv4 Multicast"; + else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + return "IPv4 labeled-unicast"; else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) return "IPv4 VPN"; else if (afi == AFI_IP && safi == SAFI_ENCAP) @@ -6879,6 +6934,8 @@ afi_safi_print (afi_t afi, safi_t safi) return "IPv6 Unicast"; else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) return "IPv6 Multicast"; + else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + return "IPv6 labeled-unicast"; else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) return "IPv6 VPN"; else if (afi == AFI_IP6 && safi == SAFI_ENCAP) @@ -6896,6 +6953,8 @@ afi_safi_json (afi_t afi, safi_t safi) return "IPv4Unicast"; else if (afi == AFI_IP && safi == SAFI_MULTICAST) return "IPv4Multicast"; + else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + return "IPv4LabeledUnicast"; else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) return "IPv4VPN"; else if (afi == AFI_IP && safi == SAFI_ENCAP) @@ -6904,6 +6963,8 @@ afi_safi_json (afi_t afi, safi_t safi) return "IPv6Unicast"; else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) return "IPv6Multicast"; + else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + return "IPv6LabeledUnicast"; else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) return "IPv6VPN"; else if (afi == AFI_IP6 && safi == SAFI_ENCAP) @@ -10033,6 +10094,13 @@ static struct cmd_node bgp_ipv4_multicast_node = 1, }; +static struct cmd_node bgp_ipv4_labeled_unicast_node = +{ + BGP_IPV4L_NODE, + "%s(config-router-af)# ", + 1, +}; + static struct cmd_node bgp_ipv6_unicast_node = { BGP_IPV6_NODE, @@ -10047,6 +10115,13 @@ static struct cmd_node bgp_ipv6_multicast_node = 1, }; +static struct cmd_node bgp_ipv6_labeled_unicast_node = +{ + BGP_IPV6L_NODE, + "%s(config-router-af)# ", + 1, +}; + static struct cmd_node bgp_vpnv4_node = { BGP_VPNV4_NODE, @@ -10091,8 +10166,10 @@ bgp_vty_init (void) install_node (&bgp_node, bgp_config_write); install_node (&bgp_ipv4_unicast_node, NULL); install_node (&bgp_ipv4_multicast_node, NULL); + install_node (&bgp_ipv4_labeled_unicast_node, NULL); install_node (&bgp_ipv6_unicast_node, NULL); install_node (&bgp_ipv6_multicast_node, NULL); + install_node (&bgp_ipv6_labeled_unicast_node, NULL); install_node (&bgp_vpnv4_node, NULL); install_node (&bgp_vpnv6_node, NULL); install_node (&bgp_encap_node, NULL); @@ -10103,8 +10180,10 @@ bgp_vty_init (void) install_default (BGP_NODE); install_default (BGP_IPV4_NODE); install_default (BGP_IPV4M_NODE); + install_default (BGP_IPV4L_NODE); install_default (BGP_IPV6_NODE); install_default (BGP_IPV6M_NODE); + install_default (BGP_IPV6L_NODE); install_default (BGP_VPNV4_NODE); install_default (BGP_VPNV6_NODE); install_default (BGP_ENCAP_NODE); @@ -10180,15 +10259,27 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &bgp_maxpaths_cmd); install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_cmd); install_element (BGP_NODE, &bgp_maxpaths_ibgp_cmd); - install_element(BGP_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element (BGP_NODE, &bgp_maxpaths_ibgp_cluster_cmd); install_element (BGP_NODE, &no_bgp_maxpaths_ibgp_cmd); install_element (BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cmd); - install_element(BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element (BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cluster_cmd); install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_cmd); install_element (BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cmd); - install_element(BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element (BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cluster_cmd); install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV4L_NODE, &bgp_maxpaths_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_maxpaths_cmd); + install_element (BGP_IPV6L_NODE, &bgp_maxpaths_cmd); + install_element (BGP_IPV6L_NODE, &no_bgp_maxpaths_cmd); + + install_element (BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element (BGP_IPV4L_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element (BGP_IPV6L_NODE, &no_bgp_maxpaths_ibgp_cmd); + /* "timers bgp" commands. */ install_element (BGP_NODE, &bgp_timers_cmd); install_element (BGP_NODE, &no_bgp_timers_cmd); @@ -10317,8 +10408,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &neighbor_activate_cmd); install_element (BGP_IPV4_NODE, &neighbor_activate_cmd); install_element (BGP_IPV4M_NODE, &neighbor_activate_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_activate_cmd); install_element (BGP_IPV6_NODE, &neighbor_activate_cmd); install_element (BGP_IPV6M_NODE, &neighbor_activate_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_activate_cmd); install_element (BGP_VPNV4_NODE, &neighbor_activate_cmd); install_element (BGP_VPNV6_NODE, &neighbor_activate_cmd); install_element (BGP_ENCAP_NODE, &neighbor_activate_cmd); @@ -10329,8 +10422,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &no_neighbor_activate_cmd); install_element (BGP_IPV4_NODE, &no_neighbor_activate_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_activate_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_activate_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_activate_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_activate_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_activate_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_activate_cmd); install_element (BGP_VPNV6_NODE, &no_neighbor_activate_cmd); install_element (BGP_ENCAP_NODE, &no_neighbor_activate_cmd); @@ -10346,8 +10441,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_IPV4_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_IPV4M_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_IPV6_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_IPV6M_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_VPNV4_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_VPNV6_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_ENCAP_NODE, &neighbor_set_peer_group_cmd); @@ -10357,8 +10454,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_IPV4_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_VPNV6_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_ENCAP_NODE, &no_neighbor_set_peer_group_cmd); @@ -10369,12 +10468,16 @@ bgp_vty_init (void) install_element (BGP_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV4_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV4_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV4M_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV6_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV6M_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_VPNV4_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_VPNV6_NODE, &neighbor_soft_reconfiguration_cmd); @@ -10391,10 +10494,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged_cmd); install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_attr_unchanged_cmd); install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged_cmd); install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_attr_unchanged_cmd); install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged_cmd); install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged_cmd); @@ -10420,10 +10527,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_IPV4M_NODE, &neighbor_nexthop_self_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_IPV6_NODE, &neighbor_nexthop_self_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_IPV6M_NODE, &neighbor_nexthop_self_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_VPNV4_NODE, &neighbor_nexthop_self_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd); @@ -10440,10 +10551,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd); install_element (BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_nexthop_self_force_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_nexthop_self_force_cmd); install_element (BGP_IPV6_NODE, &neighbor_nexthop_self_force_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_self_force_cmd); install_element (BGP_IPV6M_NODE, &neighbor_nexthop_self_force_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_nexthop_self_force_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_nexthop_self_force_cmd); install_element (BGP_VPNV4_NODE, &neighbor_nexthop_self_force_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_nexthop_self_force_cmd); install_element (BGP_VPNV6_NODE, &neighbor_nexthop_self_force_cmd); @@ -10456,10 +10571,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_as_override_cmd); install_element (BGP_IPV4M_NODE, &neighbor_as_override_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_as_override_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_as_override_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_as_override_cmd); install_element (BGP_IPV6_NODE, &neighbor_as_override_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_as_override_cmd); install_element (BGP_IPV6M_NODE, &neighbor_as_override_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_as_override_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_as_override_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_as_override_cmd); install_element (BGP_VPNV4_NODE, &neighbor_as_override_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_as_override_cmd); install_element (BGP_VPNV6_NODE, &neighbor_as_override_cmd); @@ -10490,6 +10609,14 @@ bgp_vty_init (void) install_element (BGP_IPV4M_NODE, &no_neighbor_remove_private_as_replace_as_cmd); install_element (BGP_IPV4M_NODE, &neighbor_remove_private_as_all_replace_as_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_remove_private_as_all_replace_as_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_remove_private_as_all_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_remove_private_as_all_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_remove_private_as_replace_as_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_remove_private_as_replace_as_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_remove_private_as_all_replace_as_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_remove_private_as_all_replace_as_cmd); install_element (BGP_IPV6_NODE, &neighbor_remove_private_as_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_remove_private_as_cmd); install_element (BGP_IPV6_NODE, &neighbor_remove_private_as_all_cmd); @@ -10506,6 +10633,14 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_remove_private_as_replace_as_cmd); install_element (BGP_IPV6M_NODE, &neighbor_remove_private_as_all_replace_as_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_remove_private_as_all_replace_as_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_remove_private_as_all_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_remove_private_as_all_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_remove_private_as_replace_as_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_remove_private_as_replace_as_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_remove_private_as_all_replace_as_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_remove_private_as_all_replace_as_cmd); install_element (BGP_VPNV4_NODE, &neighbor_remove_private_as_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_remove_private_as_cmd); install_element (BGP_VPNV4_NODE, &neighbor_remove_private_as_all_cmd); @@ -10540,6 +10675,10 @@ bgp_vty_init (void) install_element (BGP_IPV4M_NODE, &neighbor_send_community_type_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_send_community_type_cmd); install_element (BGP_IPV6_NODE, &neighbor_send_community_cmd); install_element (BGP_IPV6_NODE, &neighbor_send_community_type_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_send_community_cmd); @@ -10548,6 +10687,10 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &neighbor_send_community_type_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_send_community_type_cmd); install_element (BGP_VPNV4_NODE, &neighbor_send_community_cmd); install_element (BGP_VPNV4_NODE, &neighbor_send_community_type_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_send_community_cmd); @@ -10572,10 +10715,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_IPV4M_NODE, &neighbor_route_reflector_client_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_IPV6_NODE, &neighbor_route_reflector_client_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_IPV6M_NODE, &neighbor_route_reflector_client_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_reflector_client_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd); @@ -10592,10 +10739,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_IPV4M_NODE, &neighbor_route_server_client_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_IPV6_NODE, &neighbor_route_server_client_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_IPV6M_NODE, &neighbor_route_server_client_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_server_client_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_VPNV6_NODE, &neighbor_route_server_client_cmd); @@ -10612,10 +10763,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV4M_NODE, &neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV6_NODE, &neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV6M_NODE, &neighbor_addpath_tx_all_paths_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_addpath_tx_all_paths_cmd); install_element (BGP_VPNV4_NODE, &neighbor_addpath_tx_all_paths_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd); install_element (BGP_VPNV6_NODE, &neighbor_addpath_tx_all_paths_cmd); @@ -10628,10 +10783,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV4M_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV6_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV6M_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_VPNV4_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); install_element (BGP_VPNV6_NODE, &neighbor_addpath_tx_bestpath_per_as_cmd); @@ -10659,10 +10818,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV4M_NODE, &neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV6_NODE, &neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV6M_NODE, &neighbor_capability_orf_prefix_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_capability_orf_prefix_cmd); /* "neighbor capability dynamic" commands.*/ install_element (BGP_NODE, &neighbor_capability_dynamic_cmd); @@ -10699,12 +10862,18 @@ bgp_vty_init (void) install_element (BGP_IPV4M_NODE, &neighbor_default_originate_cmd); install_element (BGP_IPV4M_NODE, &neighbor_default_originate_rmap_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_default_originate_cmd); install_element (BGP_IPV6_NODE, &neighbor_default_originate_cmd); install_element (BGP_IPV6_NODE, &neighbor_default_originate_rmap_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_default_originate_cmd); install_element (BGP_IPV6M_NODE, &neighbor_default_originate_cmd); install_element (BGP_IPV6M_NODE, &neighbor_default_originate_rmap_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_default_originate_cmd); /* "neighbor port" commands. */ install_element (BGP_NODE, &neighbor_port_cmd); @@ -10718,10 +10887,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_weight_cmd); install_element (BGP_IPV4M_NODE, &neighbor_weight_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_weight_cmd); install_element (BGP_IPV6_NODE, &neighbor_weight_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_weight_cmd); install_element (BGP_IPV6M_NODE, &neighbor_weight_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_weight_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_weight_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_weight_cmd); install_element (BGP_VPNV4_NODE, &neighbor_weight_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_weight_cmd); install_element (BGP_VPNV6_NODE, &neighbor_weight_cmd); @@ -10762,10 +10935,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_IPV4M_NODE, &neighbor_distribute_list_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_IPV6_NODE, &neighbor_distribute_list_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_IPV6M_NODE, &neighbor_distribute_list_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_distribute_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_VPNV6_NODE, &neighbor_distribute_list_cmd); @@ -10782,10 +10959,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_IPV4M_NODE, &neighbor_prefix_list_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_IPV6_NODE, &neighbor_prefix_list_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_IPV6M_NODE, &neighbor_prefix_list_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_prefix_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_VPNV6_NODE, &neighbor_prefix_list_cmd); @@ -10802,10 +10983,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_IPV4M_NODE, &neighbor_filter_list_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_filter_list_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_IPV6_NODE, &neighbor_filter_list_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_IPV6M_NODE, &neighbor_filter_list_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_filter_list_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_filter_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_VPNV6_NODE, &neighbor_filter_list_cmd); @@ -10822,10 +11007,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_route_map_cmd); install_element (BGP_IPV4M_NODE, &neighbor_route_map_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_route_map_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_route_map_cmd); install_element (BGP_IPV6_NODE, &neighbor_route_map_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_route_map_cmd); install_element (BGP_IPV6M_NODE, &neighbor_route_map_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_route_map_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_route_map_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_map_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_map_cmd); install_element (BGP_VPNV6_NODE, &neighbor_route_map_cmd); @@ -10842,10 +11031,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_IPV4M_NODE, &neighbor_unsuppress_map_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_IPV6_NODE, &neighbor_unsuppress_map_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_IPV6M_NODE, &neighbor_unsuppress_map_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_VPNV4_NODE, &neighbor_unsuppress_map_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd); @@ -10877,6 +11070,13 @@ bgp_vty_init (void) install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_restart_cmd); install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_cmd); install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_cmd); install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_cmd); install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_warning_cmd); @@ -10891,6 +11091,13 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_restart_cmd); install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_cmd); install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_cmd); install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_cmd); install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_warning_cmd); @@ -10929,10 +11136,14 @@ bgp_vty_init (void) install_element (BGP_IPV4_NODE, &no_neighbor_allowas_in_cmd); install_element (BGP_IPV4M_NODE, &neighbor_allowas_in_cmd); install_element (BGP_IPV4M_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV4L_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV4L_NODE, &no_neighbor_allowas_in_cmd); install_element (BGP_IPV6_NODE, &neighbor_allowas_in_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_allowas_in_cmd); install_element (BGP_IPV6M_NODE, &neighbor_allowas_in_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV6L_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV6L_NODE, &no_neighbor_allowas_in_cmd); install_element (BGP_VPNV4_NODE, &neighbor_allowas_in_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_allowas_in_cmd); install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_cmd); @@ -10958,8 +11169,10 @@ bgp_vty_init (void) /* "exit-address-family" command. */ install_element (BGP_IPV4_NODE, &exit_address_family_cmd); install_element (BGP_IPV4M_NODE, &exit_address_family_cmd); + install_element (BGP_IPV4L_NODE, &exit_address_family_cmd); install_element (BGP_IPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); + install_element (BGP_IPV6L_NODE, &exit_address_family_cmd); install_element (BGP_VPNV4_NODE, &exit_address_family_cmd); install_element (BGP_VPNV6_NODE, &exit_address_family_cmd); install_element (BGP_ENCAP_NODE, &exit_address_family_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 33d24d530..a0aabcbd2 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -28,11 +28,13 @@ struct bgp; #define BGP_AFI_CMD_STR "<ipv4|ipv6>" #define BGP_AFI_HELP_STR "Address Family\nAddress Family\n" -#define BGP_SAFI_CMD_STR "<unicast|multicast|vpn|encap>" +#define BGP_SAFI_CMD_STR "<unicast|multicast|vpn|encap|labeled-unicast>" #define BGP_SAFI_HELP_STR \ "Address Family modifier\n" \ "Address Family modifier\n" \ "Address Family modifier\n" \ + "Address Family modifier\n" \ + "Address Family modifier\n" \ "Address Family modifier\n" #define BGP_AFI_SAFI_CMD_STR BGP_AFI_CMD_STR" "BGP_SAFI_CMD_STR #define BGP_AFI_SAFI_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_HELP_STR diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 72bd081a7..d76eb951d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -34,6 +34,7 @@ Boston, MA 02111-1307, USA. */ #include "lib/json.h" #include "lib/bfd.h" #include "filter.h" +#include "mpls.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_route.h" @@ -46,6 +47,7 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nht.h" #include "bgpd/bgp_bfd.h" +#include "bgpd/bgp_label.h" #if ENABLE_BGP_VNC # include "bgpd/rfapi/rfapi_backend.h" # include "bgpd/rfapi/vnc_export_bgp.h" @@ -57,6 +59,7 @@ struct zclient *zclient = NULL; /* Growable buffer for nexthops sent to zebra */ struct stream *bgp_nexthop_buf = NULL; struct stream *bgp_ifindices_buf = NULL; +struct stream *bgp_label_buf = NULL; /* These array buffers are used in making a copy of the attributes for route-map apply. Arrays are being used here to minimize mallocs and @@ -178,6 +181,14 @@ bgp_update_interface_nbrs (struct bgp *bgp, struct interface *ifp, } } +static int +bgp_read_fec_update (int command, struct zclient *zclient, + zebra_size_t length) +{ + bgp_parse_fec_update(); + return 0; +} + static void bgp_start_interface_nbrs (struct bgp *bgp, struct interface *ifp) { @@ -1204,8 +1215,8 @@ bgp_table_map_apply (struct route_map *map, struct prefix *p, } void -bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, - afi_t afi, safi_t safi) +bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info, + struct bgp *bgp, afi_t afi, safi_t safi) { u_int32_t flags; u_char distance; @@ -1216,6 +1227,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, struct bgp_info local_info; struct bgp_info *info_cp = &local_info; route_tag_t tag; + u_int32_t label; /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. @@ -1271,7 +1283,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if ((oldsize = stream_get_size (bgp_nexthop_buf)) < (sizeof (struct in_addr *) * nhcount)) { - newsize = (sizeof (struct in_addr *) * nhcount); + newsize = sizeof (struct in_addr *) * nhcount; newsize = stream_resize (bgp_nexthop_buf, newsize); if (newsize == oldsize) { @@ -1282,6 +1294,25 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, stream_reset (bgp_nexthop_buf); nexthop = NULL; + /* For labeled unicast, each nexthop has a label too. Resize label + * buffer, if required. + */ + if (safi == SAFI_LABELED_UNICAST) + { + if ((oldsize = stream_get_size (bgp_label_buf)) < + (sizeof (unsigned int) * nhcount)) + { + newsize = (sizeof (unsigned int) * nhcount); + newsize = stream_resize (bgp_label_buf, newsize); + if (newsize == oldsize) + { + zlog_err ("can't resize label buffer"); + return; + } + } + stream_reset (bgp_label_buf); + } + /* Metric is currently based on the best-path only. */ metric = info->attr->med; @@ -1311,6 +1342,11 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, { stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); valid_nh_count++; + if (safi == SAFI_LABELED_UNICAST) + { + label = label_pton(info->extra->tag); + stream_put (bgp_label_buf, &label, sizeof (u_int32_t)); + } } for (mpinfo = bgp_info_mpath_first (info); mpinfo; @@ -1336,6 +1372,11 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, continue; stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); + if (safi == SAFI_LABELED_UNICAST) + { + label = label_pton(mpinfo->extra->tag); + stream_put (bgp_label_buf, &label, sizeof (u_int32_t)); + } valid_nh_count++; } @@ -1344,8 +1385,10 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, api.type = ZEBRA_ROUTE_BGP; api.instance = 0; api.message = 0; - api.safi = safi; + api.safi = (safi == SAFI_LABELED_UNICAST) ? SAFI_UNICAST : safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + if (safi == SAFI_LABELED_UNICAST) + SET_FLAG (api.message, ZAPI_MESSAGE_LABEL); /* Note that this currently only applies to Null0 routes for aggregates. * ZEBRA_FLAG_BLACKHOLE signals zapi_ipv4_route to encode a special @@ -1358,6 +1401,16 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, api.nexthop_num = valid_nh_count; api.nexthop = (struct in_addr **)STREAM_DATA (bgp_nexthop_buf); + if (safi == SAFI_LABELED_UNICAST) + { + api.label_num = valid_nh_count; + api.label = (unsigned int *)STREAM_DATA (bgp_label_buf); + } + else + { + api.label_num = 0; + api.label = NULL; + } api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = metric; @@ -1379,14 +1432,22 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; + char label_buf[20]; zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI " count %d", (valid_nh_count ? "add":"delete"), bgp->vrf_id, inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag, api.nexthop_num); for (i = 0; i < api.nexthop_num; i++) - zlog_debug(" IPv4 [nexthop %d] %s", i+1, - inet_ntop(AF_INET, api.nexthop[i], buf[1], sizeof(buf[1]))); + { + label_buf[0] = '\0'; + if (safi == SAFI_LABELED_UNICAST) + sprintf(label_buf, "label %u", api.label[i]); + zlog_debug(" nhop [%d]: %s %s", + i+1, + inet_ntop(AF_INET, api.nexthop[i], buf[1], sizeof(buf[1])), + label_buf); + } } zapi_ipv4_route (valid_nh_count ? ZEBRA_IPV4_ROUTE_ADD: ZEBRA_IPV4_ROUTE_DELETE, @@ -1431,6 +1492,25 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, } stream_reset (bgp_ifindices_buf); + /* For labeled unicast, each nexthop has a label too. Resize label + * buffer, if required. + */ + if (safi == SAFI_LABELED_UNICAST) + { + if ((oldsize = stream_get_size (bgp_label_buf)) < + (sizeof (unsigned int) * nhcount)) + { + newsize = (sizeof (unsigned int) * nhcount); + newsize = stream_resize (bgp_label_buf, newsize); + if (newsize == oldsize) + { + zlog_err ("can't resize label buffer"); + return; + } + } + stream_reset (bgp_label_buf); + } + ifindex = 0; nexthop = NULL; @@ -1476,6 +1556,11 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, } stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); + if (safi == SAFI_LABELED_UNICAST) + { + label = label_pton(info->extra->tag); + stream_put (bgp_label_buf, &label, sizeof (u_int32_t)); + } valid_nh_count++; } @@ -1518,6 +1603,11 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); + if (safi == SAFI_LABELED_UNICAST) + { + label = label_pton(mpinfo->extra->tag); + stream_put (bgp_label_buf, &label, sizeof (u_int32_t)); + } valid_nh_count++; } @@ -1527,8 +1617,10 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, api.type = ZEBRA_ROUTE_BGP; api.instance = 0; api.message = 0; - api.safi = safi; + api.safi = (safi == SAFI_LABELED_UNICAST) ? SAFI_UNICAST : safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + if (safi == SAFI_LABELED_UNICAST) + SET_FLAG (api.message, ZAPI_MESSAGE_LABEL); /* Note that this currently only applies to Null0 routes for aggregates. * ZEBRA_FLAG_BLACKHOLE signals zapi_ipv6_route to encode a special @@ -1544,6 +1636,16 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); api.ifindex_num = valid_nh_count; api.ifindex = (ifindex_t *)STREAM_DATA (bgp_ifindices_buf); + if (safi == SAFI_LABELED_UNICAST) + { + api.label_num = valid_nh_count; + api.label = (unsigned int *)STREAM_DATA (bgp_label_buf); + } + else + { + api.label_num = 0; + api.label = NULL; + } SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = metric; api.tag = 0; @@ -1566,13 +1668,22 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; + char label_buf[20]; zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, valid_nh_count ? "add" : "delete", bgp->vrf_id, inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); for (i = 0; i < api.nexthop_num; i++) - zlog_debug(" IPv6 [nexthop %d] %s", i+1, - inet_ntop(AF_INET6, api.nexthop[i], buf[1], sizeof(buf[1]))); + { + label_buf[0] = '\0'; + if (safi == SAFI_LABELED_UNICAST) + sprintf(label_buf, "label %u", api.label[i]); + zlog_debug(" nhop [%d]: %s if %s %s", + i+1, + inet_ntop(AF_INET6, api.nexthop[i], buf[1], sizeof(buf[1])), + ifindex2ifname (api.ifindex[i], bgp->vrf_id), + label_buf); + } } if (valid_nh_count) @@ -1588,13 +1699,22 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, if (bgp_debug_zebra(p)) { int i; + char label_buf[20]; zlog_debug("Tx IPv6 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI, valid_nh_count ? "add" : "delete", bgp->vrf_id, inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), p->prefixlen, api.metric, api.tag); for (i = 0; i < api.nexthop_num; i++) - zlog_debug(" IPv6 [nexthop %d] %s", i+1, - inet_ntop(AF_INET6, api.nexthop[i], buf[1], sizeof(buf[1]))); + { + label_buf[0] = '\0'; + if (safi == SAFI_LABELED_UNICAST) + sprintf(label_buf, "label %u", api.label[i]); + zlog_debug(" nhop [%d]: %s if %s %s", + i+1, + inet_ntop(AF_INET6, api.nexthop[i], buf[1], sizeof(buf[1])), + ifindex2ifname (api.ifindex[i], bgp->vrf_id), + label_buf); + } } zapi_ipv6_route (valid_nh_count ? @@ -1626,7 +1746,7 @@ bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi) if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) && ri->type == ZEBRA_ROUTE_BGP && ri->sub_type == BGP_ROUTE_NORMAL) - bgp_zebra_announce (&rn->p, ri, bgp, afi, safi); + bgp_zebra_announce (rn, &rn->p, ri, bgp, afi, safi); } void @@ -1673,10 +1793,14 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) api.type = ZEBRA_ROUTE_BGP; api.instance = 0; api.message = 0; - api.safi = safi; + api.safi = (safi == SAFI_LABELED_UNICAST) ? SAFI_UNICAST : safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + if (safi == SAFI_LABELED_UNICAST) + SET_FLAG (api.message, ZAPI_MESSAGE_LABEL); api.nexthop_num = 0; api.nexthop = NULL; + api.label_num = 0; + api.label = NULL; api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; @@ -1712,11 +1836,14 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) api.type = ZEBRA_ROUTE_BGP; api.instance = 0; api.message = 0; - api.safi = safi; + api.safi = (safi == SAFI_LABELED_UNICAST) ? SAFI_UNICAST : safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + if (safi == SAFI_LABELED_UNICAST) + SET_FLAG (api.message, ZAPI_MESSAGE_LABEL); api.nexthop_num = 0; api.nexthop = NULL; api.ifindex_num = 0; + api.label_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; api.tag = 0; @@ -2141,9 +2268,11 @@ bgp_zebra_init (struct thread_master *master) zclient->redistribute_route_ipv6_del = zebra_read_ipv6; zclient->nexthop_update = bgp_read_nexthop_update; zclient->import_check_update = bgp_read_import_check_update; + zclient->fec_update = bgp_read_fec_update; bgp_nexthop_buf = stream_new(BGP_NEXTHOP_BUF_SIZE); bgp_ifindices_buf = stream_new(BGP_IFINDICES_BUF_SIZE); + bgp_label_buf = stream_new(BGP_LABEL_BUF_SIZE); } void diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index d22a00e8f..bc4e36352 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -23,9 +23,11 @@ Boston, MA 02111-1307, USA. */ #define BGP_NEXTHOP_BUF_SIZE (8 * sizeof (struct in_addr *)) #define BGP_IFINDICES_BUF_SIZE (8 * sizeof (unsigned int)) +#define BGP_LABEL_BUF_SIZE (8 * sizeof (unsigned int)) extern struct stream *bgp_nexthop_buf; extern struct stream *bgp_ifindices_buf; +extern struct stream *bgp_label_buf; extern void bgp_zebra_init (struct thread_master *master); extern void bgp_zebra_destroy (void); @@ -34,8 +36,8 @@ extern int bgp_config_write_maxpaths (struct vty *, struct bgp *, afi_t, safi_t, int *); extern int bgp_config_write_redistribute (struct vty *, struct bgp *, afi_t, safi_t, int *); -extern void bgp_zebra_announce (struct prefix *, struct bgp_info *, struct bgp *, - afi_t, safi_t); +extern void bgp_zebra_announce (struct bgp_node *, struct prefix *, + struct bgp_info *, struct bgp *, afi_t, safi_t); extern void bgp_zebra_announce_table (struct bgp *, afi_t, safi_t); extern void bgp_zebra_withdraw (struct prefix *, struct bgp_info *, safi_t); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 3f81c1c50..1c73fb940 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1636,6 +1636,8 @@ peer_as_change (struct peer *peer, as_t as, int as_specified) PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_LABELED_UNICAST], + PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MPLS_VPN], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_ENCAP], @@ -1644,6 +1646,8 @@ peer_as_change (struct peer *peer, as_t as, int as_specified) PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_LABELED_UNICAST], + PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MPLS_VPN], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_ENCAP], @@ -3608,10 +3612,12 @@ peer_active (struct peer *peer) return 0; if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] + || peer->afc[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_UNICAST] || peer->afc[AFI_IP6][SAFI_MULTICAST] + || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP6][SAFI_MPLS_VPN] || peer->afc[AFI_IP6][SAFI_ENCAP]) return 1; @@ -3624,10 +3630,12 @@ peer_active_nego (struct peer *peer) { if (peer->afc_nego[AFI_IP][SAFI_UNICAST] || peer->afc_nego[AFI_IP][SAFI_MULTICAST] + || peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP][SAFI_ENCAP] || peer->afc_nego[AFI_IP6][SAFI_UNICAST] || peer->afc_nego[AFI_IP6][SAFI_MULTICAST] + || peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP6][SAFI_ENCAP]) return 1; @@ -7262,6 +7270,8 @@ bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi, { if (safi == SAFI_UNICAST) vty_out (vty, "ipv4 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_out (vty, "ipv4 labeled-unicast"); else if (safi == SAFI_MULTICAST) vty_out (vty, "ipv4 multicast"); else if (safi == SAFI_MPLS_VPN) @@ -7273,6 +7283,8 @@ bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi, { if (safi == SAFI_UNICAST) vty_out (vty, "ipv6 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_out (vty, "ipv6 labeled-unicast"); else if (safi == SAFI_MULTICAST) vty_out (vty, "ipv6 multicast"); else if (safi == SAFI_MPLS_VPN) @@ -7579,6 +7591,9 @@ bgp_config_write (struct vty *vty) /* IPv4 multicast configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MULTICAST); + /* IPv4 labeled-unicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); + /* IPv4 VPN configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MPLS_VPN); @@ -7591,6 +7606,9 @@ bgp_config_write (struct vty *vty) /* IPv6 multicast configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MULTICAST); + /* IPv6 labeled-unicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_LABELED_UNICAST); + /* IPv6 VPN configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MPLS_VPN); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a72974bc1..1b97a2503 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -76,6 +76,8 @@ enum bgp_af_index BGP_AF_IPV4_ENCAP, BGP_AF_IPV6_ENCAP, BGP_AF_L2VPN_EVPN, + BGP_AF_IPV4_LBL_UNICAST, + BGP_AF_IPV6_LBL_UNICAST, BGP_AF_MAX }; @@ -971,6 +973,7 @@ struct bgp_nlri #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_ENCAP 23 #define BGP_ATTR_LARGE_COMMUNITIES 32 +#define BGP_ATTR_LABEL_INDEX 40 #if ENABLE_BGP_VNC #define BGP_ATTR_VNC 255 #endif @@ -1394,6 +1397,9 @@ afindex (afi_t afi, safi_t safi) case SAFI_MULTICAST: return BGP_AF_IPV4_MULTICAST; break; + case SAFI_LABELED_UNICAST: + return BGP_AF_IPV4_LBL_UNICAST; + break; case SAFI_MPLS_VPN: return BGP_AF_IPV4_VPN; break; @@ -1414,7 +1420,10 @@ afindex (afi_t afi, safi_t safi) case SAFI_MULTICAST: return BGP_AF_IPV6_MULTICAST; break; - case SAFI_MPLS_VPN: + case SAFI_LABELED_UNICAST: + return BGP_AF_IPV6_LBL_UNICAST; + break; + case SAFI_MPLS_VPN: return BGP_AF_IPV6_VPN; break; case SAFI_ENCAP: @@ -1456,6 +1465,7 @@ peer_afi_active_nego (const struct peer *peer, afi_t afi) { if (peer->afc_nego[afi][SAFI_UNICAST] || peer->afc_nego[afi][SAFI_MULTICAST] + || peer->afc_nego[afi][SAFI_LABELED_UNICAST] || peer->afc_nego[afi][SAFI_MPLS_VPN] || peer->afc_nego[afi][SAFI_ENCAP]) return 1; @@ -1470,10 +1480,12 @@ peer_group_af_configured (struct peer_group *group) if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] + || peer->afc[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_UNICAST] || peer->afc[AFI_IP6][SAFI_MULTICAST] + || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP6][SAFI_MPLS_VPN] || peer->afc[AFI_IP6][SAFI_ENCAP]) return 1; diff --git a/configure.ac b/configure.ac index 4f0dcdf85..9b8b4e109 100755 --- a/configure.ac +++ b/configure.ac @@ -369,11 +369,11 @@ dnl ---------- AC_MSG_CHECKING(whether this OS has MPLS stack) case "$host" in *-linux*) - MPLS_METHOD="zebra_mpls_netlink.o" + MPLS_METHOD="zebra_mpls_netlink.o zebra_mpls.o" AC_MSG_RESULT(Linux MPLS) ;; *-openbsd*) - MPLS_METHOD="zebra_mpls_openbsd.o" + MPLS_METHOD="zebra_mpls_openbsd.o zebra_mpls.o" AC_MSG_RESULT(OpenBSD MPLS) ;; *) diff --git a/debian/frr.dirs b/debian/frr.dirs index 58290080d..56699b2da 100644 --- a/debian/frr.dirs +++ b/debian/frr.dirs @@ -1,5 +1,6 @@ etc/logrotate.d/ etc/frr/ +etc/iproute2/rt_protos.d/ usr/share/doc/frr/ usr/share/doc/frr/examples/ usr/share/lintian/overrides/ diff --git a/debian/frr.install b/debian/frr.install index 45b3b973b..e81ebbc5b 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -18,5 +18,6 @@ usr/share/man/man8/isisd.8 usr/share/man/man8/watchfrr.8 usr/share/snmp/mibs/ cumulus/etc/* etc/ +tools/etc/* etc/ tools/*.service lib/systemd/system debian/frr.conf usr/lib/tmpfiles.d diff --git a/doc/Building_FRR_on_Ubuntu1604.md b/doc/Building_FRR_on_Ubuntu1604.md index 70a8159cc..327b7d68a 100644 --- a/doc/Building_FRR_on_Ubuntu1604.md +++ b/doc/Building_FRR_on_Ubuntu1604.md @@ -38,6 +38,7 @@ an example.) cd frr ./bootstrap.sh ./configure \ + --prefix=/usr \ --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr \ @@ -110,3 +111,38 @@ Add the following lines to `/etc/modules-load.d/modules.conf`: mpls-iptunnel **Reboot** or use `sysctl -p` to apply the same config to the running system + + +### Install the systemd service + + sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service + sudo install -m 644 cumulus/etc/default/frr /etc/default/frr + sudo install -m 644 cumulus/etc/frr/daemons /etc/frr/daemons + sudo install -m 644 cumulus/etc/frr/debian.conf /etc/frr/debian.conf + sudo install -m 644 cumulus/etc/frr/Frr.conf /etc/frr/Frr.conf + sudo install -m 644 -o frr -g frr cumulus/etc/frr/vtysh.conf /etc/frr/vtysh.conf + +### Enable daemons + +Edit `/etc/frr/daemons` and change the value from "no" to "yes" for those daemons you want to start by systemd. +For example. + + zebra=yes + bgpd=yes + ospfd=yes + ospf6d=yes + ripd=yes + ripngd=yes + isisd=yes + +### Enable the systemd serivce +Edit `/etc/systemd/system/frr.service` and remove the line **OnFailure=heartbeat-failed@%n.service** +For example. + + [Unit] + Description=Cumulus Linux FRR + After=syslog.target networking.service + Â Â +### Start the systemd service +- systemctl start frr +- use `systemctl status frr` to check its status. diff --git a/isisd/Makefile.am b/isisd/Makefile.am index c97385f87..2973820ee 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -16,7 +16,7 @@ libisis_a_SOURCES = \ isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \ isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \ isis_spf.c isis_redist.c isis_route.c isis_routemap.c isis_te.c \ - isis_vty.c + isis_vty.c isis_mt.c noinst_HEADERS = \ @@ -25,7 +25,7 @@ noinst_HEADERS = \ isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \ isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \ iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_redist.h \ - isis_route.h isis_routemap.h isis_te.h + isis_route.h isis_routemap.h isis_te.h isis_mt.h isisd_SOURCES = \ isis_main.c $(libisis_a_SOURCES) \ diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index f55092487..fea99ec90 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -47,6 +47,7 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_spf.h" #include "isisd/isis_events.h" +#include "isisd/isis_mt.h" extern struct isis *isis; @@ -148,6 +149,8 @@ isis_delete_adj (void *arg) if (adj->ipv6_addrs) list_delete (adj->ipv6_addrs); + adj_mt_finish(adj); + XFREE (MTYPE_ISIS_ADJACENCY, adj); return; } @@ -521,3 +524,20 @@ isis_adj_build_up_list (struct list *adjdb, struct list *list) return; } + +int +isis_adj_usage2levels(enum isis_adj_usage usage) +{ + switch (usage) + { + case ISIS_ADJ_LEVEL1: + return IS_LEVEL_1; + case ISIS_ADJ_LEVEL2: + return IS_LEVEL_2; + case ISIS_ADJ_LEVEL1AND2: + return IS_LEVEL_1 | IS_LEVEL_2; + default: + break; + } + return 0; +} diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 8539b03d6..4f89e3096 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -97,6 +97,8 @@ struct isis_adjacency int flaps; /* number of adjacency flaps */ struct thread *t_expire; /* expire after hold_time */ struct isis_circuit *circuit; /* back pointer */ + uint16_t *mt_set; /* Topologies this adjacency is valid for */ + unsigned int mt_count; /* Number of entries in mt_set */ }; struct isis_adjacency *isis_adj_lookup (const u_char * sysid, struct list *adjdb); @@ -112,5 +114,6 @@ int isis_adj_expire (struct thread *thread); void isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail); void isis_adj_build_neigh_list (struct list *adjdb, struct list *list); void isis_adj_build_up_list (struct list *adjdb, struct list *list); +int isis_adj_usage2levels(enum isis_adj_usage usage); #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 3a5eaf558..0a1610b6f 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -212,16 +212,11 @@ isis_sock_init (struct isis_circuit *circuit) goto end; } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (if_is_broadcast(circuit->interface)) { circuit->tx = isis_send_pdu_bcast; circuit->rx = isis_recv_pdu_bcast; } - else if (circuit->circ_type == CIRCUIT_T_P2P) - { - circuit->tx = isis_send_pdu_p2p; - circuit->rx = isis_recv_pdu_p2p; - } else { zlog_warn ("isis_sock_init(): unknown circuit type"); @@ -284,23 +279,6 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) } int -isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) -{ - int bytesread; - - bytesread = stream_read (circuit->rcv_stream, circuit->fd, - circuit->interface->mtu); - - if (bytesread < 0) - { - zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno)); - return ISIS_WARNING; - } - - return ISIS_OK; -} - -int isis_send_pdu_bcast (struct isis_circuit *circuit, int level) { struct ether_header *eth; @@ -327,7 +305,8 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) else memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN); memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN); - eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN; + eth->ether_type = htons(isis_ethertype(frame_size)); /* * Then the LLC @@ -354,10 +333,4 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) return ISIS_OK; } -int -isis_send_pdu_p2p (struct isis_circuit *circuit, int level) -{ - return ISIS_OK; -} - #endif /* ISIS_METHOD == ISIS_METHOD_BPF */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 6207ae189..6caf8200e 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -60,6 +60,7 @@ #include "isisd/isis_csm.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" DEFINE_QOBJ_TYPE(isis_circuit) @@ -102,6 +103,8 @@ isis_circuit_new () circuit->mtc = mpls_te_circuit_new(); + circuit_mt_init(circuit); + QOBJ_REG (circuit, isis_circuit); return circuit; @@ -117,6 +120,8 @@ isis_circuit_del (struct isis_circuit *circuit) isis_circuit_if_unbind (circuit, circuit->interface); + circuit_mt_finish(circuit); + /* and lastly the circuit itself */ XFREE (MTYPE_ISIS_CIRCUIT, circuit); @@ -1215,6 +1220,7 @@ isis_interface_config_write (struct vty *vty) VTY_NEWLINE); write++; } + write += circuit_write_mt_settings(circuit, vty); } vty_out (vty, "!%s", VTY_NEWLINE); } @@ -1383,6 +1389,22 @@ isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) } int +isis_circuit_mt_enabled_set (struct isis_circuit *circuit, uint16_t mtid, + bool enabled) +{ + struct isis_circuit_mt_setting *setting; + + setting = circuit_get_mt_setting(circuit, mtid); + if (setting->enabled != enabled) + { + setting->enabled = enabled; + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } + + return CMD_SUCCESS; +} + +int isis_if_new_hook (struct interface *ifp) { return 0; diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index bb0dc0f98..16dfa6304 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -123,6 +123,7 @@ struct isis_circuit struct mpls_te_circuit *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ + struct list *mt_settings; /* IS-IS MT Settings */ struct list *ip_addrs; /* our IP addresses */ int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ @@ -187,4 +188,6 @@ int isis_circuit_passwd_unset (struct isis_circuit *circuit); int isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd); int isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd); +int isis_circuit_mt_enabled_set (struct isis_circuit *circuit, uint16_t mtid, bool enabled); + #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h index 17616d671..ec0f6fb62 100644 --- a/isisd/isis_constants.h +++ b/isisd/isis_constants.h @@ -171,4 +171,14 @@ #define ETH_ALEN 6 #endif +#define MAX_LLC_LEN 0x5ff +#define ETHERTYPE_EXT_LLC 0x8870 + +static inline uint16_t isis_ethertype(size_t len) +{ + if (len > MAX_LLC_LEN) + return ETHERTYPE_EXT_LLC; + return len; +} + #endif /* ISIS_CONSTANTS_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index f633a8fb7..955a73ef6 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -53,6 +53,7 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" /* staticly assigned vars for printing purposes */ char lsp_bits_string[200]; /* FIXME: enough ? */ @@ -501,6 +502,7 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream, expected |= TLVFLAG_TE_IPV4_REACHABILITY; expected |= TLVFLAG_TE_ROUTER_ID; } + expected |= TLVFLAG_MT_ROUTER_INFORMATION; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV4_INT_REACHABILITY; expected |= TLVFLAG_IPV4_EXT_REACHABILITY; @@ -826,6 +828,107 @@ lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost) lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE); } +static void +lsp_print_mt_reach(struct list *list, struct vty *vty, + char dynhost, uint16_t mtid) +{ + struct listnode *node; + struct te_is_neigh *neigh; + + for (ALL_LIST_ELEMENTS_RO (list, node, neigh)) + { + u_char lspid[255]; + + lspid_print(neigh->neigh_id, lspid, dynhost, 0); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out(vty, " Metric : %-8d IS-Extended : %s%s", + GET_TE_METRIC(neigh), lspid, VTY_NEWLINE); + } + else + { + vty_out(vty, " Metric : %-8d MT-Reach : %s %s%s", + GET_TE_METRIC(neigh), lspid, + isis_mtid2str(mtid), VTY_NEWLINE); + } + if (IS_MPLS_TE(isisMplsTE)) + mpls_te_print_detail(vty, neigh); + } +} + +static void +lsp_print_mt_ipv6_reach(struct list *list, struct vty *vty, uint16_t mtid) +{ + struct listnode *node; + struct ipv6_reachability *ipv6_reach; + struct in6_addr in6; + u_char buff[BUFSIZ]; + + for (ALL_LIST_ELEMENTS_RO (list, node, ipv6_reach)) + { + memset (&in6, 0, sizeof (in6)); + memcpy (in6.s6_addr, ipv6_reach->prefix, + PSIZE (ipv6_reach->prefix_len)); + inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if ((ipv6_reach->control_info & + CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) + vty_out (vty, " Metric : %-8d IPv6-Internal : %s/%d%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, VTY_NEWLINE); + else + vty_out (vty, " Metric : %-8d IPv6-External : %s/%d%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, VTY_NEWLINE); + } + else + { + if ((ipv6_reach->control_info & + CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) + vty_out (vty, " Metric : %-8d IPv6-MT-Int : %s/%d %s%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, + isis_mtid2str(mtid), VTY_NEWLINE); + else + vty_out (vty, " Metric : %-8d IPv6-MT-Ext : %s/%d %s%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, + isis_mtid2str(mtid), VTY_NEWLINE); + } + } +} + +static void +lsp_print_mt_ipv4_reach(struct list *list, struct vty *vty, uint16_t mtid) +{ + struct listnode *node; + struct te_ipv4_reachability *te_ipv4_reach; + + for (ALL_LIST_ELEMENTS_RO (list, node, te_ipv4_reach)) + { + if (mtid == ISIS_MT_IPV4_UNICAST) + { + /* FIXME: There should be better way to output this stuff. */ + vty_out (vty, " Metric : %-8d IPv4-Extended : %s/%d%s", + ntohl (te_ipv4_reach->te_metric), + inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, + te_ipv4_reach->control)), + te_ipv4_reach->control & 0x3F, VTY_NEWLINE); + } + else + { + /* FIXME: There should be better way to output this stuff. */ + vty_out (vty, " Metric : %-8d IPv4-MT : %s/%d %s%s", + ntohl (te_ipv4_reach->te_metric), + inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, + te_ipv4_reach->control)), + te_ipv4_reach->control & 0x3F, + isis_mtid2str(mtid), VTY_NEWLINE); + } + } +} + void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) { @@ -833,13 +936,12 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) int i; struct listnode *lnode; struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; struct ipv4_reachability *ipv4_reach; struct in_addr *ipv4_addr; - struct te_ipv4_reachability *te_ipv4_reach; - struct ipv6_reachability *ipv6_reach; - struct in6_addr in6; - u_char buff[BUFSIZ]; + struct mt_router_info *mt_router_info; + struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; + struct tlv_mt_neighbors *mt_is_neigh; + struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; u_char LSPid[255]; u_char hostname[255]; u_char ipv4_reach_prefix[20]; @@ -877,6 +979,14 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) } } + for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.mt_router_info, lnode, mt_router_info)) + { + vty_out (vty, " MT : %s%s%s", + isis_mtid2str(mt_router_info->mtid), + mt_router_info->overload ? " (overload)" : "", + VTY_NEWLINE); + } + /* for the hostname tlv */ if (lsp->tlv_data.hostname) { @@ -946,49 +1056,31 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) ipv4_reach->metrics.metric_default, ipv4_reach_prefix, ipv4_reach_mask, VTY_NEWLINE); } - + /* IPv6 tlv */ - if (lsp->tlv_data.ipv6_reachs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, lnode, ipv6_reach)) - { - memset (&in6, 0, sizeof (in6)); - memcpy (in6.s6_addr, ipv6_reach->prefix, - PSIZE (ipv6_reach->prefix_len)); - inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); - if ((ipv6_reach->control_info & - CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) - vty_out (vty, " Metric : %-8d IPv6-Internal : %s/%d%s", - ntohl (ipv6_reach->metric), - buff, ipv6_reach->prefix_len, VTY_NEWLINE); - else - vty_out (vty, " Metric : %-8d IPv6-External : %s/%d%s", - ntohl (ipv6_reach->metric), - buff, ipv6_reach->prefix_len, VTY_NEWLINE); - } + lsp_print_mt_ipv6_reach(lsp->tlv_data.ipv6_reachs, vty, + ISIS_MT_IPV4_UNICAST); + + /* MT IPv6 reachability tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv6_reachs, lnode, mt_ipv6_reachs)) + lsp_print_mt_ipv6_reach(mt_ipv6_reachs->list, vty, mt_ipv6_reachs->mtid); /* TE IS neighbor tlv */ - if (lsp->tlv_data.te_is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh)) - { - lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0); - vty_out (vty, " Metric : %-8d IS-Extended : %s%s", - GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE); - if (IS_MPLS_TE(isisMplsTE)) - mpls_te_print_detail(vty, te_is_neigh); - } + lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty, + dynhost, ISIS_MT_IPV4_UNICAST); + + /* MT IS neighbor tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_is_neighs, lnode, mt_is_neigh)) + lsp_print_mt_reach(mt_is_neigh->list, vty, dynhost, mt_is_neigh->mtid); /* TE IPv4 tlv */ - if (lsp->tlv_data.te_ipv4_reachs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, lnode, - te_ipv4_reach)) - { - /* FIXME: There should be better way to output this stuff. */ - vty_out (vty, " Metric : %-8d IPv4-Extended : %s/%d%s", - ntohl (te_ipv4_reach->te_metric), - inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, - te_ipv4_reach->control)), - te_ipv4_reach->control & 0x3F, VTY_NEWLINE); - } + lsp_print_mt_ipv4_reach(lsp->tlv_data.te_ipv4_reachs, vty, + ISIS_MT_IPV4_UNICAST); + + /* MT IPv4 reachability tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv4_reachs, lnode, mt_ipv4_reachs)) + lsp_print_mt_ipv4_reach(mt_ipv4_reachs->list, vty, mt_ipv4_reachs->mtid); + vty_out (vty, "%s", VTY_NEWLINE); return; @@ -1028,6 +1120,42 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) return lsp_count; } +static void +_lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, + int frag_thold, + unsigned int tlv_build_func (struct list *, struct stream *, + void *arg), + void *arg) +{ + while (*from && listcount(*from)) + { + unsigned int count; + + count = tlv_build_func(*from, lsp->pdu, arg); + + if (listcount(*to) != 0 || count != listcount(*from)) + { + struct listnode *node, *nnode; + void *elem; + + for (ALL_LIST_ELEMENTS(*from, node, nnode, elem)) + { + if (!count) + break; + listnode_add (*to, elem); + list_delete_node (*from, node); + --count; + } + } + else + { + list_free (*to); + *to = *from; + *from = NULL; + } + } +} + #define FRAG_THOLD(S,T) \ ((STREAM_SIZE(S)*T)/100) @@ -1085,64 +1213,6 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, return; } -/* Process IS_NEIGHBOURS TLV with TE subTLVs */ -void -lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, int frag_thold) -{ - int count, size = 0; - struct listnode *node, *nextnode; - struct te_is_neigh *elem; - - /* Start computing real size of TLVs */ - for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) - size = size + elem->sub_tlvs_length + IS_NEIGHBOURS_LEN; - - /* can we fit all ? */ - if (!FRAG_NEEDED (lsp->pdu, frag_thold, size)) - { - tlv_add_te_is_neighs (*from, lsp->pdu); - if (listcount (*to) != 0) - { - for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) - { - listnode_add (*to, elem); - list_delete_node (*from, node); - } - } - else - { - list_free (*to); - *to = *from; - *from = NULL; - } - } - else - { - /* fit all we can */ - /* Compute remaining place in LSP PDU */ - count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - - (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); - /* Determine size of TE SubTLVs */ - elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); - count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; - if (count > 0) - { - while (count > 0) - { - listnode_add (*to, listgetdata ((struct listnode *)listhead (*from))); - listnode_delete (*from, listgetdata ((struct listnode *)listhead (*from))); - - elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); - count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; - } - - tlv_add_te_is_neighs (*to, lsp->pdu); - } - } - lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); - return; -} - static u_int16_t lsp_rem_lifetime (struct isis_area *area, int level) { @@ -1278,6 +1348,24 @@ lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area, } } +static struct list * +tlv_get_ipv6_reach_list(struct isis_area *area, struct tlvs *tlv_data) +{ + uint16_t mtid = isis_area_ipv6_topology(area); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlv_data->ipv6_reachs) + { + tlv_data->ipv6_reachs = list_new(); + tlv_data->ipv6_reachs->del = free_tlv; + } + return tlv_data->ipv6_reachs; + } + + struct tlv_mt_ipv6_reachs *reachs = tlvs_get_mt_ipv6_reachs(tlv_data, mtid); + return reachs->list; +} + static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, struct tlvs *tlv_data) @@ -1287,6 +1375,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, struct prefix_ipv6 *ipv6; struct isis_ext_info *info; struct ipv6_reachability *ip6reach; + struct list *reach_list = NULL; er_table = get_ext_reach(area, AF_INET6, lsp->level); if (!er_table) @@ -1300,11 +1389,9 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, ipv6 = (struct prefix_ipv6*)&rn->p; info = rn->info; - if (tlv_data->ipv6_reachs == NULL) - { - tlv_data->ipv6_reachs = list_new(); - tlv_data->ipv6_reachs->del = free_tlv; - } + if (!reach_list) + reach_list = tlv_get_ipv6_reach_list(area, tlv_data); + ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach)); if (info->metric > MAX_WIDE_PATH_METRIC) ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC); @@ -1313,7 +1400,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, ip6reach->control_info = DISTRIBUTION_EXTERNAL; ip6reach->prefix_len = ipv6->prefixlen; memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix)); - listnode_add(tlv_data->ipv6_reachs, ip6reach); + listnode_add(reach_list, ip6reach); } } @@ -1342,6 +1429,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) struct te_ipv4_reachability *te_ipreach; struct isis_adjacency *nei; struct prefix_ipv6 *ipv6, ip6prefix; + struct list *ipv6_reachs = NULL; struct ipv6_reachability *ip6reach; struct tlvs tlv_data; struct isis_lsp *lsp0 = lsp; @@ -1402,6 +1490,32 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); } + if (area_is_mt(area)) + { + lsp_debug("ISIS (%s): Adding MT router tlv...", area->area_tag); + lsp->tlv_data.mt_router_info = list_new(); + lsp->tlv_data.mt_router_info->del = free_tlv; + + struct isis_area_mt_setting **mt_settings; + unsigned int mt_count; + + mt_settings = area_mt_settings(area, &mt_count); + for (unsigned int i = 0; i < mt_count; i++) + { + struct mt_router_info *info; + + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_settings[i]->mtid; + info->overload = mt_settings[i]->overload; + listnode_add(lsp->tlv_data.mt_router_info, info); + lsp_debug("ISIS (%s): MT %s", area->area_tag, isis_mtid2str(info->mtid)); + } + tlv_add_mt_router_info (lsp->tlv_data.mt_router_info, lsp->pdu); + } + else + { + lsp_debug("ISIS (%s): Not adding MT router tlv (disabled)", area->area_tag); + } /* Dynamic Hostname */ if (area->dynhostname) { @@ -1551,12 +1665,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) if (circuit->ipv6_router && circuit->ipv6_non_link && circuit->ipv6_non_link->count > 0) { + if (!ipv6_reachs) + ipv6_reachs = tlv_get_ipv6_reach_list(area, &tlv_data); - if (tlv_data.ipv6_reachs == NULL) - { - tlv_data.ipv6_reachs = list_new (); - tlv_data.ipv6_reachs->del = free_tlv; - } for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) { ip6reach = @@ -1579,7 +1690,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr, sizeof (ip6reach->prefix)); - listnode_add (tlv_data.ipv6_reachs, ip6reach); + listnode_add (ipv6_reachs, ip6reach); } } @@ -1658,10 +1769,8 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ te_is_neigh->sub_tlvs_length = 0; - listnode_add (tlv_data.te_is_neighs, te_is_neigh); - lsp_debug("ISIS (%s): Adding DIS %s.%02x as te-style neighbor", - area->area_tag, sysid_print(te_is_neigh->neigh_id), - LSP_PSEUDO_ID(te_is_neigh->neigh_id)); + tlvs_add_mt_bcast(&tlv_data, circuit, level, te_is_neigh); + XFREE(MTYPE_ISIS_TLV, te_is_neigh); } } } @@ -1718,9 +1827,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) else /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ te_is_neigh->sub_tlvs_length = 0; - listnode_add (tlv_data.te_is_neighs, te_is_neigh); - lsp_debug("ISIS (%s): Adding te-style is reach for %s", area->area_tag, - sysid_print(te_is_neigh->neigh_id)); + + tlvs_add_mt_p2p(&tlv_data, circuit, te_is_neigh); + XFREE(MTYPE_ISIS_TLV, te_is_neigh); } } else @@ -1766,35 +1875,62 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) lsp0, area, level); } - /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit() - * for now. lsp_tlv_fit() needs to be fixed to deal with variable length - * TLVs (sub TLVs!). */ while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) { if (lsp->tlv_data.te_ipv4_reachs == NULL) lsp->tlv_data.te_ipv4_reachs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, - &lsp->tlv_data.te_ipv4_reachs, - TE_IPV4_REACH_LEN, area->lsp_frag_threshold, - tlv_add_te_ipv4_reachs); + _lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs, + area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, NULL); if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv4_reachs, node, mt_ipv4_reachs)) + { + while (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) + { + struct tlv_mt_ipv4_reachs *frag_mt_ipv4_reachs; + + frag_mt_ipv4_reachs = tlvs_get_mt_ipv4_reachs(&lsp->tlv_data, mt_ipv4_reachs->mtid); + _lsp_tlv_fit (lsp, &mt_ipv4_reachs->list, &frag_mt_ipv4_reachs->list, + area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, + &mt_ipv4_reachs->mtid); + if (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) { if (lsp->tlv_data.ipv6_reachs == NULL) lsp->tlv_data.ipv6_reachs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, - &lsp->tlv_data.ipv6_reachs, - IPV6_REACH_LEN, area->lsp_frag_threshold, - tlv_add_ipv6_reachs); + _lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, &lsp->tlv_data.ipv6_reachs, + area->lsp_frag_threshold, tlv_add_ipv6_reachs, NULL); if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv6_reachs, node, mt_ipv6_reachs)) + { + while (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) + { + struct tlv_mt_ipv6_reachs *frag_mt_ipv6_reachs; + + frag_mt_ipv6_reachs = tlvs_get_mt_ipv6_reachs(&lsp->tlv_data, mt_ipv6_reachs->mtid); + _lsp_tlv_fit (lsp, &mt_ipv6_reachs->list, &frag_mt_ipv6_reachs->list, + area->lsp_frag_threshold, tlv_add_ipv6_reachs, + &mt_ipv6_reachs->mtid); + if (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) { if (lsp->tlv_data.is_neighs == NULL) @@ -1812,13 +1948,31 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) { if (lsp->tlv_data.te_is_neighs == NULL) lsp->tlv_data.te_is_neighs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, - IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, - tlv_add_te_is_neighs); + _lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, + area->lsp_frag_threshold, tlv_add_te_is_neighs, NULL); if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + + struct tlv_mt_neighbors *mt_neighs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_is_neighs, node, mt_neighs)) + { + while (mt_neighs->list && listcount(mt_neighs->list)) + { + struct tlv_mt_neighbors *frag_mt_neighs; + + frag_mt_neighs = tlvs_get_mt_neighbors(&lsp->tlv_data, mt_neighs->mtid); + _lsp_tlv_fit (lsp, &mt_neighs->list, &frag_mt_neighs->list, + area->lsp_frag_threshold, tlv_add_te_is_neighs, + &mt_neighs->mtid); + if (mt_neighs->list && listcount(mt_neighs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); free_tlvs (&tlv_data); @@ -2255,7 +2409,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu); if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0) - tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu); + tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu, NULL); if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0) tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 24fae57a7..6f697df62 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -108,8 +108,6 @@ void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost); int lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost); const char *lsp_bits2string (u_char *); -void lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, - struct list **to, int frag_thold); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags (struct isis_lsp *lsp); diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c new file mode 100644 index 000000000..552365ad1 --- /dev/null +++ b/isisd/isis_mt.c @@ -0,0 +1,724 @@ +/* + * IS-IS Rout(e)ing protocol - Multi Topology Support + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include <zebra.h> +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_mt.h" + +DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting") +DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting") +DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info") +DEFINE_MTYPE_STATIC(ISISD, MT_NEIGHBORS, "ISIS MT Neighbors for TLV") +DEFINE_MTYPE_STATIC(ISISD, MT_IPV4_REACHS, "ISIS MT IPv4 Reachabilities for TLV") +DEFINE_MTYPE_STATIC(ISISD, MT_IPV6_REACHS, "ISIS MT IPv6 Reachabilities for TLV") + +uint16_t isis_area_ipv6_topology(struct isis_area *area) +{ + struct isis_area_mt_setting *area_mt_setting; + area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST); + + if (area_mt_setting && area_mt_setting->enabled) + return ISIS_MT_IPV6_UNICAST; + return ISIS_MT_IPV4_UNICAST; +} + +/* MT naming api */ +const char *isis_mtid2str(uint16_t mtid) +{ + static char buf[sizeof("65535")]; + + switch(mtid) + { + case ISIS_MT_IPV4_UNICAST: + return "ipv4-unicast"; + case ISIS_MT_IPV4_MGMT: + return "ipv4-mgmt"; + case ISIS_MT_IPV6_UNICAST: + return "ipv6-unicast"; + case ISIS_MT_IPV4_MULTICAST: + return "ipv4-multicast"; + case ISIS_MT_IPV6_MULTICAST: + return "ipv6-multicast"; + case ISIS_MT_IPV6_MGMT: + return "ipv6-mgmt"; + default: + snprintf(buf, sizeof(buf), "%" PRIu16, mtid); + return buf; + } +} + +uint16_t isis_str2mtid(const char *name) +{ + if (!strcmp(name,"ipv4-unicast")) + return ISIS_MT_IPV4_UNICAST; + if (!strcmp(name,"ipv4-mgmt")) + return ISIS_MT_IPV4_MGMT; + if (!strcmp(name,"ipv6-unicast")) + return ISIS_MT_IPV6_UNICAST; + if (!strcmp(name,"ipv4-multicast")) + return ISIS_MT_IPV4_MULTICAST; + if (!strcmp(name,"ipv6-multicast")) + return ISIS_MT_IPV6_MULTICAST; + if (!strcmp(name,"ipv6-mgmt")) + return ISIS_MT_IPV6_MGMT; + return -1; +} + +/* General MT settings api */ + +struct mt_setting { + ISIS_MT_INFO_FIELDS; +}; + +static void * +lookup_mt_setting(struct list *mt_list, uint16_t mtid) +{ + struct listnode *node; + struct mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) + { + if (setting->mtid == mtid) + return setting; + } + return NULL; +} + +static void +add_mt_setting(struct list **mt_list, void *setting) +{ + if (!*mt_list) + *mt_list = list_new(); + listnode_add(*mt_list, setting); +} + +/* Area specific MT settings api */ + +struct isis_area_mt_setting* +area_lookup_mt_setting(struct isis_area *area, uint16_t mtid) +{ + return lookup_mt_setting(area->mt_settings, mtid); +} + +struct isis_area_mt_setting* +area_new_mt_setting(struct isis_area *area, uint16_t mtid) +{ + struct isis_area_mt_setting *setting; + + setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting)); + setting->mtid = mtid; + return setting; +} + +static void +area_free_mt_setting(void *setting) +{ + XFREE(MTYPE_MT_AREA_SETTING, setting); +} + +void +area_add_mt_setting(struct isis_area *area, struct isis_area_mt_setting *setting) +{ + add_mt_setting(&area->mt_settings, setting); +} + +void +area_mt_init(struct isis_area *area) +{ + struct isis_area_mt_setting *v4_unicast_setting; + + /* MTID 0 is always enabled */ + v4_unicast_setting = area_new_mt_setting(area, ISIS_MT_IPV4_UNICAST); + v4_unicast_setting->enabled = true; + add_mt_setting(&area->mt_settings, v4_unicast_setting); + area->mt_settings->del = area_free_mt_setting; +} + +void +area_mt_finish(struct isis_area *area) +{ + list_delete(area->mt_settings); + area->mt_settings = NULL; +} + +struct isis_area_mt_setting * +area_get_mt_setting(struct isis_area *area, uint16_t mtid) +{ + struct isis_area_mt_setting *setting; + + setting = area_lookup_mt_setting(area, mtid); + if (!setting) + { + setting = area_new_mt_setting(area, mtid); + area_add_mt_setting(area, setting); + } + return setting; +} + +int +area_write_mt_settings(struct isis_area *area, struct vty *vty) +{ + int written = 0; + struct listnode *node; + struct isis_area_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + const char *name = isis_mtid2str(setting->mtid); + if (name && setting->enabled) + { + if (setting->mtid == ISIS_MT_IPV4_UNICAST) + continue; /* always enabled, no need to write out config */ + vty_out (vty, " topology %s%s%s", name, + setting->overload ? " overload" : "", + VTY_NEWLINE); + written++; + } + } + return written; +} + +bool area_is_mt(struct isis_area *area) +{ + struct listnode *node, *node2; + struct isis_area_mt_setting *setting; + struct isis_circuit *circuit; + struct isis_circuit_mt_setting *csetting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + if (setting->enabled && setting->mtid != ISIS_MT_IPV4_UNICAST) + return true; + } + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + { + for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node2, csetting)) + { + if (!csetting->enabled && csetting->mtid == ISIS_MT_IPV4_UNICAST) + return true; + } + } + + return false; +} + +struct isis_area_mt_setting** +area_mt_settings(struct isis_area *area, unsigned int *mt_count) +{ + static unsigned int size = 0; + static struct isis_area_mt_setting **rv = NULL; + + unsigned int count = 0; + struct listnode *node; + struct isis_area_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + if (!setting->enabled) + continue; + + count++; + if (count > size) + { + rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv)); + size = count; + } + rv[count-1] = setting; + } + + *mt_count = count; + return rv; +} + +/* Circuit specific MT settings api */ + +struct isis_circuit_mt_setting* +circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + return lookup_mt_setting(circuit->mt_settings, mtid); +} + +struct isis_circuit_mt_setting* +circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + struct isis_circuit_mt_setting *setting; + + setting = XCALLOC(MTYPE_MT_CIRCUIT_SETTING, sizeof(*setting)); + setting->mtid = mtid; + setting->enabled = true; /* Enabled is default for circuit */ + return setting; +} + +static void +circuit_free_mt_setting(void *setting) +{ + XFREE(MTYPE_MT_CIRCUIT_SETTING, setting); +} + +void +circuit_add_mt_setting(struct isis_circuit *circuit, + struct isis_circuit_mt_setting *setting) +{ + add_mt_setting(&circuit->mt_settings, setting); +} + +void +circuit_mt_init(struct isis_circuit *circuit) +{ + circuit->mt_settings = list_new(); + circuit->mt_settings->del = circuit_free_mt_setting; +} + +void +circuit_mt_finish(struct isis_circuit *circuit) +{ + list_delete(circuit->mt_settings); + circuit->mt_settings = NULL; +} + +struct isis_circuit_mt_setting* +circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + struct isis_circuit_mt_setting *setting; + + setting = circuit_lookup_mt_setting(circuit, mtid); + if (!setting) + { + setting = circuit_new_mt_setting(circuit, mtid); + circuit_add_mt_setting(circuit, setting); + } + return setting; +} + +int +circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty) +{ + int written = 0; + struct listnode *node; + struct isis_circuit_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO (circuit->mt_settings, node, setting)) + { + const char *name = isis_mtid2str(setting->mtid); + if (name && !setting->enabled) + { + vty_out (vty, " no isis topology %s%s", name, VTY_NEWLINE); + written++; + } + } + return written; +} + +struct isis_circuit_mt_setting** +circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count) +{ + static unsigned int size = 0; + static struct isis_circuit_mt_setting **rv = NULL; + + struct isis_area_mt_setting **area_settings; + unsigned int area_count; + + unsigned int count = 0; + + struct listnode *node; + struct isis_circuit_mt_setting *setting; + + area_settings = area_mt_settings(circuit->area, &area_count); + + for (unsigned int i = 0; i < area_count; i++) + { + for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) + { + if (setting->mtid != area_settings[i]->mtid) + continue; + break; + } + if (!setting) + setting = circuit_get_mt_setting(circuit, area_settings[i]->mtid); + + if (!setting->enabled) + continue; + + count++; + if (count > size) + { + rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv)); + size = count; + } + rv[count-1] = setting; + } + + *mt_count = count; + return rv; +} + +/* ADJ specific MT API */ +static void adj_mt_set(struct isis_adjacency *adj, unsigned int index, + uint16_t mtid) +{ + if (adj->mt_count < index + 1) + { + adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set, + (index + 1) * sizeof(*adj->mt_set)); + adj->mt_count = index + 1; + } + adj->mt_set[index] = mtid; +} + +bool +tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, + struct isis_adjacency *adj) +{ + struct isis_circuit_mt_setting **mt_settings; + unsigned int circuit_mt_count; + + unsigned int intersect_count = 0; + + uint16_t *old_mt_set = NULL; + unsigned int old_mt_count; + + old_mt_count = adj->mt_count; + if (old_mt_count) + { + old_mt_set = XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set)); + memcpy(old_mt_set, adj->mt_set, old_mt_count * sizeof(*old_mt_set)); + } + + mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count); + for (unsigned int i = 0; i < circuit_mt_count; i++) + { + if (!tlvs->mt_router_info) + { + /* Other end does not have MT enabled */ + if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST) + adj_mt_set(adj, intersect_count++, ISIS_MT_IPV4_UNICAST); + } + else + { + struct listnode *node; + struct mt_router_info *info; + for (ALL_LIST_ELEMENTS_RO(tlvs->mt_router_info, node, info)) + { + if (mt_settings[i]->mtid == info->mtid) + adj_mt_set(adj, intersect_count++, info->mtid); + } + } + } + adj->mt_count = intersect_count; + + bool changed = false; + + if (adj->mt_count != old_mt_count) + changed = true; + + if (!changed && old_mt_count + && memcmp(adj->mt_set, old_mt_set, + old_mt_count * sizeof(*old_mt_set))) + changed = true; + + if (old_mt_count) + XFREE(MTYPE_TMP, old_mt_set); + + return changed; +} + +bool +adj_has_mt(struct isis_adjacency *adj, uint16_t mtid) +{ + for (unsigned int i = 0; i < adj->mt_count; i++) + if (adj->mt_set[i] == mtid) + return true; + return false; +} + +void +adj_mt_finish(struct isis_adjacency *adj) +{ + XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set); + adj->mt_count = 0; +} + +/* TLV Router info api */ +struct mt_router_info* +tlvs_lookup_mt_router_info(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_router_info, mtid); +} + +/* TLV MT Neighbors api */ +struct tlv_mt_neighbors* +tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_is_neighs, mtid); +} + +static struct tlv_mt_neighbors* +tlvs_new_mt_neighbors(uint16_t mtid) +{ + struct tlv_mt_neighbors *rv; + + rv = XCALLOC(MTYPE_MT_NEIGHBORS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_neighbors(void *arg) +{ + struct tlv_mt_neighbors *neighbors = arg; + + if (neighbors && neighbors->list) + list_delete(neighbors->list); + XFREE(MTYPE_MT_NEIGHBORS, neighbors); +} + +static void +tlvs_add_mt_neighbors(struct tlvs *tlvs, struct tlv_mt_neighbors *neighbors) +{ + add_mt_setting(&tlvs->mt_is_neighs, neighbors); + tlvs->mt_is_neighs->del = tlvs_free_mt_neighbors; +} + +struct tlv_mt_neighbors* +tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_lookup_mt_neighbors(tlvs, mtid); + if (!neighbors) + { + neighbors = tlvs_new_mt_neighbors(mtid); + tlvs_add_mt_neighbors(tlvs, neighbors); + } + return neighbors; +} + +/* TLV MT IPv4 reach api */ +struct tlv_mt_ipv4_reachs* +tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_ipv4_reachs, mtid); +} + +static struct tlv_mt_ipv4_reachs* +tlvs_new_mt_ipv4_reachs(uint16_t mtid) +{ + struct tlv_mt_ipv4_reachs *rv; + + rv = XCALLOC(MTYPE_MT_IPV4_REACHS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_ipv4_reachs(void *arg) +{ + struct tlv_mt_ipv4_reachs *reachs = arg; + + if (reachs && reachs->list) + list_delete(reachs->list); + XFREE(MTYPE_MT_IPV4_REACHS, reachs); +} + +static void +tlvs_add_mt_ipv4_reachs(struct tlvs *tlvs, struct tlv_mt_ipv4_reachs *reachs) +{ + add_mt_setting(&tlvs->mt_ipv4_reachs, reachs); + tlvs->mt_ipv4_reachs->del = tlvs_free_mt_ipv4_reachs; +} + +struct tlv_mt_ipv4_reachs* +tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_ipv4_reachs *reachs; + + reachs = tlvs_lookup_mt_ipv4_reachs(tlvs, mtid); + if (!reachs) + { + reachs = tlvs_new_mt_ipv4_reachs(mtid); + tlvs_add_mt_ipv4_reachs(tlvs, reachs); + } + return reachs; +} + +/* TLV MT IPv6 reach api */ +struct tlv_mt_ipv6_reachs* +tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_ipv6_reachs, mtid); +} + +static struct tlv_mt_ipv6_reachs* +tlvs_new_mt_ipv6_reachs(uint16_t mtid) +{ + struct tlv_mt_ipv6_reachs *rv; + + rv = XCALLOC(MTYPE_MT_IPV6_REACHS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_ipv6_reachs(void *arg) +{ + struct tlv_mt_ipv6_reachs *reachs = arg; + + if (reachs && reachs->list) + list_delete(reachs->list); + XFREE(MTYPE_MT_IPV6_REACHS, reachs); +} + +static void +tlvs_add_mt_ipv6_reachs(struct tlvs *tlvs, struct tlv_mt_ipv6_reachs *reachs) +{ + add_mt_setting(&tlvs->mt_ipv6_reachs, reachs); + tlvs->mt_ipv6_reachs->del = tlvs_free_mt_ipv6_reachs; +} + +struct tlv_mt_ipv6_reachs* +tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_ipv6_reachs *reachs; + + reachs = tlvs_lookup_mt_ipv6_reachs(tlvs, mtid); + if (!reachs) + { + reachs = tlvs_new_mt_ipv6_reachs(mtid); + tlvs_add_mt_ipv6_reachs(tlvs, reachs); + } + return reachs; +} + +static void +mt_set_add(uint16_t **mt_set, unsigned int *size, + unsigned int *index, uint16_t mtid) +{ + for (unsigned int i = 0; i < *index; i++) + { + if ((*mt_set)[i] == mtid) + return; + } + + if (*index >= *size) + { + *mt_set = XREALLOC(MTYPE_TMP, *mt_set, sizeof(**mt_set) * ((*index) + 1)); + *size = (*index) + 1; + } + + (*mt_set)[*index] = mtid; + *index = (*index) + 1; +} + +static uint16_t * +circuit_bcast_mt_set(struct isis_circuit *circuit, int level, + unsigned int *mt_count) +{ + static uint16_t *rv; + static unsigned int size; + struct listnode *node; + struct isis_adjacency *adj; + + unsigned int count = 0; + + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + { + *mt_count = 0; + return NULL; + } + + for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) + { + if (adj->adj_state != ISIS_ADJ_UP) + continue; + for (unsigned int i = 0; i < adj->mt_count; i++) + mt_set_add(&rv, &size, &count, adj->mt_set[i]); + } + + *mt_count = count; + return rv; +} + +static void +tlvs_add_mt_set(struct isis_area *area, + struct tlvs *tlvs, unsigned int mt_count, + uint16_t *mt_set, struct te_is_neigh *neigh) +{ + for (unsigned int i = 0; i < mt_count; i++) + { + uint16_t mtid = mt_set[i]; + struct te_is_neigh *ne_copy; + + ne_copy = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ne_copy)); + memcpy(ne_copy, neigh, sizeof(*ne_copy)); + + if (mt_set[i] == ISIS_MT_IPV4_UNICAST) + { + listnode_add(tlvs->te_is_neighs, ne_copy); + lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor", + area->area_tag, sysid_print(ne_copy->neigh_id), + LSP_PSEUDO_ID(ne_copy->neigh_id)); + } + else + { + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_get_mt_neighbors(tlvs, mtid); + neighbors->list->del = free_tlv; + listnode_add(neighbors->list, ne_copy); + lsp_debug("ISIS (%s): Adding %s.%02x as mt-style neighbor for %s", + area->area_tag, sysid_print(ne_copy->neigh_id), + LSP_PSEUDO_ID(ne_copy->neigh_id), isis_mtid2str(mtid)); + } + } +} + +void +tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit, + int level, struct te_is_neigh *neigh) +{ + unsigned int mt_count; + uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, + &mt_count); + + tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, neigh); +} + +void +tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit, + struct te_is_neigh *neigh) +{ + struct isis_adjacency *adj = circuit->u.p2p.neighbor; + + tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, neigh); +} diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h new file mode 100644 index 000000000..d4dc4c6f2 --- /dev/null +++ b/isisd/isis_mt.h @@ -0,0 +1,146 @@ +/* + * IS-IS Rout(e)ing protocol - Multi Topology Support + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef ISIS_MT_H +#define ISIS_MT_H + +#define ISIS_MT_MASK 0x0fff +#define ISIS_MT_OL_MASK 0x8000 + +#define ISIS_MT_IPV4_UNICAST 0 +#define ISIS_MT_IPV4_MGMT 1 +#define ISIS_MT_IPV6_UNICAST 2 +#define ISIS_MT_IPV4_MULTICAST 3 +#define ISIS_MT_IPV6_MULTICAST 4 +#define ISIS_MT_IPV6_MGMT 5 + +#define ISIS_MT_NAMES \ + "<ipv4-unicast" \ + "|ipv4-mgmt" \ + "|ipv6-unicast" \ + "|ipv4-multicast" \ + "|ipv6-multicast" \ + "|ipv6-mgmt" \ + ">" + +#define ISIS_MT_DESCRIPTIONS \ + "IPv4 unicast topology\n" \ + "IPv4 management topology\n" \ + "IPv6 unicast topology\n" \ + "IPv4 multicast topology\n" \ + "IPv6 multicast topology\n" \ + "IPv6 management topology\n" + +#define ISIS_MT_INFO_FIELDS \ + uint16_t mtid; + +struct list; + +struct isis_area_mt_setting { + ISIS_MT_INFO_FIELDS + bool enabled; + bool overload; +}; + +struct isis_circuit_mt_setting { + ISIS_MT_INFO_FIELDS + bool enabled; +}; + +struct tlv_mt_neighbors { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +struct tlv_mt_ipv4_reachs { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +struct tlv_mt_ipv6_reachs { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +const char *isis_mtid2str(uint16_t mtid); +uint16_t isis_str2mtid(const char *name); + +struct isis_adjacency; +struct isis_area; +struct isis_circuit; +struct tlvs; +struct te_is_neigh; + +uint16_t isis_area_ipv6_topology(struct isis_area *area); + +struct mt_router_info* tlvs_lookup_mt_router_info(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_neighbors* tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_neighbors* tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_ipv4_reachs* tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_ipv4_reachs* tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_ipv6_reachs* tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_ipv6_reachs* tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid); + +struct isis_area_mt_setting* area_lookup_mt_setting(struct isis_area *area, + uint16_t mtid); +struct isis_area_mt_setting* area_new_mt_setting(struct isis_area *area, + uint16_t mtid); +void area_add_mt_setting(struct isis_area *area, + struct isis_area_mt_setting *setting); + +void area_mt_init(struct isis_area *area); +void area_mt_finish(struct isis_area *area); +struct isis_area_mt_setting* area_get_mt_setting(struct isis_area *area, + uint16_t mtid); +int area_write_mt_settings(struct isis_area *area, struct vty *vty); +bool area_is_mt(struct isis_area *area); +struct isis_area_mt_setting** area_mt_settings(struct isis_area *area, + unsigned int *mt_count); + +struct isis_circuit_mt_setting* circuit_lookup_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +struct isis_circuit_mt_setting* circuit_new_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +void circuit_add_mt_setting(struct isis_circuit *circuit, + struct isis_circuit_mt_setting *setting); +void circuit_mt_init(struct isis_circuit *circuit); +void circuit_mt_finish(struct isis_circuit *circuit); +struct isis_circuit_mt_setting* circuit_get_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty); +struct isis_circuit_mt_setting** circuit_mt_settings(struct isis_circuit *circuit, + unsigned int *mt_count); +bool tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, + struct isis_adjacency *adj); +bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid); +void adj_mt_finish(struct isis_adjacency *adj); +void tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit, + int level, struct te_is_neigh *neigh); +void tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit, + struct te_is_neigh *neigh); +#endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 5fbf6c194..9e90acf2e 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -53,6 +53,7 @@ #include "isisd/isis_csm.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" #define ISIS_MINIMUM_FIXED_HDR_LEN 15 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ @@ -471,6 +472,7 @@ process_p2p_hello (struct isis_circuit *circuit) expected |= TLVFLAG_NLPID; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + expected |= TLVFLAG_MT_ROUTER_INFORMATION; auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, @@ -633,6 +635,8 @@ process_p2p_hello (struct isis_circuit *circuit) if (found & TLVFLAG_IPV6_ADDR) tlvs_to_adj_ipv6_addrs (&tlvs, adj); + bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj); + /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, @@ -869,6 +873,13 @@ process_p2p_hello (struct isis_circuit *circuit) /* down - area mismatch */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); } + + if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) + { + lsp_regenerate_schedule(adj->circuit->area, + isis_adj_usage2levels(adj->adj_usage), 0); + } + /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ /* FIXME - Missing parts */ @@ -1021,6 +1032,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) expected |= TLVFLAG_NLPID; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + expected |= TLVFLAG_MT_ROUTER_INFORMATION; auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, @@ -1223,6 +1235,8 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) adj->circuit_t = hdr.circuit_t; + bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj); + /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, @@ -1266,6 +1280,9 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) "no LAN Neighbours TLV found"); } + if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) + lsp_regenerate_schedule(adj->circuit->area, level, 0); + out: if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -2337,6 +2354,37 @@ send_hello (struct isis_circuit *circuit, int level) if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream)) return ISIS_WARNING; + /* + * MT Supported TLV + * + * TLV gets included if no topology is enabled on the interface, + * if one topology other than #0 is enabled, or if multiple topologies + * are enabled. + */ + struct isis_circuit_mt_setting **mt_settings; + unsigned int mt_count; + + mt_settings = circuit_mt_settings(circuit, &mt_count); + if ((mt_count == 0 && area_is_mt(circuit->area)) + || (mt_count == 1 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST) + || (mt_count > 1)) + { + struct list *mt_info = list_new(); + mt_info->del = free_tlv; + + for (unsigned int i = 0; i < mt_count; i++) + { + struct mt_router_info *info; + + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_settings[i]->mtid; + /* overload info is not valid in IIH, so it's not included here */ + listnode_add(mt_info, info); + } + tlv_add_mt_router_info (mt_info, circuit->snd_stream); + list_free(mt_info); + } + /* IPv6 Interface Address TLV */ if (circuit->ipv6_router && circuit->ipv6_link && listcount (circuit->ipv6_link) > 0) diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index dd07a9c6f..5c434b90d 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -371,7 +371,9 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); sa.sll_family = AF_PACKET; - sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + + size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN; + sa.sll_protocol = htons(isis_ethertype(frame_size)); sa.sll_ifindex = circuit->interface->ifindex; sa.sll_halen = ETH_ALEN; /* RFC5309 section 4.1 recommends ALL_ISS */ @@ -418,7 +420,6 @@ isis_send_pdu_p2p (struct isis_circuit *circuit, int level) stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); sa.sll_family = AF_PACKET; - sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); sa.sll_ifindex = circuit->interface->ifindex; sa.sll_halen = ETH_ALEN; if (level == 1) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 554fa563a..d85f08f50 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -5,6 +5,7 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2017 Christian Franke <chris@opensourcerouting.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas published by the Free @@ -50,6 +51,14 @@ #include "isis_spf.h" #include "isis_route.h" #include "isis_csm.h" +#include "isis_mt.h" + +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); + +struct isis_spf_run { + struct isis_area *area; + int level; +}; /* 7.2.7 */ static void @@ -142,35 +151,24 @@ vtype2string (enum vertextype vtype) default: return "UNKNOWN"; } - return NULL; /* Not reached */ + return NULL; /* Not reached */ } static const char * vid2string (struct isis_vertex *vertex, char * buff, int size) { - switch (vertex->type) + if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) { - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: return print_sys_hostname (vertex->N.id); - break; - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - case VTYPE_ES: - return print_sys_hostname (vertex->N.id); - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: + } + + if (VTYPE_IP(vertex->type)) + { prefix2str ((struct prefix *) &vertex->N.prefix, buff, size); - break; - default: - return "UNKNOWN"; + return buff; } - return (char *) buff; + return "UNKNOWN"; } static struct isis_vertex * @@ -181,26 +179,17 @@ isis_vertex_new (void *id, enum vertextype vtype) vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex)); vertex->type = vtype; - switch (vtype) + + if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { - case VTYPE_ES: - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN); - break; - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1); - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: - memcpy (&vertex->N.prefix, (struct prefix *) id, - sizeof (struct prefix)); - break; - default: + } + else if (VTYPE_IP(vtype)) + { + memcpy (&vertex->N.prefix, (struct prefix *) id, sizeof (struct prefix)); + } + else + { zlog_err ("WTF!"); } @@ -280,7 +269,7 @@ isis_spftree_del (struct isis_spftree *spftree) return; } -void +static void isis_spftree_adj_del (struct isis_spftree *spftree, struct isis_adjacency *adj) { struct listnode *node; @@ -394,23 +383,24 @@ isis_root_system_lsp (struct isis_area *area, int level, u_char *sysid) * Add this IS to the root of SPT */ static struct isis_vertex * -isis_spf_add_root (struct isis_spftree *spftree, int level, u_char *sysid) +isis_spf_add_root (struct isis_spftree *spftree, u_char *sysid) { struct isis_vertex *vertex; struct isis_lsp *lsp; #ifdef EXTREME_DEBUG char buff[PREFIX2STR_BUFFER]; #endif /* EXTREME_DEBUG */ + u_char id[ISIS_SYS_ID_LEN + 1]; - lsp = isis_root_system_lsp (spftree->area, level, sysid); - if (lsp == NULL) - zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level); + memcpy(id, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(id) = 0; - if (!spftree->area->oldmetric) - vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS); - else - vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS); + lsp = isis_root_system_lsp (spftree->area, spftree->level, sysid); + if (lsp == NULL) + zlog_warn ("ISIS-Spf: could not find own l%d LSP!", spftree->level); + vertex = isis_vertex_new (id, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS); listnode_add (spftree->paths, vertex); #ifdef EXTREME_DEBUG @@ -432,44 +422,51 @@ isis_find_vertex (struct list *list, void *id, enum vertextype vtype) for (ALL_LIST_ELEMENTS_RO (list, node, vertex)) { if (vertex->type != vtype) - continue; - switch (vtype) - { - case VTYPE_ES: - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0) - return vertex; - break; - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: - if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) - return vertex; - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: - p1 = (struct prefix *) id; - p2 = (struct prefix *) &vertex->N.id; - if (p1->family == p2->family && p1->prefixlen == p2->prefixlen && - memcmp (&p1->u.prefix, &p2->u.prefix, - PSIZE (p1->prefixlen)) == 0) - return vertex; - break; - } + continue; + if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) + { + if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) + return vertex; + } + if (VTYPE_IP(vertex->type)) + { + p1 = (struct prefix *) id; + p2 = (struct prefix *) &vertex->N.id; + if (p1->family == p2->family + && p1->prefixlen == p2->prefixlen + && !memcmp(&p1->u.prefix, &p2->u.prefix, PSIZE (p1->prefixlen))) + { + return vertex; + } + } } return NULL; } /* + * Compares vertizes for sorting in the TENT list. Returns true + * if candidate should be considered before current, false otherwise. + */ +static bool +tent_cmp (struct isis_vertex *current, struct isis_vertex *candidate) +{ + if (current->d_N > candidate->d_N) + return true; + + if (current->d_N == candidate->d_N + && current->type > candidate->type) + return true; + + return false; +} + +/* * Add a vertex to TENT sorted by cost and by vertextype on tie break situation */ static struct isis_vertex * isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, - void *id, uint32_t cost, int depth, int family, + void *id, uint32_t cost, int depth, struct isis_adjacency *adj, struct isis_vertex *parent) { struct isis_vertex *vertex, *v; @@ -515,17 +512,11 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, for (node = listhead (spftree->tents); node; node = listnextnode (node)) { v = listgetdata (node); - if (v->d_N > vertex->d_N) - { - listnode_add_before (spftree->tents, node, vertex); - break; - } - else if (v->d_N == vertex->d_N && v->type > vertex->type) - { - /* Tie break, add according to type */ + if (tent_cmp(v, vertex)) + { listnode_add_before (spftree->tents, node, vertex); - break; - } + break; + } } if (node == NULL) @@ -537,7 +528,7 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, static void isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, void *id, struct isis_adjacency *adj, uint32_t cost, - int family, struct isis_vertex *parent) + struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -576,13 +567,13 @@ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, } } - isis_spf_add2tent (spftree, vtype, id, cost, 1, family, adj, parent); + isis_spf_add2tent (spftree, vtype, id, cost, 1, adj, parent); return; } static void process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, - uint32_t dist, uint16_t depth, int family, + uint32_t dist, uint16_t depth, struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -670,7 +661,7 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, (parent ? print_sys_hostname (parent->N.id) : "null")); #endif /* EXTREME_DEBUG */ - isis_spf_add2tent (spftree, vtype, id, dist, depth, family, NULL, parent); + isis_spf_add2tent (spftree, vtype, id, dist, depth, NULL, parent); return; } @@ -679,9 +670,10 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, */ static int isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, - uint32_t cost, uint16_t depth, int family, + uint32_t cost, uint16_t depth, u_char *root_sysid, struct isis_vertex *parent) { + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->lsp_header->lsp_id); struct listnode *node, *fragnode = NULL; uint32_t dist; struct is_neigh *is_neigh; @@ -692,8 +684,14 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, struct prefix prefix; struct ipv6_reachability *ip6reach; static const u_char null_sysid[ISIS_SYS_ID_LEN]; + struct mt_router_info *mt_router_info = NULL; + + if (spftree->mtid != ISIS_MT_IPV4_UNICAST) + mt_router_info = tlvs_lookup_mt_router_info(&lsp->tlv_data, spftree->mtid); - if (!speaks (lsp->tlv_data.nlpids, family)) + if (!pseudo_lsp + && (spftree->mtid == ISIS_MT_IPV4_UNICAST && !speaks(lsp->tlv_data.nlpids, spftree->family)) + && !mt_router_info) return ISIS_OK; lspfragloop: @@ -707,9 +705,13 @@ lspfragloop: zlog_debug ("ISIS-Spf: process_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id)); #endif /* EXTREME_DEBUG */ - if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) + /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ + if (pseudo_lsp + || (spftree->mtid == ISIS_MT_IPV4_UNICAST && !ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) + || (mt_router_info && !mt_router_info->overload)) + { - if (lsp->tlv_data.is_neighs) + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) { @@ -717,95 +719,126 @@ lspfragloop: /* Two way connectivity */ if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if (!memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + if (!pseudo_lsp && !memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) continue; dist = cost + is_neigh->metrics.metric_default; - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; - process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, - depth + 1, family, parent); + process_N (spftree, LSP_PSEUDO_ID(is_neigh->neigh_id) ? VTYPE_PSEUDO_IS + : VTYPE_NONPSEUDO_IS, + (void *) is_neigh->neigh_id, dist, depth + 1, parent); } } - if (lsp->tlv_data.te_is_neighs) - { - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, - te_is_neigh)) + + struct list *te_is_neighs = NULL; + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + te_is_neighs = lsp->tlv_data.te_is_neighs; + } + else + { + struct tlv_mt_neighbors *mt_neighbors; + mt_neighbors = tlvs_lookup_mt_neighbors(&lsp->tlv_data, spftree->mtid); + if (mt_neighbors) + te_is_neighs = mt_neighbors->list; + } + for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if (!memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + if (!pseudo_lsp && !memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) continue; dist = cost + GET_TE_METRIC(te_is_neigh); - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; - process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, - depth + 1, family, parent); + process_N (spftree, LSP_PSEUDO_ID(te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS + : VTYPE_NONPSEUDO_TE_IS, + (void *) te_is_neigh->neigh_id, dist, depth + 1, parent); } - } } - if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs) - { - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_INTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); - } - } - if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs) + if (!pseudo_lsp + && spftree->family == AF_INET + && spftree->mtid == ISIS_MT_IPV4_UNICAST) { + struct list *reachs[] = {lsp->tlv_data.ipv4_int_reachs, + lsp->tlv_data.ipv4_ext_reachs}; + prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_EXTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); - } + for (unsigned int i = 0; i < array_size(reachs); i++) + { + vtype = (reachs[i] == lsp->tlv_data.ipv4_int_reachs) ? VTYPE_IPREACH_INTERNAL + : VTYPE_IPREACH_EXTERNAL; + for (ALL_LIST_ELEMENTS_RO (reachs[i], node, ipreach)) + { + dist = cost + ipreach->metrics.metric_default; + prefix.u.prefix4 = ipreach->prefix; + prefix.prefixlen = ip_masklen (ipreach->mask); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + parent); + } + } } - if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs) + + if (!pseudo_lsp && spftree->family == AF_INET) { + struct list *ipv4reachs = NULL; + + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + ipv4reachs = lsp->tlv_data.te_ipv4_reachs; + } + else + { + struct tlv_mt_ipv4_reachs *mt_reachs; + mt_reachs = tlvs_lookup_mt_ipv4_reachs(&lsp->tlv_data, spftree->mtid); + if (mt_reachs) + ipv4reachs = mt_reachs->list; + } + prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, - node, te_ipv4_reach)) + for (ALL_LIST_ELEMENTS_RO (ipv4reachs, node, te_ipv4_reach)) { assert ((te_ipv4_reach->control & 0x3F) <= IPV4_MAX_BITLEN); dist = cost + ntohl (te_ipv4_reach->te_metric); - vtype = VTYPE_IPREACH_TE; prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start, te_ipv4_reach->control); prefix.prefixlen = (te_ipv4_reach->control & 0x3F); apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); + process_N (spftree, VTYPE_IPREACH_TE, (void *) &prefix, dist, depth + 1, + parent); } } - if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs) + + if (!pseudo_lsp + && spftree->family == AF_INET6) { + struct list *ipv6reachs = NULL; + + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + ipv6reachs = lsp->tlv_data.ipv6_reachs; + } + else + { + struct tlv_mt_ipv6_reachs *mt_reachs; + mt_reachs = tlvs_lookup_mt_ipv6_reachs(&lsp->tlv_data, spftree->mtid); + if (mt_reachs) + ipv6reachs = mt_reachs->list; + } + prefix.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, node, ip6reach)) + for (ALL_LIST_ELEMENTS_RO (ipv6reachs, node, ip6reach)) { assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN); dist = cost + ntohl(ip6reach->metric); vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? - VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; + VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; prefix.prefixlen = ip6reach->prefix_len; memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix, PSIZE (ip6reach->prefix_len)); apply_mask (&prefix); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); + parent); } } @@ -824,76 +857,8 @@ lspfragloop: } static int -isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, - struct isis_lsp *lsp, uint32_t cost, - uint16_t depth, int family, - u_char *root_sysid, - struct isis_vertex *parent) -{ - struct listnode *node, *fragnode = NULL; - struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; - enum vertextype vtype; - uint32_t dist; - -pseudofragloop: - - if (lsp->lsp_header->seq_num == 0) - { - zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num" - " - do not process"); - return ISIS_WARNING; - } - -#ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: process_pseudo_lsp %s", - print_sys_hostname(lsp->lsp_header->lsp_id)); -#endif /* EXTREME_DEBUG */ - - /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ - - if (lsp->tlv_data.is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) - { - /* Two way connectivity */ - if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) - continue; - dist = cost + is_neigh->metrics.metric_default; - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; - process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, - depth + 1, family, parent); - } - if (lsp->tlv_data.te_is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh)) - { - /* Two way connectivity */ - if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) - continue; - dist = cost + GET_TE_METRIC(te_is_neigh); - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; - process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, - depth + 1, family, parent); - } - - if (fragnode == NULL) - fragnode = listhead (lsp->lspu.frags); - else - fragnode = listnextnode (fragnode); - - if (fragnode) - { - lsp = listgetdata (fragnode); - goto pseudofragloop; - } - - return ISIS_OK; -} - -static int -isis_spf_preload_tent (struct isis_spftree *spftree, int level, - int family, u_char *root_sysid, +isis_spf_preload_tent (struct isis_spftree *spftree, + u_char *root_sysid, struct isis_vertex *parent) { struct isis_circuit *circuit; @@ -908,21 +873,25 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, u_char lsp_id[ISIS_SYS_ID_LEN + 2]; static u_char null_lsp_id[ISIS_SYS_ID_LEN + 2]; struct prefix_ipv6 *ipv6; + struct isis_circuit_mt_setting *circuit_mt; for (ALL_LIST_ELEMENTS_RO (spftree->area->circuit_list, cnode, circuit)) { + circuit_mt = circuit_lookup_mt_setting(circuit, spftree->mtid); + if (circuit_mt && !circuit_mt->enabled) + continue; if (circuit->state != C_STATE_UP) continue; - if (!(circuit->is_type & level)) + if (!(circuit->is_type & spftree->level)) continue; - if (family == AF_INET && !circuit->ip_router) + if (spftree->family == AF_INET && !circuit->ip_router) continue; - if (family == AF_INET6 && !circuit->ipv6_router) + if (spftree->family == AF_INET6 && !circuit->ipv6_router) continue; /* * Add IP(v6) addresses of this circuit */ - if (family == AF_INET) + if (spftree->family == AF_INET) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) @@ -931,10 +900,10 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, prefix.prefixlen = ipv4->prefixlen; apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix, - NULL, 0, family, parent); + NULL, 0, parent); } } - if (family == AF_INET6) + if (spftree->family == AF_INET6) { prefix.family = AF_INET6; for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) @@ -943,7 +912,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, prefix.u.prefix6 = ipv6->prefix; apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL, - &prefix, NULL, 0, family, parent); + &prefix, NULL, 0, parent); } } if (circuit->circ_type == CIRCUIT_T_BROADCAST) @@ -952,45 +921,46 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, * Add the adjacencies */ adj_list = list_new (); - adjdb = circuit->u.bc.adjdb[level - 1]; + adjdb = circuit->u.bc.adjdb[spftree->level - 1]; isis_adj_build_up_list (adjdb, adj_list); if (listcount (adj_list) == 0) { list_delete (adj_list); if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: no L%d adjacencies on circuit %s", - level, circuit->interface->name); + spftree->level, circuit->interface->name); continue; } for (ALL_LIST_ELEMENTS_RO (adj_list, anode, adj)) { - if (!speaks (&adj->nlpids, family)) - continue; + if (!adj_has_mt(adj, spftree->mtid)) + continue; + if (spftree->mtid == ISIS_MT_IPV4_UNICAST && !speaks (&adj->nlpids, spftree->family)) + continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], - family, parent); + circuit->te_metric[spftree->level - 1], + parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: - isis_spf_add_local (spftree, - spftree->area->oldmetric ? - VTYPE_NONPSEUDO_IS : - VTYPE_NONPSEUDO_TE_IS, - adj->sysid, adj, - circuit->te_metric[level - 1], - family, parent); memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lsp_id) = 0; LSP_FRAGMENT (lsp_id) = 0; - lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + isis_spf_add_local (spftree, + spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + lsp_id, adj, + circuit->te_metric[spftree->level - 1], + parent); + lsp = lsp_search (lsp_id, spftree->area->lspdb[spftree->level - 1]); if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) zlog_warn ("ISIS-Spf: No LSP %s found for IS adjacency " "L%d on %s (ID %u)", - rawlspid_print (lsp_id), level, + rawlspid_print (lsp_id), spftree->level, circuit->interface->name, circuit->circuit_id); break; case ISIS_SYSTYPE_UNKNOWN: @@ -1002,7 +972,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, /* * Add the pseudonode */ - if (level == 1) + if (spftree->level == 1) memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); @@ -1011,55 +981,59 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, { if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: No L%d DR on %s (ID %d)", - level, circuit->interface->name, circuit->circuit_id); + spftree->level, circuit->interface->name, circuit->circuit_id); continue; } adj = isis_adj_lookup (lsp_id, adjdb); /* if no adj, we are the dis or error */ - if (!adj && !circuit->u.bc.is_dr[level - 1]) + if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) { zlog_warn ("ISIS-Spf: No adjacency found from root " "to L%d DR %s on %s (ID %d)", - level, rawlspid_print (lsp_id), + spftree->level, rawlspid_print (lsp_id), circuit->interface->name, circuit->circuit_id); continue; } - lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + lsp = lsp_search (lsp_id, spftree->area->lspdb[spftree->level - 1]); if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) { zlog_warn ("ISIS-Spf: No lsp (%p) found from root " "to L%d DR %s on %s (ID %d)", - (void *)lsp, level, rawlspid_print (lsp_id), + (void *)lsp, spftree->level, rawlspid_print (lsp_id), circuit->interface->name, circuit->circuit_id); continue; } - isis_spf_process_pseudo_lsp (spftree, lsp, - circuit->te_metric[level - 1], 0, - family, root_sysid, parent); + isis_spf_process_lsp (spftree, lsp, + circuit->te_metric[spftree->level - 1], 0, + root_sysid, parent); } else if (circuit->circ_type == CIRCUIT_T_P2P) { adj = circuit->u.p2p.neighbor; if (!adj) continue; + if (!adj_has_mt(adj, spftree->mtid)) + continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], family, + circuit->te_metric[spftree->level - 1], parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: - if (speaks (&adj->nlpids, family)) + memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (lsp_id) = 0; + LSP_FRAGMENT (lsp_id) = 0; + if (spftree->mtid != ISIS_MT_IPV4_UNICAST || speaks (&adj->nlpids, spftree->family)) isis_spf_add_local (spftree, - spftree->area->oldmetric ? - VTYPE_NONPSEUDO_IS : - VTYPE_NONPSEUDO_TE_IS, - adj->sysid, - adj, circuit->te_metric[level - 1], - family, parent); + spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + lsp_id, + adj, circuit->te_metric[spftree->level - 1], + parent); break; case ISIS_SYSTYPE_UNKNOWN: default: @@ -1086,8 +1060,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, * now we just put the child pointer(s) in place */ static void -add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, - int level) +add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex) { char buff[PREFIX2STR_BUFFER]; @@ -1102,11 +1075,11 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ - if (vertex->type > VTYPE_ES) + if (VTYPE_IP(vertex->type)) { if (listcount (vertex->Adj_N) > 0) isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N, - vertex->depth, vertex->Adj_N, spftree->area, level); + vertex->depth, vertex->Adj_N, spftree->area, spftree->level); else if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: no adjacencies do not install route for " "%s depth %d dist %d", vid2string (vertex, buff, sizeof (buff)), @@ -1117,12 +1090,16 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, } static void -init_spt (struct isis_spftree *spftree) +init_spt (struct isis_spftree *spftree, int mtid, int level, int family) { spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del; list_delete_all_node (spftree->tents); list_delete_all_node (spftree->paths); spftree->tents->del = spftree->paths->del = NULL; + + spftree->mtid = mtid; + spftree->level = level; + spftree->family = family; return; } @@ -1139,6 +1116,7 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) struct route_table *table = NULL; struct timeval time_now; unsigned long long start_time, end_time; + uint16_t mtid; /* Get time that can't roll backwards. */ monotime(&time_now); @@ -1160,14 +1138,20 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) isis_route_invalidate_table (area, table); + /* We only support ipv4-unicast and ipv6-unicast as topologies for now */ + if (family == AF_INET6) + mtid = isis_area_ipv6_topology(area); + else + mtid = ISIS_MT_IPV4_UNICAST; + /* * C.2.5 Step 0 */ - init_spt (spftree); + init_spt (spftree, mtid, level, family); /* a) */ - root_vertex = isis_spf_add_root (spftree, level, sysid); + root_vertex = isis_spf_add_root (spftree, sysid); /* b) */ - retval = isis_spf_preload_tent (spftree, level, family, sysid, root_vertex); + retval = isis_spf_preload_tent (spftree, sysid, root_vertex); if (retval != ISIS_OK) { zlog_warn ("ISIS-Spf: failed to load TENT SPF-root:%s", print_sys_hostname(sysid)); @@ -1196,37 +1180,22 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) /* Remove from tent list and add to paths list */ list_delete_node (spftree->tents, node); - add_to_paths (spftree, vertex, level); - switch (vertex->type) + add_to_paths (spftree, vertex); + if (VTYPE_IS(vertex->type)) { - case VTYPE_PSEUDO_IS: - case VTYPE_NONPSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: - case VTYPE_NONPSEUDO_TE_IS: memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (lsp_id) = 0; lsp = lsp_search (lsp_id, area->lspdb[level - 1]); if (lsp && lsp->lsp_header->rem_lifetime != 0) { - if (LSP_PSEUDO_ID (lsp_id)) - { - isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, sysid, - vertex); - } - else - { - isis_spf_process_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, sysid, vertex); - } + isis_spf_process_lsp (spftree, lsp, vertex->d_N, + vertex->depth, sysid, vertex); } else { zlog_warn ("ISIS-Spf: No LSP found for %s", rawlspid_print (lsp_id)); } - break; - default:; } } @@ -1243,17 +1212,17 @@ out: } static int -isis_run_spf_l1 (struct thread *thread) +isis_run_spf_cb (struct thread *thread) { - struct isis_area *area; + struct isis_spf_run *run = THREAD_ARG (thread); + struct isis_area *area = run->area; + int level = run->level; int retval = ISIS_OK; - area = THREAD_ARG (thread); - assert (area); + XFREE(MTYPE_ISIS_SPF_RUN, run); + area->spf_timer[level - 1] = NULL; - area->spf_timer[0] = NULL; - - if (!(area->is_type & IS_LEVEL_1)) + if (!(area->is_type & level)) { if (isis->debugs & DEBUG_SPF_EVENTS) zlog_warn ("ISIS-SPF (%s) area does not share level", @@ -1262,43 +1231,26 @@ isis_run_spf_l1 (struct thread *thread) } if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag); + zlog_debug ("ISIS-Spf (%s) L%d SPF needed, periodic SPF", + area->area_tag, level); if (area->ip_circuits) - retval = isis_run_spf (area, 1, AF_INET, isis->sysid); + retval = isis_run_spf (area, level, AF_INET, isis->sysid); if (area->ipv6_circuits) - retval = isis_run_spf (area, 1, AF_INET6, isis->sysid); + retval = isis_run_spf (area, level, AF_INET6, isis->sysid); return retval; } -static int -isis_run_spf_l2 (struct thread *thread) +static struct isis_spf_run* +isis_run_spf_arg(struct isis_area *area, int level) { - struct isis_area *area; - int retval = ISIS_OK; - - area = THREAD_ARG (thread); - assert (area); + struct isis_spf_run *run = XMALLOC(MTYPE_ISIS_SPF_RUN, sizeof(*run)); - area->spf_timer[1] = NULL; + run->area = area; + run->level = level; - if (!(area->is_type & IS_LEVEL_2)) - { - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); - return ISIS_WARNING; - } - - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag); - - if (area->ip_circuits) - retval = isis_run_spf (area, 2, AF_INET, isis->sysid); - if (area->ipv6_circuits) - retval = isis_run_spf (area, 2, AF_INET6, isis->sysid); - - return retval; + return run; } int @@ -1323,16 +1275,9 @@ isis_spf_schedule (struct isis_area *area, int level) if (area->spf_timer[level - 1]) return ISIS_OK; - if (level == 1) - { - THREAD_TIMER_MSEC_ON(master, area->spf_timer[0], - isis_run_spf_l1, area, delay); - } - else - { - THREAD_TIMER_MSEC_ON(master, area->spf_timer[1], - isis_run_spf_l2, area, delay); - } + THREAD_TIMER_MSEC_ON(master, area->spf_timer[level-1], + isis_run_spf_cb, isis_run_spf_arg(area, level), + delay); return ISIS_OK; } @@ -1352,12 +1297,9 @@ isis_spf_schedule (struct isis_area *area, int level) return retval; } - if (level == 1) - THREAD_TIMER_ON (master, area->spf_timer[0], isis_run_spf_l1, area, - area->min_spf_interval[0] - diff); - else - THREAD_TIMER_ON (master, area->spf_timer[1], isis_run_spf_l2, area, - area->min_spf_interval[1] - diff); + THREAD_TIMER_ON (master, area->spf_timer[level-1], + isis_run_spf_cb, isis_run_spf_arg(area, level), + area->min_spf_interval[level-1] - diff); if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %d sec from now", @@ -1427,14 +1369,23 @@ isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid) DEFUN (show_isis_topology, show_isis_topology_cmd, - "show isis topology", + "show isis topology [<level-1|level-2>]", SHOW_STR "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n") + "IS-IS paths to Intermediate Systems\n" + "Paths to all level-1 routers in the area\n" + "Paths to all level-2 routers in the domain\n") { + int levels; struct listnode *node; struct isis_area *area; - int level; + + if (argc < 4) + levels = ISIS_LEVEL1|ISIS_LEVEL2; + else if (!strcmp(argv[3]->arg, "level-1")) + levels = ISIS_LEVEL1; + else + levels = ISIS_LEVEL2; if (!isis->area_list || isis->area_list->count == 0) return CMD_SUCCESS; @@ -1444,23 +1395,26 @@ DEFUN (show_isis_topology, vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", VTY_NEWLINE); - for (level = 0; level < ISIS_LEVELS; level++) + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - if (area->ip_circuits > 0 && area->spftree[level] - && area->spftree[level]->paths->count > 0) + if ((level & levels) == 0) + continue; + + if (area->ip_circuits > 0 && area->spftree[level-1] + && area->spftree[level-1]->paths->count > 0) { vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s", - level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree[level]->paths, isis->sysid); + level, VTY_NEWLINE); + isis_print_paths (vty, area->spftree[level-1]->paths, isis->sysid); vty_out (vty, "%s", VTY_NEWLINE); } - if (area->ipv6_circuits > 0 && area->spftree6[level] - && area->spftree6[level]->paths->count > 0) + if (area->ipv6_circuits > 0 && area->spftree6[level-1] + && area->spftree6[level-1]->paths->count > 0) { vty_out (vty, "IS-IS paths to level-%d routers that speak IPv6%s", - level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[level]->paths, isis->sysid); + level, VTY_NEWLINE); + isis_print_paths (vty, area->spftree6[level-1]->paths, isis->sysid); vty_out (vty, "%s", VTY_NEWLINE); } } @@ -1471,92 +1425,8 @@ DEFUN (show_isis_topology, return CMD_SUCCESS; } -DEFUN (show_isis_topology_l1, - show_isis_topology_l1_cmd, - "show isis topology level-1", - SHOW_STR - "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n" - "Paths to all level-1 routers in the area\n") -{ - struct listnode *node; - struct isis_area *area; - - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; - - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) - { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - - if (area->ip_circuits > 0 && area->spftree[0] - && area->spftree[0]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree[0]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - if (area->ipv6_circuits > 0 && area->spftree6[0] - && area->spftree6[0]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[0]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN (show_isis_topology_l2, - show_isis_topology_l2_cmd, - "show isis topology level-2", - SHOW_STR - "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n" - "Paths to all level-2 routers in the domain\n") -{ - struct listnode *node; - struct isis_area *area; - - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; - - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) - { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - - if (area->ip_circuits > 0 && area->spftree[1] - && area->spftree[1]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree[1]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - if (area->ipv6_circuits > 0 && area->spftree6[1] - && area->spftree6[1]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[1]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - void isis_spf_cmds_init () { install_element (VIEW_NODE, &show_isis_topology_cmd); - install_element (VIEW_NODE, &show_isis_topology_l1_cmd); - install_element (VIEW_NODE, &show_isis_topology_l2_cmd); } diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index fb534542d..9f06dbb60 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -38,6 +38,10 @@ enum vertextype VTYPE_IP6REACH_EXTERNAL }; +#define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS) +#define VTYPE_ES(t) ((t) == VTYPE_ES) +#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL) + /* * Triple <N, d(N), {Adj(N)}> */ @@ -66,12 +70,14 @@ struct isis_spftree unsigned int runcount; /* number of runs since uptime */ time_t last_run_timestamp; /* last run timestamp for scheduling */ time_t last_run_duration; /* last run duration in msec */ + + uint16_t mtid; + int family; + int level; }; struct isis_spftree * isis_spftree_new (struct isis_area *area); void isis_spftree_del (struct isis_spftree *spftree); -void isis_spftree_adj_del (struct isis_spftree *spftree, - struct isis_adjacency *adj); void spftree_area_init (struct isis_area *area); void spftree_area_del (struct isis_area *area); void spftree_area_adj_del (struct isis_area *area, diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 4192fff9a..b033e35a2 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -43,6 +43,7 @@ #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" void free_tlv (void *val) @@ -61,10 +62,14 @@ free_tlvs (struct tlvs *tlvs) { if (tlvs->area_addrs) list_delete (tlvs->area_addrs); + if (tlvs->mt_router_info) + list_delete (tlvs->mt_router_info); if (tlvs->is_neighs) list_delete (tlvs->is_neighs); if (tlvs->te_is_neighs) list_delete (tlvs->te_is_neighs); + if (tlvs->mt_is_neighs) + list_delete (tlvs->mt_is_neighs); if (tlvs->es_neighs) list_delete (tlvs->es_neighs); if (tlvs->lsp_entries) @@ -81,16 +86,293 @@ free_tlvs (struct tlvs *tlvs) list_delete (tlvs->ipv4_ext_reachs); if (tlvs->te_ipv4_reachs) list_delete (tlvs->te_ipv4_reachs); + if (tlvs->mt_ipv4_reachs) + list_delete (tlvs->mt_ipv4_reachs); if (tlvs->ipv6_addrs) list_delete (tlvs->ipv6_addrs); if (tlvs->ipv6_reachs) list_delete (tlvs->ipv6_reachs); + if (tlvs->mt_ipv6_reachs) + list_delete (tlvs->mt_ipv6_reachs); memset (tlvs, 0, sizeof (struct tlvs)); return; } +static int +parse_mtid(uint16_t *mtid, bool read_mtid, + unsigned int *length, u_char **pnt) +{ + if (!read_mtid) + { + *mtid = ISIS_MT_IPV4_UNICAST; + return ISIS_OK; + } + + uint16_t mtid_buf; + + if (*length < sizeof(mtid_buf)) + { + zlog_warn("ISIS-TLV: mt tlv too short to contain MT id"); + return ISIS_WARNING; + } + + memcpy(&mtid_buf, *pnt, sizeof(mtid_buf)); + *pnt += sizeof(mtid_buf); + *length -= sizeof(mtid_buf); + + *mtid = ntohs(mtid_buf) & ISIS_MT_MASK; + return ISIS_OK; +} + +static int +parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *neigh_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->te_is_neighs) + { + tlvs->te_is_neighs = list_new(); + tlvs->te_is_neighs->del = free_tlv; + } + neigh_list = tlvs->te_is_neighs; + } + else + { + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_get_mt_neighbors(tlvs, mtid); + neighbors->list->del = free_tlv; + neigh_list = neighbors->list; + } + + while (length >= IS_NEIGHBOURS_LEN) + { + struct te_is_neigh *neigh = XCALLOC(MTYPE_ISIS_TLV, sizeof(*neigh)); + + memcpy(neigh, pnt, IS_NEIGHBOURS_LEN); + pnt += IS_NEIGHBOURS_LEN; + length -= IS_NEIGHBOURS_LEN; + + if (neigh->sub_tlvs_length > length) + { + zlog_warn("ISIS-TLV: neighbor subtlv length exceeds TLV size"); + XFREE(MTYPE_ISIS_TLV, neigh); + return ISIS_WARNING; + } + + memcpy(neigh->sub_tlvs, pnt, neigh->sub_tlvs_length); + pnt += neigh->sub_tlvs_length; + length -= neigh->sub_tlvs_length; + + listnode_add(neigh_list, neigh); + } + + if (length) + { + zlog_warn("ISIS-TLV: TE/MT neighor TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +static int +parse_mt_ipv4_reachs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *reach_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->te_ipv4_reachs) + { + tlvs->te_ipv4_reachs = list_new(); + tlvs->te_ipv4_reachs->del = free_tlv; + } + reach_list = tlvs->te_ipv4_reachs; + } + else + { + struct tlv_mt_ipv4_reachs *reachs; + + reachs = tlvs_get_mt_ipv4_reachs(tlvs, mtid); + reachs->list->del = free_tlv; + reach_list = reachs->list; + } + + while (length >= 5) /* Metric + Control */ + { + struct te_ipv4_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, TE_IPV4_REACH_LEN); + + memcpy(reach, pnt, 5); /* Metric + Control */ + pnt += 5; + length -= 5; + + unsigned char prefixlen = reach->control & 0x3F; + + if (prefixlen > IPV4_MAX_BITLEN) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix length %d", prefixlen); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + if (length < (unsigned int)PSIZE(prefixlen)) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix too long for tlv"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + memcpy(&reach->prefix_start, pnt, PSIZE(prefixlen)); + pnt += PSIZE(prefixlen); + length -= PSIZE(prefixlen); + + if (reach->control & TE_IPV4_HAS_SUBTLV) + { + if (length < 1) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLV missing"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + u_char subtlv_len = *pnt; + pnt++; + length--; + + if (length < subtlv_len) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLVs have oversize"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + /* Skip Sub-TLVs for now */ + pnt += subtlv_len; + length -= subtlv_len; + } + listnode_add(reach_list, reach); + } + + if (length) + { + zlog_warn("ISIS-TLV: TE/MT ipv4 reachability TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +static int +parse_mt_ipv6_reachs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *reach_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->ipv6_reachs) + { + tlvs->ipv6_reachs = list_new(); + tlvs->ipv6_reachs->del = free_tlv; + } + reach_list = tlvs->ipv6_reachs; + } + else + { + struct tlv_mt_ipv6_reachs *reachs; + + reachs = tlvs_get_mt_ipv6_reachs(tlvs, mtid); + reachs->list->del = free_tlv; + reach_list = reachs->list; + } + + while (length >= 6) /* Metric + Control + Prefixlen */ + { + struct ipv6_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*reach)); + + memcpy(reach, pnt, 6); /* Metric + Control + Prefixlen */ + pnt += 6; + length -= 6; + + if (reach->prefix_len > IPV6_MAX_BITLEN) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix length %d", reach->prefix_len); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + if (length < (unsigned int)PSIZE(reach->prefix_len)) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix too long for tlv"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + memcpy(&reach->prefix, pnt, PSIZE(reach->prefix_len)); + pnt += PSIZE(reach->prefix_len); + length -= PSIZE(reach->prefix_len); + + if (reach->control_info & CTRL_INFO_SUBTLVS) + { + if (length < 1) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLV missing"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + u_char subtlv_len = *pnt; + pnt++; + length--; + + if (length < subtlv_len) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLVs have oversize"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + /* Skip Sub-TLVs for now */ + pnt += subtlv_len; + length -= subtlv_len; + } + listnode_add(reach_list, reach); + } + + if (length) + { + zlog_warn("ISIS-TLV: (MT) IPv6 reachability TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + /* * Parses the tlvs found in the variant length part of the PDU. * Caller tells with flags in "expected" which TLV's it is interested in. @@ -103,17 +385,13 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, struct lan_neigh *lan_nei; struct area_addr *area_addr; struct is_neigh *is_nei; - struct te_is_neigh *te_is_nei; struct es_neigh *es_nei; struct lsp_entry *lsp_entry; struct in_addr *ipv4_addr; struct ipv4_reachability *ipv4_reach; - struct te_ipv4_reachability *te_ipv4_reach; struct in6_addr *ipv6_addr; - struct ipv6_reachability *ipv6_reach; - int prefix_octets; int value_len, retval = ISIS_OK; - u_char *start = stream, *pnt = stream, *endpnt; + u_char *start = stream, *pnt = stream; *found = 0; memset (tlvs, 0, sizeof (struct tlvs)); @@ -207,54 +485,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case TE_IS_NEIGHBOURS: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Neighbour ID | 7 - * +---------------------------------------------------------------+ - * | TE Metric | 3 - * +---------------------------------------------------------------+ - * | SubTLVs Length | 1 - * +---------------------------------------------------------------+ - * : : - */ *found |= TLVFLAG_TE_IS_NEIGHS; #ifdef EXTREME_TLV_DEBUG zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d", areatag, length); #endif /* EXTREME_TLV_DEBUG */ if (TLVFLAG_TE_IS_NEIGHS & *expected) - { - while (length > value_len) - { - te_is_nei = (struct te_is_neigh *) pnt; - value_len += IS_NEIGHBOURS_LEN; - pnt += IS_NEIGHBOURS_LEN; - /* FIXME - subtlvs are handled here, for now we skip */ - /* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */ - /* So, it must be copied in a new te_is_neigh structure */ - /* rather than just initialize pointer to the original LSP PDU */ - /* to avoid consider the rest of lspdu as subTLVs or buffer overflow */ - if (IS_MPLS_TE(isisMplsTE)) - { - struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh)); - memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1); - memcpy(new->te_metric, te_is_nei->te_metric, 3); - new->sub_tlvs_length = te_is_nei->sub_tlvs_length; - memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length); - te_is_nei = new; - } - /* Skip SUB TLVs payload */ - value_len += te_is_nei->sub_tlvs_length; - pnt += te_is_nei->sub_tlvs_length; - - if (!tlvs->te_is_neighs) - tlvs->te_is_neighs = list_new (); - listnode_add (tlvs->te_is_neighs, te_is_nei); - } - } - else - { - pnt += length; - } + retval = parse_mt_is_neighs(tlvs, false, length, pnt); + pnt += length; + break; + + case MT_IS_NEIGHBOURS: + *found |= TLVFLAG_TE_IS_NEIGHS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): MT IS Neighbours length %d", + areatag, length); +#endif + if (TLVFLAG_TE_IS_NEIGHS & *expected) + retval = parse_mt_is_neighs(tlvs, true, length, pnt); + pnt += length; break; case ES_NEIGHBOURS: @@ -577,71 +826,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case TE_IPV4_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | TE Metric | 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | U/D | sTLV? | Prefix Mask Len | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Prefix | 0-4 - * +---------------------------------------------------------------+ - * | sub tlvs | - * +---------------------------------------------------------------+ - * : : - */ *found |= TLVFLAG_TE_IPV4_REACHABILITY; #ifdef EXTREME_TLV_DEBUG zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d", - areatag, length); + areatag, length); #endif /* EXTREME_TLV_DEBUG */ - endpnt = pnt + length; if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) - { - while (length > value_len) - { - te_ipv4_reach = (struct te_ipv4_reachability *) pnt; - if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN) - { - zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach" - "ability prefix length %d", areatag, - te_ipv4_reach->control & 0x3F); - retval = ISIS_WARNING; - break; - } - if (!tlvs->te_ipv4_reachs) - tlvs->te_ipv4_reachs = list_new (); - listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach); - - /* Metric + Control-Byte + Prefix */ - unsigned int entry_len = 5 + PSIZE(te_ipv4_reach->control & 0x3F); - value_len += entry_len; - pnt += entry_len; - - if (te_ipv4_reach->control & TE_IPV4_HAS_SUBTLV) - { - if (length <= value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLV missing", - areatag); - retval = ISIS_WARNING; - break; - } - u_char subtlv_len = *pnt; - value_len += subtlv_len + 1; - pnt += subtlv_len + 1; - if (length < value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLVs have oversize", - areatag); - retval = ISIS_WARNING; - break; - } - } - } - } - - pnt = endpnt; + retval = parse_mt_ipv4_reachs(tlvs, false, length, pnt); + pnt += length; + break; + case MT_IPV4_REACHABILITY: + *found |= TLVFLAG_TE_IPV4_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv4 MT Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) + retval = parse_mt_ipv4_reachs(tlvs, true, length, pnt); + pnt += length; break; - case IPV6_ADDR: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * + IP version 6 address + 16 @@ -672,67 +875,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case IPV6_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Default Metric | 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Control Informantion | - * +---------------------------------------------------------------+ - * | IPv6 Prefix Length |--+ - * +---------------------------------------------------------------+ | - * | IPv6 Prefix |<-+ - * +---------------------------------------------------------------+ - */ *found |= TLVFLAG_IPV6_REACHABILITY; - endpnt = pnt + length; - +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ if (*expected & TLVFLAG_IPV6_REACHABILITY) - { - while (length > value_len) - { - ipv6_reach = (struct ipv6_reachability *) pnt; - if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN) - { - zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach" - "ability prefix length %d", areatag, - ipv6_reach->prefix_len); - retval = ISIS_WARNING; - break; - } - - prefix_octets = ((ipv6_reach->prefix_len + 7) / 8); - value_len += prefix_octets + 6; - pnt += prefix_octets + 6; - - if (ipv6_reach->control_info & CTRL_INFO_SUBTLVS) - { - if (length <= value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLV missing", - areatag); - retval = ISIS_WARNING; - break; - } - u_char subtlv_len = *pnt; - value_len += subtlv_len + 1; - pnt += subtlv_len + 1; - if (length < value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLVs have oversize", - areatag); - retval = ISIS_WARNING; - break; - } - } - /* FIXME: sub-tlvs */ - if (!tlvs->ipv6_reachs) - tlvs->ipv6_reachs = list_new (); - listnode_add (tlvs->ipv6_reachs, ipv6_reach); - } - } - - pnt = endpnt; + retval = parse_mt_ipv6_reachs(tlvs, false, length, pnt); + pnt += length; + break; + case MT_IPV6_REACHABILITY: + *found |= TLVFLAG_IPV6_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV6_REACHABILITY) + retval = parse_mt_ipv6_reachs(tlvs, true, length, pnt); + pnt += length; break; - case WAY3_HELLO: /* +---------------------------------------------------------------+ * | Adjacency state | 1 @@ -786,6 +947,42 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, pnt += length; break; + case MT_ROUTER_INFORMATION: + *found |= TLVFLAG_MT_ROUTER_INFORMATION; + if (*expected & TLVFLAG_MT_ROUTER_INFORMATION) + { + if (!tlvs->mt_router_info) + { + tlvs->mt_router_info = list_new(); + tlvs->mt_router_info->del = free_tlv; + } + while (length > value_len) + { + uint16_t mt_info; + struct mt_router_info *info; + + if (value_len + sizeof(mt_info) > length) { + zlog_warn("ISIS-TLV (%s): TLV 229 is truncated.", areatag); + pnt += length - value_len; + break; + } + + memcpy(&mt_info, pnt, sizeof(mt_info)); + pnt += sizeof(mt_info); + value_len += sizeof(mt_info); + + mt_info = ntohs(mt_info); + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_info & ISIS_MT_MASK; + info->overload = mt_info & ISIS_MT_OL_MASK; + listnode_add(tlvs->mt_router_info, info); + } + } + else + { + pnt += length; + } + break; default: zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d", areatag, type, length); @@ -826,6 +1023,31 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) } int +tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream) +{ + struct listnode *node; + struct mt_router_info *info; + + uint16_t value[127]; + uint16_t *pos = value; + + for (ALL_LIST_ELEMENTS_RO(mt_router_info, node, info)) + { + uint16_t mt_info; + + mt_info = info->mtid; + if (info->overload) + mt_info |= ISIS_MT_OL_MASK; + + *pos = htons(mt_info); + pos++; + } + + return add_tlv(MT_ROUTER_INFORMATION, (pos - value) * sizeof(*pos), + (u_char*)value, stream); +} + +int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream) { struct listnode *node; @@ -887,26 +1109,44 @@ tlv_add_is_neighs (struct list *is_neighs, struct stream *stream) return add_tlv (IS_NEIGHBOURS, pos - value, value, stream); } -int -tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) +static size_t +max_tlv_size(struct stream *stream) +{ + size_t avail = stream_get_size (stream) - stream_get_endp(stream); + + if (avail < 2) + return 0; + + if (avail < 257) + return avail - 2; + + return 255; +} + +unsigned int +tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg) { struct listnode *node; struct te_is_neigh *te_is_neigh; u_char value[255]; u_char *pos = value; - int retval; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { /* FIXME: Check if Total SubTLVs size doesn't exceed 255 */ - if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255) - { - retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - + if ((size_t)(pos - value) + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > max_size) + break; + memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); pos += ISIS_SYS_ID_LEN + 1; memcpy (pos, te_is_neigh->te_metric, 3); @@ -920,9 +1160,17 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length); pos += te_is_neigh->sub_tlvs_length; } + consumed++; } - return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IS_NEIGHBOURS + : TE_IS_NEIGHBOURS, + pos - value, value, stream); + assert(rv == ISIS_OK); + } + return consumed; } int @@ -1100,37 +1348,49 @@ tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream) } -int -tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream) +unsigned int +tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg) { struct listnode *node; struct te_ipv4_reachability *te_reach; u_char value[255]; u_char *pos = value; - u_char prefix_size; - int retval; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach)) { - prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1); + unsigned char prefixlen = te_reach->control & 0x3F; + + if ((size_t)(pos - value) + 5 + PSIZE(prefixlen) > max_size) + break; - if (pos - value + (5 + prefix_size) > 255) - { - retval = - add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } *(u_int32_t *) pos = te_reach->te_metric; pos += 4; *pos = te_reach->control; pos++; - memcpy (pos, &te_reach->prefix_start, prefix_size); - pos += prefix_size; + memcpy (pos, &te_reach->prefix_start, PSIZE(prefixlen)); + pos += PSIZE(prefixlen); + consumed++; + } + + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV4_REACHABILITY + : TE_IPV4_REACHABILITY, + pos - value, value, stream); + assert(rv == ISIS_OK); } - return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); + return consumed; } int @@ -1158,36 +1418,49 @@ tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream) return add_tlv (IPV6_ADDR, pos - value, value, stream); } -int -tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream) +unsigned int +tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg) { struct listnode *node; struct ipv6_reachability *ip6reach; u_char value[255]; u_char *pos = value; - int retval, prefix_octets; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach)) { - if (pos - value + IPV6_MAX_BYTELEN + 6 > 255) - { - retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *(uint32_t *) pos = ip6reach->metric; + if ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) > max_size) + break; + + *(uint32_t *)pos = ip6reach->metric; pos += 4; *pos = ip6reach->control_info; pos++; - prefix_octets = ((ip6reach->prefix_len + 7) / 8); *pos = ip6reach->prefix_len; pos++; - memcpy (pos, ip6reach->prefix, prefix_octets); - pos += prefix_octets; + memcpy (pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len)); + pos += PSIZE(ip6reach->prefix_len); + consumed++; + } + + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV6_REACHABILITY + : IPV6_REACHABILITY, + pos - value, value, stream); + assert(rv == ISIS_OK); } - return add_tlv (IPV6_REACHABILITY, pos - value, value, stream); + return consumed; } int diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h index f899b9e9d..2135f5071 100644 --- a/isisd/isis_tlv.h +++ b/isisd/isis_tlv.h @@ -24,6 +24,8 @@ #ifndef _ZEBRA_ISIS_TLV_H #define _ZEBRA_ISIS_TLV_H +#include "isisd/isis_mt.h" + /* * The list of TLVs we (should) support. * ____________________________________________________________________________ @@ -109,8 +111,12 @@ #define TE_IPV4_REACHABILITY 135 #define DYNAMIC_HOSTNAME 137 #define GRACEFUL_RESTART 211 +#define MT_IS_NEIGHBOURS 222 +#define MT_ROUTER_INFORMATION 229 #define IPV6_ADDR 232 +#define MT_IPV4_REACHABILITY 235 #define IPV6_REACHABILITY 236 +#define MT_IPV6_REACHABILITY 237 #define WAY3_HELLO 240 #define ROUTER_INFORMATION 242 @@ -250,6 +256,12 @@ struct ipv6_reachability #define CTRL_INFO_SUBTLVS 0x20 +struct mt_router_info +{ + ISIS_MT_INFO_FIELDS + bool overload; +}; + /* * Pointer to each tlv type, filled by parse_tlvs() */ @@ -260,8 +272,10 @@ struct tlvs struct nlpids *nlpids; struct te_router_id *router_id; struct list *area_addrs; + struct list *mt_router_info; struct list *is_neighs; struct list *te_is_neighs; + struct list *mt_is_neighs; struct list *es_neighs; struct list *lsp_entries; struct list *prefix_neighs; @@ -270,8 +284,10 @@ struct tlvs struct list *ipv4_int_reachs; struct list *ipv4_ext_reachs; struct list *te_ipv4_reachs; + struct list *mt_ipv4_reachs; struct list *ipv6_addrs; struct list *ipv6_reachs; + struct list *mt_ipv6_reachs; struct isis_passwd auth_info; }; @@ -301,6 +317,7 @@ struct tlvs #define TLVFLAG_TE_ROUTER_ID (1<<19) #define TLVFLAG_CHECKSUM (1<<20) #define TLVFLAG_GRACEFUL_RESTART (1<<21) +#define TLVFLAG_MT_ROUTER_INFORMATION (1<<22) void init_tlvs (struct tlvs *tlvs, uint32_t expected); void free_tlvs (struct tlvs *tlvs); @@ -310,9 +327,10 @@ int parse_tlvs (char *areatag, u_char * stream, int size, int add_tlv (u_char, u_char, u_char *, struct stream *); void free_tlv (void *val); +int tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream); int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream); int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream); -int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream); +unsigned int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg); int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream); int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream); int tlv_add_checksum (struct checksum *checksum, struct stream *stream); @@ -325,9 +343,9 @@ int tlv_add_dynamic_hostname (struct hostname *hostname, int tlv_add_lsp_entries (struct list *lsps, struct stream *stream); int tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream); int tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream); -int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream); +unsigned int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg); int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream); -int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream); +unsigned int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg); int tlv_add_padding (struct stream *stream); diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c index 721959859..1658ca373 100644 --- a/isisd/isis_vty.c +++ b/isisd/isis_vty.c @@ -29,6 +29,7 @@ #include "isis_circuit.h" #include "isis_csm.h" #include "isis_misc.h" +#include "isis_mt.h" #include "isisd.h" static struct isis_circuit * @@ -1271,6 +1272,48 @@ DEFUN (no_psnp_interval_l2, return CMD_SUCCESS; } +DEFUN (circuit_topology, + circuit_topology_cmd, + "isis topology " ISIS_MT_NAMES, + "IS-IS commands\n" + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[2]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, true); +} + +DEFUN (no_circuit_topology, + no_circuit_topology_cmd, + "no isis topology " ISIS_MT_NAMES, + NO_STR + "IS-IS commands\n" + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[3]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, false); +} static int validate_metric_style_narrow (struct vty *vty, struct isis_area *area) @@ -2116,6 +2159,9 @@ isis_vty_init (void) install_element (INTERFACE_NODE, &psnp_interval_l2_cmd); install_element (INTERFACE_NODE, &no_psnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &circuit_topology_cmd); + install_element (INTERFACE_NODE, &no_circuit_topology_cmd); + install_element (ISIS_NODE, &metric_style_cmd); install_element (ISIS_NODE, &no_metric_style_cmd); diff --git a/isisd/isisd.c b/isisd/isisd.c index f226c4a1f..179e43098 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -56,6 +56,7 @@ #include "isisd/isis_zebra.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" struct isis *isis = NULL; @@ -156,6 +157,8 @@ isis_area_create (const char *area_tag) area->lsp_frag_threshold = 90; area->lsp_mtu = DEFAULT_LSP_MTU; + area_mt_init(area); + area->area_tag = strdup (area_tag); listnode_add (isis->area_list, area); area->isis = isis; @@ -296,6 +299,8 @@ isis_area_destroy (struct vty *vty, const char *area_tag) free (area->area_tag); + area_mt_finish(area); + XFREE (MTYPE_ISIS_AREA, area); if (listcount (isis->area_list) == 0) @@ -307,6 +312,33 @@ isis_area_destroy (struct vty *vty, const char *area_tag) return CMD_SUCCESS; } +static void +area_set_mt_enabled(struct isis_area *area, uint16_t mtid, bool enabled) +{ + struct isis_area_mt_setting *setting; + + setting = area_get_mt_setting(area, mtid); + if (setting->enabled != enabled) + { + setting->enabled = enabled; + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } +} + +static void +area_set_mt_overload(struct isis_area *area, uint16_t mtid, bool overload) +{ + struct isis_area_mt_setting *setting; + + setting = area_get_mt_setting(area, mtid); + if (setting->overload != overload) + { + setting->overload = overload; + if (setting->enabled) + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } +} + int area_net_title (struct vty *vty, const char *net_title) { @@ -1626,6 +1658,61 @@ DEFUN (no_net, return area_clear_net_title (vty, argv[idx_word]->arg); } +DEFUN (isis_topology, + isis_topology_cmd, + "topology " ISIS_MT_NAMES " [overload]", + "Configure IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS + "Set overload bit for topology\n") +{ + VTY_DECLVAR_CONTEXT (isis_area, area); + + const char *arg = argv[1]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out (vty, "Cannot configure IPv4 unicast topology%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + area_set_mt_enabled(area, mtid, true); + area_set_mt_overload(area, mtid, (argc == 3)); + return CMD_SUCCESS; +} + +DEFUN (no_isis_topology, + no_isis_topology_cmd, + "no topology " ISIS_MT_NAMES " [overload]", + NO_STR + "Configure IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS + "Set overload bit for topology\n") +{ + VTY_DECLVAR_CONTEXT (isis_area, area); + + const char *arg = argv[2]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out (vty, "Cannot configure IPv4 unicast topology%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + area_set_mt_enabled(area, mtid, false); + area_set_mt_overload(area, mtid, false); + return CMD_SUCCESS; +} + void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu) { area->lsp_mtu = lsp_mtu; @@ -2148,6 +2235,7 @@ isis_config_write (struct vty *vty) write++; } + write += area_write_mt_settings(area, vty); } isis_mpls_te_config_write_router(vty); } @@ -2254,6 +2342,9 @@ isis_init () install_element (ISIS_NODE, &net_cmd); install_element (ISIS_NODE, &no_net_cmd); + install_element (ISIS_NODE, &isis_topology_cmd); + install_element (ISIS_NODE, &no_isis_topology_cmd); + install_element (ISIS_NODE, &log_adj_changes_cmd); install_element (ISIS_NODE, &no_log_adj_changes_cmd); diff --git a/isisd/isisd.h b/isisd/isisd.h index e1d3a69f8..a8cf3673f 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -120,6 +120,8 @@ struct isis_area int ip_circuits; /* logging adjacency changes? */ u_char log_adj_changes; + /* multi topology settings */ + struct list *mt_settings; int ipv6_circuits; /* Counters */ u_int32_t circuit_state_changes; diff --git a/ldpd/lde.c b/ldpd/lde.c index 25f7178b6..859d47431 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -80,10 +80,6 @@ static zebra_capabilities_t _caps_p [] = static struct zebra_privs_t lde_privs = { -#if defined(FRR_USER) && defined(FRR_GROUP) - .user = FRR_USER, - .group = FRR_GROUP, -#endif #if defined(VTY_GROUP) .vty_group = VTY_GROUP, #endif @@ -164,10 +160,8 @@ void lde_init(struct ldpd_init *init) { /* drop privileges */ - if (init->user) - lde_privs.user = init->user; - if (init->group) - lde_privs.group = init->group; + lde_privs.user = init->user; + lde_privs.group = init->group; zprivs_init(&lde_privs); #ifdef HAVE_PLEDGE diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 20cc9f744..017eec250 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -66,10 +66,6 @@ static zebra_capabilities_t _caps_p [] = struct zebra_privs_t ldpe_privs = { -#if defined(FRR_USER) && defined(FRR_GROUP) - .user = FRR_USER, - .group = FRR_GROUP, -#endif #if defined(VTY_GROUP) .vty_group = VTY_GROUP, #endif @@ -143,10 +139,8 @@ void ldpe_init(struct ldpd_init *init) { /* drop privileges */ - if (init->user) - ldpe_privs.user = init->user; - if (init->group) - ldpe_privs.group = init->group; + ldpe_privs.user = init->user; + ldpe_privs.group = init->group; zprivs_init(&ldpe_privs); /* listen on ldpd control socket */ diff --git a/lib/Makefile.am b/lib/Makefile.am index c33893be6..6e3c6d680 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -35,6 +35,7 @@ libfrr_la_SOURCES = \ sha256.c \ module.c \ hook.c \ + frr_pthread.c \ # end BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h @@ -76,6 +77,7 @@ pkginclude_HEADERS = \ hook.h \ libfrr.h \ sha256.h \ + frr_pthread.h \ # end noinst_HEADERS = \ diff --git a/lib/command.c b/lib/command.c index f0d7a64eb..ba1e0bcec 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1055,9 +1055,11 @@ node_parent ( enum node_type node ) case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_IPV4L_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: case BGP_EVPN_NODE: + case BGP_IPV6L_NODE: ret = BGP_NODE; break; case KEYCHAIN_KEY_NODE: @@ -1415,6 +1417,7 @@ cmd_exit (struct vty *vty) break; case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_IPV4L_NODE: case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: @@ -1426,6 +1429,7 @@ cmd_exit (struct vty *vty) case BGP_IPV6_NODE: case BGP_IPV6M_NODE: case BGP_EVPN_NODE: + case BGP_IPV6L_NODE: vty->node = BGP_NODE; break; case LDP_IPV4_NODE: @@ -1493,9 +1497,11 @@ DEFUN (config_end, case BGP_VPNV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_IPV4L_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: case BGP_EVPN_NODE: + case BGP_IPV6L_NODE: case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: diff --git a/lib/command.h b/lib/command.h index c8172caa7..8f6abc85b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -97,8 +97,10 @@ enum node_type BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV4L_NODE, /* BGP IPv4 labeled unicast address family. */ BGP_IPV6_NODE, /* BGP IPv6 address family */ BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ + BGP_IPV6L_NODE, /* BGP IPv6 labeled unicast address family. */ BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ BGP_VRF_POLICY_NODE, /* BGP VRF policy */ diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c new file mode 100644 index 000000000..0408bca09 --- /dev/null +++ b/lib/frr_pthread.c @@ -0,0 +1,184 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#include <zebra.h> +#include <pthread.h> + +#include "frr_pthread.h" +#include "memory.h" +#include "hash.h" + +DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread"); + +static unsigned int next_id = 0; + +/* Hash table of all frr_pthreads along with synchronization primitive(s) and + * hash table callbacks. + * ------------------------------------------------------------------------ */ +static struct hash *pthread_table; +static pthread_mutex_t pthread_table_mtx = PTHREAD_MUTEX_INITIALIZER; + +/* pthread_table->hash_cmp */ +static int pthread_table_hash_cmp(const void *value1, const void *value2) +{ + const struct frr_pthread *tq1 = value1; + const struct frr_pthread *tq2 = value2; + + return (tq1->id == tq2->id); +} + +/* pthread_table->hash_key */ +static unsigned int pthread_table_hash_key(void *value) +{ + return ((struct frr_pthread *)value)->id; +} +/* ------------------------------------------------------------------------ */ + +void frr_pthread_init() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + pthread_table = + hash_create(pthread_table_hash_key, pthread_table_hash_cmp); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +void frr_pthread_finish() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_clean(pthread_table, (void (*)(void *))frr_pthread_destroy); + hash_free(pthread_table); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt = NULL; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + + if (!hash_lookup(pthread_table, &holder)) { + struct frr_pthread *fpt = + XCALLOC(MTYPE_FRR_PTHREAD, + sizeof(struct frr_pthread)); + fpt->id = id; + fpt->master = thread_master_create(); + fpt->start_routine = start_routine; + fpt->stop_routine = stop_routine; + fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); + + hash_get(pthread_table, fpt, hash_alloc_intern); + } + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +void frr_pthread_destroy(struct frr_pthread *fpt) +{ + thread_master_free(fpt->master); + XFREE(MTYPE_FRR_PTHREAD, fpt->name); + XFREE(MTYPE_FRR_PTHREAD, fpt); +} + +struct frr_pthread *frr_pthread_get(unsigned int id) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + fpt = hash_lookup(pthread_table, &holder); + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + int ret; + + if (!fpt) + return -1; + + ret = pthread_create(&fpt->thread, attr, fpt->start_routine, arg); + + /* Per pthread_create(3), the contents of fpt->thread are undefined if + * pthread_create() did not succeed. Reset this value to zero. */ + if (ret < 0) + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + + return ret; +} + +/** + * Calls the stop routine for the frr_pthread and resets any relevant fields. + * + * @param fpt - the frr_pthread to stop + * @param result - pointer to result pointer + * @return the return code from the stop routine + */ +static int frr_pthread_stop_actual(struct frr_pthread *fpt, void **result) +{ + int ret = (*fpt->stop_routine) (result, fpt); + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + return ret; +} + +int frr_pthread_stop(unsigned int id, void **result) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + return frr_pthread_stop_actual(fpt, result); +} + +/** + * Callback for hash_iterate to stop all frr_pthread's. + */ +static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg) +{ + struct frr_pthread *fpt = hb->data; + frr_pthread_stop_actual(fpt, NULL); +} + +void frr_pthread_stop_all() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_iterate(pthread_table, frr_pthread_stop_all_iter, NULL); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +unsigned int frr_pthread_get_id() +{ + return next_id++; +} diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h new file mode 100644 index 000000000..b4954367f --- /dev/null +++ b/lib/frr_pthread.h @@ -0,0 +1,145 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#ifndef _FRR_PTHREAD_H +#define _FRR_PTHREAD_H + +#include <pthread.h> +#include "thread.h" + +struct frr_pthread { + + /* pthread id */ + pthread_t thread; + + /* frr thread identifier */ + unsigned int id; + + /* thread master for this pthread's thread.c event loop */ + struct thread_master *master; + + /* start routine */ + void *(*start_routine) (void *); + + /* stop routine */ + int (*stop_routine) (void **, struct frr_pthread *); + + /* the (hopefully descriptive) name of this thread */ + char *name; +}; + +/* Initializes this module. + * + * Must be called before using any of the other functions. + */ +void frr_pthread_init(void); + +/* Uninitializes this module. + * + * Destroys all registered frr_pthread's and internal data structures. + * + * It is safe to call frr_pthread_init() after this function to reinitialize + * the module. + */ +void frr_pthread_finish(void); + +/* Creates a new frr_pthread. + * + * If the provided ID is already assigned to an existing frr_pthread, the + * return value will be NULL. + * + * @param name - the name of the thread. Doesn't have to be unique, but it + * probably should be. This value is copied and may be safely free'd upon + * return. + * + * @param id - the integral ID of the thread. MUST be unique. The caller may + * use this id to retrieve the thread. + * + * @param start_routine - start routine for the pthread, will be passed to + * pthread_create (see those docs for details) + * + * @param stop_routine - stop routine for the pthread, called to terminate the + * thread. This function should gracefully stop the pthread and clean up any + * thread-specific resources. The passed pointer is used to return a data + * result. + * + * @return the created frr_pthread upon success, or NULL upon failure + */ +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)); + +/* Destroys an frr_pthread. + * + * Assumes that the associated pthread, if any, has already terminated. + * + * @param fpt - the frr_pthread to destroy + */ +void frr_pthread_destroy(struct frr_pthread *fpt); + +/* Gets an existing frr_pthread by its id. + * + * @return frr_thread associated with the provided id, or NULL on error + */ +struct frr_pthread *frr_pthread_get(unsigned int id); + +/* Creates a new pthread and binds it to a frr_pthread. + * + * This function is a wrapper for pthread_create. The first parameter is the + * frr_pthread to bind the created pthread to. All subsequent arguments are + * passed unmodified to pthread_create(). + * + * This function returns the same code as pthread_create(). If the value is + * zero, the provided frr_pthread is bound to a running POSIX thread. If the + * value is less than zero, the provided frr_pthread is guaranteed to be a + * clean instance that may be susbsequently passed to frr_pthread_run(). + * + * @param id - frr_pthread to bind the created pthread to + * @param attr - see pthread_create(3) + * @param arg - see pthread_create(3) + * + * @return see pthread_create(3) + */ +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg); + +/* Stops an frr_pthread with a result. + * + * @param id - frr_pthread to stop + * @param result - where to store the thread's result, if any. May be NULL if a + * result is not needed. + */ +int frr_pthread_stop(unsigned int id, void **result); + +/* Stops all frr_pthread's. */ +void frr_pthread_stop_all(void); + +/* Returns a unique identifier for use with frr_pthread_new(). + * + * Internally, this is an integer that increments after each call to this + * function. Because the number of pthreads created should never exceed INT_MAX + * during the life of the program, there is no overflow protection. If by + * chance this function returns an ID which is already in use, + * frr_pthread_new() will fail when it is provided. + * + * @return unique identifier + */ +unsigned int frr_pthread_get_id(void); + +#endif /* _FRR_PTHREAD_H */ diff --git a/lib/mpls.h b/lib/mpls.h index 13a46e101..f4f360c95 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -85,7 +85,8 @@ enum lsp_types_t { ZEBRA_LSP_NONE = 0, /* No LSP. */ ZEBRA_LSP_STATIC = 1, /* Static LSP. */ - ZEBRA_LSP_LDP = 2 /* LDP LSP. */ + ZEBRA_LSP_LDP = 2, /* LDP LSP. */ + ZEBRA_LSP_BGP = 3 /* BGP LSP. */ }; /* Functions for basic label operations. */ @@ -122,6 +123,11 @@ mpls_lse_decode (mpls_lse_t lse, mpls_label_t *label, *ttl = MPLS_LABEL_TTL(local_lse); } +/* Invalid label index value (when used with BGP Prefix-SID). Should + * match the BGP definition. + */ +#define MPLS_INVALID_LABEL_INDEX 0xFFFFFFFF + /* Printable string for labels (with consideration for reserved values). */ static inline char * diff --git a/lib/nexthop.c b/lib/nexthop.c index 7b8ac95e8..a6420fea3 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -92,6 +92,28 @@ nexthop_type_to_str (enum nexthop_types_t nh_type) return desc[nh_type]; } +/* + * Check if the labels match for the 2 nexthops specified. + */ +int +nexthop_labels_match (struct nexthop *nh1, struct nexthop *nh2) +{ + struct nexthop_label *nhl1, *nhl2; + + nhl1 = nh1->nh_label; + nhl2 = nh2->nh_label; + if ((nhl1 && !nhl2) || (!nhl1 && nhl2)) + return 0; + + if (nhl1->num_labels != nhl2->num_labels) + return 0; + + if (memcmp (nhl1->label, nhl2->label, nhl1->num_labels)) + return 0; + + return 1; +} + struct nexthop * nexthop_new (void) { diff --git a/lib/nexthop.h b/lib/nexthop.h index e66e0eee2..83c5b850b 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -117,6 +117,7 @@ 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); +extern int nexthop_labels_match (struct nexthop *nh1, struct nexthop *nh2); extern const char * nexthop2str (struct nexthop *nexthop, char *str, int size); #endif /*_LIB_NEXTHOP_H */ diff --git a/lib/prefix.h b/lib/prefix.h index eb3ae3daf..786c2bf7e 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -244,6 +244,8 @@ union prefixconstptr /* Count prefix size from mask length */ #define PSIZE(a) (((a) + 7) / (8)) +#define BSIZE(a) ((a) * (8)) + /* Prefix's family member. */ #define PREFIX_FAMILY(p) ((p)->family) diff --git a/lib/stream.c b/lib/stream.c index 301ebc627..32dde1ca0 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -919,6 +919,31 @@ stream_put_prefix (struct stream *s, struct prefix *p) return stream_put_prefix_addpath (s, p, 0, 0); } +/* Put NLRI with label */ +int +stream_put_labeled_prefix (struct stream *s, struct prefix *p, u_char *label) +{ + size_t psize; + + STREAM_VERIFY_SANE(s); + + psize = PSIZE (p->prefixlen); + + if (STREAM_WRITEABLE (s) < (psize + 3)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + stream_putc (s, (p->prefixlen + 24)); + stream_putc(s, label[0]); + stream_putc(s, label[1]); + stream_putc(s, label[2]); + memcpy (s->data + s->endp, &p->u.prefix, psize); + s->endp += psize; + + return (psize + 3); +} /* Read size from fd. */ int diff --git a/lib/stream.h b/lib/stream.h index 1e2bc89b3..b7bf31bf7 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -181,7 +181,8 @@ extern int stream_put_prefix_addpath (struct stream *, struct prefix *, int addpath_encode, u_int32_t addpath_tx_id); extern int stream_put_prefix (struct stream *, struct prefix *); - +extern int stream_put_labeled_prefix (struct stream *, struct prefix *, + u_char *); extern void stream_get (void *, struct stream *, size_t); extern void stream_get_from (void *, struct stream *, size_t, size_t); extern u_char stream_getc (struct stream *); diff --git a/lib/thread.c b/lib/thread.c index e707fc584..d4ed5d1a0 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -41,7 +41,7 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") #include <mach/mach_time.h> #endif -/* Relative time, since startup */ +static pthread_mutex_t cpu_record_mtx = PTHREAD_MUTEX_INITIALIZER; static struct hash *cpu_record = NULL; static unsigned long @@ -137,9 +137,14 @@ cpu_record_print(struct vty *vty, thread_type filter) vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_out(vty, " Type Thread%s", VTY_NEWLINE); - hash_iterate(cpu_record, - (void(*)(struct hash_backet*,void*))cpu_record_hash_print, - args); + + pthread_mutex_lock (&cpu_record_mtx); + { + hash_iterate(cpu_record, + (void(*)(struct hash_backet*,void*))cpu_record_hash_print, + args); + } + pthread_mutex_unlock (&cpu_record_mtx); if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); @@ -216,16 +221,25 @@ cpu_record_hash_clear (struct hash_backet *bucket, if ( !(a->types & *filter) ) return; - hash_release (cpu_record, bucket->data); + pthread_mutex_lock (&cpu_record_mtx); + { + hash_release (cpu_record, bucket->data); + } + pthread_mutex_unlock (&cpu_record_mtx); } static void cpu_record_clear (thread_type filter) { thread_type *tmp = &filter; - hash_iterate (cpu_record, - (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, - tmp); + + pthread_mutex_lock (&cpu_record_mtx); + { + hash_iterate (cpu_record, + (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, + tmp); + } + pthread_mutex_unlock (&cpu_record_mtx); } DEFUN (clear_thread_cpu, @@ -326,16 +340,20 @@ thread_master_create (void) getrlimit(RLIMIT_NOFILE, &limit); - if (cpu_record == NULL) - cpu_record - = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *))cpu_record_hash_cmp); + pthread_mutex_lock (&cpu_record_mtx); + { + if (cpu_record == NULL) + cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *)) + cpu_record_hash_cmp); + } + pthread_mutex_unlock (&cpu_record_mtx); rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); if (rv == NULL) - { - return NULL; - } + return NULL; + + pthread_mutex_init (&rv->mtx, NULL); rv->fd_limit = (int)limit.rlim_cur; rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); @@ -358,6 +376,8 @@ thread_master_create (void) rv->background = pqueue_create(); rv->timer->cmp = rv->background->cmp = thread_timer_cmp; rv->timer->update = rv->background->update = thread_timer_update; + rv->spin = true; + rv->handle_signals = true; #if defined(HAVE_POLL) rv->handler.pfdsize = rv->fd_limit; @@ -498,11 +518,16 @@ thread_queue_free (struct thread_master *m, struct pqueue *queue) void thread_master_free_unused (struct thread_master *m) { - struct thread *t; - while ((t = thread_trim_head(&m->unuse)) != NULL) - { - XFREE(MTYPE_THREAD, t); - } + pthread_mutex_lock (&m->mtx); + { + struct thread *t; + while ((t = thread_trim_head(&m->unuse)) != NULL) + { + pthread_mutex_destroy (&t->mtx); + XFREE(MTYPE_THREAD, t); + } + } + pthread_mutex_unlock (&m->mtx); } /* Stop thread scheduler. */ @@ -516,25 +541,37 @@ thread_master_free (struct thread_master *m) thread_list_free (m, &m->ready); thread_list_free (m, &m->unuse); thread_queue_free (m, m->background); + pthread_mutex_destroy (&m->mtx); #if defined(HAVE_POLL) XFREE (MTYPE_THREAD_MASTER, m->handler.pfds); #endif XFREE (MTYPE_THREAD_MASTER, m); - if (cpu_record) - { - hash_clean (cpu_record, cpu_record_hash_free); - hash_free (cpu_record); - cpu_record = NULL; - } + pthread_mutex_lock (&cpu_record_mtx); + { + if (cpu_record) + { + hash_clean (cpu_record, cpu_record_hash_free); + hash_free (cpu_record); + cpu_record = NULL; + } + } + pthread_mutex_unlock (&cpu_record_mtx); } /* Return remain time in second. */ unsigned long thread_timer_remain_second (struct thread *thread) { - int64_t remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + int64_t remain; + + pthread_mutex_lock (&thread->mtx); + { + remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + } + pthread_mutex_unlock (&thread->mtx); + return remain < 0 ? 0 : remain; } @@ -545,7 +582,11 @@ struct timeval thread_timer_remain(struct thread *thread) { struct timeval remain; - monotime_until(&thread->u.sands, &remain); + pthread_mutex_lock (&thread->mtx); + { + monotime_until(&thread->u.sands, &remain); + } + pthread_mutex_unlock (&thread->mtx); return remain; } @@ -560,8 +601,11 @@ thread_get (struct thread_master *m, u_char type, if (! thread) { thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread)); + /* mutex only needs to be initialized at struct creation. */ + pthread_mutex_init (&thread->mtx, NULL); m->alloc++; } + thread->type = type; thread->add_type = type; thread->master = m; @@ -584,8 +628,12 @@ thread_get (struct thread_master *m, u_char type, { tmp.func = func; tmp.funcname = funcname; - thread->hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); + pthread_mutex_lock (&cpu_record_mtx); + { + thread->hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + pthread_mutex_unlock (&cpu_record_mtx); } thread->hist->total_active++; thread->func = func; @@ -650,15 +698,45 @@ static int fd_select (struct thread_master *m, int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *timer_wait) { int num; + + /* If timer_wait is null here, that means either select() or poll() should + * block indefinitely, unless the thread_master has overriden it. select() + * and poll() differ in the timeout values they interpret as an indefinite + * block; select() requires a null pointer, while poll takes a millisecond + * value of -1. + * + * The thread_master owner has the option of overriding the default behavior + * by setting ->selectpoll_timeout. If the value is positive, it specifies + * the maximum number of milliseconds to wait. If the timeout is -1, it + * specifies that we should never wait and always return immediately even if + * no event is detected. If the value is zero, the behavior is default. + */ + #if defined(HAVE_POLL) - /* recalc timeout for poll. Attention NULL pointer is no timeout with - select, where with poll no timeount is -1 */ int timeout = -1; - if (timer_wait != NULL) + + if (timer_wait != NULL && m->selectpoll_timeout == 0) // use the default value timeout = (timer_wait->tv_sec*1000) + (timer_wait->tv_usec/1000); + else if (m->selectpoll_timeout > 0) // use the user's timeout + timeout = m->selectpoll_timeout; + else if (m->selectpoll_timeout < 0) // effect a poll (return immediately) + timeout = 0; num = poll (m->handler.pfds, m->handler.pfdcount + m->handler.pfdcountsnmp, timeout); #else + struct timeval timeout; + if (m->selectpoll_timeout > 0) // use the user's timeout + { + timeout.tv_sec = m->selectpoll_timeout / 1000; + timeout.tv_usec = (m->selectpoll_timeout % 1000) * 1000; + timer_wait = &timeout; + } + else if (m->selectpoll_timeout < 0) // effect a poll (return immediately) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + timer_wait = &timeout; + } num = select (size, read, write, except, timer_wait); #endif @@ -703,36 +781,43 @@ funcname_thread_add_read_write (int dir, struct thread_master *m, { struct thread *thread = NULL; -#if !defined(HAVE_POLL) - thread_fd_set *fdset = NULL; - if (dir == THREAD_READ) - fdset = &m->handler.readfd; - else - fdset = &m->handler.writefd; -#endif - + pthread_mutex_lock (&m->mtx); + { #if defined (HAVE_POLL) - thread = generic_thread_add(m, func, arg, fd, dir, debugargpass); - - if (thread == NULL) - return NULL; + thread = generic_thread_add(m, func, arg, fd, dir, debugargpass); #else - if (FD_ISSET (fd, fdset)) - { - zlog_warn ("There is already %s fd [%d]", - (dir == THREAD_READ) ? "read" : "write", fd); - return NULL; - } + thread_fd_set *fdset = NULL; + if (dir == THREAD_READ) + fdset = &m->handler.readfd; + else + fdset = &m->handler.writefd; - FD_SET (fd, fdset); - thread = thread_get (m, dir, func, arg, debugargpass); + if (FD_ISSET (fd, fdset)) + { + zlog_warn ("There is already %s fd [%d]", + (dir == THREAD_READ) ? "read" : "write", fd); + } + else + { + FD_SET (fd, fdset); + thread = thread_get (m, dir, func, arg, debugargpass); + } #endif - thread->u.fd = fd; - if (dir == THREAD_READ) - thread_add_fd (m->read, thread); - else - thread_add_fd (m->write, thread); + if (thread) + { + pthread_mutex_lock (&thread->mtx); + { + thread->u.fd = fd; + if (dir == THREAD_READ) + thread_add_fd (m->read, thread); + else + thread_add_fd (m->write, thread); + } + pthread_mutex_unlock (&thread->mtx); + } + } + pthread_mutex_unlock (&m->mtx); return thread; } @@ -753,13 +838,21 @@ funcname_thread_add_timer_timeval (struct thread_master *m, assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); assert (time_relative); - queue = ((type == THREAD_TIMER) ? m->timer : m->background); - thread = thread_get (m, type, func, arg, debugargpass); + pthread_mutex_lock (&m->mtx); + { + queue = ((type == THREAD_TIMER) ? m->timer : m->background); + thread = thread_get (m, type, func, arg, debugargpass); - monotime(&thread->u.sands); - timeradd(&thread->u.sands, time_relative, &thread->u.sands); + pthread_mutex_lock (&thread->mtx); + { + monotime(&thread->u.sands); + timeradd(&thread->u.sands, time_relative, &thread->u.sands); + pqueue_enqueue(thread, queue); + } + pthread_mutex_unlock (&thread->mtx); + } + pthread_mutex_unlock (&m->mtx); - pqueue_enqueue(thread, queue); return thread; } @@ -847,9 +940,17 @@ funcname_thread_add_event (struct thread_master *m, assert (m != NULL); - thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); - thread->u.val = val; - thread_list_add (&m->event, thread); + pthread_mutex_lock (&m->mtx); + { + thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); + pthread_mutex_lock (&thread->mtx); + { + thread->u.val = val; + thread_list_add (&m->event, thread); + } + pthread_mutex_unlock (&thread->mtx); + } + pthread_mutex_unlock (&m->mtx); return thread; } @@ -880,14 +981,22 @@ thread_cancel_read_or_write (struct thread *thread, short int state) fd_clear_read_write (thread); } -/* Cancel thread from scheduler. */ +/** + * Cancel thread from scheduler. + * + * This function is *NOT* MT-safe. DO NOT call it from any other pthread except + * the one which owns thread->master. + */ void thread_cancel (struct thread *thread) { struct thread_list *list = NULL; struct pqueue *queue = NULL; struct thread **thread_array = NULL; - + + pthread_mutex_lock (&thread->master->mtx); + pthread_mutex_lock (&thread->mtx); + switch (thread->type) { case THREAD_READ: @@ -919,15 +1028,14 @@ thread_cancel (struct thread *thread) queue = thread->master->background; break; default: - return; + goto done; break; } if (queue) { assert(thread->index >= 0); - assert(thread == queue->array[thread->index]); - pqueue_remove_at(thread->index, queue); + pqueue_remove (thread, queue); } else if (list) { @@ -943,6 +1051,10 @@ thread_cancel (struct thread *thread) } thread_add_unuse (thread->master, thread); + +done: + pthread_mutex_unlock (&thread->mtx); + pthread_mutex_unlock (&thread->master->mtx); } /* Delete all events which has argument value arg. */ @@ -951,39 +1063,48 @@ thread_cancel_event (struct thread_master *m, void *arg) { unsigned int ret = 0; struct thread *thread; + struct thread *t; - thread = m->event.head; - while (thread) - { - struct thread *t; - - t = thread; - thread = t->next; - - if (t->arg == arg) + pthread_mutex_lock (&m->mtx); + { + thread = m->event.head; + while (thread) + { + t = thread; + pthread_mutex_lock (&t->mtx); { - ret++; - thread_list_delete (&m->event, t); - thread_add_unuse (m, t); + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->event, t); + thread_add_unuse (m, t); + } } - } - - /* thread can be on the ready list too */ - thread = m->ready.head; - while (thread) - { - struct thread *t; - - t = thread; - thread = t->next; + pthread_mutex_unlock (&t->mtx); + } - if (t->arg == arg) + /* thread can be on the ready list too */ + thread = m->ready.head; + while (thread) + { + t = thread; + pthread_mutex_lock (&t->mtx); { - ret++; - thread_list_delete (&m->ready, t); - thread_add_unuse (m, t); + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->ready, t); + thread_add_unuse (m, t); + } } - } + pthread_mutex_unlock (&t->mtx); + } + } + pthread_mutex_unlock (&m->mtx); return ret; } @@ -1143,18 +1264,24 @@ thread_fetch (struct thread_master *m, struct thread *fetch) struct timeval *timer_wait = &timer_val; struct timeval *timer_wait_bg; - while (1) + do { int num = 0; /* Signals pre-empt everything */ - quagga_sigevent_process (); + if (m->handle_signals) + quagga_sigevent_process (); + pthread_mutex_lock (&m->mtx); /* Drain the ready queue of already scheduled jobs, before scheduling * more. */ if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } /* To be fair to all kinds of threads, and avoid starvation, we * need to be careful to consider all thread types for scheduling @@ -1194,8 +1321,12 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (num < 0) { if (errno == EINTR) - continue; /* signal received - process it */ + { + pthread_mutex_unlock (&m->mtx); + continue; /* signal received - process it */ + } zlog_warn ("select() error: %s", safe_strerror (errno)); + pthread_mutex_unlock (&m->mtx); return NULL; } @@ -1215,15 +1346,28 @@ thread_fetch (struct thread_master *m, struct thread *fetch) list at this time. If this is code is uncommented, then background timer threads will not run unless there is nothing else to do. */ if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } #endif /* Background timer/events, lowest priority */ thread_timer_process (m->background, &now); if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); - } + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } + + pthread_mutex_unlock (&m->mtx); + + } while (m->spin); + + return NULL; } unsigned long @@ -1248,13 +1392,23 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) int thread_should_yield (struct thread *thread) { - return monotime_since(&thread->real, NULL) > (int64_t)thread->yield; + int result; + pthread_mutex_lock (&thread->mtx); + { + result = monotime_since(&thread->real, NULL) > (int64_t)thread->yield; + } + pthread_mutex_unlock (&thread->mtx); + return result; } void thread_set_yield_time (struct thread *thread, unsigned long yield_time) { - thread->yield = yield_time; + pthread_mutex_lock (&thread->mtx); + { + thread->yield = yield_time; + } + pthread_mutex_unlock (&thread->mtx); } void @@ -1324,6 +1478,7 @@ funcname_thread_execute (struct thread_master *m, memset (&dummy, 0, sizeof (struct thread)); + pthread_mutex_init (&dummy.mtx, NULL); dummy.type = THREAD_EVENT; dummy.add_type = THREAD_EXECUTE; dummy.master = NULL; @@ -1332,8 +1487,12 @@ funcname_thread_execute (struct thread_master *m, tmp.func = dummy.func = func; tmp.funcname = dummy.funcname = funcname; - dummy.hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); + pthread_mutex_lock (&cpu_record_mtx); + { + dummy.hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + pthread_mutex_unlock (&cpu_record_mtx); dummy.schedfrom = schedfrom; dummy.schedfrom_line = fromln; diff --git a/lib/thread.h b/lib/thread.h index 34adcc4d0..18fd340ba 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -24,6 +24,7 @@ #include <zebra.h> #include "monotime.h" +#include <pthread.h> struct rusage_t { @@ -84,6 +85,10 @@ struct thread_master int fd_limit; struct fd_handler handler; unsigned long alloc; + long selectpoll_timeout; + bool spin; + bool handle_signals; + pthread_mutex_t mtx; }; typedef unsigned char thread_type; @@ -110,6 +115,7 @@ struct thread const char *funcname; const char *schedfrom; int schedfrom_line; + pthread_mutex_t mtx; }; struct cpu_thread_history @@ -746,9 +746,11 @@ vty_end_config (struct vty *vty) case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_IPV4L_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: case BGP_EVPN_NODE: + case BGP_IPV6L_NODE: case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: diff --git a/lib/zclient.c b/lib/zclient.c index d2a518631..e3eadf22a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -733,6 +733,18 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, s = zclient->obuf; stream_reset (s); + /* Some checks for labeled-unicast. The current expectation is that each + * nexthop is accompanied by a label in the case of labeled-unicast. + */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL) && + CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + /* We expect prefixes installed with labels and the number to match + * the number of nexthops. + */ + assert (api->label_num == api->nexthop_num); + } + zclient_create_header (s, cmd, api->vrf_id); /* Put type and nexthop. */ @@ -749,7 +761,7 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) - { + { /* traditional 32-bit data units */ if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE)) { @@ -765,6 +777,9 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, { stream_putc (s, NEXTHOP_TYPE_IPV4); stream_put_in_addr (s, api->nexthop[i]); + /* For labeled-unicast, each nexthop is followed by label. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL)) + stream_putl (s, api->label[i]); } for (i = 0; i < api->ifindex_num; i++) { @@ -800,6 +815,18 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, s = zclient->obuf; stream_reset (s); + /* Some checks for labeled-unicast. The current expectation is that each + * nexthop is accompanied by a label in the case of labeled-unicast. + */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL) && + CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + /* We expect prefixes installed with labels and the number to match + * the number of nexthops. + */ + assert (api->label_num == api->nexthop_num); + } + zclient_create_header (s, cmd, api->vrf_id); /* Put type and nexthop. */ @@ -831,6 +858,9 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, { stream_putc (s, NEXTHOP_TYPE_IPV6); stream_write (s, (u_char *)api->nexthop[i], 16); + /* For labeled-unicast, each nexthop is followed by label. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL)) + stream_putl (s, api->label[i]); } for (i = 0; i < api->ifindex_num; i++) { @@ -869,6 +899,18 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, s = zclient->obuf; stream_reset (s); + /* Some checks for labeled-unicast. The current expectation is that each + * nexthop is accompanied by a label in the case of labeled-unicast. + */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL) && + CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + /* We expect prefixes installed with labels and the number to match + * the number of nexthops. + */ + assert (api->label_num == api->nexthop_num); + } + zclient_create_header (s, cmd, api->vrf_id); /* Put type and nexthop. */ @@ -907,6 +949,9 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, { stream_putc (s, NEXTHOP_TYPE_IPV6); stream_write (s, (u_char *)api->nexthop[i], 16); + /* For labeled-unicast, each nexthop is followed by label. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_LABEL)) + stream_putl (s, api->label[i]); } for (i = 0; i < api->ifindex_num; i++) { @@ -1879,6 +1924,12 @@ zclient_read (struct thread *thread) if (zclient->interface_link_params) (*zclient->interface_link_params) (command, zclient, length); break; + case ZEBRA_FEC_UPDATE: + if (zclient_debug) + zlog_debug("zclient rcvd fec update\n"); + if (zclient->fec_update) + (*zclient->fec_update) (command, zclient, length); + break; default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index d3d0a202c..a54bf420d 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -94,6 +94,9 @@ typedef enum { ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_GET_LABEL_CHUNK, ZEBRA_RELEASE_LABEL_CHUNK, + ZEBRA_FEC_REGISTER, + ZEBRA_FEC_UNREGISTER, + ZEBRA_FEC_UPDATE, } zebra_message_types_t; struct redist_proto @@ -164,6 +167,7 @@ struct zclient int (*redistribute_route_ipv4_del) (int, struct zclient *, uint16_t, vrf_id_t); int (*redistribute_route_ipv6_add) (int, struct zclient *, uint16_t, vrf_id_t); int (*redistribute_route_ipv6_del) (int, struct zclient *, uint16_t, vrf_id_t); + int (*fec_update) (int, struct zclient *, uint16_t); }; /* Zebra API message flag. */ @@ -174,6 +178,7 @@ struct zclient #define ZAPI_MESSAGE_TAG 0x10 #define ZAPI_MESSAGE_MTU 0x20 #define ZAPI_MESSAGE_SRCPFX 0x40 +#define ZAPI_MESSAGE_LABEL 0x80 /* Zserv protocol message header */ struct zserv_header @@ -206,6 +211,9 @@ struct zapi_ipv4 u_char ifindex_num; ifindex_t *ifindex; + u_char label_num; + unsigned int *label; + u_char distance; u_int32_t metric; @@ -297,6 +305,9 @@ struct zapi_ipv6 u_char ifindex_num; ifindex_t *ifindex; + u_char label_num; + unsigned int *label; + u_char distance; u_int32_t metric; diff --git a/lib/zebra.h b/lib/zebra.h index 760264d75..cd72dc67f 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -393,6 +393,9 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +/* Zebra FEC flags. */ +#define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x1 + #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ #endif @@ -413,11 +416,13 @@ typedef enum { #define SAFI_ENCAP 5 #define SAFI_RESERVED_5 5 #define SAFI_EVPN 6 -#define SAFI_MAX 7 +#define SAFI_LABELED_UNICAST 7 +#define SAFI_MAX 8 #define IANA_SAFI_RESERVED 0 #define IANA_SAFI_UNICAST 1 #define IANA_SAFI_MULTICAST 2 +#define IANA_SAFI_LABELED_UNICAST 4 #define IANA_SAFI_ENCAP 7 #define IANA_SAFI_MPLS_VPN 128 @@ -512,6 +517,8 @@ static inline safi_t safi_iana2int (safi_t safi) return SAFI_ENCAP; if (safi == IANA_SAFI_EVPN) return SAFI_EVPN; + if (safi == IANA_SAFI_LABELED_UNICAST) + return SAFI_LABELED_UNICAST; return SAFI_MAX; } @@ -527,6 +534,8 @@ static inline safi_t safi_int2iana (safi_t safi) return IANA_SAFI_ENCAP; if (safi == SAFI_EVPN) return IANA_SAFI_EVPN; + if (safi == SAFI_LABELED_UNICAST) + return IANA_SAFI_LABELED_UNICAST; return IANA_SAFI_RESERVED; } diff --git a/tools/etc/iproute2/rt_protos.d/frr.conf b/tools/etc/iproute2/rt_protos.d/frr.conf new file mode 100644 index 000000000..3f55b1126 --- /dev/null +++ b/tools/etc/iproute2/rt_protos.d/frr.conf @@ -0,0 +1,8 @@ +# Additional protocol strings defined by frr for each of its daemons + +186 bgp +187 isis +188 ospf +189 rip +190 ripng +191 static @@ -532,8 +532,15 @@ case "$1" in fi if [ -z "$dmn" -o "$dmn" = "zebra" ]; then - echo "Removing all routes made by zebra." + echo "Removing all routes made by FRR." + ip route flush proto bgp + ip route flush proto ospf + ip route flush proto static + ip route flush proto rip + ip route flush proto ripng ip route flush proto zebra + ip route flush proto isis + else [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 0af17abea..d545a73be 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -310,7 +310,8 @@ vtysh_execute_func (const char *line, int pager) || saved_node == BGP_ENCAP_NODE || saved_node == BGP_ENCAPV6_NODE || saved_node == BGP_IPV4_NODE || saved_node == BGP_IPV6_NODE || saved_node == BGP_IPV4M_NODE - || saved_node == BGP_IPV6M_NODE || saved_node == BGP_EVPN_NODE) + || saved_node == BGP_IPV4L_NODE || saved_node == BGP_IPV6L_NODE + || saved_node == BGP_IPV6M_NODE || saved_node == BGP_EVPN_NODE) && (tried == 1)) { vtysh_execute("exit-address-family"); @@ -948,6 +949,12 @@ static struct cmd_node bgp_ipv4m_node = "%s(config-router-af)# " }; +static struct cmd_node bgp_ipv4l_node = +{ + BGP_IPV4L_NODE, + "%s(config-router-af)# " +}; + static struct cmd_node bgp_ipv6_node = { BGP_IPV6_NODE, @@ -966,6 +973,12 @@ static struct cmd_node bgp_evpn_node = "%s(config-router-af)# " }; +static struct cmd_node bgp_ipv6l_node = +{ + BGP_IPV6L_NODE, + "%s(config-router-af)# " +}; + static struct cmd_node bgp_vnc_defaults_node = { BGP_VNC_DEFAULTS_NODE, @@ -1122,7 +1135,7 @@ DEFUNSH (VTYSH_BGPD, "address-family vpnv4 [unicast]", "Enter Address Family command mode\n" "Address Family\n" - "Address Family Modifier\n") + "Address Family modifier\n") { vty->node = BGP_VPNV4_NODE; return CMD_SUCCESS; @@ -1134,7 +1147,7 @@ DEFUNSH (VTYSH_BGPD, "address-family vpnv6 [unicast]", "Enter Address Family command mode\n" "Address Family\n" - "Address Family Modifier\n") + "Address Family modifier\n") { vty->node = BGP_VPNV6_NODE; return CMD_SUCCESS; @@ -1164,15 +1177,16 @@ DEFUNSH (VTYSH_BGPD, } DEFUNSH (VTYSH_BGPD, - address_family_ipv4_unicast, - address_family_ipv4_unicast_cmd, - "address-family ipv4 [<unicast|multicast|vpn|encap>]", - "Enter Address Family command mode\n" - "Address Family\n" - "Address Family Modifier\n" - "Address Family Modifier\n" - "Address Family Modifier\n" - "Address Family Modifier\n") + address_family_ipv4, + address_family_ipv4_cmd, + "address-family ipv4 [<unicast|multicast|vpn|encap|labeled-unicast>]", + "Enter Address Family command mode\n" + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n") { int idx = 0; @@ -1185,6 +1199,9 @@ DEFUNSH (VTYSH_BGPD, else if (argv_find (argv, argc, "vpn", &idx)) vty->node = BGP_VPNV4_NODE; + else if (argv_find (argv, argc, "labeled-unicast", &idx)) + vty->node = BGP_IPV4L_NODE; + else vty->node = BGP_IPV4_NODE; @@ -1192,15 +1209,16 @@ DEFUNSH (VTYSH_BGPD, } DEFUNSH (VTYSH_BGPD, - address_family_ipv6, - address_family_ipv6_cmd, - "address-family ipv6 [<unicast|multicast|vpn|encap>]", - "Enter Address Family command mode\n" - "Address Family\n" - "Address Family Modifier\n" - "Address Family Modifier\n" - "Address Family Modifier\n" - "Address Family Modifier\n") + address_family_ipv6, + address_family_ipv6_cmd, + "address-family ipv6 [<unicast|multicast|vpn|encap|labeled-unicast>]", + "Enter Address Family command mode\n" + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n") { int idx = 0; @@ -1213,6 +1231,9 @@ DEFUNSH (VTYSH_BGPD, else if (argv_find (argv, argc, "vpn", &idx)) vty->node = BGP_VPNV6_NODE; + else if (argv_find (argv, argc, "labeled-unicast", &idx)) + vty->node = BGP_IPV6L_NODE; + else vty->node = BGP_IPV6_NODE; @@ -1550,8 +1571,10 @@ vtysh_exit (struct vty *vty) case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_IPV4L_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_IPV6L_NODE: case BGP_VRF_POLICY_NODE: case BGP_EVPN_NODE: case BGP_VNC_DEFAULTS_NODE: @@ -1610,11 +1633,13 @@ DEFUNSH (VTYSH_BGPD, { if (vty->node == BGP_IPV4_NODE || vty->node == BGP_IPV4M_NODE + || vty->node == BGP_IPV4L_NODE || vty->node == BGP_VPNV4_NODE || vty->node == BGP_VPNV6_NODE || vty->node == BGP_ENCAP_NODE || vty->node == BGP_ENCAPV6_NODE || vty->node == BGP_IPV6_NODE + || vty->node == BGP_IPV6L_NODE || vty->node == BGP_IPV6M_NODE) vty->node = BGP_NODE; return CMD_SUCCESS; @@ -3160,8 +3185,10 @@ vtysh_init_vty (void) install_node (&bgp_encapv6_node, NULL); install_node (&bgp_ipv4_node, NULL); install_node (&bgp_ipv4m_node, NULL); + install_node (&bgp_ipv4l_node, NULL); install_node (&bgp_ipv6_node, NULL); install_node (&bgp_ipv6m_node, NULL); + install_node (&bgp_ipv6l_node, NULL); install_node (&bgp_vrf_policy_node, NULL); install_node (&bgp_evpn_node, NULL); install_node (&bgp_vnc_defaults_node, NULL); @@ -3199,9 +3226,11 @@ vtysh_init_vty (void) vtysh_install_default (BGP_ENCAPV6_NODE); vtysh_install_default (BGP_IPV4_NODE); vtysh_install_default (BGP_IPV4M_NODE); + vtysh_install_default (BGP_IPV4L_NODE); vtysh_install_default (BGP_IPV6_NODE); vtysh_install_default (BGP_IPV6M_NODE); vtysh_install_default (BGP_EVPN_NODE); + vtysh_install_default (BGP_IPV6L_NODE); #if ENABLE_BGP_VNC vtysh_install_default (BGP_VRF_POLICY_NODE); vtysh_install_default (BGP_VNC_DEFAULTS_NODE); @@ -3273,11 +3302,15 @@ vtysh_init_vty (void) install_element (BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV4M_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_IPV4L_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_IPV4L_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV6_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_EVPN_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_IPV6L_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_IPV6L_NODE, &vtysh_quit_bgpd_cmd); #if defined (ENABLE_BGP_VNC) install_element (BGP_VRF_POLICY_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_VRF_POLICY_NODE, &vtysh_quit_bgpd_cmd); @@ -3317,12 +3350,14 @@ vtysh_init_vty (void) install_element (BGP_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV4_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV4M_NODE, &vtysh_end_all_cmd); + install_element (BGP_IPV4L_NODE, &vtysh_end_all_cmd); install_element (BGP_VPNV4_NODE, &vtysh_end_all_cmd); install_element (BGP_VPNV6_NODE, &vtysh_end_all_cmd); install_element (BGP_ENCAP_NODE, &vtysh_end_all_cmd); install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd); + install_element (BGP_IPV6L_NODE, &vtysh_end_all_cmd); install_element (BGP_VRF_POLICY_NODE, &vtysh_end_all_cmd); install_element (BGP_EVPN_NODE, &vtysh_end_all_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); @@ -3379,7 +3414,7 @@ vtysh_init_vty (void) install_element (BGP_NODE, &vnc_nve_group_cmd); install_element (BGP_NODE, &vnc_l2_group_cmd); #endif - install_element (BGP_NODE, &address_family_ipv4_unicast_cmd); + install_element (BGP_NODE, &address_family_ipv4_cmd); install_element (BGP_NODE, &address_family_ipv6_cmd); install_element (BGP_NODE, &address_family_evpn_cmd); install_element (BGP_VPNV4_NODE, &exit_address_family_cmd); @@ -3388,9 +3423,11 @@ vtysh_init_vty (void) install_element (BGP_ENCAPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV4_NODE, &exit_address_family_cmd); install_element (BGP_IPV4M_NODE, &exit_address_family_cmd); + install_element (BGP_IPV4L_NODE, &exit_address_family_cmd); install_element (BGP_IPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); install_element (BGP_EVPN_NODE, &exit_address_family_cmd); + install_element (BGP_IPV6L_NODE, &exit_address_family_cmd); install_element (BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd); diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b46..6c5e06906 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -31,7 +31,7 @@ zebra_SOURCES = \ redistribute.c debug.c rtadv.c zebra_vty.c \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c \ zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ - zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ + zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls_vty.c \ zebra_mroute.c \ label_manager.c \ # end @@ -40,7 +40,7 @@ testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c + zebra_memory.c zebra_mpls_vty.c zebra_mpls_null.c noinst_HEADERS = \ zebra_memory.h \ @@ -88,7 +88,7 @@ EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ rt_socket.c rtread_netlink.c rtread_sysctl.c \ rtread_getmsg.c kernel_socket.c kernel_netlink.c \ ioctl.c ioctl_solaris.c \ - zebra_mpls_netlink.c zebra_mpls_openbsd.c \ + zebra_mpls_netlink.c zebra_mpls_openbsd.c zebra_mpls.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB client : client_main.o ../lib/libfrr.la diff --git a/zebra/interface.c b/zebra/interface.c index 1d015e858..1eefe1339 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -691,6 +691,7 @@ if_delete_update (struct interface *ifp) for setting ifindex to IFINDEX_INTERNAL after processing the interface deletion message. */ ifp->ifindex = IFINDEX_INTERNAL; + ifp->node = NULL; } /* VRF change for an interface */ diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 49394bd6f..e97420321 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -100,6 +100,11 @@ static const struct message rtproto_str[] = { {RTPROT_BIRD, "BIRD"}, #endif /* RTPROT_BIRD */ {RTPROT_MROUTED, "mroute"}, + {RTPROT_BGP, "BGP"}, + {RTPROT_OSPF, "OSPF"}, + {RTPROT_ISIS, "IS-IS"}, + {RTPROT_RIP, "RIP"}, + {RTPROT_RIPNG, "RIPNG"}, {0, NULL} }; diff --git a/zebra/rib.h b/zebra/rib.h index 5381d76b9..8f6cff0d8 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -372,7 +372,7 @@ extern void rib_init (void); extern unsigned long rib_score_proto (u_char proto, u_short instance); extern void rib_queue_add (struct route_node *rn); extern void meta_queue_free (struct meta_queue *mq); - +extern int zebra_rib_labeled_unicast (struct rib *rib); extern struct route_table *rib_table_ipv6; extern void rib_unlink (struct route_node *, struct rib *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a544593dd..77f03a2c6 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -103,6 +103,47 @@ struct gw_family_t union g_addr gate; }; +static inline int is_selfroute(int proto) +{ + if ((proto == RTPROT_BGP) || (proto == RTPROT_OSPF) || + (proto == RTPROT_STATIC) || (proto == RTPROT_ZEBRA) || + (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG)) { + return 1; + } + + return 0; +} + +static inline int get_rt_proto(int proto) +{ + switch (proto) { + case ZEBRA_ROUTE_BGP: + proto = RTPROT_BGP; + break; + case ZEBRA_ROUTE_OSPF: + case ZEBRA_ROUTE_OSPF6: + proto = RTPROT_OSPF; + break; + case ZEBRA_ROUTE_STATIC: + proto = RTPROT_STATIC; + break; + case ZEBRA_ROUTE_ISIS: + proto = RTPROT_ISIS; + break; + case ZEBRA_ROUTE_RIP: + proto = RTPROT_RIP; + break; + case ZEBRA_ROUTE_RIPNG: + proto = RTPROT_RIPNG; + break; + default: + proto = RTPROT_ZEBRA; + break; + } + + return proto; +} + /* Pending: create an efficient table_id (in a tree/hash) based lookup) */ @@ -171,7 +212,7 @@ netlink_route_change_read_unicast (struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; if (!startup && - rtm->rtm_protocol == RTPROT_ZEBRA && + is_selfroute (rtm->rtm_protocol) && h->nlmsg_type == RTM_NEWROUTE) return 0; @@ -196,7 +237,7 @@ netlink_route_change_read_unicast (struct sockaddr_nl *snl, struct nlmsghdr *h, } /* Route which inserted by Zebra. */ - if (rtm->rtm_protocol == RTPROT_ZEBRA) + if (is_selfroute(rtm->rtm_protocol)) flags |= ZEBRA_FLAG_SELFROUTE; if (tb[RTA_OIF]) @@ -1137,7 +1178,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct prefix *src_p, req.r.rtm_family = family; req.r.rtm_dst_len = p->prefixlen; req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; - req.r.rtm_protocol = RTPROT_ZEBRA; + req.r.rtm_protocol = get_rt_proto(rib->type); req.r.rtm_scope = RT_SCOPE_UNIVERSE; if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 93ee622e3..af58a0f0d 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -28,6 +28,14 @@ #define NL_DEFAULT_ROUTE_METRIC 20 +/* Additional protocol strings to push into routes */ +#define RTPROT_BGP 186 +#define RTPROT_ISIS 187 +#define RTPROT_OSPF 188 +#define RTPROT_RIP 189 +#define RTPROT_RIPNG 190 + + extern void clear_nhlfe_installed (zebra_lsp_t *lsp); extern int diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5a3ed7545..76263024c 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -47,6 +47,7 @@ #include "zebra/zebra_mpls.h" DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") +DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC 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") @@ -58,6 +59,32 @@ int mpls_enabled; extern struct zebra_t zebrad; /* static function declarations */ + +static void +fec_evaluate (struct zebra_vrf *zvrf, int add); +static u_int32_t +fec_derive_label_from_index (struct zebra_vrf *vrf, zebra_fec_t *fec); +static int +lsp_install (struct zebra_vrf *zvrf, mpls_label_t label, + struct route_node *rn, struct rib *rib); +static int +lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t label); +static int +fec_change_update_lsp (struct zebra_vrf *zvrf, zebra_fec_t *fec, mpls_label_t old_label); +static int +fec_send (zebra_fec_t *fec, struct zserv *client); +static void +fec_update_clients (zebra_fec_t *fec); +static void +fec_print (zebra_fec_t *fec, struct vty *vty); +static zebra_fec_t * +fec_find (struct route_table *table, struct prefix *p); +static zebra_fec_t * +fec_add (struct route_table *table, struct prefix *p, mpls_label_t label, + u_int32_t flags, u_int32_t label_index); +static int +fec_del (zebra_fec_t *fec); + static unsigned int label_hash (void *p); static int @@ -68,6 +95,7 @@ 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 @@ -84,21 +112,24 @@ 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); + union g_addr *gate, 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); + 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); + ifindex_t ifindex, mpls_label_t out_label); static int nhlfe_del (zebra_nhlfe_t *snhlfe); +static void +nhlfe_out_label_update (zebra_nhlfe_t *nhlfe, struct nexthop_label *nh_label); static int mpls_lsp_uninstall_all (struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); @@ -112,14 +143,13 @@ static void * slsp_alloc (void *p); static int snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex); + union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t * snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex); + union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t * snhlfe_add (zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex, - mpls_label_t out_label); + union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label); static int snhlfe_del (zebra_snhlfe_t *snhlfe); static int @@ -135,6 +165,469 @@ mpls_processq_init (struct zebra_t *zebra); /* Static functions */ /* + * Install label forwarding entry based on labeled-route entry. + */ +static int +lsp_install (struct zebra_vrf *zvrf, mpls_label_t label, + struct route_node *rn, struct rib *rib) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; + enum lsp_types_t lsp_type; + char buf[BUFSIZ]; + int added, changed; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* See if route entry is selected; we really expect only 1 entry here. */ + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + return 0; + + lsp_type = lsp_type_from_rib_type (rib->type); + added = changed = 0; + + /* Locate or allocate LSP entry. */ + tmp_ile.in_label = label; + lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + + /* For each active nexthop, create NHLFE. Note that we deliberately skip + * recursive nexthops right now, because intermediate hops won't understand + * the label advertised by the recursive nexthop (plus we don't have the + * logic yet to push multiple labels). + */ + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + { + /* Skip inactive and recursive entries. */ + if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + nhlfe = nhlfe_find (lsp, lsp_type, nexthop->type, &nexthop->gate, + nexthop->ifindex); + if (nhlfe) + { + /* Clear deleted flag (in case it was set) */ + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + if (nexthop_labels_match (nhlfe->nexthop, nexthop)) + /* No change */ + continue; + + + if (IS_ZEBRA_DEBUG_MPLS) + { + nhlfe2str (nhlfe, buf, BUFSIZ); + zlog_debug ("LSP in-label %u type %d nexthop %s " + "out-label changed", + lsp->ile.in_label, lsp_type, buf); + } + + /* Update out label, trigger processing. */ + nhlfe_out_label_update (nhlfe, nexthop->nh_label); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + changed++; + } + else + { + /* Add LSP entry to this nexthop */ + nhlfe = nhlfe_add (lsp, lsp_type, nexthop->type, + &nexthop->gate, nexthop->ifindex, + nexthop->nh_label->label[0]); + 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", + lsp->ile.in_label, lsp_type, buf, + nexthop->nh_label->label[0]); + } + + lsp->addr_family = NHLFE_FAMILY (nhlfe); + + /* Mark NHLFE as changed. */ + SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + added++; + } + } + + /* Queue LSP for processing if necessary. If no NHLFE got added (special + * case), delete the LSP entry; this case results in somewhat ugly logging. + */ + if (added || changed) + { + 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; +} + +/* + * Uninstall all non-static NHLFEs of a label forwarding entry. If all + * NHLFEs are removed, the entire entry is deleted. + */ +static int +lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe, *nhlfe_next; + 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 = 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 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", + label, nhlfe->type, buf, nhlfe->flags); + } + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)) + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED); + } + else + { + nhlfe_del (nhlfe); + } + } + + /* Queue LSP for processing, if needed, else delete. */ + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) + { + 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 ("Del 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; +} + +/* + * This function is invoked upon change to label block configuration; it + * will walk all registered FECs with label-index and appropriately update + * their local labels and trigger client updates. + */ +static void +fec_evaluate (struct zebra_vrf *zvrf, int add) +{ + struct route_node *rn; + zebra_fec_t *fec; + u_int32_t old_label, new_label; + int af; + char buf[BUFSIZ]; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + if (zvrf->fec_table[af] == NULL) + continue; + + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + if ((fec = rn->info) == NULL) + continue; + + /* Skip configured FECs and those without a label index. */ + if (fec->flags & FEC_FLAG_CONFIGURED || + fec->label_index == MPLS_INVALID_LABEL_INDEX) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + prefix2str(&rn->p, buf, BUFSIZ); + + /* Save old label, determine new label. */ + old_label = fec->label; + if (add) + { + new_label = zvrf->mpls_srgb.start_label + fec->label_index; + if (new_label >= zvrf->mpls_srgb.end_label) + new_label = MPLS_INVALID_LABEL; + } + else + new_label = MPLS_INVALID_LABEL; + + /* If label has changed, update FEC and clients. */ + if (new_label == old_label) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Update fec %s new label %u upon label block %s", + buf, new_label, add ? "ADD" : "DEL"); + + fec->label = new_label; + fec_update_clients (fec); + + /* Update label forwarding entries appropriately */ + fec_change_update_lsp (zvrf, fec, old_label); + } + } +} + +/* + * Derive (if possible) and update the local label for the FEC based on + * its label index. The index is "acceptable" if it falls within the + * globally configured label block (SRGB). + */ +static u_int32_t +fec_derive_label_from_index (struct zebra_vrf *zvrf, zebra_fec_t *fec) +{ + u_int32_t label; + + if (fec->label_index != MPLS_INVALID_LABEL_INDEX && + zvrf->mpls_srgb.start_label && + ((label = zvrf->mpls_srgb.start_label + fec->label_index) < + zvrf->mpls_srgb.end_label)) + fec->label = label; + else + fec->label = MPLS_INVALID_LABEL; + + return fec->label; +} + +/* + * There is a change for this FEC. Install or uninstall label forwarding + * entries, as appropriate. + */ +static int +fec_change_update_lsp (struct zebra_vrf *zvrf, zebra_fec_t *fec, mpls_label_t old_label) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + afi_t afi; + + /* Uninstall label forwarding entry, if previously installed. */ + if (old_label != MPLS_INVALID_LABEL && + old_label != MPLS_IMP_NULL_LABEL) + lsp_uninstall (zvrf, old_label); + + /* Install label forwarding entry corr. to new label, if needed. */ + if (fec->label == MPLS_INVALID_LABEL || + fec->label == MPLS_IMP_NULL_LABEL) + return 0; + + afi = family2afi(PREFIX_FAMILY(&fec->rn->p)); + table = zebra_vrf_table (afi, SAFI_UNICAST, zvrf_id (zvrf)); + if (!table) + return 0; + + /* See if labeled route exists. */ + rn = route_node_lookup (table, &fec->rn->p); + if (!rn) + return 0; + + RNODE_FOREACH_RIB (rn, rib) + { + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (!rib || !zebra_rib_labeled_unicast (rib)) + return 0; + + if (lsp_install (zvrf, fec->label, rn, rib)) + return -1; + + return 0; +} + +/* + * Inform about FEC to a registered client. + */ +static int +fec_send (zebra_fec_t *fec, struct zserv *client) +{ + struct stream *s; + struct route_node *rn; + + rn = fec->rn; + + /* Get output stream. */ + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_FEC_UPDATE, VRF_DEFAULT); + + stream_putw(s, rn->p.family); + stream_put_prefix (s, &rn->p); + stream_putl(s, fec->label); + stream_putw_at(s, 0, stream_get_endp(s)); + return zebra_server_send_message(client); +} + +/* + * Update all registered clients about this FEC. Caller should've updated + * FEC and ensure no duplicate updates. + */ +static void +fec_update_clients (zebra_fec_t *fec) +{ + struct listnode *node; + struct zserv *client; + + for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, client)) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Update client %s", zebra_route_string(client->proto)); + fec_send(fec, client); + } +} + + +/* + * Print a FEC-label binding entry. + */ +static void +fec_print (zebra_fec_t *fec, struct vty *vty) +{ + struct route_node *rn; + struct listnode *node; + struct zserv *client; + char buf[BUFSIZ]; + + rn = fec->rn; + prefix2str(&rn->p, buf, BUFSIZ); + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + vty_out(vty, " Label: %s", label2str(fec->label, buf, BUFSIZ)); + if (fec->label_index != MPLS_INVALID_LABEL_INDEX) + vty_out(vty, ", Label Index: %u", fec->label_index); + vty_out(vty, "%s", VTY_NEWLINE); + if (!list_isempty(fec->client_list)) + { + vty_out(vty, " Client list:"); + for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, client)) + vty_out(vty, " %s(fd %d)", + zebra_route_string(client->proto), client->sock); + vty_out(vty, "%s", VTY_NEWLINE); + } +} + +/* + * Locate FEC-label binding that matches with passed info. + */ +static zebra_fec_t * +fec_find (struct route_table *table, struct prefix *p) +{ + struct route_node *rn; + + apply_mask (p); + rn = route_node_lookup(table, p); + if (!rn) + return NULL; + + route_unlock_node(rn); + return (rn->info); +} + +/* + * Add a FEC. This may be upon a client registering for a binding + * or when a binding is configured. + */ +static zebra_fec_t * +fec_add (struct route_table *table, struct prefix *p, + mpls_label_t label, u_int32_t flags, u_int32_t label_index) +{ + struct route_node *rn; + zebra_fec_t *fec; + + apply_mask (p); + + /* Lookup (or add) route node.*/ + rn = route_node_get (table, p); + if (!rn) + return NULL; + + fec = rn->info; + + if (!fec) + { + fec = XCALLOC (MTYPE_FEC, sizeof(zebra_fec_t)); + if (!fec) + return NULL; + + rn->info = fec; + fec->rn = rn; + fec->label = label; + fec->client_list = list_new(); + } + else + route_unlock_node (rn); /* for the route_node_get */ + + fec->label_index = label_index; + fec->flags = flags; + + return fec; +} + +/* + * Delete a FEC. This may be upon the last client deregistering for + * a FEC and no binding exists or when the binding is deleted and there + * are no registered clients. + */ +static int +fec_del (zebra_fec_t *fec) +{ + list_free (fec->client_list); + fec->rn->info = NULL; + route_unlock_node (fec->rn); + XFREE (MTYPE_FEC, fec); + return 0; +} + +/* * Hash function for label. */ static unsigned int @@ -602,7 +1095,7 @@ 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) + union g_addr *gate, ifindex_t ifindex) { struct nexthop *nhop; int cmp = 1; @@ -644,7 +1137,7 @@ nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, 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) + ifindex_t ifindex) { zebra_nhlfe_t *nhlfe; @@ -655,7 +1148,7 @@ nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, { if (nhlfe->type != lsp_type) continue; - if (!nhlfe_nhop_match (nhlfe, gtype, gate, ifname, ifindex)) + if (!nhlfe_nhop_match (nhlfe, gtype, gate, ifindex)) break; } @@ -669,7 +1162,7 @@ nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type, 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) + ifindex_t ifindex, mpls_label_t out_label) { zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; @@ -759,6 +1252,15 @@ nhlfe_del (zebra_nhlfe_t *nhlfe) return 0; } +/* + * Update label for NHLFE entry. + */ +static void +nhlfe_out_label_update (zebra_nhlfe_t *nhlfe, struct nexthop_label *nh_label) +{ + nhlfe->nexthop->nh_label->label[0] = nh_label->label[0]; +} + static int mpls_lsp_uninstall_all (struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type) @@ -1026,7 +1528,7 @@ slsp_cmp (zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) */ static int snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) + union g_addr *gate, ifindex_t ifindex) { int cmp = 1; @@ -1058,7 +1560,7 @@ snhlfe_match (zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, */ static zebra_snhlfe_t * snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) + union g_addr *gate, ifindex_t ifindex) { zebra_snhlfe_t *snhlfe; @@ -1067,7 +1569,7 @@ snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) { - if (!snhlfe_match (snhlfe, gtype, gate, ifname, ifindex)) + if (!snhlfe_match (snhlfe, gtype, gate, ifindex)) break; } @@ -1081,7 +1583,7 @@ snhlfe_find (zebra_slsp_t *slsp, enum nexthop_types_t gtype, */ static zebra_snhlfe_t * snhlfe_add (zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex, + union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label) { zebra_snhlfe_t *snhlfe; @@ -1230,7 +1732,7 @@ mpls_str2label (const char *label_str, u_int8_t *num_labels, *num_labels = 0; for (i = 0; i < MPLS_MAX_LABELS; i++) { - u_int32_t label; + mpls_label_t label; label = strtoul(label_str, &endp, 0); @@ -1275,6 +1777,486 @@ mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, } /* + * Install dynamic LSP entry. + */ +int +zebra_mpls_lsp_install (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib) +{ + struct route_table *table; + zebra_fec_t *fec; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; + if (!table) + return -1; + + /* See if there is a configured label binding for this FEC. */ + fec = fec_find (table, &rn->p); + if (!fec || fec->label == MPLS_INVALID_LABEL) + return 0; + + /* We cannot install a label forwarding entry if local label is the + * implicit-null label. + */ + if (fec->label == MPLS_IMP_NULL_LABEL) + return 0; + + if (lsp_install (zvrf, fec->label, rn, rib)) + return -1; + + return 0; +} + +/* + * Uninstall dynamic LSP entry, if any. + */ +int +zebra_mpls_lsp_uninstall (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib) +{ + struct route_table *table; + zebra_fec_t *fec; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; + if (!table) + return -1; + + /* See if there is a configured label binding for this FEC. */ + fec = fec_find (table, &rn->p); + if (!fec || fec->label == MPLS_INVALID_LABEL) + return 0; + + /* Uninstall always removes all dynamic NHLFEs. */ + return lsp_uninstall (zvrf, fec->label); +} + +/* + * Registration from a client for the label binding for a FEC. If a binding + * already exists, it is informed to the client. + * NOTE: If there is a manually configured label binding, that is used. + * Otherwise, if aa label index is specified, it means we have to allocate the + * label from a locally configured label block (SRGB), if one exists and index + * is acceptable. + */ +int +zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, + u_int32_t label_index, struct zserv *client) +{ + struct route_table *table; + zebra_fec_t *fec; + char buf[BUFSIZ]; + int new_client; + int label_change = 0; + u_int32_t old_label; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; + if (!table) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + prefix2str(p, buf, BUFSIZ); + + /* Locate FEC */ + fec = fec_find (table, p); + if (!fec) + { + fec = fec_add (table, p, MPLS_INVALID_LABEL, 0, label_index); + if (!fec) + { + prefix2str(p, buf, BUFSIZ); + zlog_err("Failed to add FEC %s upon register, client %s", + buf, zebra_route_string(client->proto)); + return -1; + } + + old_label = MPLS_INVALID_LABEL; + new_client = 1; + } + else + { + /* Client may register same FEC with different label index. */ + new_client = (listnode_lookup(fec->client_list, client) == NULL); + if (!new_client && fec->label_index == label_index) + /* Duplicate register */ + return 0; + + /* Save current label, update label index */ + old_label = fec->label; + fec->label_index = label_index; + } + + if (new_client) + listnode_add (fec->client_list, client); + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("FEC %s Label Index %u %s by client %s", + buf, label_index, new_client ? "registered" : "updated", + zebra_route_string(client->proto)); + + /* If not a configured FEC, derive the local label (from label index) + * or reset it. + */ + if (!(fec->flags & FEC_FLAG_CONFIGURED)) + { + fec_derive_label_from_index (zvrf, fec); + + /* If no label change, exit. */ + if (fec->label == old_label) + return 0; + + label_change = 1; + } + + /* If new client or label change, update client and install or uninstall + * label forwarding entry as needed. + */ + /* Inform client of label, if needed. */ + if ((new_client && fec->label != MPLS_INVALID_LABEL) || + label_change) + { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Update client label %u", fec->label); + fec_send (fec, client); + } + + if (new_client || label_change) + return fec_change_update_lsp (zvrf, fec, old_label); + + return 0; +} + +/* + * Deregistration from a client for the label binding for a FEC. The FEC + * itself is deleted if no other registered clients exist and there is no + * label bound to the FEC. + */ +int +zebra_mpls_fec_unregister (struct zebra_vrf *zvrf, struct prefix *p, + struct zserv *client) +{ + struct route_table *table; + zebra_fec_t *fec; + char buf[BUFSIZ]; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; + if (!table) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + prefix2str(p, buf, BUFSIZ); + + fec = fec_find (table, p); + if (!fec) + { + prefix2str(p, buf, BUFSIZ); + zlog_err("Failed to find FEC %s upon unregister, client %s", + buf, zebra_route_string(client->proto)); + return -1; + } + + listnode_delete(fec->client_list, client); + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("FEC %s unregistered by client %s", + buf, zebra_route_string(client->proto)); + + /* If not a configured entry, delete the FEC if no other clients. Before + * deleting, see if any LSP needs to be uninstalled. + */ + if (!(fec->flags & FEC_FLAG_CONFIGURED) && + list_isempty(fec->client_list)) + { + mpls_label_t old_label = fec->label; + fec->label = MPLS_INVALID_LABEL; /* reset */ + fec_change_update_lsp (zvrf, fec, old_label); + fec_del (fec); + } + + return 0; +} + +/* + * Cleanup any FECs registered by this client. + */ +int +zebra_mpls_cleanup_fecs_for_client (struct zebra_vrf *zvrf, struct zserv *client) +{ + struct route_node *rn; + zebra_fec_t *fec; + struct listnode *node; + struct zserv *fec_client; + int af; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + if (zvrf->fec_table[af] == NULL) + continue; + + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + fec = rn->info; + if (!fec || list_isempty(fec->client_list)) + continue; + + for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, fec_client)) + { + if (fec_client == client) + { + listnode_delete(fec->client_list, fec_client); + if (!(fec->flags & FEC_FLAG_CONFIGURED) && + list_isempty(fec->client_list)) + fec_del (fec); + break; + } + } + } + } + + return 0; +} + +/* + * Return FEC (if any) to which this label is bound. + * Note: Only works for per-prefix binding and when the label is not + * implicit-null. + * TODO: Currently walks entire table, can optimize later with another + * hash.. + */ +zebra_fec_t * +zebra_mpls_fec_for_label (struct zebra_vrf *zvrf, mpls_label_t label) +{ + struct route_node *rn; + zebra_fec_t *fec; + int af; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + if (zvrf->fec_table[af] == NULL) + continue; + + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + fec = rn->info; + if (fec->label == label) + return fec; + } + } + + return NULL; +} + +/* + * Inform if specified label is currently bound to a FEC or not. + */ +int +zebra_mpls_label_already_bound (struct zebra_vrf *zvrf, mpls_label_t label) +{ + return (zebra_mpls_fec_for_label (zvrf, label) ? 1 : 0); +} + +/* + * Add static FEC to label binding. If there are clients registered for this + * FEC, notify them. If there are labeled routes for this FEC, install the + * label forwarding entry. +*/ +int +zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, + mpls_label_t in_label) +{ + struct route_table *table; + zebra_fec_t *fec; + char buf[BUFSIZ]; + mpls_label_t old_label; + int ret = 0; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; + if (!table) + return -1; + + if (IS_ZEBRA_DEBUG_MPLS) + prefix2str(p, buf, BUFSIZ); + + /* Update existing FEC or create a new one. */ + fec = fec_find (table, p); + if (!fec) + { + fec = fec_add (table, p, in_label, FEC_FLAG_CONFIGURED, + MPLS_INVALID_LABEL_INDEX); + if (!fec) + { + prefix2str(p, buf, BUFSIZ); + zlog_err ("Failed to add FEC %s upon config", buf); + return -1; + } + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Add fec %s label %u", buf, in_label); + } + else + { + fec->flags |= FEC_FLAG_CONFIGURED; + if (fec->label == in_label) + /* Duplicate config */ + return 0; + + /* Label change, update clients. */ + old_label = fec->label; + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Update fec %s new label %u", buf, in_label); + + fec->label = in_label; + fec_update_clients (fec); + + /* Update label forwarding entries appropriately */ + ret = fec_change_update_lsp (zvrf, fec, old_label); + } + + return ret; +} + +/* + * Remove static FEC to label binding. If there are no clients registered + * for this FEC, delete the FEC; else notify clients + * Note: Upon delete of static binding, if label index exists for this FEC, + * client may need to be updated with derived label. + */ +int +zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p) +{ + struct route_table *table; + zebra_fec_t *fec; + mpls_label_t old_label; + char buf[BUFSIZ]; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; + if (!table) + return -1; + + fec = fec_find (table, p); + if (!fec) + { + prefix2str(p, buf, BUFSIZ); + zlog_err("Failed to find FEC %s upon delete", buf); + return -1; + } + + if (IS_ZEBRA_DEBUG_MPLS) + { + prefix2str(p, buf, BUFSIZ); + zlog_debug ("Delete fec %s label index %u", + buf, fec->label_index); + } + + old_label = fec->label; + fec->flags &= ~FEC_FLAG_CONFIGURED; + fec->label = MPLS_INVALID_LABEL; + + /* If no client exists, just delete the FEC. */ + if (list_isempty(fec->client_list)) + { + fec_del (fec); + return 0; + } + + /* Derive the local label (from label index) or reset it. */ + fec_derive_label_from_index (zvrf, fec); + + /* If there is a label change, update clients. */ + if (fec->label == old_label) + return 0; + fec_update_clients (fec); + + /* Update label forwarding entries appropriately */ + return fec_change_update_lsp (zvrf, fec, old_label); +} + +/* + * Display MPLS FEC to label binding configuration (VTY command handler). + */ +int +zebra_mpls_write_fec_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + struct route_node *rn; + int af; + zebra_fec_t *fec; + char buf[BUFSIZ]; + int write = 0; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + if (zvrf->fec_table[af] == NULL) + continue; + + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + + char lstr[BUFSIZ]; + fec = rn->info; + + if (!(fec->flags & FEC_FLAG_CONFIGURED)) + continue; + + write = 1; + prefix2str(&rn->p, buf, BUFSIZ); + vty_out(vty, "mpls label bind %s %s%s", buf, + label2str(fec->label, lstr, BUFSIZ), VTY_NEWLINE); + } + } + + return write; +} + +/* + * Display MPLS FEC to label binding (VTY command handler). + */ +void +zebra_mpls_print_fec_table (struct vty *vty, struct zebra_vrf *zvrf) +{ + struct route_node *rn; + int af; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + if (zvrf->fec_table[af] == NULL) + continue; + + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + fec_print (rn->info, vty); + } + } +} + +/* + * Display MPLS FEC to label binding for a specific FEC (VTY command handler). + */ +void +zebra_mpls_print_fec (struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p) +{ + struct route_table *table; + struct route_node *rn; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; + if (!table) + return; + + apply_mask (p); + rn = route_node_lookup(table, p); + if (!rn) + return; + + route_unlock_node(rn); + if (!rn->info) + return; + + fec_print (rn->info, vty); +} + +/* * Install/uninstall a FEC-To-NHLFE (FTN) binding. */ int @@ -1361,7 +2343,7 @@ int mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, mpls_label_t out_label, enum nexthop_types_t gtype, union g_addr *gate, - char *ifname, ifindex_t ifindex) + ifindex_t ifindex) { struct hash *lsp_table; zebra_ile_t tmp_ile; @@ -1379,7 +2361,7 @@ mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc); if (!lsp) return -1; - nhlfe = nhlfe_find (lsp, type, gtype, gate, ifname, ifindex); + nhlfe = nhlfe_find (lsp, type, gtype, gate, ifindex); if (nhlfe) { struct nexthop *nh = nhlfe->nexthop; @@ -1408,8 +2390,7 @@ mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add (lsp, type, gtype, gate, - ifname, ifindex, out_label); + nhlfe = nhlfe_add (lsp, type, gtype, gate, ifindex, out_label); if (!nhlfe) return -1; @@ -1438,7 +2419,7 @@ mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex) + union g_addr *gate, ifindex_t ifindex) { struct hash *lsp_table; zebra_ile_t tmp_ile; @@ -1456,7 +2437,7 @@ mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_lookup (lsp_table, &tmp_ile); if (!lsp) return 0; - nhlfe = nhlfe_find (lsp, type, gtype, gate, ifname, ifindex); + nhlfe = nhlfe_find (lsp, type, gtype, gate, ifindex); if (!nhlfe) return 0; @@ -1561,7 +2542,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) int zebra_mpls_lsp_label_consistent (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) + union g_addr *gate, ifindex_t ifindex) { struct hash *slsp_table; zebra_ile_t tmp_ile; @@ -1579,7 +2560,7 @@ zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, if (!slsp) return 1; - snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + snhlfe = snhlfe_find (slsp, gtype, gate, ifindex); if (snhlfe) { if (snhlfe->out_label == out_label) @@ -1619,7 +2600,7 @@ zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, int zebra_mpls_static_lsp_add (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) + union g_addr *gate, ifindex_t ifindex) { struct hash *slsp_table; zebra_ile_t tmp_ile; @@ -1637,7 +2618,7 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, slsp = hash_get (slsp_table, &tmp_ile, slsp_alloc); if (!slsp) return -1; - snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + snhlfe = snhlfe_find (slsp, gtype, gate, ifindex); if (snhlfe) { if (snhlfe->out_label == out_label) @@ -1656,7 +2637,7 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, else { /* Add static LSP entry to this nexthop */ - snhlfe = snhlfe_add (slsp, gtype, gate, ifname, ifindex, out_label); + snhlfe = snhlfe_add (slsp, gtype, gate, ifindex, out_label); if (!snhlfe) return -1; @@ -1670,7 +2651,7 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, /* (Re)Install LSP in the main table. */ if (mpls_lsp_install (zvrf, ZEBRA_LSP_STATIC, in_label, out_label, gtype, - gate, ifname, ifindex)) + gate, ifindex)) return -1; return 0; @@ -1686,7 +2667,7 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, int 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) + ifindex_t ifindex) { struct hash *slsp_table; zebra_ile_t tmp_ile; @@ -1719,7 +2700,7 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, else { /* Find specific NHLFE, exit if not found. */ - snhlfe = snhlfe_find (slsp, gtype, gate, ifname, ifindex); + snhlfe = snhlfe_find (slsp, gtype, gate, ifindex); if (!snhlfe) return 0; @@ -1733,7 +2714,7 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, /* Uninstall LSP from the main table. */ mpls_lsp_uninstall (zvrf, ZEBRA_LSP_STATIC, in_label, gtype, gate, - ifname, ifindex); + ifindex); /* Delete static LSP NHLFE */ snhlfe_del (snhlfe); @@ -1902,6 +2883,51 @@ zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) } /* + * Add/update global label block. + */ +int +zebra_mpls_label_block_add (struct zebra_vrf *zvrf, u_int32_t start_label, + u_int32_t end_label) +{ + zvrf->mpls_srgb.start_label = start_label; + zvrf->mpls_srgb.end_label = end_label; + + /* Evaluate registered FECs to see if any get a label or not. */ + fec_evaluate (zvrf, 1); + return 0; +} + +/* + * Delete global label block. + */ +int +zebra_mpls_label_block_del (struct zebra_vrf *zvrf) +{ + zvrf->mpls_srgb.start_label = 0; + zvrf->mpls_srgb.end_label = 0; + + /* Process registered FECs to clear their local label, if needed. */ + fec_evaluate (zvrf, 0); + return 0; +} + +/* + * Display MPLS global label block configuration (VTY command handler). + */ +int +zebra_mpls_write_label_block_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + if (zvrf->mpls_srgb.start_label == 0) + return 0; + + vty_out(vty, "mpls label global-block %u %u%s", + zvrf->mpls_srgb.start_label, zvrf->mpls_srgb.end_label, + VTY_NEWLINE); + + return 1; +} + +/* * Called upon process exiting, need to delete LSP forwarding * entries from the kernel. * NOTE: Currently supported only for default VRF. @@ -1927,7 +2953,11 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) return; zvrf->slsp_table = hash_create(label_hash, label_cmp); zvrf->lsp_table = hash_create(label_hash, label_cmp); + zvrf->fec_table[AFI_IP] = route_table_init(); + zvrf->fec_table[AFI_IP6] = route_table_init(); zvrf->mpls_flags = 0; + zvrf->mpls_srgb.start_label = 0; + zvrf->mpls_srgb.end_label = 0; } /* diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index a871fac65..e271edd2e 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -52,6 +52,7 @@ typedef struct zebra_snhlfe_t_ zebra_snhlfe_t; typedef struct zebra_slsp_t_ zebra_slsp_t; typedef struct zebra_nhlfe_t_ zebra_nhlfe_t; typedef struct zebra_lsp_t_ zebra_lsp_t; +typedef struct zebra_fec_t_ zebra_fec_t; /* * (Outgoing) nexthop label forwarding entry configuration @@ -147,6 +148,27 @@ struct zebra_lsp_t_ u_char addr_family; }; +/* + * FEC to label binding. + */ +struct zebra_fec_t_ +{ + /* FEC (prefix) */ + struct route_node *rn; + + /* In-label - either statically bound or derived from label block. */ + mpls_label_t label; + + /* Label index (into global label block), if valid */ + u_int32_t label_index; + + /* Flags. */ + u_int32_t flags; +#define FEC_FLAG_CONFIGURED (1 << 0) + + /* Clients interested in this FEC. */ + struct list *client_list; +}; /* Function declarations. */ @@ -165,6 +187,116 @@ mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, char *buf, int len); /* + * Add/update global label block. + */ +int +zebra_mpls_label_block_add (struct zebra_vrf *zvrf, u_int32_t start_label, + u_int32_t end_label); + +/* + * Delete global label block. + */ +int +zebra_mpls_label_block_del (struct zebra_vrf *vrf); + +/* + * Display MPLS global label block configuration (VTY command handler). + */ +int +zebra_mpls_write_label_block_config (struct vty *vty, struct zebra_vrf *vrf); + +/* + * Install dynamic LSP entry. + */ +int +zebra_mpls_lsp_install (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib); + +/* + * Uninstall dynamic LSP entry, if any. + */ +int +zebra_mpls_lsp_uninstall (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib); + +/* + * Registration from a client for the label binding for a FEC. If a binding + * already exists, it is informed to the client. + * NOTE: If there is a manually configured label binding, that is used. + * Otherwise, if aa label index is specified, it means we have to allocate the + * label from a locally configured label block (SRGB), if one exists and index + * is acceptable. + */ +int +zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, + u_int32_t label_index, struct zserv *client); + +/* + * Deregistration from a client for the label binding for a FEC. The FEC + * itself is deleted if no other registered clients exist and there is no + * label bound to the FEC. + */ +int +zebra_mpls_fec_unregister (struct zebra_vrf *zvrf, struct prefix *p, + struct zserv *client); + +/* + * Cleanup any FECs registered by this client. + */ +int +zebra_mpls_cleanup_fecs_for_client (struct zebra_vrf *zvrf, struct zserv *client); + +/* + * Return FEC (if any) to which this label is bound. + * Note: Only works for per-prefix binding and when the label is not + * implicit-null. + * TODO: Currently walks entire table, can optimize later with another + * hash.. + */ +zebra_fec_t * +zebra_mpls_fec_for_label (struct zebra_vrf *zvrf, mpls_label_t label); + +/* + * Inform if specified label is currently bound to a FEC or not. + */ +int +zebra_mpls_label_already_bound (struct zebra_vrf *zvrf, mpls_label_t label); + +/* + * Add static FEC to label binding. If there are clients registered for this + * FEC, notify them. If there are labeled routes for this FEC, install the + * label forwarding entry. + */ +int +zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, + mpls_label_t in_label); + +/* + * Remove static FEC to label binding. If there are no clients registered + * for this FEC, delete the FEC; else notify clients. + * Note: Upon delete of static binding, if label index exists for this FEC, + * client may need to be updated with derived label. + */ +int +zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p); + +/* + * Display MPLS FEC to label binding configuration (VTY command handler). + */ +int +zebra_mpls_write_fec_config (struct vty *vty, struct zebra_vrf *zvrf); + +/* + * Display MPLS FEC to label binding (VTY command handler). + */ +void +zebra_mpls_print_fec_table (struct vty *vty, struct zebra_vrf *zvrf); + +/* + * Display MPLS FEC to label binding for a specific FEC (VTY command handler). + */ +void +zebra_mpls_print_fec (struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p); + +/* * Install/uninstall a FEC-To-NHLFE (FTN) binding. */ int @@ -182,7 +314,7 @@ int mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, mpls_label_t out_label, enum nexthop_types_t gtype, union g_addr *gate, - char *ifname, ifindex_t ifindex); + ifindex_t ifindex); /* * Uninstall a particular NHLFE in the forwarding table. If this is @@ -191,7 +323,7 @@ mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, char *ifname, ifindex_t ifindex); + union g_addr *gate, ifindex_t ifindex); /* * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. @@ -216,7 +348,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi); int zebra_mpls_lsp_label_consistent (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); + union g_addr *gate, ifindex_t ifindex); #endif /* HAVE_CUMULUS */ /* @@ -229,7 +361,7 @@ zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, int zebra_mpls_static_lsp_add (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); + union g_addr *gate, ifindex_t ifindex); /* * Delete static LSP entry. This may be the delete of one particular @@ -241,7 +373,7 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, int 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); + ifindex_t ifindex); /* * Schedule all MPLS label forwarding entries for processing. @@ -324,6 +456,8 @@ lsp_type_from_rib_type (int rib_type) { case ZEBRA_ROUTE_STATIC: return ZEBRA_LSP_STATIC; + case ZEBRA_ROUTE_BGP: + return ZEBRA_LSP_BGP; default: return ZEBRA_LSP_NONE; } @@ -339,6 +473,8 @@ nhlfe_type2str(enum lsp_types_t lsp_type) return "Static"; case ZEBRA_LSP_LDP: return "LDP"; + case ZEBRA_LSP_BGP: + return "BGP"; default: return "Unknown"; } diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 23f5e7295..168c8d003 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -27,3 +27,209 @@ int kernel_add_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_upd_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_del_lsp (zebra_lsp_t *lsp) { return 0; } int mpls_kernel_init (void) { return -1; }; + +int mpls_enabled; + +char * +mpls_label2str (u_int8_t num_labels, mpls_label_t *labels, + char *buf, int len) +{ + return NULL; +} + +int +mpls_str2label (const char *label_str, u_int8_t *num_labels, + mpls_label_t *labels) +{ + return 0; +} + +int +zebra_mpls_label_block_add (struct zebra_vrf *vrf, u_int32_t start_label, + u_int32_t end_label) +{ + return 0; +} + +int +zebra_mpls_label_block_del (struct zebra_vrf *zvrf) +{ + return 0; +} + +int +zebra_mpls_write_label_block_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + return 0; +} + +int +zebra_mpls_lsp_install (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib) +{ + return 0; +} + +int +zebra_mpls_lsp_uninstall (struct zebra_vrf *zvrf, struct route_node *rn, struct rib *rib) +{ + return 0; +} + +void +zebra_mpls_init_tables (struct zebra_vrf *zvrf) +{ +} + +void +zebra_mpls_print_lsp (struct vty *vty, struct zebra_vrf *zvrf, mpls_label_t label, + u_char use_json) +{ +} + +void +zebra_mpls_print_lsp_table (struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) +{ +} + +int +zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + return 0; +} + +#ifdef HAVE_CUMULUS +int +zebra_mpls_lsp_label_consistent (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, ifindex_t ifindex) +{ + return 0; +} +#endif + +int +zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label, + mpls_label_t out_label, enum nexthop_types_t gtype, + union g_addr *gate, ifindex_t ifindex) +{ + return 0; +} + +int +zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label, + enum nexthop_types_t gtype, union g_addr *gate, + ifindex_t ifindex) +{ + return 0; +} + +void +zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf) +{ +} + +void +zebra_mpls_close_tables (struct zebra_vrf *zvrf) +{ +} + +zebra_fec_t * +zebra_mpls_fec_for_label (struct zebra_vrf *zvrf, mpls_label_t label) +{ + return NULL; +} + +int +zebra_mpls_label_already_bound (struct zebra_vrf *zvrf, mpls_label_t label) +{ + return 0; +} + +int +zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, + mpls_label_t in_label) +{ + return 0; +} + +int +zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p) +{ + return 0; +} + +int +zebra_mpls_write_fec_config (struct vty *vty, struct zebra_vrf *zvrf) +{ + return 0; +} + +void +zebra_mpls_print_fec_table (struct vty *vty, struct zebra_vrf *zvrf) +{ +} + +void +zebra_mpls_print_fec (struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p) +{ +} + +int +zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, + u_int32_t label_index, struct zserv *client) +{ + return 0; +} + +int +zebra_mpls_fec_unregister (struct zebra_vrf *zvrf, struct prefix *p, + struct zserv *client) +{ + return 0; +} + +int +zebra_mpls_cleanup_fecs_for_client (struct zebra_vrf *zvrf, struct zserv *client) +{ + return 0; +} + +void mpls_ldp_lsp_uninstall_all (struct hash_backet *backet, void *ctxt) +{ + return; +} + +void mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) +{ + return; +} + +void zebra_mpls_init (void) +{ + return; +} + +int mpls_lsp_install (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, mpls_label_t out_label, + enum nexthop_types_t gtype, union g_addr *gate, + ifindex_t ifindex) +{ + return 0; +} + +int mpls_lsp_uninstall (struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, enum nexthop_types_t gtype, + union g_addr *gate, ifindex_t ifindex) +{ + return 0; +} + +int mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, + struct prefix *prefix, enum nexthop_types_t gtype, + union g_addr *gate, ifindex_t ifindex, u_int8_t distance, + mpls_label_t out_label) +{ + return 0; +} + diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index dd381723c..7662cf416 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -133,7 +133,7 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, #if defined(HAVE_CUMULUS) /* Check that label value is consistent. */ if (!zebra_mpls_lsp_label_consistent (zvrf, in_label, out_label, gtype, - &gate, NULL, 0)) + &gate, 0)) { vty_out (vty, "%% Label value not consistent%s", VTY_NEWLINE); @@ -142,10 +142,10 @@ zebra_mpls_transit_lsp (struct vty *vty, int add_cmd, const char *inlabel_str, #endif /* HAVE_CUMULUS */ ret = zebra_mpls_static_lsp_add (zvrf, in_label, out_label, gtype, - &gate, NULL, 0); + &gate, 0); } else - ret = zebra_mpls_static_lsp_del (zvrf, in_label, gtype, &gate, NULL, 0); + ret = zebra_mpls_static_lsp_del (zvrf, in_label, gtype, &gate, 0); if (ret) { @@ -209,6 +209,109 @@ DEFUN (no_mpls_transit_lsp_all, return zebra_mpls_transit_lsp (vty, 0, argv[3]->arg, NULL, NULL, NULL); } +static int +zebra_mpls_bind (struct vty *vty, int add_cmd, const char *prefix, + const char *label_str) +{ + struct zebra_vrf *zvrf; + struct prefix p; + u_int32_t label; + int ret; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + { + vty_out (vty, "%% Default VRF does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + memset(&p, 0, sizeof(struct prefix)); + ret = str2prefix(prefix, &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (add_cmd) + { + if (!label_str) + { + vty_out (vty, "%% No label binding specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(label_str, "implicit-null")) + label = MPLS_IMP_NULL_LABEL; + else if (!strcmp(label_str, "explicit-null")) + { + if (p.family == AF_INET) + label = MPLS_V4_EXP_NULL_LABEL; + else + label = MPLS_V6_EXP_NULL_LABEL; + } + else + { + label = atoi(label_str); + if (!IS_MPLS_UNRESERVED_LABEL(label)) + { + vty_out (vty, "%% Invalid label%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (zebra_mpls_label_already_bound (zvrf, label)) + { + vty_out (vty, "%% Label already bound to a FEC%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + ret = zebra_mpls_static_fec_add (zvrf, &p, label); + } + else + ret = zebra_mpls_static_fec_del (zvrf, &p); + + if (ret) + { + vty_out (vty, "%% FEC to label binding cannot be %s%s", + add_cmd ? "added" : "deleted", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (mpls_label_bind, + mpls_label_bind_cmd, + "mpls label bind <A.B.C.D/M|X:X::X:X/M> <(16-1048575)|implicit-null|explicit-null>", + MPLS_STR + "Label configuration\n" + "Establish FEC to label binding\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "MPLS Label to bind\n" + "Use Implicit-Null Label\n" + "Use Explicit-Null Label\n") +{ + return zebra_mpls_bind (vty, 1, argv[3]->arg, argv[4]->arg); +} + +DEFUN (no_mpls_label_bind, + no_mpls_label_bind_cmd, + "no mpls label bind <A.B.C.D/M|X:X::X:X/M> [<(16-1048575)|implicit-null>]", + NO_STR + MPLS_STR + "Label configuration\n" + "Establish FEC to label binding\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "MPLS Label to bind\n" + "Use Implicit-Null Label\n") + +{ + return zebra_mpls_bind (vty, 0, argv[4]->arg, NULL); +} + /* Static route configuration. */ DEFUN (ip_route_label, ip_route_label_cmd, @@ -777,9 +880,45 @@ zebra_mpls_config (struct vty *vty) return 0; write += zebra_mpls_write_lsp_config(vty, zvrf); + write += zebra_mpls_write_fec_config(vty, zvrf); + write += zebra_mpls_write_label_block_config (vty, zvrf); return write; } +DEFUN (show_mpls_fec, + show_mpls_fec_cmd, + "show mpls fec [<A.B.C.D/M|X:X::X:X/M>]", + SHOW_STR + MPLS_STR + "MPLS FEC table\n" + "FEC to display information about\n" + "FEC to display information about\n") +{ + struct zebra_vrf *zvrf; + struct prefix p; + int ret; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + if (argc == 3) + zebra_mpls_print_fec_table(vty, zvrf); + else + { + memset(&p, 0, sizeof(struct prefix)); + ret = str2prefix(argv[3]->arg, &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + zebra_mpls_print_fec (vty, zvrf, &p); + } + + return CMD_SUCCESS; +} + DEFUN (show_mpls_table, show_mpls_table_cmd, "show mpls table [json]", @@ -827,6 +966,85 @@ DEFUN (show_mpls_status, return CMD_SUCCESS; } +static int +zebra_mpls_global_block (struct vty *vty, int add_cmd, + const char *start_label_str, const char *end_label_str) +{ + int ret; + u_int32_t start_label; + u_int32_t end_label; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); + if (!zvrf) + { + vty_out (vty, "%% Default VRF does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (add_cmd) + { + if (!start_label_str || !end_label_str) + { + vty_out (vty, "%% Labels not specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + start_label = atoi(start_label_str); + end_label = atoi(end_label_str); + if (!IS_MPLS_UNRESERVED_LABEL(start_label) || + !IS_MPLS_UNRESERVED_LABEL(end_label)) + { + vty_out (vty, "%% Invalid label%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (end_label < start_label) + { + vty_out (vty, "%% End label is less than Start label%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = zebra_mpls_label_block_add (zvrf, start_label, end_label); + } + else + ret = zebra_mpls_label_block_del (zvrf); + + if (ret) + { + vty_out (vty, "%% Global label block could not be %s%s", + add_cmd ? "added" : "deleted", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (mpls_label_global_block, + mpls_label_global_block_cmd, + "mpls label global-block (16-1048575) (16-1048575)", + MPLS_STR + "Label configuration\n" + "Configure global label block\n" + "Start label\n" + "End label\n") +{ + return zebra_mpls_global_block (vty, 1, argv[3]->arg, argv[4]->arg); +} + +DEFUN (no_mpls_label_global_block, + no_mpls_label_global_block_cmd, + "no mpls label global-block [(16-1048575) (16-1048575)]", + NO_STR + MPLS_STR + "Label configuration\n" + "Configure global label block\n" + "Start label\n" + "End label\n") +{ + return zebra_mpls_global_block (vty, 0, NULL, NULL); +} + /* MPLS node for MPLS LSP. */ static struct cmd_node mpls_node = { MPLS_NODE, "", 1 }; @@ -876,7 +1094,13 @@ zebra_mpls_vty_init (void) install_element (CONFIG_NODE, &no_mpls_transit_lsp_cmd); install_element (CONFIG_NODE, &no_mpls_transit_lsp_out_label_cmd); install_element (CONFIG_NODE, &no_mpls_transit_lsp_all_cmd); + install_element (CONFIG_NODE, &mpls_label_bind_cmd); + install_element (CONFIG_NODE, &no_mpls_label_bind_cmd); + + install_element (CONFIG_NODE, &mpls_label_global_block_cmd); + install_element (CONFIG_NODE, &no_mpls_label_global_block_cmd); install_element (VIEW_NODE, &show_mpls_table_cmd); install_element (VIEW_NODE, &show_mpls_table_lsp_cmd); + install_element (VIEW_NODE, &show_mpls_fec_cmd); } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b3e70e46f..e4d583d5f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1084,7 +1084,25 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) return rib->nexthop_active_num; } +/* + * Is this RIB labeled-unicast? It must be of type BGP and all paths + * (nexthops) must have a label. + */ +int +zebra_rib_labeled_unicast (struct rib *rib) +{ + struct nexthop *nexthop = NULL, *tnexthop; + int recursing; + + if (rib->type != ZEBRA_ROUTE_BGP) + return 0; + + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + if (!nexthop->nh_label || !nexthop->nh_label->num_labels) + return 0; + return 1; +} /* Update flag indicates whether this is a "replace" or not. Currently, this * is only used for IPv4. @@ -1177,7 +1195,12 @@ rib_uninstall (struct route_node *rn, struct rib *rib) if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); - UNSET_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB); + + /* If labeled-unicast route, uninstall transit LSP. */ + if (zebra_rib_labeled_unicast (rib)) + zebra_mpls_lsp_uninstall (info->zvrf, rn, rib); + + UNSET_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB); } if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) @@ -1272,6 +1295,10 @@ rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, zvrf_id (zvrf), buf, rn, new, new->type); } + /* If labeled-unicast route, install transit LSP. */ + if (zebra_rib_labeled_unicast (new)) + zebra_mpls_lsp_install (zvrf, rn, new); + if (!RIB_SYSTEM_ROUTE (new)) { if (rib_install_kernel (rn, new, NULL)) @@ -1301,6 +1328,10 @@ rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, zvrf_id (zvrf), buf, rn, old, old->type); } + /* If labeled-unicast route, uninstall transit LSP. */ + if (zebra_rib_labeled_unicast (old)) + zebra_mpls_lsp_uninstall (zvrf, rn, old); + if (!RIB_SYSTEM_ROUTE (old)) rib_uninstall_kernel (rn, old); @@ -1354,6 +1385,10 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, /* Non-system route should be installed. */ if (!RIB_SYSTEM_ROUTE (new)) { + /* If labeled-unicast route, install transit LSP. */ + if (zebra_rib_labeled_unicast (new)) + zebra_mpls_lsp_install (zvrf, rn, new); + if (rib_install_kernel (rn, new, old)) { char buf[SRCDEST2STR_BUFFER]; @@ -1368,6 +1403,10 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, { if (RIB_SYSTEM_ROUTE(new)) { + /* If labeled-unicast route, uninstall transit LSP. */ + if (zebra_rib_labeled_unicast (old)) + zebra_mpls_lsp_uninstall (zvrf, rn, old); + if (!RIB_SYSTEM_ROUTE (old)) rib_uninstall_kernel (rn, old); } @@ -1404,6 +1443,10 @@ rib_process_update_fib (struct zebra_vrf *zvrf, struct route_node *rn, nh_active ? "install failed" : "nexthop inactive"); } + /* If labeled-unicast route, uninstall transit LSP. */ + if (zebra_rib_labeled_unicast (old)) + zebra_mpls_lsp_uninstall (zvrf, rn, old); + if (!RIB_SYSTEM_ROUTE (old)) rib_uninstall_kernel (rn, old); UNSET_FLAG (new->status, RIB_ENTRY_SELECTED_FIB); @@ -2934,8 +2977,10 @@ rib_tables_iter_next (rib_tables_iter_t *iter) } afi_safis[] = { { AFI_IP, SAFI_UNICAST }, { AFI_IP, SAFI_MULTICAST }, + { AFI_IP, SAFI_LABELED_UNICAST }, { AFI_IP6, SAFI_UNICAST }, { AFI_IP6, SAFI_MULTICAST }, + { AFI_IP6, SAFI_LABELED_UNICAST }, }; table = NULL; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 96d631d64..74c2a5217 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -25,6 +25,13 @@ #include <zebra/zebra_ns.h> +/* MPLS (Segment Routing) global block */ +typedef struct mpls_srgb_t_ +{ + u_int32_t start_label; + u_int32_t end_label; +} mpls_srgb_t; + /* Routing table instance. */ struct zebra_vrf { @@ -79,6 +86,12 @@ struct zebra_vrf /* MPLS label forwarding table */ struct hash *lsp_table; + /* MPLS FEC binding table */ + struct route_table *fec_table[AFI_MAX]; + + /* MPLS Segment Routing Global block */ + mpls_srgb_t mpls_srgb; + /* MPLS processing flags */ u_int16_t mpls_flags; #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) diff --git a/zebra/zserv.c b/zebra/zserv.c index 3477dc36d..416e5444e 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -934,6 +934,109 @@ zserv_rnh_unregister (struct zserv *client, int sock, u_short length, return 0; } +#define ZEBRA_MIN_FEC_LENGTH 9 + +/* FEC register */ +static int +zserv_fec_register (struct zserv *client, int sock, u_short length) +{ + struct stream *s; + struct zebra_vrf *zvrf; + u_short l = 0; + struct prefix p; + u_int16_t flags; + u_int32_t label_index = MPLS_INVALID_LABEL_INDEX; + + s = client->ibuf; + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; // unexpected + + /* + * The minimum amount of data that can be sent for one fec + * registration + */ + if (length < ZEBRA_MIN_FEC_LENGTH) + { + zlog_err ("fec_register: Received a fec register of length %d, it is of insufficient size to properly decode", + length); + return -1; + } + + while (l < length) + { + flags = stream_getw(s); + p.family = stream_getw(s); + if (p.family != AF_INET && + p.family != AF_INET6) + { + zlog_err ("fec_register: Received unknown family type %d\n", + p.family); + return -1; + } + p.prefixlen = stream_getc(s); + l += 5; + stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); + l += PSIZE(p.prefixlen); + if (flags & ZEBRA_FEC_REGISTER_LABEL_INDEX) + { + label_index = stream_getl(s); + l += 4; + } + zebra_mpls_fec_register (zvrf, &p, label_index, client); + } + + return 0; +} + +/* FEC unregister */ +static int +zserv_fec_unregister (struct zserv *client, int sock, u_short length) +{ + struct stream *s; + struct zebra_vrf *zvrf; + u_short l = 0; + struct prefix p; + //u_int16_t flags; + + s = client->ibuf; + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; // unexpected + + /* + * The minimum amount of data that can be sent for one + * fec unregistration + */ + if (length < ZEBRA_MIN_FEC_LENGTH) + { + zlog_err ("fec_unregister: Received a fec unregister of length %d, it is of insufficient size to properly decode", + length); + return -1; + } + + while (l < length) + { + //flags = stream_getw(s); + (void)stream_getw(s); + p.family = stream_getw(s); + if (p.family != AF_INET && + p.family != AF_INET6) + { + zlog_err ("fec_unregister: Received unknown family type %d\n", + p.family); + return -1; + } + p.prefixlen = stream_getc(s); + l += 5; + stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); + l += PSIZE(p.prefixlen); + zebra_mpls_fec_unregister (zvrf, &p, client); + } + + return 0; +} + /* Modified version of zsend_ipv4_nexthop_lookup(): Query unicast rib if nexthop is not found on mrib. @@ -1075,13 +1178,15 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) struct rib *rib; struct prefix p; u_char message; - struct in_addr nexthop; + struct in_addr nhop_addr; u_char nexthop_num; u_char nexthop_type; struct stream *s; ifindex_t ifindex; safi_t safi; int ret; + mpls_label_t label; + struct nexthop *nexthop; /* Get input stream. */ s = client->ibuf; @@ -1123,13 +1228,19 @@ zread_ipv4_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) rib_nexthop_ifindex_add (rib, ifindex); break; case NEXTHOP_TYPE_IPV4: - nexthop.s_addr = stream_get_ipv4 (s); - rib_nexthop_ipv4_add (rib, &nexthop, NULL); + nhop_addr.s_addr = stream_get_ipv4 (s); + nexthop = rib_nexthop_ipv4_add (rib, &nhop_addr, NULL); + /* For labeled-unicast, each nexthop is followed by label. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_LABEL)) + { + label = (mpls_label_t)stream_getl (s); + nexthop_add_labels (nexthop, nexthop->nh_label_type, 1, &label); + } break; case NEXTHOP_TYPE_IPV4_IFINDEX: - nexthop.s_addr = stream_get_ipv4 (s); + nhop_addr.s_addr = stream_get_ipv4 (s); ifindex = stream_getl (s); - rib_nexthop_ipv4_ifindex_add (rib, &nexthop, NULL, ifindex); + rib_nexthop_ipv4_ifindex_add (rib, &nhop_addr, NULL, ifindex); break; case NEXTHOP_TYPE_IPV6: stream_forward_getp (s, IPV6_MAX_BYTELEN); @@ -1222,6 +1333,11 @@ zread_ipv4_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) break; case NEXTHOP_TYPE_IPV4: nexthop.s_addr = stream_get_ipv4 (s); + /* For labeled-unicast, each nexthop is followed by label, but + * we don't care for delete. + */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_LABEL)) + stream_forward_getp (s, sizeof(u_int32_t)); nexthop_p = (union g_addr *)&nexthop; break; case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -1407,7 +1523,7 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) { unsigned int i; struct stream *s; - struct in6_addr nexthop; + struct in6_addr nhop_addr; struct rib *rib; u_char message; u_char nexthop_num; @@ -1418,11 +1534,14 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) static struct in6_addr nexthops[MULTIPATH_NUM]; static unsigned int ifindices[MULTIPATH_NUM]; int ret; + static mpls_label_t labels[MULTIPATH_NUM]; + mpls_label_t label; + struct nexthop *nexthop; /* Get input stream. */ s = client->ibuf; - memset (&nexthop, 0, sizeof (struct in6_addr)); + memset (&nhop_addr, 0, sizeof (struct in6_addr)); /* Allocate new rib. */ rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); @@ -1471,10 +1590,17 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) switch (nexthop_type) { case NEXTHOP_TYPE_IPV6: - stream_get (&nexthop, s, 16); - if (nh_count < multipath_num) { - nexthops[nh_count++] = nexthop; - } + stream_get (&nhop_addr, s, 16); + if (nh_count < MULTIPATH_NUM) + { + /* For labeled-unicast, each nexthop is followed by label. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_LABEL)) + { + label = (mpls_label_t)stream_getl (s); + labels[nh_count++] = label; + } + nexthops[nh_count++] = nhop_addr; + } break; case NEXTHOP_TYPE_IFINDEX: if (if_count < multipath_num) { @@ -1492,9 +1618,11 @@ zread_ipv6_add (struct zserv *client, u_short length, struct zebra_vrf *zvrf) { if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i])) { if ((i < if_count) && ifindices[i]) - rib_nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]); + nexthop = rib_nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]); else - rib_nexthop_ipv6_add (rib, &nexthops[i]); + nexthop = rib_nexthop_ipv6_add (rib, &nexthops[i]); + if (CHECK_FLAG (message, ZAPI_MESSAGE_LABEL)) + nexthop_add_labels (nexthop, nexthop->nh_label_type, 1, &labels[i]); } else { if ((i < if_count) && ifindices[i]) @@ -1591,6 +1719,11 @@ zread_ipv6_delete (struct zserv *client, u_short length, struct zebra_vrf *zvrf) { case NEXTHOP_TYPE_IPV6: stream_get (&nexthop, s, 16); + /* For labeled-unicast, each nexthop is followed by label, but + * we don't care for delete. + */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_LABEL)) + stream_forward_getp (s, sizeof(u_int32_t)); pnexthop = (union g_addr *)&nexthop; break; case NEXTHOP_TYPE_IFINDEX: @@ -1761,14 +1894,14 @@ zread_mpls_labels (int command, struct zserv *client, u_short length, if (command == ZEBRA_MPLS_LABELS_ADD) { mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate, - NULL, ifindex); + ifindex); if (out_label != MPLS_IMP_NULL_LABEL) mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, distance, out_label); } else if (command == ZEBRA_MPLS_LABELS_DELETE) { - mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, NULL, ifindex); + mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, ifindex); if (out_label != MPLS_IMP_NULL_LABEL) mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, distance, out_label); @@ -1975,6 +2108,9 @@ zebra_client_close (struct zserv *client) /* Release Label Manager chunks */ release_daemon_chunks (client->proto, client->instance); + /* Cleanup any FECs registered by this client. */ + zebra_mpls_cleanup_fecs_for_client (vrf_info_lookup(VRF_DEFAULT), client); + /* Close file descriptor. */ if (client->sock) { @@ -2263,6 +2399,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_RELEASE_LABEL_CHUNK: zread_label_manager_request (command, client, vrf_id); break; + case ZEBRA_FEC_REGISTER: + zserv_fec_register (client, sock, length); + break; + case ZEBRA_FEC_UNREGISTER: + zserv_fec_unregister (client, sock, length); + break; default: zlog_info ("Zebra received unknown command %d", command); break; |