blob: e3d16daf846817722a4e308d6f4571137a5566b9 [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;
Charles Chan3b00e1b2015-08-26 23:12:52 +080021import org.onosproject.cfg.ComponentConfigService;
Charles Chanecfdfb72015-11-24 19:05:50 -080022import org.onosproject.core.CoreService;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070023import org.onosproject.net.DeviceId;
24import org.onosproject.net.config.ConfigFactory;
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -070025import org.onosproject.net.config.NetworkConfigEvent;
26import org.onosproject.net.config.NetworkConfigListener;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070027import org.onosproject.net.config.NetworkConfigRegistry;
28import org.onosproject.net.config.basics.SubjectFactories;
alshabibb452fd72015-04-22 20:46:20 -070029import org.onosproject.net.driver.DriverService;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070030import org.onosproject.openflow.config.OpenFlowDeviceConfig;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
32import org.onosproject.openflow.controller.Dpid;
33import org.onosproject.openflow.controller.OpenFlowController;
34import org.onosproject.openflow.controller.OpenFlowEventListener;
Jian Lia78cdb22016-04-21 13:03:58 -070035import org.onosproject.openflow.controller.OpenFlowMessageListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.openflow.controller.OpenFlowPacketContext;
37import org.onosproject.openflow.controller.OpenFlowSwitch;
38import org.onosproject.openflow.controller.OpenFlowSwitchListener;
39import org.onosproject.openflow.controller.PacketListener;
40import org.onosproject.openflow.controller.RoleState;
41import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080042import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070043import org.osgi.service.component.annotations.Activate;
44import org.osgi.service.component.annotations.Component;
45import org.osgi.service.component.annotations.Deactivate;
46import org.osgi.service.component.annotations.Modified;
47import org.osgi.service.component.annotations.Reference;
48import org.osgi.service.component.annotations.ReferenceCardinality;
Marc De Leenheerb9311372015-07-09 11:36:49 -070049import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsEntry;
50import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsReply;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070051import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
52import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080053import org.projectfloodlight.openflow.protocol.OFFactories;
Cem Türker3baff672017-10-12 15:09:01 +030054import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
alshabib64def642014-12-02 23:27:37 -080056import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
57import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080058import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
59import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
60import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
61import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070062import org.projectfloodlight.openflow.protocol.OFMessage;
63import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070064import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070065import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
tom7ef8ff92014-09-17 13:08:06 -070066import org.projectfloodlight.openflow.protocol.OFPortStatus;
Cem Türker3baff672017-10-12 15:09:01 +030067import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
68import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070069import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080070import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070071import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
72import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070073import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
74import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070075import org.slf4j.Logger;
76import org.slf4j.LoggerFactory;
77
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -070078import java.util.ArrayList;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080079import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070080import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070081import java.util.List;
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -070082import java.util.Objects;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080083import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070084import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080085import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080086import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070087import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080088import java.util.concurrent.ExecutorService;
89import java.util.concurrent.Executors;
90import java.util.concurrent.locks.Lock;
91import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070092
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080093import static org.onlab.util.Tools.groupedThreads;
Ray Milkey309f9a02018-10-12 14:09:37 -070094import static org.onosproject.openflow.controller.impl.OsgiPropertyConstants.*;
Ozge AYAZ60aded22017-06-20 08:35:30 +000095
Ray Milkey309f9a02018-10-12 14:09:37 -070096@Component(
97 immediate = true,
98 service = OpenFlowController.class,
99 property = {
100 OFPORTS + "=" + OFPORTS_DEFAULT,
Ray Milkey2d7bca12018-10-17 14:51:52 -0700101 WORKER_THREADS + ":Integer=" + WORKER_THREADS_DEFAULT,
Ray Milkey309f9a02018-10-12 14:09:37 -0700102 TLS_MODE + "=" + TLS_MODE_DEFAULT,
103 KEY_STORE + "=" + KEY_STORE_DEFAULT,
104 KEY_STORE_PASSWORD + "=" + KEY_STORE_PASSWORD_DEFAULT,
105 TRUST_STORE + "=" + TRUST_STORE_DEFAULT,
106 TRUST_STORE_PASSWORD + "=" + TRUST_STORE_PASSWORD_DEFAULT,
107 }
108)
tom7ef8ff92014-09-17 13:08:06 -0700109public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -0800110 private static final String APP_ID = "org.onosproject.openflow-base";
Andrea Campanella86e0c562017-11-23 16:38:24 +0100111 protected static final String SCHEME = "of";
tom7ef8ff92014-09-17 13:08:06 -0700112
113 private static final Logger log =
114 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800117 protected CoreService coreService;
118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb452fd72015-04-22 20:46:20 -0700120 protected DriverService driverService;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chan3b00e1b2015-08-26 23:12:52 +0800123 protected ComponentConfigService cfgService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700126 protected NetworkConfigRegistry netCfgService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 //@Property(name = "openflowPorts", value = DEFAULT_OFPORT,
129 // label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
Ray Milkey309f9a02018-10-12 14:09:37 -0700130 //private String openflowPortsValue = OFPORTS_DEFAULT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 //@Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
133 // label = "Number of controller worker threads")
Ray Milkey309f9a02018-10-12 14:09:37 -0700134 //private int workerThreads = DEFAULT_WORKER_THREADS;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 //@Property(name = "tlsMode", value = "",
137 // label = "TLS mode for OpenFlow channel; options are: disabled [default], enabled, strict")
Ray Milkey309f9a02018-10-12 14:09:37 -0700138 //private String tlsModeString;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 //@Property(name = "keyStore", value = "",
141 // label = "File path to key store for TLS connections")
Ray Milkey309f9a02018-10-12 14:09:37 -0700142 //private String keyStore;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 //@Property(name = "keyStorePassword", value = "",
145 // label = "Key store password")
Ray Milkey309f9a02018-10-12 14:09:37 -0700146 //private String keyStorePassword;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 //@Property(name = "trustStore", value = "",
149 // label = "File path to trust store for TLS connections")
Ray Milkey309f9a02018-10-12 14:09:37 -0700150 //private String trustStore;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 //@Property(name = "trustStorePassword", value = "",
153 // label = "Trust store password")
Ray Milkey309f9a02018-10-12 14:09:37 -0700154 //private String trustStorePassword;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700155
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800156 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800157 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800158
159 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800160 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700161
Prince Pereirae7798032016-07-08 16:31:58 +0530162 //Separate executor thread for handling error messages and barrier replies for same failed
163 // transactions to avoid context switching of thread
164 protected ExecutorService executorErrorMsgs =
165 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
166
167 //concurrent hashmap to track failed transactions
168 protected ConcurrentMap<Long, Boolean> errorMsgs =
169 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800170 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700171 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800172 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700173 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800174 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700175 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700176
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700177 // Key: dpid, value: map with key: long (XID), value: completable future
178 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
179 new ConcurrentHashMap<>();
180
tom7ef8ff92014-09-17 13:08:06 -0700181 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700182 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700183
184 protected Multimap<Integer, PacketListener> ofPacketListener =
185 ArrayListMultimap.create();
186
Jonathan Hart6d44d192015-05-11 18:01:19 -0700187 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700188
Jian Lia78cdb22016-04-21 13:03:58 -0700189 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800190
sangho6a0bb172015-02-05 12:24:48 -0800191 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
192 ArrayListMultimap.create();
193
Cem Türker3baff672017-10-12 15:09:01 +0300194 protected Multimap<Dpid, OFFlowLightweightStatsEntry> fullFlowLightweightStats =
195 ArrayListMultimap.create();
196
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700197 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
198 ArrayListMultimap.create();
199
sangho6a0bb172015-02-05 12:24:48 -0800200 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
201 ArrayListMultimap.create();
202
203 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800204 ArrayListMultimap.create();
205
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700206 // deprecated in 1.11.0, no longer referenced from anywhere
207 @Deprecated
sangho538108b2015-04-08 14:29:20 -0700208 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
209 ArrayListMultimap.create();
210
Ozge AYAZ60aded22017-06-20 08:35:30 +0000211 protected Multimap<Dpid, OFQueueStatsEntry> fullQueueStats =
212 ArrayListMultimap.create();
213
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700214 protected final ConfigFactory factory =
215 new ConfigFactory<DeviceId, OpenFlowDeviceConfig>(
216 SubjectFactories.DEVICE_SUBJECT_FACTORY,
217 OpenFlowDeviceConfig.class, OpenFlowDeviceConfig.CONFIG_KEY) {
218 @Override
219 public OpenFlowDeviceConfig createConfig() {
220 return new OpenFlowDeviceConfig();
221 }
222 };
223
tom7ef8ff92014-09-17 13:08:06 -0700224 private final Controller ctrl = new Controller();
225
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700226 private final NetworkConfigListener netCfgListener = new NetworkConfigListener() {
227 @Override
228 public boolean isRelevant(NetworkConfigEvent event) {
229 return OpenFlowDeviceConfig.class.equals(event.configClass());
230 }
231
232 @Override
233 public void event(NetworkConfigEvent event) {
234 // We only receive NetworkConfigEvents
235 OpenFlowDeviceConfig prevConfig = null;
236 if (event.prevConfig().isPresent()) {
237 prevConfig = (OpenFlowDeviceConfig) event.prevConfig().get();
238 }
239
240 OpenFlowDeviceConfig newConfig = null;
241 if (event.config().isPresent()) {
242 newConfig = (OpenFlowDeviceConfig) event.config().get();
243 }
244
245 boolean closeConnection = false;
246 if (prevConfig != null && newConfig != null) {
247 if (!Objects.equals(prevConfig.keyAlias(), newConfig.keyAlias())) {
248 closeConnection = true;
249 }
250 } else if (prevConfig != null) {
251 // config was removed
252 closeConnection = true;
253 }
254 if (closeConnection) {
255 if (event.subject() instanceof DeviceId) {
256 DeviceId deviceId = (DeviceId) event.subject();
257 Dpid dpid = Dpid.dpid(deviceId.uri());
258 OpenFlowSwitch sw = getSwitch(dpid);
259 if (sw != null && ctrl.tlsParams.mode == Controller.TlsMode.STRICT) {
260 sw.disconnectSwitch();
261 log.info("Disconnecting switch {} because key has been updated or removed", dpid);
262 }
263 }
264 }
265 }
266 };
267
tom7ef8ff92014-09-17 13:08:06 -0700268 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800269 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700270 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800271 cfgService.registerProperties(getClass());
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700272 netCfgService.registerConfigFactory(factory);
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700273 netCfgService.addListener(netCfgListener);
Brian O'Connorff278502015-09-22 14:49:52 -0700274 ctrl.setConfigParams(context.getProperties());
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700275 ctrl.start(agent, driverService, netCfgService);
tom7ef8ff92014-09-17 13:08:06 -0700276 }
277
Andrea Campanella3556f362016-04-28 15:18:10 -0700278 private void cleanup() {
279 // Close listening channel and all OF channels. Clean information about switches
280 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800281 ctrl.stop();
282 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700283 connectedSwitches.clear();
284 activeMasterSwitches.clear();
285 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800286 }
287
tom7ef8ff92014-09-17 13:08:06 -0700288 @Deactivate
289 public void deactivate() {
Thiago Santos61725402016-08-05 17:58:56 -0300290 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800291 cfgService.unregisterProperties(getClass(), false);
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700292 netCfgService.removeListener(netCfgListener);
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700293 netCfgService.unregisterConfigFactory(factory);
tom7ef8ff92014-09-17 13:08:06 -0700294 }
295
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800296 @Modified
297 public void modified(ComponentContext context) {
Brian O'Connorff278502015-09-22 14:49:52 -0700298 ctrl.setConfigParams(context.getProperties());
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800299 }
300
tom7ef8ff92014-09-17 13:08:06 -0700301 @Override
302 public Iterable<OpenFlowSwitch> getSwitches() {
303 return connectedSwitches.values();
304 }
305
306 @Override
307 public Iterable<OpenFlowSwitch> getMasterSwitches() {
308 return activeMasterSwitches.values();
309 }
310
311 @Override
312 public Iterable<OpenFlowSwitch> getEqualSwitches() {
313 return activeEqualSwitches.values();
314 }
315
316 @Override
317 public OpenFlowSwitch getSwitch(Dpid dpid) {
318 return connectedSwitches.get(dpid);
319 }
320
321 @Override
322 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
323 return activeMasterSwitches.get(dpid);
324 }
325
326 @Override
327 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
328 return activeEqualSwitches.get(dpid);
329 }
330
331 @Override
332 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700333 if (!ofSwitchListener.contains(listener)) {
334 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700335 }
336 }
337
338 @Override
339 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700340 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700341 }
342
343 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700344 public void addMessageListener(OpenFlowMessageListener listener) {
345 ofMessageListener.add(listener);
346 }
347
348 @Override
349 public void removeMessageListener(OpenFlowMessageListener listener) {
350 ofMessageListener.remove(listener);
351 }
352
353 @Override
tom7ef8ff92014-09-17 13:08:06 -0700354 public void addPacketListener(int priority, PacketListener listener) {
355 ofPacketListener.put(priority, listener);
356 }
357
358 @Override
359 public void removePacketListener(PacketListener listener) {
360 ofPacketListener.values().remove(listener);
361 }
362
363 @Override
alshabibeec3a062014-09-17 18:01:26 -0700364 public void addEventListener(OpenFlowEventListener listener) {
365 ofEventListener.add(listener);
366 }
367
368 @Override
369 public void removeEventListener(OpenFlowEventListener listener) {
370 ofEventListener.remove(listener);
371 }
372
373 @Override
tom7ef8ff92014-09-17 13:08:06 -0700374 public void write(Dpid dpid, OFMessage msg) {
375 this.getSwitch(dpid).sendMsg(msg);
376 }
377
378 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700379 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
380 write(dpid, msg);
381
382 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700383 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700384
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700385 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700386 xids.put(msg.getXid(), future);
387
388 return future;
389 }
390
391 @Override
tom7ef8ff92014-09-17 13:08:06 -0700392 public void processPacket(Dpid dpid, OFMessage msg) {
sangyun-han69ed4462016-07-27 12:10:12 +0900393 OpenFlowSwitch sw = this.getSwitch(dpid);
Laszlo Pappb68fe7e2017-11-24 17:06:59 +0000394 if (log.isTraceEnabled()) {
395 log.trace("Processing message from switch {} via openflow: {}", dpid, msg);
396 }
sangyun-han69ed4462016-07-27 12:10:12 +0900397
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700398 // Check if someone is waiting for this message
399 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700400 if (xids != null) {
401 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
402 if (future != null) {
403 future.complete(msg);
404 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700405 }
406
tom7ef8ff92014-09-17 13:08:06 -0700407 switch (msg.getType()) {
408 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700409 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700410 l.portChanged(dpid, (OFPortStatus) msg);
411 }
412 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700413 case FEATURES_REPLY:
414 for (OpenFlowSwitchListener l : ofSwitchListener) {
415 l.switchChanged(dpid);
416 }
417 break;
tom7ef8ff92014-09-17 13:08:06 -0700418 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900419 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700420 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900421 break;
422 }
tom7ef8ff92014-09-17 13:08:06 -0700423 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
Ozge AYAZ60aded22017-06-20 08:35:30 +0000424 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700425 for (PacketListener p : ofPacketListener.values()) {
426 p.handlePacket(pktCtx);
427 }
428 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800429 // TODO: Consider using separate threadpool for sensitive messages.
430 // ie. Back to back error could cause us to starve.
431 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800432 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800433 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530434 case ERROR:
435 log.debug("Received error message from {}: {}", dpid, msg);
436 errorMsgs.putIfAbsent(msg.getXid(), true);
437 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
438 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700439 case STATS_REPLY:
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700440 processStatsReply(dpid, (OFStatsReply) msg);
alshabib64def642014-12-02 23:27:37 -0800441 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700442 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530443 if (errorMsgs.containsKey(msg.getXid())) {
444 //To make oferror msg handling and corresponding barrier reply serialized,
445 // executorErrorMsgs is used for both transaction
446 errorMsgs.remove(msg.getXid());
447 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
448 } else {
449 executorBarrier.execute(new OFMessageHandler(dpid, msg));
450 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700451 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700452 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900453 if (sw == null) {
454 log.error("Switch {} is not found", dpid);
455 break;
456 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700457 long experimenter = ((OFExperimenter) msg).getExperimenter();
458 if (experimenter == 0x748771) {
459 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700460 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900461 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
462 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700463 portDesc.setPortNo(circuitPortStatus.getPortNo())
464 .setHwAddr(circuitPortStatus.getHwAddr())
465 .setName(circuitPortStatus.getName())
466 .setConfig(circuitPortStatus.getConfig())
467 .setState(circuitPortStatus.getState());
468 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
469 for (OpenFlowSwitchListener l : ofSwitchListener) {
470 l.portChanged(dpid, portStatus.build());
471 }
472 } else {
473 log.warn("Handling experimenter type {} not yet implemented",
474 ((OFExperimenter) msg).getExperimenter(), msg);
475 }
476 break;
tom7ef8ff92014-09-17 13:08:06 -0700477 default:
478 log.warn("Handling message type {} not yet implemented {}",
479 msg.getType(), msg);
480 }
481 }
482
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700483 private void processStatsReply(Dpid dpid, OFStatsReply reply) {
484 switch (reply.getStatsType()) {
485 case QUEUE:
486 Collection<OFQueueStatsEntry> queueStatsEntries = publishQueueStats(dpid, (OFQueueStatsReply) reply);
487 if (queueStatsEntries != null) {
488 OFQueueStatsReply.Builder rep =
489 OFFactories.getFactory(reply.getVersion()).buildQueueStatsReply();
490 rep.setEntries(ImmutableList.copyOf(queueStatsEntries));
491 rep.setXid(reply.getXid());
492 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
493 }
494 break;
495
496 case PORT_DESC:
497 for (OpenFlowSwitchListener l : ofSwitchListener) {
498 l.switchChanged(dpid);
499 }
500 break;
501
502 case FLOW:
503 Collection<OFFlowStatsEntry> flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
504 if (flowStats != null) {
505 OFFlowStatsReply.Builder rep =
506 OFFactories.getFactory(reply.getVersion()).buildFlowStatsReply();
507 rep.setEntries(ImmutableList.copyOf(flowStats));
508 rep.setXid(reply.getXid());
509 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
510 }
511 break;
Cem Türker3baff672017-10-12 15:09:01 +0300512 case FLOW_LIGHTWEIGHT:
513 Collection<OFFlowLightweightStatsEntry> flowLightweightStats =
514 publishFlowStatsLightweight(dpid, (OFFlowLightweightStatsReply) reply);
515 if (flowLightweightStats != null) {
516 OFFlowLightweightStatsReply.Builder rep =
517 OFFactories.getFactory(reply.getVersion()).buildFlowLightweightStatsReply();
518 rep.setEntries(ImmutableList.copyOf(flowLightweightStats));
519 rep.setXid(reply.getXid());
520 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
521 }
522 break;
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700523 case TABLE:
524 Collection<OFTableStatsEntry> tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
525 if (tableStats != null) {
526 OFTableStatsReply.Builder rep =
527 OFFactories.getFactory(reply.getVersion()).buildTableStatsReply();
528 rep.setEntries(ImmutableList.copyOf(tableStats));
529 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
530 }
531 break;
532
533 case GROUP:
534 Collection<OFGroupStatsEntry> groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
535 if (groupStats != null) {
536 OFGroupStatsReply.Builder rep =
537 OFFactories.getFactory(reply.getVersion()).buildGroupStatsReply();
538 rep.setEntries(ImmutableList.copyOf(groupStats));
539 rep.setXid(reply.getXid());
540 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
541 }
542 break;
543
544 case GROUP_DESC:
545 Collection<OFGroupDescStatsEntry> groupDescStats = publishGroupDescStats(dpid,
546 (OFGroupDescStatsReply) reply);
547 if (groupDescStats != null) {
548 OFGroupDescStatsReply.Builder rep =
549 OFFactories.getFactory(reply.getVersion()).buildGroupDescStatsReply();
550 rep.setEntries(ImmutableList.copyOf(groupDescStats));
551 rep.setXid(reply.getXid());
552 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
553 }
554 break;
555
556 case PORT:
557 executorMsgs.execute(new OFMessageHandler(dpid, reply));
558 break;
559
560 case METER:
561 executorMsgs.execute(new OFMessageHandler(dpid, reply));
562 break;
563
564 case EXPERIMENTER:
565 if (reply instanceof OFCalientFlowStatsReply) {
566 OpenFlowSwitch sw = this.getSwitch(dpid);
567 // Convert Calient flow statistics to regular flow stats
568 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
569 if (sw == null) {
570 log.error("Switch {} is not found", dpid);
571 break;
572 }
573 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
574 List<OFFlowStatsEntry> entries = new ArrayList<>();
575 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) reply).getEntries()) {
576
577 // Single instruction, i.e., output to port
578 OFActionOutput action = sw.factory()
579 .actions()
580 .buildOutput()
581 .setPort(entry.getOutPort())
582 .build();
583 OFInstruction instruction = sw.factory()
584 .instructions()
585 .applyActions(Collections.singletonList(action));
586 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
587 .setMatch(entry.getMatch())
588 .setTableId(entry.getTableId())
589 .setDurationSec(entry.getDurationSec())
590 .setDurationNsec(entry.getDurationNsec())
591 .setPriority(entry.getPriority())
592 .setIdleTimeout(entry.getIdleTimeout())
593 .setHardTimeout(entry.getHardTimeout())
594 .setFlags(entry.getFlags())
595 .setCookie(entry.getCookie())
596 .setInstructions(Collections.singletonList(instruction))
597 .build();
598 entries.add(fs);
599 }
600 fsr.setEntries(entries);
601
602 flowStats = publishFlowStats(dpid, fsr.build());
603 if (flowStats != null) {
604 OFFlowStatsReply.Builder rep =
605 sw.factory().buildFlowStatsReply();
606 rep.setEntries(ImmutableList.copyOf(flowStats));
607 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
608 }
609 } else {
610 executorMsgs.execute(new OFMessageHandler(dpid, reply));
611 }
612 break;
613 default:
614 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
615 break;
616 }
617 }
618
sangho6a0bb172015-02-05 12:24:48 -0800619 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
620 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800621 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800622 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800623 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800624 return fullFlowStats.removeAll(dpid);
625 }
626 return null;
627 }
628
Cem Türker3baff672017-10-12 15:09:01 +0300629 private synchronized Collection<OFFlowLightweightStatsEntry> publishFlowStatsLightweight(
630 Dpid dpid,
631 OFFlowLightweightStatsReply reply) {
632 //TODO: Get rid of synchronized
633 fullFlowLightweightStats.putAll(dpid, reply.getEntries());
634 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
635 return fullFlowLightweightStats.removeAll(dpid);
636 }
637 return null;
638 }
639
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700640 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
641 OFTableStatsReply reply) {
642 //TODO: Get rid of synchronized
643 fullTableStats.putAll(dpid, reply.getEntries());
644 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
645 return fullTableStats.removeAll(dpid);
646 }
647 return null;
648 }
649
sangho6a0bb172015-02-05 12:24:48 -0800650 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
651 OFGroupStatsReply reply) {
652 //TODO: Get rid of synchronized
653 fullGroupStats.putAll(dpid, reply.getEntries());
654 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
655 return fullGroupStats.removeAll(dpid);
656 }
657 return null;
658 }
659
660 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
661 OFGroupDescStatsReply reply) {
662 //TODO: Get rid of synchronized
663 fullGroupDescStats.putAll(dpid, reply.getEntries());
664 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
665 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800666 }
667 return null;
668 }
669
Ozge AYAZ60aded22017-06-20 08:35:30 +0000670 private synchronized Collection<OFQueueStatsEntry> publishQueueStats(Dpid dpid, OFQueueStatsReply reply) {
671 fullQueueStats.putAll(dpid, reply.getEntries());
672 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
673 return fullQueueStats.removeAll(dpid);
674 }
675 return null;
676 }
677
tom7ef8ff92014-09-17 13:08:06 -0700678 @Override
679 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700680 final OpenFlowSwitch sw = getSwitch(dpid);
681 if (sw == null) {
682 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
683 return;
684 }
685 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700686 }
687
688 /**
689 * Implementation of an OpenFlow Agent which is responsible for
690 * keeping track of connected switches and the state in which
691 * they are.
692 */
693 public class OpenFlowSwitchAgent implements OpenFlowAgent {
694
695 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
696 private final Lock switchLock = new ReentrantLock();
697
698 @Override
699 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700700
tom7ef8ff92014-09-17 13:08:06 -0700701 if (connectedSwitches.get(dpid) != null) {
702 log.error("Trying to add connectedSwitch but found a previous "
703 + "value for dpid: {}", dpid);
704 return false;
705 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700706 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700707 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700708 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700709 l.switchAdded(dpid);
710 }
711 return true;
712 }
713 }
714
715 @Override
716 public boolean validActivation(Dpid dpid) {
717 if (connectedSwitches.get(dpid) == null) {
718 log.error("Trying to activate switch but is not in "
719 + "connected switches: dpid {}. Aborting ..",
720 dpid);
721 return false;
722 }
723 if (activeMasterSwitches.get(dpid) != null ||
724 activeEqualSwitches.get(dpid) != null) {
725 log.error("Trying to activate switch but it is already "
726 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800727 + "Found in activeEqual: {}. Aborting ..",
728 dpid,
729 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
730 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700731 return false;
732 }
733 return true;
734 }
735
736
737 @Override
738 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
739 switchLock.lock();
740 try {
741 if (!validActivation(dpid)) {
742 return false;
743 }
744 activeMasterSwitches.put(dpid, sw);
745 return true;
746 } finally {
747 switchLock.unlock();
748 }
749 }
750
751 @Override
752 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
753 switchLock.lock();
754 try {
755 if (!validActivation(dpid)) {
756 return false;
757 }
758 activeEqualSwitches.put(dpid, sw);
759 log.info("Added Activated EQUAL Switch {}", dpid);
760 return true;
761 } finally {
762 switchLock.unlock();
763 }
764 }
765
766 @Override
767 public void transitionToMasterSwitch(Dpid dpid) {
768 switchLock.lock();
769 try {
770 if (activeMasterSwitches.containsKey(dpid)) {
771 return;
772 }
773 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
774 if (sw == null) {
775 sw = getSwitch(dpid);
776 if (sw == null) {
777 log.error("Transition to master called on sw {}, but switch "
778 + "was not found in controller-cache", dpid);
779 return;
780 }
781 }
782 log.info("Transitioned switch {} to MASTER", dpid);
783 activeMasterSwitches.put(dpid, sw);
784 } finally {
785 switchLock.unlock();
786 }
787 }
788
789
790 @Override
791 public void transitionToEqualSwitch(Dpid dpid) {
792 switchLock.lock();
793 try {
794 if (activeEqualSwitches.containsKey(dpid)) {
795 return;
796 }
797 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
798 if (sw == null) {
799 sw = getSwitch(dpid);
800 if (sw == null) {
801 log.error("Transition to equal called on sw {}, but switch "
802 + "was not found in controller-cache", dpid);
803 return;
804 }
805 }
806 log.info("Transitioned switch {} to EQUAL", dpid);
807 activeEqualSwitches.put(dpid, sw);
808 } finally {
809 switchLock.unlock();
810 }
811
812 }
813
814 @Override
815 public void removeConnectedSwitch(Dpid dpid) {
816 connectedSwitches.remove(dpid);
817 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
818 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700819 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700820 sw = activeEqualSwitches.remove(dpid);
821 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700822 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700823 l.switchRemoved(dpid);
824 }
825 }
826
827 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700828 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
829 for (OpenFlowMessageListener listener : ofMessageListener) {
830 listener.handleOutgoingMessage(dpid, m);
831 }
832 }
833
834
835 @Override
tom7ef8ff92014-09-17 13:08:06 -0700836 public void processMessage(Dpid dpid, OFMessage m) {
837 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700838
839 for (OpenFlowMessageListener listener : ofMessageListener) {
840 listener.handleIncomingMessage(dpid, m);
841 }
tom7ef8ff92014-09-17 13:08:06 -0700842 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700843
844 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700845 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700846 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700847 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700848 }
849 }
tom7ef8ff92014-09-17 13:08:06 -0700850 }
851
Jian Li152b8852015-12-07 14:47:25 -0800852 /**
Jian Li2266bff2016-04-21 11:01:25 -0700853 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800854 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800855 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700856
Ray Milkey9c9cde42018-01-12 14:22:06 -0800857 final OFMessage msg;
858 final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700859
860 public OFMessageHandler(Dpid dpid, OFMessage msg) {
861 this.msg = msg;
862 this.dpid = dpid;
863 }
864
865 @Override
866 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700867 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700868 listener.handleMessage(dpid, msg);
869 }
870 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700871 }
tom7ef8ff92014-09-17 13:08:06 -0700872}