Checkstyle enforcer
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / DataPacketMuxDemux.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
9 package org.opendaylight.controller.protocol_plugin.openflow.internal;
10
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17 import java.util.concurrent.CopyOnWriteArrayList;
18
19 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketListen;
20 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketMux;
21 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
22 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
23 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
24 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
25 import org.openflow.protocol.OFMessage;
26 import org.openflow.protocol.OFPacketIn;
27 import org.openflow.protocol.OFPacketOut;
28 import org.openflow.protocol.OFPort;
29 import org.openflow.protocol.OFType;
30 import org.openflow.protocol.action.OFAction;
31 import org.openflow.protocol.action.OFActionOutput;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import org.opendaylight.controller.sal.core.ConstructionException;
36 import org.opendaylight.controller.sal.core.ContainerFlow;
37 import org.opendaylight.controller.sal.core.IContainerListener;
38 import org.opendaylight.controller.sal.core.Node;
39 import org.opendaylight.controller.sal.core.NodeConnector;
40 import org.opendaylight.controller.sal.core.Property;
41 import org.opendaylight.controller.sal.core.UpdateType;
42 import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService;
43 import org.opendaylight.controller.sal.packet.PacketResult;
44 import org.opendaylight.controller.sal.packet.RawPacket;
45 import org.opendaylight.controller.sal.utils.GlobalConstants;
46 import org.opendaylight.controller.sal.utils.HexEncode;
47
48 public class DataPacketMuxDemux implements IContainerListener,
49         IMessageListener, IDataPacketMux, IInventoryShimExternalListener {
50     protected static final Logger logger = LoggerFactory
51             .getLogger(DataPacketMuxDemux.class);
52     private IController controller = null;
53     private ConcurrentMap<Long, ISwitch> swID2ISwitch = new ConcurrentHashMap<Long, ISwitch>();
54     // Gives a map between a Container and all the DataPacket listeners on SAL
55     private ConcurrentMap<String, IPluginOutDataPacketService> pluginOutDataPacketServices = new ConcurrentHashMap<String, IPluginOutDataPacketService>();
56     // Gives a map between a NodeConnector and the containers to which it
57     // belongs
58     private ConcurrentMap<NodeConnector, List<String>> nc2Container = new ConcurrentHashMap<NodeConnector, List<String>>();
59     // Gives a map between a Container and the FlowSpecs on it
60     private ConcurrentMap<String, List<ContainerFlow>> container2FlowSpecs = new ConcurrentHashMap<String, List<ContainerFlow>>();
61     // Track local data packet listener
62     private List<IDataPacketListen> iDataPacketListen = new CopyOnWriteArrayList<IDataPacketListen>();
63
64     void setIDataPacketListen(IDataPacketListen s) {
65         if (this.iDataPacketListen != null) {
66             if (!this.iDataPacketListen.contains(s)) {
67                 logger.debug("Added new IDataPacketListen");
68                 this.iDataPacketListen.add(s);
69             }
70         }
71     }
72
73     void unsetIDataPacketListen(IDataPacketListen s) {
74         if (this.iDataPacketListen != null) {
75             if (this.iDataPacketListen.contains(s)) {
76                 logger.debug("Removed IDataPacketListen");
77                 this.iDataPacketListen.remove(s);
78             }
79         }
80     }
81
82     void setPluginOutDataPacketService(Map<String, Object> props,
83             IPluginOutDataPacketService s) {
84         if (props == null) {
85             logger.error("Didn't receive the service properties");
86             return;
87         }
88         String containerName = (String) props.get("containerName");
89         if (containerName == null) {
90             logger.error("containerName not supplied");
91             return;
92         }
93         if (this.pluginOutDataPacketServices != null) {
94             // It's expected only one SAL per container as long as the
95             // replication is done in the SAL implementation toward
96             // the different APPS
97             this.pluginOutDataPacketServices.put(containerName, s);
98             logger.debug("New outService for container: {}", containerName);
99         }
100     }
101
102     void unsetPluginOutDataPacketService(Map<String, Object> props,
103             IPluginOutDataPacketService s) {
104         if (props == null) {
105             logger.error("Didn't receive the service properties");
106             return;
107         }
108         String containerName = (String) props.get("containerName");
109         if (containerName == null) {
110             logger.error("containerName not supplied");
111             return;
112         }
113         if (this.pluginOutDataPacketServices != null) {
114             this.pluginOutDataPacketServices.remove(containerName);
115             logger.debug("Removed outService for container: {}", containerName);
116         }
117     }
118
119     void setController(IController s) {
120         logger.debug("Controller provider set in DATAPACKET SERVICES");
121         this.controller = s;
122     }
123
124     void unsetController(IController s) {
125         if (this.controller == s) {
126             logger.debug("Controller provider UNset in DATAPACKET SERVICES");
127             this.controller = null;
128         }
129     }
130
131     /**
132      * Function called by the dependency manager when all the required
133      * dependencies are satisfied
134      *
135      */
136     void init() {
137         this.controller.addMessageListener(OFType.PACKET_IN, this);
138     }
139
140     /**
141      * Function called by the dependency manager when at least one dependency
142      * become unsatisfied or when the component is shutting down because for
143      * example bundle is being stopped.
144      *
145      */
146     void destroy() {
147         this.controller.removeMessageListener(OFType.PACKET_IN, this);
148
149         // Clear state that may need to be reused on component restart
150         this.pluginOutDataPacketServices.clear();
151         this.nc2Container.clear();
152         this.container2FlowSpecs.clear();
153         this.controller = null;
154         this.swID2ISwitch.clear();
155     }
156
157     @Override
158     public void receive(ISwitch sw, OFMessage msg) {
159         if (sw == null || msg == null
160                 || this.pluginOutDataPacketServices == null) {
161             // Something fishy, we cannot do anything
162             logger.debug(
163                     "sw: {} and/or msg: {} and/or pluginOutDataPacketServices: {} is null!",
164                     new Object[] { sw, msg, this.pluginOutDataPacketServices });
165             return;
166         }
167         if (msg instanceof OFPacketIn) {
168             OFPacketIn ofPacket = (OFPacketIn) msg;
169             Long ofSwitchID = Long.valueOf(sw.getId());
170             Short ofPortID = Short.valueOf(ofPacket.getInPort());
171
172             try {
173                 Node n = new Node(Node.NodeIDType.OPENFLOW, ofSwitchID);
174                 NodeConnector p = PortConverter.toNodeConnector(ofPortID, n);
175                 RawPacket dataPacket = new RawPacket(ofPacket.getPacketData());
176                 dataPacket.setIncomingNodeConnector(p);
177
178                 // Try to dispatch the packet locally, in here we will
179                 // pass the parsed packet simply because once the
180                 // packet infra is settled all the packets will passed
181                 // around as parsed and read-only
182                 for (int i = 0; i < this.iDataPacketListen.size(); i++) {
183                     IDataPacketListen s = this.iDataPacketListen.get(i);
184                     if (s.receiveDataPacket(dataPacket).equals(
185                             PacketResult.CONSUME)) {
186                         logger.trace("Consumed locally data packet");
187                         return;
188                     }
189                 }
190
191                 // Now dispatch the packet toward SAL at least for
192                 // default container, we need to revisit this in a general
193                 // slicing architecture refresh
194                 IPluginOutDataPacketService defaultOutService = this.pluginOutDataPacketServices
195                         .get(GlobalConstants.DEFAULT.toString());
196                 if (defaultOutService != null) {
197                     defaultOutService.receiveDataPacket(dataPacket);
198                     if (logger.isTraceEnabled()) {
199                       logger.trace(
200                               "Dispatched to apps a frame of size: {} on " +
201                               "container: {}: {}", new Object[] {
202                                     ofPacket.getPacketData().length,
203                                     GlobalConstants.DEFAULT.toString(),
204                                     HexEncode.bytesToHexString(dataPacket
205                                             .getPacketData()) });
206                     }
207                 }
208                 // Now check the mapping between nodeConnector and
209                 // Container and later on optimally filter based on
210                 // flowSpec
211                 List<String> containersRX = this.nc2Container.get(p);
212                 if (containersRX != null) {
213                     for (int i = 0; i < containersRX.size(); i++) {
214                         String container = containersRX.get(i);
215                         IPluginOutDataPacketService s = this.pluginOutDataPacketServices
216                                 .get(container);
217                         if (s != null) {
218                             // TODO add filtering on a per-flowSpec base
219                             s.receiveDataPacket(dataPacket);
220                             if (logger.isTraceEnabled()) {
221                               logger.trace(
222                                       "Dispatched to apps a frame of size: {}" +
223                                       " on container: {}: {}", new Object[] {
224                                             ofPacket.getPacketData().length,
225                                             container,
226                                             HexEncode.bytesToHexString(dataPacket
227                                                     .getPacketData()) });
228                             }
229                         }
230                     }
231                 }
232
233                 // This is supposed to be the catch all for all the
234                 // DataPacket hence we will assume it has been handled
235                 return;
236             } catch (ConstructionException cex) {
237             }
238
239             // If we reach this point something went wrong.
240             return;
241         } else {
242             // We don't care about non-data packets
243             return;
244         }
245     }
246
247     @Override
248     public void transmitDataPacket(RawPacket outPkt) {
249         // Sanity check area
250         if (outPkt == null) {
251             logger.debug("outPkt is null!");
252             return;
253         }
254
255         NodeConnector outPort = outPkt.getOutgoingNodeConnector();
256         if (outPort == null) {
257             logger.debug("outPort is null! outPkt: {}", outPkt);
258             return;
259         }
260
261         if (!outPort.getType().equals(
262                 NodeConnector.NodeConnectorIDType.OPENFLOW)) {
263             // The output Port is not of type OpenFlow
264             logger.debug("outPort is not OF Type! outPort: {}", outPort);
265             return;
266         }
267
268         Short port = (Short) outPort.getID();
269         Long swID = (Long) outPort.getNode().getID();
270         ISwitch sw = this.swID2ISwitch.get(swID);
271
272         if (sw == null) {
273             // If we cannot get the controller descriptor we cannot even
274             // send out the frame
275             logger.debug("swID: {} - sw is null!", swID);
276             return;
277         }
278
279         byte[] data = outPkt.getPacketData();
280         // build action
281         OFActionOutput action = new OFActionOutput().setPort(port);
282         // build packet out
283         OFPacketOut po = new OFPacketOut()
284                 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
285                 .setInPort(OFPort.OFPP_NONE)
286                 .setActions(Collections.singletonList((OFAction) action))
287                 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
288
289         po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength()
290                 + data.length);
291         po.setPacketData(data);
292
293         // send PACKET_OUT at high priority
294         sw.asyncFastSend(po);
295         logger.trace("Transmitted a frame of size: {}", data.length);
296     }
297
298     public void addNode(Node node, Set<Property> props) {
299         if (node == null) {
300             logger.debug("node is null!");
301             return;
302         }
303
304         long sid = (Long) node.getID();
305         ISwitch sw = controller.getSwitches().get(sid);
306         if (sw == null) {
307             logger.debug("sid: {} - sw is null!", sid);
308             return;
309         }
310         this.swID2ISwitch.put(sw.getId(), sw);
311     }
312
313     public void removeNode(Node node) {
314         if (node == null) {
315             logger.debug("node is null!");
316             return;
317         }
318
319         long sid = (Long) node.getID();
320         ISwitch sw = controller.getSwitches().get(sid);
321         if (sw == null) {
322             logger.debug("sid: {} - sw is null!", sid);
323             return;
324         }
325         this.swID2ISwitch.remove(sw.getId());
326     }
327
328     @Override
329     public void tagUpdated(String containerName, Node n, short oldTag,
330             short newTag, UpdateType t) {
331         // Do nothing
332     }
333
334     @Override
335     public void containerFlowUpdated(String containerName,
336             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
337         if (this.container2FlowSpecs == null) {
338             logger.error("container2FlowSpecs is NULL");
339             return;
340         }
341         List<ContainerFlow> fSpecs = this.container2FlowSpecs
342                 .get(containerName);
343         if (fSpecs == null) {
344             fSpecs = new CopyOnWriteArrayList<ContainerFlow>();
345         }
346         switch (t) {
347         case ADDED:
348             if (!fSpecs.contains(previousFlow)) {
349                 fSpecs.add(previousFlow);
350             }
351             break;
352         case REMOVED:
353             if (fSpecs.contains(previousFlow)) {
354                 fSpecs.remove(previousFlow);
355             }
356             break;
357         case CHANGED:
358             break;
359         }
360     }
361
362     @Override
363     public void nodeConnectorUpdated(String containerName, NodeConnector p,
364             UpdateType t) {
365         if (this.nc2Container == null) {
366             logger.error("nc2Container is NULL");
367             return;
368         }
369         List<String> containers = this.nc2Container.get(p);
370         if (containers == null) {
371             containers = new CopyOnWriteArrayList<String>();
372         }
373         boolean updateMap = false;
374         switch (t) {
375         case ADDED:
376             if (!containers.contains(containerName)) {
377                 containers.add(containerName);
378                 updateMap = true;
379             }
380             break;
381         case REMOVED:
382             if (containers.contains(containerName)) {
383                 containers.remove(containerName);
384                 updateMap = true;
385             }
386             break;
387         case CHANGED:
388             break;
389         }
390         if (updateMap) {
391             if (containers.isEmpty()) {
392                 // Do cleanup to reduce memory footprint if no
393                 // elements to be tracked
394                 this.nc2Container.remove(p);
395             } else {
396                 this.nc2Container.put(p, containers);
397             }
398         }
399     }
400
401     @Override
402     public void containerModeUpdated(UpdateType t) {
403         // do nothing
404     }
405
406     @Override
407     public void updateNode(Node node, UpdateType type, Set<Property> props) {
408         switch (type) {
409         case ADDED:
410             addNode(node, props);
411             break;
412         case REMOVED:
413             removeNode(node);
414             break;
415         default:
416             break;
417         }
418     }
419
420     @Override
421     public void updateNodeConnector(NodeConnector nodeConnector,
422             UpdateType type, Set<Property> props) {
423         // do nothing
424     }
425 }