blob: 4427c728766dc1c7a161e4ea1cb7a1d104582d54 [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;
32import org.onosproject.net.device.DeviceEvent.Type;
33import org.onosproject.net.device.DeviceListener;
34import org.onosproject.net.device.DeviceService;
Thomas Vachuska3358af22015-05-19 18:40:34 -070035import org.onosproject.net.driver.DefaultDriverProviderService;
alshabibb452fd72015-04-22 20:46:20 -070036import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
38import org.onosproject.openflow.controller.Dpid;
39import org.onosproject.openflow.controller.OpenFlowController;
40import org.onosproject.openflow.controller.OpenFlowEventListener;
Jian Lia78cdb22016-04-21 13:03:58 -070041import org.onosproject.openflow.controller.OpenFlowMessageListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.openflow.controller.OpenFlowPacketContext;
43import org.onosproject.openflow.controller.OpenFlowSwitch;
44import org.onosproject.openflow.controller.OpenFlowSwitchListener;
45import org.onosproject.openflow.controller.PacketListener;
46import org.onosproject.openflow.controller.RoleState;
47import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080048import org.osgi.service.component.ComponentContext;
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;
54import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080056import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
57import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
58import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
59import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070060import org.projectfloodlight.openflow.protocol.OFMessage;
61import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070062import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070063import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
64import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070065import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070066import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080067import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070068import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
69import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070070import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
71import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070072import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080075import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070076import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070077import java.util.LinkedList;
78import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070079import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080080import java.util.Set;
81import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080082import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070083import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080084import java.util.concurrent.ExecutorService;
85import java.util.concurrent.Executors;
86import java.util.concurrent.locks.Lock;
87import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070088
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080089import static org.onlab.util.Tools.groupedThreads;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070090import static org.onosproject.openflow.controller.Dpid.dpid;
tom7ef8ff92014-09-17 13:08:06 -070091
92@Component(immediate = true)
93@Service
94public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080095 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -070096 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -070097 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -070098
99 private static final Logger log =
100 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
101
alshabibb452fd72015-04-22 20:46:20 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800103 protected CoreService coreService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700106 protected DriverService driverService;
107
Thomas Vachuska3358af22015-05-19 18:40:34 -0700108 // References exists merely for sequencing purpose to assure drivers are loaded
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected DefaultDriverProviderService defaultDriverProviderService;
111
Charles Chan3b00e1b2015-08-26 23:12:52 +0800112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected ComponentConfigService cfgService;
114
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceService deviceService;
117
118
Brian O'Connore2a399e2015-09-22 15:32:50 -0700119 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
120 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
121 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800122
123 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700124 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800125 private int workerThreads = DEFAULT_WORKER_THREADS;
126
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800127 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800128 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800129
130 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800131 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700132
Prince Pereirae7798032016-07-08 16:31:58 +0530133 //Separate executor thread for handling error messages and barrier replies for same failed
134 // transactions to avoid context switching of thread
135 protected ExecutorService executorErrorMsgs =
136 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
137
138 //concurrent hashmap to track failed transactions
139 protected ConcurrentMap<Long, Boolean> errorMsgs =
140 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800141 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700142 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800143 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700144 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800145 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700146 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700147
148 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700149 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700150
151 protected Multimap<Integer, PacketListener> ofPacketListener =
152 ArrayListMultimap.create();
153
Jonathan Hart6d44d192015-05-11 18:01:19 -0700154 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700155
Jian Lia78cdb22016-04-21 13:03:58 -0700156 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800157
sangho6a0bb172015-02-05 12:24:48 -0800158 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
159 ArrayListMultimap.create();
160
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700161 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
162 ArrayListMultimap.create();
163
sangho6a0bb172015-02-05 12:24:48 -0800164 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
165 ArrayListMultimap.create();
166
167 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800168 ArrayListMultimap.create();
169
sangho538108b2015-04-08 14:29:20 -0700170 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
171 ArrayListMultimap.create();
172
tom7ef8ff92014-09-17 13:08:06 -0700173 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700174 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700175
176 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800177 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700178 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800179 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700180 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700181 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700182 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700183 }
184
Andrea Campanella3556f362016-04-28 15:18:10 -0700185 private void cleanup() {
186 // Close listening channel and all OF channels. Clean information about switches
187 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800188 ctrl.stop();
189 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700190 connectedSwitches.clear();
191 activeMasterSwitches.clear();
192 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800193 }
194
tom7ef8ff92014-09-17 13:08:06 -0700195 @Deactivate
196 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700197 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300198 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800199 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700200 }
201
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800202 @Modified
203 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800204 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700205 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800206 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800207 }
208
tom7ef8ff92014-09-17 13:08:06 -0700209 @Override
210 public Iterable<OpenFlowSwitch> getSwitches() {
211 return connectedSwitches.values();
212 }
213
214 @Override
215 public Iterable<OpenFlowSwitch> getMasterSwitches() {
216 return activeMasterSwitches.values();
217 }
218
219 @Override
220 public Iterable<OpenFlowSwitch> getEqualSwitches() {
221 return activeEqualSwitches.values();
222 }
223
224 @Override
225 public OpenFlowSwitch getSwitch(Dpid dpid) {
226 return connectedSwitches.get(dpid);
227 }
228
229 @Override
230 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
231 return activeMasterSwitches.get(dpid);
232 }
233
234 @Override
235 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
236 return activeEqualSwitches.get(dpid);
237 }
238
239 @Override
240 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700241 if (!ofSwitchListener.contains(listener)) {
242 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700243 }
244 }
245
246 @Override
247 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700248 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700249 }
250
251 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700252 public void addMessageListener(OpenFlowMessageListener listener) {
253 ofMessageListener.add(listener);
254 }
255
256 @Override
257 public void removeMessageListener(OpenFlowMessageListener listener) {
258 ofMessageListener.remove(listener);
259 }
260
261 @Override
tom7ef8ff92014-09-17 13:08:06 -0700262 public void addPacketListener(int priority, PacketListener listener) {
263 ofPacketListener.put(priority, listener);
264 }
265
266 @Override
267 public void removePacketListener(PacketListener listener) {
268 ofPacketListener.values().remove(listener);
269 }
270
271 @Override
alshabibeec3a062014-09-17 18:01:26 -0700272 public void addEventListener(OpenFlowEventListener listener) {
273 ofEventListener.add(listener);
274 }
275
276 @Override
277 public void removeEventListener(OpenFlowEventListener listener) {
278 ofEventListener.remove(listener);
279 }
280
281 @Override
tom7ef8ff92014-09-17 13:08:06 -0700282 public void write(Dpid dpid, OFMessage msg) {
283 this.getSwitch(dpid).sendMsg(msg);
284 }
285
286 @Override
287 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800288 Collection<OFFlowStatsEntry> flowStats;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700289 Collection<OFTableStatsEntry> tableStats;
sangho6a0bb172015-02-05 12:24:48 -0800290 Collection<OFGroupStatsEntry> groupStats;
291 Collection<OFGroupDescStatsEntry> groupDescStats;
sangho538108b2015-04-08 14:29:20 -0700292 Collection<OFPortStatsEntry> portStats;
sangho6a0bb172015-02-05 12:24:48 -0800293
sangyun-han69ed4462016-07-27 12:10:12 +0900294 OpenFlowSwitch sw = this.getSwitch(dpid);
295
tom7ef8ff92014-09-17 13:08:06 -0700296 switch (msg.getType()) {
297 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700298 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700299 l.portChanged(dpid, (OFPortStatus) msg);
300 }
301 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700302 case FEATURES_REPLY:
303 for (OpenFlowSwitchListener l : ofSwitchListener) {
304 l.switchChanged(dpid);
305 }
306 break;
tom7ef8ff92014-09-17 13:08:06 -0700307 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900308 if (sw == null) {
309 log.error("Switch {} is not found", dpid);
310 break;
311 }
tom7ef8ff92014-09-17 13:08:06 -0700312 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
sangyun-han69ed4462016-07-27 12:10:12 +0900313 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700314 for (PacketListener p : ofPacketListener.values()) {
315 p.handlePacket(pktCtx);
316 }
317 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800318 // TODO: Consider using separate threadpool for sensitive messages.
319 // ie. Back to back error could cause us to starve.
320 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800321 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800322 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530323 case ERROR:
324 log.debug("Received error message from {}: {}", dpid, msg);
325 errorMsgs.putIfAbsent(msg.getXid(), true);
326 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
327 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700328 case STATS_REPLY:
329 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800330 switch (reply.getStatsType()) {
331 case PORT_DESC:
332 for (OpenFlowSwitchListener l : ofSwitchListener) {
333 l.switchChanged(dpid);
334 }
335 break;
336 case FLOW:
337 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
338 if (flowStats != null) {
339 OFFlowStatsReply.Builder rep =
340 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
341 rep.setEntries(Lists.newLinkedList(flowStats));
ssyoon9030fbcd92015-08-17 10:42:07 +0900342 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800343 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800344 }
345 break;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700346 case TABLE:
347 tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
348 if (tableStats != null) {
349 OFTableStatsReply.Builder rep =
350 OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
351 rep.setEntries(Lists.newLinkedList(tableStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800352 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700353 }
354 break;
sangho6a0bb172015-02-05 12:24:48 -0800355 case GROUP:
356 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
357 if (groupStats != null) {
358 OFGroupStatsReply.Builder rep =
359 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
360 rep.setEntries(Lists.newLinkedList(groupStats));
361 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800362 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800363 }
364 break;
365 case GROUP_DESC:
366 groupDescStats = publishGroupDescStats(dpid,
367 (OFGroupDescStatsReply) reply);
368 if (groupDescStats != null) {
369 OFGroupDescStatsReply.Builder rep =
Marc De Leenheerb9311372015-07-09 11:36:49 -0700370 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
sangho6a0bb172015-02-05 12:24:48 -0800371 rep.setEntries(Lists.newLinkedList(groupDescStats));
372 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800373 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800374 }
375 break;
sangho538108b2015-04-08 14:29:20 -0700376 case PORT:
Andrea Campanelladda93562016-03-02 11:08:12 -0800377 executorMsgs.execute(new OFMessageHandler(dpid, reply));
sangho538108b2015-04-08 14:29:20 -0700378 break;
alshabib5eb79392015-08-19 18:09:55 -0700379 case METER:
Andrea Campanelladda93562016-03-02 11:08:12 -0800380 executorMsgs.execute(new OFMessageHandler(dpid, reply));
alshabib5eb79392015-08-19 18:09:55 -0700381 break;
Marc De Leenheerb9311372015-07-09 11:36:49 -0700382 case EXPERIMENTER:
383 if (reply instanceof OFCalientFlowStatsReply) {
384 // Convert Calient flow statistics to regular flow stats
385 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
sangyun-han69ed4462016-07-27 12:10:12 +0900386 if (sw == null) {
387 log.error("Switch {} is not found", dpid);
388 break;
389 }
390 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
Marc De Leenheerb9311372015-07-09 11:36:49 -0700391 List<OFFlowStatsEntry> entries = new LinkedList<>();
392 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
393
394 // Single instruction, i.e., output to port
395 OFActionOutput action = OFFactories
396 .getFactory(msg.getVersion())
397 .actions()
398 .buildOutput()
399 .setPort(entry.getOutPort())
400 .build();
401 OFInstruction instruction = OFFactories
402 .getFactory(msg.getVersion())
403 .instructions()
404 .applyActions(Collections.singletonList(action));
sangyun-han69ed4462016-07-27 12:10:12 +0900405 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
Marc De Leenheerb9311372015-07-09 11:36:49 -0700406 .setMatch(entry.getMatch())
407 .setTableId(entry.getTableId())
408 .setDurationSec(entry.getDurationSec())
409 .setDurationNsec(entry.getDurationNsec())
410 .setPriority(entry.getPriority())
411 .setIdleTimeout(entry.getIdleTimeout())
412 .setHardTimeout(entry.getHardTimeout())
413 .setFlags(entry.getFlags())
414 .setCookie(entry.getCookie())
415 .setInstructions(Collections.singletonList(instruction))
416 .build();
417 entries.add(fs);
418 }
419 fsr.setEntries(entries);
420
421 flowStats = publishFlowStats(dpid, fsr.build());
422 if (flowStats != null) {
423 OFFlowStatsReply.Builder rep =
424 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
425 rep.setEntries(Lists.newLinkedList(flowStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800426 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700427 }
428 } else {
Andrea Campanelladda93562016-03-02 11:08:12 -0800429 executorMsgs.execute(new OFMessageHandler(dpid, reply));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700430 }
431 break;
sangho6a0bb172015-02-05 12:24:48 -0800432 default:
alshabib5eb79392015-08-19 18:09:55 -0700433 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
Marc De Leenheerb9311372015-07-09 11:36:49 -0700434 break;
alshabib64def642014-12-02 23:27:37 -0800435 }
436 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700437 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530438 if (errorMsgs.containsKey(msg.getXid())) {
439 //To make oferror msg handling and corresponding barrier reply serialized,
440 // executorErrorMsgs is used for both transaction
441 errorMsgs.remove(msg.getXid());
442 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
443 } else {
444 executorBarrier.execute(new OFMessageHandler(dpid, msg));
445 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700446 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700447 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900448 if (sw == null) {
449 log.error("Switch {} is not found", dpid);
450 break;
451 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700452 long experimenter = ((OFExperimenter) msg).getExperimenter();
453 if (experimenter == 0x748771) {
454 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700455 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900456 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
457 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700458 portDesc.setPortNo(circuitPortStatus.getPortNo())
459 .setHwAddr(circuitPortStatus.getHwAddr())
460 .setName(circuitPortStatus.getName())
461 .setConfig(circuitPortStatus.getConfig())
462 .setState(circuitPortStatus.getState());
463 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
464 for (OpenFlowSwitchListener l : ofSwitchListener) {
465 l.portChanged(dpid, portStatus.build());
466 }
467 } else {
468 log.warn("Handling experimenter type {} not yet implemented",
469 ((OFExperimenter) msg).getExperimenter(), msg);
470 }
471 break;
tom7ef8ff92014-09-17 13:08:06 -0700472 default:
473 log.warn("Handling message type {} not yet implemented {}",
474 msg.getType(), msg);
475 }
476 }
477
sangho6a0bb172015-02-05 12:24:48 -0800478 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
479 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800480 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800481 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800482 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800483 return fullFlowStats.removeAll(dpid);
484 }
485 return null;
486 }
487
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700488 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
489 OFTableStatsReply reply) {
490 //TODO: Get rid of synchronized
491 fullTableStats.putAll(dpid, reply.getEntries());
492 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
493 return fullTableStats.removeAll(dpid);
494 }
495 return null;
496 }
497
sangho6a0bb172015-02-05 12:24:48 -0800498 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
499 OFGroupStatsReply reply) {
500 //TODO: Get rid of synchronized
501 fullGroupStats.putAll(dpid, reply.getEntries());
502 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
503 return fullGroupStats.removeAll(dpid);
504 }
505 return null;
506 }
507
508 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
509 OFGroupDescStatsReply reply) {
510 //TODO: Get rid of synchronized
511 fullGroupDescStats.putAll(dpid, reply.getEntries());
512 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
513 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800514 }
515 return null;
516 }
517
sangho538108b2015-04-08 14:29:20 -0700518 private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
519 OFPortStatsReply reply) {
520 fullPortStats.putAll(dpid, reply.getEntries());
521 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
522 return fullPortStats.removeAll(dpid);
523 }
524 return null;
525 }
526
tom7ef8ff92014-09-17 13:08:06 -0700527 @Override
528 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700529 final OpenFlowSwitch sw = getSwitch(dpid);
530 if (sw == null) {
531 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
532 return;
533 }
534 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700535 }
536
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700537 class InternalDeviceListener implements DeviceListener {
538
539 @Override
540 public boolean isRelevant(DeviceEvent event) {
541 return event.type() == Type.DEVICE_REMOVED;
542 }
543
544 @Override
545 public void event(DeviceEvent event) {
546 switch (event.type()) {
547 case DEVICE_ADDED:
548 break;
549 case DEVICE_AVAILABILITY_CHANGED:
550 break;
551 case DEVICE_REMOVED:
552 // Device administratively removed, disconnect
553 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
554 .ifPresent(OpenFlowSwitch::disconnectSwitch);
555 break;
556 case DEVICE_SUSPENDED:
557 break;
558 case DEVICE_UPDATED:
559 break;
560 case PORT_ADDED:
561 break;
562 case PORT_REMOVED:
563 break;
564 case PORT_STATS_UPDATED:
565 break;
566 case PORT_UPDATED:
567 break;
568 default:
569 break;
570
571 }
572
573 }
574
575 }
576
tom7ef8ff92014-09-17 13:08:06 -0700577 /**
578 * Implementation of an OpenFlow Agent which is responsible for
579 * keeping track of connected switches and the state in which
580 * they are.
581 */
582 public class OpenFlowSwitchAgent implements OpenFlowAgent {
583
584 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
585 private final Lock switchLock = new ReentrantLock();
586
587 @Override
588 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700589
tom7ef8ff92014-09-17 13:08:06 -0700590 if (connectedSwitches.get(dpid) != null) {
591 log.error("Trying to add connectedSwitch but found a previous "
592 + "value for dpid: {}", dpid);
593 return false;
594 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700595 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700596 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700597 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700598 l.switchAdded(dpid);
599 }
600 return true;
601 }
602 }
603
604 @Override
605 public boolean validActivation(Dpid dpid) {
606 if (connectedSwitches.get(dpid) == null) {
607 log.error("Trying to activate switch but is not in "
608 + "connected switches: dpid {}. Aborting ..",
609 dpid);
610 return false;
611 }
612 if (activeMasterSwitches.get(dpid) != null ||
613 activeEqualSwitches.get(dpid) != null) {
614 log.error("Trying to activate switch but it is already "
615 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800616 + "Found in activeEqual: {}. Aborting ..",
617 dpid,
618 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
619 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700620 return false;
621 }
622 return true;
623 }
624
625
626 @Override
627 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
628 switchLock.lock();
629 try {
630 if (!validActivation(dpid)) {
631 return false;
632 }
633 activeMasterSwitches.put(dpid, sw);
634 return true;
635 } finally {
636 switchLock.unlock();
637 }
638 }
639
640 @Override
641 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
642 switchLock.lock();
643 try {
644 if (!validActivation(dpid)) {
645 return false;
646 }
647 activeEqualSwitches.put(dpid, sw);
648 log.info("Added Activated EQUAL Switch {}", dpid);
649 return true;
650 } finally {
651 switchLock.unlock();
652 }
653 }
654
655 @Override
656 public void transitionToMasterSwitch(Dpid dpid) {
657 switchLock.lock();
658 try {
659 if (activeMasterSwitches.containsKey(dpid)) {
660 return;
661 }
662 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
663 if (sw == null) {
664 sw = getSwitch(dpid);
665 if (sw == null) {
666 log.error("Transition to master called on sw {}, but switch "
667 + "was not found in controller-cache", dpid);
668 return;
669 }
670 }
671 log.info("Transitioned switch {} to MASTER", dpid);
672 activeMasterSwitches.put(dpid, sw);
673 } finally {
674 switchLock.unlock();
675 }
676 }
677
678
679 @Override
680 public void transitionToEqualSwitch(Dpid dpid) {
681 switchLock.lock();
682 try {
683 if (activeEqualSwitches.containsKey(dpid)) {
684 return;
685 }
686 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
687 if (sw == null) {
688 sw = getSwitch(dpid);
689 if (sw == null) {
690 log.error("Transition to equal called on sw {}, but switch "
691 + "was not found in controller-cache", dpid);
692 return;
693 }
694 }
695 log.info("Transitioned switch {} to EQUAL", dpid);
696 activeEqualSwitches.put(dpid, sw);
697 } finally {
698 switchLock.unlock();
699 }
700
701 }
702
703 @Override
704 public void removeConnectedSwitch(Dpid dpid) {
705 connectedSwitches.remove(dpid);
706 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
707 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700708 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700709 sw = activeEqualSwitches.remove(dpid);
710 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700711 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700712 l.switchRemoved(dpid);
713 }
714 }
715
716 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700717 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
718 for (OpenFlowMessageListener listener : ofMessageListener) {
719 listener.handleOutgoingMessage(dpid, m);
720 }
721 }
722
723
724 @Override
tom7ef8ff92014-09-17 13:08:06 -0700725 public void processMessage(Dpid dpid, OFMessage m) {
726 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700727
728 for (OpenFlowMessageListener listener : ofMessageListener) {
729 listener.handleIncomingMessage(dpid, m);
730 }
tom7ef8ff92014-09-17 13:08:06 -0700731 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700732
733 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700734 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700735 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700736 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700737 }
738 }
tom7ef8ff92014-09-17 13:08:06 -0700739 }
740
Jian Li152b8852015-12-07 14:47:25 -0800741 /**
Jian Li2266bff2016-04-21 11:01:25 -0700742 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800743 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800744 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700745
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800746 protected final OFMessage msg;
747 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700748
749 public OFMessageHandler(Dpid dpid, OFMessage msg) {
750 this.msg = msg;
751 this.dpid = dpid;
752 }
753
754 @Override
755 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700756 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700757 listener.handleMessage(dpid, msg);
758 }
759 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700760 }
tom7ef8ff92014-09-17 13:08:06 -0700761}