blob: 08444b1772cf26d45a78eda96a60363e428e1ee4 [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() {
Charles Chanecfdfb72015-11-24 19:05:50 -0800101 setConnected(false);
tom7ef8ff92014-09-17 13:08:06 -0700102 this.channel.close();
103 }
104
105 @Override
Charles Chan5b7ec342015-10-18 20:55:41 -0700106 public void sendMsg(OFMessage msg) {
107 this.sendMsg(Collections.singletonList(msg));
tom7ef8ff92014-09-17 13:08:06 -0700108 }
109
110 @Override
111 public final void sendMsg(List<OFMessage> msgs) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800112 /*
113 It is possible that in this block, we transition to SLAVE/EQUAL.
114 If this is the case, the supplied messages will race with the
115 RoleRequest message, and they could be rejected by the switch.
116 In the interest of performance, we will not protect this block with
117 a synchronization primitive, because the message would have just been
118 dropped anyway.
119 */
120 if (role == RoleState.MASTER) {
121 // fast path send when we are master
122
123 sendMsgsOnChannel(msgs);
124 return;
125 }
126 // check to see if mastership transition is in progress
127 synchronized (messagesPendingMastership) {
128 /*
129 messagesPendingMastership is used as synchronization variable for
130 all mastership related changes. In this block, mastership (including
131 role update) will have either occurred or not.
132 */
133 if (role == RoleState.MASTER) {
134 // transition to MASTER complete, send messages
135 sendMsgsOnChannel(msgs);
136 return;
137 }
138
139 List<OFMessage> messages = messagesPendingMastership.get();
140 if (messages != null) {
141 // we are transitioning to MASTER, so add messages to queue
142 messages.addAll(msgs);
143 log.debug("Enqueue message for switch {}. queue size after is {}",
144 dpid, messages.size());
145 } else {
146 // not transitioning to MASTER
147 log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
148 dpid, role, channel.isConnected(), msgs);
149 }
150 }
151 }
152
153 private void sendMsgsOnChannel(List<OFMessage> msgs) {
154 if (channel.isConnected()) {
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700155 channel.write(msgs);
Charles Chan5b7ec342015-10-18 20:55:41 -0700156 } else {
Brian O'Connore755caa2015-11-16 16:43:09 -0800157 log.warn("Dropping messages for switch {} because channel is not connected: {}",
158 dpid, msgs);
alshabib339a3d92014-09-26 17:54:32 -0700159 }
tom7ef8ff92014-09-17 13:08:06 -0700160 }
161
162 @Override
alshabibb452fd72015-04-22 20:46:20 -0700163 public final void sendRoleRequest(OFMessage msg) {
164 if (msg instanceof OFRoleRequest ||
165 msg instanceof OFNiciraControllerRoleRequest) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800166 sendMsgsOnChannel(Collections.singletonList(msg));
alshabibb452fd72015-04-22 20:46:20 -0700167 return;
168 }
169 throw new IllegalArgumentException("Someone is trying to send " +
170 "a non role request message");
171 }
tom7ef8ff92014-09-17 13:08:06 -0700172
Ayaka Koshibe3c240772015-05-20 16:23:40 -0700173 @Override
alshabiba2df7b2a2015-05-06 13:57:10 -0700174 public final void sendHandshakeMessage(OFMessage message) {
175 if (!this.isDriverHandshakeComplete()) {
Brian O'Connore755caa2015-11-16 16:43:09 -0800176 sendMsgsOnChannel(Collections.singletonList(message));
alshabiba2df7b2a2015-05-06 13:57:10 -0700177 }
178 }
179
tom7ef8ff92014-09-17 13:08:06 -0700180 @Override
181 public final boolean isConnected() {
182 return this.connected;
183 }
184
185 @Override
186 public final void setConnected(boolean connected) {
187 this.connected = connected;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700188 }
tom7ef8ff92014-09-17 13:08:06 -0700189
190 @Override
191 public final void setChannel(Channel channel) {
192 this.channel = channel;
Ray Milkeye53f1712015-01-16 09:17:16 -0800193 final SocketAddress address = channel.getRemoteAddress();
194 if (address instanceof InetSocketAddress) {
195 final InetSocketAddress inetAddress = (InetSocketAddress) address;
196 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700197 if (ipAddress.isIp4()) {
Ray Milkeye53f1712015-01-16 09:17:16 -0800198 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
199 } else {
200 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
201 }
202 }
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700203 }
tom7ef8ff92014-09-17 13:08:06 -0700204
Ray Milkeye53f1712015-01-16 09:17:16 -0800205 @Override
206 public String channelId() {
207 return channelId;
208 }
209
tom7ef8ff92014-09-17 13:08:06 -0700210 //************************
211 // Switch features related
212 //************************
213
214 @Override
215 public final long getId() {
216 return this.dpid.value();
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700217 }
tom7ef8ff92014-09-17 13:08:06 -0700218
219 @Override
220 public final String getStringId() {
221 return this.dpid.toString();
222 }
223
224 @Override
225 public final void setOFVersion(OFVersion ofV) {
226 this.ofVersion = ofV;
227 }
228
229 @Override
230 public void setTableFull(boolean full) {
231 this.tableFull = full;
232 }
233
234 @Override
235 public void setFeaturesReply(OFFeaturesReply featuresReply) {
236 this.features = featuresReply;
237 }
238
239 @Override
240 public abstract Boolean supportNxRole();
241
242 //************************
243 // Message handling
244 //************************
245 /**
246 * Handle the message coming from the dataplane.
247 *
248 * @param m the actual message
249 */
250 @Override
251 public final void handleMessage(OFMessage m) {
Thomas Vachuska39274462014-12-02 13:23:50 -0800252 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
alshabib339a3d92014-09-26 17:54:32 -0700253 this.agent.processMessage(dpid, m);
254 }
tom7ef8ff92014-09-17 13:08:06 -0700255 }
256
257 @Override
258 public RoleState getRole() {
259 return role;
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700260 }
tom7ef8ff92014-09-17 13:08:06 -0700261
262 @Override
263 public final boolean connectSwitch() {
264 return this.agent.addConnectedSwitch(dpid, this);
265 }
266
267 @Override
268 public final boolean activateMasterSwitch() {
269 return this.agent.addActivatedMasterSwitch(dpid, this);
270 }
271
272 @Override
273 public final boolean activateEqualSwitch() {
274 return this.agent.addActivatedEqualSwitch(dpid, this);
275 }
276
277 @Override
278 public final void transitionToEqualSwitch() {
279 this.agent.transitionToEqualSwitch(dpid);
280 }
281
282 @Override
283 public final void transitionToMasterSwitch() {
284 this.agent.transitionToMasterSwitch(dpid);
Brian O'Connore755caa2015-11-16 16:43:09 -0800285 synchronized (messagesPendingMastership) {
286 List<OFMessage> messages = messagesPendingMastership.get();
287 if (messages != null) {
288 this.sendMsg(messages);
289 log.debug("Sending {} pending messages to switch {}",
290 messages.size(), dpid);
291 messagesPendingMastership.set(null);
292 }
293 // perform role transition after clearing messages queue
294 this.role = RoleState.MASTER;
Charles Chan5b7ec342015-10-18 20:55:41 -0700295 }
tom7ef8ff92014-09-17 13:08:06 -0700296 }
297
298 @Override
299 public final void removeConnectedSwitch() {
300 this.agent.removeConnectedSwitch(dpid);
301 }
302
303 @Override
304 public OFFactory factory() {
305 return OFFactories.getFactory(ofVersion);
306 }
307
308 @Override
309 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700310 this.ports.add(portDescReply);
311 }
312
313 @Override
314 public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
315 this.ports.addAll(portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700316 }
317
318 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700319 public void returnRoleReply(RoleState requested, RoleState response) {
320 this.agent.returnRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700321 }
322
323 @Override
tom7ef8ff92014-09-17 13:08:06 -0700324 public abstract void startDriverHandshake();
325
326 @Override
327 public abstract boolean isDriverHandshakeComplete();
328
329 @Override
330 public abstract void processDriverHandshakeMessage(OFMessage m);
331
alshabib339a3d92014-09-26 17:54:32 -0700332
333 // Role Handling
334
tom7ef8ff92014-09-17 13:08:06 -0700335 @Override
336 public void setRole(RoleState role) {
337 try {
Brian O'Connore755caa2015-11-16 16:43:09 -0800338 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
339 // perform role transition to SLAVE/EQUAL before sending role request
340 this.role = role;
341 }
tom7ef8ff92014-09-17 13:08:06 -0700342 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
Madan Jampanif2af7712015-05-29 18:43:52 -0700343 log.debug("Sending role {} to switch {}", role, getStringId());
Brian O'Connore755caa2015-11-16 16:43:09 -0800344 if (role == RoleState.MASTER) {
345 synchronized (messagesPendingMastership) {
346 if (messagesPendingMastership.get() == null) {
347 log.debug("Initializing new message queue for switch {}", dpid);
348 /*
349 The presence of messagesPendingMastership indicates that
350 a switch is currently transitioning to MASTER, but
351 is still awaiting role reply from switch.
352 */
353 messagesPendingMastership.set(Lists.newArrayList());
354 }
Charles Chan5b7ec342015-10-18 20:55:41 -0700355 }
alshabib339a3d92014-09-26 17:54:32 -0700356 }
Brian O'Connore755caa2015-11-16 16:43:09 -0800357 } else if (role == RoleState.MASTER) {
358 // role request not support; transition switch to MASTER
alshabib7814e9f2014-09-30 11:52:12 -0700359 this.role = role;
tom7ef8ff92014-09-17 13:08:06 -0700360 }
361 } catch (IOException e) {
362 log.error("Unable to write to switch {}.", this.dpid);
363 }
364 }
365
alshabib339a3d92014-09-26 17:54:32 -0700366 @Override
367 public void reassertRole() {
Brian O'Connore755caa2015-11-16 16:43:09 -0800368 // TODO should messages be sent directly or queue during reassertion?
alshabib339a3d92014-09-26 17:54:32 -0700369 if (this.getRole() == RoleState.MASTER) {
370 log.warn("Received permission error from switch {} while " +
371 "being master. Reasserting master role.",
372 this.getStringId());
373 this.setRole(RoleState.MASTER);
374 }
375 }
376
tom7ef8ff92014-09-17 13:08:06 -0700377 @Override
378 public void handleRole(OFMessage m) throws SwitchStateException {
379 RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
380 RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
381 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
382 if (rri.getRole() == RoleState.MASTER) {
383 this.transitionToMasterSwitch();
384 } else if (rri.getRole() == RoleState.EQUAL ||
Brian O'Connore755caa2015-11-16 16:43:09 -0800385 rri.getRole() == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700386 this.transitionToEqualSwitch();
387 }
alshabib339a3d92014-09-26 17:54:32 -0700388 } else {
alshabib4785eec2014-12-04 16:45:45 -0800389 log.warn("Failed to set role for {}", this.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700390 }
391 }
392
393 @Override
394 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
395 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
396 if (r == null) {
397 // The message wasn't really a Nicira role reply. We just
398 // dispatch it to the OFMessage listeners in this case.
399 this.handleMessage(m);
alshabibdfc7afb2014-10-21 20:13:27 -0700400 return;
tom7ef8ff92014-09-17 13:08:06 -0700401 }
402
403 RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
404 new RoleReplyInfo(r, null, m.getXid()));
405 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
406 if (r == RoleState.MASTER) {
407 this.transitionToMasterSwitch();
408 } else if (r == RoleState.EQUAL ||
Brian O'Connore755caa2015-11-16 16:43:09 -0800409 r == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700410 this.transitionToEqualSwitch();
411 }
alshabib339a3d92014-09-26 17:54:32 -0700412 } else {
alshabibdfc7afb2014-10-21 20:13:27 -0700413 this.disconnectSwitch();
tom7ef8ff92014-09-17 13:08:06 -0700414 }
415 }
416
417 @Override
418 public boolean handleRoleError(OFErrorMsg error) {
419 try {
420 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
421 } catch (SwitchStateException e) {
422 this.disconnectSwitch();
423 }
424 return true;
425 }
426
tom7ef8ff92014-09-17 13:08:06 -0700427 @Override
428 public final void setAgent(OpenFlowAgent ag) {
429 if (this.agent == null) {
430 this.agent = ag;
431 }
432 }
433
434 @Override
435 public final void setRoleHandler(RoleHandler roleHandler) {
436 if (this.roleMan == null) {
437 this.roleMan = roleHandler;
438 }
439 }
440
441 @Override
442 public void setSwitchDescription(OFDescStatsReply d) {
443 this.desc = d;
444 }
445
446 @Override
447 public int getNextTransactionId() {
448 return this.xidCounter.getAndIncrement();
449 }
450
451 @Override
452 public List<OFPortDesc> getPorts() {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700453 return this.ports.stream()
Brian O'Connore755caa2015-11-16 16:43:09 -0800454 .flatMap(portReply -> portReply.getEntries().stream())
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700455 .collect(Collectors.toList());
tom7ef8ff92014-09-17 13:08:06 -0700456 }
457
458 @Override
Ray Milkeyd3edd032015-01-16 11:38:58 -0800459 public String manufacturerDescription() {
tom7ef8ff92014-09-17 13:08:06 -0700460 return this.desc.getMfrDesc();
461 }
462
tom7ef8ff92014-09-17 13:08:06 -0700463 @Override
464 public String datapathDescription() {
465 return this.desc.getDpDesc();
466 }
467
tom7ef8ff92014-09-17 13:08:06 -0700468 @Override
469 public String hardwareDescription() {
470 return this.desc.getHwDesc();
471 }
472
473 @Override
474 public String softwareDescription() {
475 return this.desc.getSwDesc();
476 }
477
478 @Override
479 public String serialNumber() {
480 return this.desc.getSerialNum();
481 }
482
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700483 @Override
Marc De Leenheerb9311372015-07-09 11:36:49 -0700484 public Device.Type deviceType() {
485 return Device.Type.SWITCH;
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700486 }
487
alshabibb452fd72015-04-22 20:46:20 -0700488 @Override
489 public String toString() {
490 return this.getClass().getName() + " [" + ((channel != null)
491 ? channel.getRemoteAddress() : "?")
492 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
493 }
tom7ef8ff92014-09-17 13:08:06 -0700494}