2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sample.l2switch.md.packet;
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;
36 import java.util.HashMap;
37 import java.util.List;
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
45 public class PacketHandler implements PacketProcessingListener {
47 private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
49 private PacketProcessingService packetProcessingService;
50 private AddressTracker addressTracker;
51 private FlowWriterService flowWriterService;
52 private InventoryService inventoryService;
54 public void setAddressTracker(AddressTracker addressTracker) {
55 this.addressTracker = addressTracker;
58 public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
59 this.packetProcessingService = packetProcessingService;
62 public void setFlowWriterService(FlowWriterService flowWriterService) {
63 this.flowWriterService = flowWriterService;
66 public void setInventoryService(InventoryService inventoryService) {
67 this.inventoryService = inventoryService;
71 * The handler function for all incoming packets.
72 * @param packetReceived The incoming packet.
75 public void onPacketReceived(PacketReceived packetReceived) {
77 if(packetReceived == null) return;
80 byte[] payload = packetReceived.getPayload();
81 RawPacket rawPacket = new RawPacket(payload);
82 NodeConnectorRef ingress = packetReceived.getIngress();
84 Packet packet = decodeDataPacket(rawPacket);
86 if(!(packet instanceof Ethernet)) return;
88 handleEthernetPacket(packet, ingress);
90 } catch(Exception e) {
91 _logger.error("Failed to handle packet {}", packetReceived, e);
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.
100 private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
101 byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
102 byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
104 if (srcMac == null || srcMac.length == 0) return;
106 Object enclosedPacket = packet.getPayload();
108 if (enclosedPacket instanceof LLDP)
109 return; // LLDP packets are handled by OpenFlowPlugin
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);
117 addressTracker.addAddress(srcMacAddress, ingress);
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);
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());
135 // sendpacket to external links minus ingress
136 floodExternalPorts(payload, ingress);
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.
145 private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
146 List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
147 externalPorts.remove(ingress);
149 for (NodeConnectorRef egress : externalPorts) {
150 sendPacketOut(payload, getControllerNodeConnector(egress), egress);
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.
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) //
169 packetProcessingService.transmitPacket(input);
173 * Decodes an incoming packet.
174 * @param raw The raw packet to be decoded.
175 * @return The decoded form of the raw packet.
177 private Packet decodeDataPacket(RawPacket raw) {
181 byte[] data = raw.getPacketData();
182 if(data.length <= 0) {
185 if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
186 Ethernet res = new Ethernet();
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());
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.
203 private MacAddress toMacAddress(byte[] dataLinkAddress) {
204 return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
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.
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());
222 return controllerSwitchNodeConnector;