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.Objects;
12 import java.util.concurrent.Future;
14 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
15 import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
16 import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
17 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
18 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
24 import org.opendaylight.yangtools.yang.common.RpcError;
25 import org.opendaylight.yangtools.yang.common.RpcResult;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 import com.google.common.util.concurrent.FutureCallback;
30 import com.google.common.util.concurrent.Futures;
31 import com.google.common.util.concurrent.JdkFutureAdapters;
32 import com.google.common.util.concurrent.ListenableFuture;
33 import com.google.common.util.concurrent.SettableFuture;
39 public class HandshakeManagerImpl implements HandshakeManager {
41 private static final long activeXID = 20L;
43 private static final Logger LOG = LoggerFactory
44 .getLogger(HandshakeManagerImpl.class);
46 private Short lastProposedVersion;
47 private Short lastReceivedVersion;
48 private final List<Short> versionOrder;
51 private final ConnectionAdapter connectionAdapter;
52 private Short version;
53 private ErrorHandler errorHandler;
57 private Short highestVersion;
59 private Long activeXid;
61 private HandshakeListener handshakeListener;
63 private boolean useVersionBitmap;
66 * @param connectionAdapter connection adaptor for switch
67 * @param highestVersion highest openflow version
68 * @param versionOrder list of version in order for connection protocol negotiation
70 public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion,
71 List<Short> versionOrder) {
72 this.highestVersion = highestVersion;
73 this.versionOrder = versionOrder;
74 this.connectionAdapter = connectionAdapter;
78 public void setHandshakeListener(HandshakeListener handshakeListener) {
79 this.handshakeListener = handshakeListener;
83 public synchronized void shake(HelloMessage receivedHello) {
85 if (version != null) {
86 // Some switches respond with a second HELLO acknowledging our HELLO
87 // but we've already completed the handshake based on the negotiated
88 // version and have registered this switch.
89 LOG.debug("Hello recieved after handshake already settled ... ignoring.");
93 LOG.trace("handshake STARTED");
94 setActiveXid(activeXID);
97 if (receivedHello == 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 = receivedHello.getVersion();
107 List<Elements> elements = receivedHello.getElements();
108 setActiveXid(receivedHello.getXid());
109 List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
110 LOG.debug("Hello message: version={}, xid={}, bitmap={}", remoteVersion,
111 receivedHello.getXid(), remoteVersionBitmap);
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 LOG.trace("ret - shake fail - closing");
123 handshakeListener.onHandshakeFailure();
128 * @param remoteVersion remote version
129 * @throws Exception exception
131 private void handleStepByStepVersionNegotiation(final Short remoteVersion) throws Exception {
132 LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}",
133 remoteVersion, lastProposedVersion, highestVersion);
135 if (lastProposedVersion == null) {
136 // first hello has not been sent yet, send it and either wait for next remote
137 // version or proceed
138 lastProposedVersion = proposeNextVersion(remoteVersion);
139 final Long nextHelloXid = getNextXid();
140 ListenableFuture<Void> helloResult = sendHelloMessage(lastProposedVersion, nextHelloXid);
141 Futures.addCallback(helloResult, new FutureCallback<Void>() {
143 public void onSuccess(Void result) {
145 stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
146 } catch (Exception e) {
147 errorHandler.handleException(e, null);
148 handshakeListener.onHandshakeFailure();
153 public void onFailure(Throwable t) {
154 LOG.info("hello sending seriously failed [{}]", nextHelloXid);
155 LOG.trace("detail of hello send problem", t);
159 stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
163 private void stepByStepVersionSubStep(Short remoteVersion, Short lastProposedVersion) throws Exception {
164 if (remoteVersion.equals(lastProposedVersion)) {
165 postHandshake(lastProposedVersion, getNextXid());
166 LOG.trace("ret - OK - switch answered with lastProposedVersion");
168 checkNegotiationStalling(remoteVersion);
170 if (remoteVersion > (lastProposedVersion == null ? highestVersion : this.lastProposedVersion)) {
171 // wait for next version
172 LOG.trace("ret - wait");
174 //propose lower version
175 handleLowerVersionProposal(remoteVersion);
181 * @param remoteVersion remote version
182 * @throws Exception exception
184 private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
185 Short proposedVersion;
186 // find the version from header version field
187 proposedVersion = proposeNextVersion(remoteVersion);
188 lastProposedVersion = proposedVersion;
189 sendHelloMessage(proposedVersion, getNextXid());
191 if (! Objects.equals(proposedVersion, remoteVersion)) {
192 LOG.trace("ret - sent+wait");
194 LOG.trace("ret - sent+OK");
195 postHandshake(proposedVersion, getNextXid());
200 * @param elements version elements
201 * @throws Exception exception
203 private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
204 final Short proposedVersion = proposeCommonBitmapVersion(elements);
205 if (lastProposedVersion == null) {
206 // first hello has not been sent yet
207 Long nexHelloXid = getNextXid();
208 ListenableFuture<Void> helloDone = sendHelloMessage(proposedVersion, nexHelloXid);
209 Futures.addCallback(helloDone, new FutureCallback<Void>() {
211 public void onSuccess(Void result) {
212 LOG.trace("ret - DONE - versionBitmap");
213 postHandshake(proposedVersion, getNextXid());
217 public void onFailure(Throwable t) {
221 LOG.trace("next proposal [{}] with versionBitmap hooked ..", nexHelloXid);
223 LOG.trace("ret - DONE - versionBitmap");
224 postHandshake(proposedVersion, getNextXid());
232 private Long getNextXid() {
240 private void setActiveXid(Long xid) {
241 this.activeXid = xid;
245 * @param remoteVersion remove version
247 private void checkNegotiationStalling(Short remoteVersion) {
248 if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
249 throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
251 lastReceivedVersion = remoteVersion;
255 public Short getVersion() {
260 * find common highest supported bitmap version
261 * @param list bitmap list
262 * @return proposed bitmap value
264 protected Short proposeCommonBitmapVersion(List<Elements> list) {
265 Short supportedHighestVersion = null;
266 if((null != list) && (0 != list.size())) {
267 for(Elements element : list) {
268 List<Boolean> bitmap = element.getVersionBitmap();
269 // check for version bitmap
270 for(short bitPos : ConnectionConductor.versionOrder) {
271 // with all the version it should work.
272 if(bitmap.get(bitPos % Integer.SIZE)) {
273 supportedHighestVersion = bitPos;
279 if(null == supportedHighestVersion) {
280 LOG.trace("versionBitmap: no common version found");
281 throw new IllegalArgumentException("no common version found in versionBitmap");
285 return supportedHighestVersion;
289 * find supported version based on remoteVersion
290 * @param remoteVersion openflow version supported by remote entity
291 * @return openflow version
293 protected short proposeNextVersion(short remoteVersion) {
294 Short proposal = null;
295 for (short offer : versionOrder) {
296 if (offer <= remoteVersion) {
301 if (proposal == null) {
302 throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
309 * send hello reply without versionBitmap
310 * @param helloVersion initial hello version for openflow connection negotiation
311 * @param helloXid transaction id
314 private ListenableFuture<Void> sendHelloMessage(Short helloVersion, final Long helloXid) throws Exception {
317 HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
319 final SettableFuture<Void> resultFtr = SettableFuture.create();
321 LOG.debug("sending hello message: version{}, xid={}, version bitmap={}",
322 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
324 Future<RpcResult<Void>> helloResult = connectionAdapter.hello(helloInput);
326 ListenableFuture<RpcResult<Void>> rpcResultListenableFuture = JdkFutureAdapters.listenInPoolThread(helloResult);
327 Futures.addCallback(rpcResultListenableFuture, new FutureCallback<RpcResult<Void>>() {
329 public void onSuccess(RpcResult<Void> result) {
330 if (result.isSuccessful()) {
331 LOG.debug("hello successfully sent, xid={}, addr={}", helloXid, connectionAdapter.getRemoteAddress());
334 for (RpcError error : result.getErrors()) {
335 LOG.debug("hello sending failed [{}]: i:{} s:{} m:{}, addr:{}", helloXid,
336 error.getInfo(), error.getSeverity(), error.getMessage(),
337 connectionAdapter.getRemoteAddress());
338 if (error.getCause() != null) {
339 LOG.trace("DETAIL of sending hello failure", error.getCause());
342 resultFtr.cancel(false);
343 handshakeListener.onHandshakeFailure();
348 public void onFailure(Throwable t) {
349 LOG.warn("sending of hello failed seriously [{}, addr:{}]: {}", helloXid,
350 connectionAdapter.getRemoteAddress(), t.getMessage());
351 LOG.trace("DETAIL of sending of hello failure:", t);
352 resultFtr.cancel(false);
353 handshakeListener.onHandshakeFailure();
356 LOG.trace("sending hello message [{}] - result hooked ..", helloXid);
362 * after handshake set features, register to session
363 * @param proposedVersion proposed openflow version
364 * @param xid transaction id
366 protected void postHandshake(final Short proposedVersion, final Long xid) {
368 version = proposedVersion;
370 LOG.debug("version set: {}", proposedVersion);
372 GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
373 featuresBuilder.setVersion(version).setXid(xid);
374 LOG.debug("sending feature request for version={} and xid={}", version, xid);
375 Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
376 .getFeatures(featuresBuilder.build());
378 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(featuresFuture),
379 new FutureCallback<RpcResult<GetFeaturesOutput>>() {
381 public void onSuccess(RpcResult<GetFeaturesOutput> rpcFeatures) {
382 LOG.trace("features are back");
383 if (rpcFeatures.isSuccessful()) {
384 GetFeaturesOutput featureOutput = rpcFeatures.getResult();
386 LOG.debug("obtained features: datapathId={}",
387 featureOutput.getDatapathId());
388 LOG.debug("obtained features: auxiliaryId={}",
389 featureOutput.getAuxiliaryId());
390 LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}",
391 version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
392 handshakeListener.onHandshakeSuccessful(featureOutput, proposedVersion);
395 LOG.warn("issuing disconnect during handshake [{}]", connectionAdapter.getRemoteAddress());
396 for (RpcError rpcError : rpcFeatures.getErrors()) {
397 LOG.debug("handshake - features failure [{}]: i:{} | m:{} | s:{}", xid,
398 rpcError.getInfo(), rpcError.getMessage(), rpcError.getSeverity(),
402 handshakeListener.onHandshakeFailure();
405 LOG.debug("postHandshake DONE");
409 public void onFailure(Throwable t) {
410 LOG.warn("getting feature failed seriously [{}, addr:{}]: {}", xid,
411 connectionAdapter.getRemoteAddress(), t.getMessage());
412 LOG.trace("DETAIL of sending of hello failure:", t);
416 LOG.debug("future features [{}] hooked ..", xid);
421 public void setUseVersionBitmap(boolean useVersionBitmap) {
422 this.useVersionBitmap = useVersionBitmap;
426 public void setErrorHandler(ErrorHandler errorHandler) {
427 this.errorHandler = errorHandler;