Removed checkstyle warnings.
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPSessionNegotiator.java
index ba7c8736b43bc6e6420e8346b9fa9dbe8fb38509..d8e445e18f51dc10379faf213bb1985b8c794521 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.protocol.bgp.rib.impl;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
 import io.netty.channel.Channel;
 import io.netty.util.Timeout;
 import io.netty.util.Timer;
@@ -18,152 +21,170 @@ import java.util.concurrent.TimeUnit;
 
 import javax.annotation.concurrent.GuardedBy;
 
+import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
 import org.opendaylight.protocol.bgp.parser.BGPError;
-import org.opendaylight.protocol.bgp.parser.BGPMessage;
-import org.opendaylight.protocol.bgp.parser.BGPParameter;
 import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
-import org.opendaylight.protocol.bgp.parser.message.BGPKeepAliveMessage;
-import org.opendaylight.protocol.bgp.parser.message.BGPNotificationMessage;
-import org.opendaylight.protocol.bgp.parser.message.BGPOpenMessage;
-import org.opendaylight.protocol.bgp.parser.parameter.CapabilityParameter;
-import org.opendaylight.protocol.bgp.parser.parameter.MultiprotocolCapability;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
 import org.opendaylight.protocol.framework.AbstractSessionNegotiator;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpAddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpSubsequentAddressFamily;
+import org.opendaylight.protocol.util.Values;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Keepalive;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.KeepaliveBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Notify;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.NotifyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.OpenBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
+import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-public final class BGPSessionNegotiator extends AbstractSessionNegotiator<BGPMessage, BGPSessionImpl> {
-       // 4 minutes recommended in http://tools.ietf.org/html/rfc4271#section-8.2.2
-       // FIXME to actual value
-       protected static final int INITIAL_HOLDTIMER = 1;
-
-       @VisibleForTesting
-       public enum State {
-               /**
-                * Negotiation has not started yet.
-                */
-               Idle,
-               /**
-                * We have sent our Open message, and are waiting for the peer's Open message.
-                */
-               OpenSent,
-               /**
-                * We have received the peer's Open message, which is acceptable, and we're waiting the acknowledgement of our
-                * Open message.
-                */
-               OpenConfirm,
-               /**
-                * The negotiation finished.
-                */
-               Finished,
-       }
-
-       private static final Logger logger = LoggerFactory.getLogger(BGPSessionNegotiator.class);
-       private final BGPSessionListener listener;
-       private final Timer timer;
-       private final BGPSessionPreferences localPref;
-
-       @GuardedBy("this")
-       private BGPOpenMessage remotePref;
-
-       @GuardedBy("this")
-       private State state = State.Idle;
-
-       @GuardedBy("this")
-       private BGPSessionImpl session;
-
-       public BGPSessionNegotiator(final Timer timer, final Promise<BGPSessionImpl> promise, final Channel channel,
-                       final BGPSessionPreferences initialPrefs, final BGPSessionListener listener) {
-               super(promise, channel);
-               this.listener = Preconditions.checkNotNull(listener);
-               this.localPref = Preconditions.checkNotNull(initialPrefs);
-               this.timer = Preconditions.checkNotNull(timer);
-       }
-
-       @Override
-       protected void startNegotiation() {
-               Preconditions.checkState(this.state == State.Idle);
-               this.channel.writeAndFlush(new BGPOpenMessage(this.localPref.getMyAs(), (short) this.localPref.getHoldTime(), this.localPref.getBgpId(), this.localPref.getParams()));
-               this.state = State.OpenSent;
-
-               final Object lock = this;
-               this.timer.newTimeout(new TimerTask() {
-                       @Override
-                       public void run(final Timeout timeout) throws Exception {
-                               synchronized (lock) {
-                                       if (BGPSessionNegotiator.this.state != State.Finished) {
-                                               negotiationFailed(new BGPDocumentedException("HoldTimer expired", BGPError.FSM_ERROR));
-                                               BGPSessionNegotiator.this.channel.writeAndFlush(new BGPNotificationMessage(BGPError.HOLD_TIMER_EXPIRED));
-                                               BGPSessionNegotiator.this.state = State.Finished;
-                                       }
-                               }
-                       }
-               }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
-       }
-
-       @Override
-       protected synchronized void handleMessage(final BGPMessage msg) {
-               logger.debug("Channel {} handling message in state {}", this.channel, this.state);
-
-               switch (this.state) {
-               case Finished:
-               case Idle:
-                       throw new IllegalStateException("Unexpected state " + this.state);
-               case OpenConfirm:
-                       if (msg instanceof BGPKeepAliveMessage) {
-                               negotiationSuccessful(this.session);
-                       } else if (msg instanceof BGPNotificationMessage) {
-                               final BGPNotificationMessage ntf = (BGPNotificationMessage) msg;
-                               negotiationFailed(new BGPDocumentedException("Peer refusal", ntf.getError()));
-                       }
-                       this.state = State.Finished;
-                       return;
-               case OpenSent:
-                       if (msg instanceof BGPOpenMessage) {
-                               final BGPOpenMessage openObj = (BGPOpenMessage) msg;
-
-                               final List<BGPParameter> prefs = openObj.getOptParams();
-                               if (prefs != null && !prefs.isEmpty()) {
-                                       for (final BGPParameter param : openObj.getOptParams()) {
-                                               if (param instanceof CapabilityParameter) {
-                                                       if (((CapabilityParameter) param).getCode() == MultiprotocolCapability.CODE) {
-                                                               final MultiprotocolCapability cap = (MultiprotocolCapability) param;
-                                                               if (cap.getAfi() == BgpAddressFamily.Linkstate && cap.getSafi() == BgpSubsequentAddressFamily.Linkstate) {
-                                                                       this.remotePref = openObj;
-                                                                       this.channel.writeAndFlush(new BGPKeepAliveMessage());
-                                                                       this.session = new BGPSessionImpl(this.timer, this.listener, this.channel, this.remotePref);
-                                                                       this.state = State.OpenConfirm;
-                                                                       logger.debug("Channel {} moved to OpenConfirm state with remote proposal {}", this.channel,
-                                                                                       this.remotePref);
-                                                                       return;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                               final BGPNotificationMessage ntf = new BGPNotificationMessage(BGPError.UNSPECIFIC_OPEN_ERROR);
-                               this.channel.writeAndFlush(ntf);
-                               negotiationFailed(new BGPDocumentedException("Linkstate capability not advertised.", ntf.getError()));
-                               this.state = State.Finished;
-                               return;
-                       }
-                       break;
-               }
-
-               // Catch-all for unexpected message
-               logger.warn("Channel {} state {} unexpected message {}", this.channel, this.state, msg);
-               this.channel.writeAndFlush(new BGPNotificationMessage(BGPError.FSM_ERROR));
-               negotiationFailed(new BGPDocumentedException("Unexpected message", BGPError.FSM_ERROR));
-               this.state = State.Finished;
-       }
-
-       public synchronized State getState() {
-               return this.state;
-       }
+public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notification, BGPSessionImpl> {
+    // 4 minutes recommended in http://tools.ietf.org/html/rfc4271#section-8.2.2
+    protected static final int INITIAL_HOLDTIMER = 4;
+
+    /**
+     * @see <a href="http://tools.ietf.org/html/rfc6793">BGP Support for 4-Octet AS Number Space</a>
+     */
+    private static final int AS_TRANS = 23456;
+
+    @VisibleForTesting
+    public enum State {
+        /**
+         * Negotiation has not started yet.
+         */
+        Idle,
+        /**
+         * We have sent our Open message, and are waiting for the peer's Open message.
+         */
+        OpenSent,
+        /**
+         * We have received the peer's Open message, which is acceptable, and we're waiting the acknowledgement of our
+         * Open message.
+         */
+        OpenConfirm,
+        /**
+         * The negotiation finished.
+         */
+        Finished,
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(BGPSessionNegotiator.class);
+    private final BGPSessionPreferences localPref;
+    private final BGPSessionListener listener;
+    private final AsNumber remoteAs;
+    private final Timer timer;
+
+    @GuardedBy("this")
+    private State state = State.Idle;
+
+    @GuardedBy("this")
+    private BGPSessionImpl session;
+
+    public BGPSessionNegotiator(final Timer timer, final Promise<BGPSessionImpl> promise, final Channel channel,
+            final BGPSessionPreferences initialPrefs, final AsNumber remoteAs, final BGPSessionListener listener) {
+        super(promise, channel);
+        this.listener = Preconditions.checkNotNull(listener);
+        this.localPref = Preconditions.checkNotNull(initialPrefs);
+        this.remoteAs = Preconditions.checkNotNull(remoteAs);
+        this.timer = Preconditions.checkNotNull(timer);
+    }
+
+    @Override
+    protected void startNegotiation() {
+        Preconditions.checkState(this.state == State.Idle);
+        int as = this.localPref.getMyAs().getValue().intValue();
+        // Set as AS_TRANS if the value is bigger than 2B
+        if (as > Values.UNSIGNED_SHORT_MAX_VALUE) {
+            as = AS_TRANS;
+        }
+        this.sendMessage(new OpenBuilder().setMyAsNumber(as).setHoldTimer(this.localPref.getHoldTime()).setBgpIdentifier(
+                this.localPref.getBgpId()).setBgpParameters(this.localPref.getParams()).build());
+        this.state = State.OpenSent;
+
+        final Object lock = this;
+        this.timer.newTimeout(new TimerTask() {
+            @Override
+            public void run(final Timeout timeout) {
+                synchronized (lock) {
+                    if (BGPSessionNegotiator.this.state != State.Finished) {
+                        BGPSessionNegotiator.this.sendMessage(buildErrorNotify(BGPError.HOLD_TIMER_EXPIRED));
+                        negotiationFailed(new BGPDocumentedException("HoldTimer expired", BGPError.FSM_ERROR));
+                        BGPSessionNegotiator.this.state = State.Finished;
+                    }
+                }
+            }
+        }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
+    }
+
+    @Override
+    protected synchronized void handleMessage(final Notification msg) {
+        LOG.debug("Channel {} handling message in state {}", this.channel, this.state);
+
+        switch (this.state) {
+        case Finished:
+        case Idle:
+            this.sendMessage(buildErrorNotify(BGPError.FSM_ERROR));
+            return;
+        case OpenConfirm:
+            if (msg instanceof Keepalive) {
+                negotiationSuccessful(this.session);
+                LOG.info("BGP Session with peer {} established successfully.", this.channel);
+            } else if (msg instanceof Notify) {
+                final Notify ntf = (Notify) msg;
+                negotiationFailed(new BGPDocumentedException("Peer refusal", BGPError.forValue(ntf.getErrorCode(), ntf.getErrorSubcode())));
+            }
+            this.state = State.Finished;
+            return;
+        case OpenSent:
+            if (msg instanceof Open) {
+                final Open openObj = (Open) msg;
+                handleOpen(openObj);
+                return;
+            }
+            break;
+        }
+
+        // Catch-all for unexpected message
+        LOG.warn("Channel {} state {} unexpected message {}", this.channel, this.state, msg);
+        this.sendMessage(buildErrorNotify(BGPError.FSM_ERROR));
+        negotiationFailed(new BGPDocumentedException("Unexpected message", BGPError.FSM_ERROR));
+        this.state = State.Finished;
+    }
+
+    private static Notify buildErrorNotify(final BGPError err) {
+        return new NotifyBuilder().setErrorCode(err.getCode()).setErrorSubcode(err.getSubcode()).build();
+    }
+
+    private void handleOpen(final Open openObj) {
+        final AsNumber as = AsNumberUtil.advertizedAsNumber(openObj);
+        if (!this.remoteAs.equals(as)) {
+            LOG.warn("Unexpected remote AS number. Expecting {}, got {}", this.remoteAs, as);
+            this.sendMessage(buildErrorNotify(BGPError.BAD_PEER_AS));
+            negotiationFailed(new BGPDocumentedException("Peer AS number mismatch", BGPError.BAD_PEER_AS));
+            this.state = State.Finished;
+            return;
+        }
+
+        final List<BgpParameters> prefs = openObj.getBgpParameters();
+        if (prefs != null && !prefs.isEmpty()) {
+            if (!prefs.containsAll(this.localPref.getParams())) {
+                LOG.info("BGP Open message session parameters differ, session still accepted.");
+            }
+            this.sendMessage(new KeepaliveBuilder().build());
+            this.session = new BGPSessionImpl(this.timer, this.listener, this.channel, openObj, this.localPref.getHoldTime());
+            this.state = State.OpenConfirm;
+            LOG.debug("Channel {} moved to OpenConfirm state with remote proposal {}", this.channel, openObj);
+            return;
+        }
+
+        this.sendMessage(buildErrorNotify(BGPError.UNSPECIFIC_OPEN_ERROR));
+        negotiationFailed(new BGPDocumentedException("Open message unacceptable. Check the configuration of BGP speaker.", BGPError.UNSPECIFIC_OPEN_ERROR));
+        this.state = State.Finished;
+    }
+
+    public synchronized State getState() {
+        return this.state;
+    }
 }