int stream_put_prefix (struct stream *, struct prefix *);
size_t
-bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi,
+bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi,
struct bpacket_attr_vec_arr *vecarr,
struct attr *attr)
{
stream_putc (s, safi); /* SAFI */
/* Nexthop */
- switch (afi)
+ switch (nh_afi)
{
case AFI_IP:
switch (safi)
/* Remember current pointer. */
cp = stream_get_endp (s);
- if (p && !(afi == AFI_IP && safi == SAFI_UNICAST))
+ if (p && !((afi == AFI_IP && safi == SAFI_UNICAST) &&
+ !peer_cap_enhe(peer)))
{
- mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, vecarr, attr);
+ mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi,
+ (peer_cap_enhe(peer) ? AFI_IP6 : afi),
+ vecarr, attr);
bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag);
bgp_packet_mpattr_end(s, mpattrlen_pos);
}
send_as4_path = 1; /* we'll do this later, at the correct place */
/* Nexthop attribute. */
- if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP)
+ if (afi == AFI_IP && !peer_cap_enhe(peer))
{
- stream_putc (s, BGP_ATTR_FLAG_TRANS);
- stream_putc (s, BGP_ATTR_NEXT_HOP);
- 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);
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_NEXT_HOP);
+ 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);
+ }
+ else if (safi == SAFI_UNICAST && peer_cap_enhe(from))
+ {
+ /*
+ * Likely this is the case when an IPv4 prefix was received with
+ * Extended Next-hop capability and now being advertised to
+ * non-ENHE peers.
+ * Setting the mandatory (ipv4) next-hop attribute here to enable
+ * implicit next-hop self with correct (ipv4 address family).
+ */
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_NEXT_HOP);
+ bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, NULL);
+ stream_putc (s, 4);
+ stream_put_ipv4 (s, 0);
+ }
}
/* MED attribute. */
* finally the _end() function.
*/
extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi,
- struct bpacket_attr_vec_arr *vecarr,
+ afi_t nh_afi,
+ struct bpacket_attr_vec_arr *vecarr,
struct attr *attr);
extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
struct prefix *p, struct prefix_rd *prd,
if (ri)
{
+ is_bgp_static_route = ((ri->type == ZEBRA_ROUTE_BGP) &&
+ (ri->sub_type == BGP_ROUTE_STATIC)) ? 1 : 0;
+
+ /* Since Extended Next-hop Encoding (RFC5549) support, we want to derive
+ address-family from the next-hop. */
+ if (!is_bgp_static_route)
+ afi = BGP_ATTR_NEXTHOP_AFI_IP6(ri->attr) ? AFI_IP6 : AFI_IP;
+
/* This will return TRUE if the global IPv6 NH is a link local addr */
if (make_prefix(afi, ri, &p) < 0)
return 1;
- is_bgp_static_route = ((ri->type == ZEBRA_ROUTE_BGP) &&
- (ri->sub_type == BGP_ROUTE_STATIC)) ? 1 : 0;
}
else if (peer)
{
return 0;
}
+static int
+bgp_capability_enhe (struct peer *peer, struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT (peer);
+ size_t end = stream_get_getp (s) + hdr->length;
+
+ while (stream_get_getp (s) + 6 <= end)
+ {
+ afi_t afi = stream_getw (s);
+ safi_t safi = stream_getw (s);
+ afi_t nh_afi = stream_getw (s);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s Received with value triple (afi/safi/next-hop afi): %u/%u/%u",
+ peer->host, afi, safi, nh_afi);
+
+ if (!bgp_afi_safi_valid_indices (afi, &safi))
+ return -1;
+
+ if (afi != AFI_IP || nh_afi != AFI_IP6)
+ {
+ zlog_warn ("%s Extended Next-hop capability, wrong afi/next-hop afi: %u/%u",
+ peer->host, afi, nh_afi);
+ return -1;
+ }
+
+ /* Until SAFIs other than SAFI_UNICAST are supported */
+ if (safi != SAFI_UNICAST)
+ zlog_warn ("%s Extended Next-hop capability came with unsupported SAFI: %u",
+ peer->host, safi);
+
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV);
+
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_ADV))
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_NEGO);
+ }
+
+ SET_FLAG (peer->cap, PEER_CAP_ENHE_RCV);
+
+ return 0;
+}
+
static const struct message capcode_str[] =
{
{ CAPABILITY_CODE_MP, "MultiProtocol Extensions" },
{ CAPABILITY_CODE_AS4, "4-octet AS number" },
{ CAPABILITY_CODE_ADDPATH, "AddPath" },
{ CAPABILITY_CODE_DYNAMIC, "Dynamic" },
+ { CAPABILITY_CODE_ENHE, "Extended Next Hop Encoding" },
{ CAPABILITY_CODE_DYNAMIC_OLD, "Dynamic (Old)" },
{ CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)" },
{ CAPABILITY_CODE_ORF_OLD, "ORF (Old)" },
[CAPABILITY_CODE_ADDPATH] = CAPABILITY_CODE_ADDPATH_LEN,
[CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN,
[CAPABILITY_CODE_DYNAMIC_OLD] = CAPABILITY_CODE_DYNAMIC_LEN,
+ [CAPABILITY_CODE_ENHE] = CAPABILITY_CODE_ENHE_LEN,
[CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN,
[CAPABILITY_CODE_ORF_OLD] = sizeof (struct capability_orf_entry),
[CAPABILITY_CODE_HOSTNAME] = CAPABILITY_CODE_MIN_HOSTNAME_LEN,
case CAPABILITY_CODE_ADDPATH:
case CAPABILITY_CODE_DYNAMIC:
case CAPABILITY_CODE_DYNAMIC_OLD:
+ case CAPABILITY_CODE_ENHE:
case CAPABILITY_CODE_HOSTNAME:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code])
if (bgp_capability_hostname (peer, &caphdr))
return -1;
break;
+ case CAPABILITY_CODE_ENHE:
+ if (bgp_capability_enhe (peer, &caphdr))
+ return -1;
+ break;
default:
if (caphdr.code > 128)
{
stream_putc (s, SAFI_MPLS_LABELED_VPN);
}
#ifdef HAVE_IPV6
+ /* Currently supporting RFC-5549 for Link-Local peering only */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_CAPABILITY_ENHE) &&
+ peer->su.sa.sa_family == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ {
+ /* RFC 5549 Extended Next Hop Encoding */
+ SET_FLAG (peer->cap, PEER_CAP_ENHE_ADV);
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_ENHE_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_ENHE);
+ stream_putc (s, CAPABILITY_CODE_ENHE_LEN);
+ /* Currently supporting for SAFI_UNICAST only */
+ SET_FLAG (peer->af_cap[AFI_IP][SAFI_UNICAST], PEER_CAP_ENHE_AF_ADV);
+ stream_putw (s, AFI_IP);
+ stream_putw (s, SAFI_UNICAST);
+ stream_putw (s, AFI_IP6);
+
+ if (CHECK_FLAG (peer->af_cap[AFI_IP][SAFI_UNICAST], PEER_CAP_ENHE_AF_RCV))
+ SET_FLAG (peer->af_cap[AFI_IP][SAFI_UNICAST], PEER_CAP_ENHE_AF_NEGO);
+ }
/* IPv6 unicast. */
if (peer->afc[AFI_IP6][SAFI_UNICAST])
{
#define CAPABILITY_CODE_DYNAMIC_OLD 66 /* Dynamic Capability, deprecated since 2003 */
#define CAPABILITY_CODE_DYNAMIC 67 /* Dynamic Capability */
#define CAPABILITY_CODE_ADDPATH 69 /* Addpath Capability */
+#define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */
#define CAPABILITY_CODE_HOSTNAME 131 /* Advertise hostname capabilty */
#define CAPABILITY_CODE_AS4_LEN 4
#define CAPABILITY_CODE_ADDPATH_LEN 4
#define CAPABILITY_CODE_MIN_HOSTNAME_LEN 2
+#define CAPABILITY_CODE_ENHE_LEN 6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */
/* Cooperative Route Filtering Capability. */
* Of course, the operator can always set it through the route-map, if
* so desired.
*/
- if (p->family == AF_INET6)
+ if (p->family == AF_INET6 || peer_cap_enhe(peer))
{
attr->extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
if (!reflect)
if (!reflect ||
CHECK_FLAG (peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_SELF_ALL))
- subgroup_announce_reset_nhop (p->family, attr);
+ subgroup_announce_reset_nhop ((peer_cap_enhe(peer) ? AF_INET6 : p->family), attr);
}
else if (peer->sort == BGP_PEER_EBGP)
{
break;
}
if (!paf)
- subgroup_announce_reset_nhop (p->family, attr);
+ subgroup_announce_reset_nhop ((peer_cap_enhe(peer) ? AF_INET6 : p->family), attr);
}
}
bgp_attr_dup (attr, riattr);
/* next-hop-set */
- if ((p->family == AF_INET && attr->nexthop.s_addr == 0)
+ if ((p->family == AF_INET && !peer_cap_enhe(rsclient)
+ && attr->nexthop.s_addr == 0)
#ifdef HAVE_IPV6
- || (p->family == AF_INET6 &&
+ || ((p->family == AF_INET6 || peer_cap_enhe(rsclient)) &&
IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
#endif /* HAVE_IPV6 */
)
if (safi == SAFI_MPLS_VPN)
memcpy (&attr->extra->mp_nexthop_global_in, &rsclient->nexthop.v4,
IPV4_MAX_BYTELEN);
- else
+ else if (!peer_cap_enhe(rsclient))
memcpy (&attr->nexthop, &rsclient->nexthop.v4, IPV4_MAX_BYTELEN);
}
#ifdef HAVE_IPV6
/* Set IPv6 nexthop. */
- if (p->family == AF_INET6)
+ if (p->family == AF_INET6 || peer_cap_enhe(rsclient))
{
/* IPv6 global nexthop must be included. */
memcpy (&attr->extra->mp_nexthop_global, &rsclient->nexthop.v6_global,
}
#ifdef HAVE_IPV6
- if (p->family == AF_INET6)
+ if (p->family == AF_INET6 || peer_cap_enhe(rsclient))
{
struct attr_extra *attre = attr->extra;
bgp_attr_unintern (&attr_new2);
/* IPv4 unicast next hop check. */
- if ((afi == AFI_IP) && ((safi == SAFI_UNICAST) || safi == SAFI_MULTICAST))
+ if ((afi == AFI_IP) && ((safi == SAFI_UNICAST && !peer_cap_enhe(peer))
+ || safi == SAFI_MULTICAST))
{
/* Next hop must not be 0.0.0.0 nor Class D/E address. */
if (new_attr.nexthop.s_addr == 0
}
/* IPv4 unicast next hop check. */
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer))
{
/* Next hop must not be 0.0.0.0 nor Class D/E address. */
if (new_attr.nexthop.s_addr == 0
{
/* IPv4 Next Hop */
- if (p->family == AF_INET)
+ if (p->family == AF_INET
+ && (safi == SAFI_MPLS_VPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (json_paths)
{
#ifdef HAVE_IPV6
/* IPv6 Next Hop */
- else if (p->family == AF_INET6)
+ else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr))
{
int len;
char buf[BUFSIZ];
/* Print attribute */
if (attr)
{
- if (p->family == AF_INET)
+ if (p->family == AF_INET
+ && (safi == SAFI_MPLS_VPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (safi == SAFI_MPLS_VPN)
vty_out (vty, "%-16s",
vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
}
#ifdef HAVE_IPV6
- else if (p->family == AF_INET6)
+ else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr))
{
int len;
char buf[BUFSIZ];
attr = binfo->attr;
if (attr)
{
- if (p->family == AF_INET)
+ if (p->family == AF_INET
+ && (safi == SAFI_MPLS_VPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (safi == SAFI_MPLS_VPN)
vty_out (vty, "%-16s",
vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
}
#ifdef HAVE_IPV6
- else if (p->family == AF_INET6)
+ else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr))
{
assert (attr->extra);
char buf[BUFSIZ];
vty_out (vty, "%s", VTY_NEWLINE);
/* Line2 display Next-hop, Neighbor, Router-id */
- if (p->family == AF_INET)
+ if (p->family == AF_INET
+ && (safi == SAFI_MPLS_VPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (safi == SAFI_MPLS_VPN)
{
if (binfo->peer == bgp->peer_self)
{
- if (p->family == AF_INET)
+ if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
{
if (json_paths)
json_string = json_object_new_string("0.0.0.0");
u_char tag[3];
};
+#define BGP_ATTR_NEXTHOP_AFI_IP6(attr) \
+ (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) && \
+ (attr)->extra && ((attr)->extra->mp_nexthop_len == 16 || \
+ (attr)->extra->mp_nexthop_len == 32))
#define BGP_INFO_COUNTABLE(BI) \
(! CHECK_FLAG ((BI)->flags, BGP_INFO_HISTORY) \
&& ! CHECK_FLAG ((BI)->flags, BGP_INFO_REMOVED))
#define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV)
#define PEER_UPDGRP_AF_CAP_FLAGS (PEER_CAP_ORF_PREFIX_SM_RCV | \
- PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV |\
+ PEER_CAP_ENHE_AF_NEGO)
typedef enum
{
route_map_sets_nh = CHECK_FLAG (vec->flags,
BPACKET_ATTRVEC_FLAGS_RMAP_CHANGED);
- if (paf->afi == AFI_IP)
+ if (paf->afi == AFI_IP && !peer_cap_enhe(paf->peer))
{
struct in_addr v4nh;
#endif
}
- else if (paf->afi == AFI_IP6)
+ else if (paf->afi == AFI_IP6 || peer_cap_enhe(paf->peer))
{
struct in6_addr v6nhglobal;
struct in6_addr v6nhlocal;
}
}
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if ((afi == AFI_IP && safi == SAFI_UNICAST) &&
+ !peer_cap_enhe(peer))
stream_put_prefix (s, &rn->p);
else
{
if (stream_empty (snlri))
mpattrlen_pos = bgp_packet_mpattr_start (snlri, afi, safi,
- &vecarr, adv->baa->attr);
+ (peer_cap_enhe(peer) ? AFI_IP6 : afi),
+ &vecarr, adv->baa->attr);
bgp_packet_mpattr_prefix (snlri, afi, safi, &rn->p, prd, tag);
}
struct stream *s;
struct bgp_adj_out *adj;
struct bgp_advertise *adv;
+ struct peer *peer;
struct bgp_node *rn;
bgp_size_t unfeasible_len;
bgp_size_t total_attr_len;
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);
s = subgrp->work;
else
first_time = 0;
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if (afi == AFI_IP && safi == SAFI_UNICAST &&
+ !peer_cap_enhe(peer))
stream_put_prefix (s, &rn->p);
else
{
if (!stream_empty (s))
{
- if (afi == AFI_IP && safi == SAFI_UNICAST)
+ if (afi == AFI_IP && safi == SAFI_UNICAST &&
+ !peer_cap_enhe(peer))
{
unfeasible_len
= stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN;
stream_putw_at (s, pos, total_attr_len);
/* NLRI set. */
- if (p.family == AF_INET && safi == SAFI_UNICAST)
+ if (p.family == AF_INET && safi == SAFI_UNICAST &&
+ !peer_cap_enhe(peer))
stream_put_prefix (s, &p);
/* Set size. */
void
subgroup_default_withdraw_packet (struct update_subgroup *subgrp)
{
+ struct peer *peer;
struct stream *s;
struct stream *packet;
struct prefix p;
if (DISABLE_BGP_ANNOUNCE)
return;
+ peer = SUBGRP_PEER (subgrp);
afi = SUBGRP_AFI (subgrp);
safi = SUBGRP_SAFI (subgrp);
stream_putw (s, 0);
/* Withdrawn Routes. */
- if (p.family == AF_INET && safi == SAFI_UNICAST)
+ if (p.family == AF_INET && safi == SAFI_UNICAST &&
+ !peer_cap_enhe(peer))
{
stream_put_prefix (s, &p);
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY);
}
+/* neighbor capability extended next hop encoding */
+DEFUN (neighbor_capability_enhe,
+ neighbor_capability_enhe_cmd,
+ NEIGHBOR_CMD2 "capability extended-nexthop",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise extended next-hop capability to the peer\n")
+{
+ return peer_flag_set_vty (vty, argv[0], PEER_FLAG_CAPABILITY_ENHE);
+}
+
+DEFUN (no_neighbor_capability_enhe,
+ no_neighbor_capability_enhe_cmd,
+ NO_NEIGHBOR_CMD2 "capability extended-nexthop",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise extended next-hop capability to the peer\n")
+{
+ return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_CAPABILITY_ENHE);
+}
+
static int
peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi,
safi_t safi, u_int32_t flag, int set)
vty_out (vty, "%s", VTY_NEWLINE);
}
+ /* Extended nexthop */
+ if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_RCV)
+ || CHECK_FLAG (p->cap, PEER_CAP_ENHE_ADV))
+ {
+ vty_out (vty, " Extended nexthop:");
+ if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_ADV))
+ vty_out (vty, " advertised");
+ if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_RCV))
+ vty_out (vty, " %sreceived",
+ CHECK_FLAG (p->cap, PEER_CAP_ENHE_ADV) ? "and " : "");
+ vty_out (vty, "%s", VTY_NEWLINE);
+
+ if (CHECK_FLAG (p->cap, PEER_CAP_ENHE_RCV))
+ {
+ vty_out (vty, " Address families by peer:%s ", VTY_NEWLINE);
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (p->af_cap[AFI_IP][safi], PEER_CAP_ENHE_AF_RCV))
+ vty_out (vty, " %s%s",
+ afi_safi_print (AFI_IP, safi), VTY_NEWLINE);
+ }
+ }
+
/* Route Refresh */
if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV)
|| CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV)
install_element (BGP_NODE, &neighbor_capability_route_refresh_cmd);
install_element (BGP_NODE, &no_neighbor_capability_route_refresh_cmd);
+ /* "neighbor capability extended-nexthop" commands.*/
+ install_element (BGP_NODE, &neighbor_capability_enhe_cmd);
+ install_element (BGP_NODE, &no_neighbor_capability_enhe_cmd);
+
/* "neighbor capability orf prefix-list" commands.*/
install_element (BGP_NODE, &neighbor_capability_orf_prefix_cmd);
install_element (BGP_NODE, &no_neighbor_capability_orf_prefix_cmd);
nhcount = 1 + bgp_info_mpath_count (info);
- if (p->family == AF_INET)
+ if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(info->attr))
{
struct zapi_ipv4 api;
struct in_addr *nexthop;
#ifdef HAVE_IPV6
/* We have to think about a IPv6 link-local address curse. */
- if (p->family == AF_INET6)
+ if (p->family == AF_INET6 ||
+ (p->family == AF_INET && BGP_ATTR_NEXTHOP_AFI_IP6(info->attr)))
{
unsigned int ifindex;
struct in6_addr *nexthop;
api.tag = tag;
}
- if (bgp_debug_zebra(p))
+ if (p->family == AF_INET)
{
- int i;
- zlog_debug("Zebra send: IPv6 route %s %s/%d metric %u tag %d",
- valid_nh_count ? "add" : "delete",
- 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])));
+ if (bgp_debug_zebra(p))
+ {
+ int i;
+ zlog_debug("Zebra send: IPv4 route %s %s/%d metric %u tag %d",
+ valid_nh_count ? "add" : "delete",
+ 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])));
+ }
+
+ if (valid_nh_count)
+ zapi_ipv4_route_ipv6_nexthop (ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD,
+ zclient, (struct prefix_ipv4 *) p, &api);
+ else
+ zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE,
+ zclient, (struct prefix_ipv4 *) p, &api);
}
+ else
+ {
+ if (bgp_debug_zebra(p))
+ {
+ int i;
+ zlog_debug("Zebra send: IPv6 route %s %s/%d metric %u tag %d",
+ valid_nh_count ? "add" : "delete",
+ 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])));
+ }
- zapi_ipv6_route (valid_nh_count ? ZEBRA_IPV6_ROUTE_ADD : ZEBRA_IPV6_ROUTE_DELETE,
- zclient, (struct prefix_ipv6 *) p, &api);
+ zapi_ipv6_route (valid_nh_count ?
+ ZEBRA_IPV6_ROUTE_ADD : ZEBRA_IPV6_ROUTE_DELETE,
+ zclient, (struct prefix_ipv6 *) p, &api);
+ }
}
#endif /* HAVE_IPV6 */
}
{ PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none },
{ PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset },
{ PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset },
+ { PEER_FLAG_CAPABILITY_ENHE, 0, peer_change_reset },
{ PEER_FLAG_BFD, 0, peer_change_none },
{ 0, 0, 0 }
};
vty_out (vty, " neighbor %s capability dynamic%s", addr,
VTY_NEWLINE);
+ /* Extended next-hop capability. */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ if (! peer_group_active (peer) ||
+ ! CHECK_FLAG (g_peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ vty_out (vty, " neighbor %s capability extended-nexthop%s", addr,
+ VTY_NEWLINE);
+
/* dont capability negotiation. */
if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
if (! peer_group_active (peer) ||
#define PEER_CAP_ADDPATH_RCV (1 << 12) /* addpath received */
#define PEER_CAP_HOSTNAME_ADV (1 << 13) /* hostname advertised */
#define PEER_CAP_HOSTNAME_RCV (1 << 14) /* hostname received */
+#define PEER_CAP_ENHE_ADV (1 << 15) /* Extended nexthop advertised */
+#define PEER_CAP_ENHE_RCV (1 << 16) /* Extended nexthop received */
/* Capability flags (reset in bgp_stop) */
u_int16_t af_cap[AFI_MAX][SAFI_MAX];
#define PEER_CAP_ADDPATH_AF_TX_RCV (1 << 9) /* addpath tx received */
#define PEER_CAP_ADDPATH_AF_RX_ADV (1 << 10) /* addpath rx advertised */
#define PEER_CAP_ADDPATH_AF_RX_RCV (1 << 11) /* addpath rx received */
+#define PEER_CAP_ENHE_AF_ADV (1 << 12) /* Extended nexthopi afi/safi advertised */
+#define PEER_CAP_ENHE_AF_RCV (1 << 13) /* Extended nexthop afi/safi received */
+#define PEER_CAP_ENHE_AF_NEGO (1 << 14) /* Extended nexthop afi/safi negotiated */
/* Global configuration flags. */
u_int32_t flags;
#define PEER_FLAG_BFD (1 << 11) /* bfd */
#define PEER_FLAG_LONESOUL (1 << 12)
#define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 13) /* dynamic neighbor */
+#define PEER_FLAG_CAPABILITY_ENHE (1 << 14) /* Extended next-hop (rfc 5549)*/
/* NSF mode (graceful restart) */
u_char nsf[AFI_MAX][SAFI_MAX];
return (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR)) ? 1 : 0;
}
+/*
+ * Currently supporting RFC 5549 for AFI_IP/SAFI_UNICAST only.
+ *
+ * Note: When other RFC-5549 applicable SAFIs to be supported, that should
+ * come as an argument to this routine.
+ */
+static inline int
+peer_cap_enhe (struct peer *peer)
+{
+ return (CHECK_FLAG(peer->af_cap[AFI_IP][SAFI_UNICAST], PEER_CAP_ENHE_AF_NEGO));
+}
+
#endif /* _QUAGGA_BGPD_H */
}
#ifdef HAVE_IPV6
+int
+zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient,
+ struct prefix_ipv4 *p, struct zapi_ipv6 *api)
+{
+ int i;
+ int psize;
+ struct stream *s;
+
+ /* Reset stream. */
+ s = zclient->obuf;
+ stream_reset (s);
+
+ zclient_create_header (s, cmd);
+
+ /* Put type and nexthop. */
+ stream_putc (s, api->type);
+ stream_putw (s, api->instance);
+ stream_putc (s, api->flags);
+ stream_putc (s, api->message);
+ stream_putw (s, api->safi);
+
+ /* Put prefix information. */
+ psize = PSIZE (p->prefixlen);
+ stream_putc (s, p->prefixlen);
+ stream_write (s, (u_char *) & p->prefix, psize);
+
+ /* Nexthop, ifindex, distance and metric information. */
+ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP))
+ {
+ if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE))
+ {
+ stream_putc (s, 1);
+ stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE);
+ /* XXX assert(api->nexthop_num == 0); */
+ /* XXX assert(api->ifindex_num == 0); */
+ }
+ else
+ stream_putc (s, api->nexthop_num + api->ifindex_num);
+
+ for (i = 0; i < api->nexthop_num; i++)
+ {
+ stream_putc (s, ZEBRA_NEXTHOP_IPV6);
+ stream_write (s, (u_char *)api->nexthop[i], 16);
+ }
+ for (i = 0; i < api->ifindex_num; i++)
+ {
+ stream_putc (s, ZEBRA_NEXTHOP_IFINDEX);
+ stream_putl (s, api->ifindex[i]);
+ }
+ }
+
+ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE))
+ stream_putc (s, api->distance);
+ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC))
+ stream_putl (s, api->metric);
+ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG))
+ stream_putw (s, api->tag);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ return zclient_send_message(zclient);
+}
+
int
zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
struct zapi_ipv6 *api)
extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient,
struct prefix_ipv6 *p, struct zapi_ipv6 *api);
+extern int zapi_ipv4_route_ipv6_nexthop (u_char, struct zclient *,
+ struct prefix_ipv4 *, struct zapi_ipv6 *);
#endif /* HAVE_IPV6 */
#endif /* _ZEBRA_ZCLIENT_H */
#define ZEBRA_IMPORT_ROUTE_REGISTER 30
#define ZEBRA_IMPORT_ROUTE_UNREGISTER 31
#define ZEBRA_IMPORT_CHECK_UPDATE 32
-#define ZEBRA_MESSAGE_MAX 33
+#define ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD 33
+#define ZEBRA_MESSAGE_MAX 34
/* Marker value used in new Zserv, in the byte location corresponding
* the command value in the old zserv header. To allow old and new
u_char distance, u_int32_t vrf_id);
extern int
-rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t,
+rib_add_ipv6_multipath (struct prefix *, struct rib *, safi_t,
unsigned long);
extern int
}
int
-rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi,
+rib_add_ipv6_multipath (struct prefix *p, struct rib *rib, safi_t safi,
unsigned long ifindex)
{
struct route_table *table;
int ret = 0;
unsigned int table_id = 0;
- if (rib)
- table_id = rib->table;
- else
- return 0; /* why are we getting called with NULL rib */
-
- /* Lookup table. */
- if ((table_id == RT_TABLE_MAIN) || (table_id == zebrad.rtm_table_default))
+ if (p->family == AF_INET)
{
- table = vrf_table (AFI_IP6, safi, 0);
+ if (!rib)
+ return 0;
+
+ table = vrf_table (AFI_IP, safi, 0);
+ if (!table)
+ return 0;
+ /* Make it sure prefixlen is applied to the prefix. */
+ apply_mask_ipv4 (p);
}
else
{
- table = vrf_other_route_table(AFI_IP6, table_id, 0);
- }
+ if (rib)
+ table_id = rib->table;
+ else
+ return 0; /* why are we getting called with NULL rib */
- if (! table)
- return 0;
+ /* Lookup table. */
+ if ((table_id == RT_TABLE_MAIN) || (table_id == zebrad.rtm_table_default))
+ {
+ table = vrf_table (AFI_IP6, safi, 0);
+ }
+ else
+ {
+ table = vrf_other_route_table(AFI_IP6, table_id, 0);
+ }
- /* Make sure mask is applied. */
- apply_mask_ipv6 (p);
+ if (! table)
+ return 0;
+
+ /* Make sure mask is applied. */
+ apply_mask_ipv6 (p);
+
+ }
/* Set default distance by route type. */
if (rib->distance == 0)
struct rib *rib;
struct nexthop *nexthop, *tnexthop;
int recursing;
+ char buf[BUFSIZ];
RNODE_FOREACH_RIB (rn, rib)
{
if (nexthop->ifindex)
vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex));
break;
+#ifdef HAVE_IPV6
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFNAME:
+ vty_out (vty, " %s",
+ inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+ if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+ vty_out (vty, ", %s", nexthop->ifname);
+ else if (nexthop->ifindex)
+ vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex));
+ break;
+#endif /* HAVE_IPV6 */
case NEXTHOP_TYPE_IFINDEX:
vty_out (vty, " directly connected, %s",
ifindex2ifname (nexthop->ifindex));
if (nexthop->ifindex)
vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex));
break;
+#ifdef HAVE_IPV6
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFNAME:
+ vty_out (vty, " via %s",
+ inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+ if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+ vty_out (vty, ", %s", nexthop->ifname);
+ else if (nexthop->ifindex)
+ vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex));
+ break;
+#endif /* HAVE_IPV6 */
+
case NEXTHOP_TYPE_IFINDEX:
vty_out (vty, " is directly connected, %s",
ifindex2ifname (nexthop->ifindex));
vty_out (vty, SHOW_ROUTE_V4_HEADER);
first = 0;
}
- vty_show_ip_route (vty, rn, rib);
+ vty_show_ip_route (vty, rn, rib);
}
return CMD_SUCCESS;
}
}
#ifdef HAVE_IPV6
+/* Zebra server IPv6 prefix add function. */
+static int
+zread_ipv4_route_ipv6_nexthop_add (struct zserv *client, u_short length)
+{
+ int i;
+ struct stream *s;
+ struct in6_addr nexthop;
+ struct rib *rib;
+ u_char message;
+ u_char nexthop_num;
+ u_char nexthop_type;
+ unsigned long ifindex;
+ struct prefix_ipv4 p;
+ u_char ifname_len;
+ safi_t safi;
+ static struct in6_addr nexthops[MULTIPATH_NUM];
+ static unsigned int ifindices[MULTIPATH_NUM];
+ int ret;
+
+ /* Get input stream. */
+ s = client->ibuf;
+
+ ifindex = 0;
+ memset (&nexthop, 0, sizeof (struct in6_addr));
+
+ /* Allocate new rib. */
+ rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+
+ /* Type, flags, message. */
+ rib->type = stream_getc (s);
+ rib->instance = stream_getw (s);
+ rib->flags = stream_getc (s);
+ message = stream_getc (s);
+ safi = stream_getw (s);
+ rib->uptime = time (NULL);
+
+ /* IPv4 prefix. */
+ memset (&p, 0, sizeof (struct prefix_ipv4));
+ p.family = AF_INET;
+ p.prefixlen = stream_getc (s);
+ stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+ /* We need to give nh-addr, nh-ifindex with the same next-hop object
+ * to the rib to ensure that IPv6 multipathing works; need to coalesce
+ * these. Clients should send the same number of paired set of
+ * next-hop-addr/next-hop-ifindices. */
+ if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
+ {
+ int nh_count = 0;
+ int if_count = 0;
+ int max_nh_if = 0;
+
+ nexthop_num = stream_getc (s);
+ for (i = 0; i < nexthop_num; i++)
+ {
+ nexthop_type = stream_getc (s);
+
+ switch (nexthop_type)
+ {
+ case ZEBRA_NEXTHOP_IPV6:
+ stream_get (&nexthop, s, 16);
+ if (nh_count < MULTIPATH_NUM) {
+ nexthops[nh_count++] = nexthop;
+ }
+ break;
+ case ZEBRA_NEXTHOP_IFINDEX:
+ if (if_count < MULTIPATH_NUM) {
+ ifindices[if_count++] = stream_getl (s);
+ }
+ break;
+ case ZEBRA_NEXTHOP_BLACKHOLE:
+ nexthop_blackhole_add (rib);
+ break;
+ }
+ }
+
+ max_nh_if = (nh_count > if_count) ? nh_count : if_count;
+ for (i = 0; i < max_nh_if; i++)
+ {
+ if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i])) {
+ if ((i < if_count) && ifindices[i]) {
+ nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]);
+ }
+ else {
+ nexthop_ipv6_add (rib, &nexthops[i]);
+ }
+ }
+ else {
+ if ((i < if_count) && ifindices[i]) {
+ nexthop_ifindex_add (rib, ifindices[i]);
+ }
+ }
+ }
+ }
+
+ /* Distance. */
+ if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+ rib->distance = stream_getc (s);
+
+ /* Metric. */
+ if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+ rib->metric = stream_getl (s);
+
+ /* Tag */
+ if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG))
+ rib->tag = stream_getw (s);
+ else
+ rib->tag = 0;
+
+ /* Table */
+ rib->table=zebrad.rtm_table_default;
+ ret = rib_add_ipv6_multipath ((struct prefix *)&p, rib, safi, ifindex);
+ /* Stats */
+ if (ret > 0)
+ client->v4_route_add_cnt++;
+ else if (ret < 0)
+ client->v4_route_upd8_cnt++;
+
+ return 0;
+}
+
/* Zebra server IPv6 prefix add function. */
static int
zread_ipv6_add (struct zserv *client, u_short length)
/* Table */
rib->table=zebrad.rtm_table_default;
- ret = rib_add_ipv6_multipath (&p, rib, safi, ifindex);
+ ret = rib_add_ipv6_multipath ((struct prefix *)&p, rib, safi, ifindex);
/* Stats */
if (ret > 0)
client->v6_route_add_cnt++;
zread_ipv4_delete (client, length);
break;
#ifdef HAVE_IPV6
+ case ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD:
+ zread_ipv4_route_ipv6_nexthop_add (client, length);
+ break;
case ZEBRA_IPV6_ROUTE_ADD:
zread_ipv6_add (client, length);
break;