blob: c71741923e26fb1acbaafe1f7aefbf919c2739bc [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
Brian O'Connore755caa2015-11-16 16:43:09 -080019import com.google.common.collect.Lists;
tom7ef8ff92014-09-17 13:08:06 -070020import org.jboss.netty.channel.Channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080021import org.onlab.packet.IpAddress;
Marc De Leenheerb9311372015-07-09 11:36:49 -070022import org.onosproject.net.Device;
alshabibb452fd72015-04-22 20:46:20 -070023import org.onosproject.net.driver.AbstractHandlerBehaviour;
Brian O'Connorabafb502014-12-02 22:26:20 -080024import org.onosproject.openflow.controller.Dpid;
25import org.onosproject.openflow.controller.RoleState;
tom7ef8ff92014-09-17 13:08:06 -070026import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
27import org.projectfloodlight.openflow.protocol.OFErrorMsg;
28import org.projectfloodlight.openflow.protocol.OFExperimenter;
29import org.projectfloodlight.openflow.protocol.OFFactories;
30import org.projectfloodlight.openflow.protocol.OFFactory;
31import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
32import org.projectfloodlight.openflow.protocol.OFMessage;
alshabibb452fd72015-04-22 20:46:20 -070033import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070034import org.projectfloodlight.openflow.protocol.OFPortDesc;
35import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
Thomas Vachuska39274462014-12-02 13:23:50 -080036import org.projectfloodlight.openflow.protocol.OFPortStatus;
tom7ef8ff92014-09-17 13:08:06 -070037import org.projectfloodlight.openflow.protocol.OFRoleReply;
alshabibb452fd72015-04-22 20:46:20 -070038import org.projectfloodlight.openflow.protocol.OFRoleRequest;
tom7ef8ff92014-09-17 13:08:06 -070039import org.projectfloodlight.openflow.protocol.OFVersion;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Thomas Vachuska1c681d72015-05-18 14:58:53 -070043import java.io.IOException;
44import java.net.InetSocketAddress;
45import java.net.SocketAddress;
46import java.util.ArrayList;
47import java.util.Collections;
48import java.util.List;
49import java.util.concurrent.atomic.AtomicInteger;
Brian O'Connore755caa2015-11-16 16:43:09 -080050import java.util.concurrent.atomic.AtomicReference;
Thomas Vachuska1c681d72015-05-18 14:58:53 -070051import java.util.stream.Collectors;
52
tom7ef8ff92014-09-17 13:08:06 -070053/**
54 * An abstract representation of an OpenFlow switch. Can be extended by others
55 * to serve as a base for their vendor specific representation of a switch.
56 */
alshabibb452fd72015-04-22 20:46:20 -070057public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
58 implements OpenFlowSwitchDriver {
tom7ef8ff92014-09-17 13:08:06 -070059
Yuta HIGUCHIf5416d82014-10-24 21:17:40 -070060 protected final Logger log = LoggerFactory.getLogger(getClass());
tom7ef8ff92014-09-17 13:08:06 -070061
alshabibb452fd72015-04-22 20:46:20 -070062 private Channel channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080063 protected String channelId;
tom7ef8ff92014-09-17 13:08:06 -070064
65 private boolean connected;
66 protected boolean startDriverHandshakeCalled = false;
alshabibb452fd72015-04-22 20:46:20 -070067 private Dpid dpid;
tom7ef8ff92014-09-17 13:08:06 -070068 private OpenFlowAgent agent;
69 private final AtomicInteger xidCounter = new AtomicInteger(0);
70
71 private OFVersion ofVersion;
72
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070073 protected List<OFPortDescStatsReply> ports = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -070074
75 protected boolean tableFull;
76
77 private RoleHandler roleMan;
78
Brian O'Connore755caa2015-11-16 16:43:09 -080079 // TODO this is accessed from multiple threads, but volatile may have performance implications
80 protected volatile RoleState role;
tom7ef8ff92014-09-17 13:08:06 -070081
82 protected OFFeaturesReply features;
83 protected OFDescStatsReply desc;
84
Brian O'Connore755caa2015-11-16 16:43:09 -080085 private final AtomicReference<List<OFMessage>> messagesPendingMastership
86 = new AtomicReference<>();
Charles Chan5b7ec342015-10-18 20:55:41 -070087
alshabibb452fd72015-04-22 20:46:20 -070088 @Override
89 public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
tom7ef8ff92014-09-17 13:08:06 -070090 this.dpid = dpid;
91 this.desc = desc;
alshabibb452fd72015-04-22 20:46:20 -070092 this.ofVersion = ofv;
tom7ef8ff92014-09-17 13:08:06 -070093 }
94
95 //************************
96 // Channel related
97 //************************
98
99 @Override
100 public final void disconnectSwitch() {
101 this.channel.close();
102 }
103
104 @Override
Charles Chan5b7ec342015-10-18 20:55:41 -0700105 public void sendMsg(OFMessage msg) {
106 this.sendMsg(Collections.singletonList(msg));
tom7ef8ff92014-09-17 13:08:06 -0700107 }
108
109 @Override
110 public final void sendMsg(List<OFMessage> msgs) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800111 /*
112 It is possible that in this block, we transition to SLAVE/EQUAL.
113 If this is the case, the supplied messages will race with the
114 RoleRequest message, and they could be rejected by the switch.
115 In the interest of performance, we will not protect this block with
116 a synchronization primitive, because the message would have just been
117 dropped anyway.
118 */
119 if (role == RoleState.MASTER) {
120 // fast path send when we are master
121
122 sendMsgsOnChannel(msgs);
123 return;
124 }
125 // check to see if mastership transition is in progress
126 synchronized (messagesPendingMastership) {
127 /*
128 messagesPendingMastership is used as synchronization variable for
129 all mastership related changes. In this block, mastership (including
130 role update) will have either occurred or not.
131 */
132 if (role == RoleState.MASTER) {
133 // transition to MASTER complete, send messages
134 sendMsgsOnChannel(msgs);
135 return;
136 }
137
138 List<OFMessage> messages = messagesPendingMastership.get();
139 if (messages != null) {
140 // we are transitioning to MASTER, so add messages to queue
141 messages.addAll(msgs);
142 log.debug("Enqueue message for switch {}. queue size after is {}",
143 dpid, messages.size());
144 } else {
145 // not transitioning to MASTER
146 log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
147 dpid, role, channel.isConnected(), msgs);
148 }
149 }
150 }
151
152 private void sendMsgsOnChannel(List<OFMessage> msgs) {
153 if (channel.isConnected()) {
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700154 channel.write(msgs);
Charles Chan5b7ec342015-10-18 20:55:41 -0700155 } else {
Brian O'Connore755caa2015-11-16 16:43:09 -0800156 log.warn("Dropping messages for switch {} because channel is not connected: {}",
157 dpid, msgs);
alshabib339a3d92014-09-26 17:54:32 -0700158 }
tom7ef8ff92014-09-17 13:08:06 -0700159 }
160
161 @Override
alshabibb452fd72015-04-22 20:46:20 -0700162 public final void sendRoleRequest(OFMessage msg) {
163 if (msg instanceof OFRoleRequest ||
164 msg instanceof OFNiciraControllerRoleRequest) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800165 sendMsgsOnChannel(Collections.singletonList(msg));
alshabibb452fd72015-04-22 20:46:20 -0700166 return;
167 }
168 throw new IllegalArgumentException("Someone is trying to send " +
169 "a non role request message");
170 }
tom7ef8ff92014-09-17 13:08:06 -0700171
Ayaka Koshibe3c240772015-05-20 16:23:40 -0700172 @Override
alshabiba2df7b2a2015-05-06 13:57:10 -0700173 public final void sendHandshakeMessage(OFMessage message) {
174 if (!this.isDriverHandshakeComplete()) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800175 sendMsgsOnChannel(Collections.singletonList(message));
alshabiba2df7b2a2015-05-06 13:57:10 -0700176 }
177 }
178
tom7ef8ff92014-09-17 13:08:06 -0700179 @Override
180 public final boolean isConnected() {
181 return this.connected;
182 }
183
184 @Override
185 public final void setConnected(boolean connected) {
186 this.connected = connected;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700187 }
tom7ef8ff92014-09-17 13:08:06 -0700188
189 @Override
190 public final void setChannel(Channel channel) {
191 this.channel = channel;
Ray Milkeye53f1712015-01-16 09:17:16 -0800192 final SocketAddress address = channel.getRemoteAddress();
193 if (address instanceof InetSocketAddress) {
194 final InetSocketAddress inetAddress = (InetSocketAddress) address;
195 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700196 if (ipAddress.isIp4()) {
Ray Milkeye53f1712015-01-16 09:17:16 -0800197 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
198 } else {
199 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
200 }
201 }
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700202 }
tom7ef8ff92014-09-17 13:08:06 -0700203
Ray Milkeye53f1712015-01-16 09:17:16 -0800204 @Override
205 public String channelId() {
206 return channelId;
207 }
208
tom7ef8ff92014-09-17 13:08:06 -0700209 //************************
210 // Switch features related
211 //************************
212
213 @Override
214 public final long getId() {
215 return this.dpid.value();
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700216 }
tom7ef8ff92014-09-17 13:08:06 -0700217
218 @Override
219 public final String getStringId() {
220 return this.dpid.toString();
221 }
222
223 @Override
224 public final void setOFVersion(OFVersion ofV) {
225 this.ofVersion = ofV;
226 }
227
228 @Override
229 public void setTableFull(boolean full) {
230 this.tableFull = full;
231 }
232
233 @Override
234 public void setFeaturesReply(OFFeaturesReply featuresReply) {
235 this.features = featuresReply;
236 }
237
238 @Override
239 public abstract Boolean supportNxRole();
240
241 //************************
242 // Message handling
243 //************************
244 /**
245 * Handle the message coming from the dataplane.
246 *
247 * @param m the actual message
248 */
249 @Override
250 public final void handleMessage(OFMessage m) {
Thomas Vachuska39274462014-12-02 13:23:50 -0800251 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
alshabib339a3d92014-09-26 17:54:32 -0700252 this.agent.processMessage(dpid, m);
253 }
tom7ef8ff92014-09-17 13:08:06 -0700254 }
255
256 @Override
257 public RoleState getRole() {
258 return role;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700259 }
tom7ef8ff92014-09-17 13:08:06 -0700260
261 @Override
262 public final boolean connectSwitch() {
263 return this.agent.addConnectedSwitch(dpid, this);
264 }
265
266 @Override
267 public final boolean activateMasterSwitch() {
268 return this.agent.addActivatedMasterSwitch(dpid, this);
269 }
270
271 @Override
272 public final boolean activateEqualSwitch() {
273 return this.agent.addActivatedEqualSwitch(dpid, this);
274 }
275
276 @Override
277 public final void transitionToEqualSwitch() {
278 this.agent.transitionToEqualSwitch(dpid);
279 }
280
281 @Override
282 public final void transitionToMasterSwitch() {
283 this.agent.transitionToMasterSwitch(dpid);
Brian O'Connore755caa2015-11-16 16:43:09 -0800284 synchronized (messagesPendingMastership) {
285 List<OFMessage> messages = messagesPendingMastership.get();
286 if (messages != null) {
287 this.sendMsg(messages);
288 log.debug("Sending {} pending messages to switch {}",
289 messages.size(), dpid);
290 messagesPendingMastership.set(null);
291 }
292 // perform role transition after clearing messages queue
293 this.role = RoleState.MASTER;
Charles Chan5b7ec342015-10-18 20:55:41 -0700294 }
tom7ef8ff92014-09-17 13:08:06 -0700295 }
296
297 @Override
298 public final void removeConnectedSwitch() {
299 this.agent.removeConnectedSwitch(dpid);
300 }
301
302 @Override
303 public OFFactory factory() {
304 return OFFactories.getFactory(ofVersion);
305 }
306
307 @Override
308 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700309 this.ports.add(portDescReply);
310 }
311
312 @Override
313 public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
314 this.ports.addAll(portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700315 }
316
317 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700318 public void returnRoleReply(RoleState requested, RoleState response) {
319 this.agent.returnRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700320 }
321
322 @Override
tom7ef8ff92014-09-17 13:08:06 -0700323 public abstract void startDriverHandshake();
324
325 @Override
326 public abstract boolean isDriverHandshakeComplete();
327
328 @Override
329 public abstract void processDriverHandshakeMessage(OFMessage m);
330
alshabib339a3d92014-09-26 17:54:32 -0700331
332 // Role Handling
333
tom7ef8ff92014-09-17 13:08:06 -0700334 @Override
335 public void setRole(RoleState role) {
336 try {
Brian O'Connore755caa2015-11-16 16:43:09 -0800337 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
338 // perform role transition to SLAVE/EQUAL before sending role request
339 this.role = role;
340 }
tom7ef8ff92014-09-17 13:08:06 -0700341 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
Madan Jampanif2af7712015-05-29 18:43:52 -0700342 log.debug("Sending role {} to switch {}", role, getStringId());
Brian O'Connore755caa2015-11-16 16:43:09 -0800343 if (role == RoleState.MASTER) {
344 synchronized (messagesPendingMastership) {
345 if (messagesPendingMastership.get() == null) {
346 log.debug("Initializing new message queue for switch {}", dpid);
347 /*
348 The presence of messagesPendingMastership indicates that
349 a switch is currently transitioning to MASTER, but
350 is still awaiting role reply from switch.
351 */
352 messagesPendingMastership.set(Lists.newArrayList());
353 }
Charles Chan5b7ec342015-10-18 20:55:41 -0700354 }
alshabib339a3d92014-09-26 17:54:32 -0700355 }
Brian O'Connore755caa2015-11-16 16:43:09 -0800356 } else if (role == RoleState.MASTER) {
357 // role request not support; transition switch to MASTER
alshabib7814e9f2014-09-30 11:52:12 -0700358 this.role = role;
tom7ef8ff92014-09-17 13:08:06 -0700359 }
360 } catch (IOException e) {
361 log.error("Unable to write to switch {}.", this.dpid);
362 }
363 }
364
alshabib339a3d92014-09-26 17:54:32 -0700365 @Override
366 public void reassertRole() {
Brian O'Connore755caa2015-11-16 16:43:09 -0800367 // TODO should messages be sent directly or queue during reassertion?
alshabib339a3d92014-09-26 17:54:32 -0700368 if (this.getRole() == RoleState.MASTER) {
369 log.warn("Received permission error from switch {} while " +
370 "being master. Reasserting master role.",
371 this.getStringId());
372 this.setRole(RoleState.MASTER);
373 }
374 }
375
tom7ef8ff92014-09-17 13:08:06 -0700376 @Override
377 public void handleRole(OFMessage m) throws SwitchStateException {
378 RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
379 RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
380 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
381 if (rri.getRole() == RoleState.MASTER) {
382 this.transitionToMasterSwitch();
383 } else if (rri.getRole() == RoleState.EQUAL ||
Brian O'Connore755caa2015-11-16 16:43:09 -0800384 rri.getRole() == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700385 this.transitionToEqualSwitch();
386 }
alshabib339a3d92014-09-26 17:54:32 -0700387 } else {
alshabib4785eec2014-12-04 16:45:45 -0800388 log.warn("Failed to set role for {}", this.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700389 }
390 }
391
392 @Override
393 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
394 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
395 if (r == null) {
396 // The message wasn't really a Nicira role reply. We just
397 // dispatch it to the OFMessage listeners in this case.
398 this.handleMessage(m);
alshabibdfc7afb2014-10-21 20:13:27 -0700399 return;
tom7ef8ff92014-09-17 13:08:06 -0700400 }
401
402 RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
403 new RoleReplyInfo(r, null, m.getXid()));
404 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
405 if (r == RoleState.MASTER) {
406 this.transitionToMasterSwitch();
407 } else if (r == RoleState.EQUAL ||
Brian O'Connore755caa2015-11-16 16:43:09 -0800408 r == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700409 this.transitionToEqualSwitch();
410 }
alshabib339a3d92014-09-26 17:54:32 -0700411 } else {
alshabibdfc7afb2014-10-21 20:13:27 -0700412 this.disconnectSwitch();
tom7ef8ff92014-09-17 13:08:06 -0700413 }
414 }
415
416 @Override
417 public boolean handleRoleError(OFErrorMsg error) {
418 try {
419 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
420 } catch (SwitchStateException e) {
421 this.disconnectSwitch();
422 }
423 return true;
424 }
425
tom7ef8ff92014-09-17 13:08:06 -0700426 @Override
427 public final void setAgent(OpenFlowAgent ag) {
428 if (this.agent == null) {
429 this.agent = ag;
430 }
431 }
432
433 @Override
434 public final void setRoleHandler(RoleHandler roleHandler) {
435 if (this.roleMan == null) {
436 this.roleMan = roleHandler;
437 }
438 }
439
440 @Override
441 public void setSwitchDescription(OFDescStatsReply d) {
442 this.desc = d;
443 }
444
445 @Override
446 public int getNextTransactionId() {
447 return this.xidCounter.getAndIncrement();
448 }
449
450 @Override
451 public List<OFPortDesc> getPorts() {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700452 return this.ports.stream()
Brian O'Connore755caa2015-11-16 16:43:09 -0800453 .flatMap(portReply -> portReply.getEntries().stream())
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700454 .collect(Collectors.toList());
tom7ef8ff92014-09-17 13:08:06 -0700455 }
456
457 @Override
Ray Milkeyd3edd032015-01-16 11:38:58 -0800458 public String manufacturerDescription() {
tom7ef8ff92014-09-17 13:08:06 -0700459 return this.desc.getMfrDesc();
460 }
461
tom7ef8ff92014-09-17 13:08:06 -0700462 @Override
463 public String datapathDescription() {
464 return this.desc.getDpDesc();
465 }
466
tom7ef8ff92014-09-17 13:08:06 -0700467 @Override
468 public String hardwareDescription() {
469 return this.desc.getHwDesc();
470 }
471
472 @Override
473 public String softwareDescription() {
474 return this.desc.getSwDesc();
475 }
476
477 @Override
478 public String serialNumber() {
479 return this.desc.getSerialNum();
480 }
481
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700482 @Override
Marc De Leenheerb9311372015-07-09 11:36:49 -0700483 public Device.Type deviceType() {
484 return Device.Type.SWITCH;
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700485 }
486
alshabibb452fd72015-04-22 20:46:20 -0700487 @Override
488 public String toString() {
489 return this.getClass().getName() + " [" + ((channel != null)
490 ? channel.getRemoteAddress() : "?")
491 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
492 }
tom7ef8ff92014-09-17 13:08:06 -0700493}