blob: 05b68a4b11f77e2c32a9465f39e664cc60fb359e [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Cem Türker3baff672017-10-12 15:09:01 +030052import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsEntry;
53import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
alshabib64def642014-12-02 23:27:37 -080054import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080056import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
57import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
58import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
59import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070060import org.projectfloodlight.openflow.protocol.OFMessage;
61import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070062import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070063import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
tom7ef8ff92014-09-17 13:08:06 -070064import org.projectfloodlight.openflow.protocol.OFPortStatus;
Cem Türker3baff672017-10-12 15:09:01 +030065import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
66import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070067import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080068import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070069import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
70import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070071import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
72import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070073import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -070076import java.util.ArrayList;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080077import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070078import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070079import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070080import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080081import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070082import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080083import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080084import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070085import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080086import java.util.concurrent.ExecutorService;
87import java.util.concurrent.Executors;
88import java.util.concurrent.locks.Lock;
89import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070090
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080091import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone71494d2016-11-15 14:45:25 -080092import static org.onosproject.net.Device.Type.CONTROLLER;
93import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070094import static org.onosproject.openflow.controller.Dpid.dpid;
Ozge AYAZ60aded22017-06-20 08:35:30 +000095
tom7ef8ff92014-09-17 13:08:06 -070096
97@Component(immediate = true)
98@Service
99public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -0800100 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -0700101 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700102 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -0700103
104 private static final Logger log =
105 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
106
alshabibb452fd72015-04-22 20:46:20 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800108 protected CoreService coreService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700111 protected DriverService driverService;
112
Charles Chan3b00e1b2015-08-26 23:12:52 +0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected ComponentConfigService cfgService;
115
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected DeviceService deviceService;
118
119
Brian O'Connore2a399e2015-09-22 15:32:50 -0700120 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
121 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
122 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800123
124 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700125 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800126 private int workerThreads = DEFAULT_WORKER_THREADS;
127
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800128 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800129 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800130
131 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800132 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700133
Prince Pereirae7798032016-07-08 16:31:58 +0530134 //Separate executor thread for handling error messages and barrier replies for same failed
135 // transactions to avoid context switching of thread
136 protected ExecutorService executorErrorMsgs =
137 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
138
139 //concurrent hashmap to track failed transactions
140 protected ConcurrentMap<Long, Boolean> errorMsgs =
141 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800142 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700143 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800144 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700145 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800146 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700147 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700148
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700149 // Key: dpid, value: map with key: long (XID), value: completable future
150 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
151 new ConcurrentHashMap<>();
152
tom7ef8ff92014-09-17 13:08:06 -0700153 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700154 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700155
156 protected Multimap<Integer, PacketListener> ofPacketListener =
157 ArrayListMultimap.create();
158
Jonathan Hart6d44d192015-05-11 18:01:19 -0700159 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700160
Jian Lia78cdb22016-04-21 13:03:58 -0700161 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800162
sangho6a0bb172015-02-05 12:24:48 -0800163 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
164 ArrayListMultimap.create();
165
Cem Türker3baff672017-10-12 15:09:01 +0300166 protected Multimap<Dpid, OFFlowLightweightStatsEntry> fullFlowLightweightStats =
167 ArrayListMultimap.create();
168
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700169 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
170 ArrayListMultimap.create();
171
sangho6a0bb172015-02-05 12:24:48 -0800172 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
173 ArrayListMultimap.create();
174
175 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800176 ArrayListMultimap.create();
177
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700178 // deprecated in 1.11.0, no longer referenced from anywhere
179 @Deprecated
sangho538108b2015-04-08 14:29:20 -0700180 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
181 ArrayListMultimap.create();
182
Ozge AYAZ60aded22017-06-20 08:35:30 +0000183 protected Multimap<Dpid, OFQueueStatsEntry> fullQueueStats =
184 ArrayListMultimap.create();
185
tom7ef8ff92014-09-17 13:08:06 -0700186 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700187 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700188
189 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800190 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700191 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800192 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700193 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700194 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700195 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700196 }
197
Andrea Campanella3556f362016-04-28 15:18:10 -0700198 private void cleanup() {
199 // Close listening channel and all OF channels. Clean information about switches
200 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800201 ctrl.stop();
202 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700203 connectedSwitches.clear();
204 activeMasterSwitches.clear();
205 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800206 }
207
tom7ef8ff92014-09-17 13:08:06 -0700208 @Deactivate
209 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700210 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300211 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800212 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700213 }
214
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800215 @Modified
216 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800217 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700218 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800219 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800220 }
221
tom7ef8ff92014-09-17 13:08:06 -0700222 @Override
223 public Iterable<OpenFlowSwitch> getSwitches() {
224 return connectedSwitches.values();
225 }
226
227 @Override
228 public Iterable<OpenFlowSwitch> getMasterSwitches() {
229 return activeMasterSwitches.values();
230 }
231
232 @Override
233 public Iterable<OpenFlowSwitch> getEqualSwitches() {
234 return activeEqualSwitches.values();
235 }
236
237 @Override
238 public OpenFlowSwitch getSwitch(Dpid dpid) {
239 return connectedSwitches.get(dpid);
240 }
241
242 @Override
243 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
244 return activeMasterSwitches.get(dpid);
245 }
246
247 @Override
248 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
249 return activeEqualSwitches.get(dpid);
250 }
251
252 @Override
253 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700254 if (!ofSwitchListener.contains(listener)) {
255 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700256 }
257 }
258
259 @Override
260 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700261 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700262 }
263
264 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700265 public void addMessageListener(OpenFlowMessageListener listener) {
266 ofMessageListener.add(listener);
267 }
268
269 @Override
270 public void removeMessageListener(OpenFlowMessageListener listener) {
271 ofMessageListener.remove(listener);
272 }
273
274 @Override
tom7ef8ff92014-09-17 13:08:06 -0700275 public void addPacketListener(int priority, PacketListener listener) {
276 ofPacketListener.put(priority, listener);
277 }
278
279 @Override
280 public void removePacketListener(PacketListener listener) {
281 ofPacketListener.values().remove(listener);
282 }
283
284 @Override
alshabibeec3a062014-09-17 18:01:26 -0700285 public void addEventListener(OpenFlowEventListener listener) {
286 ofEventListener.add(listener);
287 }
288
289 @Override
290 public void removeEventListener(OpenFlowEventListener listener) {
291 ofEventListener.remove(listener);
292 }
293
294 @Override
tom7ef8ff92014-09-17 13:08:06 -0700295 public void write(Dpid dpid, OFMessage msg) {
296 this.getSwitch(dpid).sendMsg(msg);
297 }
298
299 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700300 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
301 write(dpid, msg);
302
303 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700304 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700305
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700306 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700307 xids.put(msg.getXid(), future);
308
309 return future;
310 }
311
312 @Override
tom7ef8ff92014-09-17 13:08:06 -0700313 public void processPacket(Dpid dpid, OFMessage msg) {
sangyun-han69ed4462016-07-27 12:10:12 +0900314 OpenFlowSwitch sw = this.getSwitch(dpid);
315
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700316 // Check if someone is waiting for this message
317 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700318 if (xids != null) {
319 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
320 if (future != null) {
321 future.complete(msg);
322 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700323 }
324
tom7ef8ff92014-09-17 13:08:06 -0700325 switch (msg.getType()) {
326 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700327 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700328 l.portChanged(dpid, (OFPortStatus) msg);
329 }
330 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700331 case FEATURES_REPLY:
332 for (OpenFlowSwitchListener l : ofSwitchListener) {
333 l.switchChanged(dpid);
334 }
335 break;
tom7ef8ff92014-09-17 13:08:06 -0700336 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900337 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700338 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900339 break;
340 }
tom7ef8ff92014-09-17 13:08:06 -0700341 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
Ozge AYAZ60aded22017-06-20 08:35:30 +0000342 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700343 for (PacketListener p : ofPacketListener.values()) {
344 p.handlePacket(pktCtx);
345 }
346 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800347 // TODO: Consider using separate threadpool for sensitive messages.
348 // ie. Back to back error could cause us to starve.
349 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800350 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800351 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530352 case ERROR:
353 log.debug("Received error message from {}: {}", dpid, msg);
354 errorMsgs.putIfAbsent(msg.getXid(), true);
355 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
356 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700357 case STATS_REPLY:
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700358 processStatsReply(dpid, (OFStatsReply) msg);
alshabib64def642014-12-02 23:27:37 -0800359 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700360 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530361 if (errorMsgs.containsKey(msg.getXid())) {
362 //To make oferror msg handling and corresponding barrier reply serialized,
363 // executorErrorMsgs is used for both transaction
364 errorMsgs.remove(msg.getXid());
365 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
366 } else {
367 executorBarrier.execute(new OFMessageHandler(dpid, msg));
368 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700369 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700370 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900371 if (sw == null) {
372 log.error("Switch {} is not found", dpid);
373 break;
374 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700375 long experimenter = ((OFExperimenter) msg).getExperimenter();
376 if (experimenter == 0x748771) {
377 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700378 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900379 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
380 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700381 portDesc.setPortNo(circuitPortStatus.getPortNo())
382 .setHwAddr(circuitPortStatus.getHwAddr())
383 .setName(circuitPortStatus.getName())
384 .setConfig(circuitPortStatus.getConfig())
385 .setState(circuitPortStatus.getState());
386 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
387 for (OpenFlowSwitchListener l : ofSwitchListener) {
388 l.portChanged(dpid, portStatus.build());
389 }
390 } else {
391 log.warn("Handling experimenter type {} not yet implemented",
392 ((OFExperimenter) msg).getExperimenter(), msg);
393 }
394 break;
tom7ef8ff92014-09-17 13:08:06 -0700395 default:
396 log.warn("Handling message type {} not yet implemented {}",
397 msg.getType(), msg);
398 }
399 }
400
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700401 private void processStatsReply(Dpid dpid, OFStatsReply reply) {
402 switch (reply.getStatsType()) {
403 case QUEUE:
404 Collection<OFQueueStatsEntry> queueStatsEntries = publishQueueStats(dpid, (OFQueueStatsReply) reply);
405 if (queueStatsEntries != null) {
406 OFQueueStatsReply.Builder rep =
407 OFFactories.getFactory(reply.getVersion()).buildQueueStatsReply();
408 rep.setEntries(ImmutableList.copyOf(queueStatsEntries));
409 rep.setXid(reply.getXid());
410 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
411 }
412 break;
413
414 case PORT_DESC:
415 for (OpenFlowSwitchListener l : ofSwitchListener) {
416 l.switchChanged(dpid);
417 }
418 break;
419
420 case FLOW:
421 Collection<OFFlowStatsEntry> flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
422 if (flowStats != null) {
423 OFFlowStatsReply.Builder rep =
424 OFFactories.getFactory(reply.getVersion()).buildFlowStatsReply();
425 rep.setEntries(ImmutableList.copyOf(flowStats));
426 rep.setXid(reply.getXid());
427 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
428 }
429 break;
Cem Türker3baff672017-10-12 15:09:01 +0300430 case FLOW_LIGHTWEIGHT:
431 Collection<OFFlowLightweightStatsEntry> flowLightweightStats =
432 publishFlowStatsLightweight(dpid, (OFFlowLightweightStatsReply) reply);
433 if (flowLightweightStats != null) {
434 OFFlowLightweightStatsReply.Builder rep =
435 OFFactories.getFactory(reply.getVersion()).buildFlowLightweightStatsReply();
436 rep.setEntries(ImmutableList.copyOf(flowLightweightStats));
437 rep.setXid(reply.getXid());
438 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
439 }
440 break;
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700441 case TABLE:
442 Collection<OFTableStatsEntry> tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
443 if (tableStats != null) {
444 OFTableStatsReply.Builder rep =
445 OFFactories.getFactory(reply.getVersion()).buildTableStatsReply();
446 rep.setEntries(ImmutableList.copyOf(tableStats));
447 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
448 }
449 break;
450
451 case GROUP:
452 Collection<OFGroupStatsEntry> groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
453 if (groupStats != null) {
454 OFGroupStatsReply.Builder rep =
455 OFFactories.getFactory(reply.getVersion()).buildGroupStatsReply();
456 rep.setEntries(ImmutableList.copyOf(groupStats));
457 rep.setXid(reply.getXid());
458 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
459 }
460 break;
461
462 case GROUP_DESC:
463 Collection<OFGroupDescStatsEntry> groupDescStats = publishGroupDescStats(dpid,
464 (OFGroupDescStatsReply) reply);
465 if (groupDescStats != null) {
466 OFGroupDescStatsReply.Builder rep =
467 OFFactories.getFactory(reply.getVersion()).buildGroupDescStatsReply();
468 rep.setEntries(ImmutableList.copyOf(groupDescStats));
469 rep.setXid(reply.getXid());
470 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
471 }
472 break;
473
474 case PORT:
475 executorMsgs.execute(new OFMessageHandler(dpid, reply));
476 break;
477
478 case METER:
479 executorMsgs.execute(new OFMessageHandler(dpid, reply));
480 break;
481
482 case EXPERIMENTER:
483 if (reply instanceof OFCalientFlowStatsReply) {
484 OpenFlowSwitch sw = this.getSwitch(dpid);
485 // Convert Calient flow statistics to regular flow stats
486 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
487 if (sw == null) {
488 log.error("Switch {} is not found", dpid);
489 break;
490 }
491 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
492 List<OFFlowStatsEntry> entries = new ArrayList<>();
493 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) reply).getEntries()) {
494
495 // Single instruction, i.e., output to port
496 OFActionOutput action = sw.factory()
497 .actions()
498 .buildOutput()
499 .setPort(entry.getOutPort())
500 .build();
501 OFInstruction instruction = sw.factory()
502 .instructions()
503 .applyActions(Collections.singletonList(action));
504 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
505 .setMatch(entry.getMatch())
506 .setTableId(entry.getTableId())
507 .setDurationSec(entry.getDurationSec())
508 .setDurationNsec(entry.getDurationNsec())
509 .setPriority(entry.getPriority())
510 .setIdleTimeout(entry.getIdleTimeout())
511 .setHardTimeout(entry.getHardTimeout())
512 .setFlags(entry.getFlags())
513 .setCookie(entry.getCookie())
514 .setInstructions(Collections.singletonList(instruction))
515 .build();
516 entries.add(fs);
517 }
518 fsr.setEntries(entries);
519
520 flowStats = publishFlowStats(dpid, fsr.build());
521 if (flowStats != null) {
522 OFFlowStatsReply.Builder rep =
523 sw.factory().buildFlowStatsReply();
524 rep.setEntries(ImmutableList.copyOf(flowStats));
525 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
526 }
527 } else {
528 executorMsgs.execute(new OFMessageHandler(dpid, reply));
529 }
530 break;
531 default:
532 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
533 break;
534 }
535 }
536
sangho6a0bb172015-02-05 12:24:48 -0800537 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
538 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800539 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800540 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800541 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800542 return fullFlowStats.removeAll(dpid);
543 }
544 return null;
545 }
546
Cem Türker3baff672017-10-12 15:09:01 +0300547 private synchronized Collection<OFFlowLightweightStatsEntry> publishFlowStatsLightweight(
548 Dpid dpid,
549 OFFlowLightweightStatsReply reply) {
550 //TODO: Get rid of synchronized
551 fullFlowLightweightStats.putAll(dpid, reply.getEntries());
552 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
553 return fullFlowLightweightStats.removeAll(dpid);
554 }
555 return null;
556 }
557
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700558 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
559 OFTableStatsReply reply) {
560 //TODO: Get rid of synchronized
561 fullTableStats.putAll(dpid, reply.getEntries());
562 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
563 return fullTableStats.removeAll(dpid);
564 }
565 return null;
566 }
567
sangho6a0bb172015-02-05 12:24:48 -0800568 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
569 OFGroupStatsReply reply) {
570 //TODO: Get rid of synchronized
571 fullGroupStats.putAll(dpid, reply.getEntries());
572 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
573 return fullGroupStats.removeAll(dpid);
574 }
575 return null;
576 }
577
578 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
579 OFGroupDescStatsReply reply) {
580 //TODO: Get rid of synchronized
581 fullGroupDescStats.putAll(dpid, reply.getEntries());
582 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
583 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800584 }
585 return null;
586 }
587
Ozge AYAZ60aded22017-06-20 08:35:30 +0000588 private synchronized Collection<OFQueueStatsEntry> publishQueueStats(Dpid dpid, OFQueueStatsReply reply) {
589 fullQueueStats.putAll(dpid, reply.getEntries());
590 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
591 return fullQueueStats.removeAll(dpid);
592 }
593 return null;
594 }
595
tom7ef8ff92014-09-17 13:08:06 -0700596 @Override
597 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700598 final OpenFlowSwitch sw = getSwitch(dpid);
599 if (sw == null) {
600 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
601 return;
602 }
603 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700604 }
605
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700606 class InternalDeviceListener implements DeviceListener {
607
608 @Override
609 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800610 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700611 }
612
613 @Override
614 public void event(DeviceEvent event) {
615 switch (event.type()) {
616 case DEVICE_ADDED:
617 break;
618 case DEVICE_AVAILABILITY_CHANGED:
619 break;
620 case DEVICE_REMOVED:
621 // Device administratively removed, disconnect
622 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
623 .ifPresent(OpenFlowSwitch::disconnectSwitch);
624 break;
625 case DEVICE_SUSPENDED:
626 break;
627 case DEVICE_UPDATED:
628 break;
629 case PORT_ADDED:
630 break;
631 case PORT_REMOVED:
632 break;
633 case PORT_STATS_UPDATED:
634 break;
635 case PORT_UPDATED:
636 break;
637 default:
638 break;
639
640 }
641
642 }
643
644 }
645
tom7ef8ff92014-09-17 13:08:06 -0700646 /**
647 * Implementation of an OpenFlow Agent which is responsible for
648 * keeping track of connected switches and the state in which
649 * they are.
650 */
651 public class OpenFlowSwitchAgent implements OpenFlowAgent {
652
653 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
654 private final Lock switchLock = new ReentrantLock();
655
656 @Override
657 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700658
tom7ef8ff92014-09-17 13:08:06 -0700659 if (connectedSwitches.get(dpid) != null) {
660 log.error("Trying to add connectedSwitch but found a previous "
661 + "value for dpid: {}", dpid);
662 return false;
663 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700664 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700665 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700666 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700667 l.switchAdded(dpid);
668 }
669 return true;
670 }
671 }
672
673 @Override
674 public boolean validActivation(Dpid dpid) {
675 if (connectedSwitches.get(dpid) == null) {
676 log.error("Trying to activate switch but is not in "
677 + "connected switches: dpid {}. Aborting ..",
678 dpid);
679 return false;
680 }
681 if (activeMasterSwitches.get(dpid) != null ||
682 activeEqualSwitches.get(dpid) != null) {
683 log.error("Trying to activate switch but it is already "
684 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800685 + "Found in activeEqual: {}. Aborting ..",
686 dpid,
687 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
688 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700689 return false;
690 }
691 return true;
692 }
693
694
695 @Override
696 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
697 switchLock.lock();
698 try {
699 if (!validActivation(dpid)) {
700 return false;
701 }
702 activeMasterSwitches.put(dpid, sw);
703 return true;
704 } finally {
705 switchLock.unlock();
706 }
707 }
708
709 @Override
710 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
711 switchLock.lock();
712 try {
713 if (!validActivation(dpid)) {
714 return false;
715 }
716 activeEqualSwitches.put(dpid, sw);
717 log.info("Added Activated EQUAL Switch {}", dpid);
718 return true;
719 } finally {
720 switchLock.unlock();
721 }
722 }
723
724 @Override
725 public void transitionToMasterSwitch(Dpid dpid) {
726 switchLock.lock();
727 try {
728 if (activeMasterSwitches.containsKey(dpid)) {
729 return;
730 }
731 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
732 if (sw == null) {
733 sw = getSwitch(dpid);
734 if (sw == null) {
735 log.error("Transition to master called on sw {}, but switch "
736 + "was not found in controller-cache", dpid);
737 return;
738 }
739 }
740 log.info("Transitioned switch {} to MASTER", dpid);
741 activeMasterSwitches.put(dpid, sw);
742 } finally {
743 switchLock.unlock();
744 }
745 }
746
747
748 @Override
749 public void transitionToEqualSwitch(Dpid dpid) {
750 switchLock.lock();
751 try {
752 if (activeEqualSwitches.containsKey(dpid)) {
753 return;
754 }
755 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
756 if (sw == null) {
757 sw = getSwitch(dpid);
758 if (sw == null) {
759 log.error("Transition to equal called on sw {}, but switch "
760 + "was not found in controller-cache", dpid);
761 return;
762 }
763 }
764 log.info("Transitioned switch {} to EQUAL", dpid);
765 activeEqualSwitches.put(dpid, sw);
766 } finally {
767 switchLock.unlock();
768 }
769
770 }
771
772 @Override
773 public void removeConnectedSwitch(Dpid dpid) {
774 connectedSwitches.remove(dpid);
775 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
776 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700777 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700778 sw = activeEqualSwitches.remove(dpid);
779 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700780 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700781 l.switchRemoved(dpid);
782 }
783 }
784
785 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700786 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
787 for (OpenFlowMessageListener listener : ofMessageListener) {
788 listener.handleOutgoingMessage(dpid, m);
789 }
790 }
791
792
793 @Override
tom7ef8ff92014-09-17 13:08:06 -0700794 public void processMessage(Dpid dpid, OFMessage m) {
795 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700796
797 for (OpenFlowMessageListener listener : ofMessageListener) {
798 listener.handleIncomingMessage(dpid, m);
799 }
tom7ef8ff92014-09-17 13:08:06 -0700800 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700801
802 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700803 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700804 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700805 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700806 }
807 }
tom7ef8ff92014-09-17 13:08:06 -0700808 }
809
Jian Li152b8852015-12-07 14:47:25 -0800810 /**
Jian Li2266bff2016-04-21 11:01:25 -0700811 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800812 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800813 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700814
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800815 protected final OFMessage msg;
816 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700817
818 public OFMessageHandler(Dpid dpid, OFMessage msg) {
819 this.msg = msg;
820 this.dpid = dpid;
821 }
822
823 @Override
824 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700825 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700826 listener.handleMessage(dpid, msg);
827 }
828 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700829 }
tom7ef8ff92014-09-17 13:08:06 -0700830}