diff options
Diffstat (limited to 'framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java')
-rw-r--r-- | framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java new file mode 100644 index 00000000..5b5a259e --- /dev/null +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java @@ -0,0 +1,497 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.onlab.packet.Ip4Address; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A class for handling BGP OPEN messages. + */ +final class BgpOpen { + private static final Logger log = LoggerFactory.getLogger(BgpOpen.class); + + /** + * Default constructor. + * <p> + * The constructor is private to prevent creating an instance of + * this utility class. + */ + private BgpOpen() { + } + + /** + * Processes BGP OPEN message. + * + * @param bgpSession the BGP Session to use + * @param ctx the Channel Handler Context + * @param message the message to process + */ + static void processBgpOpen(BgpSession bgpSession, + ChannelHandlerContext ctx, + ChannelBuffer message) { + int minLength = + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; + if (message.readableBytes() < minLength) { + log.debug("BGP RX OPEN Error from {}: " + + "Message length {} too short. Must be at least {}", + bgpSession.remoteInfo().address(), + message.readableBytes(), minLength); + // + // ERROR: Bad Message Length + // + // Send NOTIFICATION and close the connection + ChannelBuffer txMessage = + BgpNotification.prepareBgpNotificationBadMessageLength( + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); + ctx.getChannel().write(txMessage); + bgpSession.closeSession(ctx); + return; + } + + // + // Parse the OPEN message + // + // Remote BGP version + int remoteBgpVersion = message.readUnsignedByte(); + if (remoteBgpVersion != BgpConstants.BGP_VERSION) { + log.debug("BGP RX OPEN Error from {}: " + + "Unsupported BGP version {}. Should be {}", + bgpSession.remoteInfo().address(), remoteBgpVersion, + BgpConstants.BGP_VERSION); + // + // ERROR: Unsupported Version Number + // + // Send NOTIFICATION and close the connection + int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE; + int errorSubcode = BgpConstants.Notifications.OpenMessageError.UNSUPPORTED_VERSION_NUMBER; + ChannelBuffer data = ChannelBuffers.buffer(2); + data.writeShort(BgpConstants.BGP_VERSION); + ChannelBuffer txMessage = + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, + data); + ctx.getChannel().write(txMessage); + bgpSession.closeSession(ctx); + return; + } + bgpSession.remoteInfo().setBgpVersion(remoteBgpVersion); + + // Remote AS number + long remoteAs = message.readUnsignedShort(); + bgpSession.remoteInfo().setAsNumber(remoteAs); + // + // NOTE: Currently, the local AS number is always set to the remote AS. + // This is done, because the peer setup is always iBGP. + // In the future, the local AS number should be configured as part + // of an explicit BGP peering configuration. + // + bgpSession.localInfo().setAsNumber(remoteAs); + + // Remote Hold Time + long remoteHoldtime = message.readUnsignedShort(); + if ((remoteHoldtime != 0) && + (remoteHoldtime < BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME)) { + log.debug("BGP RX OPEN Error from {}: " + + "Unacceptable Hold Time field {}. " + + "Should be 0 or at least {}", + bgpSession.remoteInfo().address(), remoteHoldtime, + BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME); + // + // ERROR: Unacceptable Hold Time + // + // Send NOTIFICATION and close the connection + int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE; + int errorSubcode = BgpConstants.Notifications.OpenMessageError.UNACCEPTABLE_HOLD_TIME; + ChannelBuffer txMessage = + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, + null); + ctx.getChannel().write(txMessage); + bgpSession.closeSession(ctx); + return; + } + bgpSession.remoteInfo().setHoldtime(remoteHoldtime); + // + // NOTE: Currently. the local BGP Holdtime is always set to the remote + // BGP holdtime. + // In the future, the local BGP Holdtime should be configured as part + // of an explicit BGP peering configuration. + // + bgpSession.localInfo().setHoldtime(remoteHoldtime); + + // Remote BGP Identifier + Ip4Address remoteBgpId = + Ip4Address.valueOf((int) message.readUnsignedInt()); + bgpSession.remoteInfo().setBgpId(remoteBgpId); + + // Parse the Optional Parameters + try { + parseOptionalParameters(bgpSession, ctx, message); + } catch (BgpMessage.BgpParseException e) { + // ERROR: Error parsing optional parameters + log.debug("BGP RX OPEN Error from {}: " + + "Exception parsing Optional Parameters: {}", + bgpSession.remoteInfo().address(), e); + // + // ERROR: Invalid Optional Parameters: Unspecific + // + // Send NOTIFICATION and close the connection + int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE; + int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC; + ChannelBuffer txMessage = + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, + null); + ctx.getChannel().write(txMessage); + bgpSession.closeSession(ctx); + return; + } + + // + // NOTE: Prepare the BGP OPEN message before the original local AS + // is overwritten by the 4-octet AS number + // + ChannelBuffer txOpenMessage = prepareBgpOpen(bgpSession.localInfo()); + + // + // Use the 4-octet AS number in lieu of the "My AS" field + // See RFC 6793, Section 4.1, second paragraph. + // + if (bgpSession.remoteInfo().as4OctetCapability()) { + long as4Number = bgpSession.remoteInfo().as4Number(); + bgpSession.remoteInfo().setAsNumber(as4Number); + bgpSession.localInfo().setAsNumber(as4Number); + } + + // + // Verify that the AS number is same for all other BGP Sessions + // NOTE: This check applies only for our use-case where all BGP + // sessions are iBGP. + // + for (BgpSession bs : bgpSession.getBgpSessionManager().getBgpSessions()) { + if ((bs.remoteInfo().asNumber() != 0) && + (bgpSession.remoteInfo().asNumber() != + bs.remoteInfo().asNumber())) { + log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " + + "Expected {}", + bgpSession.remoteInfo().address(), + bgpSession.remoteInfo().asNumber(), + bs.remoteInfo().asNumber()); + // + // ERROR: Bad Peer AS + // + // Send NOTIFICATION and close the connection + int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE; + int errorSubcode = BgpConstants.Notifications.OpenMessageError.BAD_PEER_AS; + ChannelBuffer txMessage = + BgpNotification.prepareBgpNotification(errorCode, + errorSubcode, null); + ctx.getChannel().write(txMessage); + bgpSession.closeSession(ctx); + return; + } + } + + log.debug("BGP RX OPEN message from {}: " + + "BGPv{} AS {} BGP-ID {} Holdtime {}", + bgpSession.remoteInfo().address(), + bgpSession.remoteInfo().bgpVersion(), + bgpSession.remoteInfo().asNumber(), + bgpSession.remoteInfo().bgpId(), + bgpSession.remoteInfo().holdtime()); + + // Send my OPEN followed by KEEPALIVE + ctx.getChannel().write(txOpenMessage); + // + ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive(); + ctx.getChannel().write(txMessage); + + // Start the KEEPALIVE timer + bgpSession.restartKeepaliveTimer(ctx); + + // Start the Session Timeout timer + bgpSession.restartSessionTimeoutTimer(ctx); + } + + /** + * Prepares BGP OPEN message. + * + * @param localInfo the BGP Session local information to use + * @return the message to transmit (BGP header included) + */ + static ChannelBuffer prepareBgpOpen(BgpSessionInfo localInfo) { + ChannelBuffer message = + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); + + // + // Prepare the OPEN message payload + // + message.writeByte(localInfo.bgpVersion()); + message.writeShort((int) localInfo.asNumber()); + message.writeShort((int) localInfo.holdtime()); + message.writeInt(localInfo.bgpId().toInt()); + + // Prepare the optional BGP Capabilities + ChannelBuffer capabilitiesMessage = + prepareBgpOpenCapabilities(localInfo); + 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 BgpMessage.BgpParseException + */ + private static void parseOptionalParameters(BgpSession bgpSession, + ChannelHandlerContext ctx, + ChannelBuffer message) + throws BgpMessage.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 BgpMessage.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 BgpMessage.BgpParseException(errorMsg); + } + int paramLen = message.readUnsignedByte(); + if (message.readerIndex() + paramLen > optParamEnd) { + // ERROR: Malformed Optional Parameters + String errorMsg = "Malformed Optional Parameters"; + throw new BgpMessage.BgpParseException(errorMsg); + } + + // + // Extract the Optional Parameter Value based on the Parameter Type + // + switch (paramType) { + case BgpConstants.Open.Capabilities.TYPE: + // Optional Parameter Type: Capabilities + if (paramLen < BgpConstants.Open.Capabilities.MIN_LENGTH) { + // ERROR: Malformed Capability + String errorMsg = "Malformed Capability Type " + paramType; + throw new BgpMessage.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 BgpMessage.BgpParseException(errorMsg); + } + + switch (capabCode) { + case BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE: + // Multiprotocol Extensions Capabilities (RFC 4760) + if (capabLen != BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH) { + // ERROR: Multiprotocol Extension Length Error + String errorMsg = "Multiprotocol Extension Length Error"; + throw new BgpMessage.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 + // + // NOTE: For now we just copy the remote AFI/SAFI setting + // to the local configuration. + // + if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4 && + safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST) { + bgpSession.remoteInfo().setIpv4Unicast(); + bgpSession.localInfo().setIpv4Unicast(); + } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4 && + safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST) { + bgpSession.remoteInfo().setIpv4Multicast(); + bgpSession.localInfo().setIpv4Multicast(); + } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6 && + safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST) { + bgpSession.remoteInfo().setIpv6Unicast(); + bgpSession.localInfo().setIpv6Unicast(); + } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6 && + safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST) { + bgpSession.remoteInfo().setIpv6Multicast(); + bgpSession.localInfo().setIpv6Multicast(); + } else { + log.debug("BGP RX OPEN Capability: Unknown AFI = {} SAFI = {}", + afi, safi); + } + break; + + case BgpConstants.Open.Capabilities.As4Octet.CODE: + // Support for 4-octet AS Number Capabilities (RFC 6793) + if (capabLen != BgpConstants.Open.Capabilities.As4Octet.LENGTH) { + // ERROR: 4-octet AS Number Capability Length Error + String errorMsg = "4-octet AS Number Capability Length Error"; + throw new BgpMessage.BgpParseException(errorMsg); + } + long as4Number = message.readUnsignedInt(); + + bgpSession.remoteInfo().setAs4OctetCapability(); + bgpSession.remoteInfo().setAs4Number(as4Number); + + // + // Copy remote 4-octet AS Number Capabilities and AS + // Number. This is a temporary setting until local AS + // number configuration is supported. + // + bgpSession.localInfo().setAs4OctetCapability(); + bgpSession.localInfo().setAs4Number(as4Number); + 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 localInfo the BGP Session local information to use + * @return the buffer with the BGP Capabilities to transmit + */ + private static ChannelBuffer prepareBgpOpenCapabilities( + BgpSessionInfo localInfo) { + ChannelBuffer message = + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); + + // + // Write the Multiprotocol Extensions Capabilities + // + + // IPv4 unicast + if (localInfo.ipv4Unicast()) { + message.writeByte(BgpConstants.Open.Capabilities.TYPE); // Param type + message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH + + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Param len + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE); // Capab. code + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Capab. len + message.writeShort( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4); + message.writeByte(0); // Reserved field + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST); + } + // IPv4 multicast + if (localInfo.ipv4Multicast()) { + message.writeByte(BgpConstants.Open.Capabilities.TYPE); // Param type + message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH + + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Param len + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE); // Capab. code + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Capab. len + message.writeShort( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4); + message.writeByte(0); // Reserved field + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST); + } + // IPv6 unicast + if (localInfo.ipv6Unicast()) { + message.writeByte(BgpConstants.Open.Capabilities.TYPE); // Param type + message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH + + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Param len + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE); // Capab. code + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Capab. len + message.writeShort( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6); + message.writeByte(0); // Reserved field + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST); + } + // IPv6 multicast + if (localInfo.ipv6Multicast()) { + message.writeByte(BgpConstants.Open.Capabilities.TYPE); // Param type + message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH + + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Param len + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE); // Capab. code + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH); // Capab. len + message.writeShort( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6); + message.writeByte(0); // Reserved field + message.writeByte( + BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST); + } + + // 4 octet AS path capability + if (localInfo.as4OctetCapability()) { + message.writeByte(BgpConstants.Open.Capabilities.TYPE); // Param type + message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH + + BgpConstants.Open.Capabilities.As4Octet.LENGTH); // Param len + message.writeByte(BgpConstants.Open.Capabilities.As4Octet.CODE); // Capab. code + message.writeByte(BgpConstants.Open.Capabilities.As4Octet.LENGTH); // Capab. len + message.writeInt((int) localInfo.as4Number()); + } + return message; + } +} |