acd76d6965204b72b73e49d33b0a1a153e11a4bf
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / HandshakeManagerImpl.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.openflow.md.core;
9
10 import java.util.List;
11 import java.util.concurrent.Future;
12 import java.util.concurrent.TimeUnit;
13 import java.util.concurrent.TimeoutException;
14
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;
24
25 /**
26  * @author mirehak
27  *
28  */
29 public class HandshakeManagerImpl implements HandshakeManager {
30     
31     private static final Logger LOG = LoggerFactory
32             .getLogger(HandshakeManagerImpl.class);
33     
34     private Short lastProposedVersion;
35     private Short lastReceivedVersion;
36     private final List<Short> versionOrder;
37     
38     private HelloMessage receivedHello;
39     private final ConnectionAdapter connectionAdapter;
40     private GetFeaturesOutput features;
41     private Short version;
42     private ErrorHandler errorHandler;
43     
44     private long maxTimeout = 1000;
45     private TimeUnit maxTimeoutUnit = TimeUnit.MILLISECONDS;
46     private Short highestVersion;
47
48     private Long activeXid;
49
50     private HandshakeListener handshakeListener;
51
52     private boolean useVersionBitmap;
53     
54     @Override
55     public void setReceivedHello(HelloMessage receivedHello) {
56         this.receivedHello = receivedHello;
57     }
58     
59     /**
60      * @param connectionAdapter 
61      * @param highestVersion 
62      * @param versionOrder
63      */
64     public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion, 
65             List<Short> versionOrder) {
66         this.highestVersion = highestVersion;
67         this.versionOrder = versionOrder;
68         this.connectionAdapter = connectionAdapter;
69     }
70     
71     @Override
72     public void setHandshakeListener(HandshakeListener handshakeListener) {
73         this.handshakeListener = handshakeListener;
74     }
75
76     @Override
77     public synchronized void shake() {
78         LOG.trace("handshake STARTED");
79         setActiveXid(20L);
80         HelloMessage receivedHelloLoc = receivedHello;
81         
82         try {
83             if (receivedHelloLoc == null) {
84                 // first Hello sending
85                 sendHelloMessage(highestVersion, getNextXid());
86                 lastProposedVersion = highestVersion;
87                 LOG.trace("ret - firstHello+wait");
88                 return;
89             }
90
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());
98         
99             if (useVersionBitmap && remoteVersionBitmap != null) {
100                 // versionBitmap on both sides -> ONE STEP DECISION
101                 handleVersionBitmapNegotiation(elements);
102             } else { 
103                 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying 
104                 handleStepByStepVersionNegotiation(remoteVersion);
105             }
106         } catch (Exception ex) {
107             errorHandler.handleException(ex, null);
108             connectionAdapter.disconnect();
109             LOG.trace("ret - shake fail: {}", ex.getMessage());
110         }
111     }
112
113     /**
114      * @param remoteVersion
115      * @throws Exception 
116      */
117     private void handleStepByStepVersionNegotiation(Short remoteVersion) throws Exception {
118         LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}", 
119                 remoteVersion, lastProposedVersion, highestVersion);
120         
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());
126         }
127         
128         if (remoteVersion == lastProposedVersion) {
129             postHandshake(lastProposedVersion, getNextXid());
130             LOG.trace("ret - OK - switch answered with lastProposedVersion");
131         } else {
132             checkNegotiationStalling(remoteVersion);
133
134             if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
135                 // wait for next version
136                 LOG.trace("ret - wait");
137             } else {
138                 //propose lower version
139                 handleLowerVersionProposal(remoteVersion);
140             }
141         }
142     }
143
144     /**
145      * @param remoteVersion
146      * @throws Exception 
147      */
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());
154
155         if (proposedVersion != remoteVersion) {
156             LOG.trace("ret - sent+wait");
157         } else {
158             LOG.trace("ret - sent+OK");
159             postHandshake(proposedVersion, getNextXid());
160         }
161     }
162
163     /**
164      * @param elements
165      * @throws Exception 
166      */
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());
173         }
174         postHandshake(proposedVersion, getNextXid());
175         LOG.trace("ret - OK - versionBitmap");
176     }
177     
178     /**
179      * 
180      * @return
181      */
182     private Long getNextXid() {
183         activeXid += 1; 
184         return activeXid;
185     }
186
187     /**
188      * @param xid
189      */
190     private void setActiveXid(Long xid) {
191         this.activeXid = xid;
192     }
193     
194     /**
195      * @param remoteVersion
196      */
197     private void checkNegotiationStalling(Short remoteVersion) {
198         if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
199             throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
200         }
201         lastReceivedVersion = remoteVersion;
202     }
203
204     @Override
205     public GetFeaturesOutput getFeatures() {
206         return features;
207     }
208     
209     @Override
210     public Short getVersion() {
211         return version;
212     }
213
214     /**
215      * find common highest supported bitmap version
216      * @param list
217      * @return
218      */
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;
229                         break;
230                     }
231                 }
232             }
233             
234             if(null == supportedHighestVersion) {
235                 throw new IllegalArgumentException("no common version found in versionBitmap");
236             }
237         }
238
239         return supportedHighestVersion;
240     }
241
242     /**
243      * find supported version based on remoteVersion
244      * @param remoteVersion
245      * @return
246      */
247     protected short proposeNextVersion(short remoteVersion) {
248         Short proposal = null;
249         for (short offer : versionOrder) {
250             if (offer <= remoteVersion) {
251                 proposal = offer;
252                 break;
253             }
254         }
255         if (proposal == null) {
256             throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
257                     + remoteVersion);
258         }
259         return proposal;
260     }
261     
262     /**
263      * send hello reply without versionBitmap
264      * @param helloVersion
265      * @param helloXid
266      * @throws Exception 
267      */
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);
272         
273         LOG.debug("sending hello message: version{}, xid={}, version bitmap={}", 
274                 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
275         
276         try {
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.");
282             throw e;
283         }
284     }
285
286
287     /**
288      * after handshake set features, register to session
289      * @param proposedVersion
290      * @param xId
291      * @throws Exception 
292      */
293     protected void postHandshake(Short proposedVersion, Long xid) throws Exception {
294         // set version
295         long maxTimeout = 3000;
296         version = proposedVersion;
297
298         LOG.debug("version set: {}", proposedVersion);
299         // request features
300         GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
301         featuresBuilder.setVersion(version).setXid(xid);
302         LOG.debug("sending feature request for version={} and xid={}", version, xid);
303         Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
304                 .getFeatures(featuresBuilder.build());
305         LOG.debug("waiting for features");
306         try {
307             RpcResult<GetFeaturesOutput> rpcFeatures = 
308                     featuresFuture.get(maxTimeout, maxTimeoutUnit);
309             RpcUtil.smokeRpc(rpcFeatures);
310             
311             GetFeaturesOutput featureOutput =  rpcFeatures.getResult();
312             
313             LOG.debug("obtained features: datapathId={}",
314                     featureOutput.getDatapathId());
315             LOG.debug("obtained features: auxiliaryId={}",
316                     featureOutput.getAuxiliaryId());
317             LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}", 
318                     version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
319             
320             handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
321         } catch (TimeoutException e) {
322             // handshake failed
323             LOG.warn("issuing disconnect during handshake, reason: future expired", e);
324             connectionAdapter.disconnect();
325             throw e;
326         } catch (Exception e) {
327             // handshake failed
328             LOG.warn("issuing disconnect during handshake, reason - RPC: {}", e.getMessage(), e);
329             connectionAdapter.disconnect();
330             throw e;
331         }
332         
333         LOG.debug("postHandshake DONE");
334     }
335
336     @Override
337     public void setUseVersionBitmap(boolean useVersionBitmap) {
338         this.useVersionBitmap = useVersionBitmap;
339     }
340     
341     @Override
342     public void setErrorHandler(ErrorHandler errorHandler) {
343         this.errorHandler = errorHandler;
344     }
345 }