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 com.google.common.util.concurrent.FutureCallback;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.JdkFutureAdapters;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.SettableFuture;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.concurrent.Future;
18 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
19 import org.opendaylight.openflowplugin.api.OFConstants;
20 import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
21 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
22 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
28 import org.opendaylight.yangtools.yang.common.RpcError;
29 import org.opendaylight.yangtools.yang.common.RpcResult;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 public class HandshakeManagerImpl implements HandshakeManager {
35 private static final long activeXID = 20L;
37 private static final Logger LOG = LoggerFactory
38 .getLogger(HandshakeManagerImpl.class);
40 private Short lastProposedVersion;
41 private Short lastReceivedVersion;
42 private final List<Short> versionOrder;
45 private final ConnectionAdapter connectionAdapter;
46 private Short version;
47 private ErrorHandler errorHandler;
51 private Short highestVersion;
53 private Long activeXid;
55 private HandshakeListener handshakeListener;
57 private boolean useVersionBitmap;
60 * @param connectionAdapter connection adaptor for switch
61 * @param highestVersion highest openflow version
62 * @param versionOrder list of version in order for connection protocol negotiation
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;
77 public synchronized void shake(HelloMessage receivedHello) {
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");
88 setActiveXid(activeXID);
91 if (receivedHello == null) {
92 // first Hello sending
93 sendHelloMessage(highestVersion, getNextXid());
94 lastProposedVersion = highestVersion;
95 LOG.trace("ret - firstHello+wait");
99 // process the 2. and later hellos
100 Short remoteVersion = receivedHello.getVersion();
101 List<Elements> elements = receivedHello.getElements();
102 setActiveXid(receivedHello.getXid());
103 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
104 LOG.debug("Hello message: version={}, xid={}, bitmap={}", remoteVersion,
105 receivedHello.getXid(), remoteVersionBitmap);
107 if (useVersionBitmap && remoteVersionBitmap != null) {
108 // versionBitmap on both sides -> ONE STEP DECISION
109 handleVersionBitmapNegotiation(elements);
111 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
112 handleStepByStepVersionNegotiation(remoteVersion);
114 } catch (Exception ex) {
115 errorHandler.handleException(ex);
116 LOG.trace("ret - shake fail - closing");
117 handshakeListener.onHandshakeFailure();
122 * @param remoteVersion remote version
123 * @throws Exception exception
125 private void handleStepByStepVersionNegotiation(final Short remoteVersion) throws Exception {
126 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
127 remoteVersion, lastProposedVersion, highestVersion);
129 if (lastProposedVersion == null) {
130 // first hello has not been sent yet, send it and either wait for next remote
131 // version or proceed
132 lastProposedVersion = proposeNextVersion(remoteVersion);
133 final Long nextHelloXid = getNextXid();
134 ListenableFuture<Void> helloResult = sendHelloMessage(lastProposedVersion, nextHelloXid);
135 Futures.addCallback(helloResult, new FutureCallback<Void>() {
137 public void onSuccess(Void result) {
139 stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
140 } catch (Exception e) {
141 errorHandler.handleException(e);
142 handshakeListener.onHandshakeFailure();
147 public void onFailure(Throwable t) {
148 LOG.info("hello sending seriously failed [{}]", nextHelloXid);
149 LOG.trace("detail of hello send problem", t);
153 stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
157 private void stepByStepVersionSubStep(Short remoteVersion, Short lastProposedVersion) throws Exception {
158 if (remoteVersion.equals(lastProposedVersion)) {
159 postHandshake(lastProposedVersion, getNextXid());
160 LOG.trace("ret - OK - switch answered with lastProposedVersion");
162 checkNegotiationStalling(remoteVersion);
164 if (remoteVersion > (lastProposedVersion == null ? highestVersion : this.lastProposedVersion)) {
165 // wait for next version
166 LOG.trace("ret - wait");
168 //propose lower version
169 handleLowerVersionProposal(remoteVersion);
175 * @param remoteVersion remote version
176 * @throws Exception exception
178 private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
179 Short proposedVersion;
180 // find the version from header version field
181 proposedVersion = proposeNextVersion(remoteVersion);
182 lastProposedVersion = proposedVersion;
183 sendHelloMessage(proposedVersion, getNextXid());
185 if (! Objects.equals(proposedVersion, remoteVersion)) {
186 LOG.trace("ret - sent+wait");
188 LOG.trace("ret - sent+OK");
189 postHandshake(proposedVersion, getNextXid());
194 * @param elements version elements
195 * @throws Exception exception
197 private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
198 final Short proposedVersion = proposeCommonBitmapVersion(elements);
199 if (lastProposedVersion == null) {
200 // first hello has not been sent yet
201 Long nexHelloXid = getNextXid();
202 ListenableFuture<Void> helloDone = sendHelloMessage(proposedVersion, nexHelloXid);
203 Futures.addCallback(helloDone, new FutureCallback<Void>() {
205 public void onSuccess(Void result) {
206 LOG.trace("ret - DONE - versionBitmap");
207 postHandshake(proposedVersion, getNextXid());
211 public void onFailure(Throwable t) {
215 LOG.trace("next proposal [{}] with versionBitmap hooked ..", nexHelloXid);
217 LOG.trace("ret - DONE - versionBitmap");
218 postHandshake(proposedVersion, getNextXid());
226 private Long getNextXid() {
234 private void setActiveXid(Long xid) {
235 this.activeXid = xid;
239 * @param remoteVersion remove version
241 private void checkNegotiationStalling(Short remoteVersion) {
242 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
243 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
245 lastReceivedVersion = remoteVersion;
249 public Short getVersion() {
254 * find common highest supported bitmap version
255 * @param list bitmap list
256 * @return proposed bitmap value
258 protected Short proposeCommonBitmapVersion(List<Elements> list) {
259 Short supportedHighestVersion = null;
260 if((null != list) && (0 != list.size())) {
261 for(Elements element : list) {
262 List<Boolean> bitmap = element.getVersionBitmap();
263 // check for version bitmap
264 for(short bitPos : OFConstants.VERSION_ORDER) {
265 // with all the version it should work.
266 if(bitmap.get(bitPos % Integer.SIZE)) {
267 supportedHighestVersion = bitPos;
273 if(null == supportedHighestVersion) {
274 LOG.trace("versionBitmap: no common version found");
275 throw new IllegalArgumentException("no common version found in versionBitmap");
279 return supportedHighestVersion;
283 * find supported version based on remoteVersion
284 * @param remoteVersion openflow version supported by remote entity
285 * @return openflow version
287 protected short proposeNextVersion(short remoteVersion) {
288 Short proposal = null;
289 for (short offer : versionOrder) {
290 if (offer <= remoteVersion) {
295 if (proposal == null) {
296 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
303 * send hello reply without versionBitmap
304 * @param helloVersion initial hello version for openflow connection negotiation
305 * @param helloXid transaction id
308 private ListenableFuture<Void> sendHelloMessage(Short helloVersion, final Long helloXid) throws Exception {
311 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
313 final SettableFuture<Void> resultFtr = SettableFuture.create();
315 LOG.debug("sending hello message: version{}, xid={}, version bitmap={}",
316 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
318 Future<RpcResult<Void>> helloResult = connectionAdapter.hello(helloInput);
320 ListenableFuture<RpcResult<Void>> rpcResultListenableFuture = JdkFutureAdapters.listenInPoolThread(helloResult);
321 Futures.addCallback(rpcResultListenableFuture, new FutureCallback<RpcResult<Void>>() {
323 public void onSuccess(RpcResult<Void> result) {
324 if (result.isSuccessful()) {
325 LOG.debug("hello successfully sent, xid={}, addr={}", helloXid, connectionAdapter.getRemoteAddress());
328 for (RpcError error : result.getErrors()) {
329 LOG.debug("hello sending failed [{}]: i:{} s:{} m:{}, addr:{}", helloXid,
330 error.getInfo(), error.getSeverity(), error.getMessage(),
331 connectionAdapter.getRemoteAddress());
332 if (error.getCause() != null) {
333 LOG.trace("DETAIL of sending hello failure", error.getCause());
336 resultFtr.cancel(false);
337 handshakeListener.onHandshakeFailure();
342 public void onFailure(Throwable t) {
343 LOG.warn("sending of hello failed seriously [{}, addr:{}]: {}", helloXid,
344 connectionAdapter.getRemoteAddress(), t.getMessage());
345 LOG.trace("DETAIL of sending of hello failure:", t);
346 resultFtr.cancel(false);
347 handshakeListener.onHandshakeFailure();
350 LOG.trace("sending hello message [{}] - result hooked ..", helloXid);
356 * after handshake set features, register to session
357 * @param proposedVersion proposed openflow version
358 * @param xid transaction id
360 protected void postHandshake(final Short proposedVersion, final Long xid) {
362 version = proposedVersion;
364 LOG.debug("version set: {}", proposedVersion);
366 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
367 featuresBuilder.setVersion(version).setXid(xid);
368 LOG.debug("sending feature request for version={} and xid={}", version, xid);
369 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
370 .getFeatures(featuresBuilder.build());
372 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(featuresFuture),
373 new FutureCallback<RpcResult<GetFeaturesOutput>>() {
375 public void onSuccess(RpcResult<GetFeaturesOutput> rpcFeatures) {
376 LOG.trace("features are back");
377 if (rpcFeatures.isSuccessful()) {
378 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
380 LOG.debug("obtained features: datapathId={}",
381 featureOutput.getDatapathId());
382 LOG.debug("obtained features: auxiliaryId={}",
383 featureOutput.getAuxiliaryId());
384 LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
385 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
386 handshakeListener.onHandshakeSuccessful(featureOutput, proposedVersion);
389 LOG.warn("issuing disconnect during handshake [{}]", connectionAdapter.getRemoteAddress());
390 for (RpcError rpcError : rpcFeatures.getErrors()) {
391 LOG.debug("handshake - features failure [{}]: i:{} | m:{} | s:{}", xid,
392 rpcError.getInfo(), rpcError.getMessage(), rpcError.getSeverity(),
396 handshakeListener.onHandshakeFailure();
399 LOG.debug("postHandshake DONE");
403 public void onFailure(Throwable t) {
404 LOG.warn("getting feature failed seriously [{}, addr:{}]: {}", xid,
405 connectionAdapter.getRemoteAddress(), t.getMessage());
406 LOG.trace("DETAIL of sending of hello failure:", t);
410 LOG.debug("future features [{}] hooked ..", xid);
415 public void setUseVersionBitmap(boolean useVersionBitmap) {
416 this.useVersionBitmap = useVersionBitmap;
420 public void setErrorHandler(ErrorHandler errorHandler) {
421 this.errorHandler = errorHandler;