blob: 474f8247834007ce0783ed3d9c22d550a67be1ee [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;
alshabib64def642014-12-02 23:27:37 -080019import com.google.common.collect.Lists;
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;
62import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070063import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070064import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080065import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070066import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
67import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070068import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
69import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070070import org.slf4j.Logger;
71import org.slf4j.LoggerFactory;
72
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.LinkedList;
76import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070077import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080078import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070079import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080080import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080081import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070082import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080083import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
85import java.util.concurrent.locks.Lock;
86import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070087
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080088import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone71494d2016-11-15 14:45:25 -080089import static org.onosproject.net.Device.Type.CONTROLLER;
90import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070091import static org.onosproject.openflow.controller.Dpid.dpid;
Ozge AYAZ60aded22017-06-20 08:35:30 +000092import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
93import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
94
tom7ef8ff92014-09-17 13:08:06 -070095
96@Component(immediate = true)
97@Service
98public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080099 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -0700100 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700101 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -0700102
103 private static final Logger log =
104 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
105
alshabibb452fd72015-04-22 20:46:20 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800107 protected CoreService coreService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700110 protected DriverService driverService;
111
Charles Chan3b00e1b2015-08-26 23:12:52 +0800112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected ComponentConfigService cfgService;
114
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceService deviceService;
117
118
Brian O'Connore2a399e2015-09-22 15:32:50 -0700119 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
120 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
121 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800122
123 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700124 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800125 private int workerThreads = DEFAULT_WORKER_THREADS;
126
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800127 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800128 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800129
130 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800131 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700132
Prince Pereirae7798032016-07-08 16:31:58 +0530133 //Separate executor thread for handling error messages and barrier replies for same failed
134 // transactions to avoid context switching of thread
135 protected ExecutorService executorErrorMsgs =
136 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
137
138 //concurrent hashmap to track failed transactions
139 protected ConcurrentMap<Long, Boolean> errorMsgs =
140 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800141 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700142 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800143 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700144 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800145 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700146 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700147
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700148 // Key: dpid, value: map with key: long (XID), value: completable future
149 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
150 new ConcurrentHashMap<>();
151
tom7ef8ff92014-09-17 13:08:06 -0700152 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700153 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700154
155 protected Multimap<Integer, PacketListener> ofPacketListener =
156 ArrayListMultimap.create();
157
Jonathan Hart6d44d192015-05-11 18:01:19 -0700158 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700159
Jian Lia78cdb22016-04-21 13:03:58 -0700160 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800161
sangho6a0bb172015-02-05 12:24:48 -0800162 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
163 ArrayListMultimap.create();
164
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700165 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
166 ArrayListMultimap.create();
167
sangho6a0bb172015-02-05 12:24:48 -0800168 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
169 ArrayListMultimap.create();
170
171 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800172 ArrayListMultimap.create();
173
sangho538108b2015-04-08 14:29:20 -0700174 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
175 ArrayListMultimap.create();
176
Ozge AYAZ60aded22017-06-20 08:35:30 +0000177 protected Multimap<Dpid, OFQueueStatsEntry> fullQueueStats =
178 ArrayListMultimap.create();
179
tom7ef8ff92014-09-17 13:08:06 -0700180 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700181 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700182
183 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800184 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700185 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800186 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700187 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700188 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700189 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700190 }
191
Andrea Campanella3556f362016-04-28 15:18:10 -0700192 private void cleanup() {
193 // Close listening channel and all OF channels. Clean information about switches
194 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800195 ctrl.stop();
196 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700197 connectedSwitches.clear();
198 activeMasterSwitches.clear();
199 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800200 }
201
tom7ef8ff92014-09-17 13:08:06 -0700202 @Deactivate
203 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700204 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300205 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800206 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700207 }
208
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800209 @Modified
210 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800211 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700212 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800213 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800214 }
215
tom7ef8ff92014-09-17 13:08:06 -0700216 @Override
217 public Iterable<OpenFlowSwitch> getSwitches() {
218 return connectedSwitches.values();
219 }
220
221 @Override
222 public Iterable<OpenFlowSwitch> getMasterSwitches() {
223 return activeMasterSwitches.values();
224 }
225
226 @Override
227 public Iterable<OpenFlowSwitch> getEqualSwitches() {
228 return activeEqualSwitches.values();
229 }
230
231 @Override
232 public OpenFlowSwitch getSwitch(Dpid dpid) {
233 return connectedSwitches.get(dpid);
234 }
235
236 @Override
237 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
238 return activeMasterSwitches.get(dpid);
239 }
240
241 @Override
242 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
243 return activeEqualSwitches.get(dpid);
244 }
245
246 @Override
247 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700248 if (!ofSwitchListener.contains(listener)) {
249 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700250 }
251 }
252
253 @Override
254 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700255 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700256 }
257
258 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700259 public void addMessageListener(OpenFlowMessageListener listener) {
260 ofMessageListener.add(listener);
261 }
262
263 @Override
264 public void removeMessageListener(OpenFlowMessageListener listener) {
265 ofMessageListener.remove(listener);
266 }
267
268 @Override
tom7ef8ff92014-09-17 13:08:06 -0700269 public void addPacketListener(int priority, PacketListener listener) {
270 ofPacketListener.put(priority, listener);
271 }
272
273 @Override
274 public void removePacketListener(PacketListener listener) {
275 ofPacketListener.values().remove(listener);
276 }
277
278 @Override
alshabibeec3a062014-09-17 18:01:26 -0700279 public void addEventListener(OpenFlowEventListener listener) {
280 ofEventListener.add(listener);
281 }
282
283 @Override
284 public void removeEventListener(OpenFlowEventListener listener) {
285 ofEventListener.remove(listener);
286 }
287
288 @Override
tom7ef8ff92014-09-17 13:08:06 -0700289 public void write(Dpid dpid, OFMessage msg) {
290 this.getSwitch(dpid).sendMsg(msg);
291 }
292
293 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700294 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
295 write(dpid, msg);
296
297 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700298 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700299
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700300 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700301 xids.put(msg.getXid(), future);
302
303 return future;
304 }
305
Ozge AYAZ60aded22017-06-20 08:35:30 +0000306 // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700307 @Override
tom7ef8ff92014-09-17 13:08:06 -0700308 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800309 Collection<OFFlowStatsEntry> flowStats;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700310 Collection<OFTableStatsEntry> tableStats;
sangho6a0bb172015-02-05 12:24:48 -0800311 Collection<OFGroupStatsEntry> groupStats;
312 Collection<OFGroupDescStatsEntry> groupDescStats;
Ozge AYAZ60aded22017-06-20 08:35:30 +0000313 Collection<OFQueueStatsEntry> queueStatsEntries;
sangho6a0bb172015-02-05 12:24:48 -0800314
sangyun-han69ed4462016-07-27 12:10:12 +0900315 OpenFlowSwitch sw = this.getSwitch(dpid);
316
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700317 // Check if someone is waiting for this message
318 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700319 if (xids != null) {
320 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
321 if (future != null) {
322 future.complete(msg);
323 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700324 }
325
tom7ef8ff92014-09-17 13:08:06 -0700326 switch (msg.getType()) {
327 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700328 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700329 l.portChanged(dpid, (OFPortStatus) msg);
330 }
331 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700332 case FEATURES_REPLY:
333 for (OpenFlowSwitchListener l : ofSwitchListener) {
334 l.switchChanged(dpid);
335 }
336 break;
tom7ef8ff92014-09-17 13:08:06 -0700337 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900338 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700339 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900340 break;
341 }
tom7ef8ff92014-09-17 13:08:06 -0700342 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
Ozge AYAZ60aded22017-06-20 08:35:30 +0000343 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700344 for (PacketListener p : ofPacketListener.values()) {
345 p.handlePacket(pktCtx);
346 }
347 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800348 // TODO: Consider using separate threadpool for sensitive messages.
349 // ie. Back to back error could cause us to starve.
350 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800351 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800352 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530353 case ERROR:
354 log.debug("Received error message from {}: {}", dpid, msg);
355 errorMsgs.putIfAbsent(msg.getXid(), true);
356 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
357 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700358 case STATS_REPLY:
359 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800360 switch (reply.getStatsType()) {
Ozge AYAZ60aded22017-06-20 08:35:30 +0000361 case QUEUE:
362 queueStatsEntries = publishQueueStats(dpid, (OFQueueStatsReply) reply);
363 if (queueStatsEntries != null) {
364 OFQueueStatsReply.Builder rep =
365 OFFactories.getFactory(msg.getVersion()).buildQueueStatsReply();
366 rep.setEntries(Lists.newLinkedList(queueStatsEntries));
367 rep.setXid(reply.getXid());
368 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
369 }
370 break;
sangho6a0bb172015-02-05 12:24:48 -0800371 case PORT_DESC:
372 for (OpenFlowSwitchListener l : ofSwitchListener) {
373 l.switchChanged(dpid);
374 }
375 break;
376 case FLOW:
377 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
378 if (flowStats != null) {
379 OFFlowStatsReply.Builder rep =
380 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
381 rep.setEntries(Lists.newLinkedList(flowStats));
ssyoon9030fbcd92015-08-17 10:42:07 +0900382 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800383 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800384 }
385 break;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700386 case TABLE:
387 tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
388 if (tableStats != null) {
389 OFTableStatsReply.Builder rep =
390 OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
391 rep.setEntries(Lists.newLinkedList(tableStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800392 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700393 }
394 break;
sangho6a0bb172015-02-05 12:24:48 -0800395 case GROUP:
396 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
397 if (groupStats != null) {
398 OFGroupStatsReply.Builder rep =
399 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
400 rep.setEntries(Lists.newLinkedList(groupStats));
401 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800402 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800403 }
404 break;
405 case GROUP_DESC:
406 groupDescStats = publishGroupDescStats(dpid,
407 (OFGroupDescStatsReply) reply);
408 if (groupDescStats != null) {
409 OFGroupDescStatsReply.Builder rep =
Marc De Leenheerb9311372015-07-09 11:36:49 -0700410 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
sangho6a0bb172015-02-05 12:24:48 -0800411 rep.setEntries(Lists.newLinkedList(groupDescStats));
412 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800413 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800414 }
415 break;
sangho538108b2015-04-08 14:29:20 -0700416 case PORT:
Andrea Campanelladda93562016-03-02 11:08:12 -0800417 executorMsgs.execute(new OFMessageHandler(dpid, reply));
sangho538108b2015-04-08 14:29:20 -0700418 break;
alshabib5eb79392015-08-19 18:09:55 -0700419 case METER:
Andrea Campanelladda93562016-03-02 11:08:12 -0800420 executorMsgs.execute(new OFMessageHandler(dpid, reply));
alshabib5eb79392015-08-19 18:09:55 -0700421 break;
Marc De Leenheerb9311372015-07-09 11:36:49 -0700422 case EXPERIMENTER:
423 if (reply instanceof OFCalientFlowStatsReply) {
424 // Convert Calient flow statistics to regular flow stats
425 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
sangyun-han69ed4462016-07-27 12:10:12 +0900426 if (sw == null) {
427 log.error("Switch {} is not found", dpid);
428 break;
429 }
430 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
Marc De Leenheerb9311372015-07-09 11:36:49 -0700431 List<OFFlowStatsEntry> entries = new LinkedList<>();
432 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
433
434 // Single instruction, i.e., output to port
435 OFActionOutput action = OFFactories
436 .getFactory(msg.getVersion())
437 .actions()
438 .buildOutput()
439 .setPort(entry.getOutPort())
440 .build();
441 OFInstruction instruction = OFFactories
442 .getFactory(msg.getVersion())
443 .instructions()
444 .applyActions(Collections.singletonList(action));
sangyun-han69ed4462016-07-27 12:10:12 +0900445 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
Marc De Leenheerb9311372015-07-09 11:36:49 -0700446 .setMatch(entry.getMatch())
447 .setTableId(entry.getTableId())
448 .setDurationSec(entry.getDurationSec())
449 .setDurationNsec(entry.getDurationNsec())
450 .setPriority(entry.getPriority())
451 .setIdleTimeout(entry.getIdleTimeout())
452 .setHardTimeout(entry.getHardTimeout())
453 .setFlags(entry.getFlags())
454 .setCookie(entry.getCookie())
455 .setInstructions(Collections.singletonList(instruction))
456 .build();
457 entries.add(fs);
458 }
459 fsr.setEntries(entries);
460
461 flowStats = publishFlowStats(dpid, fsr.build());
462 if (flowStats != null) {
463 OFFlowStatsReply.Builder rep =
464 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
465 rep.setEntries(Lists.newLinkedList(flowStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800466 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700467 }
468 } else {
Andrea Campanelladda93562016-03-02 11:08:12 -0800469 executorMsgs.execute(new OFMessageHandler(dpid, reply));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700470 }
471 break;
sangho6a0bb172015-02-05 12:24:48 -0800472 default:
alshabib5eb79392015-08-19 18:09:55 -0700473 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
Marc De Leenheerb9311372015-07-09 11:36:49 -0700474 break;
alshabib64def642014-12-02 23:27:37 -0800475 }
476 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700477 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530478 if (errorMsgs.containsKey(msg.getXid())) {
479 //To make oferror msg handling and corresponding barrier reply serialized,
480 // executorErrorMsgs is used for both transaction
481 errorMsgs.remove(msg.getXid());
482 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
483 } else {
484 executorBarrier.execute(new OFMessageHandler(dpid, msg));
485 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700486 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700487 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900488 if (sw == null) {
489 log.error("Switch {} is not found", dpid);
490 break;
491 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700492 long experimenter = ((OFExperimenter) msg).getExperimenter();
493 if (experimenter == 0x748771) {
494 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700495 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900496 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
497 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700498 portDesc.setPortNo(circuitPortStatus.getPortNo())
499 .setHwAddr(circuitPortStatus.getHwAddr())
500 .setName(circuitPortStatus.getName())
501 .setConfig(circuitPortStatus.getConfig())
502 .setState(circuitPortStatus.getState());
503 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
504 for (OpenFlowSwitchListener l : ofSwitchListener) {
505 l.portChanged(dpid, portStatus.build());
506 }
507 } else {
508 log.warn("Handling experimenter type {} not yet implemented",
509 ((OFExperimenter) msg).getExperimenter(), msg);
510 }
511 break;
tom7ef8ff92014-09-17 13:08:06 -0700512 default:
513 log.warn("Handling message type {} not yet implemented {}",
514 msg.getType(), msg);
515 }
516 }
517
sangho6a0bb172015-02-05 12:24:48 -0800518 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
519 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800520 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800521 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800522 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800523 return fullFlowStats.removeAll(dpid);
524 }
525 return null;
526 }
527
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700528 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
529 OFTableStatsReply reply) {
530 //TODO: Get rid of synchronized
531 fullTableStats.putAll(dpid, reply.getEntries());
532 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
533 return fullTableStats.removeAll(dpid);
534 }
535 return null;
536 }
537
sangho6a0bb172015-02-05 12:24:48 -0800538 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
539 OFGroupStatsReply reply) {
540 //TODO: Get rid of synchronized
541 fullGroupStats.putAll(dpid, reply.getEntries());
542 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
543 return fullGroupStats.removeAll(dpid);
544 }
545 return null;
546 }
547
548 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
549 OFGroupDescStatsReply reply) {
550 //TODO: Get rid of synchronized
551 fullGroupDescStats.putAll(dpid, reply.getEntries());
552 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
553 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800554 }
555 return null;
556 }
557
sangho538108b2015-04-08 14:29:20 -0700558 private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
559 OFPortStatsReply reply) {
560 fullPortStats.putAll(dpid, reply.getEntries());
561 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
562 return fullPortStats.removeAll(dpid);
563 }
564 return null;
565 }
566
Ozge AYAZ60aded22017-06-20 08:35:30 +0000567 private synchronized Collection<OFQueueStatsEntry> publishQueueStats(Dpid dpid, OFQueueStatsReply reply) {
568 fullQueueStats.putAll(dpid, reply.getEntries());
569 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
570 return fullQueueStats.removeAll(dpid);
571 }
572 return null;
573 }
574
tom7ef8ff92014-09-17 13:08:06 -0700575 @Override
576 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700577 final OpenFlowSwitch sw = getSwitch(dpid);
578 if (sw == null) {
579 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
580 return;
581 }
582 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700583 }
584
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700585 class InternalDeviceListener implements DeviceListener {
586
587 @Override
588 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800589 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700590 }
591
592 @Override
593 public void event(DeviceEvent event) {
594 switch (event.type()) {
595 case DEVICE_ADDED:
596 break;
597 case DEVICE_AVAILABILITY_CHANGED:
598 break;
599 case DEVICE_REMOVED:
600 // Device administratively removed, disconnect
601 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
602 .ifPresent(OpenFlowSwitch::disconnectSwitch);
603 break;
604 case DEVICE_SUSPENDED:
605 break;
606 case DEVICE_UPDATED:
607 break;
608 case PORT_ADDED:
609 break;
610 case PORT_REMOVED:
611 break;
612 case PORT_STATS_UPDATED:
613 break;
614 case PORT_UPDATED:
615 break;
616 default:
617 break;
618
619 }
620
621 }
622
623 }
624
tom7ef8ff92014-09-17 13:08:06 -0700625 /**
626 * Implementation of an OpenFlow Agent which is responsible for
627 * keeping track of connected switches and the state in which
628 * they are.
629 */
630 public class OpenFlowSwitchAgent implements OpenFlowAgent {
631
632 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
633 private final Lock switchLock = new ReentrantLock();
634
635 @Override
636 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700637
tom7ef8ff92014-09-17 13:08:06 -0700638 if (connectedSwitches.get(dpid) != null) {
639 log.error("Trying to add connectedSwitch but found a previous "
640 + "value for dpid: {}", dpid);
641 return false;
642 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700643 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700644 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700645 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700646 l.switchAdded(dpid);
647 }
648 return true;
649 }
650 }
651
652 @Override
653 public boolean validActivation(Dpid dpid) {
654 if (connectedSwitches.get(dpid) == null) {
655 log.error("Trying to activate switch but is not in "
656 + "connected switches: dpid {}. Aborting ..",
657 dpid);
658 return false;
659 }
660 if (activeMasterSwitches.get(dpid) != null ||
661 activeEqualSwitches.get(dpid) != null) {
662 log.error("Trying to activate switch but it is already "
663 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800664 + "Found in activeEqual: {}. Aborting ..",
665 dpid,
666 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
667 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700668 return false;
669 }
670 return true;
671 }
672
673
674 @Override
675 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
676 switchLock.lock();
677 try {
678 if (!validActivation(dpid)) {
679 return false;
680 }
681 activeMasterSwitches.put(dpid, sw);
682 return true;
683 } finally {
684 switchLock.unlock();
685 }
686 }
687
688 @Override
689 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
690 switchLock.lock();
691 try {
692 if (!validActivation(dpid)) {
693 return false;
694 }
695 activeEqualSwitches.put(dpid, sw);
696 log.info("Added Activated EQUAL Switch {}", dpid);
697 return true;
698 } finally {
699 switchLock.unlock();
700 }
701 }
702
703 @Override
704 public void transitionToMasterSwitch(Dpid dpid) {
705 switchLock.lock();
706 try {
707 if (activeMasterSwitches.containsKey(dpid)) {
708 return;
709 }
710 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
711 if (sw == null) {
712 sw = getSwitch(dpid);
713 if (sw == null) {
714 log.error("Transition to master called on sw {}, but switch "
715 + "was not found in controller-cache", dpid);
716 return;
717 }
718 }
719 log.info("Transitioned switch {} to MASTER", dpid);
720 activeMasterSwitches.put(dpid, sw);
721 } finally {
722 switchLock.unlock();
723 }
724 }
725
726
727 @Override
728 public void transitionToEqualSwitch(Dpid dpid) {
729 switchLock.lock();
730 try {
731 if (activeEqualSwitches.containsKey(dpid)) {
732 return;
733 }
734 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
735 if (sw == null) {
736 sw = getSwitch(dpid);
737 if (sw == null) {
738 log.error("Transition to equal called on sw {}, but switch "
739 + "was not found in controller-cache", dpid);
740 return;
741 }
742 }
743 log.info("Transitioned switch {} to EQUAL", dpid);
744 activeEqualSwitches.put(dpid, sw);
745 } finally {
746 switchLock.unlock();
747 }
748
749 }
750
751 @Override
752 public void removeConnectedSwitch(Dpid dpid) {
753 connectedSwitches.remove(dpid);
754 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
755 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700756 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700757 sw = activeEqualSwitches.remove(dpid);
758 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700759 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700760 l.switchRemoved(dpid);
761 }
762 }
763
764 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700765 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
766 for (OpenFlowMessageListener listener : ofMessageListener) {
767 listener.handleOutgoingMessage(dpid, m);
768 }
769 }
770
771
772 @Override
tom7ef8ff92014-09-17 13:08:06 -0700773 public void processMessage(Dpid dpid, OFMessage m) {
774 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700775
776 for (OpenFlowMessageListener listener : ofMessageListener) {
777 listener.handleIncomingMessage(dpid, m);
778 }
tom7ef8ff92014-09-17 13:08:06 -0700779 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700780
781 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700782 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700783 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700784 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700785 }
786 }
tom7ef8ff92014-09-17 13:08:06 -0700787 }
788
Jian Li152b8852015-12-07 14:47:25 -0800789 /**
Jian Li2266bff2016-04-21 11:01:25 -0700790 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800791 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800792 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700793
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800794 protected final OFMessage msg;
795 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700796
797 public OFMessageHandler(Dpid dpid, OFMessage msg) {
798 this.msg = msg;
799 this.dpid = dpid;
800 }
801
802 @Override
803 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700804 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700805 listener.handleMessage(dpid, msg);
806 }
807 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700808 }
tom7ef8ff92014-09-17 13:08:06 -0700809}