summaryrefslogtreecommitdiff
path: root/bgpd/bgp_open.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_open.c')
-rw-r--r--bgpd/bgp_open.c793
1 files changed, 793 insertions, 0 deletions
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
new file mode 100644
index 00000000..a3e86b06
--- /dev/null
+++ b/bgpd/bgp_open.c
@@ -0,0 +1,793 @@
+/* BGP open message handling
+ Copyright (C) 1998, 1999 Kunihiro Ishiguro
+
+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 "linklist.h"
+#include "prefix.h"
+#include "stream.h"
+#include "thread.h"
+#include "log.h"
+#include "command.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+
+/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
+ negotiate remote peer supports extentions or not. But if
+ remote-peer doesn't supports negotiation process itself. We would
+ like to do manual configuration.
+
+ So there is many configurable point. First of all we want set each
+ peer whether we send capability negotiation to the peer or not.
+ Next, if we send capability to the peer we want to set my capabilty
+ inforation at each peer. */
+
+void
+bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+{
+ u_char *pnt;
+ u_char *end;
+ struct capability cap;
+
+ pnt = peer->notify.data;
+ end = pnt + peer->notify.length;
+
+ while (pnt < end)
+ {
+ memcpy(&cap, pnt, sizeof(struct capability));
+
+ if (pnt + 2 > end)
+ return;
+ if (pnt + (cap.length + 2) > end)
+ return;
+
+ if (cap.code == CAPABILITY_CODE_MP)
+ {
+ vty_out (vty, " Capability error for: Multi protocol ");
+
+ switch (ntohs (cap.mpc.afi))
+ {
+ case AFI_IP:
+ vty_out (vty, "AFI IPv4, ");
+ break;
+ case AFI_IP6:
+ vty_out (vty, "AFI IPv6, ");
+ break;
+ default:
+ vty_out (vty, "AFI Unknown %d, ", ntohs (cap.mpc.afi));
+ break;
+ }
+ switch (cap.mpc.safi)
+ {
+ case SAFI_UNICAST:
+ vty_out (vty, "SAFI Unicast");
+ break;
+ case SAFI_MULTICAST:
+ vty_out (vty, "SAFI Multicast");
+ break;
+ case SAFI_UNICAST_MULTICAST:
+ vty_out (vty, "SAFI Unicast Multicast");
+ break;
+ case BGP_SAFI_VPNV4:
+ vty_out (vty, "SAFI MPLS-VPN");
+ break;
+ default:
+ vty_out (vty, "SAFI Unknown %d ", cap.mpc.safi);
+ break;
+ }
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
+ else if (cap.code >= 128)
+ vty_out (vty, " Capability error: vendor specific capability code %d",
+ cap.code);
+ else
+ vty_out (vty, " Capability error: unknown capability code %d",
+ cap.code);
+
+ pnt += cap.length + 2;
+ }
+}
+
+/* Set negotiated capability value. */
+int
+bgp_capability_mp (struct peer *peer, struct capability *cap)
+{
+ if (ntohs (cap->mpc.afi) == AFI_IP)
+ {
+ if (cap->mpc.safi == SAFI_UNICAST)
+ {
+ peer->afc_recv[AFI_IP][SAFI_UNICAST] = 1;
+
+ if (peer->afc[AFI_IP][SAFI_UNICAST])
+ peer->afc_nego[AFI_IP][SAFI_UNICAST] = 1;
+ else
+ return -1;
+ }
+ else if (cap->mpc.safi == SAFI_MULTICAST)
+ {
+ peer->afc_recv[AFI_IP][SAFI_MULTICAST] = 1;
+
+ if (peer->afc[AFI_IP][SAFI_MULTICAST])
+ peer->afc_nego[AFI_IP][SAFI_MULTICAST] = 1;
+ else
+ return -1;
+ }
+ else if (cap->mpc.safi == BGP_SAFI_VPNV4)
+ {
+ peer->afc_recv[AFI_IP][SAFI_MPLS_VPN] = 1;
+
+ if (peer->afc[AFI_IP][SAFI_MPLS_VPN])
+ peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] = 1;
+ else
+ return -1;
+ }
+ else
+ return -1;
+ }
+#ifdef HAVE_IPV6
+ else if (ntohs (cap->mpc.afi) == AFI_IP6)
+ {
+ if (cap->mpc.safi == SAFI_UNICAST)
+ {
+ peer->afc_recv[AFI_IP6][SAFI_UNICAST] = 1;
+
+ if (peer->afc[AFI_IP6][SAFI_UNICAST])
+ peer->afc_nego[AFI_IP6][SAFI_UNICAST] = 1;
+ else
+ return -1;
+ }
+ else if (cap->mpc.safi == SAFI_MULTICAST)
+ {
+ peer->afc_recv[AFI_IP6][SAFI_MULTICAST] = 1;
+
+ if (peer->afc[AFI_IP6][SAFI_MULTICAST])
+ peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = 1;
+ else
+ return -1;
+ }
+ else
+ return -1;
+ }
+#endif /* HAVE_IPV6 */
+ else
+ {
+ /* Unknown Address Family. */
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi,
+ u_char type, u_char mode)
+{
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s Addr-family %d/%d has ORF type/mode %d/%d not supported",
+ peer->host, afi, safi, type, mode);
+}
+
+int
+bgp_capability_orf (struct peer *peer, struct capability *cap,
+ u_char *pnt)
+{
+ afi_t afi = ntohs(cap->mpc.afi);
+ safi_t safi = cap->mpc.safi;
+ u_char number_of_orfs;
+ u_char type;
+ u_char mode;
+ u_int16_t sm_cap = 0; /* capability send-mode receive */
+ u_int16_t rm_cap = 0; /* capability receive-mode receive */
+ int i;
+
+ /* Check length. */
+ if (cap->length < 7)
+ {
+ zlog_info ("%s ORF Capability length error %d",
+ peer->host, cap->length);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has ORF CAP(%s) for afi/safi: %u/%u",
+ peer->host, (cap->code == CAPABILITY_CODE_ORF ?
+ "new" : "old"), afi, safi);
+
+ /* Check AFI and SAFI. */
+ if ((afi != AFI_IP && afi != AFI_IP6)
+ || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+ && safi != BGP_SAFI_VPNV4))
+ {
+ zlog_info ("%s Addr-family %d/%d not supported. Ignoring the ORF capability",
+ peer->host, afi, safi);
+ return -1;
+ }
+
+ number_of_orfs = *pnt++;
+
+ for (i = 0 ; i < number_of_orfs ; i++)
+ {
+ type = *pnt++;
+ mode = *pnt++;
+
+ /* ORF Mode error check */
+ if (mode != ORF_MODE_BOTH && mode != ORF_MODE_SEND
+ && mode != ORF_MODE_RECEIVE)
+ {
+ bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+ continue;
+ }
+
+ /* ORF Type and afi/safi error check */
+ if (cap->code == CAPABILITY_CODE_ORF)
+ {
+ if (type == ORF_TYPE_PREFIX &&
+ ((afi == AFI_IP && safi == SAFI_UNICAST)
+ || (afi == AFI_IP && safi == SAFI_MULTICAST)
+ || (afi == AFI_IP6 && safi == SAFI_UNICAST)))
+ {
+ sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+ rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has Prefixlist ORF(%d) capability as %s for afi/safi: %d/%d",
+ peer->host, ORF_TYPE_PREFIX, (mode == ORF_MODE_SEND ? "SEND" :
+ mode == ORF_MODE_RECEIVE ? "RECEIVE" : "BOTH") , afi, safi);
+ }
+ else
+ {
+ bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+ continue;
+ }
+ }
+ else if (cap->code == CAPABILITY_CODE_ORF_OLD)
+ {
+ if (type == ORF_TYPE_PREFIX_OLD &&
+ ((afi == AFI_IP && safi == SAFI_UNICAST)
+ || (afi == AFI_IP && safi == SAFI_MULTICAST)
+ || (afi == AFI_IP6 && safi == SAFI_UNICAST)))
+ {
+ sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV;
+ rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV;
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has Prefixlist ORF(%d) capability as %s for afi/safi: %d/%d",
+ peer->host, ORF_TYPE_PREFIX_OLD, (mode == ORF_MODE_SEND ? "SEND" :
+ mode == ORF_MODE_RECEIVE ? "RECEIVE" : "BOTH") , afi, safi);
+ }
+ else
+ {
+ bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+ continue;
+ }
+ }
+ else
+ {
+ bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+ continue;
+ }
+
+ switch (mode)
+ {
+ case ORF_MODE_BOTH:
+ SET_FLAG (peer->af_cap[afi][safi], sm_cap);
+ SET_FLAG (peer->af_cap[afi][safi], rm_cap);
+ break;
+ case ORF_MODE_SEND:
+ SET_FLAG (peer->af_cap[afi][safi], sm_cap);
+ break;
+ case ORF_MODE_RECEIVE:
+ SET_FLAG (peer->af_cap[afi][safi], rm_cap);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* Parse given capability. */
+int
+bgp_capability_parse (struct peer *peer, u_char *pnt, u_char length,
+ u_char **error)
+{
+ int ret;
+ u_char *end;
+ struct capability cap;
+
+ end = pnt + length;
+
+ while (pnt < end)
+ {
+ afi_t afi;
+ safi_t safi;
+
+ /* Fetch structure to the byte stream. */
+ memcpy (&cap, pnt, sizeof (struct capability));
+
+ afi = ntohs(cap.mpc.afi);
+ safi = cap.mpc.safi;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has CAPABILITY code: %d, length %d",
+ peer->host, cap.code, cap.length);
+
+ /* We need at least capability code and capability length. */
+ if (pnt + 2 > end)
+ {
+ zlog_info ("%s Capability length error", peer->host);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ /* Capability length check. */
+ if (pnt + (cap.length + 2) > end)
+ {
+ zlog_info ("%s Capability length error", peer->host);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ /* We know MP Capability Code. */
+ if (cap.code == CAPABILITY_CODE_MP)
+ {
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u",
+ peer->host, afi, safi);
+
+ /* Ignore capability when override-capability is set. */
+ if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+ {
+ /* Set negotiated value. */
+ ret = bgp_capability_mp (peer, &cap);
+
+ /* Unsupported Capability. */
+ if (ret < 0)
+ {
+ /* Store return data. */
+ memcpy (*error, &cap, cap.length + 2);
+ *error += cap.length + 2;
+ }
+ }
+ }
+ else if (cap.code == CAPABILITY_CODE_REFRESH
+ || cap.code == CAPABILITY_CODE_REFRESH_OLD)
+ {
+ /* Check length. */
+ if (cap.length != 0)
+ {
+ zlog_info ("%s Route Refresh Capability length error %d",
+ peer->host, cap.length);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has ROUTE-REFRESH capability(%s) for all address-families",
+ peer->host,
+ cap.code == CAPABILITY_CODE_REFRESH_OLD ? "old" : "new");
+
+ /* BGP refresh capability */
+ if (cap.code == CAPABILITY_CODE_REFRESH_OLD)
+ SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV);
+ else
+ SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV);
+ }
+ else if (cap.code == CAPABILITY_CODE_ORF
+ || cap.code == CAPABILITY_CODE_ORF_OLD)
+ bgp_capability_orf (peer, &cap, pnt + sizeof (struct capability));
+ else if (cap.code == CAPABILITY_CODE_DYNAMIC)
+ {
+ /* Check length. */
+ if (cap.length != 0)
+ {
+ zlog_info ("%s Dynamic Capability length error %d",
+ peer->host, cap.length);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s OPEN has DYNAMIC capability", peer->host);
+
+ SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV);
+ }
+
+ else if (cap.code > 128)
+ {
+ /* We don't send Notification for unknown vendor specific
+ capabilities. It seems reasonable for now... */
+ zlog_warn ("%s Vendor specific capability %d",
+ peer->host, cap.code);
+ }
+ else
+ {
+ zlog_warn ("%s unrecognized capability code: %d - ignored",
+ peer->host, cap.code);
+ memcpy (*error, &cap, cap.length + 2);
+ *error += cap.length + 2;
+ }
+
+ pnt += cap.length + 2;
+ }
+ return 0;
+}
+
+int
+bgp_auth_parse (struct peer *peer, u_char *pnt, size_t length)
+{
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_AUTH_FAILURE);
+ return -1;
+}
+
+int
+strict_capability_same (struct peer *peer)
+{
+ int i, j;
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+ if (peer->afc[i][j] != peer->afc_nego[i][j])
+ return 0;
+ return 1;
+}
+
+/* Parse open option */
+int
+bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
+{
+ int ret;
+ u_char *end;
+ u_char opt_type;
+ u_char opt_length;
+ u_char *pnt;
+ u_char *error;
+ u_char error_data[BGP_MAX_PACKET_SIZE];
+
+ /* Fetch pointer. */
+ pnt = stream_pnt (peer->ibuf);
+
+ ret = 0;
+ opt_type = 0;
+ opt_length = 0;
+ end = pnt + length;
+ error = error_data;
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s rcv OPEN w/ OPTION parameter len: %u",
+ peer->host, length);
+
+ while (pnt < end)
+ {
+ /* Check the length. */
+ if (pnt + 2 > end)
+ {
+ zlog_info ("%s Option length error", peer->host);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ /* Fetch option type and length. */
+ opt_type = *pnt++;
+ opt_length = *pnt++;
+
+ /* Option length check. */
+ if (pnt + opt_length > end)
+ {
+ zlog_info ("%s Option length error", peer->host);
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ return -1;
+ }
+
+ if (BGP_DEBUG (normal, NORMAL))
+ zlog_info ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
+ peer->host, opt_type,
+ opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" :
+ opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown",
+ opt_length);
+
+ switch (opt_type)
+ {
+ case BGP_OPEN_OPT_AUTH:
+ ret = bgp_auth_parse (peer, pnt, opt_length);
+ break;
+ case BGP_OPEN_OPT_CAP:
+ ret = bgp_capability_parse (peer, pnt, opt_length, &error);
+ *capability = 1;
+ break;
+ default:
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_PARAM);
+ ret = -1;
+ break;
+ }
+
+ /* Parse error. To accumulate all unsupported capability codes,
+ bgp_capability_parse does not return -1 when encounter
+ unsupported capability code. To detect that, please check
+ error and erro_data pointer, like below. */
+ if (ret < 0)
+ return -1;
+
+ /* Forward pointer. */
+ pnt += opt_length;
+ }
+
+ /* All OPEN option is parsed. Check capability when strict compare
+ flag is enabled.*/
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
+ {
+ /* If Unsupported Capability exists. */
+ if (error != error_data)
+ {
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data, error - error_data);
+ return -1;
+ }
+
+ /* Check local capability does not negotiated with remote
+ peer. */
+ if (! strict_capability_same (peer))
+ {
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+
+ /* Check there is no common capability send Unsupported Capability
+ error. */
+ if (*capability && ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+ {
+ if (! peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST])
+ {
+ plog_err (peer->log, "%s [Error] No common capability", peer->host);
+
+ if (error != error_data)
+
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data, error - error_data);
+ else
+ bgp_notify_send (peer,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+bgp_open_capability_orf (struct stream *s, struct peer *peer,
+ afi_t afi, safi_t safi, u_char code)
+{
+ u_char cap_len;
+ u_char orf_len;
+ unsigned long capp;
+ unsigned long orfp;
+ unsigned long numberp;
+ int number_of_orfs = 0;
+
+ if (safi == SAFI_MPLS_VPN)
+ safi = BGP_SAFI_VPNV4;
+
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_putp (s); /* Set Capability Len Pointer */
+ stream_putc (s, 0); /* Capability Length */
+ stream_putc (s, code); /* Capability Code */
+ orfp = stream_get_putp (s); /* Set ORF Len Pointer */
+ stream_putc (s, 0); /* ORF Length */
+ stream_putw (s, afi);
+ stream_putc (s, 0);
+ stream_putc (s, safi);
+ numberp = stream_get_putp (s); /* Set Number Pointer */
+ stream_putc (s, 0); /* Number of ORFs */
+
+ /* Address Prefix ORF */
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+ {
+ stream_putc (s, (code == CAPABILITY_CODE_ORF ?
+ ORF_TYPE_PREFIX : ORF_TYPE_PREFIX_OLD));
+
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+ && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+ {
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV);
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc (s, ORF_MODE_BOTH);
+ }
+ else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM))
+ {
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV);
+ stream_putc (s, ORF_MODE_SEND);
+ }
+ else
+ {
+ SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc (s, ORF_MODE_RECEIVE);
+ }
+ number_of_orfs++;
+ }
+
+ /* Total Number of ORFs. */
+ stream_putc_at (s, numberp, number_of_orfs);
+
+ /* Total ORF Len. */
+ orf_len = stream_get_putp (s) - orfp - 1;
+ stream_putc_at (s, orfp, orf_len);
+
+ /* Total Capability Len. */
+ cap_len = stream_get_putp (s) - capp - 1;
+ stream_putc_at (s, capp, cap_len);
+}
+
+/* Fill in capability open option to the packet. */
+void
+bgp_open_capability (struct stream *s, struct peer *peer)
+{
+ u_char len;
+ unsigned long cp;
+ afi_t afi;
+ safi_t safi;
+
+ /* Remember current pointer for Opt Parm Len. */
+ cp = stream_get_putp (s);
+
+ /* Opt Parm Len. */
+ stream_putc (s, 0);
+
+ /* Do not send capability. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)
+ || CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
+ return;
+
+ /* When the peer is IPv4 unicast only, do not send capability. */
+ if (! peer->afc[AFI_IP][SAFI_MULTICAST]
+ && ! peer->afc[AFI_IP][SAFI_MPLS_VPN]
+ && ! peer->afc[AFI_IP6][SAFI_UNICAST]
+ && ! peer->afc[AFI_IP6][SAFI_MULTICAST]
+ && CHECK_FLAG (peer->flags, PEER_FLAG_NO_ROUTE_REFRESH_CAP)
+ && ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+ PEER_FLAG_ORF_PREFIX_SM)
+ && ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+ PEER_FLAG_ORF_PREFIX_RM)
+ && ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST],
+ PEER_FLAG_ORF_PREFIX_SM)
+ && ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST],
+ PEER_FLAG_ORF_PREFIX_RM)
+ && ! CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+ return;
+
+ /* IPv4 unicast. */
+ if (peer->afc[AFI_IP][SAFI_UNICAST])
+ {
+ peer->afc_adv[AFI_IP][SAFI_UNICAST] = 1;
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, AFI_IP);
+ stream_putc (s, 0);
+ stream_putc (s, SAFI_UNICAST);
+ }
+ /* IPv4 multicast. */
+ if (peer->afc[AFI_IP][SAFI_MULTICAST])
+ {
+ peer->afc_adv[AFI_IP][SAFI_MULTICAST] = 1;
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, AFI_IP);
+ stream_putc (s, 0);
+ stream_putc (s, SAFI_MULTICAST);
+ }
+ /* IPv4 VPN */
+ if (peer->afc[AFI_IP][SAFI_MPLS_VPN])
+ {
+ peer->afc_adv[AFI_IP][SAFI_MPLS_VPN] = 1;
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, AFI_IP);
+ stream_putc (s, 0);
+ stream_putc (s, BGP_SAFI_VPNV4);
+ }
+#ifdef HAVE_IPV6
+ /* IPv6 unicast. */
+ if (peer->afc[AFI_IP6][SAFI_UNICAST])
+ {
+ peer->afc_adv[AFI_IP6][SAFI_UNICAST] = 1;
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, AFI_IP6);
+ stream_putc (s, 0);
+ stream_putc (s, SAFI_UNICAST);
+ }
+ /* IPv6 multicast. */
+ if (peer->afc[AFI_IP6][SAFI_MULTICAST])
+ {
+ peer->afc_adv[AFI_IP6][SAFI_MULTICAST] = 1;
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_MP);
+ stream_putc (s, CAPABILITY_CODE_MP_LEN);
+ stream_putw (s, AFI_IP6);
+ stream_putc (s, 0);
+ stream_putc (s, SAFI_MULTICAST);
+ }
+#endif /* HAVE_IPV6 */
+
+ /* Route refresh. */
+ if (! CHECK_FLAG (peer->flags, PEER_FLAG_NO_ROUTE_REFRESH_CAP))
+ {
+ SET_FLAG (peer->cap, PEER_CAP_REFRESH_ADV);
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_REFRESH_OLD);
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN);
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_REFRESH);
+ stream_putc (s, CAPABILITY_CODE_REFRESH_LEN);
+ }
+
+ /* ORF capability. */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+ {
+ bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF_OLD);
+ bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF);
+ }
+
+ /* Dynamic capability. */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+ {
+ SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV);
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC);
+ stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN);
+ }
+
+ /* Total Opt Parm Len. */
+ len = stream_get_putp (s) - cp - 1;
+ stream_putc_at (s, cp, len);
+}