2 * Copyright 2014-2015 Open Networking Laboratory
4 * 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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onosproject.openflow.controller.driver;
19 import com.google.common.collect.Lists;
20 import org.jboss.netty.channel.Channel;
21 import org.onlab.packet.IpAddress;
22 import org.onosproject.net.Device;
23 import org.onosproject.net.driver.AbstractHandlerBehaviour;
24 import org.onosproject.openflow.controller.Dpid;
25 import org.onosproject.openflow.controller.RoleState;
26 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
27 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
28 import org.projectfloodlight.openflow.protocol.OFExperimenter;
29 import org.projectfloodlight.openflow.protocol.OFFactories;
30 import org.projectfloodlight.openflow.protocol.OFFactory;
31 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
32 import org.projectfloodlight.openflow.protocol.OFMessage;
33 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
34 import org.projectfloodlight.openflow.protocol.OFPortDesc;
35 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
36 import org.projectfloodlight.openflow.protocol.OFPortStatus;
37 import org.projectfloodlight.openflow.protocol.OFRoleReply;
38 import org.projectfloodlight.openflow.protocol.OFRoleRequest;
39 import org.projectfloodlight.openflow.protocol.OFVersion;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import java.io.IOException;
44 import java.net.InetSocketAddress;
45 import java.net.SocketAddress;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.concurrent.atomic.AtomicInteger;
50 import java.util.concurrent.atomic.AtomicReference;
51 import java.util.stream.Collectors;
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.
57 public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
58 implements OpenFlowSwitchDriver {
60 protected final Logger log = LoggerFactory.getLogger(getClass());
62 private Channel channel;
63 protected String channelId;
65 private boolean connected;
66 protected boolean startDriverHandshakeCalled = false;
68 private OpenFlowAgent agent;
69 private final AtomicInteger xidCounter = new AtomicInteger(0);
71 private OFVersion ofVersion;
73 protected List<OFPortDescStatsReply> ports = new ArrayList<>();
75 protected boolean tableFull;
77 private RoleHandler roleMan;
79 // TODO this is accessed from multiple threads, but volatile may have performance implications
80 protected volatile RoleState role;
82 protected OFFeaturesReply features;
83 protected OFDescStatsReply desc;
85 private final AtomicReference<List<OFMessage>> messagesPendingMastership
86 = new AtomicReference<>();
89 public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
95 //************************
97 //************************
100 public final void disconnectSwitch() {
101 this.channel.close();
105 public void sendMsg(OFMessage msg) {
106 this.sendMsg(Collections.singletonList(msg));
110 public final void sendMsg(List<OFMessage> msgs) {
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
119 if (role == RoleState.MASTER) {
120 // fast path send when we are master
122 sendMsgsOnChannel(msgs);
125 // check to see if mastership transition is in progress
126 synchronized (messagesPendingMastership) {
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.
132 if (role == RoleState.MASTER) {
133 // transition to MASTER complete, send messages
134 sendMsgsOnChannel(msgs);
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());
145 // not transitioning to MASTER
146 log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
147 dpid, role, channel.isConnected(), msgs);
152 private void sendMsgsOnChannel(List<OFMessage> msgs) {
153 if (channel.isConnected()) {
156 log.warn("Dropping messages for switch {} because channel is not connected: {}",
162 public final void sendRoleRequest(OFMessage msg) {
163 if (msg instanceof OFRoleRequest ||
164 msg instanceof OFNiciraControllerRoleRequest) {
165 sendMsgsOnChannel(Collections.singletonList(msg));
168 throw new IllegalArgumentException("Someone is trying to send " +
169 "a non role request message");
173 public final void sendHandshakeMessage(OFMessage message) {
174 if (!this.isDriverHandshakeComplete()) {
175 sendMsgsOnChannel(Collections.singletonList(message));
180 public final boolean isConnected() {
181 return this.connected;
185 public final void setConnected(boolean connected) {
186 this.connected = connected;
190 public final void setChannel(Channel channel) {
191 this.channel = channel;
192 final SocketAddress address = channel.getRemoteAddress();
193 if (address instanceof InetSocketAddress) {
194 final InetSocketAddress inetAddress = (InetSocketAddress) address;
195 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
196 if (ipAddress.isIp4()) {
197 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
199 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
205 public String channelId() {
209 //************************
210 // Switch features related
211 //************************
214 public final long getId() {
215 return this.dpid.value();
219 public final String getStringId() {
220 return this.dpid.toString();
224 public final void setOFVersion(OFVersion ofV) {
225 this.ofVersion = ofV;
229 public void setTableFull(boolean full) {
230 this.tableFull = full;
234 public void setFeaturesReply(OFFeaturesReply featuresReply) {
235 this.features = featuresReply;
239 public abstract Boolean supportNxRole();
241 //************************
243 //************************
245 * Handle the message coming from the dataplane.
247 * @param m the actual message
250 public final void handleMessage(OFMessage m) {
251 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
252 this.agent.processMessage(dpid, m);
257 public RoleState getRole() {
262 public final boolean connectSwitch() {
263 return this.agent.addConnectedSwitch(dpid, this);
267 public final boolean activateMasterSwitch() {
268 return this.agent.addActivatedMasterSwitch(dpid, this);
272 public final boolean activateEqualSwitch() {
273 return this.agent.addActivatedEqualSwitch(dpid, this);
277 public final void transitionToEqualSwitch() {
278 this.agent.transitionToEqualSwitch(dpid);
282 public final void transitionToMasterSwitch() {
283 this.agent.transitionToMasterSwitch(dpid);
284 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);
292 // perform role transition after clearing messages queue
293 this.role = RoleState.MASTER;
298 public final void removeConnectedSwitch() {
299 this.agent.removeConnectedSwitch(dpid);
303 public OFFactory factory() {
304 return OFFactories.getFactory(ofVersion);
308 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
309 this.ports.add(portDescReply);
313 public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
314 this.ports.addAll(portDescReplies);
318 public void returnRoleReply(RoleState requested, RoleState response) {
319 this.agent.returnRoleReply(dpid, requested, response);
323 public abstract void startDriverHandshake();
326 public abstract boolean isDriverHandshakeComplete();
329 public abstract void processDriverHandshakeMessage(OFMessage m);
335 public void setRole(RoleState role) {
337 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
338 // perform role transition to SLAVE/EQUAL before sending role request
341 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
342 log.debug("Sending role {} to switch {}", role, getStringId());
343 if (role == RoleState.MASTER) {
344 synchronized (messagesPendingMastership) {
345 if (messagesPendingMastership.get() == null) {
346 log.debug("Initializing new message queue for switch {}", dpid);
348 The presence of messagesPendingMastership indicates that
349 a switch is currently transitioning to MASTER, but
350 is still awaiting role reply from switch.
352 messagesPendingMastership.set(Lists.newArrayList());
356 } else if (role == RoleState.MASTER) {
357 // role request not support; transition switch to MASTER
360 } catch (IOException e) {
361 log.error("Unable to write to switch {}.", this.dpid);
366 public void reassertRole() {
367 // TODO should messages be sent directly or queue during reassertion?
368 if (this.getRole() == RoleState.MASTER) {
369 log.warn("Received permission error from switch {} while " +
370 "being master. Reasserting master role.",
372 this.setRole(RoleState.MASTER);
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 ||
384 rri.getRole() == RoleState.SLAVE) {
385 this.transitionToEqualSwitch();
388 log.warn("Failed to set role for {}", this.getStringId());
393 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
394 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
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);
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 ||
408 r == RoleState.SLAVE) {
409 this.transitionToEqualSwitch();
412 this.disconnectSwitch();
417 public boolean handleRoleError(OFErrorMsg error) {
419 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
420 } catch (SwitchStateException e) {
421 this.disconnectSwitch();
427 public final void setAgent(OpenFlowAgent ag) {
428 if (this.agent == null) {
434 public final void setRoleHandler(RoleHandler roleHandler) {
435 if (this.roleMan == null) {
436 this.roleMan = roleHandler;
441 public void setSwitchDescription(OFDescStatsReply d) {
446 public int getNextTransactionId() {
447 return this.xidCounter.getAndIncrement();
451 public List<OFPortDesc> getPorts() {
452 return this.ports.stream()
453 .flatMap(portReply -> portReply.getEntries().stream())
454 .collect(Collectors.toList());
458 public String manufacturerDescription() {
459 return this.desc.getMfrDesc();
463 public String datapathDescription() {
464 return this.desc.getDpDesc();
468 public String hardwareDescription() {
469 return this.desc.getHwDesc();
473 public String softwareDescription() {
474 return this.desc.getSwDesc();
478 public String serialNumber() {
479 return this.desc.getSerialNum();
483 public Device.Type deviceType() {
484 return Device.Type.SWITCH;
488 public String toString() {
489 return this.getClass().getName() + " [" + ((channel != null)
490 ? channel.getRemoteAddress() : "?")
491 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";