/* 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 #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" #include "bgpd/bgp_vty.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) { char *pnt; 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_debug ("%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_debug ("%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_debug ("%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_debug ("%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_debug ("%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_debug ("%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 != CAPABILITY_CODE_REFRESH_LEN) { 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_debug ("%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_RESTART) { struct graceful_restart_af graf; u_int16_t restart_flag_time; int restart_bit = 0; int forwarding_bit = 0; u_char *restart_pnt; u_char *restart_end; /* Check length. */ if (cap.length < CAPABILITY_CODE_RESTART_LEN) { zlog_info ("%s Graceful Restart Capability length error %d", peer->host, cap.length); bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); return -1; } SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV); restart_flag_time = ntohs(cap.mpc.afi); if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT)) restart_bit = 1; UNSET_FLAG (restart_flag_time, 0xF000); peer->restart_time_rcv = restart_flag_time; if (BGP_DEBUG (normal, NORMAL)) { zlog_debug ("%s OPEN has Graceful Restart capability", peer->host); zlog_debug ("%s Peer has%srestarted. Restart Time : %d", peer->host, restart_bit ? " " : " not ", peer->restart_time_rcv); } restart_pnt = pnt + 4; restart_end = pnt + cap.length + 2; while (restart_pnt < restart_end) { memcpy (&graf, restart_pnt, sizeof (struct graceful_restart_af)); afi = ntohs(graf.afi); safi = graf.safi; if (CHECK_FLAG (graf.flag, RESTART_F_BIT)) forwarding_bit = 1; if (strcmp (afi_safi_print (afi, safi), "Unknown") == 0) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported. I gnore the Graceful Restart capability", peer->host, afi, safi); } else if (! peer->afc[afi][safi]) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled. Ignore the Graceful Restart capability", peer->host, afi, safi); } else { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s Address family %s is%spreserved", peer->host, afi_safi_print (afi, safi), forwarding_bit ? " " : " not "); if (forwarding_bit) SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV); } forwarding_bit = 0; restart_pnt += 4; } } else if (cap.code == CAPABILITY_CODE_DYNAMIC) { /* Check length. */ if (cap.length != CAPABILITY_CODE_DYNAMIC_LEN) { 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_debug ("%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_debug ("%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_debug ("%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); } /* Graceful restart capability */ if (bgp_flag_check (peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV); stream_putc (s, BGP_OPEN_OPT_CAP); stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); stream_putc (s, CAPABILITY_CODE_RESTART); stream_putc (s, CAPABILITY_CODE_RESTART_LEN); stream_putw (s, peer->bgp->restart_time); } /* Total Opt Parm Len. */ len = stream_get_putp (s) - cp - 1; stream_putc_at (s, cp, len); }