aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java')
-rw-r--r--framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java473
1 files changed, 473 insertions, 0 deletions
diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
new file mode 100644
index 00000000..dedf6ac3
--- /dev/null
+++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing.bgp;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.Timer;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Prefix;
+import org.onlab.packet.IpPrefix;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class for handling the BGP peer sessions.
+ * There is one instance per each BGP peer session.
+ */
+public class BgpSession extends SimpleChannelHandler {
+ private static final Logger log =
+ LoggerFactory.getLogger(BgpSession.class);
+
+ private final BgpSessionManager bgpSessionManager;
+
+ // Local flag to indicate the session is closed.
+ // It is used to avoid the Netty's asynchronous closing of a channel.
+ private boolean isClosed = false;
+
+ // BGP session info: local and remote
+ private final BgpSessionInfo localInfo; // BGP session local info
+ private final BgpSessionInfo remoteInfo; // BGP session remote info
+
+ // Timers state
+ private Timer timer = new HashedWheelTimer();
+ private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
+ private volatile Timeout sessionTimeout; // Session timeout
+
+ // BGP RIB-IN routing entries from this peer
+ private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRibIn4 =
+ new ConcurrentHashMap<>();
+ private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRibIn6 =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Constructor for a given BGP Session Manager.
+ *
+ * @param bgpSessionManager the BGP Session Manager to use
+ */
+ BgpSession(BgpSessionManager bgpSessionManager) {
+ this.bgpSessionManager = bgpSessionManager;
+ this.localInfo = new BgpSessionInfo();
+ this.remoteInfo = new BgpSessionInfo();
+
+ // NOTE: We support only BGP4
+ this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION);
+ }
+
+ /**
+ * Gets the BGP Session Manager.
+ *
+ * @return the BGP Session Manager
+ */
+ BgpSessionManager getBgpSessionManager() {
+ return bgpSessionManager;
+ }
+
+ /**
+ * Gets the BGP Session local information.
+ *
+ * @return the BGP Session local information.
+ */
+ public BgpSessionInfo localInfo() {
+ return localInfo;
+ }
+
+ /**
+ * Gets the BGP Session remote information.
+ *
+ * @return the BGP Session remote information.
+ */
+ public BgpSessionInfo remoteInfo() {
+ return remoteInfo;
+ }
+
+ /**
+ * Gets the BGP Multiprotocol Extensions for the session.
+ *
+ * @return true if the BGP Multiprotocol Extensions are enabled for the
+ * session, otherwise false
+ */
+ public boolean mpExtensions() {
+ return remoteInfo.mpExtensions() && localInfo.mpExtensions();
+ }
+
+ /**
+ * Gets the BGP session 4 octet AS path capability.
+ *
+ * @return true when the BGP session is 4 octet AS path capable
+ */
+ public boolean isAs4OctetCapable() {
+ return remoteInfo.as4OctetCapability() &&
+ localInfo.as4OctetCapability();
+ }
+
+ /**
+ * Gets the IPv4 BGP RIB-IN routing entries.
+ *
+ * @return the IPv4 BGP RIB-IN routing entries
+ */
+ public Collection<BgpRouteEntry> getBgpRibIn4() {
+ return bgpRibIn4.values();
+ }
+
+ /**
+ * Gets the IPv6 BGP RIB-IN routing entries.
+ *
+ * @return the IPv6 BGP RIB-IN routing entries
+ */
+ public Collection<BgpRouteEntry> getBgpRibIn6() {
+ return bgpRibIn6.values();
+ }
+
+ /**
+ * Finds an IPv4 BGP routing entry for a prefix in the IPv4 BGP RIB-IN.
+ *
+ * @param prefix the IPv4 prefix of the route to search for
+ * @return the IPv4 BGP routing entry if found, otherwise null
+ */
+ public BgpRouteEntry findBgpRoute(Ip4Prefix prefix) {
+ return bgpRibIn4.get(prefix);
+ }
+
+ /**
+ * Finds an IPv6 BGP routing entry for a prefix in the IPv6 BGP RIB-IN.
+ *
+ * @param prefix the IPv6 prefix of the route to search for
+ * @return the IPv6 BGP routing entry if found, otherwise null
+ */
+ public BgpRouteEntry findBgpRoute(Ip6Prefix prefix) {
+ return bgpRibIn6.get(prefix);
+ }
+
+ /**
+ * Finds a BGP routing entry for a prefix in the BGP RIB-IN. The prefix
+ * can be either IPv4 or IPv6.
+ *
+ * @param prefix the IP prefix of the route to search for
+ * @return the BGP routing entry if found, otherwise null
+ */
+ public BgpRouteEntry findBgpRoute(IpPrefix prefix) {
+ if (prefix.isIp4()) {
+ // IPv4 prefix
+ Ip4Prefix ip4Prefix = prefix.getIp4Prefix();
+ return bgpRibIn4.get(ip4Prefix);
+ }
+
+ // IPv6 prefix
+ Ip6Prefix ip6Prefix = prefix.getIp6Prefix();
+ return bgpRibIn6.get(ip6Prefix);
+ }
+
+ /**
+ * Adds a BGP route. The route can be either IPv4 or IPv6.
+ *
+ * @param bgpRouteEntry the BGP route entry to use
+ */
+ void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
+ if (bgpRouteEntry.isIp4()) {
+ // IPv4 route
+ Ip4Prefix ip4Prefix = bgpRouteEntry.prefix().getIp4Prefix();
+ bgpRibIn4.put(ip4Prefix, bgpRouteEntry);
+ } else {
+ // IPv6 route
+ Ip6Prefix ip6Prefix = bgpRouteEntry.prefix().getIp6Prefix();
+ bgpRibIn6.put(ip6Prefix, bgpRouteEntry);
+ }
+ }
+
+ /**
+ * Removes an IPv4 BGP route for a prefix.
+ *
+ * @param prefix the prefix to use
+ * @return true if the route was found and removed, otherwise false
+ */
+ boolean removeBgpRoute(Ip4Prefix prefix) {
+ return (bgpRibIn4.remove(prefix) != null);
+ }
+
+ /**
+ * Removes an IPv6 BGP route for a prefix.
+ *
+ * @param prefix the prefix to use
+ * @return true if the route was found and removed, otherwise false
+ */
+ boolean removeBgpRoute(Ip6Prefix prefix) {
+ return (bgpRibIn6.remove(prefix) != null);
+ }
+
+ /**
+ * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
+ *
+ * @param prefix the prefix to use
+ * @return true if the route was found and removed, otherwise false
+ */
+ boolean removeBgpRoute(IpPrefix prefix) {
+ if (prefix.isIp4()) {
+ return (bgpRibIn4.remove(prefix.getIp4Prefix()) != null); // IPv4
+ }
+ return (bgpRibIn6.remove(prefix.getIp6Prefix()) != null); // IPv6
+ }
+
+ /**
+ * Tests whether the session is closed.
+ * <p>
+ * NOTE: We use this method to avoid the Netty's asynchronous closing
+ * of a channel.
+ * </p>
+ * @return true if the session is closed
+ */
+ boolean isClosed() {
+ return isClosed;
+ }
+
+ /**
+ * Closes the session.
+ *
+ * @param ctx the Channel Handler Context
+ */
+ void closeSession(ChannelHandlerContext ctx) {
+ timer.stop();
+ closeChannel(ctx);
+ }
+
+ /**
+ * Closes the Netty channel.
+ *
+ * @param ctx the Channel Handler Context
+ */
+ void closeChannel(ChannelHandlerContext ctx) {
+ isClosed = true;
+ ctx.getChannel().close();
+ }
+
+ @Override
+ public void channelOpen(ChannelHandlerContext ctx,
+ ChannelStateEvent channelEvent) {
+ bgpSessionManager.addSessionChannel(channelEvent.getChannel());
+ }
+
+ @Override
+ public void channelClosed(ChannelHandlerContext ctx,
+ ChannelStateEvent channelEvent) {
+ bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
+ }
+
+ @Override
+ public void channelConnected(ChannelHandlerContext ctx,
+ ChannelStateEvent channelEvent) {
+ localInfo.setAddress(ctx.getChannel().getLocalAddress());
+ remoteInfo.setAddress(ctx.getChannel().getRemoteAddress());
+
+ // Assign the local and remote IPv4 addresses
+ InetAddress inetAddr;
+ if (localInfo.address() instanceof InetSocketAddress) {
+ inetAddr = ((InetSocketAddress) localInfo.address()).getAddress();
+ localInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
+ }
+ if (remoteInfo.address() instanceof InetSocketAddress) {
+ inetAddr = ((InetSocketAddress) remoteInfo.address()).getAddress();
+ remoteInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
+ }
+
+ log.debug("BGP Session Connected from {} on {}",
+ remoteInfo.address(), localInfo.address());
+ if (!bgpSessionManager.peerConnected(this)) {
+ log.debug("Cannot setup BGP Session Connection from {}. Closing...",
+ remoteInfo.address());
+ ctx.getChannel().close();
+ }
+
+ //
+ // Assign the local BGP ID
+ // NOTE: This should be configuration-based
+ //
+ localInfo.setBgpId(bgpSessionManager.getMyBgpId());
+ }
+
+ @Override
+ public void channelDisconnected(ChannelHandlerContext ctx,
+ ChannelStateEvent channelEvent) {
+ log.debug("BGP Session Disconnected from {} on {}",
+ ctx.getChannel().getRemoteAddress(),
+ ctx.getChannel().getLocalAddress());
+ processChannelDisconnected();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
+ log.debug("BGP Session Exception Caught from {} on {}: {}",
+ ctx.getChannel().getRemoteAddress(),
+ ctx.getChannel().getLocalAddress(),
+ e);
+ processChannelDisconnected();
+ }
+
+ /**
+ * Processes the channel being disconnected.
+ */
+ private void processChannelDisconnected() {
+ //
+ // Withdraw the routes advertised by this BGP peer
+ //
+ // NOTE: We must initialize the RIB-IN before propagating the withdraws
+ // for further processing. Otherwise, the BGP Decision Process
+ // will use those routes again.
+ //
+ Collection<BgpRouteEntry> deletedRoutes4 = bgpRibIn4.values();
+ Collection<BgpRouteEntry> deletedRoutes6 = bgpRibIn6.values();
+ bgpRibIn4 = new ConcurrentHashMap<>();
+ bgpRibIn6 = new ConcurrentHashMap<>();
+
+ // Push the updates to the BGP Merged RIB
+ BgpRouteSelector bgpRouteSelector =
+ bgpSessionManager.getBgpRouteSelector();
+ Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
+ bgpRouteSelector.routeUpdates(this, addedRoutes, deletedRoutes4);
+ bgpRouteSelector.routeUpdates(this, addedRoutes, deletedRoutes6);
+
+ bgpSessionManager.peerDisconnected(this);
+ }
+
+ /**
+ * Restarts the BGP KeepaliveTimer.
+ *
+ * @param ctx the Channel Handler Context to use
+ */
+ void restartKeepaliveTimer(ChannelHandlerContext ctx) {
+ long localKeepaliveInterval = 0;
+
+ //
+ // Compute the local Keepalive interval
+ //
+ if (localInfo.holdtime() != 0) {
+ localKeepaliveInterval = Math.max(localInfo.holdtime() /
+ BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
+ BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
+ }
+
+ // Restart the Keepalive timer
+ if (localKeepaliveInterval == 0) {
+ return; // Nothing to do
+ }
+ keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
+ localKeepaliveInterval,
+ TimeUnit.SECONDS);
+ }
+
+ /**
+ * Task class for transmitting KEEPALIVE messages.
+ */
+ private final class TransmitKeepaliveTask implements TimerTask {
+ private final ChannelHandlerContext ctx;
+
+ /**
+ * Constructor for given Channel Handler Context.
+ *
+ * @param ctx the Channel Handler Context to use
+ */
+ TransmitKeepaliveTask(ChannelHandlerContext ctx) {
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ if (timeout.isCancelled()) {
+ return;
+ }
+ if (!ctx.getChannel().isOpen()) {
+ return;
+ }
+
+ // Transmit the KEEPALIVE
+ ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
+ ctx.getChannel().write(txMessage);
+
+ // Restart the KEEPALIVE timer
+ restartKeepaliveTimer(ctx);
+ }
+ }
+
+ /**
+ * Restarts the BGP Session Timeout Timer.
+ *
+ * @param ctx the Channel Handler Context to use
+ */
+ void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
+ if (remoteInfo.holdtime() == 0) {
+ return; // Nothing to do
+ }
+ if (sessionTimeout != null) {
+ sessionTimeout.cancel();
+ }
+ sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
+ remoteInfo.holdtime(),
+ TimeUnit.SECONDS);
+ }
+
+ /**
+ * Task class for BGP Session timeout.
+ */
+ private final class SessionTimeoutTask implements TimerTask {
+ private final ChannelHandlerContext ctx;
+
+ /**
+ * Constructor for given Channel Handler Context.
+ *
+ * @param ctx the Channel Handler Context to use
+ */
+ SessionTimeoutTask(ChannelHandlerContext ctx) {
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ if (timeout.isCancelled()) {
+ return;
+ }
+ if (!ctx.getChannel().isOpen()) {
+ return;
+ }
+
+ log.debug("BGP Session Timeout: peer {}", remoteInfo.address());
+ //
+ // ERROR: Invalid Optional Parameter Length field: Unspecific
+ //
+ // Send NOTIFICATION and close the connection
+ int errorCode = BgpConstants.Notifications.HoldTimerExpired.ERROR_CODE;
+ int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
+ ChannelBuffer txMessage =
+ BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
+ null);
+ ctx.getChannel().write(txMessage);
+ closeChannel(ctx);
+ }
+ }
+}