blob: 2c19837e941a1e247bcd73e886eafaf51a40f8e1 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
tom7ef8ff92014-09-17 13:08:06 -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
tom7ef8ff92014-09-17 13:08:06 -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 */
tom7ef8ff92014-09-17 13:08:06 -070016
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.driver;
tom7ef8ff92014-09-17 13:08:06 -070018
tom7ef8ff92014-09-17 13:08:06 -070019import org.jboss.netty.channel.Channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080020import org.onlab.packet.IpAddress;
Marc De Leenheerb9311372015-07-09 11:36:49 -070021import org.onosproject.net.Device;
alshabibb452fd72015-04-22 20:46:20 -070022import org.onosproject.net.driver.AbstractHandlerBehaviour;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.openflow.controller.Dpid;
24import org.onosproject.openflow.controller.RoleState;
tom7ef8ff92014-09-17 13:08:06 -070025import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
26import org.projectfloodlight.openflow.protocol.OFErrorMsg;
27import org.projectfloodlight.openflow.protocol.OFExperimenter;
28import org.projectfloodlight.openflow.protocol.OFFactories;
29import org.projectfloodlight.openflow.protocol.OFFactory;
30import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
31import org.projectfloodlight.openflow.protocol.OFMessage;
alshabibb452fd72015-04-22 20:46:20 -070032import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070033import org.projectfloodlight.openflow.protocol.OFPortDesc;
34import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
Thomas Vachuska39274462014-12-02 13:23:50 -080035import org.projectfloodlight.openflow.protocol.OFPortStatus;
tom7ef8ff92014-09-17 13:08:06 -070036import org.projectfloodlight.openflow.protocol.OFRoleReply;
alshabibb452fd72015-04-22 20:46:20 -070037import org.projectfloodlight.openflow.protocol.OFRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070038import org.projectfloodlight.openflow.protocol.OFVersion;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Thomas Vachuska1c681d72015-05-18 14:58:53 -070042import java.io.IOException;
43import java.net.InetSocketAddress;
44import java.net.SocketAddress;
45import java.util.ArrayList;
46import java.util.Collections;
47import java.util.List;
48import java.util.concurrent.atomic.AtomicInteger;
49import java.util.stream.Collectors;
50
tom7ef8ff92014-09-17 13:08:06 -070051/**
52 * An abstract representation of an OpenFlow switch. Can be extended by others
53 * to serve as a base for their vendor specific representation of a switch.
54 */
alshabibb452fd72015-04-22 20:46:20 -070055public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
56 implements OpenFlowSwitchDriver {
tom7ef8ff92014-09-17 13:08:06 -070057
Yuta HIGUCHIf5416d82014-10-24 21:17:40 -070058 protected final Logger log = LoggerFactory.getLogger(getClass());
tom7ef8ff92014-09-17 13:08:06 -070059
alshabibb452fd72015-04-22 20:46:20 -070060 private Channel channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080061 protected String channelId;
tom7ef8ff92014-09-17 13:08:06 -070062
63 private boolean connected;
64 protected boolean startDriverHandshakeCalled = false;
alshabibb452fd72015-04-22 20:46:20 -070065 private Dpid dpid;
tom7ef8ff92014-09-17 13:08:06 -070066 private OpenFlowAgent agent;
67 private final AtomicInteger xidCounter = new AtomicInteger(0);
68
69 private OFVersion ofVersion;
70
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070071 protected List<OFPortDescStatsReply> ports = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -070072
73 protected boolean tableFull;
74
75 private RoleHandler roleMan;
76
77 protected RoleState role;
78
79 protected OFFeaturesReply features;
80 protected OFDescStatsReply desc;
81
Charles Chan5b7ec342015-10-18 20:55:41 -070082 List<OFMessage> messagesPendingMastership;
83
alshabibb452fd72015-04-22 20:46:20 -070084 @Override
85 public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
tom7ef8ff92014-09-17 13:08:06 -070086 this.dpid = dpid;
87 this.desc = desc;
alshabibb452fd72015-04-22 20:46:20 -070088 this.ofVersion = ofv;
tom7ef8ff92014-09-17 13:08:06 -070089 }
90
91 //************************
92 // Channel related
93 //************************
94
95 @Override
96 public final void disconnectSwitch() {
97 this.channel.close();
98 }
99
100 @Override
Charles Chan5b7ec342015-10-18 20:55:41 -0700101 public void sendMsg(OFMessage msg) {
102 this.sendMsg(Collections.singletonList(msg));
tom7ef8ff92014-09-17 13:08:06 -0700103 }
104
105 @Override
106 public final void sendMsg(List<OFMessage> msgs) {
Jonathan Hart9f1600a2015-07-28 13:58:31 -0700107 if (role == RoleState.MASTER && channel.isConnected()) {
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700108 channel.write(msgs);
Charles Chan5b7ec342015-10-18 20:55:41 -0700109 } else if (messagesPendingMastership != null) {
110 messagesPendingMastership.addAll(msgs);
111 log.debug("Enqueue message for switch {}. queue size after is {}",
112 dpid, messagesPendingMastership.size());
113 } else {
114 log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
115 dpid, role, channel.isConnected(), msgs);
alshabib339a3d92014-09-26 17:54:32 -0700116 }
tom7ef8ff92014-09-17 13:08:06 -0700117 }
118
119 @Override
alshabibb452fd72015-04-22 20:46:20 -0700120 public final void sendRoleRequest(OFMessage msg) {
121 if (msg instanceof OFRoleRequest ||
122 msg instanceof OFNiciraControllerRoleRequest) {
123 channel.write(Collections.singletonList(msg));
124 return;
125 }
126 throw new IllegalArgumentException("Someone is trying to send " +
127 "a non role request message");
128 }
tom7ef8ff92014-09-17 13:08:06 -0700129
Ayaka Koshibe3c240772015-05-20 16:23:40 -0700130 @Override
alshabiba2df7b2a2015-05-06 13:57:10 -0700131 public final void sendHandshakeMessage(OFMessage message) {
132 if (!this.isDriverHandshakeComplete()) {
133 channel.write(Collections.singletonList(message));
134 }
135 }
136
tom7ef8ff92014-09-17 13:08:06 -0700137 @Override
138 public final boolean isConnected() {
139 return this.connected;
140 }
141
142 @Override
143 public final void setConnected(boolean connected) {
144 this.connected = connected;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700145 }
tom7ef8ff92014-09-17 13:08:06 -0700146
147 @Override
148 public final void setChannel(Channel channel) {
149 this.channel = channel;
Ray Milkeye53f1712015-01-16 09:17:16 -0800150 final SocketAddress address = channel.getRemoteAddress();
151 if (address instanceof InetSocketAddress) {
152 final InetSocketAddress inetAddress = (InetSocketAddress) address;
153 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700154 if (ipAddress.isIp4()) {
Ray Milkeye53f1712015-01-16 09:17:16 -0800155 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
156 } else {
157 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
158 }
159 }
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700160 }
tom7ef8ff92014-09-17 13:08:06 -0700161
Ray Milkeye53f1712015-01-16 09:17:16 -0800162 @Override
163 public String channelId() {
164 return channelId;
165 }
166
tom7ef8ff92014-09-17 13:08:06 -0700167 //************************
168 // Switch features related
169 //************************
170
171 @Override
172 public final long getId() {
173 return this.dpid.value();
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700174 }
tom7ef8ff92014-09-17 13:08:06 -0700175
176 @Override
177 public final String getStringId() {
178 return this.dpid.toString();
179 }
180
181 @Override
182 public final void setOFVersion(OFVersion ofV) {
183 this.ofVersion = ofV;
184 }
185
186 @Override
187 public void setTableFull(boolean full) {
188 this.tableFull = full;
189 }
190
191 @Override
192 public void setFeaturesReply(OFFeaturesReply featuresReply) {
193 this.features = featuresReply;
194 }
195
196 @Override
197 public abstract Boolean supportNxRole();
198
199 //************************
200 // Message handling
201 //************************
202 /**
203 * Handle the message coming from the dataplane.
204 *
205 * @param m the actual message
206 */
207 @Override
208 public final void handleMessage(OFMessage m) {
Thomas Vachuska39274462014-12-02 13:23:50 -0800209 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
alshabib339a3d92014-09-26 17:54:32 -0700210 this.agent.processMessage(dpid, m);
211 }
tom7ef8ff92014-09-17 13:08:06 -0700212 }
213
214 @Override
215 public RoleState getRole() {
216 return role;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700217 }
tom7ef8ff92014-09-17 13:08:06 -0700218
219 @Override
220 public final boolean connectSwitch() {
221 return this.agent.addConnectedSwitch(dpid, this);
222 }
223
224 @Override
225 public final boolean activateMasterSwitch() {
226 return this.agent.addActivatedMasterSwitch(dpid, this);
227 }
228
229 @Override
230 public final boolean activateEqualSwitch() {
231 return this.agent.addActivatedEqualSwitch(dpid, this);
232 }
233
234 @Override
235 public final void transitionToEqualSwitch() {
236 this.agent.transitionToEqualSwitch(dpid);
237 }
238
239 @Override
240 public final void transitionToMasterSwitch() {
241 this.agent.transitionToMasterSwitch(dpid);
Charles Chan5b7ec342015-10-18 20:55:41 -0700242 if (messagesPendingMastership != null) {
243 this.sendMsg(messagesPendingMastership);
244 log.debug("Sending {} pending messages to switch {}",
245 messagesPendingMastership.size(), dpid);
246 messagesPendingMastership = null;
247 }
tom7ef8ff92014-09-17 13:08:06 -0700248 }
249
250 @Override
251 public final void removeConnectedSwitch() {
252 this.agent.removeConnectedSwitch(dpid);
253 }
254
255 @Override
256 public OFFactory factory() {
257 return OFFactories.getFactory(ofVersion);
258 }
259
260 @Override
261 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700262 this.ports.add(portDescReply);
263 }
264
265 @Override
266 public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
267 this.ports.addAll(portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700268 }
269
270 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700271 public void returnRoleReply(RoleState requested, RoleState response) {
272 this.agent.returnRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700273 }
274
275 @Override
tom7ef8ff92014-09-17 13:08:06 -0700276 public abstract void startDriverHandshake();
277
278 @Override
279 public abstract boolean isDriverHandshakeComplete();
280
281 @Override
282 public abstract void processDriverHandshakeMessage(OFMessage m);
283
alshabib339a3d92014-09-26 17:54:32 -0700284
285 // Role Handling
286
tom7ef8ff92014-09-17 13:08:06 -0700287 @Override
288 public void setRole(RoleState role) {
289 try {
tom7ef8ff92014-09-17 13:08:06 -0700290 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
Madan Jampanif2af7712015-05-29 18:43:52 -0700291 log.debug("Sending role {} to switch {}", role, getStringId());
alshabib339a3d92014-09-26 17:54:32 -0700292 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
293 this.role = role;
Charles Chan5b7ec342015-10-18 20:55:41 -0700294 } else {
295 if (messagesPendingMastership == null) {
296 log.debug("Initializing new queue for switch {}", dpid);
297 messagesPendingMastership = new ArrayList<>();
298 }
alshabib339a3d92014-09-26 17:54:32 -0700299 }
alshabib7814e9f2014-09-30 11:52:12 -0700300 } else {
301 this.role = role;
tom7ef8ff92014-09-17 13:08:06 -0700302 }
303 } catch (IOException e) {
304 log.error("Unable to write to switch {}.", this.dpid);
305 }
306 }
307
alshabib339a3d92014-09-26 17:54:32 -0700308 @Override
309 public void reassertRole() {
310 if (this.getRole() == RoleState.MASTER) {
311 log.warn("Received permission error from switch {} while " +
312 "being master. Reasserting master role.",
313 this.getStringId());
314 this.setRole(RoleState.MASTER);
315 }
316 }
317
318
tom7ef8ff92014-09-17 13:08:06 -0700319
320 @Override
321 public void handleRole(OFMessage m) throws SwitchStateException {
322 RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
323 RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
324 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
325 if (rri.getRole() == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700326 this.role = rri.getRole();
tom7ef8ff92014-09-17 13:08:06 -0700327 this.transitionToMasterSwitch();
328 } else if (rri.getRole() == RoleState.EQUAL ||
alshabib339a3d92014-09-26 17:54:32 -0700329 rri.getRole() == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700330 this.transitionToEqualSwitch();
331 }
alshabib339a3d92014-09-26 17:54:32 -0700332 } else {
alshabib4785eec2014-12-04 16:45:45 -0800333 log.warn("Failed to set role for {}", this.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700334 }
335 }
336
337 @Override
338 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
339 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
340 if (r == null) {
341 // The message wasn't really a Nicira role reply. We just
342 // dispatch it to the OFMessage listeners in this case.
343 this.handleMessage(m);
alshabibdfc7afb2014-10-21 20:13:27 -0700344 return;
tom7ef8ff92014-09-17 13:08:06 -0700345 }
346
347 RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
348 new RoleReplyInfo(r, null, m.getXid()));
349 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
350 if (r == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700351 this.role = r;
tom7ef8ff92014-09-17 13:08:06 -0700352 this.transitionToMasterSwitch();
353 } else if (r == RoleState.EQUAL ||
354 r == RoleState.SLAVE) {
355 this.transitionToEqualSwitch();
356 }
alshabib339a3d92014-09-26 17:54:32 -0700357 } else {
alshabibdfc7afb2014-10-21 20:13:27 -0700358 this.disconnectSwitch();
tom7ef8ff92014-09-17 13:08:06 -0700359 }
360 }
361
362 @Override
363 public boolean handleRoleError(OFErrorMsg error) {
364 try {
365 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
366 } catch (SwitchStateException e) {
367 this.disconnectSwitch();
368 }
369 return true;
370 }
371
alshabib339a3d92014-09-26 17:54:32 -0700372
tom7ef8ff92014-09-17 13:08:06 -0700373
374 @Override
375 public final void setAgent(OpenFlowAgent ag) {
376 if (this.agent == null) {
377 this.agent = ag;
378 }
379 }
380
381 @Override
382 public final void setRoleHandler(RoleHandler roleHandler) {
383 if (this.roleMan == null) {
384 this.roleMan = roleHandler;
385 }
386 }
387
388 @Override
389 public void setSwitchDescription(OFDescStatsReply d) {
390 this.desc = d;
391 }
392
393 @Override
394 public int getNextTransactionId() {
395 return this.xidCounter.getAndIncrement();
396 }
397
398 @Override
399 public List<OFPortDesc> getPorts() {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700400 return this.ports.stream()
401 .flatMap((portReply) -> (portReply.getEntries().stream()))
402 .collect(Collectors.toList());
403 //return Collections.unmodifiableList(ports.getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700404 }
405
406 @Override
Ray Milkeyd3edd032015-01-16 11:38:58 -0800407 public String manufacturerDescription() {
tom7ef8ff92014-09-17 13:08:06 -0700408 return this.desc.getMfrDesc();
409 }
410
411
412 @Override
413 public String datapathDescription() {
414 return this.desc.getDpDesc();
415 }
416
417
418 @Override
419 public String hardwareDescription() {
420 return this.desc.getHwDesc();
421 }
422
423 @Override
424 public String softwareDescription() {
425 return this.desc.getSwDesc();
426 }
427
428 @Override
429 public String serialNumber() {
430 return this.desc.getSerialNum();
431 }
432
alshabibb452fd72015-04-22 20:46:20 -0700433
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700434 @Override
Marc De Leenheerb9311372015-07-09 11:36:49 -0700435 public Device.Type deviceType() {
436 return Device.Type.SWITCH;
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700437 }
438
alshabib9af70072015-02-09 14:34:16 -0800439
alshabibb452fd72015-04-22 20:46:20 -0700440 @Override
441 public String toString() {
442 return this.getClass().getName() + " [" + ((channel != null)
443 ? channel.getRemoteAddress() : "?")
444 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
445 }
446
447
alshabib9af70072015-02-09 14:34:16 -0800448
tom7ef8ff92014-09-17 13:08:06 -0700449}