2 * Copyright (c) 2013 Plexxi, Inc. and others. All rights reserved.
6 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
8 * This program and the accompanying materials are made available under the
9 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
10 * and is available at http://www.eclipse.org/legal/epl-v10.html
14 * Adapted from tutorial L2 forwarding demo (http://archive.openflow.org/).
16 package org.opendaylight.affinity.l2agent;
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.List;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashSet;
25 import java.lang.String;
27 import java.util.HashMap;
28 import java.util.Timer;
29 import java.util.TimerTask;
30 import java.util.concurrent.ConcurrentHashMap;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 import org.opendaylight.controller.sal.action.Action;
36 import org.opendaylight.controller.sal.action.Output;
37 import org.opendaylight.controller.sal.action.Flood;
38 import org.opendaylight.controller.sal.core.ConstructionException;
39 import org.opendaylight.controller.sal.core.Node;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
42 import org.opendaylight.controller.sal.flowprogrammer.Flow;
43 import org.opendaylight.controller.sal.match.Match;
44 import org.opendaylight.controller.sal.match.MatchType;
45 import org.opendaylight.controller.sal.match.MatchField;
46 import org.opendaylight.controller.sal.packet.ARP;
47 import org.opendaylight.controller.sal.packet.BitBufferHelper;
48 import org.opendaylight.controller.sal.packet.Ethernet;
49 import org.opendaylight.controller.sal.packet.IDataPacketService;
50 import org.opendaylight.controller.sal.packet.IListenDataPacket;
51 import org.opendaylight.controller.sal.packet.Packet;
52 import org.opendaylight.controller.sal.packet.PacketResult;
53 import org.opendaylight.controller.sal.packet.RawPacket;
54 import org.opendaylight.controller.sal.utils.EtherTypes;
55 import org.opendaylight.controller.sal.utils.Status;
56 import org.opendaylight.controller.sal.utils.NetUtils;
57 import org.opendaylight.controller.switchmanager.ISwitchManager;
58 import org.opendaylight.controller.switchmanager.Subnet;
60 public class L2Agent implements IListenDataPacket {
61 private static final Logger logger = LoggerFactory
62 .getLogger(L2Agent.class);
63 private ISwitchManager switchManager = null;
64 private IFlowProgrammerService programmer = null;
65 private IDataPacketService dataPacketService = null;
66 private Map<Node, Map<Long, NodeConnector>> mac_to_ports = new HashMap<Node, Map<Long, NodeConnector>>();
67 private String function = "switch";
69 void setDataPacketService(IDataPacketService s) {
70 this.dataPacketService = s;
73 void unsetDataPacketService(IDataPacketService s) {
74 if (this.dataPacketService == s) {
75 this.dataPacketService = null;
79 public void setFlowProgrammerService(IFlowProgrammerService s)
84 public void unsetFlowProgrammerService(IFlowProgrammerService s) {
85 if (this.programmer == s) {
86 this.programmer = null;
90 void setSwitchManager(ISwitchManager s) {
91 logger.debug("SwitchManager set");
92 this.switchManager = s;
95 void unsetSwitchManager(ISwitchManager s) {
96 if (this.switchManager == s) {
97 logger.debug("SwitchManager removed!");
98 this.switchManager = null;
103 * Function called by the dependency manager when all the required
104 * dependencies are satisfied
108 logger.info("Initialized");
112 * Function called by the dependency manager when at least one
113 * dependency become unsatisfied or when the component is shutting
114 * down because for example bundle is being stopped.
121 * Function called by dependency manager after "init ()" is called
122 * and after the services provided by the class are registered in
123 * the service registry
127 logger.info("Started");
131 * Function called by the dependency manager before the services
132 * exported by the component are unregistered, this will be
133 * followed by a "destroy ()" calls
137 logger.info("Stopped");
140 private void floodPacket(RawPacket inPkt) {
141 NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
142 Node incoming_node = incoming_connector.getNode();
144 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
145 if (formattedPak instanceof Ethernet) {
146 byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
147 byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
149 long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
150 long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
153 Set<NodeConnector> nodeConnectors =
154 this.switchManager.getUpNodeConnectors(incoming_node);
156 for (NodeConnector p : nodeConnectors) {
157 if (!p.equals(incoming_connector)) {
159 RawPacket destPkt = new RawPacket(inPkt);
160 destPkt.setOutgoingNodeConnector(p);
161 this.dataPacketService.transmitDataPacket(destPkt);
162 } catch (ConstructionException e2) {
170 public PacketResult receiveDataPacket(RawPacket inPkt) {
172 return PacketResult.IGNORED;
174 logger.trace("Received a frame of size: {}",
175 inPkt.getPacketData().length);
177 Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
178 NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
179 Node incoming_node = incoming_connector.getNode();
181 if (formattedPak instanceof Ethernet) {
182 byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
183 byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
185 // Hub implementation
186 if (function.equals("hub")) {
188 return PacketResult.CONSUME;
193 long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
194 long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
196 Match match = new Match();
197 match.setField( new MatchField(MatchType.IN_PORT, incoming_connector) );
198 match.setField( new MatchField(MatchType.DL_DST, dstMAC.clone()) );
200 // Set up the mapping: switch -> src MAC address -> incoming port
201 if (this.mac_to_ports.get(incoming_node) == null) {
202 this.mac_to_ports.put(incoming_node, new HashMap<Long, NodeConnector>());
205 // Only replace if we don't know the mapping. This
206 // saves us from over-writing correct mappings with
207 // incorrect ones we get during flooding.
209 // TODO: this should never happen..
210 if (this.mac_to_ports.get(incoming_node).get(srcMAC_val) == null) {
211 this.mac_to_ports.get(incoming_node).put(srcMAC_val, incoming_connector);
214 NodeConnector dst_connector = this.mac_to_ports.get(incoming_node).get(dstMAC_val);
216 // Do I know the destination MAC?
217 if (dst_connector != null) {
219 List<Action> actions = new ArrayList<Action>();
220 actions.add(new Output(dst_connector));
222 Flow f = new Flow(match, actions);
224 // Modify the flow on the network node
225 Status status = programmer.addFlow(incoming_node, f);
226 if (!status.isSuccess()) {
228 "SDN Plugin failed to program the flow: {}. The failure is: {}",
229 f, status.getDescription());
230 return PacketResult.IGNORED;
232 logger.info("Installed flow {} in node {}", f, incoming_node);
234 // TODO: Testing. What do the flows on this node look like now?
235 // new FlowStatisticsConverter(flows).getFlowOnNodeList(node)
242 return PacketResult.IGNORED;
245 public NodeConnector lookup(Node node, byte [] dstMAC) {
246 return this.mac_to_ports.get(node).get(dstMAC);