]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_open.c
Added json formating support to show-...-neighbors-... bgp commands.
[mirror_frr.git] / bgpd / bgp_open.c
index de24d435a288e9901b7556637452a72642b44363..a530542a49719d49a839b9a70d2a1c6345b61872 100644 (file)
@@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "memory.h"
 #include "queue.h"
 
+#include "lib/json.h"
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_debug.h"
@@ -49,12 +50,16 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    inforation at each peer. */
 
 void
-bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+bgp_capability_vty_out (struct vty *vty, struct peer *peer, u_char use_json, json_object *json_neigh)
 {
   char *pnt;
   char *end;
   struct capability_mp_data mpc;
   struct capability_header *hdr;
+  json_object *json_cap = NULL;
+
+  if (use_json)
+    json_cap = json_object_new_object();
 
   pnt = peer->notify.data;
   end = pnt + peer->notify.length;
@@ -72,46 +77,89 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
 
       if (hdr->code == CAPABILITY_CODE_MP)
        {
-         vty_out (vty, "  Capability error for: Multi protocol ");
-
-         switch (ntohs (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 (mpc.afi));
-             break;
-           }
-         switch (mpc.safi)
-           {
-           case SAFI_UNICAST:
-             vty_out (vty, "SAFI Unicast");
-             break;
-           case SAFI_MULTICAST:
-             vty_out (vty, "SAFI Multicast");
-             break;
-           case SAFI_MPLS_LABELED_VPN:
-             vty_out (vty, "SAFI MPLS-labeled VPN");
-             break;
-           default:
-             vty_out (vty, "SAFI Unknown %d ", mpc.safi);
-             break;
-           }
-         vty_out (vty, "%s", VTY_NEWLINE);
-       }
+          if (use_json)
+            {
+              switch (ntohs (mpc.afi))
+                {
+                  case AFI_IP:
+                    json_object_string_add(json_cap, "capabilityErrorMultiProtocolAfi", "IPv4");
+                  break;
+                  case AFI_IP6:
+                    json_object_string_add(json_cap, "capabilityErrorMultiProtocolAfi", "IPv6");
+                  break;
+                  default:
+                    json_object_int_add(json_cap, "capabilityErrorMultiProtocolAfiUnknown", ntohs (mpc.afi));
+                  break;
+                }
+              switch (mpc.safi)
+                {
+                  case SAFI_UNICAST:
+                    json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "unicast");
+                  break;
+                  case SAFI_MULTICAST:
+                    json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "multicast");
+                  break;
+                  case SAFI_MPLS_LABELED_VPN:
+                    json_object_string_add(json_cap, "capabilityErrorMultiProtocolSafi", "MPLS-labeled VPN");
+                  break;
+                  default:
+                    json_object_int_add(json_cap, "capabilityErrorMultiProtocolSafiUnknown", mpc.safi);
+                  break;
+                }
+            }
+          else
+            {
+              vty_out (vty, "  Capability error for: Multi protocol ");
+              switch (ntohs (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 (mpc.afi));
+                  break;
+                }
+              switch (mpc.safi)
+                {
+                  case SAFI_UNICAST:
+                    vty_out (vty, "SAFI Unicast");
+                  break;
+                  case SAFI_MULTICAST:
+                    vty_out (vty, "SAFI Multicast");
+                  break;
+                 case SAFI_MPLS_LABELED_VPN:
+                    vty_out (vty, "SAFI MPLS-labeled VPN");
+                  break;
+                  default:
+                    vty_out (vty, "SAFI Unknown %d ", mpc.safi);
+                  break;
+                }
+              vty_out (vty, "%s", VTY_NEWLINE);
+            }
+        }
       else if (hdr->code >= 128)
-       vty_out (vty, "  Capability error: vendor specific capability code %d",
-                hdr->code);
+        {
+          if (use_json)
+            json_object_int_add(json_cap, "capabilityErrorVendorSpecificCapabilityCode", hdr->code);
+          else
+            vty_out (vty, "  Capability error: vendor specific capability code %d",
+                     hdr->code);
+        }
       else
-       vty_out (vty, "  Capability error: unknown capability code %d", 
-                hdr->code);
-
+        {
+          if (use_json)
+            json_object_int_add(json_cap, "capabilityErrorUnknownCapabilityCode", hdr->code);
+          else
+            vty_out (vty, "  Capability error: unknown capability code %d",
+                     hdr->code);
+        }
       pnt += hdr->length + 2;
     }
+  if (use_json)
+    json_object_object_add(json_neigh, "capabilityErrors", json_cap);
 }
 
 static void 
@@ -154,6 +202,14 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
   struct capability_mp_data mpc;
   struct stream *s = BGP_INPUT (peer);
   
+  /* Verify length is 4 */
+  if (hdr->length != 4)
+    {
+      zlog_warn("MP Cap: Received invalid length %d, non-multiple of 4",
+               hdr->length);
+      return -1;
+    }
+
   bgp_capability_mp_data (s, &mpc);
   
   if (bgp_debug_neighbor_events(peer))
@@ -236,7 +292,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
       zlog_info ("%s ORF Capability entry length error,"
                  " Cap length %u, num %u",
                  peer->host, hdr->length, entry.num);
-      bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+      bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
       return -1;
     }
 
@@ -342,6 +398,14 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
   int restart_bit = 0;
   size_t end = stream_get_getp (s) + caphdr->length;
 
+  /* Verify length is a multiple of 4 */
+  if ((caphdr->length-2) % 4)
+    {
+      zlog_warn("Restart Cap: Received invalid length %d, non-multiple of 4",
+               caphdr->length);
+      return -1;
+    }
+
   SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV);
   restart_flag_time = stream_getw(s);
   if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT))
@@ -370,7 +434,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
         {
           if (bgp_debug_neighbor_events(peer))
             zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported."
-                        " Ignore the Graceful Restart capability",
+                        " Ignore the Graceful Restart capability for this AFI/SAFI",
                         peer->host, afi, safi);
         }
       else if (!peer->afc[afi][safi])
@@ -398,6 +462,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
   return 0;
 }
 
+/* Unlike other capability parsing routines, this one returns 0 on error */
 static as_t
 bgp_capability_as4 (struct peer *peer, struct capability_header *hdr)
 {
@@ -426,6 +491,14 @@ bgp_capability_addpath (struct peer *peer, struct capability_header *hdr)
 
   SET_FLAG (peer->cap, PEER_CAP_ADDPATH_RCV);
 
+  /* Verify length is a multiple of 4 */
+  if (hdr->length % 4)
+    {
+      zlog_warn("Add Path: Received invalid length %d, non-multiple of 4",
+               hdr->length);
+      return -1;
+    }
+
   while (stream_get_getp (s) + 4 <= end)
     {
       afi_t afi = stream_getw (s);
@@ -439,7 +512,21 @@ bgp_capability_addpath (struct peer *peer, struct capability_header *hdr)
                     (send_receive & BGP_ADDPATH_TX) ? ", transmit" : "");
 
       if (!bgp_afi_safi_valid_indices (afi, &safi))
-        return -1;
+        {
+          if (bgp_debug_neighbor_events(peer))
+            zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported."
+                        " Ignore the Addpath Attribute for this AFI/SAFI",
+                        peer->host, afi, safi);
+         continue;
+        }
+      else if (!peer->afc[afi][safi])
+        {
+          if (bgp_debug_neighbor_events(peer))
+            zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled."
+                        " Ignore the AddPath capability for this AFI/SAFI",
+                        peer->host, afi, safi);
+         continue;
+        }
 
       if (send_receive & BGP_ADDPATH_RX)
         SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_RCV);
@@ -451,7 +538,57 @@ bgp_capability_addpath (struct peer *peer, struct capability_header *hdr)
   return 0;
 }
 
-static const struct message capcode_str[] =
+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;
+
+  /* Verify length is a multiple of 4 */
+  if (hdr->length % 6)
+    {
+      zlog_warn("Extended NH: Received invalid length %d, non-multiple of 6",
+               hdr->length);
+      return -1;
+    }
+
+  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_REFRESH,           "Route Refresh"                 },
@@ -460,6 +597,7 @@ static const struct message capcode_str[] =
   { 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)"                     },
@@ -477,6 +615,7 @@ static const size_t cap_minsizes[] =
   [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),
 };
@@ -503,12 +642,13 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
       size_t start;
       u_char *sp = stream_pnt (s);
       struct capability_header caphdr;
-      
+
+      ret = 0;
       /* We need at least capability code and capability length. */
       if (stream_get_getp(s) + 2 > end)
        {
          zlog_info ("%s Capability length error (< header)", peer->host);
-         bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
          return -1;
        }
       
@@ -520,7 +660,7 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
       if (start + caphdr.length > end)
        {
          zlog_info ("%s Capability length error (< length)", peer->host);
-         bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
          return -1;
        }
       
@@ -543,6 +683,7 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
           case CAPABILITY_CODE_ADDPATH:
           case CAPABILITY_CODE_DYNAMIC:
           case CAPABILITY_CODE_DYNAMIC_OLD:
+          case CAPABILITY_CODE_ENHE:
               /* Check length. */
               if (caphdr.length < cap_minsizes[caphdr.code])
                 {
@@ -552,7 +693,7 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
                              LOOKUP (capcode_str, caphdr.code),
                              caphdr.length, 
                             (unsigned) cap_minsizes[caphdr.code]);
-                  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+                  bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
                   return -1;
                 }
           /* we deliberately ignore unknown codes, see below */
@@ -579,6 +720,7 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
                       memcpy (*error, sp, caphdr.length + 2);
                       *error += caphdr.length + 2;
                     }
+                 ret = 0;      /* Don't return error for this */
                 }
             }
             break;
@@ -594,12 +736,10 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
             break;
           case CAPABILITY_CODE_ORF:
           case CAPABILITY_CODE_ORF_OLD:
-            if (bgp_capability_orf_entry (peer, &caphdr))
-              return -1;
+            ret = bgp_capability_orf_entry (peer, &caphdr);
             break;
           case CAPABILITY_CODE_RESTART:
-            if (bgp_capability_restart (peer, &caphdr))
-              return -1;
+            ret = bgp_capability_restart (peer, &caphdr);
             break;
           case CAPABILITY_CODE_DYNAMIC:
          case CAPABILITY_CODE_DYNAMIC_OLD:
@@ -611,11 +751,13 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
                * for the value really, only error case.
                */
               if (!bgp_capability_as4 (peer, &caphdr))
-                return -1;
+                ret = -1;
               break;            
           case CAPABILITY_CODE_ADDPATH:
-            if (bgp_capability_addpath (peer, &caphdr))
-              return -1;
+            ret = bgp_capability_addpath (peer, &caphdr);
+            break;
+          case CAPABILITY_CODE_ENHE:
+            ret = bgp_capability_enhe (peer, &caphdr);
             break;
           default:
             if (caphdr.code > 128)
@@ -633,6 +775,12 @@ bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
                 *error += caphdr.length + 2;
               }
           }
+
+      if (ret < 0)
+       {
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+         return -1;
+       }
       if (stream_get_getp(s) != (start + caphdr.length))
         {
           if (stream_get_getp(s) > (start + caphdr.length))
@@ -773,7 +921,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability)
       if (STREAM_READABLE(s) < 2)
        {
          zlog_info ("%s Option length error", peer->host);
-         bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
          return -1;
        }
 
@@ -785,7 +933,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability)
       if (STREAM_READABLE (s) < opt_length)
        {
          zlog_info ("%s Option length error", peer->host);
-         bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_OPEN_MALFORMED_ATTR);
          return -1;
        }
 
@@ -1000,6 +1148,26 @@ bgp_open_capability (struct stream *s, struct peer *peer)
       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])
     {