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.openflowplugin.ConnectionException;
17 import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
18 import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
19 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
20 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
26 import org.opendaylight.yangtools.yang.common.RpcResult;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
34 public class HandshakeManagerImpl implements HandshakeManager {
36 private static final Logger LOG = LoggerFactory
37 .getLogger(HandshakeManagerImpl.class);
39 private Short lastProposedVersion;
40 private Short lastReceivedVersion;
41 private final List<Short> versionOrder;
43 private HelloMessage receivedHello;
44 private final ConnectionAdapter connectionAdapter;
45 private GetFeaturesOutput features;
46 private Short version;
47 private ErrorHandler errorHandler;
49 private long maxTimeout = 8000;
50 private TimeUnit maxTimeoutUnit = TimeUnit.MILLISECONDS;
51 private Short highestVersion;
53 private Long activeXid;
55 private HandshakeListener handshakeListener;
57 private boolean useVersionBitmap;
60 public void setReceivedHello(HelloMessage receivedHello) {
61 this.receivedHello = receivedHello;
65 * @param connectionAdapter
66 * @param highestVersion
69 public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion,
70 List<Short> versionOrder) {
71 this.highestVersion = highestVersion;
72 this.versionOrder = versionOrder;
73 this.connectionAdapter = connectionAdapter;
77 public void setHandshakeListener(HandshakeListener handshakeListener) {
78 this.handshakeListener = handshakeListener;
84 if (version != null) {
85 // Some switches respond with a second HELLO acknowledging our HELLO
86 // but we've already completed the handshake based on the negotiated
87 // version and have registered this switch.
88 LOG.debug("Hello recieved after handshake already settled ... ignoring.");
92 LOG.trace("handshake STARTED");
94 HelloMessage receivedHelloLoc = receivedHello;
97 if (receivedHelloLoc == null) {
98 // first Hello sending
99 sendHelloMessage(highestVersion, getNextXid());
100 lastProposedVersion = highestVersion;
101 LOG.trace("ret - firstHello+wait");
105 // process the 2. and later hellos
106 Short remoteVersion = receivedHelloLoc.getVersion();
107 List<Elements> elements = receivedHelloLoc.getElements();
108 setActiveXid(receivedHelloLoc.getXid());
109 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
110 LOG.debug("Hello message: version={}, bitmap={}, xid={}", remoteVersion,
111 remoteVersionBitmap, receivedHelloLoc.getXid());
113 if (useVersionBitmap && remoteVersionBitmap != null) {
114 // versionBitmap on both sides -> ONE STEP DECISION
115 handleVersionBitmapNegotiation(elements);
117 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
118 handleStepByStepVersionNegotiation(remoteVersion);
120 } catch (Exception ex) {
121 errorHandler.handleException(ex, null);
122 connectionAdapter.disconnect();
123 handshakeListener.onHandshakeFailure();
124 LOG.trace("ret - shake fail: {}", ex.getMessage());
129 * @param remoteVersion
132 private void handleStepByStepVersionNegotiation(Short remoteVersion) throws Exception {
133 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
134 remoteVersion, lastProposedVersion, highestVersion);
136 if (lastProposedVersion == null) {
137 // first hello has not been sent yet, send it and either wait for next remote
138 // version or proceed
139 lastProposedVersion = proposeNextVersion(remoteVersion);
140 sendHelloMessage(lastProposedVersion, getNextXid());
143 if (remoteVersion == lastProposedVersion) {
144 postHandshake(lastProposedVersion, getNextXid());
145 LOG.trace("ret - OK - switch answered with lastProposedVersion");
147 checkNegotiationStalling(remoteVersion);
149 if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
150 // wait for next version
151 LOG.trace("ret - wait");
153 //propose lower version
154 handleLowerVersionProposal(remoteVersion);
160 * @param remoteVersion
163 private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
164 Short proposedVersion;
165 // find the version from header version field
166 proposedVersion = proposeNextVersion(remoteVersion);
167 lastProposedVersion = proposedVersion;
168 sendHelloMessage(proposedVersion, getNextXid());
170 if (proposedVersion != remoteVersion) {
171 LOG.trace("ret - sent+wait");
173 LOG.trace("ret - sent+OK");
174 postHandshake(proposedVersion, getNextXid());
182 private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
183 Short proposedVersion;
184 proposedVersion = proposeCommonBitmapVersion(elements);
185 if (lastProposedVersion == null) {
186 // first hello has not been sent yet
187 sendHelloMessage(proposedVersion, getNextXid());
189 postHandshake(proposedVersion, getNextXid());
190 LOG.trace("ret - OK - versionBitmap");
197 private Long getNextXid() {
205 private void setActiveXid(Long xid) {
206 this.activeXid = xid;
210 * @param remoteVersion
212 private void checkNegotiationStalling(Short remoteVersion) {
213 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
214 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
216 lastReceivedVersion = remoteVersion;
220 public GetFeaturesOutput getFeatures() {
225 public Short getVersion() {
230 * find common highest supported bitmap version
234 protected Short proposeCommonBitmapVersion(List<Elements> list) {
235 Short supportedHighestVersion = null;
236 if((null != list) && (0 != list.size())) {
237 for(Elements element : list) {
238 List<Boolean> bitmap = element.getVersionBitmap();
239 // check for version bitmap
240 for(short bitPos : ConnectionConductor.versionOrder) {
241 // with all the version it should work.
242 if(bitmap.get(bitPos % Integer.SIZE)) {
243 supportedHighestVersion = bitPos;
249 if(null == supportedHighestVersion) {
250 throw new IllegalArgumentException("no common version found in versionBitmap");
254 return supportedHighestVersion;
258 * find supported version based on remoteVersion
259 * @param remoteVersion
262 protected short proposeNextVersion(short remoteVersion) {
263 Short proposal = null;
264 for (short offer : versionOrder) {
265 if (offer <= remoteVersion) {
270 if (proposal == null) {
271 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
278 * send hello reply without versionBitmap
279 * @param helloVersion
283 private void sendHelloMessage(Short helloVersion, Long helloXid) throws Exception {
284 //Short highestVersion = ConnectionConductor.versionOrder.get(0);
285 //final Long helloXid = 21L;
286 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
288 LOG.debug("sending hello message: version{}, xid={}, version bitmap={}",
289 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
292 RpcResult<Void> helloResult = connectionAdapter.hello(helloInput).get(maxTimeout, maxTimeoutUnit);
293 RpcUtil.smokeRpc(helloResult);
294 LOG.debug("FIRST HELLO sent.");
295 } catch (Exception e) {
296 if (LOG.isTraceEnabled()) {
297 LOG.trace("FIRST HELLO sent.", e);
299 handshakeListener.onHandshakeFailure();
300 throw new ConnectionException("FIRST HELLO sending failed because of connection issue.");
306 * after handshake set features, register to session
307 * @param proposedVersion
311 protected void postHandshake(Short proposedVersion, Long xid) throws Exception {
313 version = proposedVersion;
315 LOG.debug("version set: {}", proposedVersion);
317 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
318 featuresBuilder.setVersion(version).setXid(xid);
319 LOG.debug("sending feature request for version={} and xid={}", version, xid);
320 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
321 .getFeatures(featuresBuilder.build());
322 LOG.debug("waiting for features");
324 RpcResult<GetFeaturesOutput> rpcFeatures =
325 featuresFuture.get(maxTimeout, maxTimeoutUnit);
326 RpcUtil.smokeRpc(rpcFeatures);
328 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
330 LOG.debug("obtained features: datapathId={}",
331 featureOutput.getDatapathId());
332 LOG.debug("obtained features: auxiliaryId={}",
333 featureOutput.getAuxiliaryId());
334 LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
335 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
337 handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
338 } catch (TimeoutException e) {
340 LOG.warn("issuing disconnect during handshake, reason: future expired", e);
341 connectionAdapter.disconnect();
342 handshakeListener.onHandshakeFailure();
344 } catch (Exception e) {
346 LOG.warn("issuing disconnect during handshake, reason - RPC: {}", e.getMessage(), e);
347 connectionAdapter.disconnect();
348 handshakeListener.onHandshakeFailure();
352 LOG.debug("postHandshake DONE");
356 public void setUseVersionBitmap(boolean useVersionBitmap) {
357 this.useVersionBitmap = useVersionBitmap;
361 public void setErrorHandler(ErrorHandler errorHandler) {
362 this.errorHandler = errorHandler;