blob: 21f2c0f1f38e61837ace5fc30cb73cb02d80c8df [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;
tom7ef8ff92014-09-17 13:08:06 -070092
93@Component(immediate = true)
94@Service
95public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080096 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -070097 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -070098 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -070099
100 private static final Logger log =
101 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
102
alshabibb452fd72015-04-22 20:46:20 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800104 protected CoreService coreService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700107 protected DriverService driverService;
108
Charles Chan3b00e1b2015-08-26 23:12:52 +0800109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ComponentConfigService cfgService;
111
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
114
115
Brian O'Connore2a399e2015-09-22 15:32:50 -0700116 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
117 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
118 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800119
120 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700121 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800122 private int workerThreads = DEFAULT_WORKER_THREADS;
123
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800124 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800125 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800126
127 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800128 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700129
Prince Pereirae7798032016-07-08 16:31:58 +0530130 //Separate executor thread for handling error messages and barrier replies for same failed
131 // transactions to avoid context switching of thread
132 protected ExecutorService executorErrorMsgs =
133 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
134
135 //concurrent hashmap to track failed transactions
136 protected ConcurrentMap<Long, Boolean> errorMsgs =
137 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800138 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700139 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800140 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700141 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800142 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700143 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700144
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700145 // Key: dpid, value: map with key: long (XID), value: completable future
146 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
147 new ConcurrentHashMap<>();
148
tom7ef8ff92014-09-17 13:08:06 -0700149 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700150 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700151
152 protected Multimap<Integer, PacketListener> ofPacketListener =
153 ArrayListMultimap.create();
154
Jonathan Hart6d44d192015-05-11 18:01:19 -0700155 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700156
Jian Lia78cdb22016-04-21 13:03:58 -0700157 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800158
sangho6a0bb172015-02-05 12:24:48 -0800159 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
160 ArrayListMultimap.create();
161
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700162 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
163 ArrayListMultimap.create();
164
sangho6a0bb172015-02-05 12:24:48 -0800165 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
166 ArrayListMultimap.create();
167
168 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800169 ArrayListMultimap.create();
170
sangho538108b2015-04-08 14:29:20 -0700171 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
172 ArrayListMultimap.create();
173
tom7ef8ff92014-09-17 13:08:06 -0700174 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700175 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700176
177 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800178 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700179 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800180 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700181 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700182 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700183 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700184 }
185
Andrea Campanella3556f362016-04-28 15:18:10 -0700186 private void cleanup() {
187 // Close listening channel and all OF channels. Clean information about switches
188 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800189 ctrl.stop();
190 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700191 connectedSwitches.clear();
192 activeMasterSwitches.clear();
193 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800194 }
195
tom7ef8ff92014-09-17 13:08:06 -0700196 @Deactivate
197 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700198 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300199 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800200 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700201 }
202
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800203 @Modified
204 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800205 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700206 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800207 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800208 }
209
tom7ef8ff92014-09-17 13:08:06 -0700210 @Override
211 public Iterable<OpenFlowSwitch> getSwitches() {
212 return connectedSwitches.values();
213 }
214
215 @Override
216 public Iterable<OpenFlowSwitch> getMasterSwitches() {
217 return activeMasterSwitches.values();
218 }
219
220 @Override
221 public Iterable<OpenFlowSwitch> getEqualSwitches() {
222 return activeEqualSwitches.values();
223 }
224
225 @Override
226 public OpenFlowSwitch getSwitch(Dpid dpid) {
227 return connectedSwitches.get(dpid);
228 }
229
230 @Override
231 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
232 return activeMasterSwitches.get(dpid);
233 }
234
235 @Override
236 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
237 return activeEqualSwitches.get(dpid);
238 }
239
240 @Override
241 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700242 if (!ofSwitchListener.contains(listener)) {
243 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700244 }
245 }
246
247 @Override
248 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700249 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700250 }
251
252 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700253 public void addMessageListener(OpenFlowMessageListener listener) {
254 ofMessageListener.add(listener);
255 }
256
257 @Override
258 public void removeMessageListener(OpenFlowMessageListener listener) {
259 ofMessageListener.remove(listener);
260 }
261
262 @Override
tom7ef8ff92014-09-17 13:08:06 -0700263 public void addPacketListener(int priority, PacketListener listener) {
264 ofPacketListener.put(priority, listener);
265 }
266
267 @Override
268 public void removePacketListener(PacketListener listener) {
269 ofPacketListener.values().remove(listener);
270 }
271
272 @Override
alshabibeec3a062014-09-17 18:01:26 -0700273 public void addEventListener(OpenFlowEventListener listener) {
274 ofEventListener.add(listener);
275 }
276
277 @Override
278 public void removeEventListener(OpenFlowEventListener listener) {
279 ofEventListener.remove(listener);
280 }
281
282 @Override
tom7ef8ff92014-09-17 13:08:06 -0700283 public void write(Dpid dpid, OFMessage msg) {
284 this.getSwitch(dpid).sendMsg(msg);
285 }
286
287 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700288 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
289 write(dpid, msg);
290
291 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700292 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap<>());
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700293
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700294 CompletableFuture<OFMessage> future = new CompletableFuture<>();
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700295 xids.put(msg.getXid(), future);
296
297 return future;
298 }
299
300 @Override
tom7ef8ff92014-09-17 13:08:06 -0700301 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800302 Collection<OFFlowStatsEntry> flowStats;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700303 Collection<OFTableStatsEntry> tableStats;
sangho6a0bb172015-02-05 12:24:48 -0800304 Collection<OFGroupStatsEntry> groupStats;
305 Collection<OFGroupDescStatsEntry> groupDescStats;
306
sangyun-han69ed4462016-07-27 12:10:12 +0900307 OpenFlowSwitch sw = this.getSwitch(dpid);
308
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700309 // Check if someone is waiting for this message
310 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700311 if (xids != null) {
312 CompletableFuture<OFMessage> future = xids.remove(msg.getXid());
313 if (future != null) {
314 future.complete(msg);
315 }
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700316 }
317
tom7ef8ff92014-09-17 13:08:06 -0700318 switch (msg.getType()) {
319 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700320 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700321 l.portChanged(dpid, (OFPortStatus) msg);
322 }
323 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700324 case FEATURES_REPLY:
325 for (OpenFlowSwitchListener l : ofSwitchListener) {
326 l.switchChanged(dpid);
327 }
328 break;
tom7ef8ff92014-09-17 13:08:06 -0700329 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900330 if (sw == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700331 log.error("Ignoring PACKET_IN, switch {} is not found", dpid);
sangyun-han69ed4462016-07-27 12:10:12 +0900332 break;
333 }
tom7ef8ff92014-09-17 13:08:06 -0700334 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
sangyun-han69ed4462016-07-27 12:10:12 +0900335 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700336 for (PacketListener p : ofPacketListener.values()) {
337 p.handlePacket(pktCtx);
338 }
339 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800340 // TODO: Consider using separate threadpool for sensitive messages.
341 // ie. Back to back error could cause us to starve.
342 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800343 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800344 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530345 case ERROR:
346 log.debug("Received error message from {}: {}", dpid, msg);
347 errorMsgs.putIfAbsent(msg.getXid(), true);
348 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
349 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700350 case STATS_REPLY:
351 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800352 switch (reply.getStatsType()) {
353 case PORT_DESC:
354 for (OpenFlowSwitchListener l : ofSwitchListener) {
355 l.switchChanged(dpid);
356 }
357 break;
358 case FLOW:
359 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
360 if (flowStats != null) {
361 OFFlowStatsReply.Builder rep =
362 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
363 rep.setEntries(Lists.newLinkedList(flowStats));
ssyoon9030fbcd92015-08-17 10:42:07 +0900364 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800365 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800366 }
367 break;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700368 case TABLE:
369 tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
370 if (tableStats != null) {
371 OFTableStatsReply.Builder rep =
372 OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
373 rep.setEntries(Lists.newLinkedList(tableStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800374 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700375 }
376 break;
sangho6a0bb172015-02-05 12:24:48 -0800377 case GROUP:
378 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
379 if (groupStats != null) {
380 OFGroupStatsReply.Builder rep =
381 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
382 rep.setEntries(Lists.newLinkedList(groupStats));
383 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800384 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800385 }
386 break;
387 case GROUP_DESC:
388 groupDescStats = publishGroupDescStats(dpid,
389 (OFGroupDescStatsReply) reply);
390 if (groupDescStats != null) {
391 OFGroupDescStatsReply.Builder rep =
Marc De Leenheerb9311372015-07-09 11:36:49 -0700392 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
sangho6a0bb172015-02-05 12:24:48 -0800393 rep.setEntries(Lists.newLinkedList(groupDescStats));
394 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800395 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800396 }
397 break;
sangho538108b2015-04-08 14:29:20 -0700398 case PORT:
Andrea Campanelladda93562016-03-02 11:08:12 -0800399 executorMsgs.execute(new OFMessageHandler(dpid, reply));
sangho538108b2015-04-08 14:29:20 -0700400 break;
alshabib5eb79392015-08-19 18:09:55 -0700401 case METER:
Andrea Campanelladda93562016-03-02 11:08:12 -0800402 executorMsgs.execute(new OFMessageHandler(dpid, reply));
alshabib5eb79392015-08-19 18:09:55 -0700403 break;
Marc De Leenheerb9311372015-07-09 11:36:49 -0700404 case EXPERIMENTER:
405 if (reply instanceof OFCalientFlowStatsReply) {
406 // Convert Calient flow statistics to regular flow stats
407 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
sangyun-han69ed4462016-07-27 12:10:12 +0900408 if (sw == null) {
409 log.error("Switch {} is not found", dpid);
410 break;
411 }
412 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
Marc De Leenheerb9311372015-07-09 11:36:49 -0700413 List<OFFlowStatsEntry> entries = new LinkedList<>();
414 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
415
416 // Single instruction, i.e., output to port
417 OFActionOutput action = OFFactories
418 .getFactory(msg.getVersion())
419 .actions()
420 .buildOutput()
421 .setPort(entry.getOutPort())
422 .build();
423 OFInstruction instruction = OFFactories
424 .getFactory(msg.getVersion())
425 .instructions()
426 .applyActions(Collections.singletonList(action));
sangyun-han69ed4462016-07-27 12:10:12 +0900427 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
Marc De Leenheerb9311372015-07-09 11:36:49 -0700428 .setMatch(entry.getMatch())
429 .setTableId(entry.getTableId())
430 .setDurationSec(entry.getDurationSec())
431 .setDurationNsec(entry.getDurationNsec())
432 .setPriority(entry.getPriority())
433 .setIdleTimeout(entry.getIdleTimeout())
434 .setHardTimeout(entry.getHardTimeout())
435 .setFlags(entry.getFlags())
436 .setCookie(entry.getCookie())
437 .setInstructions(Collections.singletonList(instruction))
438 .build();
439 entries.add(fs);
440 }
441 fsr.setEntries(entries);
442
443 flowStats = publishFlowStats(dpid, fsr.build());
444 if (flowStats != null) {
445 OFFlowStatsReply.Builder rep =
446 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
447 rep.setEntries(Lists.newLinkedList(flowStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800448 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700449 }
450 } else {
Andrea Campanelladda93562016-03-02 11:08:12 -0800451 executorMsgs.execute(new OFMessageHandler(dpid, reply));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700452 }
453 break;
sangho6a0bb172015-02-05 12:24:48 -0800454 default:
alshabib5eb79392015-08-19 18:09:55 -0700455 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
Marc De Leenheerb9311372015-07-09 11:36:49 -0700456 break;
alshabib64def642014-12-02 23:27:37 -0800457 }
458 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700459 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530460 if (errorMsgs.containsKey(msg.getXid())) {
461 //To make oferror msg handling and corresponding barrier reply serialized,
462 // executorErrorMsgs is used for both transaction
463 errorMsgs.remove(msg.getXid());
464 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
465 } else {
466 executorBarrier.execute(new OFMessageHandler(dpid, msg));
467 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700468 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700469 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900470 if (sw == null) {
471 log.error("Switch {} is not found", dpid);
472 break;
473 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700474 long experimenter = ((OFExperimenter) msg).getExperimenter();
475 if (experimenter == 0x748771) {
476 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700477 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900478 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
479 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700480 portDesc.setPortNo(circuitPortStatus.getPortNo())
481 .setHwAddr(circuitPortStatus.getHwAddr())
482 .setName(circuitPortStatus.getName())
483 .setConfig(circuitPortStatus.getConfig())
484 .setState(circuitPortStatus.getState());
485 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
486 for (OpenFlowSwitchListener l : ofSwitchListener) {
487 l.portChanged(dpid, portStatus.build());
488 }
489 } else {
490 log.warn("Handling experimenter type {} not yet implemented",
491 ((OFExperimenter) msg).getExperimenter(), msg);
492 }
493 break;
tom7ef8ff92014-09-17 13:08:06 -0700494 default:
495 log.warn("Handling message type {} not yet implemented {}",
496 msg.getType(), msg);
497 }
498 }
499
sangho6a0bb172015-02-05 12:24:48 -0800500 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
501 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800502 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800503 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800504 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800505 return fullFlowStats.removeAll(dpid);
506 }
507 return null;
508 }
509
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700510 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
511 OFTableStatsReply reply) {
512 //TODO: Get rid of synchronized
513 fullTableStats.putAll(dpid, reply.getEntries());
514 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
515 return fullTableStats.removeAll(dpid);
516 }
517 return null;
518 }
519
sangho6a0bb172015-02-05 12:24:48 -0800520 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
521 OFGroupStatsReply reply) {
522 //TODO: Get rid of synchronized
523 fullGroupStats.putAll(dpid, reply.getEntries());
524 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
525 return fullGroupStats.removeAll(dpid);
526 }
527 return null;
528 }
529
530 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
531 OFGroupDescStatsReply reply) {
532 //TODO: Get rid of synchronized
533 fullGroupDescStats.putAll(dpid, reply.getEntries());
534 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
535 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800536 }
537 return null;
538 }
539
sangho538108b2015-04-08 14:29:20 -0700540 private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
541 OFPortStatsReply reply) {
542 fullPortStats.putAll(dpid, reply.getEntries());
543 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
544 return fullPortStats.removeAll(dpid);
545 }
546 return null;
547 }
548
tom7ef8ff92014-09-17 13:08:06 -0700549 @Override
550 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700551 final OpenFlowSwitch sw = getSwitch(dpid);
552 if (sw == null) {
553 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
554 return;
555 }
556 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700557 }
558
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700559 class InternalDeviceListener implements DeviceListener {
560
561 @Override
562 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800563 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700564 }
565
566 @Override
567 public void event(DeviceEvent event) {
568 switch (event.type()) {
569 case DEVICE_ADDED:
570 break;
571 case DEVICE_AVAILABILITY_CHANGED:
572 break;
573 case DEVICE_REMOVED:
574 // Device administratively removed, disconnect
575 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
576 .ifPresent(OpenFlowSwitch::disconnectSwitch);
577 break;
578 case DEVICE_SUSPENDED:
579 break;
580 case DEVICE_UPDATED:
581 break;
582 case PORT_ADDED:
583 break;
584 case PORT_REMOVED:
585 break;
586 case PORT_STATS_UPDATED:
587 break;
588 case PORT_UPDATED:
589 break;
590 default:
591 break;
592
593 }
594
595 }
596
597 }
598
tom7ef8ff92014-09-17 13:08:06 -0700599 /**
600 * Implementation of an OpenFlow Agent which is responsible for
601 * keeping track of connected switches and the state in which
602 * they are.
603 */
604 public class OpenFlowSwitchAgent implements OpenFlowAgent {
605
606 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
607 private final Lock switchLock = new ReentrantLock();
608
609 @Override
610 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700611
tom7ef8ff92014-09-17 13:08:06 -0700612 if (connectedSwitches.get(dpid) != null) {
613 log.error("Trying to add connectedSwitch but found a previous "
614 + "value for dpid: {}", dpid);
615 return false;
616 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700617 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700618 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700619 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700620 l.switchAdded(dpid);
621 }
622 return true;
623 }
624 }
625
626 @Override
627 public boolean validActivation(Dpid dpid) {
628 if (connectedSwitches.get(dpid) == null) {
629 log.error("Trying to activate switch but is not in "
630 + "connected switches: dpid {}. Aborting ..",
631 dpid);
632 return false;
633 }
634 if (activeMasterSwitches.get(dpid) != null ||
635 activeEqualSwitches.get(dpid) != null) {
636 log.error("Trying to activate switch but it is already "
637 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800638 + "Found in activeEqual: {}. Aborting ..",
639 dpid,
640 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
641 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700642 return false;
643 }
644 return true;
645 }
646
647
648 @Override
649 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
650 switchLock.lock();
651 try {
652 if (!validActivation(dpid)) {
653 return false;
654 }
655 activeMasterSwitches.put(dpid, sw);
656 return true;
657 } finally {
658 switchLock.unlock();
659 }
660 }
661
662 @Override
663 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
664 switchLock.lock();
665 try {
666 if (!validActivation(dpid)) {
667 return false;
668 }
669 activeEqualSwitches.put(dpid, sw);
670 log.info("Added Activated EQUAL Switch {}", dpid);
671 return true;
672 } finally {
673 switchLock.unlock();
674 }
675 }
676
677 @Override
678 public void transitionToMasterSwitch(Dpid dpid) {
679 switchLock.lock();
680 try {
681 if (activeMasterSwitches.containsKey(dpid)) {
682 return;
683 }
684 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
685 if (sw == null) {
686 sw = getSwitch(dpid);
687 if (sw == null) {
688 log.error("Transition to master called on sw {}, but switch "
689 + "was not found in controller-cache", dpid);
690 return;
691 }
692 }
693 log.info("Transitioned switch {} to MASTER", dpid);
694 activeMasterSwitches.put(dpid, sw);
695 } finally {
696 switchLock.unlock();
697 }
698 }
699
700
701 @Override
702 public void transitionToEqualSwitch(Dpid dpid) {
703 switchLock.lock();
704 try {
705 if (activeEqualSwitches.containsKey(dpid)) {
706 return;
707 }
708 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
709 if (sw == null) {
710 sw = getSwitch(dpid);
711 if (sw == null) {
712 log.error("Transition to equal called on sw {}, but switch "
713 + "was not found in controller-cache", dpid);
714 return;
715 }
716 }
717 log.info("Transitioned switch {} to EQUAL", dpid);
718 activeEqualSwitches.put(dpid, sw);
719 } finally {
720 switchLock.unlock();
721 }
722
723 }
724
725 @Override
726 public void removeConnectedSwitch(Dpid dpid) {
727 connectedSwitches.remove(dpid);
728 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
729 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700730 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700731 sw = activeEqualSwitches.remove(dpid);
732 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700733 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700734 l.switchRemoved(dpid);
735 }
736 }
737
738 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700739 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
740 for (OpenFlowMessageListener listener : ofMessageListener) {
741 listener.handleOutgoingMessage(dpid, m);
742 }
743 }
744
745
746 @Override
tom7ef8ff92014-09-17 13:08:06 -0700747 public void processMessage(Dpid dpid, OFMessage m) {
748 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700749
750 for (OpenFlowMessageListener listener : ofMessageListener) {
751 listener.handleIncomingMessage(dpid, m);
752 }
tom7ef8ff92014-09-17 13:08:06 -0700753 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700754
755 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700756 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700757 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700758 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700759 }
760 }
tom7ef8ff92014-09-17 13:08:06 -0700761 }
762
Jian Li152b8852015-12-07 14:47:25 -0800763 /**
Jian Li2266bff2016-04-21 11:01:25 -0700764 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800765 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800766 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700767
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800768 protected final OFMessage msg;
769 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700770
771 public OFMessageHandler(Dpid dpid, OFMessage msg) {
772 this.msg = msg;
773 this.dpid = dpid;
774 }
775
776 @Override
777 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700778 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700779 listener.handleMessage(dpid, msg);
780 }
781 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700782 }
tom7ef8ff92014-09-17 13:08:06 -0700783}