blob: 3e4e1c49a205fa2ea9ee77b67d91c6359dca290b [file] [log] [blame]
Jonathan Hart0961fe82014-04-03 09:56:25 -07001package net.onrc.onos.apps.forwarding;
Jonathan Hart1caaa932013-11-04 15:28:28 -08002
Jonathan Hart41d1e912013-11-24 16:50:25 -08003import java.util.ArrayList;
Jonathan Harte93aed42013-12-05 18:39:50 -08004import java.util.Collection;
Jonathan Hartd33a6cf2013-12-10 14:29:08 -08005import java.util.HashMap;
Jonathan Hart1caaa932013-11-04 15:28:28 -08006import java.util.Iterator;
TeruUf9111652014-05-14 23:10:35 -07007import java.util.LinkedList;
Jonathan Hart41d1e912013-11-24 16:50:25 -08008import java.util.List;
Jonathan Harte93aed42013-12-05 18:39:50 -08009import java.util.Map;
TeruU417fe022014-02-04 12:59:30 -080010import java.util.concurrent.Executors;
11import java.util.concurrent.ScheduledExecutorService;
12import java.util.concurrent.TimeUnit;
Jonathan Hart1caaa932013-11-04 15:28:28 -080013
Jonathan Harte93aed42013-12-05 18:39:50 -080014import net.floodlightcontroller.core.module.FloodlightModuleContext;
15import net.floodlightcontroller.core.module.IFloodlightModule;
16import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart1caaa932013-11-04 15:28:28 -080017import net.floodlightcontroller.util.MACAddress;
Jonathan Hartf5bd2582014-04-09 17:43:41 -070018import net.onrc.onos.api.packet.IPacketListener;
19import net.onrc.onos.api.packet.IPacketService;
Jonathan Hart0961fe82014-04-03 09:56:25 -070020import net.onrc.onos.apps.proxyarp.IProxyArpService;
Jonathan Hartaa380972014-04-03 10:24:46 -070021import net.onrc.onos.core.intent.Intent;
Jonathan Harta99ec672014-04-03 11:30:34 -070022import net.onrc.onos.core.intent.Intent.IntentState;
Jonathan Hartaa380972014-04-03 10:24:46 -070023import net.onrc.onos.core.intent.IntentMap;
TeruUf9111652014-05-14 23:10:35 -070024import net.onrc.onos.core.intent.IntentMap.ChangedEvent;
25import net.onrc.onos.core.intent.IntentMap.ChangedListener;
Jonathan Hartaa380972014-04-03 10:24:46 -070026import net.onrc.onos.core.intent.IntentOperation;
27import net.onrc.onos.core.intent.IntentOperationList;
28import net.onrc.onos.core.intent.PathIntent;
29import net.onrc.onos.core.intent.ShortestPathIntent;
Jonathan Hartaa380972014-04-03 10:24:46 -070030import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070031import net.onrc.onos.core.packet.Ethernet;
32import net.onrc.onos.core.registry.IControllerRegistryService;
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -070033import net.onrc.onos.core.topology.Host;
Jonathan Harte37e4e22014-05-13 19:12:02 -070034import net.onrc.onos.core.topology.ITopologyService;
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -070035import net.onrc.onos.core.topology.LinkData;
Jonathan Hartf5bd2582014-04-09 17:43:41 -070036import net.onrc.onos.core.topology.Port;
Jonathan Hart472062d2014-04-03 10:56:48 -070037import net.onrc.onos.core.topology.Switch;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070038import net.onrc.onos.core.topology.MutableTopology;
Yuta HIGUCHIfb564502014-06-16 21:29:00 -070039import net.onrc.onos.core.util.PortNumber;
Jonathan Hart23701d12014-04-03 10:45:48 -070040import net.onrc.onos.core.util.SwitchPort;
Jonathan Hart1caaa932013-11-04 15:28:28 -080041
Jonathan Hartc78b8f62014-08-07 22:31:09 -070042import org.projectfloodlight.openflow.util.HexString;
Jonathan Hart1caaa932013-11-04 15:28:28 -080043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
Jonathan Hartd857ad62013-12-14 18:08:17 -080046import com.google.common.collect.LinkedListMultimap;
47import com.google.common.collect.ListMultimap;
Jonathan Hart5e448782013-12-10 12:36:35 -080048
Jonathan Hartf5bd2582014-04-09 17:43:41 -070049public class Forwarding implements /*IOFMessageListener,*/ IFloodlightModule,
Jonathan Hart4de98c32014-08-11 17:03:25 -070050 IPacketListener, ChangedListener {
Ray Milkeyec838942014-04-09 11:28:43 -070051 private static final Logger log = LoggerFactory.getLogger(Forwarding.class);
TeruU7feef8a2014-04-03 00:15:49 -070052
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -070053 private static final int SLEEP_TIME_FOR_DB_HOST_INSTALLED = 100; // milliseconds
Ray Milkeyec838942014-04-09 11:28:43 -070054 private static final int NUMBER_OF_THREAD_FOR_EXECUTOR = 1;
TeruU30c0c932014-05-15 16:47:41 -070055 private static final int SRC_SWITCH_TIMEOUT_ADJUST_SECOND = 2;
56 private static final int DEFAULT_IDLE_TIMEOUT = 5;
57 private int idleTimeout = DEFAULT_IDLE_TIMEOUT;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070058
Jonathan Hartc00f5c22014-06-10 15:14:40 -070059 private static final ScheduledExecutorService EXECUTOR_SERVICE =
60 Executors.newScheduledThreadPool(NUMBER_OF_THREAD_FOR_EXECUTOR);
Jonathan Harte93aed42013-12-05 18:39:50 -080061
TeruU435df322014-06-16 23:45:13 -070062 private final HighLevelIntentChangedHandler highLevelIntentChangedHandler =
63 new HighLevelIntentChangedHandler();
64
Jonathan Hartf5bd2582014-04-09 17:43:41 -070065 private IPacketService packetService;
Ray Milkey269ffb92014-04-03 14:43:30 -070066 private IControllerRegistryService controllerRegistryService;
67
Jonathan Harte37e4e22014-05-13 19:12:02 -070068 private ITopologyService topologyService;
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070069 private MutableTopology mutableTopology;
Ray Milkey269ffb92014-04-03 14:43:30 -070070 private IPathCalcRuntimeService pathRuntime;
TeruU9e530662014-05-18 11:49:37 -070071 private IntentMap pathIntentMap;
72 private IntentMap highLevelIntentMap;
Ray Milkey269ffb92014-04-03 14:43:30 -070073
74 // TODO it seems there is a Guava collection that will time out entries.
75 // We should see if this will work here.
76 private Map<Path, PushedFlow> pendingFlows;
77 private ListMultimap<String, PacketToPush> waitingPackets;
78
79 private final Object lock = new Object();
80
TeruU435df322014-06-16 23:45:13 -070081 private class HighLevelIntentChangedHandler implements ChangedListener {
82
83 @Override
84 public void intentsChange(LinkedList<ChangedEvent> events) {
85 for (ChangedEvent event : events) {
86 ShortestPathIntent spfIntent = null;
87 if (event.intent instanceof ShortestPathIntent) {
88 spfIntent = (ShortestPathIntent) event.intent;
89 log.trace("ShortestPathIntent {}", spfIntent);
90 }
91
92 if (spfIntent == null) {
93 log.trace("ShortestPathIntent is null. Skip.");
94 continue;
95 }
96
97 switch(event.eventType) {
98 case ADDED:
99 break;
100 case REMOVED:
101 break;
102 case STATE_CHANGED:
103 if (spfIntent.getState() == IntentState.INST_NACK) {
104 flowRemoved(spfIntent);
105 }
106 break;
107 default:
108 break;
109 }
110 }
111 }
112
113 }
114
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700115 private static class PacketToPush {
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700116 public final Ethernet eth;
Ray Milkey269ffb92014-04-03 14:43:30 -0700117 public final long dpid;
118
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700119 public PacketToPush(Ethernet eth, long dpid) {
120 this.eth = eth;
Ray Milkey269ffb92014-04-03 14:43:30 -0700121 this.dpid = dpid;
122 }
123 }
124
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700125 private static class PushedFlow {
Ray Milkey269ffb92014-04-03 14:43:30 -0700126 public final String intentId;
127 public boolean installed = false;
128 public short firstOutPort;
129
130 public PushedFlow(String flowId) {
131 this.intentId = flowId;
132 }
133 }
134
Jonathan Hart8ed69c52014-04-09 13:29:16 -0700135 private static final class Path {
Ray Milkey269ffb92014-04-03 14:43:30 -0700136 public final MACAddress srcMac;
137 public final MACAddress dstMac;
138
139 public Path(MACAddress srcMac, MACAddress dstMac) {
140 this.srcMac = srcMac;
141 this.dstMac = dstMac;
142 }
143
144 @Override
145 public boolean equals(Object other) {
146 if (!(other instanceof Path)) {
147 return false;
148 }
149
150 Path otherPath = (Path) other;
151 return srcMac.equals(otherPath.srcMac) &&
152 dstMac.equals(otherPath.dstMac);
153 }
154
155 @Override
156 public int hashCode() {
157 int hash = 17;
158 hash = 31 * hash + srcMac.hashCode();
159 hash = 31 * hash + dstMac.hashCode();
160 return hash;
161 }
162
163 @Override
164 public String toString() {
165 return "(" + srcMac + ") => (" + dstMac + ")";
166 }
167 }
168
169 @Override
170 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hart4de98c32014-08-11 17:03:25 -0700171 return null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700172 }
173
174 @Override
175 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Jonathan Hart4de98c32014-08-11 17:03:25 -0700176 return null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700177 }
178
179 @Override
180 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
181 List<Class<? extends IFloodlightService>> dependencies =
182 new ArrayList<Class<? extends IFloodlightService>>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700183 dependencies.add(IControllerRegistryService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700184 dependencies.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 dependencies.add(IPathCalcRuntimeService.class);
186 // We don't use the IProxyArpService directly, but reactive forwarding
187 // requires it to be loaded and answering ARP requests
188 dependencies.add(IProxyArpService.class);
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700189 dependencies.add(IPacketService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700190 return dependencies;
191 }
192
193 @Override
194 public void init(FloodlightModuleContext context) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700195 controllerRegistryService = context.getServiceImpl(IControllerRegistryService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700196 topologyService = context.getServiceImpl(ITopologyService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700197 pathRuntime = context.getServiceImpl(IPathCalcRuntimeService.class);
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700198 packetService = context.getServiceImpl(IPacketService.class);
TeruU7feef8a2014-04-03 00:15:49 -0700199
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 pendingFlows = new HashMap<Path, PushedFlow>();
201 waitingPackets = LinkedListMultimap.create();
202 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800203
Ray Milkey269ffb92014-04-03 14:43:30 -0700204 @Override
205 public void startUp(FloodlightModuleContext context) {
TeruU30c0c932014-05-15 16:47:41 -0700206 Map<String, String> configOptions = context.getConfigParams(this);
207
208 try {
209 if (Integer.parseInt(configOptions.get("idletimeout")) > 0) {
210 idleTimeout = Integer.parseInt(configOptions.get("idletimeout"));
211 log.info("idle_timeout for Forwarding is set to {}.", idleTimeout);
212 } else {
213 log.info("idle_timeout for Forwarding is less than 0. Use default {}.", idleTimeout);
214 }
215 } catch (NumberFormatException e) {
216 log.info("idle_timeout related config options were not set. Use default.");
217 }
218
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700219 packetService.registerPacketListener(this);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800220
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700221 mutableTopology = topologyService.getTopology();
TeruU9e530662014-05-18 11:49:37 -0700222 highLevelIntentMap = pathRuntime.getHighLevelIntents();
TeruU435df322014-06-16 23:45:13 -0700223 highLevelIntentMap.addChangeListener(highLevelIntentChangedHandler);
TeruU9e530662014-05-18 11:49:37 -0700224 pathIntentMap = pathRuntime.getPathIntents();
225 pathIntentMap.addChangeListener(this);
Ray Milkey269ffb92014-04-03 14:43:30 -0700226 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800227
Ray Milkey269ffb92014-04-03 14:43:30 -0700228 @Override
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700229 public void receive(Switch sw, Port inPort, Ethernet eth) {
TeruUf9111652014-05-14 23:10:35 -0700230 if (log.isTraceEnabled()) {
231 log.trace("Receive PACKET_IN swId {}, portId {}", sw.getDpid(), inPort.getNumber());
232 }
TeruU417fe022014-02-04 12:59:30 -0800233
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700234 if (eth.getEtherType() != Ethernet.TYPE_IPV4) {
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700235 // Only handle IPv4 packets right now
236 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700237 }
TeruU417fe022014-02-04 12:59:30 -0800238
Ray Milkey269ffb92014-04-03 14:43:30 -0700239 if (eth.isBroadcast() || eth.isMulticast()) {
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700240 handleBroadcast(sw, inPort, eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700241 } else {
242 // Unicast
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700243 handlePacketIn(sw, inPort, eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700244 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700245 }
TeruU417fe022014-02-04 12:59:30 -0800246
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700247 private void handleBroadcast(Switch sw, Port inPort, Ethernet eth) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700248 if (log.isTraceEnabled()) {
249 log.trace("Sending broadcast packet to other ONOS instances");
250 }
Jonathan Hart0444d932014-01-22 15:06:17 -0800251
pingping-lin0426dee2014-08-27 15:03:17 -0700252 packetService.broadcastPacketOutInternalEdge(eth,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700253 new SwitchPort(sw.getDpid(), inPort.getNumber()));
Ray Milkey269ffb92014-04-03 14:43:30 -0700254 }
Pavlin Radoslavova3818db2014-03-20 19:26:08 -0700255
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700256 private void handlePacketIn(Switch sw, Port inPort, Ethernet eth) {
TeruUf9111652014-05-14 23:10:35 -0700257 if (log.isTraceEnabled()) {
258 log.trace("Start handlePacketIn swId {}, portId {}", sw.getDpid(), inPort.getNumber());
259 }
TeruU7feef8a2014-04-03 00:15:49 -0700260
Ray Milkey269ffb92014-04-03 14:43:30 -0700261 String destinationMac =
262 HexString.toHexString(eth.getDestinationMACAddress());
Jonathan Hart0444d932014-01-22 15:06:17 -0800263
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700264 // FIXME #getHostByMac() is a blocking call, so it may be better way
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700265 // to handle it to avoid the condition.
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700266 Host hostObject = mutableTopology.getHostByMac(
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700267 MACAddress.valueOf(destinationMac));
TeruU6464af02014-02-06 21:38:45 -0800268
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700269 if (hostObject == null) {
270 log.debug("No host entry found for {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700271 destinationMac);
Jonathan Hart1caaa932013-11-04 15:28:28 -0800272
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700273 //Host is not in the DB, so wait it until the host is added.
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700274 EXECUTOR_SERVICE.schedule(new WaitDeviceArp(sw, inPort, eth),
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700275 SLEEP_TIME_FOR_DB_HOST_INSTALLED, TimeUnit.MILLISECONDS);
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 return;
277 }
Pavlin Radoslavov7208e9a2013-12-11 14:31:07 -0800278
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700279 continueHandlePacketIn(sw, inPort, eth, hostObject);
Ray Milkey269ffb92014-04-03 14:43:30 -0700280 }
TeruU417fe022014-02-04 12:59:30 -0800281
Ray Milkey269ffb92014-04-03 14:43:30 -0700282 private class WaitDeviceArp implements Runnable {
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700283 Switch sw;
284 Port inPort;
Ray Milkey269ffb92014-04-03 14:43:30 -0700285 Ethernet eth;
TeruU417fe022014-02-04 12:59:30 -0800286
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700287 public WaitDeviceArp(Switch sw, Port inPort, Ethernet eth) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700288 super();
289 this.sw = sw;
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700290 this.inPort = inPort;
Ray Milkey269ffb92014-04-03 14:43:30 -0700291 this.eth = eth;
292 }
TeruU417fe022014-02-04 12:59:30 -0800293
Ray Milkey269ffb92014-04-03 14:43:30 -0700294 @Override
295 public void run() {
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700296 Host hostObject = mutableTopology.getHostByMac(MACAddress.valueOf(eth.getDestinationMACAddress()));
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700297 if (hostObject == null) {
298 log.debug("wait {}ms and host was not found. " +
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700299 "Send broadcast packet and the thread finish.",
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700300 SLEEP_TIME_FOR_DB_HOST_INSTALLED);
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700301 handleBroadcast(sw, inPort, eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 return;
303 }
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700304 log.debug("wait {}ms and host {} was found, continue",
305 SLEEP_TIME_FOR_DB_HOST_INSTALLED, hostObject.getMacAddress());
306 continueHandlePacketIn(sw, inPort, eth, hostObject);
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 }
308 }
Jonathan Hart0444d932014-01-22 15:06:17 -0800309
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700310 private void continueHandlePacketIn(Switch sw, Port inPort, Ethernet eth, Host hostObject) {
TeruU7feef8a2014-04-03 00:15:49 -0700311
TeruU9e530662014-05-18 11:49:37 -0700312 log.trace("Start continuehandlePacketIn");
TeruU7feef8a2014-04-03 00:15:49 -0700313
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700314 //Iterator<IPortObject> ports = hostObject.getAttachedPorts().iterator();
315 Iterator<net.onrc.onos.core.topology.Port> ports = hostObject.getAttachmentPoints().iterator();
Ray Milkey269ffb92014-04-03 14:43:30 -0700316 if (!ports.hasNext()) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700317 log.debug("No attachment point found for host {} - broadcasting packet",
318 hostObject.getMacAddress());
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700319 handleBroadcast(sw, inPort, eth);
Ray Milkey269ffb92014-04-03 14:43:30 -0700320 return;
321 }
TeruU7feef8a2014-04-03 00:15:49 -0700322
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700323 //This code assumes the host has only one port. It should be problem.
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700324 Port destinationPort = ports.next();
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700325 short destinationPortNum = destinationPort.getNumber().shortValue();
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700326 Switch destinationSw = destinationPort.getSwitch();
327 long destinationDpid = destinationSw.getDpid().value();
Ray Milkey269ffb92014-04-03 14:43:30 -0700328
Ray Milkey269ffb92014-04-03 14:43:30 -0700329 SwitchPort srcSwitchPort = new SwitchPort(
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700330 sw.getDpid(),
331 inPort.getNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700332 SwitchPort dstSwitchPort = new SwitchPort(
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700333 destinationSw.getDpid(),
334 destinationPort.getNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700335
336 MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
337 MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
TeruU9e530662014-05-18 11:49:37 -0700338 Path pathspec = new Path(srcMacAddress, dstMacAddress);
339 IntentOperationList operations = new IntentOperationList();
Ray Milkey269ffb92014-04-03 14:43:30 -0700340
341 synchronized (lock) {
342 //TODO check concurrency
TeruU9e530662014-05-18 11:49:37 -0700343
Ray Milkey269ffb92014-04-03 14:43:30 -0700344 PushedFlow existingFlow = pendingFlows.get(pathspec);
345
346 //A path is installed side by side to reduce a path timeout and a wrong state.
347 if (existingFlow != null) {
348 // We've already start to install a flow for this pair of MAC addresses
349 if (log.isDebugEnabled()) {
350 log.debug("Found existing the same pathspec {}, intent ID is {}",
351 pathspec,
352 existingFlow.intentId);
353 }
354
Ray Milkey269ffb92014-04-03 14:43:30 -0700355 // Find the correct port here. We just assume the PI is from
356 // the first hop switch, but this is definitely not always
357 // the case. We'll have to retrieve the flow from HZ every time
358 // because it could change (be rerouted) sometimes.
359 if (existingFlow.installed) {
360 // Flow has been sent to the switches so it is safe to
361 // send a packet out now
362
TeruU9e530662014-05-18 11:49:37 -0700363 // TODO Here highLevelIntentMap and pathIntentMap would be problem,
364 // because it doesn't have global information as of May 2014.
365 // However usually these lines here is used when we got packet-in and this class think
366 // the path for the packet is installed already, so it is pretty rare.
367 // I will leave it for now, and will work in the next step.
368 Intent highLevelIntent = highLevelIntentMap.getIntent(existingFlow.intentId);
369 if (highLevelIntent == null) {
370 log.debug("Intent ID {} is null in HighLevelIntentMap. return.", existingFlow.intentId);
371 return;
372 }
373
374 if (highLevelIntent.getState() != IntentState.INST_ACK) {
375 log.debug("Intent ID {}'s state is not INST_ACK. return.", existingFlow.intentId);
376 return;
377 }
378
379 ShortestPathIntent spfIntent = null;
380 if (highLevelIntent instanceof ShortestPathIntent) {
381 spfIntent = (ShortestPathIntent) highLevelIntent;
Ray Milkey269ffb92014-04-03 14:43:30 -0700382 } else {
TeruUf9111652014-05-14 23:10:35 -0700383 log.debug("Intent ID {} is not PathIntent or null. return.", existingFlow.intentId);
Ray Milkey269ffb92014-04-03 14:43:30 -0700384 return;
385 }
386
TeruU9e530662014-05-18 11:49:37 -0700387 PathIntent pathIntent = (PathIntent) pathIntentMap.getIntent(spfIntent.getPathIntentId());
388 if (pathIntent == null) {
389 log.debug("PathIntent ID {} is null in PathIntentMap. return.", existingFlow.intentId);
390 return;
391 }
392
393 if (pathIntent.getState() != IntentState.INST_ACK) {
394 log.debug("Intent ID {}'s state is not INST_ACK. return.", existingFlow.intentId);
395 return;
396 }
397
398 boolean isflowEntryForThisSwitch = false;
Yuta HIGUCHI1fc395e2014-05-13 14:06:28 -0700399 net.onrc.onos.core.intent.Path path = pathIntent.getPath();
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700400 // FIXME should switch to PortNumber, etc.
TeruU9e530662014-05-18 11:49:37 -0700401 long outPort = -1;
402
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700403 if (spfIntent.getDstSwitchDpid() == sw.getDpid().value()) {
TeruU9e530662014-05-18 11:49:37 -0700404 log.trace("The packet-in sw dpid {} is on the path.", sw.getDpid());
405 isflowEntryForThisSwitch = true;
406 outPort = spfIntent.getDstPortNumber();
407 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700408
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700409 for (Iterator<LinkData> i = path.iterator(); i.hasNext();) {
410 LinkData ld = i.next();
TeruU9e530662014-05-18 11:49:37 -0700411
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700412 if (ld.getSrc().getDpid().equals(sw.getDpid())) {
TeruU9e530662014-05-18 11:49:37 -0700413 log.trace("The packet-in sw dpid {} is on the path.", sw.getDpid());
Ray Milkey269ffb92014-04-03 14:43:30 -0700414 isflowEntryForThisSwitch = true;
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700415 outPort = ld.getSrc().getPortNumber().value();
Ray Milkey269ffb92014-04-03 14:43:30 -0700416 break;
417 }
418 }
419
Ray Milkey6c4f2fe2014-04-11 09:47:23 -0700420 if (!isflowEntryForThisSwitch) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700421 // If we don't find a flow entry for that switch, then we're
422 // in the middle of a rerouting (or something's gone wrong).
423 // This packet will be dropped as a victim of the rerouting.
424 log.debug("Dropping packet on flow {} between {}-{}",
425 existingFlow.intentId,
426 srcMacAddress, dstMacAddress);
427 } else {
TeruU9e530662014-05-18 11:49:37 -0700428 if (outPort < 0) {
429 outPort = existingFlow.firstOutPort;
430 }
Jonathan Hartf5bd2582014-04-09 17:43:41 -0700431
TeruU9e530662014-05-18 11:49:37 -0700432 log.debug("Sending packet out from sw {}, outport{}", sw.getDpid(), outPort);
Jonathan Harte3702f22014-04-29 02:56:56 -0700433 packetService.sendPacket(eth, new SwitchPort(
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700434 sw.getDpid(), PortNumber.uint16((short) outPort)));
Ray Milkey269ffb92014-04-03 14:43:30 -0700435 }
436 } else {
437 // Flow path has not yet been installed to switches so save the
438 // packet out for later
TeruU9e530662014-05-18 11:49:37 -0700439 log.trace("Put a packet into the waiting list. flowId {}", existingFlow.intentId);
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700440 waitingPackets.put(existingFlow.intentId, new PacketToPush(eth, sw.getDpid().value()));
Ray Milkey269ffb92014-04-03 14:43:30 -0700441 }
442 return;
443 }
444
TeruU9e530662014-05-18 11:49:37 -0700445 String intentId = Long.toString(controllerRegistryService.getNextUniqueId());
Ray Milkey269ffb92014-04-03 14:43:30 -0700446 ShortestPathIntent intent = new ShortestPathIntent(intentId,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700447 sw.getDpid().value(), inPort.getNumber().value(), srcMacAddress.toLong(),
448 destinationDpid, destinationPortNum, dstMacAddress.toLong());
Ray Milkeyff735142014-05-22 19:06:02 -0700449
TeruU30c0c932014-05-15 16:47:41 -0700450 intent.setIdleTimeout(idleTimeout + SRC_SWITCH_TIMEOUT_ADJUST_SECOND);
451 intent.setFirstSwitchIdleTimeout(idleTimeout);
Ray Milkey269ffb92014-04-03 14:43:30 -0700452 IntentOperation.Operator operator = IntentOperation.Operator.ADD;
453 operations.add(operator, intent);
TeruU9e530662014-05-18 11:49:37 -0700454 log.debug("Adding new flow between {} at {} and {} at {}",
455 new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
Ray Milkey269ffb92014-04-03 14:43:30 -0700456
TeruU9e530662014-05-18 11:49:37 -0700457 // Add to waiting lists
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700458 waitingPackets.put(intentId, new PacketToPush(eth, sw.getDpid().value()));
TeruU9e530662014-05-18 11:49:37 -0700459 log.trace("Put a Packet in the wating list. intent ID {}, related pathspec {}", intentId, pathspec);
460 pendingFlows.put(pathspec, new PushedFlow(intentId));
461 log.trace("Put a Path {} in the pending flow, intent ID {}", pathspec, intentId);
Ray Milkey269ffb92014-04-03 14:43:30 -0700462 }
TeruU9e530662014-05-18 11:49:37 -0700463 pathRuntime.executeIntentOperations(operations);
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 }
465
TeruU435df322014-06-16 23:45:13 -0700466 public void flowRemoved(ShortestPathIntent spfIntent) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700467 if (log.isTraceEnabled()) {
TeruU435df322014-06-16 23:45:13 -0700468 log.trace("ShortestPathIntent {} was removed", spfIntent.getId());
Ray Milkey269ffb92014-04-03 14:43:30 -0700469 }
470
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 MACAddress srcMacAddress = MACAddress.valueOf(spfIntent.getSrcMac());
472 MACAddress dstMacAddress = MACAddress.valueOf(spfIntent.getDstMac());
473 Path removedPath = new Path(srcMacAddress, dstMacAddress);
Ray Milkey269ffb92014-04-03 14:43:30 -0700474 synchronized (lock) {
475 // There *shouldn't* be any packets queued if the flow has
476 // just been removed.
477 List<PacketToPush> packets = waitingPackets.removeAll(spfIntent.getId());
478 if (!packets.isEmpty()) {
479 log.warn("Removed flow {} has packets queued.", spfIntent.getId());
480 }
TeruU9e530662014-05-18 11:49:37 -0700481
Ray Milkey269ffb92014-04-03 14:43:30 -0700482 pendingFlows.remove(removedPath);
483 log.debug("Removed from the pendingFlow: Path {}, Flow ID {}", removedPath, spfIntent.getId());
484 }
485 }
486
487 private void flowInstalled(PathIntent installedPath) {
488 if (log.isTraceEnabled()) {
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700489 log.trace("Installed intent ID {}, path {}",
490 installedPath.getParentIntent().getId(), installedPath.getPath());
Ray Milkey269ffb92014-04-03 14:43:30 -0700491 }
492
493 ShortestPathIntent spfIntent = (ShortestPathIntent) installedPath.getParentIntent();
494 MACAddress srcMacAddress = MACAddress.valueOf(spfIntent.getSrcMac());
495 MACAddress dstMacAddress = MACAddress.valueOf(spfIntent.getDstMac());
496 Path path = new Path(srcMacAddress, dstMacAddress);
497 log.debug("Path spec {}", path);
498
499 // TODO waiting packets should time out. We could request a path that
500 // can't be installed right now because of a network partition. The path
501 // may eventually be installed, but we may have received thousands of
502 // packets in the meantime and probably don't want to send very old packets.
503
504 List<PacketToPush> packets = null;
Yuta HIGUCHI1fc395e2014-05-13 14:06:28 -0700505 net.onrc.onos.core.intent.Path graphPath = installedPath.getPath();
Ray Milkey269ffb92014-04-03 14:43:30 -0700506
TeruU220c45e2014-04-10 18:56:26 -0700507 short outPort;
508 if (graphPath.isEmpty()) {
509 outPort = (short) spfIntent.getDstPortNumber();
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700510 log.debug("Path is empty. Maybe hosts on the same switch. outPort {}", outPort);
TeruU220c45e2014-04-10 18:56:26 -0700511 } else {
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700512 outPort = graphPath.get(0).getSrc().getPortNumber().shortValue();
TeruU220c45e2014-04-10 18:56:26 -0700513 log.debug("path{}, outPort {}", graphPath, outPort);
514 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700515
516 PushedFlow existingFlow = null;
517
518 synchronized (lock) {
519 existingFlow = pendingFlows.get(path);
520
521 if (existingFlow != null) {
522 existingFlow.installed = true;
523 existingFlow.firstOutPort = outPort;
524 } else {
525 log.debug("ExistingFlow {} is null", path);
526 return;
527 }
528
529 //Check both existing flow are installed status.
530 if (existingFlow.installed) {
531 packets = waitingPackets.removeAll(existingFlow.intentId);
532 if (log.isDebugEnabled()) {
533 log.debug("removed my packets {} to push from waitingPackets. outPort {} size {}",
534 existingFlow.intentId, existingFlow.firstOutPort, packets.size());
535 }
536 } else {
537 log.debug("Forward or reverse flows hasn't been pushed yet. return");
538 return;
539 }
540 }
541
542 for (PacketToPush packet : packets) {
TeruUbd5b90e2014-06-16 14:29:33 -0700543 log.debug("Start packetToPush to sw {}, outPort {}, path {}",
544 packet.dpid, existingFlow.firstOutPort, path);
Jonathan Harte3702f22014-04-29 02:56:56 -0700545 packetService.sendPacket(packet.eth, new SwitchPort(
546 packet.dpid, existingFlow.firstOutPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700547 }
548 }
549
Ray Milkey269ffb92014-04-03 14:43:30 -0700550 @Override
TeruUf9111652014-05-14 23:10:35 -0700551 public void intentsChange(LinkedList<ChangedEvent> events) {
552 for (ChangedEvent event : events) {
553 log.debug("path intent ID {}, eventType {}", event.intent.getId() , event.eventType);
TeruUbd5b90e2014-06-16 14:29:33 -0700554
555 PathIntent pathIntent = null;
556 if (event.intent instanceof PathIntent) {
557 pathIntent = (PathIntent) event.intent;
558 log.trace("pathIntent {}", pathIntent);
559 }
560
Ray Milkeyb29e6262014-04-09 16:02:14 -0700561 if (pathIntent == null) {
TeruU435df322014-06-16 23:45:13 -0700562 log.trace("pathIntent is null. Skip.");
Ray Milkey269ffb92014-04-03 14:43:30 -0700563 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700564 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700565
Ray Milkeyb29e6262014-04-09 16:02:14 -0700566 if (!(pathIntent.getParentIntent() instanceof ShortestPathIntent)) {
TeruU435df322014-06-16 23:45:13 -0700567 log.trace("parentIntent is not ShortestPathIntent. Skip.");
Ray Milkey269ffb92014-04-03 14:43:30 -0700568 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700569 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700570
TeruUf9111652014-05-14 23:10:35 -0700571 switch(event.eventType) {
572 case ADDED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700573 break;
TeruUf9111652014-05-14 23:10:35 -0700574 case REMOVED:
TeruU435df322014-06-16 23:45:13 -0700575 flowRemoved((ShortestPathIntent) pathIntent.getParentIntent());
Ray Milkey269ffb92014-04-03 14:43:30 -0700576 break;
TeruUf9111652014-05-14 23:10:35 -0700577 case STATE_CHANGED:
578 IntentState state = pathIntent.getState();
TeruU5d2c9392014-06-09 20:02:02 -0700579 log.debug("This is STATE_CHANGED. intent id {} is now state {}",
580 pathIntent.getId() , state);
TeruUf9111652014-05-14 23:10:35 -0700581 switch (state) {
582 case INST_REQ:
583 break;
584 case INST_ACK:
585 flowInstalled(pathIntent);
586 break;
587 case INST_NACK:
TeruU435df322014-06-16 23:45:13 -0700588 flowRemoved((ShortestPathIntent) pathIntent.getParentIntent());
TeruUf9111652014-05-14 23:10:35 -0700589 break;
590 case DEL_REQ:
591 break;
592 case DEL_ACK:
TeruU435df322014-06-16 23:45:13 -0700593 flowRemoved((ShortestPathIntent) pathIntent.getParentIntent());
TeruUf9111652014-05-14 23:10:35 -0700594 break;
595 case DEL_PENDING:
596 break;
TeruUbd5b90e2014-06-16 14:29:33 -0700597 case REROUTE_REQ:
598 break;
TeruUf9111652014-05-14 23:10:35 -0700599 default:
600 break;
601 }
Ray Milkey149693c2014-05-20 14:58:53 -0700602 break;
Ray Milkey269ffb92014-04-03 14:43:30 -0700603 default:
604 break;
605 }
606 }
607 }
Jonathan Hart1caaa932013-11-04 15:28:28 -0800608}