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;
13 import java.util.concurrent.TimeoutException;
15 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
21 import org.opendaylight.yangtools.yang.common.RpcResult;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
29 public class HandshakeManagerImpl implements HandshakeManager {
31 private static final Logger LOG = LoggerFactory
32 .getLogger(HandshakeManagerImpl.class);
34 private Short lastProposedVersion;
35 private Short lastReceivedVersion;
36 private final List<Short> versionOrder;
38 private HelloMessage receivedHello;
39 private final ConnectionAdapter connectionAdapter;
40 private GetFeaturesOutput features;
41 private Short version;
42 private ErrorHandler errorHandler;
44 private long maxTimeout = 8000;
45 private TimeUnit maxTimeoutUnit = TimeUnit.MILLISECONDS;
46 private Short highestVersion;
48 private Long activeXid;
50 private HandshakeListener handshakeListener;
52 private boolean useVersionBitmap;
55 public void setReceivedHello(HelloMessage receivedHello) {
56 this.receivedHello = receivedHello;
60 * @param connectionAdapter
61 * @param highestVersion
64 public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion,
65 List<Short> versionOrder) {
66 this.highestVersion = highestVersion;
67 this.versionOrder = versionOrder;
68 this.connectionAdapter = connectionAdapter;
72 public void setHandshakeListener(HandshakeListener handshakeListener) {
73 this.handshakeListener = handshakeListener;
78 LOG.trace("handshake STARTED");
80 HelloMessage receivedHelloLoc = receivedHello;
83 if (receivedHelloLoc == null) {
84 // first Hello sending
85 sendHelloMessage(highestVersion, getNextXid());
86 lastProposedVersion = highestVersion;
87 LOG.trace("ret - firstHello+wait");
91 // process the 2. and later hellos
92 Short remoteVersion = receivedHelloLoc.getVersion();
93 List<Elements> elements = receivedHelloLoc.getElements();
94 setActiveXid(receivedHelloLoc.getXid());
95 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
96 LOG.debug("Hello message: version={}, bitmap={}, xid={}", remoteVersion,
97 remoteVersionBitmap, receivedHelloLoc.getXid());
99 if (useVersionBitmap && remoteVersionBitmap != null) {
100 // versionBitmap on both sides -> ONE STEP DECISION
101 handleVersionBitmapNegotiation(elements);
103 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
104 handleStepByStepVersionNegotiation(remoteVersion);
106 } catch (Exception ex) {
107 errorHandler.handleException(ex, null);
108 connectionAdapter.disconnect();
109 LOG.trace("ret - shake fail: {}", ex.getMessage());
114 * @param remoteVersion
117 private void handleStepByStepVersionNegotiation(Short remoteVersion) throws Exception {
118 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
119 remoteVersion, lastProposedVersion, highestVersion);
121 if (lastProposedVersion == null) {
122 // first hello has not been sent yet, send it and either wait for next remote
123 // version or proceed
124 lastProposedVersion = proposeNextVersion(remoteVersion);
125 sendHelloMessage(lastProposedVersion, getNextXid());
128 if (remoteVersion == lastProposedVersion) {
129 postHandshake(lastProposedVersion, getNextXid());
130 LOG.trace("ret - OK - switch answered with lastProposedVersion");
132 checkNegotiationStalling(remoteVersion);
134 if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
135 // wait for next version
136 LOG.trace("ret - wait");
138 //propose lower version
139 handleLowerVersionProposal(remoteVersion);
145 * @param remoteVersion
148 private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
149 Short proposedVersion;
150 // find the version from header version field
151 proposedVersion = proposeNextVersion(remoteVersion);
152 lastProposedVersion = proposedVersion;
153 sendHelloMessage(proposedVersion, getNextXid());
155 if (proposedVersion != remoteVersion) {
156 LOG.trace("ret - sent+wait");
158 LOG.trace("ret - sent+OK");
159 postHandshake(proposedVersion, getNextXid());
167 private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
168 Short proposedVersion;
169 proposedVersion = proposeCommonBitmapVersion(elements);
170 if (lastProposedVersion == null) {
171 // first hello has not been sent yet
172 sendHelloMessage(proposedVersion, getNextXid());
174 postHandshake(proposedVersion, getNextXid());
175 LOG.trace("ret - OK - versionBitmap");
182 private Long getNextXid() {
190 private void setActiveXid(Long xid) {
191 this.activeXid = xid;
195 * @param remoteVersion
197 private void checkNegotiationStalling(Short remoteVersion) {
198 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
199 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
201 lastReceivedVersion = remoteVersion;
205 public GetFeaturesOutput getFeatures() {
210 public Short getVersion() {
215 * find common highest supported bitmap version
219 protected Short proposeCommonBitmapVersion(List<Elements> list) {
220 Short supportedHighestVersion = null;
221 if((null != list) && (0 != list.size())) {
222 for(Elements element : list) {
223 List<Boolean> bitmap = element.getVersionBitmap();
224 // check for version bitmap
225 for(short bitPos : ConnectionConductor.versionOrder) {
226 // with all the version it should work.
227 if(bitmap.get(bitPos % Integer.SIZE)) {
228 supportedHighestVersion = bitPos;
234 if(null == supportedHighestVersion) {
235 throw new IllegalArgumentException("no common version found in versionBitmap");
239 return supportedHighestVersion;
243 * find supported version based on remoteVersion
244 * @param remoteVersion
247 protected short proposeNextVersion(short remoteVersion) {
248 Short proposal = null;
249 for (short offer : versionOrder) {
250 if (offer <= remoteVersion) {
255 if (proposal == null) {
256 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
263 * send hello reply without versionBitmap
264 * @param helloVersion
268 private void sendHelloMessage(Short helloVersion, Long helloXid) throws Exception {
269 //Short highestVersion = ConnectionConductor.versionOrder.get(0);
270 //final Long helloXid = 21L;
271 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
273 LOG.debug("sending hello message: version{}, xid={}, version bitmap={}",
274 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
277 RpcResult<Void> helloResult = connectionAdapter.hello(helloInput).get(maxTimeout, maxTimeoutUnit);
278 RpcUtil.smokeRpc(helloResult);
279 LOG.debug("FIRST HELLO sent.");
280 } catch (Exception e) {
281 LOG.debug("FIRST HELLO sending failed.");
288 * after handshake set features, register to session
289 * @param proposedVersion
293 protected void postHandshake(Short proposedVersion, Long xid) throws Exception {
295 version = proposedVersion;
297 LOG.debug("version set: {}", proposedVersion);
299 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
300 featuresBuilder.setVersion(version).setXid(xid);
301 LOG.debug("sending feature request for version={} and xid={}", version, xid);
302 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
303 .getFeatures(featuresBuilder.build());
304 LOG.debug("waiting for features");
306 RpcResult<GetFeaturesOutput> rpcFeatures =
307 featuresFuture.get(maxTimeout, maxTimeoutUnit);
308 RpcUtil.smokeRpc(rpcFeatures);
310 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
312 LOG.debug("obtained features: datapathId={}",
313 featureOutput.getDatapathId());
314 LOG.debug("obtained features: auxiliaryId={}",
315 featureOutput.getAuxiliaryId());
316 LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
317 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
319 handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
320 } catch (TimeoutException e) {
322 LOG.warn("issuing disconnect during handshake, reason: future expired", e);
323 connectionAdapter.disconnect();
325 } catch (Exception e) {
327 LOG.warn("issuing disconnect during handshake, reason - RPC: {}", e.getMessage(), e);
328 connectionAdapter.disconnect();
332 LOG.debug("postHandshake DONE");
336 public void setUseVersionBitmap(boolean useVersionBitmap) {
337 this.useVersionBitmap = useVersionBitmap;
341 public void setErrorHandler(ErrorHandler errorHandler) {
342 this.errorHandler = errorHandler;