blob: 69184216542d9f81fd9d7b09612ee82cbf125892 [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 Hartd33a6cf2013-12-10 14:29:08 -08006import java.util.HashMap;
Jonathan Hart1caaa932013-11-04 15:28:28 -08007import java.util.Iterator;
Jonathan Harte93aed42013-12-05 18:39:50 -08008import java.util.List;
9import java.util.Map;
Jonathan Hart5e448782013-12-10 12:36:35 -080010import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart1caaa932013-11-04 15:28:28 -080011
12import net.floodlightcontroller.core.FloodlightContext;
13import net.floodlightcontroller.core.IFloodlightProviderService;
14import net.floodlightcontroller.core.IOFMessageListener;
15import net.floodlightcontroller.core.IOFSwitch;
Jonathan Harte93aed42013-12-05 18:39:50 -080016import net.floodlightcontroller.core.module.FloodlightModuleContext;
17import net.floodlightcontroller.core.module.IFloodlightModule;
18import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080019import net.floodlightcontroller.packet.Ethernet;
20import net.floodlightcontroller.util.MACAddress;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080021import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080022import net.onrc.onos.ofcontroller.core.IDeviceStorage;
23import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
24import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
25import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
26import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
Jonathan Hart1caaa932013-11-04 15:28:28 -080027import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
28import net.onrc.onos.ofcontroller.topology.TopologyManager;
29import net.onrc.onos.ofcontroller.util.CallerId;
30import net.onrc.onos.ofcontroller.util.DataPath;
31import net.onrc.onos.ofcontroller.util.Dpid;
Jonathan Hart4fb16d82013-11-07 22:41:32 -080032import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
Jonathan Hart1caaa932013-11-04 15:28:28 -080033import net.onrc.onos.ofcontroller.util.FlowId;
34import net.onrc.onos.ofcontroller.util.FlowPath;
35import net.onrc.onos.ofcontroller.util.FlowPathType;
36import net.onrc.onos.ofcontroller.util.FlowPathUserState;
37import net.onrc.onos.ofcontroller.util.Port;
38import net.onrc.onos.ofcontroller.util.SwitchPort;
39
40import org.openflow.protocol.OFMessage;
41import org.openflow.protocol.OFPacketIn;
Jonathan Hart41d1e912013-11-24 16:50:25 -080042import org.openflow.protocol.OFPacketOut;
43import org.openflow.protocol.OFPort;
Jonathan Hart1caaa932013-11-04 15:28:28 -080044import org.openflow.protocol.OFType;
Jonathan Hart41d1e912013-11-24 16:50:25 -080045import org.openflow.protocol.action.OFAction;
46import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart1caaa932013-11-04 15:28:28 -080047import org.openflow.util.HexString;
48import org.slf4j.Logger;
49import org.slf4j.LoggerFactory;
50
Jonathan Hart5e448782013-12-10 12:36:35 -080051import com.google.common.collect.HashMultimap;
52import com.google.common.collect.Multimap;
53import com.google.common.collect.Multimaps;
54
55public class Forwarding implements IOFMessageListener, IFloodlightModule,
56 IForwardingService {
Jonathan Hart1caaa932013-11-04 15:28:28 -080057 private final static Logger log = LoggerFactory.getLogger(Forwarding.class);
58
59 private IFloodlightProviderService floodlightProvider;
60 private IFlowService flowService;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080061 private IDatagridService datagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080062
63 private IDeviceStorage deviceStorage;
64 private TopologyManager topologyService;
65
Jonathan Hart5e448782013-12-10 12:36:35 -080066 private Map<Path, Long> pendingFlows;
67 private Multimap<Long, PacketToPush> waitingPackets;
68
69 public class PacketToPush {
70 public final OFPacketOut packet;
71 public final long dpid;
Jonathan Hart1caaa932013-11-04 15:28:28 -080072
Jonathan Hart5e448782013-12-10 12:36:35 -080073 public PacketToPush(OFPacketOut packet, long dpid) {
74 this.packet = packet;
75 this.dpid = dpid;
76 }
77 }
78
79 public final class Path {
80 public final SwitchPort srcPort;
81 public final SwitchPort dstPort;
82
83 public Path(SwitchPort src, SwitchPort dst) {
84 srcPort = new SwitchPort(new Dpid(src.dpid().value()),
85 new Port(src.port().value()));
86 dstPort = new SwitchPort(new Dpid(dst.dpid().value()),
87 new Port(dst.port().value()));
88 }
89
90 @Override
91 public boolean equals(Object other) {
92 if (!(other instanceof Path)) {
93 return false;
94 }
95
96 Path otherPath = (Path) other;
97 return srcPort.equals(otherPath.srcPort) &&
98 dstPort.equals(otherPath.dstPort);
99 }
100
101 @Override
102 public int hashCode() {
103 int hash = 17;
104 hash = 31 * hash + srcPort.hashCode();
105 hash = 31 * hash + dstPort.hashCode();
106 return hash;
107 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800108 }
109
Jonathan Harte93aed42013-12-05 18:39:50 -0800110 @Override
111 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartd33a6cf2013-12-10 14:29:08 -0800112 List<Class<? extends IFloodlightService>> services =
113 new ArrayList<Class<? extends IFloodlightService>>(1);
114 services.add(IForwardingService.class);
115 return services;
Jonathan Harte93aed42013-12-05 18:39:50 -0800116 }
117
118 @Override
119 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hartd33a6cf2013-12-10 14:29:08 -0800120 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
121 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(1);
122 impls.put(IForwardingService.class, this);
123 return impls;
Jonathan Harte93aed42013-12-05 18:39:50 -0800124 }
125
126 @Override
127 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
128 List<Class<? extends IFloodlightService>> dependencies =
129 new ArrayList<Class<? extends IFloodlightService>>();
130 dependencies.add(IFloodlightProviderService.class);
131 dependencies.add(IFlowService.class);
132 dependencies.add(IDatagridService.class);
133 return dependencies;
134 }
135
136 @Override
137 public void init(FloodlightModuleContext context) {
138 this.floodlightProvider =
139 context.getServiceImpl(IFloodlightProviderService.class);
140 this.flowService = context.getServiceImpl(IFlowService.class);
141 this.datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800142
143 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
144
Jonathan Hart5e448782013-12-10 12:36:35 -0800145 pendingFlows = new ConcurrentHashMap<Path, Long>();
146 waitingPackets = Multimaps.synchronizedSetMultimap(
147 HashMultimap.<Long, PacketToPush>create());
148
Jonathan Hart1caaa932013-11-04 15:28:28 -0800149 deviceStorage = new DeviceStorageImpl();
150 deviceStorage.init("");
151 topologyService = new TopologyManager();
152 topologyService.init("");
153 }
154
Jonathan Harte93aed42013-12-05 18:39:50 -0800155 @Override
156 public void startUp(FloodlightModuleContext context) {
Jonathan Hart1caaa932013-11-04 15:28:28 -0800157 // no-op
158 }
159
160 @Override
161 public String getName() {
162 return "onosforwarding";
163 }
164
165 @Override
166 public boolean isCallbackOrderingPrereq(OFType type, String name) {
167 return (type == OFType.PACKET_IN) &&
168 (name.equals("devicemanager") || name.equals("proxyarpmanager"));
169 }
170
171 @Override
172 public boolean isCallbackOrderingPostreq(OFType type, String name) {
173 return false;
174 }
175
176 @Override
177 public Command receive(
178 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
179
180 if (msg.getType() != OFType.PACKET_IN) {
181 return Command.CONTINUE;
182 }
183
184 OFPacketIn pi = (OFPacketIn) msg;
185
186 Ethernet eth = IFloodlightProviderService.bcStore.
187 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
188
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800189 // We only want to handle unicast IPv4
190 if (eth.isBroadcast() || eth.isMulticast() ||
191 eth.getEtherType() != Ethernet.TYPE_IPv4) {
Jonathan Hart1caaa932013-11-04 15:28:28 -0800192 return Command.CONTINUE;
193 }
194
195 handlePacketIn(sw, pi, eth);
196
197 return Command.STOP;
198 }
199
200 private void handlePacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
Jonathan Hart5e448782013-12-10 12:36:35 -0800201 String destinationMac =
202 HexString.toHexString(eth.getDestinationMACAddress());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800203
204 IDeviceObject deviceObject = deviceStorage.getDeviceByMac(
205 destinationMac);
206
207 if (deviceObject == null) {
208 log.debug("No device entry found for {}", destinationMac);
209 return;
210 }
211
Jonathan Hart5e448782013-12-10 12:36:35 -0800212 Iterator<IPortObject> ports = deviceObject.getAttachedPorts().iterator();
Jonathan Hart1caaa932013-11-04 15:28:28 -0800213 if (!ports.hasNext()) {
214 log.debug("No attachment point found for device {}", destinationMac);
215 return;
216 }
217 IPortObject portObject = ports.next();
218 short destinationPort = portObject.getNumber();
219 ISwitchObject switchObject = portObject.getSwitch();
220 long destinationDpid = HexString.toLong(switchObject.getDPID());
221
Jonathan Hart41d1e912013-11-24 16:50:25 -0800222 // TODO SwitchPort, Dpid and Port should probably be immutable
223 // (also, are Dpid and Port are even necessary?)
Jonathan Hart1caaa932013-11-04 15:28:28 -0800224 SwitchPort srcSwitchPort = new SwitchPort(
225 new Dpid(sw.getId()), new Port(pi.getInPort()));
226 SwitchPort dstSwitchPort = new SwitchPort(
227 new Dpid(destinationDpid), new Port(destinationPort));
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800228
229 MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
230 MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800231
Jonathan Hart5e448782013-12-10 12:36:35 -0800232
233 DataPath datapath = new DataPath();
234 datapath.setSrcPort(srcSwitchPort);
235 datapath.setDstPort(dstSwitchPort);
236
237
238
239 Path pathspec = new Path(srcSwitchPort, dstSwitchPort);
240 // TODO check concurrency
241 Long existingFlowId = pendingFlows.get(pathspec);
242
243 if (existingFlowId != null) {
244 log.debug("Found existing flow {}",
245 HexString.toHexString(existingFlowId));
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800246
Jonathan Hart5e448782013-12-10 12:36:35 -0800247 // TODO do stuff.
248 OFPacketOut po = constructPacketOut(datapath, pi, sw);
249 waitingPackets.put(existingFlowId, new PacketToPush(po, sw.getId()));
Jonathan Hart41d1e912013-11-24 16:50:25 -0800250 return;
251 }
252
Jonathan Hart1caaa932013-11-04 15:28:28 -0800253
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800254 log.debug("Adding new flow between {} at {} and {} at {}",
255 new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
256
Jonathan Hart1caaa932013-11-04 15:28:28 -0800257
Jonathan Hart41d1e912013-11-24 16:50:25 -0800258 CallerId callerId = new CallerId("Forwarding");
259
Jonathan Hart48c2d312013-12-05 19:09:59 -0800260 //FlowId flowId = new FlowId(flowService.getNextFlowEntryId());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800261 FlowPath flowPath = new FlowPath();
Jonathan Hart48c2d312013-12-05 19:09:59 -0800262 //flowPath.setFlowId(flowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800263 flowPath.setInstallerId(callerId);
Jonathan Hart48c2d312013-12-05 19:09:59 -0800264
Jonathan Hart1caaa932013-11-04 15:28:28 -0800265 flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
266 flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800267 flowPath.setFlowEntryMatch(new FlowEntryMatch());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800268 flowPath.flowEntryMatch().enableSrcMac(srcMacAddress);
269 flowPath.flowEntryMatch().enableDstMac(dstMacAddress);
270 // For now just forward IPv4 packets. This prevents accidentally
Jonathan Hart41d1e912013-11-24 16:50:25 -0800271 // forwarding other stuff like ARP.
Jonathan Hart1caaa932013-11-04 15:28:28 -0800272 flowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
Jonathan Hart5e448782013-12-10 12:36:35 -0800273 flowPath.setDataPath(datapath);
Jonathan Hart48c2d312013-12-05 19:09:59 -0800274
Jonathan Hart5e448782013-12-10 12:36:35 -0800275
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800276
Jonathan Hart41d1e912013-11-24 16:50:25 -0800277
278 DataPath reverseDataPath = new DataPath();
279 // Reverse the ports for the reverse path
280 reverseDataPath.setSrcPort(dstSwitchPort);
281 reverseDataPath.setDstPort(srcSwitchPort);
282
Jonathan Hart41d1e912013-11-24 16:50:25 -0800283 // TODO implement copy constructor for FlowPath
284 FlowPath reverseFlowPath = new FlowPath();
Jonathan Hart48c2d312013-12-05 19:09:59 -0800285 //reverseFlowPath.setFlowId(reverseFlowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800286 reverseFlowPath.setInstallerId(callerId);
287 reverseFlowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
288 reverseFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
289 reverseFlowPath.setFlowEntryMatch(new FlowEntryMatch());
290 // Reverse the MAC addresses for the reverse path
291 reverseFlowPath.flowEntryMatch().enableSrcMac(dstMacAddress);
292 reverseFlowPath.flowEntryMatch().enableDstMac(srcMacAddress);
293 reverseFlowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
294 reverseFlowPath.setDataPath(reverseDataPath);
295 reverseFlowPath.dataPath().srcPort().dpid().toString();
296
297 // TODO what happens if no path exists?
Jonathan Hart41d1e912013-11-24 16:50:25 -0800298
Jonathan Hart5e448782013-12-10 12:36:35 -0800299 FlowId flowId = new FlowId(flowService.getNextFlowEntryId());
300 FlowId reverseFlowId = new FlowId(flowService.getNextFlowEntryId());
301
302 flowPath.setFlowId(flowId);
303 reverseFlowPath.setFlowId(reverseFlowId);
304
305 OFPacketOut po = constructPacketOut(datapath, pi, sw);
306 Path reversePathSpec = new Path(dstSwitchPort, srcSwitchPort);
307
308 // Add to waiting lists
309 pendingFlows.put(pathspec, flowId.value());
310 pendingFlows.put(reversePathSpec, reverseFlowId.value());
311 waitingPackets.put(flowId.value(), new PacketToPush(po, sw.getId()));
312
313
314
315 flowService.addFlow(reverseFlowPath);
316 flowService.addFlow(flowPath);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800317 }
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800318
Jonathan Hart5e448782013-12-10 12:36:35 -0800319 /*
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800320 private boolean flowExists(SwitchPort srcPort, MACAddress srcMac,
321 SwitchPort dstPort, MACAddress dstMac) {
322 for (FlowPath flow : datagridService.getAllFlows()) {
323 FlowEntryMatch match = flow.flowEntryMatch();
324 // TODO implement FlowEntryMatch.equals();
325 // This is painful to do properly without support in the FlowEntryMatch
326 boolean same = true;
327 if (!match.srcMac().equals(srcMac) ||
328 !match.dstMac().equals(dstMac)) {
329 same = false;
330 }
331 if (!flow.dataPath().srcPort().equals(srcPort) ||
332 !flow.dataPath().dstPort().equals(dstPort)) {
333 same = false;
334 }
335
336 if (same) {
337 log.debug("found flow entry that's the same {}-{}:::{}-{}",
338 new Object[] {srcPort, srcMac, dstPort, dstMac});
339 return true;
340 }
341 }
342
343 return false;
344 }
Jonathan Hart5e448782013-12-10 12:36:35 -0800345 */
Jonathan Hart1caaa932013-11-04 15:28:28 -0800346
Jonathan Hart5e448782013-12-10 12:36:35 -0800347 private OFPacketOut constructPacketOut(DataPath datapath, OFPacketIn pi,
348 IOFSwitch sw) {
349 //List<OFAction> actions = new ArrayList<OFAction>(1);
350 //actions.add(new OFActionOutput(port));
Jonathan Hart41d1e912013-11-24 16:50:25 -0800351
352 OFPacketOut po = new OFPacketOut();
353 po.setInPort(OFPort.OFPP_NONE)
354 .setInPort(pi.getInPort())
Jonathan Hart5e448782013-12-10 12:36:35 -0800355 .setActions(new ArrayList<OFAction>())
356 .setLengthU(OFPacketOut.MINIMUM_LENGTH);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800357
358 if (sw.getBuffers() == 0) {
359 po.setBufferId(OFPacketOut.BUFFER_ID_NONE)
360 .setPacketData(pi.getPacketData())
361 .setLengthU(po.getLengthU() + po.getPacketData().length);
362 }
363 else {
364 po.setBufferId(pi.getBufferId());
365 }
366
Jonathan Hart5e448782013-12-10 12:36:35 -0800367 return po;
368 }
369
370 @Override
371 public void flowInstalled(FlowPath installedFlowPath) {
372 // TODO check concurrency
373 // will need to sync and access both collections at once.
374 long flowId = installedFlowPath.flowId().value();
375 Collection<PacketToPush> packets = waitingPackets.removeAll(flowId);
376
377 //remove pending flows entry
378 Path pathToRemove = new Path(installedFlowPath.dataPath().srcPort(),
379 installedFlowPath.dataPath().dstPort());
380 pendingFlows.remove(pathToRemove);
381
382 for (PacketToPush packet : packets) {
383 IOFSwitch sw = floodlightProvider.getSwitches().get(packet.dpid);
384
385 OFPacketOut po = packet.packet;
386 short outPort =
387 installedFlowPath.flowEntries().get(0).outPort().value();
388 po.getActions().add(new OFActionOutput(outPort));
389 po.setActionsLength((short)
390 (po.getActionsLength() + OFActionOutput.MINIMUM_LENGTH));
391 po.setLengthU(po.getLengthU() + OFActionOutput.MINIMUM_LENGTH);
392
393 try {
394 sw.write(packet.packet, null);
395 sw.flush();
396 } catch (IOException e) {
397 log.error("Error writing packet out to switch {}:",
398 sw.getId(), e);
399 }
Jonathan Hart41d1e912013-11-24 16:50:25 -0800400 }
401 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800402}