blob: e5d4821e41b6e6b8abc2727f962b97ba9367eed1 [file] [log] [blame]
/*
* Copyright 2017-present 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 Param Type
String errorMsg = "Malformed Capabilities Optional "
+ "Parameter Type " + paramType;
throw new BgpMessage.BgpParseException(errorMsg);
}
int paramEnd = message.readerIndex() + paramLen;
// Parse Capabilities
while (message.readerIndex() < paramEnd) {
if (paramEnd - message.readerIndex() <
BgpConstants.Open.Capabilities.MIN_LENGTH) {
String errorMsg = "Malformed Capabilities";
throw new BgpMessage.BgpParseException(errorMsg);
}
int capabCode = message.readUnsignedByte();
int capabLen = message.readUnsignedByte();
if (message.readerIndex() + capabLen > paramEnd) {
// ERROR: Malformed Capability
String errorMsg = "Malformed Capability instance with "
+ "code " + capabCode;
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;
}
}