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 org.jboss.netty.channel.Channel;
 
  20 import org.onlab.packet.IpAddress;
 
  21 import org.onosproject.net.Device;
 
  22 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 
  23 import org.onosproject.openflow.controller.Dpid;
 
  24 import org.onosproject.openflow.controller.RoleState;
 
  25 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 
  26 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 
  27 import org.projectfloodlight.openflow.protocol.OFExperimenter;
 
  28 import org.projectfloodlight.openflow.protocol.OFFactories;
 
  29 import org.projectfloodlight.openflow.protocol.OFFactory;
 
  30 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 
  31 import org.projectfloodlight.openflow.protocol.OFMessage;
 
  32 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
 
  33 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 
  34 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 
  35 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 
  36 import org.projectfloodlight.openflow.protocol.OFRoleReply;
 
  37 import org.projectfloodlight.openflow.protocol.OFRoleRequest;
 
  38 import org.projectfloodlight.openflow.protocol.OFVersion;
 
  39 import org.slf4j.Logger;
 
  40 import org.slf4j.LoggerFactory;
 
  42 import java.io.IOException;
 
  43 import java.net.InetSocketAddress;
 
  44 import java.net.SocketAddress;
 
  45 import java.util.ArrayList;
 
  46 import java.util.Collections;
 
  47 import java.util.List;
 
  48 import java.util.concurrent.atomic.AtomicInteger;
 
  49 import java.util.stream.Collectors;
 
  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.
 
  55 public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
 
  56         implements OpenFlowSwitchDriver {
 
  58     protected final Logger log = LoggerFactory.getLogger(getClass());
 
  60     private Channel channel;
 
  61     protected String channelId;
 
  63     private boolean connected;
 
  64     protected boolean startDriverHandshakeCalled = false;
 
  66     private OpenFlowAgent agent;
 
  67     private final AtomicInteger xidCounter = new AtomicInteger(0);
 
  69     private OFVersion ofVersion;
 
  71     protected List<OFPortDescStatsReply> ports = new ArrayList<>();
 
  73     protected boolean tableFull;
 
  75     private RoleHandler roleMan;
 
  77     protected RoleState role;
 
  79     protected OFFeaturesReply features;
 
  80     protected OFDescStatsReply desc;
 
  82     List<OFMessage> messagesPendingMastership;
 
  85     public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
 
  91     //************************
 
  93     //************************
 
  96     public final void disconnectSwitch() {
 
 101     public void sendMsg(OFMessage msg) {
 
 102         this.sendMsg(Collections.singletonList(msg));
 
 106     public final void sendMsg(List<OFMessage> msgs) {
 
 107         if (role == RoleState.MASTER && channel.isConnected()) {
 
 109         } else if (messagesPendingMastership != null) {
 
 110             messagesPendingMastership.addAll(msgs);
 
 111             log.debug("Enqueue message for switch {}. queue size after is {}",
 
 112                       dpid, messagesPendingMastership.size());
 
 114             log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
 
 115                      dpid, role, channel.isConnected(), msgs);
 
 120     public final void sendRoleRequest(OFMessage msg) {
 
 121         if (msg instanceof OFRoleRequest ||
 
 122                 msg instanceof OFNiciraControllerRoleRequest) {
 
 123             channel.write(Collections.singletonList(msg));
 
 126         throw new IllegalArgumentException("Someone is trying to send " +
 
 127                                                    "a non role request message");
 
 131     public final void sendHandshakeMessage(OFMessage message) {
 
 132         if (!this.isDriverHandshakeComplete()) {
 
 133             channel.write(Collections.singletonList(message));
 
 138     public final boolean isConnected() {
 
 139         return this.connected;
 
 143     public final void setConnected(boolean connected) {
 
 144         this.connected = connected;
 
 148     public final void setChannel(Channel channel) {
 
 149         this.channel = channel;
 
 150         final SocketAddress address = channel.getRemoteAddress();
 
 151         if (address instanceof InetSocketAddress) {
 
 152             final InetSocketAddress inetAddress = (InetSocketAddress) address;
 
 153             final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
 
 154             if (ipAddress.isIp4()) {
 
 155                 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
 
 157                 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
 
 163     public String channelId() {
 
 167     //************************
 
 168     // Switch features related
 
 169     //************************
 
 172     public final long getId() {
 
 173         return this.dpid.value();
 
 177     public final String getStringId() {
 
 178         return this.dpid.toString();
 
 182     public final void setOFVersion(OFVersion ofV) {
 
 183         this.ofVersion = ofV;
 
 187     public void setTableFull(boolean full) {
 
 188         this.tableFull = full;
 
 192     public void setFeaturesReply(OFFeaturesReply featuresReply) {
 
 193         this.features = featuresReply;
 
 197     public abstract Boolean supportNxRole();
 
 199     //************************
 
 201     //************************
 
 203      * Handle the message coming from the dataplane.
 
 205      * @param m the actual message
 
 208     public final void handleMessage(OFMessage m) {
 
 209         if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
 
 210             this.agent.processMessage(dpid, m);
 
 215     public RoleState getRole() {
 
 220     public final boolean connectSwitch() {
 
 221         return this.agent.addConnectedSwitch(dpid, this);
 
 225     public final boolean activateMasterSwitch() {
 
 226         return this.agent.addActivatedMasterSwitch(dpid, this);
 
 230     public final boolean activateEqualSwitch() {
 
 231         return this.agent.addActivatedEqualSwitch(dpid, this);
 
 235     public final void transitionToEqualSwitch() {
 
 236         this.agent.transitionToEqualSwitch(dpid);
 
 240     public final void transitionToMasterSwitch() {
 
 241         this.agent.transitionToMasterSwitch(dpid);
 
 242         if (messagesPendingMastership != null) {
 
 243             this.sendMsg(messagesPendingMastership);
 
 244             log.debug("Sending {} pending messages to switch {}",
 
 245                      messagesPendingMastership.size(), dpid);
 
 246             messagesPendingMastership = null;
 
 251     public final void removeConnectedSwitch() {
 
 252         this.agent.removeConnectedSwitch(dpid);
 
 256     public OFFactory factory() {
 
 257         return OFFactories.getFactory(ofVersion);
 
 261     public void setPortDescReply(OFPortDescStatsReply portDescReply) {
 
 262         this.ports.add(portDescReply);
 
 266     public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
 
 267         this.ports.addAll(portDescReplies);
 
 271     public void returnRoleReply(RoleState requested, RoleState response) {
 
 272         this.agent.returnRoleReply(dpid, requested, response);
 
 276     public abstract void startDriverHandshake();
 
 279     public abstract boolean isDriverHandshakeComplete();
 
 282     public abstract void processDriverHandshakeMessage(OFMessage m);
 
 288     public void setRole(RoleState role) {
 
 290             if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
 
 291                 log.debug("Sending role {} to switch {}", role, getStringId());
 
 292                 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
 
 295                     if (messagesPendingMastership == null) {
 
 296                         log.debug("Initializing new queue for switch {}", dpid);
 
 297                         messagesPendingMastership = new ArrayList<>();
 
 303         } catch (IOException e) {
 
 304             log.error("Unable to write to switch {}.", this.dpid);
 
 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.",
 
 314             this.setRole(RoleState.MASTER);
 
 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) {
 
 326                 this.role = rri.getRole();
 
 327                 this.transitionToMasterSwitch();
 
 328             } else if (rri.getRole() == RoleState.EQUAL ||
 
 329                     rri.getRole() == RoleState.SLAVE) {
 
 330                 this.transitionToEqualSwitch();
 
 333             log.warn("Failed to set role for {}", this.getStringId());
 
 338     public void handleNiciraRole(OFMessage m) throws SwitchStateException {
 
 339         RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
 
 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);
 
 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) {
 
 352                 this.transitionToMasterSwitch();
 
 353             } else if (r == RoleState.EQUAL ||
 
 354                     r == RoleState.SLAVE) {
 
 355                 this.transitionToEqualSwitch();
 
 358             this.disconnectSwitch();
 
 363     public boolean handleRoleError(OFErrorMsg error) {
 
 365             return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
 
 366         } catch (SwitchStateException e) {
 
 367             this.disconnectSwitch();
 
 375     public final void setAgent(OpenFlowAgent ag) {
 
 376         if (this.agent == null) {
 
 382     public final void setRoleHandler(RoleHandler roleHandler) {
 
 383         if (this.roleMan == null) {
 
 384             this.roleMan = roleHandler;
 
 389     public void setSwitchDescription(OFDescStatsReply d) {
 
 394     public int getNextTransactionId() {
 
 395         return this.xidCounter.getAndIncrement();
 
 399     public List<OFPortDesc> getPorts() {
 
 400         return this.ports.stream()
 
 401                   .flatMap((portReply) -> (portReply.getEntries().stream()))
 
 402                   .collect(Collectors.toList());
 
 403         //return Collections.unmodifiableList(ports.getEntries());
 
 407     public String manufacturerDescription() {
 
 408         return this.desc.getMfrDesc();
 
 413     public String datapathDescription() {
 
 414         return this.desc.getDpDesc();
 
 419     public String hardwareDescription() {
 
 420         return this.desc.getHwDesc();
 
 424     public String softwareDescription() {
 
 425         return this.desc.getSwDesc();
 
 429     public String serialNumber() {
 
 430         return this.desc.getSerialNum();
 
 435     public Device.Type deviceType() {
 
 436         return Device.Type.SWITCH;
 
 441     public String toString() {
 
 442         return this.getClass().getName() + " [" + ((channel != null)
 
 443                 ? channel.getRemoteAddress() : "?")
 
 444                 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";