Refactor the BGP code in the SDN-IP application:
* Resolves ONOS-476
* Moved the BGP message-specific processing from class BgpSession
to per-message type classes: BgpKeepalive, BgpNotification,
BgpOpen, BgpUpdate
* Minor modifications in some of the methods or BGP-specific API
to accomodate the above change.
No functional changes.
Change-Id: I95df128fa31c60397a279aaca25a487b7991a6e1
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java
index 781403d..4c9b56e 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java
@@ -90,8 +90,9 @@
int errorSubcode =
MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
ChannelBuffer txMessage =
- bgpSession.prepareBgpNotification(errorCode, errorSubcode,
- null);
+ BgpNotification.prepareBgpNotification(errorCode,
+ errorSubcode,
+ null);
ctx.getChannel().write(txMessage);
bgpSession.closeSession(ctx);
return null;
@@ -114,7 +115,7 @@
//
// Send NOTIFICATION and close the connection
ChannelBuffer txMessage =
- bgpSession.prepareBgpNotificationBadMessageLength(length);
+ BgpNotification.prepareBgpNotificationBadMessageLength(length);
ctx.getChannel().write(txMessage);
bgpSession.closeSession(ctx);
return null;
@@ -145,16 +146,16 @@
//
switch (type) {
case BgpConstants.BGP_TYPE_OPEN:
- bgpSession.processBgpOpen(ctx, message);
+ BgpOpen.processBgpOpen(bgpSession, ctx, message);
break;
case BgpConstants.BGP_TYPE_UPDATE:
- bgpSession.processBgpUpdate(ctx, message);
+ BgpUpdate.processBgpUpdate(bgpSession, ctx, message);
break;
case BgpConstants.BGP_TYPE_NOTIFICATION:
- bgpSession.processBgpNotification(ctx, message);
+ BgpNotification.processBgpNotification(bgpSession, ctx, message);
break;
case BgpConstants.BGP_TYPE_KEEPALIVE:
- bgpSession.processBgpKeepalive(ctx, message);
+ BgpKeepalive.processBgpKeepalive(bgpSession, ctx, message);
break;
default:
//
@@ -166,8 +167,8 @@
ChannelBuffer data = ChannelBuffers.buffer(1);
data.writeByte(type);
ChannelBuffer txMessage =
- bgpSession.prepareBgpNotification(errorCode, errorSubcode,
- data);
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
ctx.getChannel().write(txMessage);
bgpSession.closeSession(ctx);
return null;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java
new file mode 100644
index 0000000..833f2f9
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 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.sdnip.bgp;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for handling BGP KEEPALIVE messages.
+ */
+final class BgpKeepalive {
+ private static final Logger log =
+ LoggerFactory.getLogger(BgpKeepalive.class);
+
+ /**
+ * Default constructor.
+ * <p>
+ * The constructor is private to prevent creating an instance of
+ * this utility class.
+ */
+ private BgpKeepalive() {
+ }
+
+ /**
+ * Processes BGP KEEPALIVE message.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param message the message to process
+ */
+ static void processBgpKeepalive(BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ ChannelBuffer message) {
+ if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
+ BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
+ log.debug("BGP RX KEEPALIVE Error from {}: " +
+ "Invalid total message length {}. Expected {}",
+ bgpSession.getRemoteAddress(),
+ message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH,
+ BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH);
+ //
+ // 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 KEEPALIVE message: nothing to do
+ //
+ log.trace("BGP RX KEEPALIVE message from {}",
+ bgpSession.getRemoteAddress());
+
+ // Start the Session Timeout timer
+ bgpSession.restartSessionTimeoutTimer(ctx);
+ }
+
+ /**
+ * Prepares BGP KEEPALIVE message.
+ *
+ * @return the message to transmit (BGP header included)
+ */
+ static ChannelBuffer prepareBgpKeepalive() {
+ ChannelBuffer message =
+ ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
+
+ //
+ // Prepare the KEEPALIVE message payload: nothing to do
+ //
+ return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE,
+ message);
+ }
+}
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
new file mode 100644
index 0000000..292872f
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 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.sdnip.bgp;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for preparing BGP messages.
+ */
+final class BgpMessage {
+ private static final Logger log =
+ LoggerFactory.getLogger(BgpMessage.class);
+
+ /**
+ * Default constructor.
+ * <p>
+ * The constructor is private to prevent creating an instance of
+ * this utility class.
+ */
+ private BgpMessage() {
+ }
+
+ /**
+ * Prepares BGP message.
+ *
+ * @param type the BGP message type
+ * @param payload the message payload to transmit (BGP header excluded)
+ * @return the message to transmit (BGP header included)
+ */
+ static ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
+ ChannelBuffer message =
+ ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
+ payload.readableBytes());
+
+ // Write the marker
+ for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
+ message.writeByte(0xff);
+ }
+
+ // Write the rest of the BGP header
+ message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
+ payload.readableBytes());
+ message.writeByte(type);
+
+ // Write the payload
+ message.writeBytes(payload);
+ return message;
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java
new file mode 100644
index 0000000..4abdbf7
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 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.sdnip.bgp;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for handling BGP NOTIFICATION messages.
+ */
+final class BgpNotification {
+ private static final Logger log =
+ LoggerFactory.getLogger(BgpNotification.class);
+
+ /**
+ * Default constructor.
+ * <p>
+ * The constructor is private to prevent creating an instance of
+ * this utility class.
+ */
+ private BgpNotification() {
+ }
+
+ /**
+ * Processes BGP NOTIFICATION message.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param message the message to process
+ */
+ static void processBgpNotification(BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ ChannelBuffer message) {
+ int minLength =
+ BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
+ if (message.readableBytes() < minLength) {
+ log.debug("BGP RX NOTIFICATION Error from {}: " +
+ "Message length {} too short. Must be at least {}",
+ bgpSession.getRemoteAddress(), message.readableBytes(),
+ minLength);
+ //
+ // ERROR: Bad Message Length
+ //
+ // NOTE: We do NOT send NOTIFICATION in response to a notification
+ return;
+ }
+
+ //
+ // Parse the NOTIFICATION message
+ //
+ int errorCode = message.readUnsignedByte();
+ int errorSubcode = message.readUnsignedByte();
+ int dataLength = message.readableBytes();
+
+ log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " +
+ "Error Subcode {} Data Length {}",
+ bgpSession.getRemoteAddress(), errorCode, errorSubcode,
+ dataLength);
+
+ //
+ // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to
+ // close the connection.
+ //
+
+ // Start the Session Timeout timer
+ bgpSession.restartSessionTimeoutTimer(ctx);
+ }
+
+ /**
+ * Prepares BGP NOTIFICATION message.
+ *
+ * @param errorCode the BGP NOTIFICATION Error Code
+ * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
+ * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
+ * @param data the BGP NOTIFICATION Data if applicable, otherwise null
+ * @return the message to transmit (BGP header included)
+ */
+ static ChannelBuffer prepareBgpNotification(int errorCode,
+ int errorSubcode,
+ ChannelBuffer data) {
+ ChannelBuffer message =
+ ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
+
+ //
+ // Prepare the NOTIFICATION message payload
+ //
+ message.writeByte(errorCode);
+ message.writeByte(errorSubcode);
+ if (data != null) {
+ message.writeBytes(data);
+ }
+ return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION,
+ message);
+ }
+
+ /**
+ * Prepares BGP NOTIFICATION message: Bad Message Length.
+ *
+ * @param length the erroneous Length field
+ * @return the message to transmit (BGP header included)
+ */
+ static ChannelBuffer prepareBgpNotificationBadMessageLength(int length) {
+ int errorCode = MessageHeaderError.ERROR_CODE;
+ int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH;
+ ChannelBuffer data = ChannelBuffers.buffer(2);
+ data.writeShort(length);
+
+ return prepareBgpNotification(errorCode, errorSubcode, data);
+ }
+}
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
new file mode 100644
index 0000000..92086f3
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2014 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.sdnip.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.onosproject.sdnip.bgp.BgpConstants.Notifications;
+import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
+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.getRemoteAddress(), 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.getRemoteAddress(), remoteBgpVersion,
+ BgpConstants.BGP_VERSION);
+ //
+ // ERROR: Unsupported Version Number
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = OpenMessageError.ERROR_CODE;
+ int errorSubcode = 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.setRemoteBgpVersion(remoteBgpVersion);
+
+ // Remote AS number
+ long remoteAs = message.readUnsignedShort();
+ //
+ // 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.getRemoteAs() != 0) && (remoteAs != bs.getRemoteAs())) {
+ log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
+ "Expected {}",
+ bgpSession.getRemoteAddress(), remoteAs,
+ bs.getRemoteAs());
+ //
+ // ERROR: Bad Peer AS
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = OpenMessageError.ERROR_CODE;
+ int errorSubcode = OpenMessageError.BAD_PEER_AS;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode,
+ errorSubcode, null);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ return;
+ }
+ }
+ bgpSession.setRemoteAs(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.getRemoteAddress(), remoteHoldtime,
+ BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME);
+ //
+ // ERROR: Unacceptable Hold Time
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = OpenMessageError.ERROR_CODE;
+ int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ return;
+ }
+ bgpSession.setRemoteHoldtime(remoteHoldtime);
+
+ // Remote BGP Identifier
+ Ip4Address remoteBgpId =
+ Ip4Address.valueOf((int) message.readUnsignedInt());
+ bgpSession.setRemoteBgpId(remoteBgpId);
+
+ // Optional Parameters
+ int optParamLen = message.readUnsignedByte();
+ if (message.readableBytes() < optParamLen) {
+ log.debug("BGP RX OPEN Error from {}: " +
+ "Invalid Optional Parameter Length field {}. " +
+ "Remaining Optional Parameters {}",
+ bgpSession.getRemoteAddress(), optParamLen,
+ message.readableBytes());
+ //
+ // ERROR: Invalid Optional Parameter Length field: Unspecific
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = OpenMessageError.ERROR_CODE;
+ int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ 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 {}",
+ bgpSession.getRemoteAddress(), remoteBgpVersion, remoteAs,
+ remoteBgpId, remoteHoldtime);
+
+ // Send my OPEN followed by KEEPALIVE
+ ChannelBuffer txMessage = prepareBgpOpen(bgpSession);
+ ctx.getChannel().write(txMessage);
+ //
+ 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 bgpSession the BGP Session to use
+ * @return the message to transmit (BGP header included)
+ */
+ private static ChannelBuffer prepareBgpOpen(BgpSession bgpSession) {
+ ChannelBuffer message =
+ ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
+
+ //
+ // Prepare the OPEN message payload
+ //
+ message.writeByte(bgpSession.getLocalBgpVersion());
+ message.writeShort((int) bgpSession.getLocalAs());
+ message.writeShort((int) bgpSession.getLocalHoldtime());
+ message.writeInt(bgpSession.getLocalBgpId().toInt());
+ message.writeByte(0); // No Optional Parameters
+ return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN,
+ 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 a60cfc8..248085b 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
@@ -18,18 +18,14 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
-import org.apache.commons.lang3.tuple.Pair;
import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
@@ -42,9 +38,6 @@
import org.onlab.packet.Ip4Prefix;
import org.onosproject.sdnip.bgp.BgpConstants.Notifications;
import org.onosproject.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -94,6 +87,18 @@
*/
BgpSession(BgpSessionManager bgpSessionManager) {
this.bgpSessionManager = bgpSessionManager;
+
+ // NOTE: We support only BGP4
+ this.localBgpVersion = BgpConstants.BGP_VERSION;
+ }
+
+ /**
+ * Gets the BGP Session Manager.
+ *
+ * @return the BGP Session Manager
+ */
+ BgpSessionManager getBgpSessionManager() {
+ return bgpSessionManager;
}
/**
@@ -101,8 +106,8 @@
*
* @return the BGP RIB-IN routing entries
*/
- public Collection<BgpRouteEntry> getBgpRibIn() {
- return bgpRibIn.values();
+ public Map<Ip4Prefix, BgpRouteEntry> bgpRibIn() {
+ return bgpRibIn;
}
/**
@@ -143,6 +148,15 @@
}
/**
+ * Sets the BGP session remote BGP version.
+ *
+ * @param remoteBgpVersion the BGP session remote BGP version to set
+ */
+ void setRemoteBgpVersion(int remoteBgpVersion) {
+ this.remoteBgpVersion = remoteBgpVersion;
+ }
+
+ /**
* Gets the BGP session remote AS number.
*
* @return the BGP session remote AS number
@@ -152,6 +166,23 @@
}
/**
+ * Sets the BGP session remote AS number.
+ *
+ * @param remoteAs the BGP session remote AS number to set
+ */
+ void setRemoteAs(long remoteAs) {
+ this.remoteAs = 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.
+ //
+ this.localAs = remoteAs;
+ }
+
+ /**
* Gets the BGP session remote Holdtime.
*
* @return the BGP session remote Holdtime
@@ -161,6 +192,32 @@
}
/**
+ * Sets the BGP session remote Holdtime.
+ *
+ * @param remoteHoldtime the BGP session remote Holdtime to set
+ */
+ void setRemoteHoldtime(long remoteHoldtime) {
+ this.remoteHoldtime = 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.
+ //
+ this.localHoldtime = remoteHoldtime;
+
+ // Set the local Keepalive interval
+ if (localHoldtime == 0) {
+ localKeepaliveInterval = 0;
+ } else {
+ localKeepaliveInterval = Math.max(localHoldtime /
+ BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
+ BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
+ }
+ }
+
+ /**
* Gets the BGP session remote BGP Identifier as an IPv4 address.
*
* @return the BGP session remote BGP Identifier as an IPv4 address
@@ -170,6 +227,15 @@
}
/**
+ * Sets the BGP session remote BGP Identifier as an IPv4 address.
+ *
+ * @param remoteBgpId the BGP session remote BGP Identifier to set
+ */
+ void setRemoteBgpId(Ip4Address remoteBgpId) {
+ this.remoteBgpId = remoteBgpId;
+ }
+
+ /**
* Gets the BGP session local address.
*
* @return the BGP session local address
@@ -179,6 +245,15 @@
}
/**
+ * Gets the BGP session local IPv4 address.
+ *
+ * @return the BGP session local IPv4 address
+ */
+ public Ip4Address getLocalIp4Address() {
+ return localIp4Address;
+ }
+
+ /**
* Gets the BGP session local BGP version.
*
* @return the BGP session local BGP version
@@ -282,6 +357,12 @@
remoteAddress);
ctx.getChannel().close();
}
+
+ //
+ // Assign the local BGP ID
+ // NOTE: This should be configuration-based
+ //
+ localBgpId = bgpSessionManager.getMyBgpId();
}
@Override
@@ -326,1458 +407,9 @@
}
/**
- * Processes BGP OPEN message.
- *
- * @param ctx the Channel Handler Context
- * @param message the message to process
- */
- void processBgpOpen(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 {}",
- remoteAddress, message.readableBytes(), minLength);
- //
- // ERROR: Bad Message Length
- //
- // Send NOTIFICATION and close the connection
- ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
- message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
-
- //
- // Parse the OPEN message
- //
- // Remote BGP version
- remoteBgpVersion = message.readUnsignedByte();
- if (remoteBgpVersion != BgpConstants.BGP_VERSION) {
- log.debug("BGP RX OPEN Error from {}: " +
- "Unsupported BGP version {}. Should be {}",
- remoteAddress, remoteBgpVersion,
- BgpConstants.BGP_VERSION);
- //
- // ERROR: Unsupported Version Number
- //
- // Send NOTIFICATION and close the connection
- int errorCode = OpenMessageError.ERROR_CODE;
- int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER;
- ChannelBuffer data = ChannelBuffers.buffer(2);
- data.writeShort(BgpConstants.BGP_VERSION);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
-
- // Remote AS number
- remoteAs = message.readUnsignedShort();
- //
- // 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 bgpSession : bgpSessionManager.getBgpSessions()) {
- if (remoteAs != bgpSession.getRemoteAs()) {
- log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
- "Expected {}",
- remoteAddress, remoteAs, bgpSession.getRemoteAs());
- //
- // ERROR: Bad Peer AS
- //
- // Send NOTIFICATION and close the connection
- int errorCode = OpenMessageError.ERROR_CODE;
- int errorSubcode = OpenMessageError.BAD_PEER_AS;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
- }
-
- // Remote Hold Time
- 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 {}",
- remoteAddress, remoteHoldtime,
- BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME);
- //
- // ERROR: Unacceptable Hold Time
- //
- // Send NOTIFICATION and close the connection
- int errorCode = OpenMessageError.ERROR_CODE;
- int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
-
- // Remote BGP Identifier
- remoteBgpId = Ip4Address.valueOf((int) message.readUnsignedInt());
-
- // Optional Parameters
- int optParamLen = message.readUnsignedByte();
- if (message.readableBytes() < optParamLen) {
- log.debug("BGP RX OPEN Error from {}: " +
- "Invalid Optional Parameter Length field {}. " +
- "Remaining Optional Parameters {}",
- remoteAddress, optParamLen, message.readableBytes());
- //
- // ERROR: Invalid Optional Parameter Length field: Unspecific
- //
- // Send NOTIFICATION and close the connection
- int errorCode = OpenMessageError.ERROR_CODE;
- int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
- // NOTE: Parse the optional parameters (if needed)
- message.readBytes(optParamLen); // NOTE: data ignored
-
- //
- // Copy some of the remote peer's state/setup to the local setup:
- // - BGP version
- // - AS number (NOTE: the peer setup is always iBGP)
- // - Holdtime
- // Also, assign the local BGP ID based on the local setup
- //
- localBgpVersion = remoteBgpVersion;
- localAs = remoteAs;
- localHoldtime = remoteHoldtime;
- localBgpId = bgpSessionManager.getMyBgpId();
-
- // Set the Keepalive interval
- if (localHoldtime == 0) {
- localKeepaliveInterval = 0;
- } else {
- localKeepaliveInterval = Math.max(localHoldtime /
- BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
- BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
- }
-
- log.debug("BGP RX OPEN message from {}: " +
- "BGPv{} AS {} BGP-ID {} Holdtime {}",
- remoteAddress, remoteBgpVersion, remoteAs,
- remoteBgpId, remoteHoldtime);
-
- // Send my OPEN followed by KEEPALIVE
- ChannelBuffer txMessage = prepareBgpOpen();
- ctx.getChannel().write(txMessage);
- //
- txMessage = prepareBgpKeepalive();
- ctx.getChannel().write(txMessage);
-
- // Start the KEEPALIVE timer
- restartKeepaliveTimer(ctx);
-
- // Start the Session Timeout timer
- restartSessionTimeoutTimer(ctx);
- }
-
- /**
- * Processes BGP UPDATE message.
- *
- * @param ctx the Channel Handler Context
- * @param message the message to process
- */
- void processBgpUpdate(ChannelHandlerContext ctx, ChannelBuffer message) {
- Collection<BgpRouteEntry> addedRoutes = null;
- Map<Ip4Prefix, BgpRouteEntry> deletedRoutes = new HashMap<>();
-
- int minLength =
- BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
- if (message.readableBytes() < minLength) {
- log.debug("BGP RX UPDATE Error from {}: " +
- "Message length {} too short. Must be at least {}",
- remoteAddress, message.readableBytes(), minLength);
- //
- // ERROR: Bad Message Length
- //
- // Send NOTIFICATION and close the connection
- ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
- message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
-
- log.debug("BGP RX UPDATE message from {}", remoteAddress);
-
- //
- // Parse the UPDATE message
- //
-
- //
- // Parse the Withdrawn Routes
- //
- int withdrawnRoutesLength = message.readUnsignedShort();
- if (withdrawnRoutesLength > message.readableBytes()) {
- // ERROR: Malformed Attribute List
- actionsBgpUpdateMalformedAttributeList(ctx);
- return;
- }
- Collection<Ip4Prefix> withdrawnPrefixes = null;
- try {
- withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength,
- message);
- } catch (BgpParseException e) {
- // ERROR: Invalid Network Field
- log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
- remoteBgpId, e);
- actionsBgpUpdateInvalidNetworkField(ctx);
- return;
- }
- for (Ip4Prefix prefix : withdrawnPrefixes) {
- log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
- remoteAddress, prefix);
- BgpRouteEntry bgpRouteEntry = bgpRibIn.get(prefix);
- if (bgpRouteEntry != null) {
- deletedRoutes.put(prefix, bgpRouteEntry);
- }
- }
-
- //
- // Parse the Path Attributes
- //
- try {
- addedRoutes = parsePathAttributes(ctx, message);
- } catch (BgpParseException e) {
- log.debug("Exception parsing Path Attributes from BGP peer {}: ",
- remoteBgpId, e);
- // NOTE: The session was already closed, so nothing else to do
- return;
- }
- // Ignore WITHDRAWN routes that are ADDED
- for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
- deletedRoutes.remove(bgpRouteEntry.prefix());
- }
-
- // Update the BGP RIB-IN
- for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) {
- bgpRibIn.remove(bgpRouteEntry.prefix());
- }
- for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
- bgpRibIn.put(bgpRouteEntry.prefix(), bgpRouteEntry);
- }
-
- // Push the updates to the BGP Merged RIB
- BgpSessionManager.BgpRouteSelector bgpRouteSelector =
- bgpSessionManager.getBgpRouteSelector();
- bgpRouteSelector.routeUpdates(this, addedRoutes,
- deletedRoutes.values());
-
- // Start the Session Timeout timer
- restartSessionTimeoutTimer(ctx);
- }
-
- /**
- * Parse BGP Path Attributes from the BGP UPDATE message.
- *
- * @param ctx the Channel Handler Context
- * @param message the message to parse
- * @return a collection of the result BGP Route Entries
- * @throws BgpParseException
- */
- private Collection<BgpRouteEntry> parsePathAttributes(
- ChannelHandlerContext ctx,
- ChannelBuffer message)
- throws BgpParseException {
- Map<Ip4Prefix, BgpRouteEntry> addedRoutes = new HashMap<>();
-
- //
- // Parsed values
- //
- Short origin = -1; // Mandatory
- BgpRouteEntry.AsPath asPath = null; // Mandatory
- Ip4Address nextHop = null; // Mandatory
- long multiExitDisc = // Optional
- BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
- Long localPref = null; // Mandatory
- Long aggregatorAsNumber = null; // Optional: unused
- Ip4Address aggregatorIpAddress = null; // Optional: unused
-
- //
- // Get and verify the Path Attributes Length
- //
- int pathAttributeLength = message.readUnsignedShort();
- if (pathAttributeLength > message.readableBytes()) {
- // ERROR: Malformed Attribute List
- actionsBgpUpdateMalformedAttributeList(ctx);
- String errorMsg = "Malformed Attribute List";
- throw new BgpParseException(errorMsg);
- }
- if (pathAttributeLength == 0) {
- return addedRoutes.values();
- }
-
- //
- // Parse the Path Attributes
- //
- int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
- while (message.readerIndex() < pathAttributeEnd) {
- int attrFlags = message.readUnsignedByte();
- if (message.readerIndex() >= pathAttributeEnd) {
- // ERROR: Malformed Attribute List
- actionsBgpUpdateMalformedAttributeList(ctx);
- String errorMsg = "Malformed Attribute List";
- throw new BgpParseException(errorMsg);
- }
- int attrTypeCode = message.readUnsignedByte();
-
- // The Attribute Flags
- boolean optionalBit = ((0x80 & attrFlags) != 0);
- boolean transitiveBit = ((0x40 & attrFlags) != 0);
- boolean partialBit = ((0x20 & attrFlags) != 0);
- boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
-
- // The Attribute Length
- int attrLen = 0;
- int attrLenOctets = 1;
- if (extendedLengthBit) {
- attrLenOctets = 2;
- }
- if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
- // ERROR: Malformed Attribute List
- actionsBgpUpdateMalformedAttributeList(ctx);
- String errorMsg = "Malformed Attribute List";
- throw new BgpParseException(errorMsg);
- }
- if (extendedLengthBit) {
- attrLen = message.readUnsignedShort();
- } else {
- attrLen = message.readUnsignedByte();
- }
- if (message.readerIndex() + attrLen > pathAttributeEnd) {
- // ERROR: Malformed Attribute List
- actionsBgpUpdateMalformedAttributeList(ctx);
- String errorMsg = "Malformed Attribute List";
- throw new BgpParseException(errorMsg);
- }
-
- //
- // Verify the Attribute Flags
- //
- verifyBgpUpdateAttributeFlags(ctx, attrTypeCode, attrLen,
- attrFlags, message);
-
- //
- // Extract the Attribute Value based on the Attribute Type Code
- //
- switch (attrTypeCode) {
-
- case BgpConstants.Update.Origin.TYPE:
- // Attribute Type Code ORIGIN
- origin = parseAttributeTypeOrigin(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- break;
-
- case BgpConstants.Update.AsPath.TYPE:
- // Attribute Type Code AS_PATH
- asPath = parseAttributeTypeAsPath(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- break;
-
- case BgpConstants.Update.NextHop.TYPE:
- // Attribute Type Code NEXT_HOP
- nextHop = parseAttributeTypeNextHop(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- break;
-
- case BgpConstants.Update.MultiExitDisc.TYPE:
- // Attribute Type Code MULTI_EXIT_DISC
- multiExitDisc =
- parseAttributeTypeMultiExitDisc(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- break;
-
- case BgpConstants.Update.LocalPref.TYPE:
- // Attribute Type Code LOCAL_PREF
- localPref =
- parseAttributeTypeLocalPref(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- break;
-
- case BgpConstants.Update.AtomicAggregate.TYPE:
- // Attribute Type Code ATOMIC_AGGREGATE
- parseAttributeTypeAtomicAggregate(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- // Nothing to do: this attribute is primarily informational
- break;
-
- case BgpConstants.Update.Aggregator.TYPE:
- // Attribute Type Code AGGREGATOR
- Pair<Long, Ip4Address> aggregator =
- parseAttributeTypeAggregator(ctx, attrTypeCode, attrLen,
- attrFlags, message);
- aggregatorAsNumber = aggregator.getLeft();
- aggregatorIpAddress = aggregator.getRight();
- break;
-
- default:
- // NOTE: Parse any new Attribute Types if needed
- if (!optionalBit) {
- // ERROR: Unrecognized Well-known Attribute
- actionsBgpUpdateUnrecognizedWellKnownAttribute(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Unrecognized Well-known Attribute: " +
- attrTypeCode;
- throw new BgpParseException(errorMsg);
- }
-
- // Skip the data from the unrecognized attribute
- log.debug("BGP RX UPDATE message from {}: " +
- "Unrecognized Attribute Type {}",
- remoteAddress, attrTypeCode);
- message.skipBytes(attrLen);
- break;
- }
- }
-
- //
- // Verify the Well-known Attributes
- //
- verifyBgpUpdateWellKnownAttributes(ctx, origin, asPath, nextHop,
- localPref);
-
- //
- // Parse the NLRI (Network Layer Reachability Information)
- //
- Collection<Ip4Prefix> addedPrefixes = null;
- int nlriLength = message.readableBytes();
- try {
- addedPrefixes = parsePackedPrefixes(nlriLength, message);
- } catch (BgpParseException e) {
- // ERROR: Invalid Network Field
- log.debug("Exception parsing NLRI from BGP peer {}: ",
- remoteBgpId, e);
- actionsBgpUpdateInvalidNetworkField(ctx);
- // Rethrow the exception
- throw e;
- }
-
- // Generate the added routes
- for (Ip4Prefix prefix : addedPrefixes) {
- BgpRouteEntry bgpRouteEntry =
- new BgpRouteEntry(this, prefix, nextHop,
- origin.byteValue(), asPath, localPref);
- bgpRouteEntry.setMultiExitDisc(multiExitDisc);
- if (bgpRouteEntry.hasAsPathLoop(localAs)) {
- log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
- "nextHop {}: contains AS Path loop",
- remoteAddress, prefix, nextHop);
- continue;
- } else {
- log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
- remoteAddress, prefix, nextHop);
- }
- addedRoutes.put(prefix, bgpRouteEntry);
- }
-
- return addedRoutes.values();
- }
-
- /**
- * Verifies BGP UPDATE Well-known Attributes.
- *
- * @param ctx the Channel Handler Context
- * @param origin the ORIGIN well-known mandatory attribute
- * @param asPath the AS_PATH well-known mandatory attribute
- * @param nextHop the NEXT_HOP well-known mandatory attribute
- * @param localPref the LOCAL_PREF required attribute
- * @throws BgpParseException
- */
- private void verifyBgpUpdateWellKnownAttributes(
- ChannelHandlerContext ctx,
- Short origin,
- BgpRouteEntry.AsPath asPath,
- Ip4Address nextHop,
- Long localPref)
- throws BgpParseException {
- //
- // Check for Missing Well-known Attributes
- //
- if ((origin == null) || (origin == -1)) {
- // Missing Attribute Type Code ORIGIN
- int type = BgpConstants.Update.Origin.TYPE;
- actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
- String errorMsg = "Missing Well-known Attribute: ORIGIN";
- throw new BgpParseException(errorMsg);
- }
- if (asPath == null) {
- // Missing Attribute Type Code AS_PATH
- int type = BgpConstants.Update.AsPath.TYPE;
- actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
- String errorMsg = "Missing Well-known Attribute: AS_PATH";
- throw new BgpParseException(errorMsg);
- }
- if (nextHop == null) {
- // Missing Attribute Type Code NEXT_HOP
- int type = BgpConstants.Update.NextHop.TYPE;
- actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
- String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
- throw new BgpParseException(errorMsg);
- }
- if (localPref == null) {
- // Missing Attribute Type Code LOCAL_PREF
- // NOTE: Required for iBGP
- int type = BgpConstants.Update.LocalPref.TYPE;
- actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
- String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
- throw new BgpParseException(errorMsg);
- }
- }
-
- /**
- * Verifies the BGP UPDATE Attribute Flags.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @throws BgpParseException
- */
- private void verifyBgpUpdateAttributeFlags(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- //
- // Assign the Attribute Type Name and the Well-known flag
- //
- String typeName = "UNKNOWN";
- boolean isWellKnown = false;
- switch (attrTypeCode) {
- case BgpConstants.Update.Origin.TYPE:
- isWellKnown = true;
- typeName = "ORIGIN";
- break;
- case BgpConstants.Update.AsPath.TYPE:
- isWellKnown = true;
- typeName = "AS_PATH";
- break;
- case BgpConstants.Update.NextHop.TYPE:
- isWellKnown = true;
- typeName = "NEXT_HOP";
- break;
- case BgpConstants.Update.MultiExitDisc.TYPE:
- isWellKnown = false;
- typeName = "MULTI_EXIT_DISC";
- break;
- case BgpConstants.Update.LocalPref.TYPE:
- isWellKnown = true;
- typeName = "LOCAL_PREF";
- break;
- case BgpConstants.Update.AtomicAggregate.TYPE:
- isWellKnown = true;
- typeName = "ATOMIC_AGGREGATE";
- break;
- case BgpConstants.Update.Aggregator.TYPE:
- isWellKnown = false;
- typeName = "AGGREGATOR";
- break;
- default:
- isWellKnown = false;
- typeName = "UNKNOWN(" + attrTypeCode + ")";
- break;
- }
-
- //
- // Verify the Attribute Flags
- //
- boolean optionalBit = ((0x80 & attrFlags) != 0);
- boolean transitiveBit = ((0x40 & attrFlags) != 0);
- boolean partialBit = ((0x20 & attrFlags) != 0);
- if ((isWellKnown && optionalBit) ||
- (isWellKnown && (!transitiveBit)) ||
- (isWellKnown && partialBit) ||
- (optionalBit && (!transitiveBit) && partialBit)) {
- //
- // ERROR: The Optional bit cannot be set for Well-known attributes
- // ERROR: The Transtive bit MUST be 1 for well-known attributes
- // ERROR: The Partial bit MUST be 0 for well-known attributes
- // ERROR: The Partial bit MUST be 0 for optional non-transitive
- // attributes
- //
- actionsBgpUpdateAttributeFlagsError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Flags Error for " + typeName + ": " +
- attrFlags;
- throw new BgpParseException(errorMsg);
- }
- }
-
- /**
- * Parses BGP UPDATE Attribute Type ORIGIN.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed ORIGIN value
- * @throws BgpParseException
- */
- private short parseAttributeTypeOrigin(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.Origin.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- message.markReaderIndex();
- short origin = message.readUnsignedByte();
- switch (origin) {
- case BgpConstants.Update.Origin.IGP:
- // FALLTHROUGH
- case BgpConstants.Update.Origin.EGP:
- // FALLTHROUGH
- case BgpConstants.Update.Origin.INCOMPLETE:
- break;
- default:
- // ERROR: Invalid ORIGIN Attribute
- message.resetReaderIndex();
- actionsBgpUpdateInvalidOriginAttribute(
- ctx, attrTypeCode, attrLen, attrFlags, message, origin);
- String errorMsg = "Invalid ORIGIN Attribute: " + origin;
- throw new BgpParseException(errorMsg);
- }
-
- return origin;
- }
-
- /**
- * Parses BGP UPDATE Attribute AS Path.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed AS Path
- * @throws BgpParseException
- */
- private BgpRouteEntry.AsPath parseAttributeTypeAsPath(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
- ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
-
- //
- // Parse the message
- //
- while (attrLen > 0) {
- if (attrLen < 2) {
- // ERROR: Malformed AS_PATH
- actionsBgpUpdateMalformedAsPath(ctx);
- String errorMsg = "Malformed AS Path";
- throw new BgpParseException(errorMsg);
- }
- // Get the Path Segment Type and Length (in number of ASes)
- short pathSegmentType = message.readUnsignedByte();
- short pathSegmentLength = message.readUnsignedByte();
- attrLen -= 2;
-
- // Verify the Path Segment Type
- switch (pathSegmentType) {
- case BgpConstants.Update.AsPath.AS_SET:
- // FALLTHROUGH
- case BgpConstants.Update.AsPath.AS_SEQUENCE:
- // FALLTHROUGH
- case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
- // FALLTHROUGH
- case BgpConstants.Update.AsPath.AS_CONFED_SET:
- break;
- default:
- // ERROR: Invalid Path Segment Type
- //
- // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
- // for "Invalid Path Segment Type", hence we return
- // the error as "Malformed AS_PATH".
- //
- actionsBgpUpdateMalformedAsPath(ctx);
- String errorMsg =
- "Invalid AS Path Segment Type: " + pathSegmentType;
- throw new BgpParseException(errorMsg);
- }
-
- // Parse the AS numbers
- if (2 * pathSegmentLength > attrLen) {
- // ERROR: Malformed AS_PATH
- actionsBgpUpdateMalformedAsPath(ctx);
- String errorMsg = "Malformed AS Path";
- throw new BgpParseException(errorMsg);
- }
- attrLen -= (2 * pathSegmentLength);
- ArrayList<Long> segmentAsNumbers = new ArrayList<>();
- while (pathSegmentLength-- > 0) {
- long asNumber = message.readUnsignedShort();
- segmentAsNumbers.add(asNumber);
- }
-
- BgpRouteEntry.PathSegment pathSegment =
- new BgpRouteEntry.PathSegment((byte) pathSegmentType,
- segmentAsNumbers);
- pathSegments.add(pathSegment);
- }
-
- return new BgpRouteEntry.AsPath(pathSegments);
- }
-
- /**
- * Parses BGP UPDATE Attribute Type NEXT_HOP.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed NEXT_HOP value
- * @throws BgpParseException
- */
- private Ip4Address parseAttributeTypeNextHop(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- message.markReaderIndex();
- Ip4Address nextHopAddress =
- Ip4Address.valueOf((int) message.readUnsignedInt());
- //
- // Check whether the NEXT_HOP IP address is semantically correct.
- // As per RFC 4271, Section 6.3:
- //
- // a) It MUST NOT be the IP address of the receiving speaker
- // b) In the case of an EBGP ....
- //
- // Here we check only (a), because (b) doesn't apply for us: all our
- // peers are iBGP.
- //
- if (nextHopAddress.equals(localIp4Address)) {
- // ERROR: Invalid NEXT_HOP Attribute
- message.resetReaderIndex();
- actionsBgpUpdateInvalidNextHopAttribute(
- ctx, attrTypeCode, attrLen, attrFlags, message,
- nextHopAddress);
- String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
- throw new BgpParseException(errorMsg);
- }
-
- return nextHopAddress;
- }
-
- /**
- * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed MULTI_EXIT_DISC value
- * @throws BgpParseException
- */
- private long parseAttributeTypeMultiExitDisc(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- long multiExitDisc = message.readUnsignedInt();
- return multiExitDisc;
- }
-
- /**
- * Parses BGP UPDATE Attribute Type LOCAL_PREF.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed LOCAL_PREF value
- * @throws BgpParseException
- */
- private long parseAttributeTypeLocalPref(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- long localPref = message.readUnsignedInt();
- return localPref;
- }
-
- /**
- * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @throws BgpParseException
- */
- private void parseAttributeTypeAtomicAggregate(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- // Nothing to do: this attribute is primarily informational
- }
-
- /**
- * Parses BGP UPDATE Attribute Type AGGREGATOR.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message to parse
- * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
- * @throws BgpParseException
- */
- private Pair<Long, Ip4Address> parseAttributeTypeAggregator(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message)
- throws BgpParseException {
-
- // Check the Attribute Length
- if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
- // ERROR: Attribute Length Error
- actionsBgpUpdateAttributeLengthError(
- ctx, attrTypeCode, attrLen, attrFlags, message);
- String errorMsg = "Attribute Length Error";
- throw new BgpParseException(errorMsg);
- }
-
- // The AGGREGATOR AS number
- long aggregatorAsNumber = message.readUnsignedShort();
- // The AGGREGATOR IP address
- Ip4Address aggregatorIpAddress =
- Ip4Address.valueOf((int) message.readUnsignedInt());
-
- Pair<Long, Ip4Address> aggregator = Pair.of(aggregatorAsNumber,
- aggregatorIpAddress);
- return aggregator;
- }
-
- /**
- * Parses a message that contains encoded IPv4 network prefixes.
- * <p>
- * The IPv4 prefixes are encoded in the form:
- * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
- * and Prefix is the IPv4 prefix (padded with trailing bits to the end
- * of an octet).
- *
- * @param totalLength the total length of the data to parse
- * @param message the message with data to parse
- * @return a collection of parsed IPv4 network prefixes
- * @throws BgpParseException
- */
- private Collection<Ip4Prefix> parsePackedPrefixes(int totalLength,
- ChannelBuffer message)
- throws BgpParseException {
- Collection<Ip4Prefix> result = new ArrayList<>();
-
- if (totalLength == 0) {
- return result;
- }
-
- // Parse the data
- int dataEnd = message.readerIndex() + totalLength;
- while (message.readerIndex() < dataEnd) {
- int prefixBitlen = message.readUnsignedByte();
- int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
- if (message.readerIndex() + prefixBytelen > dataEnd) {
- String errorMsg = "Malformed Network Prefixes";
- throw new BgpParseException(errorMsg);
- }
-
- long address = 0;
- long extraShift = (4 - prefixBytelen) * 8;
- while (prefixBytelen > 0) {
- address <<= 8;
- address |= message.readUnsignedByte();
- prefixBytelen--;
- }
- address <<= extraShift;
- Ip4Prefix prefix =
- Ip4Prefix.valueOf(Ip4Address.valueOf((int) address),
- prefixBitlen);
- result.add(prefix);
- }
-
- return result;
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Invalid Network Field Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- */
- private void actionsBgpUpdateInvalidNetworkField(
- ChannelHandlerContext ctx) {
- log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
- remoteAddress);
-
- //
- // ERROR: Invalid Network Field
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Malformed Attribute List Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- */
- private void actionsBgpUpdateMalformedAttributeList(
- ChannelHandlerContext ctx) {
- log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
- remoteAddress);
-
- //
- // ERROR: Malformed Attribute List
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Missing Well-known Attribute Error: send NOTIFICATION and close the
- * channel.
- *
- * @param ctx the Channel Handler Context
- * @param missingAttrTypeCode the missing attribute type code
- */
- private void actionsBgpUpdateMissingWellKnownAttribute(
- ChannelHandlerContext ctx,
- int missingAttrTypeCode) {
- log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
- remoteAddress, missingAttrTypeCode);
-
- //
- // ERROR: Missing Well-known Attribute
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
- ChannelBuffer data = ChannelBuffers.buffer(1);
- data.writeByte(missingAttrTypeCode);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- * @param origin the ORIGIN attribute value
- */
- private void actionsBgpUpdateInvalidOriginAttribute(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message,
- short origin) {
- log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
- remoteAddress);
-
- //
- // ERROR: Invalid ORIGIN Attribute
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
- ChannelBuffer data =
- prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
- attrFlags, message);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Attribute Flags Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- */
- private void actionsBgpUpdateAttributeFlagsError(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message) {
- log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
- remoteAddress);
-
- //
- // ERROR: Attribute Flags Error
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
- ChannelBuffer data =
- prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
- attrFlags, message);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
- * channel.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- * @param nextHop the NEXT_HOP attribute value
- */
- private void actionsBgpUpdateInvalidNextHopAttribute(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message,
- Ip4Address nextHop) {
- log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
- remoteAddress, nextHop);
-
- //
- // ERROR: Invalid ORIGIN Attribute
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
- ChannelBuffer data =
- prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
- attrFlags, message);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
- * the channel.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- */
- private void actionsBgpUpdateUnrecognizedWellKnownAttribute(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message) {
- log.debug("BGP RX UPDATE Error from {}: " +
- "Unrecognized Well-known Attribute Error: {}",
- remoteAddress, attrTypeCode);
-
- //
- // ERROR: Unrecognized Well-known Attribute
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode =
- UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
- ChannelBuffer data =
- prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
- attrFlags, message);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Attribute Length Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- */
- private void actionsBgpUpdateAttributeLengthError(
- ChannelHandlerContext ctx,
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message) {
- log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
- remoteAddress);
-
- //
- // ERROR: Attribute Length Error
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
- ChannelBuffer data =
- prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
- attrFlags, message);
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, data);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Applies the appropriate actions after detecting BGP UPDATE
- * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
- *
- * @param ctx the Channel Handler Context
- */
- private void actionsBgpUpdateMalformedAsPath(
- ChannelHandlerContext ctx) {
- log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
- remoteAddress);
-
- //
- // ERROR: Malformed AS_PATH
- //
- // Send NOTIFICATION and close the connection
- int errorCode = UpdateMessageError.ERROR_CODE;
- int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
- ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- }
-
- /**
- * Processes BGP NOTIFICATION message.
- *
- * @param ctx the Channel Handler Context
- * @param message the message to process
- */
- void processBgpNotification(ChannelHandlerContext ctx,
- ChannelBuffer message) {
- int minLength =
- BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
- if (message.readableBytes() < minLength) {
- log.debug("BGP RX NOTIFICATION Error from {}: " +
- "Message length {} too short. Must be at least {}",
- remoteAddress, message.readableBytes(), minLength);
- //
- // ERROR: Bad Message Length
- //
- // NOTE: We do NOT send NOTIFICATION in response to a notification
- return;
- }
-
- //
- // Parse the NOTIFICATION message
- //
- int errorCode = message.readUnsignedByte();
- int errorSubcode = message.readUnsignedByte();
- int dataLength = message.readableBytes();
-
- log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " +
- "Error Subcode {} Data Length {}",
- remoteAddress, errorCode, errorSubcode, dataLength);
-
- //
- // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to
- // close the connection.
- //
-
- // Start the Session Timeout timer
- restartSessionTimeoutTimer(ctx);
- }
-
- /**
- * Processes BGP KEEPALIVE message.
- *
- * @param ctx the Channel Handler Context
- * @param message the message to process
- */
- void processBgpKeepalive(ChannelHandlerContext ctx,
- ChannelBuffer message) {
- if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
- BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
- log.debug("BGP RX KEEPALIVE Error from {}: " +
- "Invalid total message length {}. Expected {}",
- remoteAddress,
- message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH,
- BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH);
- //
- // ERROR: Bad Message Length
- //
- // Send NOTIFICATION and close the connection
- ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
- message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
- ctx.getChannel().write(txMessage);
- closeSession(ctx);
- return;
- }
-
- //
- // Parse the KEEPALIVE message: nothing to do
- //
- log.trace("BGP RX KEEPALIVE message from {}", remoteAddress);
-
- // Start the Session Timeout timer
- restartSessionTimeoutTimer(ctx);
- }
-
- /**
- * Prepares BGP OPEN message.
- *
- * @return the message to transmit (BGP header included)
- */
- private ChannelBuffer prepareBgpOpen() {
- ChannelBuffer message =
- ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
-
- //
- // Prepare the OPEN message payload
- //
- message.writeByte(localBgpVersion);
- message.writeShort((int) localAs);
- message.writeShort((int) localHoldtime);
- message.writeInt(bgpSessionManager.getMyBgpId().toInt());
- message.writeByte(0); // No Optional Parameters
- return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
- }
-
- /**
- * Prepares BGP KEEPALIVE message.
- *
- * @return the message to transmit (BGP header included)
- */
- private ChannelBuffer prepareBgpKeepalive() {
- ChannelBuffer message =
- ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
-
- //
- // Prepare the KEEPALIVE message payload: nothing to do
- //
- return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
- }
-
- /**
- * Prepares BGP NOTIFICATION message.
- *
- * @param errorCode the BGP NOTIFICATION Error Code
- * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
- * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
- * @param data the BGP NOTIFICATION Data if applicable, otherwise null
- * @return the message to transmit (BGP header included)
- */
- ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
- ChannelBuffer data) {
- ChannelBuffer message =
- ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
-
- //
- // Prepare the NOTIFICATION message payload
- //
- message.writeByte(errorCode);
- message.writeByte(errorSubcode);
- if (data != null) {
- message.writeBytes(data);
- }
- return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
- }
-
- /**
- * Prepares BGP NOTIFICATION message: Bad Message Length.
- *
- * @param length the erroneous Length field
- * @return the message to transmit (BGP header included)
- */
- ChannelBuffer prepareBgpNotificationBadMessageLength(int length) {
- int errorCode = MessageHeaderError.ERROR_CODE;
- int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH;
- ChannelBuffer data = ChannelBuffers.buffer(2);
- data.writeShort(length);
-
- return prepareBgpNotification(errorCode, errorSubcode, data);
- }
-
- /**
- * Prepares BGP UPDATE Notification data payload.
- *
- * @param attrTypeCode the attribute type code
- * @param attrLen the attribute length (in octets)
- * @param attrFlags the attribute flags
- * @param message the message with the data
- * @return the buffer with the data payload for the BGP UPDATE Notification
- */
- private ChannelBuffer prepareBgpUpdateNotificationDataPayload(
- int attrTypeCode,
- int attrLen,
- int attrFlags,
- ChannelBuffer message) {
- // Compute the attribute length field octets
- boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
- int attrLenOctets = 1;
- if (extendedLengthBit) {
- attrLenOctets = 2;
- }
- ChannelBuffer data =
- ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
- data.writeByte(attrTypeCode);
- if (extendedLengthBit) {
- data.writeShort(attrLen);
- } else {
- data.writeByte(attrLen);
- }
- data.writeBytes(message, attrLen);
- return data;
- }
-
- /**
- * Prepares BGP message.
- *
- * @param type the BGP message type
- * @param payload the message payload to transmit (BGP header excluded)
- * @return the message to transmit (BGP header included)
- */
- private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
- ChannelBuffer message =
- ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
- payload.readableBytes());
-
- // Write the marker
- for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
- message.writeByte(0xff);
- }
-
- // Write the rest of the BGP header
- message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
- payload.readableBytes());
- message.writeByte(type);
-
- // Write the payload
- message.writeBytes(payload);
- return message;
- }
-
- /**
* Restarts the BGP KeepaliveTimer.
*/
- private void restartKeepaliveTimer(ChannelHandlerContext ctx) {
+ void restartKeepaliveTimer(ChannelHandlerContext ctx) {
if (localKeepaliveInterval == 0) {
return; // Nothing to do
}
@@ -1811,7 +443,7 @@
}
// Transmit the KEEPALIVE
- ChannelBuffer txMessage = prepareBgpKeepalive();
+ ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
ctx.getChannel().write(txMessage);
// Restart the KEEPALIVE timer
@@ -1822,7 +454,7 @@
/**
* Restarts the BGP Session Timeout Timer.
*/
- private void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
+ void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
if (remoteHoldtime == 0) {
return; // Nothing to do
}
@@ -1866,30 +498,10 @@
int errorCode = HoldTimerExpired.ERROR_CODE;
int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
ChannelBuffer txMessage =
- prepareBgpNotification(errorCode, errorSubcode, null);
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
ctx.getChannel().write(txMessage);
closeChannel(ctx);
}
}
-
- /**
- * An exception indicating a parsing error of the BGP message.
- */
- private static class BgpParseException extends Exception {
- /**
- * Default constructor.
- */
- public BgpParseException() {
- super();
- }
-
- /**
- * Constructor for a specific exception details message.
- *
- * @param message the message with the exception details
- */
- public BgpParseException(String message) {
- super(message);
- }
- }
}
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
new file mode 100644
index 0000000..733965e
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
@@ -0,0 +1,1236 @@
+/*
+ * Copyright 2014 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.sdnip.bgp;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.Pair;
+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.onlab.packet.Ip4Prefix;
+import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for handling BGP UPDATE messages.
+ */
+final class BgpUpdate {
+ private static final Logger log = LoggerFactory.getLogger(BgpUpdate.class);
+
+ /**
+ * Default constructor.
+ * <p>
+ * The constructor is private to prevent creating an instance of
+ * this utility class.
+ */
+ private BgpUpdate() {
+ }
+
+ /**
+ * Processes BGP UPDATE message.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param message the message to process
+ */
+ static void processBgpUpdate(BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ ChannelBuffer message) {
+ Collection<BgpRouteEntry> addedRoutes = null;
+ Map<Ip4Prefix, BgpRouteEntry> deletedRoutes = new HashMap<>();
+
+ int minLength =
+ BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
+ if (message.readableBytes() < minLength) {
+ log.debug("BGP RX UPDATE Error from {}: " +
+ "Message length {} too short. Must be at least {}",
+ bgpSession.getRemoteAddress(), 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;
+ }
+
+ log.debug("BGP RX UPDATE message from {}",
+ bgpSession.getRemoteAddress());
+
+ //
+ // Parse the UPDATE message
+ //
+
+ //
+ // Parse the Withdrawn Routes
+ //
+ int withdrawnRoutesLength = message.readUnsignedShort();
+ if (withdrawnRoutesLength > message.readableBytes()) {
+ // ERROR: Malformed Attribute List
+ actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
+ return;
+ }
+ Collection<Ip4Prefix> withdrawnPrefixes = null;
+ try {
+ withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength,
+ message);
+ } catch (BgpParseException e) {
+ // ERROR: Invalid Network Field
+ log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
+ bgpSession.getRemoteBgpId(), e);
+ actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
+ return;
+ }
+ for (Ip4Prefix prefix : withdrawnPrefixes) {
+ log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
+ bgpSession.getRemoteAddress(), prefix);
+ BgpRouteEntry bgpRouteEntry = bgpSession.bgpRibIn().get(prefix);
+ if (bgpRouteEntry != null) {
+ deletedRoutes.put(prefix, bgpRouteEntry);
+ }
+ }
+
+ //
+ // Parse the Path Attributes
+ //
+ try {
+ addedRoutes = parsePathAttributes(bgpSession, ctx, message);
+ } catch (BgpParseException e) {
+ log.debug("Exception parsing Path Attributes from BGP peer {}: ",
+ bgpSession.getRemoteBgpId(), e);
+ // NOTE: The session was already closed, so nothing else to do
+ return;
+ }
+ // Ignore WITHDRAWN routes that are ADDED
+ for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
+ deletedRoutes.remove(bgpRouteEntry.prefix());
+ }
+
+ // Update the BGP RIB-IN
+ for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) {
+ bgpSession.bgpRibIn().remove(bgpRouteEntry.prefix());
+ }
+ for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
+ bgpSession.bgpRibIn().put(bgpRouteEntry.prefix(), bgpRouteEntry);
+ }
+
+ // Push the updates to the BGP Merged RIB
+ BgpSessionManager.BgpRouteSelector bgpRouteSelector =
+ bgpSession.getBgpSessionManager().getBgpRouteSelector();
+ bgpRouteSelector.routeUpdates(bgpSession, addedRoutes,
+ deletedRoutes.values());
+
+ // Start the Session Timeout timer
+ bgpSession.restartSessionTimeoutTimer(ctx);
+ }
+
+ /**
+ * Parse BGP Path Attributes from the BGP UPDATE message.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param message the message to parse
+ * @return a collection of the result BGP Route Entries
+ * @throws BgpParseException
+ */
+ private static Collection<BgpRouteEntry> parsePathAttributes(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ ChannelBuffer message)
+ throws BgpParseException {
+ Map<Ip4Prefix, BgpRouteEntry> addedRoutes = new HashMap<>();
+
+ //
+ // Parsed values
+ //
+ Short origin = -1; // Mandatory
+ BgpRouteEntry.AsPath asPath = null; // Mandatory
+ Ip4Address nextHop = null; // Mandatory
+ long multiExitDisc = // Optional
+ BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
+ Long localPref = null; // Mandatory
+ Long aggregatorAsNumber = null; // Optional: unused
+ Ip4Address aggregatorIpAddress = null; // Optional: unused
+
+ //
+ // Get and verify the Path Attributes Length
+ //
+ int pathAttributeLength = message.readUnsignedShort();
+ if (pathAttributeLength > message.readableBytes()) {
+ // ERROR: Malformed Attribute List
+ actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
+ String errorMsg = "Malformed Attribute List";
+ throw new BgpParseException(errorMsg);
+ }
+ if (pathAttributeLength == 0) {
+ return addedRoutes.values();
+ }
+
+ //
+ // Parse the Path Attributes
+ //
+ int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
+ while (message.readerIndex() < pathAttributeEnd) {
+ int attrFlags = message.readUnsignedByte();
+ if (message.readerIndex() >= pathAttributeEnd) {
+ // ERROR: Malformed Attribute List
+ actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
+ String errorMsg = "Malformed Attribute List";
+ throw new BgpParseException(errorMsg);
+ }
+ int attrTypeCode = message.readUnsignedByte();
+
+ // The Attribute Flags
+ boolean optionalBit = ((0x80 & attrFlags) != 0);
+ boolean transitiveBit = ((0x40 & attrFlags) != 0);
+ boolean partialBit = ((0x20 & attrFlags) != 0);
+ boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
+
+ // The Attribute Length
+ int attrLen = 0;
+ int attrLenOctets = 1;
+ if (extendedLengthBit) {
+ attrLenOctets = 2;
+ }
+ if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
+ // ERROR: Malformed Attribute List
+ actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
+ String errorMsg = "Malformed Attribute List";
+ throw new BgpParseException(errorMsg);
+ }
+ if (extendedLengthBit) {
+ attrLen = message.readUnsignedShort();
+ } else {
+ attrLen = message.readUnsignedByte();
+ }
+ if (message.readerIndex() + attrLen > pathAttributeEnd) {
+ // ERROR: Malformed Attribute List
+ actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
+ String errorMsg = "Malformed Attribute List";
+ throw new BgpParseException(errorMsg);
+ }
+
+ // Verify the Attribute Flags
+ verifyBgpUpdateAttributeFlags(bgpSession, ctx, attrTypeCode,
+ attrLen, attrFlags, message);
+
+ //
+ // Extract the Attribute Value based on the Attribute Type Code
+ //
+ switch (attrTypeCode) {
+
+ case BgpConstants.Update.Origin.TYPE:
+ // Attribute Type Code ORIGIN
+ origin = parseAttributeTypeOrigin(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ break;
+
+ case BgpConstants.Update.AsPath.TYPE:
+ // Attribute Type Code AS_PATH
+ asPath = parseAttributeTypeAsPath(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ break;
+
+ case BgpConstants.Update.NextHop.TYPE:
+ // Attribute Type Code NEXT_HOP
+ nextHop = parseAttributeTypeNextHop(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ break;
+
+ case BgpConstants.Update.MultiExitDisc.TYPE:
+ // Attribute Type Code MULTI_EXIT_DISC
+ multiExitDisc =
+ parseAttributeTypeMultiExitDisc(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ break;
+
+ case BgpConstants.Update.LocalPref.TYPE:
+ // Attribute Type Code LOCAL_PREF
+ localPref =
+ parseAttributeTypeLocalPref(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ break;
+
+ case BgpConstants.Update.AtomicAggregate.TYPE:
+ // Attribute Type Code ATOMIC_AGGREGATE
+ parseAttributeTypeAtomicAggregate(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ // Nothing to do: this attribute is primarily informational
+ break;
+
+ case BgpConstants.Update.Aggregator.TYPE:
+ // Attribute Type Code AGGREGATOR
+ Pair<Long, Ip4Address> aggregator =
+ parseAttributeTypeAggregator(bgpSession, ctx,
+ attrTypeCode, attrLen,
+ attrFlags, message);
+ aggregatorAsNumber = aggregator.getLeft();
+ aggregatorIpAddress = aggregator.getRight();
+ break;
+
+ default:
+ // NOTE: Parse any new Attribute Types if needed
+ if (!optionalBit) {
+ // ERROR: Unrecognized Well-known Attribute
+ actionsBgpUpdateUnrecognizedWellKnownAttribute(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags,
+ message);
+ String errorMsg = "Unrecognized Well-known Attribute: " +
+ attrTypeCode;
+ throw new BgpParseException(errorMsg);
+ }
+
+ // Skip the data from the unrecognized attribute
+ log.debug("BGP RX UPDATE message from {}: " +
+ "Unrecognized Attribute Type {}",
+ bgpSession.getRemoteAddress(), attrTypeCode);
+ message.skipBytes(attrLen);
+ break;
+ }
+ }
+
+ // Verify the Well-known Attributes
+ verifyBgpUpdateWellKnownAttributes(bgpSession, ctx, origin, asPath,
+ nextHop, localPref);
+
+ //
+ // Parse the NLRI (Network Layer Reachability Information)
+ //
+ Collection<Ip4Prefix> addedPrefixes = null;
+ int nlriLength = message.readableBytes();
+ try {
+ addedPrefixes = parsePackedPrefixes(nlriLength, message);
+ } catch (BgpParseException e) {
+ // ERROR: Invalid Network Field
+ log.debug("Exception parsing NLRI from BGP peer {}: ",
+ bgpSession.getRemoteBgpId(), e);
+ actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
+ // Rethrow the exception
+ throw e;
+ }
+
+ // Generate the added routes
+ for (Ip4Prefix prefix : addedPrefixes) {
+ BgpRouteEntry bgpRouteEntry =
+ new BgpRouteEntry(bgpSession, prefix, nextHop,
+ origin.byteValue(), asPath, localPref);
+ bgpRouteEntry.setMultiExitDisc(multiExitDisc);
+ if (bgpRouteEntry.hasAsPathLoop(bgpSession.getLocalAs())) {
+ log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
+ "nextHop {}: contains AS Path loop",
+ bgpSession.getRemoteAddress(), prefix, nextHop);
+ continue;
+ } else {
+ log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
+ bgpSession.getRemoteAddress(), prefix, nextHop);
+ }
+ addedRoutes.put(prefix, bgpRouteEntry);
+ }
+
+ return addedRoutes.values();
+ }
+
+ /**
+ * Verifies BGP UPDATE Well-known Attributes.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param origin the ORIGIN well-known mandatory attribute
+ * @param asPath the AS_PATH well-known mandatory attribute
+ * @param nextHop the NEXT_HOP well-known mandatory attribute
+ * @param localPref the LOCAL_PREF required attribute
+ * @throws BgpParseException
+ */
+ private static void verifyBgpUpdateWellKnownAttributes(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ Short origin,
+ BgpRouteEntry.AsPath asPath,
+ Ip4Address nextHop,
+ Long localPref)
+ throws BgpParseException {
+ //
+ // Check for Missing Well-known Attributes
+ //
+ if ((origin == null) || (origin == -1)) {
+ // Missing Attribute Type Code ORIGIN
+ int type = BgpConstants.Update.Origin.TYPE;
+ actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
+ String errorMsg = "Missing Well-known Attribute: ORIGIN";
+ throw new BgpParseException(errorMsg);
+ }
+ if (asPath == null) {
+ // Missing Attribute Type Code AS_PATH
+ int type = BgpConstants.Update.AsPath.TYPE;
+ actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
+ String errorMsg = "Missing Well-known Attribute: AS_PATH";
+ throw new BgpParseException(errorMsg);
+ }
+ if (nextHop == null) {
+ // Missing Attribute Type Code NEXT_HOP
+ int type = BgpConstants.Update.NextHop.TYPE;
+ actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
+ String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
+ throw new BgpParseException(errorMsg);
+ }
+ if (localPref == null) {
+ // Missing Attribute Type Code LOCAL_PREF
+ // NOTE: Required for iBGP
+ int type = BgpConstants.Update.LocalPref.TYPE;
+ actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
+ String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
+ throw new BgpParseException(errorMsg);
+ }
+ }
+
+ /**
+ * Verifies the BGP UPDATE Attribute Flags.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @throws BgpParseException
+ */
+ private static void verifyBgpUpdateAttributeFlags(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ //
+ // Assign the Attribute Type Name and the Well-known flag
+ //
+ String typeName = "UNKNOWN";
+ boolean isWellKnown = false;
+ switch (attrTypeCode) {
+ case BgpConstants.Update.Origin.TYPE:
+ isWellKnown = true;
+ typeName = "ORIGIN";
+ break;
+ case BgpConstants.Update.AsPath.TYPE:
+ isWellKnown = true;
+ typeName = "AS_PATH";
+ break;
+ case BgpConstants.Update.NextHop.TYPE:
+ isWellKnown = true;
+ typeName = "NEXT_HOP";
+ break;
+ case BgpConstants.Update.MultiExitDisc.TYPE:
+ isWellKnown = false;
+ typeName = "MULTI_EXIT_DISC";
+ break;
+ case BgpConstants.Update.LocalPref.TYPE:
+ isWellKnown = true;
+ typeName = "LOCAL_PREF";
+ break;
+ case BgpConstants.Update.AtomicAggregate.TYPE:
+ isWellKnown = true;
+ typeName = "ATOMIC_AGGREGATE";
+ break;
+ case BgpConstants.Update.Aggregator.TYPE:
+ isWellKnown = false;
+ typeName = "AGGREGATOR";
+ break;
+ default:
+ isWellKnown = false;
+ typeName = "UNKNOWN(" + attrTypeCode + ")";
+ break;
+ }
+
+ //
+ // Verify the Attribute Flags
+ //
+ boolean optionalBit = ((0x80 & attrFlags) != 0);
+ boolean transitiveBit = ((0x40 & attrFlags) != 0);
+ boolean partialBit = ((0x20 & attrFlags) != 0);
+ if ((isWellKnown && optionalBit) ||
+ (isWellKnown && (!transitiveBit)) ||
+ (isWellKnown && partialBit) ||
+ (optionalBit && (!transitiveBit) && partialBit)) {
+ //
+ // ERROR: The Optional bit cannot be set for Well-known attributes
+ // ERROR: The Transtive bit MUST be 1 for well-known attributes
+ // ERROR: The Partial bit MUST be 0 for well-known attributes
+ // ERROR: The Partial bit MUST be 0 for optional non-transitive
+ // attributes
+ //
+ actionsBgpUpdateAttributeFlagsError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Flags Error for " + typeName + ": " +
+ attrFlags;
+ throw new BgpParseException(errorMsg);
+ }
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type ORIGIN.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed ORIGIN value
+ * @throws BgpParseException
+ */
+ private static short parseAttributeTypeOrigin(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.Origin.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ message.markReaderIndex();
+ short origin = message.readUnsignedByte();
+ switch (origin) {
+ case BgpConstants.Update.Origin.IGP:
+ // FALLTHROUGH
+ case BgpConstants.Update.Origin.EGP:
+ // FALLTHROUGH
+ case BgpConstants.Update.Origin.INCOMPLETE:
+ break;
+ default:
+ // ERROR: Invalid ORIGIN Attribute
+ message.resetReaderIndex();
+ actionsBgpUpdateInvalidOriginAttribute(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
+ origin);
+ String errorMsg = "Invalid ORIGIN Attribute: " + origin;
+ throw new BgpParseException(errorMsg);
+ }
+
+ return origin;
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute AS Path.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed AS Path
+ * @throws BgpParseException
+ */
+ private static BgpRouteEntry.AsPath parseAttributeTypeAsPath(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+ ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
+
+ //
+ // Parse the message
+ //
+ while (attrLen > 0) {
+ if (attrLen < 2) {
+ // ERROR: Malformed AS_PATH
+ actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
+ String errorMsg = "Malformed AS Path";
+ throw new BgpParseException(errorMsg);
+ }
+ // Get the Path Segment Type and Length (in number of ASes)
+ short pathSegmentType = message.readUnsignedByte();
+ short pathSegmentLength = message.readUnsignedByte();
+ attrLen -= 2;
+
+ // Verify the Path Segment Type
+ switch (pathSegmentType) {
+ case BgpConstants.Update.AsPath.AS_SET:
+ // FALLTHROUGH
+ case BgpConstants.Update.AsPath.AS_SEQUENCE:
+ // FALLTHROUGH
+ case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
+ // FALLTHROUGH
+ case BgpConstants.Update.AsPath.AS_CONFED_SET:
+ break;
+ default:
+ // ERROR: Invalid Path Segment Type
+ //
+ // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
+ // for "Invalid Path Segment Type", hence we return
+ // the error as "Malformed AS_PATH".
+ //
+ actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
+ String errorMsg =
+ "Invalid AS Path Segment Type: " + pathSegmentType;
+ throw new BgpParseException(errorMsg);
+ }
+
+ // Parse the AS numbers
+ if (2 * pathSegmentLength > attrLen) {
+ // ERROR: Malformed AS_PATH
+ actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
+ String errorMsg = "Malformed AS Path";
+ throw new BgpParseException(errorMsg);
+ }
+ attrLen -= (2 * pathSegmentLength);
+ ArrayList<Long> segmentAsNumbers = new ArrayList<>();
+ while (pathSegmentLength-- > 0) {
+ long asNumber = message.readUnsignedShort();
+ segmentAsNumbers.add(asNumber);
+ }
+
+ BgpRouteEntry.PathSegment pathSegment =
+ new BgpRouteEntry.PathSegment((byte) pathSegmentType,
+ segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ }
+
+ return new BgpRouteEntry.AsPath(pathSegments);
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type NEXT_HOP.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed NEXT_HOP value
+ * @throws BgpParseException
+ */
+ private static Ip4Address parseAttributeTypeNextHop(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ message.markReaderIndex();
+ Ip4Address nextHopAddress =
+ Ip4Address.valueOf((int) message.readUnsignedInt());
+ //
+ // Check whether the NEXT_HOP IP address is semantically correct.
+ // As per RFC 4271, Section 6.3:
+ //
+ // a) It MUST NOT be the IP address of the receiving speaker
+ // b) In the case of an EBGP ....
+ //
+ // Here we check only (a), because (b) doesn't apply for us: all our
+ // peers are iBGP.
+ //
+ if (nextHopAddress.equals(bgpSession.getLocalIp4Address())) {
+ // ERROR: Invalid NEXT_HOP Attribute
+ message.resetReaderIndex();
+ actionsBgpUpdateInvalidNextHopAttribute(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
+ nextHopAddress);
+ String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
+ throw new BgpParseException(errorMsg);
+ }
+
+ return nextHopAddress;
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed MULTI_EXIT_DISC value
+ * @throws BgpParseException
+ */
+ private static long parseAttributeTypeMultiExitDisc(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ long multiExitDisc = message.readUnsignedInt();
+ return multiExitDisc;
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type LOCAL_PREF.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed LOCAL_PREF value
+ * @throws BgpParseException
+ */
+ private static long parseAttributeTypeLocalPref(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ long localPref = message.readUnsignedInt();
+ return localPref;
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @throws BgpParseException
+ */
+ private static void parseAttributeTypeAtomicAggregate(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ // Nothing to do: this attribute is primarily informational
+ }
+
+ /**
+ * Parses BGP UPDATE Attribute Type AGGREGATOR.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message to parse
+ * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
+ * @throws BgpParseException
+ */
+ private static Pair<Long, Ip4Address> parseAttributeTypeAggregator(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message)
+ throws BgpParseException {
+
+ // Check the Attribute Length
+ if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
+ // ERROR: Attribute Length Error
+ actionsBgpUpdateAttributeLengthError(
+ bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
+ String errorMsg = "Attribute Length Error";
+ throw new BgpParseException(errorMsg);
+ }
+
+ // The AGGREGATOR AS number
+ long aggregatorAsNumber = message.readUnsignedShort();
+ // The AGGREGATOR IP address
+ Ip4Address aggregatorIpAddress =
+ Ip4Address.valueOf((int) message.readUnsignedInt());
+
+ Pair<Long, Ip4Address> aggregator = Pair.of(aggregatorAsNumber,
+ aggregatorIpAddress);
+ return aggregator;
+ }
+
+ /**
+ * Parses a message that contains encoded IPv4 network prefixes.
+ * <p>
+ * The IPv4 prefixes are encoded in the form:
+ * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
+ * and Prefix is the IPv4 prefix (padded with trailing bits to the end
+ * of an octet).
+ *
+ * @param totalLength the total length of the data to parse
+ * @param message the message with data to parse
+ * @return a collection of parsed IPv4 network prefixes
+ * @throws BgpParseException
+ */
+ private static Collection<Ip4Prefix> parsePackedPrefixes(
+ int totalLength,
+ ChannelBuffer message)
+ throws BgpParseException {
+ Collection<Ip4Prefix> result = new ArrayList<>();
+
+ if (totalLength == 0) {
+ return result;
+ }
+
+ // Parse the data
+ int dataEnd = message.readerIndex() + totalLength;
+ while (message.readerIndex() < dataEnd) {
+ int prefixBitlen = message.readUnsignedByte();
+ int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
+ if (message.readerIndex() + prefixBytelen > dataEnd) {
+ String errorMsg = "Malformed Network Prefixes";
+ throw new BgpParseException(errorMsg);
+ }
+
+ long address = 0;
+ long extraShift = (4 - prefixBytelen) * 8;
+ while (prefixBytelen > 0) {
+ address <<= 8;
+ address |= message.readUnsignedByte();
+ prefixBytelen--;
+ }
+ address <<= extraShift;
+ Ip4Prefix prefix =
+ Ip4Prefix.valueOf(Ip4Address.valueOf((int) address),
+ prefixBitlen);
+ result.add(prefix);
+ }
+
+ return result;
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Invalid Network Field Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ */
+ private static void actionsBgpUpdateInvalidNetworkField(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx) {
+ log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Invalid Network Field
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Malformed Attribute List Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ */
+ private static void actionsBgpUpdateMalformedAttributeList(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx) {
+ log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Malformed Attribute List
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Missing Well-known Attribute Error: send NOTIFICATION and close the
+ * channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param missingAttrTypeCode the missing attribute type code
+ */
+ private static void actionsBgpUpdateMissingWellKnownAttribute(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int missingAttrTypeCode) {
+ log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
+ bgpSession.getRemoteAddress(), missingAttrTypeCode);
+
+ //
+ // ERROR: Missing Well-known Attribute
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
+ ChannelBuffer data = ChannelBuffers.buffer(1);
+ data.writeByte(missingAttrTypeCode);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ * @param origin the ORIGIN attribute value
+ */
+ private static void actionsBgpUpdateInvalidOriginAttribute(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message,
+ short origin) {
+ log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Invalid ORIGIN Attribute
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
+ ChannelBuffer data =
+ prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
+ attrFlags, message);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Attribute Flags Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ */
+ private static void actionsBgpUpdateAttributeFlagsError(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message) {
+ log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Attribute Flags Error
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
+ ChannelBuffer data =
+ prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
+ attrFlags, message);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
+ * channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ * @param nextHop the NEXT_HOP attribute value
+ */
+ private static void actionsBgpUpdateInvalidNextHopAttribute(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message,
+ Ip4Address nextHop) {
+ log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
+ bgpSession.getRemoteAddress(), nextHop);
+
+ //
+ // ERROR: Invalid ORIGIN Attribute
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
+ ChannelBuffer data =
+ prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
+ attrFlags, message);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
+ * the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ */
+ private static void actionsBgpUpdateUnrecognizedWellKnownAttribute(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message) {
+ log.debug("BGP RX UPDATE Error from {}: " +
+ "Unrecognized Well-known Attribute Error: {}",
+ bgpSession.getRemoteAddress(), attrTypeCode);
+
+ //
+ // ERROR: Unrecognized Well-known Attribute
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode =
+ UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
+ ChannelBuffer data =
+ prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
+ attrFlags, message);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Attribute Length Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ */
+ private static void actionsBgpUpdateAttributeLengthError(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx,
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message) {
+ log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Attribute Length Error
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
+ ChannelBuffer data =
+ prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
+ attrFlags, message);
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ data);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Applies the appropriate actions after detecting BGP UPDATE
+ * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
+ *
+ * @param bgpSession the BGP Session to use
+ * @param ctx the Channel Handler Context
+ */
+ private static void actionsBgpUpdateMalformedAsPath(
+ BgpSession bgpSession,
+ ChannelHandlerContext ctx) {
+ log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
+ bgpSession.getRemoteAddress());
+
+ //
+ // ERROR: Malformed AS_PATH
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = UpdateMessageError.ERROR_CODE;
+ int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ bgpSession.closeSession(ctx);
+ }
+
+ /**
+ * Prepares BGP UPDATE Notification data payload.
+ *
+ * @param attrTypeCode the attribute type code
+ * @param attrLen the attribute length (in octets)
+ * @param attrFlags the attribute flags
+ * @param message the message with the data
+ * @return the buffer with the data payload for the BGP UPDATE Notification
+ */
+ private static ChannelBuffer prepareBgpUpdateNotificationDataPayload(
+ int attrTypeCode,
+ int attrLen,
+ int attrFlags,
+ ChannelBuffer message) {
+ // Compute the attribute length field octets
+ boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
+ int attrLenOctets = 1;
+ if (extendedLengthBit) {
+ attrLenOctets = 2;
+ }
+ ChannelBuffer data =
+ ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
+ data.writeByte(attrTypeCode);
+ if (extendedLengthBit) {
+ data.writeShort(attrLen);
+ } else {
+ data.writeByte(attrLen);
+ }
+ 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/BgpRoutesListCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
index 43673c3..d7b7cc7 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
@@ -81,7 +81,7 @@
// Print the routes
if (foundBgpSession != null) {
- printRoutes(foundBgpSession.getBgpRibIn());
+ printRoutes(foundBgpSession.bgpRibIn().values());
} else {
printRoutes(service.getBgpRoutes());
}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
index 5199f20..0eaaa22 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
@@ -303,7 +303,7 @@
private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
long expectedRoutes)
throws InterruptedException {
- Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
+ Collection<BgpRouteEntry> bgpRibIn = bgpSession.bgpRibIn().values();
final int maxChecks = 500; // Max wait of 5 seconds
for (int i = 0; i < maxChecks; i++) {
@@ -311,7 +311,7 @@
break;
}
Thread.sleep(10);
- bgpRibIn = bgpSession.getBgpRibIn();
+ bgpRibIn = bgpSession.bgpRibIn().values();
}
return bgpRibIn;