blob: 5a517e821ff0c57dd8275705eec00bfa2690ba47 [file] [log] [blame]
Jonathan Hart1caaa932013-11-04 15:28:28 -08001package net.onrc.onos.ofcontroller.forwarding;
2
Jonathan Hart41d1e912013-11-24 16:50:25 -08003import java.io.IOException;
Jonathan Harte93aed42013-12-05 18:39:50 -08004import java.util.ArrayList;
5import java.util.Collection;
Jonathan Hart1caaa932013-11-04 15:28:28 -08006import java.util.Iterator;
Jonathan Harte93aed42013-12-05 18:39:50 -08007import java.util.List;
8import java.util.Map;
Jonathan Hart5e448782013-12-10 12:36:35 -08009import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart1caaa932013-11-04 15:28:28 -080010
11import net.floodlightcontroller.core.FloodlightContext;
12import net.floodlightcontroller.core.IFloodlightProviderService;
13import net.floodlightcontroller.core.IOFMessageListener;
14import net.floodlightcontroller.core.IOFSwitch;
Jonathan Harte93aed42013-12-05 18:39:50 -080015import net.floodlightcontroller.core.module.FloodlightModuleContext;
16import net.floodlightcontroller.core.module.IFloodlightModule;
17import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080018import net.floodlightcontroller.packet.Ethernet;
19import net.floodlightcontroller.util.MACAddress;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080020import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080021import net.onrc.onos.ofcontroller.core.IDeviceStorage;
22import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
23import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
24import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
25import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
Jonathan Hart1caaa932013-11-04 15:28:28 -080026import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
27import net.onrc.onos.ofcontroller.topology.TopologyManager;
28import net.onrc.onos.ofcontroller.util.CallerId;
29import net.onrc.onos.ofcontroller.util.DataPath;
30import net.onrc.onos.ofcontroller.util.Dpid;
Jonathan Hart4fb16d82013-11-07 22:41:32 -080031import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
Jonathan Hart1caaa932013-11-04 15:28:28 -080032import net.onrc.onos.ofcontroller.util.FlowId;
33import net.onrc.onos.ofcontroller.util.FlowPath;
34import net.onrc.onos.ofcontroller.util.FlowPathType;
35import net.onrc.onos.ofcontroller.util.FlowPathUserState;
36import net.onrc.onos.ofcontroller.util.Port;
37import net.onrc.onos.ofcontroller.util.SwitchPort;
38
39import org.openflow.protocol.OFMessage;
40import org.openflow.protocol.OFPacketIn;
Jonathan Hart41d1e912013-11-24 16:50:25 -080041import org.openflow.protocol.OFPacketOut;
42import org.openflow.protocol.OFPort;
Jonathan Hart1caaa932013-11-04 15:28:28 -080043import org.openflow.protocol.OFType;
Jonathan Hart41d1e912013-11-24 16:50:25 -080044import org.openflow.protocol.action.OFAction;
45import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart1caaa932013-11-04 15:28:28 -080046import org.openflow.util.HexString;
47import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
Jonathan Hart5e448782013-12-10 12:36:35 -080050import com.google.common.collect.HashMultimap;
51import com.google.common.collect.Multimap;
52import com.google.common.collect.Multimaps;
53
54public class Forwarding implements IOFMessageListener, IFloodlightModule,
55 IForwardingService {
Jonathan Hart1caaa932013-11-04 15:28:28 -080056 private final static Logger log = LoggerFactory.getLogger(Forwarding.class);
57
58 private IFloodlightProviderService floodlightProvider;
59 private IFlowService flowService;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080060 private IDatagridService datagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080061
62 private IDeviceStorage deviceStorage;
63 private TopologyManager topologyService;
64
Jonathan Hart5e448782013-12-10 12:36:35 -080065 private Map<Path, Long> pendingFlows;
66 private Multimap<Long, PacketToPush> waitingPackets;
67
68 public class PacketToPush {
69 public final OFPacketOut packet;
70 public final long dpid;
Jonathan Hart1caaa932013-11-04 15:28:28 -080071
Jonathan Hart5e448782013-12-10 12:36:35 -080072 public PacketToPush(OFPacketOut packet, long dpid) {
73 this.packet = packet;
74 this.dpid = dpid;
75 }
76 }
77
78 public final class Path {
79 public final SwitchPort srcPort;
80 public final SwitchPort dstPort;
81
82 public Path(SwitchPort src, SwitchPort dst) {
83 srcPort = new SwitchPort(new Dpid(src.dpid().value()),
84 new Port(src.port().value()));
85 dstPort = new SwitchPort(new Dpid(dst.dpid().value()),
86 new Port(dst.port().value()));
87 }
88
89 @Override
90 public boolean equals(Object other) {
91 if (!(other instanceof Path)) {
92 return false;
93 }
94
95 Path otherPath = (Path) other;
96 return srcPort.equals(otherPath.srcPort) &&
97 dstPort.equals(otherPath.dstPort);
98 }
99
100 @Override
101 public int hashCode() {
102 int hash = 17;
103 hash = 31 * hash + srcPort.hashCode();
104 hash = 31 * hash + dstPort.hashCode();
105 return hash;
106 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800107 }
108
Jonathan Harte93aed42013-12-05 18:39:50 -0800109 @Override
110 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
111 return null;
112 }
113
114 @Override
115 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
116 return null;
117 }
118
119 @Override
120 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
121 List<Class<? extends IFloodlightService>> dependencies =
122 new ArrayList<Class<? extends IFloodlightService>>();
123 dependencies.add(IFloodlightProviderService.class);
124 dependencies.add(IFlowService.class);
125 dependencies.add(IDatagridService.class);
126 return dependencies;
127 }
128
129 @Override
130 public void init(FloodlightModuleContext context) {
131 this.floodlightProvider =
132 context.getServiceImpl(IFloodlightProviderService.class);
133 this.flowService = context.getServiceImpl(IFlowService.class);
134 this.datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800135
136 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
137
Jonathan Hart5e448782013-12-10 12:36:35 -0800138 pendingFlows = new ConcurrentHashMap<Path, Long>();
139 waitingPackets = Multimaps.synchronizedSetMultimap(
140 HashMultimap.<Long, PacketToPush>create());
141
Jonathan Hart1caaa932013-11-04 15:28:28 -0800142 deviceStorage = new DeviceStorageImpl();
143 deviceStorage.init("");
144 topologyService = new TopologyManager();
145 topologyService.init("");
146 }
147
Jonathan Harte93aed42013-12-05 18:39:50 -0800148 @Override
149 public void startUp(FloodlightModuleContext context) {
Jonathan Hart1caaa932013-11-04 15:28:28 -0800150 // no-op
151 }
152
153 @Override
154 public String getName() {
155 return "onosforwarding";
156 }
157
158 @Override
159 public boolean isCallbackOrderingPrereq(OFType type, String name) {
160 return (type == OFType.PACKET_IN) &&
161 (name.equals("devicemanager") || name.equals("proxyarpmanager"));
162 }
163
164 @Override
165 public boolean isCallbackOrderingPostreq(OFType type, String name) {
166 return false;
167 }
168
169 @Override
170 public Command receive(
171 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
172
173 if (msg.getType() != OFType.PACKET_IN) {
174 return Command.CONTINUE;
175 }
176
177 OFPacketIn pi = (OFPacketIn) msg;
178
179 Ethernet eth = IFloodlightProviderService.bcStore.
180 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
181
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800182 // We only want to handle unicast IPv4
183 if (eth.isBroadcast() || eth.isMulticast() ||
184 eth.getEtherType() != Ethernet.TYPE_IPv4) {
Jonathan Hart1caaa932013-11-04 15:28:28 -0800185 return Command.CONTINUE;
186 }
187
188 handlePacketIn(sw, pi, eth);
189
190 return Command.STOP;
191 }
192
193 private void handlePacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
Jonathan Hart5e448782013-12-10 12:36:35 -0800194 String destinationMac =
195 HexString.toHexString(eth.getDestinationMACAddress());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800196
197 IDeviceObject deviceObject = deviceStorage.getDeviceByMac(
198 destinationMac);
199
200 if (deviceObject == null) {
201 log.debug("No device entry found for {}", destinationMac);
202 return;
203 }
204
Jonathan Hart5e448782013-12-10 12:36:35 -0800205 Iterator<IPortObject> ports = deviceObject.getAttachedPorts().iterator();
Jonathan Hart1caaa932013-11-04 15:28:28 -0800206 if (!ports.hasNext()) {
207 log.debug("No attachment point found for device {}", destinationMac);
208 return;
209 }
210 IPortObject portObject = ports.next();
211 short destinationPort = portObject.getNumber();
212 ISwitchObject switchObject = portObject.getSwitch();
213 long destinationDpid = HexString.toLong(switchObject.getDPID());
214
Jonathan Hart41d1e912013-11-24 16:50:25 -0800215 // TODO SwitchPort, Dpid and Port should probably be immutable
216 // (also, are Dpid and Port are even necessary?)
Jonathan Hart1caaa932013-11-04 15:28:28 -0800217 SwitchPort srcSwitchPort = new SwitchPort(
218 new Dpid(sw.getId()), new Port(pi.getInPort()));
219 SwitchPort dstSwitchPort = new SwitchPort(
220 new Dpid(destinationDpid), new Port(destinationPort));
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800221
222 MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
223 MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800224
Jonathan Hart5e448782013-12-10 12:36:35 -0800225
226 DataPath datapath = new DataPath();
227 datapath.setSrcPort(srcSwitchPort);
228 datapath.setDstPort(dstSwitchPort);
229
230
231
232 Path pathspec = new Path(srcSwitchPort, dstSwitchPort);
233 // TODO check concurrency
234 Long existingFlowId = pendingFlows.get(pathspec);
235
236 if (existingFlowId != null) {
237 log.debug("Found existing flow {}",
238 HexString.toHexString(existingFlowId));
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800239
Jonathan Hart5e448782013-12-10 12:36:35 -0800240 // TODO do stuff.
241 OFPacketOut po = constructPacketOut(datapath, pi, sw);
242 waitingPackets.put(existingFlowId, new PacketToPush(po, sw.getId()));
Jonathan Hart41d1e912013-11-24 16:50:25 -0800243 return;
244 }
245
Jonathan Hart1caaa932013-11-04 15:28:28 -0800246
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800247 log.debug("Adding new flow between {} at {} and {} at {}",
248 new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
249
Jonathan Hart1caaa932013-11-04 15:28:28 -0800250
Jonathan Hart41d1e912013-11-24 16:50:25 -0800251 CallerId callerId = new CallerId("Forwarding");
252
Jonathan Hart48c2d312013-12-05 19:09:59 -0800253 //FlowId flowId = new FlowId(flowService.getNextFlowEntryId());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800254 FlowPath flowPath = new FlowPath();
Jonathan Hart48c2d312013-12-05 19:09:59 -0800255 //flowPath.setFlowId(flowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800256 flowPath.setInstallerId(callerId);
Jonathan Hart48c2d312013-12-05 19:09:59 -0800257
Jonathan Hart1caaa932013-11-04 15:28:28 -0800258 flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
259 flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800260 flowPath.setFlowEntryMatch(new FlowEntryMatch());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800261 flowPath.flowEntryMatch().enableSrcMac(srcMacAddress);
262 flowPath.flowEntryMatch().enableDstMac(dstMacAddress);
263 // For now just forward IPv4 packets. This prevents accidentally
Jonathan Hart41d1e912013-11-24 16:50:25 -0800264 // forwarding other stuff like ARP.
Jonathan Hart1caaa932013-11-04 15:28:28 -0800265 flowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
Jonathan Hart5e448782013-12-10 12:36:35 -0800266 flowPath.setDataPath(datapath);
Jonathan Hart48c2d312013-12-05 19:09:59 -0800267
Jonathan Hart5e448782013-12-10 12:36:35 -0800268
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800269
Jonathan Hart41d1e912013-11-24 16:50:25 -0800270
271 DataPath reverseDataPath = new DataPath();
272 // Reverse the ports for the reverse path
273 reverseDataPath.setSrcPort(dstSwitchPort);
274 reverseDataPath.setDstPort(srcSwitchPort);
275
Jonathan Hart41d1e912013-11-24 16:50:25 -0800276 // TODO implement copy constructor for FlowPath
277 FlowPath reverseFlowPath = new FlowPath();
Jonathan Hart48c2d312013-12-05 19:09:59 -0800278 //reverseFlowPath.setFlowId(reverseFlowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800279 reverseFlowPath.setInstallerId(callerId);
280 reverseFlowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
281 reverseFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
282 reverseFlowPath.setFlowEntryMatch(new FlowEntryMatch());
283 // Reverse the MAC addresses for the reverse path
284 reverseFlowPath.flowEntryMatch().enableSrcMac(dstMacAddress);
285 reverseFlowPath.flowEntryMatch().enableDstMac(srcMacAddress);
286 reverseFlowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
287 reverseFlowPath.setDataPath(reverseDataPath);
288 reverseFlowPath.dataPath().srcPort().dpid().toString();
289
290 // TODO what happens if no path exists?
Jonathan Hart41d1e912013-11-24 16:50:25 -0800291
Jonathan Hart5e448782013-12-10 12:36:35 -0800292 FlowId flowId = new FlowId(flowService.getNextFlowEntryId());
293 FlowId reverseFlowId = new FlowId(flowService.getNextFlowEntryId());
294
295 flowPath.setFlowId(flowId);
296 reverseFlowPath.setFlowId(reverseFlowId);
297
298 OFPacketOut po = constructPacketOut(datapath, pi, sw);
299 Path reversePathSpec = new Path(dstSwitchPort, srcSwitchPort);
300
301 // Add to waiting lists
302 pendingFlows.put(pathspec, flowId.value());
303 pendingFlows.put(reversePathSpec, reverseFlowId.value());
304 waitingPackets.put(flowId.value(), new PacketToPush(po, sw.getId()));
305
306
307
308 flowService.addFlow(reverseFlowPath);
309 flowService.addFlow(flowPath);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800310 }
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800311
Jonathan Hart5e448782013-12-10 12:36:35 -0800312 /*
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800313 private boolean flowExists(SwitchPort srcPort, MACAddress srcMac,
314 SwitchPort dstPort, MACAddress dstMac) {
315 for (FlowPath flow : datagridService.getAllFlows()) {
316 FlowEntryMatch match = flow.flowEntryMatch();
317 // TODO implement FlowEntryMatch.equals();
318 // This is painful to do properly without support in the FlowEntryMatch
319 boolean same = true;
320 if (!match.srcMac().equals(srcMac) ||
321 !match.dstMac().equals(dstMac)) {
322 same = false;
323 }
324 if (!flow.dataPath().srcPort().equals(srcPort) ||
325 !flow.dataPath().dstPort().equals(dstPort)) {
326 same = false;
327 }
328
329 if (same) {
330 log.debug("found flow entry that's the same {}-{}:::{}-{}",
331 new Object[] {srcPort, srcMac, dstPort, dstMac});
332 return true;
333 }
334 }
335
336 return false;
337 }
Jonathan Hart5e448782013-12-10 12:36:35 -0800338 */
Jonathan Hart1caaa932013-11-04 15:28:28 -0800339
Jonathan Hart5e448782013-12-10 12:36:35 -0800340 private OFPacketOut constructPacketOut(DataPath datapath, OFPacketIn pi,
341 IOFSwitch sw) {
342 //List<OFAction> actions = new ArrayList<OFAction>(1);
343 //actions.add(new OFActionOutput(port));
Jonathan Hart41d1e912013-11-24 16:50:25 -0800344
345 OFPacketOut po = new OFPacketOut();
346 po.setInPort(OFPort.OFPP_NONE)
347 .setInPort(pi.getInPort())
Jonathan Hart5e448782013-12-10 12:36:35 -0800348 .setActions(new ArrayList<OFAction>())
349 .setLengthU(OFPacketOut.MINIMUM_LENGTH);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800350
351 if (sw.getBuffers() == 0) {
352 po.setBufferId(OFPacketOut.BUFFER_ID_NONE)
353 .setPacketData(pi.getPacketData())
354 .setLengthU(po.getLengthU() + po.getPacketData().length);
355 }
356 else {
357 po.setBufferId(pi.getBufferId());
358 }
359
Jonathan Hart5e448782013-12-10 12:36:35 -0800360 return po;
361 }
362
363 @Override
364 public void flowInstalled(FlowPath installedFlowPath) {
365 // TODO check concurrency
366 // will need to sync and access both collections at once.
367 long flowId = installedFlowPath.flowId().value();
368 Collection<PacketToPush> packets = waitingPackets.removeAll(flowId);
369
370 //remove pending flows entry
371 Path pathToRemove = new Path(installedFlowPath.dataPath().srcPort(),
372 installedFlowPath.dataPath().dstPort());
373 pendingFlows.remove(pathToRemove);
374
375 for (PacketToPush packet : packets) {
376 IOFSwitch sw = floodlightProvider.getSwitches().get(packet.dpid);
377
378 OFPacketOut po = packet.packet;
379 short outPort =
380 installedFlowPath.flowEntries().get(0).outPort().value();
381 po.getActions().add(new OFActionOutput(outPort));
382 po.setActionsLength((short)
383 (po.getActionsLength() + OFActionOutput.MINIMUM_LENGTH));
384 po.setLengthU(po.getLengthU() + OFActionOutput.MINIMUM_LENGTH);
385
386 try {
387 sw.write(packet.packet, null);
388 sw.flush();
389 } catch (IOException e) {
390 log.error("Error writing packet out to switch {}:",
391 sw.getId(), e);
392 }
Jonathan Hart41d1e912013-11-24 16:50:25 -0800393 }
394 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800395}