Merge "Bug 1029: Remove dead code: clustered-data-store"
[controller.git] / opendaylight / md-sal / samples / l2switch / implementation / src / main / java / org / opendaylight / controller / sample / l2switch / md / packet / PacketHandler.java
1 /*
2  * Copyright (c) 2014 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.controller.sample.l2switch.md.packet;
9
10 import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
11 import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
12 import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
13 import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
14 import org.opendaylight.controller.sal.packet.Ethernet;
15 import org.opendaylight.controller.sal.packet.LLDP;
16 import org.opendaylight.controller.sal.packet.LinkEncap;
17 import org.opendaylight.controller.sal.packet.Packet;
18 import org.opendaylight.controller.sal.packet.RawPacket;
19 import org.opendaylight.controller.sal.utils.HexEncode;
20 import org.opendaylight.controller.sal.utils.NetUtils;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import java.util.HashMap;
37 import java.util.List;
38
39 /**
40  * PacketHandler examines Ethernet packets to find L2Addresses (mac, nodeConnector) pairings
41  * of the sender and learns them.
42  * It also forwards the data packets appropriately dependending upon whether it knows about the
43  * target or not.
44  */
45 public class PacketHandler implements PacketProcessingListener {
46
47   private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
48
49   private PacketProcessingService packetProcessingService;
50   private AddressTracker addressTracker;
51   private FlowWriterService flowWriterService;
52   private InventoryService inventoryService;
53
54   public void setAddressTracker(AddressTracker addressTracker) {
55     this.addressTracker = addressTracker;
56   }
57
58   public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
59     this.packetProcessingService = packetProcessingService;
60   }
61
62   public void setFlowWriterService(FlowWriterService flowWriterService) {
63     this.flowWriterService = flowWriterService;
64   }
65
66   public void setInventoryService(InventoryService inventoryService) {
67     this.inventoryService = inventoryService;
68   }
69
70   /**
71    * The handler function for all incoming packets.
72    * @param packetReceived  The incoming packet.
73    */
74   @Override
75   public void onPacketReceived(PacketReceived packetReceived) {
76
77     if(packetReceived == null) return;
78
79     try {
80       byte[] payload = packetReceived.getPayload();
81       RawPacket rawPacket = new RawPacket(payload);
82       NodeConnectorRef ingress = packetReceived.getIngress();
83
84       Packet packet = decodeDataPacket(rawPacket);
85
86       if(!(packet instanceof Ethernet)) return;
87
88       handleEthernetPacket(packet, ingress);
89
90     } catch(Exception e) {
91       _logger.error("Failed to handle packet {}", packetReceived, e);
92     }
93   }
94
95   /**
96    * The handler function for Ethernet packets.
97    * @param packet  The incoming Ethernet packet.
98    * @param ingress  The NodeConnector where the Ethernet packet came from.
99    */
100   private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
101     byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
102     byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
103
104     if (srcMac  == null || srcMac.length  == 0) return;
105
106     Object enclosedPacket = packet.getPayload();
107
108     if (enclosedPacket instanceof LLDP)
109       return; // LLDP packets are handled by OpenFlowPlugin
110
111     // get l2address by src mac
112     // if unknown, add l2address
113     MacAddress srcMacAddress = toMacAddress(srcMac);
114     L2Address src = addressTracker.getAddress(srcMacAddress);
115     boolean isSrcKnown = (src != null);
116     if (!isSrcKnown) {
117       addressTracker.addAddress(srcMacAddress, ingress);
118     }
119
120     // get host by dest mac
121     // if known set dest known to true
122     MacAddress destMacAddress = toMacAddress(destMac);
123     L2Address dest = addressTracker.getAddress(destMacAddress);
124     boolean isDestKnown = (dest != null);
125
126     byte[] payload = packet.getRawPayload();
127     // if (src and dest known)
128     // sendpacket to dest and add src<->dest flow
129     if(isSrcKnown & isDestKnown) {
130       flowWriterService.addMacToMacFlowsUsingShortestPath(srcMacAddress, src.getNodeConnectorRef(),
131           destMacAddress, dest.getNodeConnectorRef());
132       sendPacketOut(payload, getControllerNodeConnector(dest.getNodeConnectorRef()), dest.getNodeConnectorRef());
133     } else {
134       // if (dest unknown)
135       // sendpacket to external links minus ingress
136       floodExternalPorts(payload, ingress);
137     }
138   }
139
140   /**
141    * Floods the specified payload on external ports, which are ports not connected to switches.
142    * @param payload  The payload to be flooded.
143    * @param ingress  The NodeConnector where the payload came from.
144    */
145   private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
146     List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
147     externalPorts.remove(ingress);
148
149     for (NodeConnectorRef egress : externalPorts) {
150       sendPacketOut(payload, getControllerNodeConnector(egress), egress);
151     }
152   }
153
154   /**
155    * Sends the specified packet on the specified port.
156    * @param payload  The payload to be sent.
157    * @param ingress  The NodeConnector where the payload came from.
158    * @param egress  The NodeConnector where the payload will go.
159    */
160   private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
161     if (ingress == null || egress == null)  return;
162     InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
163     TransmitPacketInput input = new TransmitPacketInputBuilder() //
164         .setPayload(payload) //
165         .setNode(new NodeRef(egressNodePath)) //
166         .setEgress(egress) //
167         .setIngress(ingress) //
168         .build();
169     packetProcessingService.transmitPacket(input);
170   }
171
172   /**
173    * Decodes an incoming packet.
174    * @param raw  The raw packet to be decoded.
175    * @return  The decoded form of the raw packet.
176    */
177   private Packet decodeDataPacket(RawPacket raw) {
178     if(raw == null) {
179       return null;
180     }
181     byte[] data = raw.getPacketData();
182     if(data.length <= 0) {
183       return null;
184     }
185     if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
186       Ethernet res = new Ethernet();
187       try {
188         res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
189         res.setRawPayload(raw.getPacketData());
190       } catch(Exception e) {
191         _logger.warn("Failed to decode packet: {}", e.getMessage());
192       }
193       return res;
194     }
195     return null;
196   }
197
198   /**
199    * Creates a MacAddress object out of a byte array.
200    * @param dataLinkAddress  The byte-array form of a MacAddress
201    * @return  MacAddress of the specified dataLinkAddress.
202    */
203   private MacAddress toMacAddress(byte[] dataLinkAddress) {
204     return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
205   }
206
207   /**
208    * Gets the NodeConnector that connects the controller & switch for a specified switch port/node connector.
209    * @param nodeConnectorRef  The nodeConnector of a switch.
210    * @return  The NodeConnector that that connects the controller & switch.
211    */
212   private NodeConnectorRef getControllerNodeConnector(NodeConnectorRef nodeConnectorRef) {
213     NodeConnectorRef controllerSwitchNodeConnector = null;
214     HashMap<String, NodeConnectorRef> controllerSwitchConnectors = inventoryService.getControllerSwitchConnectors();
215     InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(nodeConnectorRef.getValue());
216     if (nodePath != null) {
217       NodeKey nodeKey = InstanceIdentifierUtils.getNodeKey(nodePath);
218       if (nodeKey != null) {
219         controllerSwitchNodeConnector = controllerSwitchConnectors.get(nodeKey.getId().getValue());
220       }
221     }
222     return controllerSwitchNodeConnector;
223   }
224 }