Merge "new LinkedList() mostly redundant with Arrays.asList()"
[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         sw.asyncSend(po);
294         logger.trace("Transmitted a frame of size: {}", data.length);
295     }
296
297     public void addNode(Node node, Set<Property> props) {
298         if (node == null) {
299             logger.debug("node is null!");
300             return;
301         }
302
303         long sid = (Long) node.getID();
304         ISwitch sw = controller.getSwitches().get(sid);
305         if (sw == null) {
306             logger.debug("sid: {} - sw is null!", sid);
307             return;
308         }
309         this.swID2ISwitch.put(sw.getId(), sw);
310     }
311
312     public void removeNode(Node node) {
313         if (node == null) {
314             logger.debug("node is null!");
315             return;
316         }
317
318         long sid = (Long) node.getID();
319         ISwitch sw = controller.getSwitches().get(sid);
320         if (sw == null) {
321             logger.debug("sid: {} - sw is null!", sid);
322             return;
323         }
324         this.swID2ISwitch.remove(sw.getId());
325     }
326
327     @Override
328     public void tagUpdated(String containerName, Node n, short oldTag,
329             short newTag, UpdateType t) {
330         // Do nothing
331     }
332
333     @Override
334     public void containerFlowUpdated(String containerName,
335             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
336         if (this.container2FlowSpecs == null) {
337             logger.error("container2FlowSpecs is NULL");
338             return;
339         }
340         List<ContainerFlow> fSpecs = this.container2FlowSpecs
341                 .get(containerName);
342         if (fSpecs == null) {
343             fSpecs = new CopyOnWriteArrayList<ContainerFlow>();
344         }
345         switch (t) {
346         case ADDED:
347             if (!fSpecs.contains(previousFlow)) {
348                 fSpecs.add(previousFlow);
349             }
350             break;
351         case REMOVED:
352             if (fSpecs.contains(previousFlow)) {
353                 fSpecs.remove(previousFlow);
354             }
355             break;
356         case CHANGED:
357             break;
358         }
359     }
360
361     @Override
362     public void nodeConnectorUpdated(String containerName, NodeConnector p,
363             UpdateType t) {
364         if (this.nc2Container == null) {
365             logger.error("nc2Container is NULL");
366             return;
367         }
368         List<String> containers = this.nc2Container.get(p);
369         if (containers == null) {
370             containers = new CopyOnWriteArrayList<String>();
371         }
372         boolean updateMap = false;
373         switch (t) {
374         case ADDED:
375             if (!containers.contains(containerName)) {
376                 containers.add(containerName);
377                 updateMap = true;
378             }
379             break;
380         case REMOVED:
381             if (containers.contains(containerName)) {
382                 containers.remove(containerName);
383                 updateMap = true;
384             }
385             break;
386         case CHANGED:
387             break;
388         }
389         if (updateMap) {
390             if (containers.isEmpty()) {
391                 // Do cleanup to reduce memory footprint if no
392                 // elements to be tracked
393                 this.nc2Container.remove(p);
394             } else {
395                 this.nc2Container.put(p, containers);
396             }
397         }
398     }
399
400     @Override
401     public void containerModeUpdated(UpdateType t) {
402         // do nothing
403     }
404
405     @Override
406     public void updateNode(Node node, UpdateType type, Set<Property> props) {
407         switch (type) {
408         case ADDED:
409             addNode(node, props);
410             break;
411         case REMOVED:
412             removeNode(node);
413             break;
414         default:
415             break;
416         }
417     }
418
419     @Override
420     public void updateNodeConnector(NodeConnector nodeConnector,
421             UpdateType type, Set<Property> props) {
422         // do nothing
423     }
424 }