2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.openflow.md.core;
10 import java.util.List;
11 import java.util.concurrent.Future;
12 import java.util.concurrent.TimeUnit;
14 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
20 import org.opendaylight.yangtools.yang.common.RpcResult;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
28 public class HandshakeManagerImpl implements HandshakeManager {
30 private static final Logger LOG = LoggerFactory
31 .getLogger(HandshakeManagerImpl.class);
33 private Short lastProposedVersion;
34 private Short lastReceivedVersion;
35 private final List<Short> versionOrder;
37 private HelloMessage receivedHello;
38 private final ConnectionAdapter connectionAdapter;
39 private GetFeaturesOutput features;
40 private Short version;
41 private ErrorHandler errorHandler;
43 private long maxTimeout = 1000;
44 private TimeUnit maxTimeoutUnit = TimeUnit.MILLISECONDS;
45 private Short highestVersion;
47 private Long activeXid;
49 private HandshakeListener handshakeListener;
51 private boolean useVersionBitmap;
54 public void setReceivedHello(HelloMessage receivedHello) {
55 this.receivedHello = receivedHello;
59 * @param connectionAdapter
60 * @param highestVersion
63 public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion,
64 List<Short> versionOrder) {
65 this.highestVersion = highestVersion;
66 this.versionOrder = versionOrder;
67 this.connectionAdapter = connectionAdapter;
71 public void setHandshakeListener(HandshakeListener handshakeListener) {
72 this.handshakeListener = handshakeListener;
76 public synchronized void run() {
77 LOG.info("handshake STARTED");
79 HelloMessage receivedHelloLoc = receivedHello;
81 if (receivedHelloLoc == null) {
82 // first Hello sending
83 sendHelloMessage(highestVersion, getNextXid());
84 lastProposedVersion = highestVersion;
85 LOG.debug("ret - firstHello+wait");
89 // process the 2. and later hellos
90 Short remoteVersion = receivedHelloLoc.getVersion();
91 List<Elements> elements = receivedHelloLoc.getElements();
92 setActiveXid(receivedHelloLoc.getXid());
93 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
94 LOG.debug("Hello message: version={}, bitmap={}, xid={}", remoteVersion,
95 remoteVersionBitmap, receivedHelloLoc.getXid());
98 if (useVersionBitmap && remoteVersionBitmap != null) {
99 // versionBitmap on both sides -> ONE STEP DECISION
100 handleVersionBitmapNegotiation(elements);
102 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
103 handleStepByStepVersionNegotiation(remoteVersion);
105 } catch (Exception ex) {
106 errorHandler.handleException(ex, null);
107 connectionAdapter.disconnect();
108 LOG.debug("ret - "+ex.getMessage());
113 * @param remoteVersion
115 private void handleStepByStepVersionNegotiation(Short remoteVersion) {
116 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
117 remoteVersion, lastProposedVersion, highestVersion);
118 if (remoteVersion == lastProposedVersion) {
119 postHandshake(lastProposedVersion, getNextXid());
120 LOG.debug("ret - OK - switch answered with lastProposedVersion");
122 checkNegotiationStalling(remoteVersion);
124 if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
125 // wait for next version
126 LOG.debug("ret - wait");
128 //propose lower version
129 handleLowerVersionProposal(remoteVersion);
135 * @param remoteVersion
137 private void handleLowerVersionProposal(Short remoteVersion) {
138 Short proposedVersion;
139 // find the version from header version field
140 proposedVersion = proposeNextVersion(remoteVersion);
141 lastProposedVersion = proposedVersion;
142 sendHelloMessage(proposedVersion, getNextXid());
144 if (proposedVersion != remoteVersion) {
145 LOG.debug("ret - sent+wait");
147 LOG.debug("ret - sent+OK");
148 postHandshake(proposedVersion, getNextXid());
155 private void handleVersionBitmapNegotiation(List<Elements> elements) {
156 Short proposedVersion;
157 proposedVersion = proposeCommonBitmapVersion(elements);
158 postHandshake(proposedVersion, getNextXid());
159 LOG.debug("ret - OK - versionBitmap");
166 private Long getNextXid() {
174 private void setActiveXid(Long xid) {
175 this.activeXid = xid;
179 * @param remoteVersion
181 private void checkNegotiationStalling(Short remoteVersion) {
182 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
183 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
188 public GetFeaturesOutput getFeatures() {
193 public Short getVersion() {
198 * find common highest supported bitmap version
202 protected Short proposeCommonBitmapVersion(List<Elements> list) {
203 Short supportedHighestVersion = null;
204 if((null != list) && (0 != list.size())) {
205 for(Elements element : list) {
206 List<Boolean> bitmap = element.getVersionBitmap();
207 // check for version bitmap
208 for(short bitPos : ConnectionConductor.versionOrder) {
209 // with all the version it should work.
210 if(bitmap.get(bitPos % Integer.SIZE)) {
211 supportedHighestVersion = bitPos;
217 if(null == supportedHighestVersion) {
218 throw new IllegalArgumentException("no common version found in versionBitmap");
222 return supportedHighestVersion;
226 * find supported version based on remoteVersion
227 * @param remoteVersion
230 protected short proposeNextVersion(short remoteVersion) {
231 Short proposal = null;
232 for (short offer : versionOrder) {
233 if (offer <= remoteVersion) {
238 if (proposal == null) {
239 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
246 * send hello reply without versionBitmap
247 * @param helloVersion
250 protected void sendHelloMessage(Short helloVersion, Long helloXid) {
251 //Short highestVersion = ConnectionConductor.versionOrder.get(0);
252 //final Long helloXid = 21L;
253 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
255 LOG.debug("sending first hello message: version{}, xid={}, version bitmap={}",
256 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
259 RpcResult<Void> helloResult = connectionAdapter.hello(helloInput).get(maxTimeout, maxTimeoutUnit);
260 RpcUtil.smokeRpc(helloResult);
261 LOG.debug("FIRST HELLO sent.");
262 } catch (Throwable e) {
263 LOG.debug("FIRST HELLO sending failed.");
264 errorHandler.handleException(e, null);
269 * after handshake set features, register to session
270 * @param proposedVersion
273 protected void postHandshake(Short proposedVersion, Long xid) {
275 version = proposedVersion;
276 LOG.debug("version set: " + proposedVersion);
278 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
279 featuresBuilder.setVersion(version).setXid(xid);
280 LOG.debug("sending feature request for version={} and xid={}", version, xid);
281 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
282 .getFeatures(featuresBuilder.build());
283 LOG.debug("waiting for features");
285 RpcResult<GetFeaturesOutput> rpcFeatures =
286 featuresFuture.get(maxTimeout, maxTimeoutUnit);
287 RpcUtil.smokeRpc(rpcFeatures);
289 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
291 LOG.debug("obtained features: datapathId={}",
292 featureOutput.getDatapathId());
293 LOG.debug("obtained features: auxiliaryId={}",
294 featureOutput.getAuxiliaryId());
295 LOG.info("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
296 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
298 handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
299 } catch (Throwable e) {
301 LOG.error("issuing disconnect during handshake, reason: "+e.getMessage());
302 errorHandler.handleException(e, null);
303 connectionAdapter.disconnect();
306 LOG.debug("postHandshake DONE");
310 public void setUseVersionBitmap(boolean useVersionBitmap) {
311 this.useVersionBitmap = useVersionBitmap;
315 public void setErrorHandler(ErrorHandler errorHandler) {
316 this.errorHandler = errorHandler;