Work toward IPv6 support in BGP: implement decoding/encoding of the
BGP OPEN Multiprotocol Extensions Capabilities (RFC 4760).

The corresponding BGP UPDATE decoding is not done yet, hence for now
we don't include any Capabilities in the BGP OPEN message originated by us.

This work is in the context of ONOS-422.

Change-Id: I8e1c8838adc189aa32a8edf98be976d90fc4ad42
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
index b8c0ba0..0cb74cf 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
@@ -80,6 +80,71 @@
     public static final long BGP_AS_0 = 0;
 
     /**
+     * BGP OPEN related constants.
+     */
+    public static final class Open {
+        /**
+         * Default constructor.
+         * <p>
+         * The constructor is private to prevent creating an instance of
+         * this utility class.
+         */
+        private Open() {
+        }
+
+        /**
+         * BGP OPEN: Optional Parameters related constants.
+         */
+        public static final class OptionalParameters {
+        }
+
+        /**
+         * BGP OPEN: Capabilities related constants (RFC 5492).
+         */
+        public static final class Capabilities {
+            /** BGP OPEN Optional Parameter Type: Capabilities. */
+            public static final int TYPE = 2;
+
+            /** BGP OPEN Optional Parameter minimum length. */
+            public static final int MIN_LENGTH = 2;
+
+            /**
+             * BGP OPEN: Multiprotocol Extensions Capabilities (RFC 4760).
+             */
+            public static final class MultiprotocolExtensions {
+                /** BGP OPEN Multiprotocol Extensions code. */
+                public static final int CODE = 1;
+
+                /** BGP OPEN Multiprotocol Extensions length. */
+                public static final int LENGTH = 4;
+
+                /** BGP OPEN Multiprotocol Extensions AFI: IPv4. */
+                public static final int AFI_IPV4 = 1;
+
+                /** BGP OPEN Multiprotocol Extensions AFI: IPv6. */
+                public static final int AFI_IPV6 = 2;
+
+                /** BGP OPEN Multiprotocol Extensions SAFI: unicast. */
+                public static final int SAFI_UNICAST = 1;
+
+                /** BGP OPEN Multiprotocol Extensions SAFI: multicast. */
+                public static final int SAFI_MULTICAST = 2;
+            }
+
+            /**
+             * BGP OPEN: Support for 4-octet AS Number Capability (RFC 6793).
+             */
+            public static final class As4Octet {
+                /** BGP OPEN Support for 4-octet AS Number Capability code. */
+                public static final int CODE = 65;
+
+                /** BGP OPEN 4-octet AS Number Capability length. */
+                public static final int LENGTH = 4;
+            }
+        }
+    }
+
+    /**
      * BGP UPDATE related constants.
      */
     public static final class Update {
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
index 292872f..5c7053c 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
@@ -62,4 +62,25 @@
         message.writeBytes(payload);
         return message;
     }
+
+    /**
+     * An exception indicating a parsing error of the BGP message.
+     */
+    static final class BgpParseException extends Exception {
+        /**
+         * Default constructor.
+         */
+        private BgpParseException() {
+            super();
+        }
+
+        /**
+         * Constructor for a specific exception details message.
+         *
+         * @param message the message with the exception details
+         */
+        BgpParseException(String message) {
+            super(message);
+        }
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
index 92086f3..a0b6f5e 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
@@ -21,6 +21,9 @@
 import org.onlab.packet.Ip4Address;
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications;
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
+import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities;
+import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.MultiprotocolExtensions;
+import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -153,16 +156,16 @@
             Ip4Address.valueOf((int) message.readUnsignedInt());
         bgpSession.setRemoteBgpId(remoteBgpId);
 
-        // Optional Parameters
-        int optParamLen = message.readUnsignedByte();
-        if (message.readableBytes() < optParamLen) {
+        // Parse the Optional Parameters
+        try {
+            parseOptionalParameters(bgpSession, ctx, message);
+        } catch (BgpParseException e) {
+            // ERROR: Error parsing optional parameters
             log.debug("BGP RX OPEN Error from {}: " +
-                      "Invalid Optional Parameter Length field {}. " +
-                      "Remaining Optional Parameters {}",
-                      bgpSession.getRemoteAddress(), optParamLen,
-                      message.readableBytes());
+                      "Exception parsing Optional Parameters: {}",
+                      bgpSession.getRemoteAddress(), e);
             //
-            // ERROR: Invalid Optional Parameter Length field: Unspecific
+            // ERROR: Invalid Optional Parameters: Unspecific
             //
             // Send NOTIFICATION and close the connection
             int errorCode = OpenMessageError.ERROR_CODE;
@@ -174,8 +177,6 @@
             bgpSession.closeSession(ctx);
             return;
         }
-        // NOTE: Parse the optional parameters (if needed)
-        message.readBytes(optParamLen);             // NOTE: data ignored
 
         log.debug("BGP RX OPEN message from {}: " +
                   "BGPv{} AS {} BGP-ID {} Holdtime {}",
@@ -213,8 +214,211 @@
         message.writeShort((int) bgpSession.getLocalAs());
         message.writeShort((int) bgpSession.getLocalHoldtime());
         message.writeInt(bgpSession.getLocalBgpId().toInt());
-        message.writeByte(0);               // No Optional Parameters
+
+        // Prepare the optional BGP Capabilities
+        ChannelBuffer capabilitiesMessage =
+            prepareBgpOpenCapabilities(bgpSession);
+        message.writeByte(capabilitiesMessage.readableBytes());
+        message.writeBytes(capabilitiesMessage);
+
         return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN,
                                             message);
     }
+
+    /**
+     * Parses BGP OPEN Optional Parameters.
+     *
+     * @param bgpSession the BGP Session to use
+     * @param ctx the Channel Handler Context
+     * @param message the message to process
+     * @throws BgpParseException
+     */
+    private static void parseOptionalParameters(BgpSession bgpSession,
+                                                ChannelHandlerContext ctx,
+                                                ChannelBuffer message)
+        throws BgpParseException {
+
+        //
+        // Get and verify the Optional Parameters Length
+        //
+        int optParamLength = message.readUnsignedByte();
+        if (optParamLength > message.readableBytes()) {
+            // ERROR: Invalid Optional Parameter Length
+            String errorMsg = "Invalid Optional Parameter Length field " +
+                optParamLength + ". Remaining Optional Parameters " +
+                message.readableBytes();
+            throw new BgpParseException(errorMsg);
+        }
+        if (optParamLength == 0) {
+            return;                     // No Optional Parameters
+        }
+
+        //
+        // Parse the Optional Parameters
+        //
+        int optParamEnd = message.readerIndex() + optParamLength;
+        while (message.readerIndex() < optParamEnd) {
+            int paramType = message.readUnsignedByte();
+            if (message.readerIndex() >= optParamEnd) {
+                // ERROR: Malformed Optional Parameters
+                String errorMsg = "Malformed Optional Parameters";
+                throw new BgpParseException(errorMsg);
+            }
+            int paramLen = message.readUnsignedByte();
+            if (message.readerIndex() + paramLen > optParamEnd) {
+                // ERROR: Malformed Optional Parameters
+                String errorMsg = "Malformed Optional Parameters";
+                throw new BgpParseException(errorMsg);
+            }
+
+            //
+            // Extract the Optional Parameter Value based on the Parameter Type
+            //
+            switch (paramType) {
+            case Capabilities.TYPE:
+                // Optional Parameter Type: Capabilities
+                if (paramLen < Capabilities.MIN_LENGTH) {
+                    // ERROR: Malformed Capability
+                    String errorMsg = "Malformed Capability Type " + paramType;
+                    throw new BgpParseException(errorMsg);
+                }
+                int capabEnd = message.readerIndex() + paramLen;
+                int capabCode = message.readUnsignedByte();
+                int capabLen = message.readUnsignedByte();
+                if (message.readerIndex() + capabLen > capabEnd) {
+                    // ERROR: Malformed Capability
+                    String errorMsg = "Malformed Capability Type " + paramType;
+                    throw new BgpParseException(errorMsg);
+                }
+
+                switch (capabCode) {
+                case MultiprotocolExtensions.CODE:
+                    // Multiprotocol Extensions Capabilities (RFC 4760)
+                    if (capabLen != MultiprotocolExtensions.LENGTH) {
+                        // ERROR: Multiprotocol Extension Length Error
+                        String errorMsg = "Multiprotocol Extension Length Error";
+                        throw new BgpParseException(errorMsg);
+                    }
+                    // Decode the AFI (2 octets) and SAFI (1 octet)
+                    int afi = message.readUnsignedShort();
+                    int reserved = message.readUnsignedByte();
+                    int safi = message.readUnsignedByte();
+                    log.debug("BGP RX OPEN Capability: AFI = {} SAFI = {}",
+                              afi, safi);
+                    //
+                    // Setup the AFI/SAFI in the BgpSession
+                    //
+                    if (afi == MultiprotocolExtensions.AFI_IPV4 &&
+                        safi == MultiprotocolExtensions.SAFI_UNICAST) {
+                        bgpSession.setRemoteIpv4Unicast();
+                    } else if (afi == MultiprotocolExtensions.AFI_IPV4 &&
+                               safi == MultiprotocolExtensions.SAFI_MULTICAST) {
+                        bgpSession.setRemoteIpv4Multicast();
+                    } else if (afi == MultiprotocolExtensions.AFI_IPV6 &&
+                               safi == MultiprotocolExtensions.SAFI_UNICAST) {
+                        bgpSession.setRemoteIpv6Unicast();
+                    } else if (afi == MultiprotocolExtensions.AFI_IPV6 &&
+                               safi == MultiprotocolExtensions.SAFI_MULTICAST) {
+                        bgpSession.setRemoteIpv6Multicast();
+                    } else {
+                        log.debug("BGP RX OPEN Capability: Unknown AFI = {} SAFI = {}",
+                                  afi, safi);
+                    }
+                    break;
+
+                case Capabilities.As4Octet.CODE:
+                    // Support for 4-octet AS Number Capabilities (RFC 6793)
+                    if (capabLen != Capabilities.As4Octet.LENGTH) {
+                        // ERROR: 4-octet AS Number Capability Length Error
+                        String errorMsg = "4-octet AS Number Capability Length Error";
+                        throw new BgpParseException(errorMsg);
+                    }
+                    long as4Number = message.readUnsignedInt();
+                    // TODO: Implement support for 4-octet AS Numbers
+                    log.debug("BGP RX OPEN Capability:  AS4 Number = {}",
+                              as4Number);
+                    break;
+
+                default:
+                    // Unknown Capability: ignore it
+                    log.debug("BGP RX OPEN Capability Code = {} Length = {}",
+                              capabCode, capabLen);
+                    message.readBytes(capabLen);
+                    break;
+                }
+
+                break;
+
+            default:
+                // Unknown Parameter Type: ignore it
+                log.debug("BGP RX OPEN Parameter Type = {} Length = {}",
+                          paramType, paramLen);
+                message.readBytes(paramLen);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Prepares the Capabilities for the BGP OPEN message.
+     *
+     * @param bgpSession the BGP Session to use
+     * @return the buffer with the BGP Capabilities to transmit
+     */
+    private static ChannelBuffer prepareBgpOpenCapabilities(
+                                        BgpSession bgpSession) {
+        ChannelBuffer message =
+            ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
+
+        //
+        // Write the Multiprotocol Extensions Capabilities
+        //
+
+        // IPv4 unicast
+        if (bgpSession.getLocalIpv4Unicast()) {
+            message.writeByte(Capabilities.TYPE);               // Param type
+            message.writeByte(Capabilities.MIN_LENGTH +
+                              MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(MultiprotocolExtensions.AFI_IPV4);
+            message.writeByte(0);               // Reserved field
+            message.writeByte(MultiprotocolExtensions.SAFI_UNICAST);
+        }
+        // IPv4 multicast
+        if (bgpSession.getLocalIpv4Multicast()) {
+            message.writeByte(Capabilities.TYPE);               // Param type
+            message.writeByte(Capabilities.MIN_LENGTH +
+                              MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(MultiprotocolExtensions.AFI_IPV4);
+            message.writeByte(0);               // Reserved field
+            message.writeByte(MultiprotocolExtensions.SAFI_MULTICAST);
+        }
+        // IPv6 unicast
+        if (bgpSession.getLocalIpv6Unicast()) {
+            message.writeByte(Capabilities.TYPE);               // Param type
+            message.writeByte(Capabilities.MIN_LENGTH +
+                              MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(MultiprotocolExtensions.AFI_IPV6);
+            message.writeByte(0);               // Reserved field
+            message.writeByte(MultiprotocolExtensions.SAFI_UNICAST);
+        }
+        // IPv6 multicast
+        if (bgpSession.getLocalIpv6Multicast()) {
+            message.writeByte(Capabilities.TYPE);               // Param type
+            message.writeByte(Capabilities.MIN_LENGTH +
+                              MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(MultiprotocolExtensions.AFI_IPV6);
+            message.writeByte(0);               // Reserved field
+            message.writeByte(MultiprotocolExtensions.SAFI_MULTICAST);
+        }
+
+        return message;
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
index 248085b..a4fd63c 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
@@ -61,6 +61,10 @@
     private long remoteAs;                      // 2 octets
     private long remoteHoldtime;                // 2 octets
     private Ip4Address remoteBgpId;             // 4 octets -> IPv4 address
+    private boolean remoteIpv4Unicast;          // Peer IPv4/UNICAST AFI/SAFI
+    private boolean remoteIpv4Multicast;        // Peer IPv4/MULTICAST AFI/SAFI
+    private boolean remoteIpv6Unicast;          // Peer IPv6/UNICAST AFI/SAFI
+    private boolean remoteIpv6Multicast;        // Peer IPv6/MULTICAST AFI/SAFI
     //
     private SocketAddress localAddress;         // Local IP addr/port
     private Ip4Address localIp4Address;         // Local IPv4 address
@@ -68,6 +72,10 @@
     private long localAs;                       // 2 octets
     private long localHoldtime;                 // 2 octets
     private Ip4Address localBgpId;              // 4 octets -> IPv4 address
+    private boolean localIpv4Unicast;        // Local IPv4/UNICAST AFI/SAFI
+    private boolean localIpv4Multicast;      // Local IPv4/MULTICAST AFI/SAFI
+    private boolean localIpv6Unicast;        // Local IPv6/UNICAST AFI/SAFI
+    private boolean localIpv6Multicast;      // Local IPv6/MULTICAST AFI/SAFI
     //
     private long localKeepaliveInterval;        // Keepalive interval
 
@@ -236,6 +244,82 @@
     }
 
     /**
+     * Gets the BGP session remote AFI/SAFI configuration for IPv4 unicast.
+     *
+     * @return the BGP session remote AFI/SAFI configuration for IPv4 unicast
+     */
+    public boolean getRemoteIpv4Unicast() {
+        return remoteIpv4Unicast;
+    }
+
+    /**
+     * Sets the BGP session remote AFI/SAFI configuration for IPv4 unicast.
+     */
+    void setRemoteIpv4Unicast() {
+        this.remoteIpv4Unicast = true;
+        // Copy the remote AFI/SAFI setting to the local configuration
+        // NOTE: Uncomment the line below if the AFI/SAFI is supported locally
+        // this.localIpv4Unicast = true;
+    }
+
+    /**
+     * Gets the BGP session remote AFI/SAFI configuration for IPv4 multicast.
+     *
+     * @return the BGP session remote AFI/SAFI configuration for IPv4 multicast
+     */
+    public boolean getRemoteIpv4Multicast() {
+        return remoteIpv4Multicast;
+    }
+
+    /**
+     * Sets the BGP session remote AFI/SAFI configuration for IPv4 multicast.
+     */
+    void setRemoteIpv4Multicast() {
+        this.remoteIpv4Multicast = true;
+        // Copy the remote AFI/SAFI setting to the local configuration
+        // NOTE: Uncomment the line below if the AFI/SAFI is supported locally
+        // this.localIpv4Multicast = true;
+    }
+
+    /**
+     * Gets the BGP session remote AFI/SAFI configuration for IPv6 unicast.
+     *
+     * @return the BGP session remote AFI/SAFI configuration for IPv6 unicast
+     */
+    public boolean getRemoteIpv6Unicast() {
+        return remoteIpv6Unicast;
+    }
+
+    /**
+     * Sets the BGP session remote AFI/SAFI configuration for IPv6 unicast.
+     */
+    void setRemoteIpv6Unicast() {
+        this.remoteIpv6Unicast = true;
+        // Copy the remote AFI/SAFI setting to the local configuration
+        // NOTE: Uncomment the line below if the AFI/SAFI is supported locally
+        // this.localIpv6Unicast = true;
+    }
+
+    /**
+     * Gets the BGP session remote AFI/SAFI configuration for IPv6 multicast.
+     *
+     * @return the BGP session remote AFI/SAFI configuration for IPv6 multicast
+     */
+    public boolean getRemoteIpv6Multicast() {
+        return remoteIpv6Multicast;
+    }
+
+    /**
+     * Sets the BGP session remote AFI/SAFI configuration for IPv6 multicast.
+     */
+    void setRemoteIpv6Multicast() {
+        this.remoteIpv6Multicast = true;
+        // Copy the remote AFI/SAFI setting to the local configuration
+        // NOTE: Uncomment the line below if the AFI/SAFI is supported locally
+        // this.localIpv6Multicast = true;
+    }
+
+    /**
      * Gets the BGP session local address.
      *
      * @return the BGP session local address
@@ -290,6 +374,42 @@
     }
 
     /**
+     * Gets the BGP session local AFI/SAFI configuration for IPv4 unicast.
+     *
+     * @return the BGP session local AFI/SAFI configuration for IPv4 unicast
+     */
+    public boolean getLocalIpv4Unicast() {
+        return localIpv4Unicast;
+    }
+
+    /**
+     * Gets the BGP session local AFI/SAFI configuration for IPv4 multicast.
+     *
+     * @return the BGP session local AFI/SAFI configuration for IPv4 multicast
+     */
+    public boolean getLocalIpv4Multicast() {
+        return localIpv4Multicast;
+    }
+
+    /**
+     * Gets the BGP session local AFI/SAFI configuration for IPv6 unicast.
+     *
+     * @return the BGP session local AFI/SAFI configuration for IPv6 unicast
+     */
+    public boolean getLocalIpv6Unicast() {
+        return localIpv6Unicast;
+    }
+
+    /**
+     * Gets the BGP session local AFI/SAFI configuration for IPv6 multicast.
+     *
+     * @return the BGP session local AFI/SAFI configuration for IPv6 multicast
+     */
+    public boolean getLocalIpv6Multicast() {
+        return localIpv6Multicast;
+    }
+
+    /**
      * Tests whether the session is closed.
      * <p>
      * NOTE: We use this method to avoid the Netty's asynchronous closing
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
index 733965e..67b47ef 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
@@ -27,6 +27,7 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
+import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -1212,25 +1213,4 @@
         data.writeBytes(message, attrLen);
         return data;
     }
-
-    /**
-     * An exception indicating a parsing error of the BGP message.
-     */
-    private static final class BgpParseException extends Exception {
-        /**
-         * Default constructor.
-         */
-        private BgpParseException() {
-            super();
-        }
-
-        /**
-         * Constructor for a specific exception details message.
-         *
-         * @param message the message with the exception details
-         */
-         private BgpParseException(String message) {
-             super(message);
-         }
-    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
index d65a69a..1b628f4 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
@@ -43,7 +43,11 @@
     private static final String FORMAT_NEIGHBOR_LINE2 =
         "  Remote router ID %s, IP %s, BGP version %d, Hold time %d";
     private static final String FORMAT_NEIGHBOR_LINE3 =
+        "  Remote AFI/SAFI IPv4 Unicast %s Multicast %s, IPv6 Unicast %s Multicast %s";
+    private static final String FORMAT_NEIGHBOR_LINE4 =
         "  Local  router ID %s, IP %s, BGP version %d, Hold time %d";
+    private static final String FORMAT_NEIGHBOR_LINE5 =
+        "  Local  AFI/SAFI IPv4 Unicast %s Multicast %s, IPv6 Unicast %s Multicast %s";
 
     @Override
     protected void execute() {
@@ -102,10 +106,20 @@
               bgpSession.getRemoteBgpVersion(),
               bgpSession.getRemoteHoldtime());
         print(FORMAT_NEIGHBOR_LINE3,
+              bgpSession.getRemoteIpv4Unicast() ? "YES" : "NO",
+              bgpSession.getRemoteIpv4Multicast() ? "YES" : "NO",
+              bgpSession.getRemoteIpv6Unicast() ? "YES" : "NO",
+              bgpSession.getRemoteIpv6Multicast() ? "YES" : "NO");
+        print(FORMAT_NEIGHBOR_LINE4,
               bgpSession.getLocalBgpId().toString(),
               bgpSession.getLocalAddress().toString(),
               bgpSession.getLocalBgpVersion(),
               bgpSession.getLocalHoldtime());
+        print(FORMAT_NEIGHBOR_LINE5,
+              bgpSession.getLocalIpv4Unicast() ? "YES" : "NO",
+              bgpSession.getLocalIpv4Multicast() ? "YES" : "NO",
+              bgpSession.getLocalIpv6Unicast() ? "YES" : "NO",
+              bgpSession.getLocalIpv6Multicast() ? "YES" : "NO");
     }
 
     /**
@@ -139,12 +153,20 @@
         result.put("remoteAs", bgpSession.getRemoteAs());
         result.put("remoteHoldtime", bgpSession.getRemoteHoldtime());
         result.put("remoteBgpId", bgpSession.getRemoteBgpId().toString());
+        result.put("remoteIpv4Unicast", bgpSession.getRemoteIpv4Unicast());
+        result.put("remoteIpv4Multicast", bgpSession.getRemoteIpv4Multicast());
+        result.put("remoteIpv6Unicast", bgpSession.getRemoteIpv6Unicast());
+        result.put("remoteIpv6Multicast", bgpSession.getRemoteIpv6Multicast());
         //
         result.put("localAddress", bgpSession.getLocalAddress().toString());
         result.put("localBgpVersion", bgpSession.getLocalBgpVersion());
         result.put("localAs", bgpSession.getLocalAs());
         result.put("localHoldtime", bgpSession.getLocalHoldtime());
         result.put("localBgpId", bgpSession.getLocalBgpId().toString());
+        result.put("localIpv4Unicast", bgpSession.getLocalIpv4Unicast());
+        result.put("localIpv4Multicast", bgpSession.getLocalIpv4Multicast());
+        result.put("localIpv6Unicast", bgpSession.getLocalIpv6Unicast());
+        result.put("localIpv6Multicast", bgpSession.getLocalIpv6Multicast());
 
         return result;
     }