Initial revision
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
new file mode 100644
index 0000000..a3e86b0
--- /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);
+}