blob: ee9422f85a14a503bf1dd92ab6f7962862d8afc0 [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
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700128 /** Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653. */
Ray Milkeybd508ed2019-03-19 14:22:02 -0700129 private String openflowPorts = OFPORTS_DEFAULT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800130
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700131 /** Number of controller worker threads. */
132 private int workerThreads = WORKER_THREADS_DEFAULT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800133
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700134 /** TLS mode for OpenFlow channel; options are: disabled [default], enabled, strict. */
Ray Milkeybd508ed2019-03-19 14:22:02 -0700135 private String tlsMode;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700136
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700137 /** File path to key store for TLS connections. */
138 private String keyStore;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700139
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700140 /** Key store password. */
141 private String keyStorePassword;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700142
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700143 /** File path to trust store for TLS connections. */
144 private String trustStore;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700145
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700146 /** Trust store password. */
147 private String trustStorePassword;
Brian O'Connorf7215b82018-05-10 19:12:44 -0700148
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800149 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800150 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800151
152 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800153 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700154
Prince Pereirae7798032016-07-08 16:31:58 +0530155 //Separate executor thread for handling error messages and barrier replies for same failed
156 // transactions to avoid context switching of thread
157 protected ExecutorService executorErrorMsgs =
158 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
159
160 //concurrent hashmap to track failed transactions
161 protected ConcurrentMap<Long, Boolean> errorMsgs =
162 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800163 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700164 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800165 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700166 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800167 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700168 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700169
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700170 // Key: dpid, value: map with key: long (XID), value: completable future
171 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
172 new ConcurrentHashMap<>();
173
tom7ef8ff92014-09-17 13:08:06 -0700174 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700175 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700176
177 protected Multimap<Integer, PacketListener> ofPacketListener =
178 ArrayListMultimap.create();
179
Jonathan Hart6d44d192015-05-11 18:01:19 -0700180 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700181
Jian Lia78cdb22016-04-21 13:03:58 -0700182 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800183
sangho6a0bb172015-02-05 12:24:48 -0800184 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
185 ArrayListMultimap.create();
186
Cem Türker3baff672017-10-12 15:09:01 +0300187 protected Multimap<Dpid, OFFlowLightweightStatsEntry> fullFlowLightweightStats =
188 ArrayListMultimap.create();
189
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700190 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
191 ArrayListMultimap.create();
192
sangho6a0bb172015-02-05 12:24:48 -0800193 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
194 ArrayListMultimap.create();
195
196 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800197 ArrayListMultimap.create();
198
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700199 // deprecated in 1.11.0, no longer referenced from anywhere
200 @Deprecated
sangho538108b2015-04-08 14:29:20 -0700201 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
202 ArrayListMultimap.create();
203
Ozge AYAZ60aded22017-06-20 08:35:30 +0000204 protected Multimap<Dpid, OFQueueStatsEntry> fullQueueStats =
205 ArrayListMultimap.create();
206
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700207 protected final ConfigFactory factory =
208 new ConfigFactory<DeviceId, OpenFlowDeviceConfig>(
209 SubjectFactories.DEVICE_SUBJECT_FACTORY,
210 OpenFlowDeviceConfig.class, OpenFlowDeviceConfig.CONFIG_KEY) {
211 @Override
212 public OpenFlowDeviceConfig createConfig() {
213 return new OpenFlowDeviceConfig();
214 }
215 };
216
tom7ef8ff92014-09-17 13:08:06 -0700217 private final Controller ctrl = new Controller();
218
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700219 private final NetworkConfigListener netCfgListener = new NetworkConfigListener() {
220 @Override
221 public boolean isRelevant(NetworkConfigEvent event) {
222 return OpenFlowDeviceConfig.class.equals(event.configClass());
223 }
224
225 @Override
226 public void event(NetworkConfigEvent event) {
227 // We only receive NetworkConfigEvents
228 OpenFlowDeviceConfig prevConfig = null;
229 if (event.prevConfig().isPresent()) {
230 prevConfig = (OpenFlowDeviceConfig) event.prevConfig().get();
231 }
232
233 OpenFlowDeviceConfig newConfig = null;
234 if (event.config().isPresent()) {
235 newConfig = (OpenFlowDeviceConfig) event.config().get();
236 }
237
238 boolean closeConnection = false;
239 if (prevConfig != null && newConfig != null) {
240 if (!Objects.equals(prevConfig.keyAlias(), newConfig.keyAlias())) {
241 closeConnection = true;
242 }
243 } else if (prevConfig != null) {
244 // config was removed
245 closeConnection = true;
246 }
247 if (closeConnection) {
248 if (event.subject() instanceof DeviceId) {
249 DeviceId deviceId = (DeviceId) event.subject();
250 Dpid dpid = Dpid.dpid(deviceId.uri());
251 OpenFlowSwitch sw = getSwitch(dpid);
252 if (sw != null && ctrl.tlsParams.mode == Controller.TlsMode.STRICT) {
253 sw.disconnectSwitch();
254 log.info("Disconnecting switch {} because key has been updated or removed", dpid);
255 }
256 }
257 }
258 }
259 };
260
tom7ef8ff92014-09-17 13:08:06 -0700261 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800262 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700263 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800264 cfgService.registerProperties(getClass());
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700265 netCfgService.registerConfigFactory(factory);
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700266 netCfgService.addListener(netCfgListener);
Brian O'Connorff278502015-09-22 14:49:52 -0700267 ctrl.setConfigParams(context.getProperties());
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700268 ctrl.start(agent, driverService, netCfgService);
tom7ef8ff92014-09-17 13:08:06 -0700269 }
270
Andrea Campanella3556f362016-04-28 15:18:10 -0700271 private void cleanup() {
272 // Close listening channel and all OF channels. Clean information about switches
273 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800274 ctrl.stop();
275 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700276 connectedSwitches.clear();
277 activeMasterSwitches.clear();
278 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800279 }
280
tom7ef8ff92014-09-17 13:08:06 -0700281 @Deactivate
282 public void deactivate() {
Thiago Santos61725402016-08-05 17:58:56 -0300283 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800284 cfgService.unregisterProperties(getClass(), false);
Brian O'Connor1bd4a9f2018-05-10 22:40:41 -0700285 netCfgService.removeListener(netCfgListener);
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700286 netCfgService.unregisterConfigFactory(factory);
tom7ef8ff92014-09-17 13:08:06 -0700287 }
288
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800289 @Modified
290 public void modified(ComponentContext context) {
Brian O'Connorff278502015-09-22 14:49:52 -0700291 ctrl.setConfigParams(context.getProperties());
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800292 }
293
tom7ef8ff92014-09-17 13:08:06 -0700294 @Override
295 public Iterable<OpenFlowSwitch> getSwitches() {
296 return connectedSwitches.values();
297 }
298
299 @Override
300 public Iterable<OpenFlowSwitch> getMasterSwitches() {
301 return activeMasterSwitches.values();
302 }
303
304 @Override
305 public Iterable<OpenFlowSwitch> getEqualSwitches() {
306 return activeEqualSwitches.values();
307 }
308
309 @Override
310 public OpenFlowSwitch getSwitch(Dpid dpid) {
311 return connectedSwitches.get(dpid);
312 }
313
314 @Override
315 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
316 return activeMasterSwitches.get(dpid);
317 }
318
319 @Override
320 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
321 return activeEqualSwitches.get(dpid);
322 }
323
324 @Override
325 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700326 if (!ofSwitchListener.contains(listener)) {
327 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700328 }
329 }
330
331 @Override
332 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700333 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700334 }
335
336 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700337 public void addMessageListener(OpenFlowMessageListener listener) {
338 ofMessageListener.add(listener);
339 }
340
341 @Override
342 public void removeMessageListener(OpenFlowMessageListener listener) {
343 ofMessageListener.remove(listener);
344 }
345
346 @Override
tom7ef8ff92014-09-17 13:08:06 -0700347 public void addPacketListener(int priority, PacketListener listener) {
348 ofPacketListener.put(priority, listener);
349 }
350
351 @Override
352 public void removePacketListener(PacketListener listener) {
353 ofPacketListener.values().remove(listener);
354 }
355
356 @Override
alshabibeec3a062014-09-17 18:01:26 -0700357 public void addEventListener(OpenFlowEventListener listener) {
358 ofEventListener.add(listener);
359 }
360
361 @Override
362 public void removeEventListener(OpenFlowEventListener listener) {
363 ofEventListener.remove(listener);
364 }
365
366 @Override
tom7ef8ff92014-09-17 13:08:06 -0700367 public void write(Dpid dpid, OFMessage msg) {
368 this.getSwitch(dpid).sendMsg(msg);
369 }
370
371 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700372 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
373 write(dpid, msg);
374
375 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700376 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700377
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700378 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700379 xids.put(msg.getXid(), future);
380
381 return future;
382 }
383
384 @Override
tom7ef8ff92014-09-17 13:08:06 -0700385 public void processPacket(Dpid dpid, OFMessage msg) {
sangyun-han69ed4462016-07-27 12:10:12 +0900386 OpenFlowSwitch sw = this.getSwitch(dpid);
Laszlo Pappb68fe7e2017-11-24 17:06:59 +0000387 if (log.isTraceEnabled()) {
388 log.trace("Processing message from switch {} via openflow: {}", dpid, msg);
389 }
sangyun-han69ed4462016-07-27 12:10:12 +0900390
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700391 // Check if someone is waiting for this message
392 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700393 if (xids != null) {
394 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
395 if (future != null) {
396 future.complete(msg);
397 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700398 }
399
tom7ef8ff92014-09-17 13:08:06 -0700400 switch (msg.getType()) {
401 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700402 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700403 l.portChanged(dpid, (OFPortStatus) msg);
404 }
405 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700406 case FEATURES_REPLY:
407 for (OpenFlowSwitchListener l : ofSwitchListener) {
408 l.switchChanged(dpid);
409 }
410 break;
tom7ef8ff92014-09-17 13:08:06 -0700411 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900412 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700413 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900414 break;
415 }
tom7ef8ff92014-09-17 13:08:06 -0700416 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
Ozge AYAZ60aded22017-06-20 08:35:30 +0000417 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700418 for (PacketListener p : ofPacketListener.values()) {
419 p.handlePacket(pktCtx);
420 }
421 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800422 // TODO: Consider using separate threadpool for sensitive messages.
423 // ie. Back to back error could cause us to starve.
424 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800425 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800426 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530427 case ERROR:
428 log.debug("Received error message from {}: {}", dpid, msg);
429 errorMsgs.putIfAbsent(msg.getXid(), true);
430 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
431 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700432 case STATS_REPLY:
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700433 processStatsReply(dpid, (OFStatsReply) msg);
alshabib64def642014-12-02 23:27:37 -0800434 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700435 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530436 if (errorMsgs.containsKey(msg.getXid())) {
437 //To make oferror msg handling and corresponding barrier reply serialized,
438 // executorErrorMsgs is used for both transaction
439 errorMsgs.remove(msg.getXid());
440 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
441 } else {
442 executorBarrier.execute(new OFMessageHandler(dpid, msg));
443 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700444 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700445 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900446 if (sw == null) {
447 log.error("Switch {} is not found", dpid);
448 break;
449 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700450 long experimenter = ((OFExperimenter) msg).getExperimenter();
451 if (experimenter == 0x748771) {
452 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700453 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900454 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
455 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700456 portDesc.setPortNo(circuitPortStatus.getPortNo())
457 .setHwAddr(circuitPortStatus.getHwAddr())
458 .setName(circuitPortStatus.getName())
459 .setConfig(circuitPortStatus.getConfig())
460 .setState(circuitPortStatus.getState());
461 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
462 for (OpenFlowSwitchListener l : ofSwitchListener) {
463 l.portChanged(dpid, portStatus.build());
464 }
465 } else {
466 log.warn("Handling experimenter type {} not yet implemented",
467 ((OFExperimenter) msg).getExperimenter(), msg);
468 }
469 break;
tom7ef8ff92014-09-17 13:08:06 -0700470 default:
471 log.warn("Handling message type {} not yet implemented {}",
472 msg.getType(), msg);
473 }
474 }
475
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700476 private void processStatsReply(Dpid dpid, OFStatsReply reply) {
477 switch (reply.getStatsType()) {
478 case QUEUE:
479 Collection<OFQueueStatsEntry> queueStatsEntries = publishQueueStats(dpid, (OFQueueStatsReply) reply);
480 if (queueStatsEntries != null) {
481 OFQueueStatsReply.Builder rep =
482 OFFactories.getFactory(reply.getVersion()).buildQueueStatsReply();
483 rep.setEntries(ImmutableList.copyOf(queueStatsEntries));
484 rep.setXid(reply.getXid());
485 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
486 }
487 break;
488
489 case PORT_DESC:
490 for (OpenFlowSwitchListener l : ofSwitchListener) {
491 l.switchChanged(dpid);
492 }
493 break;
494
495 case FLOW:
496 Collection<OFFlowStatsEntry> flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
497 if (flowStats != null) {
498 OFFlowStatsReply.Builder rep =
499 OFFactories.getFactory(reply.getVersion()).buildFlowStatsReply();
500 rep.setEntries(ImmutableList.copyOf(flowStats));
501 rep.setXid(reply.getXid());
502 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
503 }
504 break;
Cem Türker3baff672017-10-12 15:09:01 +0300505 case FLOW_LIGHTWEIGHT:
506 Collection<OFFlowLightweightStatsEntry> flowLightweightStats =
507 publishFlowStatsLightweight(dpid, (OFFlowLightweightStatsReply) reply);
508 if (flowLightweightStats != null) {
509 OFFlowLightweightStatsReply.Builder rep =
510 OFFactories.getFactory(reply.getVersion()).buildFlowLightweightStatsReply();
511 rep.setEntries(ImmutableList.copyOf(flowLightweightStats));
512 rep.setXid(reply.getXid());
513 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
514 }
515 break;
Yuta HIGUCHI7b41dc92017-06-22 19:37:06 -0700516 case TABLE:
517 Collection<OFTableStatsEntry> tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
518 if (tableStats != null) {
519 OFTableStatsReply.Builder rep =
520 OFFactories.getFactory(reply.getVersion()).buildTableStatsReply();
521 rep.setEntries(ImmutableList.copyOf(tableStats));
522 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
523 }
524 break;
525
526 case GROUP:
527 Collection<OFGroupStatsEntry> groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
528 if (groupStats != null) {
529 OFGroupStatsReply.Builder rep =
530 OFFactories.getFactory(reply.getVersion()).buildGroupStatsReply();
531 rep.setEntries(ImmutableList.copyOf(groupStats));
532 rep.setXid(reply.getXid());
533 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
534 }
535 break;
536
537 case GROUP_DESC:
538 Collection<OFGroupDescStatsEntry> groupDescStats = publishGroupDescStats(dpid,
539 (OFGroupDescStatsReply) reply);
540 if (groupDescStats != null) {
541 OFGroupDescStatsReply.Builder rep =
542 OFFactories.getFactory(reply.getVersion()).buildGroupDescStatsReply();
543 rep.setEntries(ImmutableList.copyOf(groupDescStats));
544 rep.setXid(reply.getXid());
545 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
546 }
547 break;
548
549 case PORT:
550 executorMsgs.execute(new OFMessageHandler(dpid, reply));
551 break;
552
553 case METER:
554 executorMsgs.execute(new OFMessageHandler(dpid, reply));
555 break;
556
557 case EXPERIMENTER:
558 if (reply instanceof OFCalientFlowStatsReply) {
559 OpenFlowSwitch sw = this.getSwitch(dpid);
560 // Convert Calient flow statistics to regular flow stats
561 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
562 if (sw == null) {
563 log.error("Switch {} is not found", dpid);
564 break;
565 }
566 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
567 List<OFFlowStatsEntry> entries = new ArrayList<>();
568 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) reply).getEntries()) {
569
570 // Single instruction, i.e., output to port
571 OFActionOutput action = sw.factory()
572 .actions()
573 .buildOutput()
574 .setPort(entry.getOutPort())
575 .build();
576 OFInstruction instruction = sw.factory()
577 .instructions()
578 .applyActions(Collections.singletonList(action));
579 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
580 .setMatch(entry.getMatch())
581 .setTableId(entry.getTableId())
582 .setDurationSec(entry.getDurationSec())
583 .setDurationNsec(entry.getDurationNsec())
584 .setPriority(entry.getPriority())
585 .setIdleTimeout(entry.getIdleTimeout())
586 .setHardTimeout(entry.getHardTimeout())
587 .setFlags(entry.getFlags())
588 .setCookie(entry.getCookie())
589 .setInstructions(Collections.singletonList(instruction))
590 .build();
591 entries.add(fs);
592 }
593 fsr.setEntries(entries);
594
595 flowStats = publishFlowStats(dpid, fsr.build());
596 if (flowStats != null) {
597 OFFlowStatsReply.Builder rep =
598 sw.factory().buildFlowStatsReply();
599 rep.setEntries(ImmutableList.copyOf(flowStats));
600 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
601 }
602 } else {
603 executorMsgs.execute(new OFMessageHandler(dpid, reply));
604 }
605 break;
606 default:
607 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
608 break;
609 }
610 }
611
sangho6a0bb172015-02-05 12:24:48 -0800612 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
613 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800614 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800615 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800616 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800617 return fullFlowStats.removeAll(dpid);
618 }
619 return null;
620 }
621
Cem Türker3baff672017-10-12 15:09:01 +0300622 private synchronized Collection<OFFlowLightweightStatsEntry> publishFlowStatsLightweight(
623 Dpid dpid,
624 OFFlowLightweightStatsReply reply) {
625 //TODO: Get rid of synchronized
626 fullFlowLightweightStats.putAll(dpid, reply.getEntries());
627 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
628 return fullFlowLightweightStats.removeAll(dpid);
629 }
630 return null;
631 }
632
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700633 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
634 OFTableStatsReply reply) {
635 //TODO: Get rid of synchronized
636 fullTableStats.putAll(dpid, reply.getEntries());
637 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
638 return fullTableStats.removeAll(dpid);
639 }
640 return null;
641 }
642
sangho6a0bb172015-02-05 12:24:48 -0800643 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
644 OFGroupStatsReply reply) {
645 //TODO: Get rid of synchronized
646 fullGroupStats.putAll(dpid, reply.getEntries());
647 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
648 return fullGroupStats.removeAll(dpid);
649 }
650 return null;
651 }
652
653 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
654 OFGroupDescStatsReply reply) {
655 //TODO: Get rid of synchronized
656 fullGroupDescStats.putAll(dpid, reply.getEntries());
657 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
658 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800659 }
660 return null;
661 }
662
Ozge AYAZ60aded22017-06-20 08:35:30 +0000663 private synchronized Collection<OFQueueStatsEntry> publishQueueStats(Dpid dpid, OFQueueStatsReply reply) {
664 fullQueueStats.putAll(dpid, reply.getEntries());
665 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
666 return fullQueueStats.removeAll(dpid);
667 }
668 return null;
669 }
670
tom7ef8ff92014-09-17 13:08:06 -0700671 @Override
672 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700673 final OpenFlowSwitch sw = getSwitch(dpid);
674 if (sw == null) {
675 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
676 return;
677 }
678 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700679 }
680
681 /**
682 * Implementation of an OpenFlow Agent which is responsible for
683 * keeping track of connected switches and the state in which
684 * they are.
685 */
686 public class OpenFlowSwitchAgent implements OpenFlowAgent {
687
688 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
689 private final Lock switchLock = new ReentrantLock();
690
691 @Override
692 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700693
tom7ef8ff92014-09-17 13:08:06 -0700694 if (connectedSwitches.get(dpid) != null) {
695 log.error("Trying to add connectedSwitch but found a previous "
696 + "value for dpid: {}", dpid);
697 return false;
698 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700699 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700700 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700701 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700702 l.switchAdded(dpid);
703 }
704 return true;
705 }
706 }
707
708 @Override
709 public boolean validActivation(Dpid dpid) {
710 if (connectedSwitches.get(dpid) == null) {
711 log.error("Trying to activate switch but is not in "
712 + "connected switches: dpid {}. Aborting ..",
713 dpid);
714 return false;
715 }
716 if (activeMasterSwitches.get(dpid) != null ||
717 activeEqualSwitches.get(dpid) != null) {
718 log.error("Trying to activate switch but it is already "
719 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800720 + "Found in activeEqual: {}. Aborting ..",
721 dpid,
722 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
723 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700724 return false;
725 }
726 return true;
727 }
728
729
730 @Override
731 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
732 switchLock.lock();
733 try {
734 if (!validActivation(dpid)) {
735 return false;
736 }
737 activeMasterSwitches.put(dpid, sw);
738 return true;
739 } finally {
740 switchLock.unlock();
741 }
742 }
743
744 @Override
745 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
746 switchLock.lock();
747 try {
748 if (!validActivation(dpid)) {
749 return false;
750 }
751 activeEqualSwitches.put(dpid, sw);
752 log.info("Added Activated EQUAL Switch {}", dpid);
753 return true;
754 } finally {
755 switchLock.unlock();
756 }
757 }
758
759 @Override
760 public void transitionToMasterSwitch(Dpid dpid) {
761 switchLock.lock();
762 try {
763 if (activeMasterSwitches.containsKey(dpid)) {
764 return;
765 }
766 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
767 if (sw == null) {
768 sw = getSwitch(dpid);
769 if (sw == null) {
770 log.error("Transition to master called on sw {}, but switch "
771 + "was not found in controller-cache", dpid);
772 return;
773 }
774 }
775 log.info("Transitioned switch {} to MASTER", dpid);
776 activeMasterSwitches.put(dpid, sw);
777 } finally {
778 switchLock.unlock();
779 }
780 }
781
782
783 @Override
784 public void transitionToEqualSwitch(Dpid dpid) {
785 switchLock.lock();
786 try {
787 if (activeEqualSwitches.containsKey(dpid)) {
788 return;
789 }
790 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
791 if (sw == null) {
792 sw = getSwitch(dpid);
793 if (sw == null) {
794 log.error("Transition to equal called on sw {}, but switch "
795 + "was not found in controller-cache", dpid);
796 return;
797 }
798 }
799 log.info("Transitioned switch {} to EQUAL", dpid);
800 activeEqualSwitches.put(dpid, sw);
801 } finally {
802 switchLock.unlock();
803 }
804
805 }
806
807 @Override
808 public void removeConnectedSwitch(Dpid dpid) {
809 connectedSwitches.remove(dpid);
810 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
811 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700812 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700813 sw = activeEqualSwitches.remove(dpid);
814 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700815 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700816 l.switchRemoved(dpid);
817 }
818 }
819
820 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700821 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
822 for (OpenFlowMessageListener listener : ofMessageListener) {
823 listener.handleOutgoingMessage(dpid, m);
824 }
825 }
826
827
828 @Override
tom7ef8ff92014-09-17 13:08:06 -0700829 public void processMessage(Dpid dpid, OFMessage m) {
830 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700831
832 for (OpenFlowMessageListener listener : ofMessageListener) {
833 listener.handleIncomingMessage(dpid, m);
834 }
tom7ef8ff92014-09-17 13:08:06 -0700835 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700836
837 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700838 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700839 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700840 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700841 }
842 }
tom7ef8ff92014-09-17 13:08:06 -0700843 }
844
Jian Li152b8852015-12-07 14:47:25 -0800845 /**
Jian Li2266bff2016-04-21 11:01:25 -0700846 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800847 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800848 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700849
Ray Milkey9c9cde42018-01-12 14:22:06 -0800850 final OFMessage msg;
851 final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700852
853 public OFMessageHandler(Dpid dpid, OFMessage msg) {
854 this.msg = msg;
855 this.dpid = dpid;
856 }
857
858 @Override
859 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700860 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700861 listener.handleMessage(dpid, msg);
862 }
863 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700864 }
tom7ef8ff92014-09-17 13:08:06 -0700865}