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;
79 if (version != null) {
80 // Some switches respond with a second HELLO acknowledging our HELLO
81 // but we've already completed the handshake based on the negotiated
82 // version and have registered this switch.
83 LOG.debug("Hello recieved after handshake already settled ... ignoring.");
87 LOG.trace("handshake STARTED");
89 HelloMessage receivedHelloLoc = receivedHello;
92 if (receivedHelloLoc == null) {
93 // first Hello sending
94 sendHelloMessage(highestVersion, getNextXid());
95 lastProposedVersion = highestVersion;
96 LOG.trace("ret - firstHello+wait");
100 // process the 2. and later hellos
101 Short remoteVersion = receivedHelloLoc.getVersion();
102 List<Elements> elements = receivedHelloLoc.getElements();
103 setActiveXid(receivedHelloLoc.getXid());
104 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
105 LOG.debug("Hello message: version={}, bitmap={}, xid={}", remoteVersion,
106 remoteVersionBitmap, receivedHelloLoc.getXid());
108 if (useVersionBitmap && remoteVersionBitmap != null) {
109 // versionBitmap on both sides -> ONE STEP DECISION
110 handleVersionBitmapNegotiation(elements);
112 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
113 handleStepByStepVersionNegotiation(remoteVersion);
115 } catch (Exception ex) {
116 errorHandler.handleException(ex, null);
117 connectionAdapter.disconnect();
118 LOG.trace("ret - shake fail: {}", ex.getMessage());
123 * @param remoteVersion
126 private void handleStepByStepVersionNegotiation(Short remoteVersion) throws Exception {
127 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
128 remoteVersion, lastProposedVersion, highestVersion);
130 if (lastProposedVersion == null) {
131 // first hello has not been sent yet, send it and either wait for next remote
132 // version or proceed
133 lastProposedVersion = proposeNextVersion(remoteVersion);
134 sendHelloMessage(lastProposedVersion, getNextXid());
137 if (remoteVersion == lastProposedVersion) {
138 postHandshake(lastProposedVersion, getNextXid());
139 LOG.trace("ret - OK - switch answered with lastProposedVersion");
141 checkNegotiationStalling(remoteVersion);
143 if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
144 // wait for next version
145 LOG.trace("ret - wait");
147 //propose lower version
148 handleLowerVersionProposal(remoteVersion);
154 * @param remoteVersion
157 private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
158 Short proposedVersion;
159 // find the version from header version field
160 proposedVersion = proposeNextVersion(remoteVersion);
161 lastProposedVersion = proposedVersion;
162 sendHelloMessage(proposedVersion, getNextXid());
164 if (proposedVersion != remoteVersion) {
165 LOG.trace("ret - sent+wait");
167 LOG.trace("ret - sent+OK");
168 postHandshake(proposedVersion, getNextXid());
176 private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
177 Short proposedVersion;
178 proposedVersion = proposeCommonBitmapVersion(elements);
179 if (lastProposedVersion == null) {
180 // first hello has not been sent yet
181 sendHelloMessage(proposedVersion, getNextXid());
183 postHandshake(proposedVersion, getNextXid());
184 LOG.trace("ret - OK - versionBitmap");
191 private Long getNextXid() {
199 private void setActiveXid(Long xid) {
200 this.activeXid = xid;
204 * @param remoteVersion
206 private void checkNegotiationStalling(Short remoteVersion) {
207 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
208 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
210 lastReceivedVersion = remoteVersion;
214 public GetFeaturesOutput getFeatures() {
219 public Short getVersion() {
224 * find common highest supported bitmap version
228 protected Short proposeCommonBitmapVersion(List<Elements> list) {
229 Short supportedHighestVersion = null;
230 if((null != list) && (0 != list.size())) {
231 for(Elements element : list) {
232 List<Boolean> bitmap = element.getVersionBitmap();
233 // check for version bitmap
234 for(short bitPos : ConnectionConductor.versionOrder) {
235 // with all the version it should work.
236 if(bitmap.get(bitPos % Integer.SIZE)) {
237 supportedHighestVersion = bitPos;
243 if(null == supportedHighestVersion) {
244 throw new IllegalArgumentException("no common version found in versionBitmap");
248 return supportedHighestVersion;
252 * find supported version based on remoteVersion
253 * @param remoteVersion
256 protected short proposeNextVersion(short remoteVersion) {
257 Short proposal = null;
258 for (short offer : versionOrder) {
259 if (offer <= remoteVersion) {
264 if (proposal == null) {
265 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
272 * send hello reply without versionBitmap
273 * @param helloVersion
277 private void sendHelloMessage(Short helloVersion, Long helloXid) throws Exception {
278 //Short highestVersion = ConnectionConductor.versionOrder.get(0);
279 //final Long helloXid = 21L;
280 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
282 LOG.debug("sending hello message: version{}, xid={}, version bitmap={}",
283 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
286 RpcResult<Void> helloResult = connectionAdapter.hello(helloInput).get(maxTimeout, maxTimeoutUnit);
287 RpcUtil.smokeRpc(helloResult);
288 LOG.debug("FIRST HELLO sent.");
289 } catch (Exception e) {
290 LOG.debug("FIRST HELLO sending failed.");
297 * after handshake set features, register to session
298 * @param proposedVersion
302 protected void postHandshake(Short proposedVersion, Long xid) throws Exception {
304 version = proposedVersion;
306 LOG.debug("version set: {}", proposedVersion);
308 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
309 featuresBuilder.setVersion(version).setXid(xid);
310 LOG.debug("sending feature request for version={} and xid={}", version, xid);
311 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
312 .getFeatures(featuresBuilder.build());
313 LOG.debug("waiting for features");
315 RpcResult<GetFeaturesOutput> rpcFeatures =
316 featuresFuture.get(maxTimeout, maxTimeoutUnit);
317 RpcUtil.smokeRpc(rpcFeatures);
319 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
321 LOG.debug("obtained features: datapathId={}",
322 featureOutput.getDatapathId());
323 LOG.debug("obtained features: auxiliaryId={}",
324 featureOutput.getAuxiliaryId());
325 LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
326 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
328 handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
329 } catch (TimeoutException e) {
331 LOG.warn("issuing disconnect during handshake, reason: future expired", e);
332 connectionAdapter.disconnect();
334 } catch (Exception e) {
336 LOG.warn("issuing disconnect during handshake, reason - RPC: {}", e.getMessage(), e);
337 connectionAdapter.disconnect();
341 LOG.debug("postHandshake DONE");
345 public void setUseVersionBitmap(boolean useVersionBitmap) {
346 this.useVersionBitmap = useVersionBitmap;
350 public void setErrorHandler(ErrorHandler errorHandler) {
351 this.errorHandler = errorHandler;