blob: 3c123bcd978dc35e4e54044685e46319f934c693 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070017
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080018import com.google.common.collect.ArrayListMultimap;
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -070019import com.google.common.collect.ImmutableList;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080020import com.google.common.collect.Multimap;
tom7ef8ff92014-09-17 13:08:06 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080024import org.apache.felix.scr.annotations.Modified;
Charles Chan3b00e1b2015-08-26 23:12:52 +080025import org.apache.felix.scr.annotations.Property;
alshabibb452fd72015-04-22 20:46:20 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
tom7ef8ff92014-09-17 13:08:06 -070028import org.apache.felix.scr.annotations.Service;
Charles Chan3b00e1b2015-08-26 23:12:52 +080029import org.onosproject.cfg.ComponentConfigService;
Charles Chanecfdfb72015-11-24 19:05:50 -080030import org.onosproject.core.CoreService;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070031import org.onosproject.net.device.DeviceEvent;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070032import org.onosproject.net.device.DeviceListener;
33import org.onosproject.net.device.DeviceService;
alshabibb452fd72015-04-22 20:46:20 -070034import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
36import org.onosproject.openflow.controller.Dpid;
37import org.onosproject.openflow.controller.OpenFlowController;
38import org.onosproject.openflow.controller.OpenFlowEventListener;
Jian Lia78cdb22016-04-21 13:03:58 -070039import org.onosproject.openflow.controller.OpenFlowMessageListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.openflow.controller.OpenFlowPacketContext;
41import org.onosproject.openflow.controller.OpenFlowSwitch;
42import org.onosproject.openflow.controller.OpenFlowSwitchListener;
43import org.onosproject.openflow.controller.PacketListener;
44import org.onosproject.openflow.controller.RoleState;
45import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080046import org.osgi.service.component.ComponentContext;
Marc De Leenheerb9311372015-07-09 11:36:49 -070047import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsEntry;
48import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsReply;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070049import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
50import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080051import org.projectfloodlight.openflow.protocol.OFFactories;
52import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
53import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080054import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
56import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
57import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070058import org.projectfloodlight.openflow.protocol.OFMessage;
59import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070060import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070061import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
tom7ef8ff92014-09-17 13:08:06 -070062import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070063import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080064import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070065import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
66import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070067import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
68import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -070072import java.util.ArrayList;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080073import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070074import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070075import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070076import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080077import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070078import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080079import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080080import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070081import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080082import java.util.concurrent.ExecutorService;
83import java.util.concurrent.Executors;
84import java.util.concurrent.locks.Lock;
85import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070086
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080087import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone71494d2016-11-15 14:45:25 -080088import static org.onosproject.net.Device.Type.CONTROLLER;
89import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070090import static org.onosproject.openflow.controller.Dpid.dpid;
Ozge AYAZ60aded22017-06-20 08:35:30 +000091import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
92import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
93
tom7ef8ff92014-09-17 13:08:06 -070094
95@Component(immediate = true)
96@Service
97public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080098 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -070099 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700100 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -0700101
102 private static final Logger log =
103 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
104
alshabibb452fd72015-04-22 20:46:20 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800106 protected CoreService coreService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700109 protected DriverService driverService;
110
Charles Chan3b00e1b2015-08-26 23:12:52 +0800111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected ComponentConfigService cfgService;
113
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected DeviceService deviceService;
116
117
Brian O'Connore2a399e2015-09-22 15:32:50 -0700118 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
119 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
120 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800121
122 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700123 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800124 private int workerThreads = DEFAULT_WORKER_THREADS;
125
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800126 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800127 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800128
129 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800130 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700131
Prince Pereirae7798032016-07-08 16:31:58 +0530132 //Separate executor thread for handling error messages and barrier replies for same failed
133 // transactions to avoid context switching of thread
134 protected ExecutorService executorErrorMsgs =
135 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
136
137 //concurrent hashmap to track failed transactions
138 protected ConcurrentMap<Long, Boolean> errorMsgs =
139 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800140 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700141 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800142 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700143 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800144 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700145 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700146
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700147 // Key: dpid, value: map with key: long (XID), value: completable future
148 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
149 new ConcurrentHashMap<>();
150
tom7ef8ff92014-09-17 13:08:06 -0700151 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700152 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700153
154 protected Multimap<Integer, PacketListener> ofPacketListener =
155 ArrayListMultimap.create();
156
Jonathan Hart6d44d192015-05-11 18:01:19 -0700157 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700158
Jian Lia78cdb22016-04-21 13:03:58 -0700159 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800160
sangho6a0bb172015-02-05 12:24:48 -0800161 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
162 ArrayListMultimap.create();
163
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700164 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
165 ArrayListMultimap.create();
166
sangho6a0bb172015-02-05 12:24:48 -0800167 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
168 ArrayListMultimap.create();
169
170 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800171 ArrayListMultimap.create();
172
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700173 // deprecated in 1.11.0, no longer referenced from anywhere
174 @Deprecated
sangho538108b2015-04-08 14:29:20 -0700175 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
176 ArrayListMultimap.create();
177
Ozge AYAZ60aded22017-06-20 08:35:30 +0000178 protected Multimap<Dpid, OFQueueStatsEntry> fullQueueStats =
179 ArrayListMultimap.create();
180
tom7ef8ff92014-09-17 13:08:06 -0700181 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700182 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700183
184 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800185 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700186 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800187 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700188 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700189 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700190 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700191 }
192
Andrea Campanella3556f362016-04-28 15:18:10 -0700193 private void cleanup() {
194 // Close listening channel and all OF channels. Clean information about switches
195 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800196 ctrl.stop();
197 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700198 connectedSwitches.clear();
199 activeMasterSwitches.clear();
200 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800201 }
202
tom7ef8ff92014-09-17 13:08:06 -0700203 @Deactivate
204 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700205 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300206 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800207 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700208 }
209
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800210 @Modified
211 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800212 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700213 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800214 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800215 }
216
tom7ef8ff92014-09-17 13:08:06 -0700217 @Override
218 public Iterable<OpenFlowSwitch> getSwitches() {
219 return connectedSwitches.values();
220 }
221
222 @Override
223 public Iterable<OpenFlowSwitch> getMasterSwitches() {
224 return activeMasterSwitches.values();
225 }
226
227 @Override
228 public Iterable<OpenFlowSwitch> getEqualSwitches() {
229 return activeEqualSwitches.values();
230 }
231
232 @Override
233 public OpenFlowSwitch getSwitch(Dpid dpid) {
234 return connectedSwitches.get(dpid);
235 }
236
237 @Override
238 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
239 return activeMasterSwitches.get(dpid);
240 }
241
242 @Override
243 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
244 return activeEqualSwitches.get(dpid);
245 }
246
247 @Override
248 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700249 if (!ofSwitchListener.contains(listener)) {
250 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700251 }
252 }
253
254 @Override
255 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700256 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700257 }
258
259 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700260 public void addMessageListener(OpenFlowMessageListener listener) {
261 ofMessageListener.add(listener);
262 }
263
264 @Override
265 public void removeMessageListener(OpenFlowMessageListener listener) {
266 ofMessageListener.remove(listener);
267 }
268
269 @Override
tom7ef8ff92014-09-17 13:08:06 -0700270 public void addPacketListener(int priority, PacketListener listener) {
271 ofPacketListener.put(priority, listener);
272 }
273
274 @Override
275 public void removePacketListener(PacketListener listener) {
276 ofPacketListener.values().remove(listener);
277 }
278
279 @Override
alshabibeec3a062014-09-17 18:01:26 -0700280 public void addEventListener(OpenFlowEventListener listener) {
281 ofEventListener.add(listener);
282 }
283
284 @Override
285 public void removeEventListener(OpenFlowEventListener listener) {
286 ofEventListener.remove(listener);
287 }
288
289 @Override
tom7ef8ff92014-09-17 13:08:06 -0700290 public void write(Dpid dpid, OFMessage msg) {
291 this.getSwitch(dpid).sendMsg(msg);
292 }
293
294 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700295 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
296 write(dpid, msg);
297
298 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700299 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700300
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700301 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700302 xids.put(msg.getXid(), future);
303
304 return future;
305 }
306
307 @Override
tom7ef8ff92014-09-17 13:08:06 -0700308 public void processPacket(Dpid dpid, OFMessage msg) {
sangyun-han69ed4462016-07-27 12:10:12 +0900309 OpenFlowSwitch sw = this.getSwitch(dpid);
310
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700311 // Check if someone is waiting for this message
312 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700313 if (xids != null) {
314 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
315 if (future != null) {
316 future.complete(msg);
317 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700318 }
319
tom7ef8ff92014-09-17 13:08:06 -0700320 switch (msg.getType()) {
321 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700322 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700323 l.portChanged(dpid, (OFPortStatus) msg);
324 }
325 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700326 case FEATURES_REPLY:
327 for (OpenFlowSwitchListener l : ofSwitchListener) {
328 l.switchChanged(dpid);
329 }
330 break;
tom7ef8ff92014-09-17 13:08:06 -0700331 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900332 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700333 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900334 break;
335 }
tom7ef8ff92014-09-17 13:08:06 -0700336 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
Ozge AYAZ60aded22017-06-20 08:35:30 +0000337 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700338 for (PacketListener p : ofPacketListener.values()) {
339 p.handlePacket(pktCtx);
340 }
341 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800342 // TODO: Consider using separate threadpool for sensitive messages.
343 // ie. Back to back error could cause us to starve.
344 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800345 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800346 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530347 case ERROR:
348 log.debug("Received error message from {}: {}", dpid, msg);
349 errorMsgs.putIfAbsent(msg.getXid(), true);
350 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
351 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700352 case STATS_REPLY:
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700353 processStatsReply(dpid, (OFStatsReply) msg);
alshabib64def642014-12-02 23:27:37 -0800354 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700355 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530356 if (errorMsgs.containsKey(msg.getXid())) {
357 //To make oferror msg handling and corresponding barrier reply serialized,
358 // executorErrorMsgs is used for both transaction
359 errorMsgs.remove(msg.getXid());
360 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
361 } else {
362 executorBarrier.execute(new OFMessageHandler(dpid, msg));
363 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700364 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700365 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900366 if (sw == null) {
367 log.error("Switch {} is not found", dpid);
368 break;
369 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700370 long experimenter = ((OFExperimenter) msg).getExperimenter();
371 if (experimenter == 0x748771) {
372 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700373 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900374 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
375 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700376 portDesc.setPortNo(circuitPortStatus.getPortNo())
377 .setHwAddr(circuitPortStatus.getHwAddr())
378 .setName(circuitPortStatus.getName())
379 .setConfig(circuitPortStatus.getConfig())
380 .setState(circuitPortStatus.getState());
381 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
382 for (OpenFlowSwitchListener l : ofSwitchListener) {
383 l.portChanged(dpid, portStatus.build());
384 }
385 } else {
386 log.warn("Handling experimenter type {} not yet implemented",
387 ((OFExperimenter) msg).getExperimenter(), msg);
388 }
389 break;
tom7ef8ff92014-09-17 13:08:06 -0700390 default:
391 log.warn("Handling message type {} not yet implemented {}",
392 msg.getType(), msg);
393 }
394 }
395
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700396 private void processStatsReply(Dpid dpid, OFStatsReply reply) {
397 switch (reply.getStatsType()) {
398 case QUEUE:
399 Collection<OFQueueStatsEntry> queueStatsEntries = publishQueueStats(dpid, (OFQueueStatsReply) reply);
400 if (queueStatsEntries != null) {
401 OFQueueStatsReply.Builder rep =
402 OFFactories.getFactory(reply.getVersion()).buildQueueStatsReply();
403 rep.setEntries(ImmutableList.copyOf(queueStatsEntries));
404 rep.setXid(reply.getXid());
405 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
406 }
407 break;
408
409 case PORT_DESC:
410 for (OpenFlowSwitchListener l : ofSwitchListener) {
411 l.switchChanged(dpid);
412 }
413 break;
414
415 case FLOW:
416 Collection<OFFlowStatsEntry> flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
417 if (flowStats != null) {
418 OFFlowStatsReply.Builder rep =
419 OFFactories.getFactory(reply.getVersion()).buildFlowStatsReply();
420 rep.setEntries(ImmutableList.copyOf(flowStats));
421 rep.setXid(reply.getXid());
422 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
423 }
424 break;
425
426 case TABLE:
427 Collection<OFTableStatsEntry> tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
428 if (tableStats != null) {
429 OFTableStatsReply.Builder rep =
430 OFFactories.getFactory(reply.getVersion()).buildTableStatsReply();
431 rep.setEntries(ImmutableList.copyOf(tableStats));
432 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
433 }
434 break;
435
436 case GROUP:
437 Collection<OFGroupStatsEntry> groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
438 if (groupStats != null) {
439 OFGroupStatsReply.Builder rep =
440 OFFactories.getFactory(reply.getVersion()).buildGroupStatsReply();
441 rep.setEntries(ImmutableList.copyOf(groupStats));
442 rep.setXid(reply.getXid());
443 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
444 }
445 break;
446
447 case GROUP_DESC:
448 Collection<OFGroupDescStatsEntry> groupDescStats = publishGroupDescStats(dpid,
449 (OFGroupDescStatsReply) reply);
450 if (groupDescStats != null) {
451 OFGroupDescStatsReply.Builder rep =
452 OFFactories.getFactory(reply.getVersion()).buildGroupDescStatsReply();
453 rep.setEntries(ImmutableList.copyOf(groupDescStats));
454 rep.setXid(reply.getXid());
455 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
456 }
457 break;
458
459 case PORT:
460 executorMsgs.execute(new OFMessageHandler(dpid, reply));
461 break;
462
463 case METER:
464 executorMsgs.execute(new OFMessageHandler(dpid, reply));
465 break;
466
467 case EXPERIMENTER:
468 if (reply instanceof OFCalientFlowStatsReply) {
469 OpenFlowSwitch sw = this.getSwitch(dpid);
470 // Convert Calient flow statistics to regular flow stats
471 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
472 if (sw == null) {
473 log.error("Switch {} is not found", dpid);
474 break;
475 }
476 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
477 List<OFFlowStatsEntry> entries = new ArrayList<>();
478 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) reply).getEntries()) {
479
480 // Single instruction, i.e., output to port
481 OFActionOutput action = sw.factory()
482 .actions()
483 .buildOutput()
484 .setPort(entry.getOutPort())
485 .build();
486 OFInstruction instruction = sw.factory()
487 .instructions()
488 .applyActions(Collections.singletonList(action));
489 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
490 .setMatch(entry.getMatch())
491 .setTableId(entry.getTableId())
492 .setDurationSec(entry.getDurationSec())
493 .setDurationNsec(entry.getDurationNsec())
494 .setPriority(entry.getPriority())
495 .setIdleTimeout(entry.getIdleTimeout())
496 .setHardTimeout(entry.getHardTimeout())
497 .setFlags(entry.getFlags())
498 .setCookie(entry.getCookie())
499 .setInstructions(Collections.singletonList(instruction))
500 .build();
501 entries.add(fs);
502 }
503 fsr.setEntries(entries);
504
505 flowStats = publishFlowStats(dpid, fsr.build());
506 if (flowStats != null) {
507 OFFlowStatsReply.Builder rep =
508 sw.factory().buildFlowStatsReply();
509 rep.setEntries(ImmutableList.copyOf(flowStats));
510 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
511 }
512 } else {
513 executorMsgs.execute(new OFMessageHandler(dpid, reply));
514 }
515 break;
516 default:
517 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
518 break;
519 }
520 }
521
sangho6a0bb172015-02-05 12:24:48 -0800522 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
523 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800524 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800525 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800526 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800527 return fullFlowStats.removeAll(dpid);
528 }
529 return null;
530 }
531
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700532 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
533 OFTableStatsReply reply) {
534 //TODO: Get rid of synchronized
535 fullTableStats.putAll(dpid, reply.getEntries());
536 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
537 return fullTableStats.removeAll(dpid);
538 }
539 return null;
540 }
541
sangho6a0bb172015-02-05 12:24:48 -0800542 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
543 OFGroupStatsReply reply) {
544 //TODO: Get rid of synchronized
545 fullGroupStats.putAll(dpid, reply.getEntries());
546 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
547 return fullGroupStats.removeAll(dpid);
548 }
549 return null;
550 }
551
552 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
553 OFGroupDescStatsReply reply) {
554 //TODO: Get rid of synchronized
555 fullGroupDescStats.putAll(dpid, reply.getEntries());
556 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
557 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800558 }
559 return null;
560 }
561
Ozge AYAZ60aded22017-06-20 08:35:30 +0000562 private synchronized Collection<OFQueueStatsEntry> publishQueueStats(Dpid dpid, OFQueueStatsReply reply) {
563 fullQueueStats.putAll(dpid, reply.getEntries());
564 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
565 return fullQueueStats.removeAll(dpid);
566 }
567 return null;
568 }
569
tom7ef8ff92014-09-17 13:08:06 -0700570 @Override
571 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700572 final OpenFlowSwitch sw = getSwitch(dpid);
573 if (sw == null) {
574 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
575 return;
576 }
577 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700578 }
579
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700580 class InternalDeviceListener implements DeviceListener {
581
582 @Override
583 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800584 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700585 }
586
587 @Override
588 public void event(DeviceEvent event) {
589 switch (event.type()) {
590 case DEVICE_ADDED:
591 break;
592 case DEVICE_AVAILABILITY_CHANGED:
593 break;
594 case DEVICE_REMOVED:
595 // Device administratively removed, disconnect
596 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
597 .ifPresent(OpenFlowSwitch::disconnectSwitch);
598 break;
599 case DEVICE_SUSPENDED:
600 break;
601 case DEVICE_UPDATED:
602 break;
603 case PORT_ADDED:
604 break;
605 case PORT_REMOVED:
606 break;
607 case PORT_STATS_UPDATED:
608 break;
609 case PORT_UPDATED:
610 break;
611 default:
612 break;
613
614 }
615
616 }
617
618 }
619
tom7ef8ff92014-09-17 13:08:06 -0700620 /**
621 * Implementation of an OpenFlow Agent which is responsible for
622 * keeping track of connected switches and the state in which
623 * they are.
624 */
625 public class OpenFlowSwitchAgent implements OpenFlowAgent {
626
627 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
628 private final Lock switchLock = new ReentrantLock();
629
630 @Override
631 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700632
tom7ef8ff92014-09-17 13:08:06 -0700633 if (connectedSwitches.get(dpid) != null) {
634 log.error("Trying to add connectedSwitch but found a previous "
635 + "value for dpid: {}", dpid);
636 return false;
637 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700638 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700639 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700640 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700641 l.switchAdded(dpid);
642 }
643 return true;
644 }
645 }
646
647 @Override
648 public boolean validActivation(Dpid dpid) {
649 if (connectedSwitches.get(dpid) == null) {
650 log.error("Trying to activate switch but is not in "
651 + "connected switches: dpid {}. Aborting ..",
652 dpid);
653 return false;
654 }
655 if (activeMasterSwitches.get(dpid) != null ||
656 activeEqualSwitches.get(dpid) != null) {
657 log.error("Trying to activate switch but it is already "
658 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800659 + "Found in activeEqual: {}. Aborting ..",
660 dpid,
661 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
662 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700663 return false;
664 }
665 return true;
666 }
667
668
669 @Override
670 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
671 switchLock.lock();
672 try {
673 if (!validActivation(dpid)) {
674 return false;
675 }
676 activeMasterSwitches.put(dpid, sw);
677 return true;
678 } finally {
679 switchLock.unlock();
680 }
681 }
682
683 @Override
684 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
685 switchLock.lock();
686 try {
687 if (!validActivation(dpid)) {
688 return false;
689 }
690 activeEqualSwitches.put(dpid, sw);
691 log.info("Added Activated EQUAL Switch {}", dpid);
692 return true;
693 } finally {
694 switchLock.unlock();
695 }
696 }
697
698 @Override
699 public void transitionToMasterSwitch(Dpid dpid) {
700 switchLock.lock();
701 try {
702 if (activeMasterSwitches.containsKey(dpid)) {
703 return;
704 }
705 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
706 if (sw == null) {
707 sw = getSwitch(dpid);
708 if (sw == null) {
709 log.error("Transition to master called on sw {}, but switch "
710 + "was not found in controller-cache", dpid);
711 return;
712 }
713 }
714 log.info("Transitioned switch {} to MASTER", dpid);
715 activeMasterSwitches.put(dpid, sw);
716 } finally {
717 switchLock.unlock();
718 }
719 }
720
721
722 @Override
723 public void transitionToEqualSwitch(Dpid dpid) {
724 switchLock.lock();
725 try {
726 if (activeEqualSwitches.containsKey(dpid)) {
727 return;
728 }
729 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
730 if (sw == null) {
731 sw = getSwitch(dpid);
732 if (sw == null) {
733 log.error("Transition to equal called on sw {}, but switch "
734 + "was not found in controller-cache", dpid);
735 return;
736 }
737 }
738 log.info("Transitioned switch {} to EQUAL", dpid);
739 activeEqualSwitches.put(dpid, sw);
740 } finally {
741 switchLock.unlock();
742 }
743
744 }
745
746 @Override
747 public void removeConnectedSwitch(Dpid dpid) {
748 connectedSwitches.remove(dpid);
749 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
750 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700751 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700752 sw = activeEqualSwitches.remove(dpid);
753 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700754 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700755 l.switchRemoved(dpid);
756 }
757 }
758
759 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700760 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
761 for (OpenFlowMessageListener listener : ofMessageListener) {
762 listener.handleOutgoingMessage(dpid, m);
763 }
764 }
765
766
767 @Override
tom7ef8ff92014-09-17 13:08:06 -0700768 public void processMessage(Dpid dpid, OFMessage m) {
769 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700770
771 for (OpenFlowMessageListener listener : ofMessageListener) {
772 listener.handleIncomingMessage(dpid, m);
773 }
tom7ef8ff92014-09-17 13:08:06 -0700774 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700775
776 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700777 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700778 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700779 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700780 }
781 }
tom7ef8ff92014-09-17 13:08:06 -0700782 }
783
Jian Li152b8852015-12-07 14:47:25 -0800784 /**
Jian Li2266bff2016-04-21 11:01:25 -0700785 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800786 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800787 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700788
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800789 protected final OFMessage msg;
790 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700791
792 public OFMessageHandler(Dpid dpid, OFMessage msg) {
793 this.msg = msg;
794 this.dpid = dpid;
795 }
796
797 @Override
798 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700799 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700800 listener.handleMessage(dpid, msg);
801 }
802 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700803 }
tom7ef8ff92014-09-17 13:08:06 -0700804}