a8ebcb068b9399f0fee0c6b8dd6370e3b810b4d2
[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                     logger.trace(
199                             "Dispatched to apps a frame of size: {} on container: {}: {}",
200                             new Object[] {
201                                     ofPacket.getPacketData().length,
202                                     GlobalConstants.DEFAULT.toString(),
203                                     HexEncode.bytesToHexString(dataPacket
204                                             .getPacketData()) });
205                 }
206                 // Now check the mapping between nodeConnector and
207                 // Container and later on optimally filter based on
208                 // flowSpec
209                 List<String> containersRX = this.nc2Container.get(p);
210                 if (containersRX != null) {
211                     for (int i = 0; i < containersRX.size(); i++) {
212                         String container = containersRX.get(i);
213                         IPluginOutDataPacketService s = this.pluginOutDataPacketServices
214                                 .get(container);
215                         if (s != null) {
216                             // TODO add filtering on a per-flowSpec base
217                             s.receiveDataPacket(dataPacket);
218                             logger.trace(
219                                     "Dispatched to apps a frame of size: {} on container: {}: {}",
220                                     new Object[] {
221                                             ofPacket.getPacketData().length,
222                                             GlobalConstants.DEFAULT.toString(),
223                                             HexEncode
224                                                     .bytesToHexString(dataPacket
225                                                             .getPacketData()) });
226
227                         }
228                     }
229                 }
230
231                 // This is supposed to be the catch all for all the
232                 // DataPacket hence we will assume it has been handled
233                 return;
234             } catch (ConstructionException cex) {
235             }
236
237             // If we reach this point something went wrong.
238             return;
239         } else {
240             // We don't care about non-data packets
241             return;
242         }
243     }
244
245     @Override
246     public void transmitDataPacket(RawPacket outPkt) {
247         // Sanity check area
248         if (outPkt == null) {
249             logger.debug("outPkt is null!");
250             return;
251         }
252
253         NodeConnector outPort = outPkt.getOutgoingNodeConnector();
254         if (outPort == null) {
255             logger.debug("outPort is null! outPkt: {}", outPkt);
256             return;
257         }
258
259         if (!outPort.getType().equals(
260                 NodeConnector.NodeConnectorIDType.OPENFLOW)) {
261             // The output Port is not of type OpenFlow
262             logger.debug("outPort is not OF Type! outPort: {}", outPort);
263             return;
264         }
265
266         Short port = (Short) outPort.getID();
267         Long swID = (Long) outPort.getNode().getID();
268         ISwitch sw = this.swID2ISwitch.get(swID);
269
270         if (sw == null) {
271             // If we cannot get the controller descriptor we cannot even
272             // send out the frame
273             logger.debug("swID: {} - sw is null!", swID);
274             return;
275         }
276
277         byte[] data = outPkt.getPacketData();
278         // build action
279         OFActionOutput action = new OFActionOutput().setPort(port);
280         // build packet out
281         OFPacketOut po = new OFPacketOut()
282                 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
283                 .setInPort(OFPort.OFPP_NONE)
284                 .setActions(Collections.singletonList((OFAction) action))
285                 .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
286
287         po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength()
288                 + data.length);
289         po.setPacketData(data);
290
291         sw.asyncSend(po);
292         logger.trace("Transmitted a frame of size: {}", data.length);
293     }
294
295     public void addNode(Node node, Set<Property> props) {
296         if (node == null) {
297             logger.debug("node is null!");
298             return;
299         }
300
301         long sid = (Long) node.getID();
302         ISwitch sw = controller.getSwitches().get(sid);
303         if (sw == null) {
304             logger.debug("sid: {} - sw is null!", sid);
305             return;
306         }
307         this.swID2ISwitch.put(sw.getId(), sw);
308     }
309
310     public void removeNode(Node node) {
311         if (node == null) {
312             logger.debug("node is null!");
313             return;
314         }
315
316         long sid = (Long) node.getID();
317         ISwitch sw = controller.getSwitches().get(sid);
318         if (sw == null) {
319             logger.debug("sid: {} - sw is null!", sid);
320             return;
321         }
322         this.swID2ISwitch.remove(sw.getId());
323     }
324
325     @Override
326     public void tagUpdated(String containerName, Node n, short oldTag,
327             short newTag, UpdateType t) {
328         // Do nothing
329     }
330
331     @Override
332     public void containerFlowUpdated(String containerName,
333             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
334         if (this.container2FlowSpecs == null) {
335             logger.error("container2FlowSpecs is NULL");
336             return;
337         }
338         List<ContainerFlow> fSpecs = this.container2FlowSpecs
339                 .get(containerName);
340         if (fSpecs == null) {
341             fSpecs = new CopyOnWriteArrayList<ContainerFlow>();
342         }
343         switch (t) {
344         case ADDED:
345             if (!fSpecs.contains(previousFlow)) {
346                 fSpecs.add(previousFlow);
347             }
348             break;
349         case REMOVED:
350             if (fSpecs.contains(previousFlow)) {
351                 fSpecs.remove(previousFlow);
352             }
353             break;
354         case CHANGED:
355             break;
356         }
357     }
358
359     @Override
360     public void nodeConnectorUpdated(String containerName, NodeConnector p,
361             UpdateType t) {
362         if (this.nc2Container == null) {
363             logger.error("nc2Container is NULL");
364             return;
365         }
366         List<String> containers = this.nc2Container.get(p);
367         if (containers == null) {
368             containers = new CopyOnWriteArrayList<String>();
369         }
370         boolean updateMap = false;
371         switch (t) {
372         case ADDED:
373             if (!containers.contains(containerName)) {
374                 containers.add(containerName);
375                 updateMap = true;
376             }
377             break;
378         case REMOVED:
379             if (containers.contains(containerName)) {
380                 containers.remove(containerName);
381                 updateMap = true;
382             }
383             break;
384         case CHANGED:
385             break;
386         }
387         if (updateMap) {
388             if (containers.isEmpty()) {
389                 // Do cleanup to reduce memory footprint if no
390                 // elements to be tracked
391                 this.nc2Container.remove(p);
392             } else {
393                 this.nc2Container.put(p, containers);
394             }
395         }
396     }
397
398     @Override
399     public void containerModeUpdated(UpdateType t) {
400         // do nothing
401     }
402
403     @Override
404     public void updateNode(Node node, UpdateType type, Set<Property> props) {
405         switch (type) {
406         case ADDED:
407             addNode(node, props);
408             break;
409         case REMOVED:
410             removeNode(node);
411             break;
412         default:
413             break;
414         }
415     }
416
417     @Override
418     public void updateNodeConnector(NodeConnector nodeConnector,
419             UpdateType type, Set<Property> props) {
420         // do nothing
421     }
422 }