blob: ea6e38486dfaa0ecb47fe13b6f86a738c9bd299d [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;
4import java.util.ArrayList;
Jonathan Hart1caaa932013-11-04 15:28:28 -08005import java.util.Iterator;
Jonathan Hart41d1e912013-11-24 16:50:25 -08006import java.util.List;
Jonathan Hart1caaa932013-11-04 15:28:28 -08007
8import net.floodlightcontroller.core.FloodlightContext;
9import net.floodlightcontroller.core.IFloodlightProviderService;
10import net.floodlightcontroller.core.IOFMessageListener;
11import net.floodlightcontroller.core.IOFSwitch;
12import net.floodlightcontroller.packet.Ethernet;
13import net.floodlightcontroller.util.MACAddress;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080014import net.onrc.onos.datagrid.IDatagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080015import net.onrc.onos.ofcontroller.core.IDeviceStorage;
16import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
17import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
18import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
19import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
Jonathan Hart1caaa932013-11-04 15:28:28 -080020import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
21import net.onrc.onos.ofcontroller.topology.TopologyManager;
22import net.onrc.onos.ofcontroller.util.CallerId;
23import net.onrc.onos.ofcontroller.util.DataPath;
24import net.onrc.onos.ofcontroller.util.Dpid;
Jonathan Hart4fb16d82013-11-07 22:41:32 -080025import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
Jonathan Hart1caaa932013-11-04 15:28:28 -080026import net.onrc.onos.ofcontroller.util.FlowId;
27import net.onrc.onos.ofcontroller.util.FlowPath;
28import net.onrc.onos.ofcontroller.util.FlowPathType;
29import net.onrc.onos.ofcontroller.util.FlowPathUserState;
30import net.onrc.onos.ofcontroller.util.Port;
31import net.onrc.onos.ofcontroller.util.SwitchPort;
32
33import org.openflow.protocol.OFMessage;
34import org.openflow.protocol.OFPacketIn;
Jonathan Hart41d1e912013-11-24 16:50:25 -080035import org.openflow.protocol.OFPacketOut;
36import org.openflow.protocol.OFPort;
Jonathan Hart1caaa932013-11-04 15:28:28 -080037import org.openflow.protocol.OFType;
Jonathan Hart41d1e912013-11-24 16:50:25 -080038import org.openflow.protocol.action.OFAction;
39import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart1caaa932013-11-04 15:28:28 -080040import org.openflow.util.HexString;
41import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44public class Forwarding implements IOFMessageListener {
45 private final static Logger log = LoggerFactory.getLogger(Forwarding.class);
46
47 private IFloodlightProviderService floodlightProvider;
48 private IFlowService flowService;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080049 private IDatagridService datagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080050
51 private IDeviceStorage deviceStorage;
52 private TopologyManager topologyService;
53
54 public Forwarding() {
55
56 }
57
58 public void init(IFloodlightProviderService floodlightProvider,
Jonathan Hartdc3ad702013-11-14 11:34:59 -080059 IFlowService flowService, IDatagridService datagridService) {
Jonathan Hart1caaa932013-11-04 15:28:28 -080060 this.floodlightProvider = floodlightProvider;
61 this.flowService = flowService;
Jonathan Hartdc3ad702013-11-14 11:34:59 -080062 this.datagridService = datagridService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080063
64 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
65
66 deviceStorage = new DeviceStorageImpl();
67 deviceStorage.init("");
68 topologyService = new TopologyManager();
69 topologyService.init("");
70 }
71
72 public void startUp() {
73 // no-op
74 }
75
76 @Override
77 public String getName() {
78 return "onosforwarding";
79 }
80
81 @Override
82 public boolean isCallbackOrderingPrereq(OFType type, String name) {
83 return (type == OFType.PACKET_IN) &&
84 (name.equals("devicemanager") || name.equals("proxyarpmanager"));
85 }
86
87 @Override
88 public boolean isCallbackOrderingPostreq(OFType type, String name) {
89 return false;
90 }
91
92 @Override
93 public Command receive(
94 IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
95
96 if (msg.getType() != OFType.PACKET_IN) {
97 return Command.CONTINUE;
98 }
99
100 OFPacketIn pi = (OFPacketIn) msg;
101
102 Ethernet eth = IFloodlightProviderService.bcStore.
103 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
104
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800105 // We only want to handle unicast IPv4
106 if (eth.isBroadcast() || eth.isMulticast() ||
107 eth.getEtherType() != Ethernet.TYPE_IPv4) {
Jonathan Hart1caaa932013-11-04 15:28:28 -0800108 return Command.CONTINUE;
109 }
110
111 handlePacketIn(sw, pi, eth);
112
113 return Command.STOP;
114 }
115
116 private void handlePacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
117 String destinationMac = HexString.toHexString(eth.getDestinationMACAddress());
118
119 IDeviceObject deviceObject = deviceStorage.getDeviceByMac(
120 destinationMac);
121
122 if (deviceObject == null) {
123 log.debug("No device entry found for {}", destinationMac);
124 return;
125 }
126
127 Iterator<IPortObject> ports = deviceObject.getAttachedPorts().iterator();
128 if (!ports.hasNext()) {
129 log.debug("No attachment point found for device {}", destinationMac);
130 return;
131 }
132 IPortObject portObject = ports.next();
133 short destinationPort = portObject.getNumber();
134 ISwitchObject switchObject = portObject.getSwitch();
135 long destinationDpid = HexString.toLong(switchObject.getDPID());
136
Jonathan Hart41d1e912013-11-24 16:50:25 -0800137 // TODO SwitchPort, Dpid and Port should probably be immutable
138 // (also, are Dpid and Port are even necessary?)
Jonathan Hart1caaa932013-11-04 15:28:28 -0800139 SwitchPort srcSwitchPort = new SwitchPort(
140 new Dpid(sw.getId()), new Port(pi.getInPort()));
141 SwitchPort dstSwitchPort = new SwitchPort(
142 new Dpid(destinationDpid), new Port(destinationPort));
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800143
144 MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
145 MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800146
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800147 if (flowExists(srcSwitchPort, srcMacAddress,
148 dstSwitchPort, dstMacAddress)) {
149 log.debug("Not adding flow because it already exists");
150
Jonathan Hart41d1e912013-11-24 16:50:25 -0800151 // TODO check reverse flow as well
152
153 DataPath shortestPath =
154 topologyService.getDatabaseShortestPath(srcSwitchPort, dstSwitchPort);
155
156 if (shortestPath == null || shortestPath.flowEntries().isEmpty()) {
157 log.warn("No path found between {} and {} - not handling packet",
158 srcSwitchPort, dstSwitchPort);
159 return;
160 }
161
162 Port outPort = shortestPath.flowEntries().get(0).outPort();
163 forwardPacket(pi, sw, outPort.value());
164 return;
165 }
166
167 // Calculate a shortest path before pushing flow mods.
168 // This will be used later by the packet-out processing, but it uses
169 // the database so will be slow, and we should do it before flow mods.
170 DataPath shortestPath =
171 topologyService.getDatabaseShortestPath(srcSwitchPort, dstSwitchPort);
172
173 if (shortestPath == null || shortestPath.flowEntries().isEmpty()) {
174 log.warn("No path found between {} and {} - not handling packet",
175 srcSwitchPort, dstSwitchPort);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800176 return;
177 }
178
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800179 log.debug("Adding new flow between {} at {} and {} at {}",
180 new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
181
Jonathan Hart1caaa932013-11-04 15:28:28 -0800182
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800183 DataPath dataPath = new DataPath();
184 dataPath.setSrcPort(srcSwitchPort);
185 dataPath.setDstPort(dstSwitchPort);
186
Jonathan Hart41d1e912013-11-24 16:50:25 -0800187 CallerId callerId = new CallerId("Forwarding");
188
Jonathan Hart97099912013-11-14 13:40:16 -0800189 FlowId flowId = new FlowId(flowService.getNextFlowEntryId());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800190 FlowPath flowPath = new FlowPath();
191 flowPath.setFlowId(flowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800192 flowPath.setInstallerId(callerId);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800193 flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
194 flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800195 flowPath.setFlowEntryMatch(new FlowEntryMatch());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800196 flowPath.flowEntryMatch().enableSrcMac(srcMacAddress);
197 flowPath.flowEntryMatch().enableDstMac(dstMacAddress);
198 // For now just forward IPv4 packets. This prevents accidentally
Jonathan Hart41d1e912013-11-24 16:50:25 -0800199 // forwarding other stuff like ARP.
Jonathan Hart1caaa932013-11-04 15:28:28 -0800200 flowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
Jonathan Hart4fb16d82013-11-07 22:41:32 -0800201 flowPath.setDataPath(dataPath);
202
Jonathan Hart1caaa932013-11-04 15:28:28 -0800203 flowService.addFlow(flowPath, flowId);
Jonathan Hart41d1e912013-11-24 16:50:25 -0800204
205
206 DataPath reverseDataPath = new DataPath();
207 // Reverse the ports for the reverse path
208 reverseDataPath.setSrcPort(dstSwitchPort);
209 reverseDataPath.setDstPort(srcSwitchPort);
210
211 FlowId reverseFlowId = new FlowId(flowService.getNextFlowEntryId());
212 // TODO implement copy constructor for FlowPath
213 FlowPath reverseFlowPath = new FlowPath();
214 reverseFlowPath.setFlowId(reverseFlowId);
215 reverseFlowPath.setInstallerId(callerId);
216 reverseFlowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
217 reverseFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
218 reverseFlowPath.setFlowEntryMatch(new FlowEntryMatch());
219 // Reverse the MAC addresses for the reverse path
220 reverseFlowPath.flowEntryMatch().enableSrcMac(dstMacAddress);
221 reverseFlowPath.flowEntryMatch().enableDstMac(srcMacAddress);
222 reverseFlowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
223 reverseFlowPath.setDataPath(reverseDataPath);
224 reverseFlowPath.dataPath().srcPort().dpid().toString();
225
226 // TODO what happens if no path exists?
227 flowService.addFlow(reverseFlowPath, reverseFlowId);
228
229
230 Port outPort = shortestPath.flowEntries().get(0).outPort();
231 forwardPacket(pi, sw, outPort.value());
Jonathan Hart1caaa932013-11-04 15:28:28 -0800232 }
Jonathan Hartdc3ad702013-11-14 11:34:59 -0800233
234 private boolean flowExists(SwitchPort srcPort, MACAddress srcMac,
235 SwitchPort dstPort, MACAddress dstMac) {
236 for (FlowPath flow : datagridService.getAllFlows()) {
237 FlowEntryMatch match = flow.flowEntryMatch();
238 // TODO implement FlowEntryMatch.equals();
239 // This is painful to do properly without support in the FlowEntryMatch
240 boolean same = true;
241 if (!match.srcMac().equals(srcMac) ||
242 !match.dstMac().equals(dstMac)) {
243 same = false;
244 }
245 if (!flow.dataPath().srcPort().equals(srcPort) ||
246 !flow.dataPath().dstPort().equals(dstPort)) {
247 same = false;
248 }
249
250 if (same) {
251 log.debug("found flow entry that's the same {}-{}:::{}-{}",
252 new Object[] {srcPort, srcMac, dstPort, dstMac});
253 return true;
254 }
255 }
256
257 return false;
258 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800259
Jonathan Hart41d1e912013-11-24 16:50:25 -0800260 private void forwardPacket(OFPacketIn pi, IOFSwitch sw, short port) {
261 List<OFAction> actions = new ArrayList<OFAction>(1);
262 actions.add(new OFActionOutput(port));
263
264 OFPacketOut po = new OFPacketOut();
265 po.setInPort(OFPort.OFPP_NONE)
266 .setInPort(pi.getInPort())
267 .setActions(actions)
268 .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
269 .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
270
271 if (sw.getBuffers() == 0) {
272 po.setBufferId(OFPacketOut.BUFFER_ID_NONE)
273 .setPacketData(pi.getPacketData())
274 .setLengthU(po.getLengthU() + po.getPacketData().length);
275 }
276 else {
277 po.setBufferId(pi.getBufferId());
278 }
279
280 try {
281 sw.write(po, null);
282 sw.flush();
283 } catch (IOException e) {
284 log.error("Error writing packet out to switch: {}", e);
285 }
286 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800287}