Add per-protocol flows, and track statistics for each protocol. Does not have the...
[affinity.git] / l2agent / src / main / java / org / opendaylight / affinity / l2agent / L2Agent.java
index 0d52c1ce66623c42dee194f94604a14f2685d3e4..b3b285b415bb30fd732de8f6bbae8a645ca90817 100644 (file)
  */
 package org.opendaylight.affinity.l2agent;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
+import java.util.Arrays;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.lang.String;
-import java.util.Map;
 import java.util.HashMap;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.opendaylight.controller.sal.action.Action;
 import org.opendaylight.controller.sal.action.Output;
-import org.opendaylight.controller.sal.action.Flood;
 import org.opendaylight.controller.sal.core.ConstructionException;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
-import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
 import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.match.MatchField;
-import org.opendaylight.controller.sal.packet.ARP;
 import org.opendaylight.controller.sal.packet.BitBufferHelper;
 import org.opendaylight.controller.sal.packet.Ethernet;
 import org.opendaylight.controller.sal.packet.IDataPacketService;
@@ -52,19 +43,16 @@ import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.PacketResult;
 import org.opendaylight.controller.sal.packet.RawPacket;
 import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.Status;
-import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
-import org.opendaylight.controller.switchmanager.Subnet;
 
 public class L2Agent implements IListenDataPacket, IfL2Agent {
-    private static final Logger logger = LoggerFactory
-            .getLogger(L2Agent.class);
+    private static final Logger logger = LoggerFactory.getLogger(L2Agent.class);
     private ISwitchManager switchManager = null;
     private IFlowProgrammerService programmer = null;
     private IDataPacketService dataPacketService = null;
     private Map<Node, Map<Long, NodeConnector>> mac_to_ports = new HashMap<Node, Map<Long, NodeConnector>>();
-    private String function = "switch";
 
     void setDataPacketService(IDataPacketService s) {
         this.dataPacketService = s;
@@ -102,7 +90,6 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
     /**
      * Function called by the dependency manager when all the required
      * dependencies are satisfied
-     *
      */
     void init() {
         logger.info("Initialized");
@@ -112,7 +99,6 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
      * Function called by the dependency manager when at least one
      * dependency become unsatisfied or when the component is shutting
      * down because for example bundle is being stopped.
-     *
      */
     void destroy() {
     }
@@ -121,7 +107,6 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
      * Function called by dependency manager after "init ()" is called
      * and after the services provided by the class are registered in
      * the service registry
-     *
      */
     void start() {
         logger.info("Started");
@@ -131,7 +116,6 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
      * Function called by the dependency manager before the services
      * exported by the component are unregistered, this will be
      * followed by a "destroy ()" calls
-     *
      */
     void stop() {
         logger.info("Stopped");
@@ -141,17 +125,7 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
         NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
         Node incoming_node = incoming_connector.getNode();
 
-        Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
-        if (formattedPak instanceof Ethernet) {
-            byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
-            byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
-
-            long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
-            long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
-        }
-
-        Set<NodeConnector> nodeConnectors =
-                this.switchManager.getUpNodeConnectors(incoming_node);
+        Set<NodeConnector> nodeConnectors = this.switchManager.getUpNodeConnectors(incoming_node);
 
         for (NodeConnector p : nodeConnectors) {
             if (!p.equals(incoming_connector)) {
@@ -159,20 +133,33 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
                     RawPacket destPkt = new RawPacket(inPkt);
                     destPkt.setOutgoingNodeConnector(p);
                     this.dataPacketService.transmitDataPacket(destPkt);
-                } catch (ConstructionException e2) {
+                } catch (ConstructionException e) {
                     continue;
                 }
             }
         }
     }
 
+    private void installFlow(Match match, List<Action> actions, Node incoming_node, short priority) {
+        Flow f = new Flow(match, actions);
+        f.setPriority(priority);
+
+        // Modify the flow on the network node
+        Status status = programmer.addFlow(incoming_node, f);
+        if (!status.isSuccess()) {
+            logger.warn("SDN Plugin failed to program the flow: {}. The failure is: {}",
+                        f, status.getDescription());
+            return;
+        }
+        logger.info("Installed flow {} in node {}", f, incoming_node);
+    }
+
     @Override
     public PacketResult receiveDataPacket(RawPacket inPkt) {
         if (inPkt == null) {
             return PacketResult.IGNORED;
         }
-        logger.trace("Received a frame of size: {}",
-                        inPkt.getPacketData().length);
+        logger.trace("Received a frame of size: {}", inPkt.getPacketData().length);
 
         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
         NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
@@ -182,63 +169,54 @@ public class L2Agent implements IListenDataPacket, IfL2Agent {
             byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
             byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
 
-            // Hub implementation
-            if (function.equals("hub")) {
-                floodPacket(inPkt);
-                return PacketResult.CONSUME;
-            }
-
-            // Switch
-            else {
-                long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
-                long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
-
-                Match match = new Match();
-                match.setField( new MatchField(MatchType.IN_PORT, incoming_connector) );
-                match.setField( new MatchField(MatchType.DL_DST, dstMAC.clone()) );
-
-                // Set up the mapping: switch -> src MAC address -> incoming port
-                if (this.mac_to_ports.get(incoming_node) == null) {
-                    this.mac_to_ports.put(incoming_node, new HashMap<Long, NodeConnector>());
-                    logger.info("Added new node = {} to mac_to_ports", incoming_node);
-                }
-
-                // Only replace if we don't know the mapping.  This
-                // saves us from over-writing correct mappings with
-                // incorrect ones we get during flooding.
-                //
-                // TODO: this should never happen..
-                if (this.mac_to_ports.get(incoming_node).get(srcMAC_val) == null) {
-                    this.mac_to_ports.get(incoming_node).put(srcMAC_val, incoming_connector);
-                    logger.info("Added new learned MAC = {} on incoming connector = {} to mac_to_ports", srcMAC_val, incoming_connector);
-                }
-
-                NodeConnector dst_connector = this.mac_to_ports.get(incoming_node).get(dstMAC_val);
-
-                // Do I know the destination MAC?
-                if (dst_connector != null) {
-
-                    List<Action> actions = new ArrayList<Action>();
-                    actions.add(new Output(dst_connector));
+            long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
+            long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
 
-                    Flow f = new Flow(match, actions);
+            // Set up the mapping: switch -> src MAC address -> incoming port
+            if (this.mac_to_ports.get(incoming_node) == null) {
+                this.mac_to_ports.put(incoming_node, new HashMap<Long, NodeConnector>());
+                logger.info("Added new node = {} to mac_to_ports", incoming_node);
+            }
 
-                    // Modify the flow on the network node
-                    Status status = programmer.addFlow(incoming_node, f);
-                    if (!status.isSuccess()) {
-                        logger.warn(
-                                "SDN Plugin failed to program the flow: {}. The failure is: {}",
-                                f, status.getDescription());
-                        return PacketResult.IGNORED;
-                    }
-                    logger.info("Installed flow {} in node {}", f, incoming_node);
+            // Only replace if we don't know the mapping.  This
+            // saves us from over-writing correct mappings with
+            // incorrect ones we get during flooding.
+            //
+            // TODO: this should never happen..
+            if (this.mac_to_ports.get(incoming_node).get(srcMAC_val) == null) {
+                this.mac_to_ports.get(incoming_node).put(srcMAC_val, incoming_connector);
+                logger.info("Added new learned MAC = {} on incoming connector = {} to mac_to_ports", srcMAC_val, incoming_connector);
+            }
 
-                    // TODO: Testing.  What do the flows on this node look like now?
-                    //                    new FlowStatisticsConverter(flows).getFlowOnNodeList(node)
-                }
-                else {
-                    floodPacket(inPkt);
+            NodeConnector dst_connector = this.mac_to_ports.get(incoming_node).get(dstMAC_val);
+
+            // Do I know the destination MAC?
+            if (dst_connector != null) {
+                
+                List<Action> actions = new ArrayList<Action>();
+                actions.add(new Output(dst_connector));
+
+                // Install a high(er)-priority flow for each of the three protocols we care about
+                List<Byte> protocolList = Arrays.asList(IPProtocols.ICMP.byteValue(), IPProtocols.TCP.byteValue(), IPProtocols.UDP.byteValue());
+                for (byte protocol : protocolList) {
+                    Match match = new Match();
+                    match.setField(new MatchField(MatchType.IN_PORT, incoming_connector));
+                    match.setField(new MatchField(MatchType.DL_DST, dstMAC.clone()));
+                    // To get the actual protocol used in this packet, if it's an IPv4 packet:
+                    // ((IPv4) formattedPak.getPayload()).getProtocol();
+                    match.setField(new MatchField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue()));
+                    match.setField(new MatchField(MatchType.NW_PROTO, protocol));
+                    installFlow(match, actions, incoming_node, (short) 2);
                 }
+                
+                // Install a low-priority flow to catch everything else
+                Match match = new Match();
+                match.setField(new MatchField(MatchType.IN_PORT, incoming_connector));
+                match.setField(new MatchField(MatchType.DL_DST, dstMAC.clone()));
+                installFlow(match, actions, incoming_node, (short) 1);
+            }
+            else {
+                floodPacket(inPkt);
             }
         }
         return PacketResult.IGNORED;