--- /dev/null
+.idea/\r
+target/\r
+*.class\r
+*.iml\r
+**/target\r
+**/bin\r
+dist\r
+**/logs\r
+products\r
+repository\r
+workspace\r
+*~\r
+target\r
+.classpath\r
+.project\r
+.settings\r
+MANIFEST.MF\r
+\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>commons.openflowplugin</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <repositories>
+ <!-- OpenDayLight Released artifact -->
+ <repository>
+ <id>opendaylight-release</id>
+ <name>opendaylight-release</name>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.release/</url>
+ </repository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <repository>
+ <id>opendaylight-snapshot</id>
+ <name>opendaylight-snapshot</name>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/</url>
+ </repository>
+ </repositories>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>central2</id>
+ <name>central2</name>
+ <url>http://nexus.opendaylight.org/content/repositories/central2/</url>
+ </pluginRepository>
+ </pluginRepositories>
+</project>
--- /dev/null
+<?xml version="1.0"?>\r
+<!DOCTYPE module PUBLIC\r
+ "-//Puppy Crawl//DTD Check Configuration 1.2//EN"\r
+ "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">\r
+\r
+<module name="Checker">\r
+ <module name="FileTabCharacter">\r
+ <property name="eachLine" value="true"/>\r
+ </module>\r
+\r
+ <module name="RegexpSingleline">\r
+ <!-- \s matches whitespace character, $ matches end of line. -->\r
+ <property name="format" value="\s+$"/>\r
+ <property name="message" value="Line has trailing spaces."/>\r
+ </module>\r
+\r
+</module>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>commons.openflowplugin</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <relativePath>../commons</relativePath>
+ </parent>
+
+ <artifactId>openflowplugin</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.sal.action,
+ org.opendaylight.controller.sal.discovery,
+ org.opendaylight.controller.sal.topology,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.flowprogrammer,
+ org.opendaylight.controller.sal.reader,
+ org.opendaylight.controller.sal.inventory,
+ org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.connection,
+ org.apache.commons.lang3.builder,
+ org.apache.commons.lang3.tuple,
+ org.apache.felix.dm,
+ org.slf4j,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ javax.net.ssl
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.openflowplugin.openflow.internal
+ </Export-Package>
+ <Embed-Dependency>
+ org.openflow.openflowj
+ </Embed-Dependency>
+ <Embed-Transitive>
+ false
+ </Embed-Transitive>
+ <Bundle-Activator>
+ org.opendaylight.openflowplugin.openflow.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.connection</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>org.openflow.openflowj</artifactId>
+ <version>1.0.2-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/**
+ * @file IDataPacketListen.java
+ *
+ * @brief Interface to dispatch locally in the protocol plugin the
+ * data packets, intended especially for Discovery, main difference
+ * here with the analogous of SAL is that this is Global
+ * inherently
+ *
+ */
+package org.opendaylight.openflowplugin.openflow;
+
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.packet.PacketResult;
+
+/**
+ * Interface to dispatch locally in the protocol plugin the
+ * data packets, intended especially for Discovery, main difference
+ * here with the analogous of SAL is that this is Global
+ * inherently.
+ */
+public interface IDataPacketListen {
+ /**
+ * Dispatch received data packet
+ *
+ * @param inPkt
+ * The incoming raw packet
+ * @return Possible results for Data packet processing handler
+ */
+ public PacketResult receiveDataPacket(RawPacket inPkt);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+/**
+ * @file IDataPacketMux.java
+ *
+ * @brief Simple wrapped interface for the IPluginInDataPacketService
+ * which will be only exported by DataPacketServices mux/demux
+ * component and will be only accessible by the openflow protocol
+ * plugin
+ */
+
+import org.opendaylight.controller.sal.packet.IPluginInDataPacketService;
+
+/**
+ * Simple wrapped interface for the IPluginInDataPacketService
+ * which will be only exported by DataPacketServices mux/demux
+ * component and will be only accessible by the openflow protocol
+ * plugin
+ */
+public interface IDataPacketMux extends IPluginInDataPacketService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import org.opendaylight.controller.sal.discovery.IDiscoveryService;
+
+/**
+ * The interface provides method to notify the local plugin listener when an
+ * edge is discovered or removed.
+ */
+public interface IDiscoveryListener extends IDiscoveryService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+
+/**
+ * Interface which defines the methods exposed by the Flow Programmer Notifier.
+ * Their implementation relays the asynchronous messages received from the
+ * network nodes to the the SAL Flow Programmer Notifier Service on the proper
+ * container.
+ */
+public interface IFlowProgrammerNotifier extends
+ IPluginOutFlowProgrammerService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+
+/**
+ * The Interface provides inventory service to the local plugin modules
+ */
+public interface IInventoryProvider extends IPluginInInventoryService {
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+/**
+ * Wrapper of Interface that provides inventory updates locally in the protocol
+ * plugin.
+ */
+public interface IInventoryShimExternalListener extends
+ IInventoryShimInternalListener {
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+
+/**
+ * The Interface provides inventory updates to inventory listeners within the
+ * protocol plugin
+ */
+public interface IInventoryShimInternalListener {
+ /**
+ * Updates node and its properties
+ *
+ * @param node {@link org.opendaylight.controller.sal.core.Node} being updated
+ * @param type {@link org.opendaylight.controller.sal.core.UpdateType}
+ * @param props set of {@link org.opendaylight.controller.sal.core.Property} such as
+ * {@link org.opendaylight.controller.sal.core.Description} and/or
+ * {@link org.opendaylight.controller.sal.core.Tier} etc.
+ */
+ public void updateNode(Node node, UpdateType type, Set<Property> props);
+
+ /**
+ * Updates node connector and its properties
+ *
+ * @param nodeConnector {@link org.opendaylight.controller.sal.core.NodeConnector} being updated
+ * @param type {@link org.opendaylight.controller.sal.core.UpdateType}
+ * @param props set of {@link org.opendaylight.controller.sal.core.Property} such as
+ * {@link org.opendaylight.controller.sal.core.Description} and/or
+ * {@link org.opendaylight.controller.sal.core.State} etc.
+ */
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props);
+
+}
--- /dev/null
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.List;
+
+import org.openflow.protocol.statistics.OFStatistics;
+
+/**
+ * Interface defines the api which gets called when the information
+ * contained in the OF statistics reply message from a network is updated with
+ * new one.
+ */
+public interface IOFStatisticsListener {
+ public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description);
+
+ public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows);
+
+ public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports);
+
+ public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.List;
+
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+
+/**
+ * Interface to expose the openflow statistics collected on the switches
+ */
+public interface IOFStatisticsManager {
+ /**
+ * Return all the statistics for all the flows present on the specified switch
+ *
+ * @param switchId the openflow datapath id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFFlowStatistics(Long switchId);
+
+ /**
+ * Return all the statistics for all the flows present on the specified switch
+ *
+ * @param switchId the openflow datapath id
+ * @param ofMatch the openflow match to query. If null, the query is intended for all the flows
+ * @param priority Priority of the wanted flow
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFFlowStatistics(Long switchId, OFMatch ofMatch, short priority);
+
+ /**
+ * Return the description statistics for the specified switch.
+ *
+ * @param switchId the openflow datapath id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFDescStatistics(Long switchId);
+
+ /**
+ * Returns the statistics for all the ports on the specified switch
+ *
+ * @param switchId the openflow datapath id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFPortStatistics(Long switchId);
+
+ /**
+ * Returns the statistics for the specified switch port
+ *
+ * @param switchId the openflow datapath id
+ * @param portId the openflow switch port id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFPortStatistics(Long switchId, short portId);
+
+ /**
+ * Returns the number of flows installed on the switch
+ *
+ * @param switchId the openflow datapath id
+ * @return the number of flows installed on the switch
+ */
+ int getFlowsNumber(long switchId);
+
+ /**
+ * Send a statistics request message to the specified switch and returns
+ * the switch response. It blocks the caller until the response has arrived
+ * from the switch or the request has timed out
+ *
+ * @param switchId the openflow datapath id of the target switch
+ * @param statType the openflow statistics type
+ * @param target the target object. For flow statistics it is the OFMatch.
+ * For port statistics, it is the port id. If null the query
+ * will be performed for all the targets for the specified
+ * statistics type.
+ *
+ * @param timeout the timeout in milliseconds the system will wait for a response
+ * from the switch, before declaring failure
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> queryStatistics(Long switchId,
+ OFStatisticsType statType, Object target);
+
+ /**
+ * Returns the averaged transmit rate for the passed switch port
+ *
+ * @param switchId the openflow datapath id of the target switch
+ * @param portId the openflow switch port id
+ * @return the median transmit rate in bits per second
+ */
+ long getTransmitRate(Long switchId, Short port);
+
+ /**
+ * Returns the statistics for the specified switch table
+ *
+ * @param switchId the openflow datapath id
+ * @param tableId the openflow switch table id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFTableStatistics(Long switchId, Byte tableId);
+
+ /**
+ * Returns all the table statistics for the node specified
+ *
+ * @param switchId the openflow datapath id
+ * @return the list of openflow statistics
+ */
+ List<OFStatistics> getOFTableStatistics(Long switchId);
+
+}
--- /dev/null
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+
+/**
+ * The Interface provides statistics updates to ReaderFilter listeners within
+ * the protocol plugin
+ */
+public interface IReadFilterInternalListener {
+
+ /**
+ * Notifies the hardware view of all the flow installed on the specified network node
+ * @param node
+ * @return
+ */
+ public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList);
+
+ /**
+ * Notifies the hardware view of the specified network node connector
+ * @param node
+ * @return
+ */
+ public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList);
+
+ /**
+ * Notifies all the table statistics for a node
+ * @param node
+ * @return
+ */
+ public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList);
+
+ /**
+ * Notifies the hardware view of all the flow installed on the specified network node
+ * @param node
+ * @return
+ */
+ public void nodeDescriptionStatisticsUpdated(Node node, NodeDescription nodeDescription);
+
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeTable;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+
+/**
+ * Interface to serve the hardware information requests coming from SAL
+ * It is implemented by the respective OF1.0 plugin component
+ *
+ */
+public interface IReadServiceFilter {
+ /**
+ * Returns the hardware image for the specified flow
+ * on the specified network node for the passed container
+ *
+ * @param container
+ * @param node
+ * @param flow
+ * @param cached
+ * @return
+ */
+ public FlowOnNode readFlow(String container, Node node, Flow flow,
+ boolean cached);
+
+ /**
+ * Returns the hardware view of all the flow installed
+ * on the specified network node for the passed container
+ *
+ * @param container
+ * @param node
+ * @param cached
+ * @return
+ */
+ public List<FlowOnNode> readAllFlow(String container, Node node,
+ boolean cached);
+
+ /**
+ * Returns the description of the network node as provided by the node itself
+ *
+ * @param node
+ * @param cached
+ * @return
+ */
+ public NodeDescription readDescription(Node node, boolean cached);
+
+ /**
+ * Returns the hardware view of the specified network node connector
+ * for the given container
+ * @param node
+ * @return
+ */
+ public NodeConnectorStatistics readNodeConnector(String container,
+ NodeConnector nodeConnector, boolean cached);
+
+ /**
+ * Returns the hardware info for all the node connectors on the
+ * specified network node for the given container
+ *
+ * @param node
+ * @return
+ */
+ public List<NodeConnectorStatistics> readAllNodeConnector(String container,
+ Node node, boolean cached);
+
+ /**
+ * Returns the table statistics of the node as specified by the given container
+ * @param node
+ * @param cached
+ * @return
+ */
+ public NodeTableStatistics readNodeTable(String container,
+ NodeTable nodeTable, boolean cached);
+
+ /**
+ * Returns the table statistics of all the tables for the specified node
+ *
+ * @param node
+ * @return
+ */
+ public List<NodeTableStatistics> readAllNodeTable(String containerName,
+ Node node, boolean cached);
+
+ /**
+ * Returns the average transmit rate for the specified node conenctor on
+ * the given container. If the node connector does not belong to the passed
+ * container a zero value is returned
+ *
+ * @param container
+ * @param nodeConnector
+ * @return tx rate [bps]
+ */
+ public long getTransmitRate(String container, NodeConnector nodeConnector);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+/**
+ * @file IRefreshInternalProvider.java
+ *
+ * @brief Topology refresh notifications requested by application
+ * to be fetched from the plugin
+ *
+ * For example, an application that has been started late, will want to
+ * be up to date with the latest topology. Hence, it requests for a
+ * topology refresh from the plugin.
+ */
+
+/**
+ * Topology refresh requested by the application from the plugin
+ *
+ */
+
+public interface IRefreshInternalProvider {
+
+ /**
+ * @param containerName
+ */
+ public void requestRefresh(String containerName);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow;
+
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
+
+/**
+ * The Interface provides Edge updates to the topology listeners
+ */
+public interface ITopologyServiceShimListener {
+ /**
+ * Called to update on Edge in the topology graph
+ *
+ * @param topoedgeupdateList
+ * List of topoedgeupdates Each topoedgeupdate includes edge, its
+ * Properties ( BandWidth and/or Latency etc) and update type.
+ */
+ public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList);
+
+ /**
+ * Called when an Edge utilization is above the safe threshold configured on
+ * the controller
+ *
+ * @param {@link org.opendaylight.controller.sal.core.Edge}
+ */
+ public void edgeOverUtilized(Edge edge);
+
+ /**
+ * Called when the Edge utilization is back to normal, below the safety
+ * threshold level configured on the controller
+ *
+ * @param {@link org.opendaylight.controller.sal.core.Edge}
+ */
+ public void edgeUtilBackToNormal(Edge edge);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core;
+
+import java.util.Map;
+
+import org.openflow.protocol.OFType;
+
+/**
+ * This interface defines an abstraction of the Open Flow Controller that allows applications to control and manage the Open Flow switches.
+ *
+ */
+public interface IController {
+
+ /**
+ * Allows application to start receiving OF messages received from switches.
+ * @param type the type of OF message that applications want to receive
+ * @param listener: Object that implements the IMessageListener
+ */
+ public void addMessageListener(OFType type, IMessageListener listener);
+
+ /**
+ * Allows application to stop receiving OF message received from switches.
+ * @param type The type of OF message that applications want to stop receiving
+ * @param listener The object that implements the IMessageListener
+ */
+ public void removeMessageListener(OFType type, IMessageListener listener);
+
+ /**
+ * Allows application to start receiving switch state change events.
+ * @param listener The object that implements the ISwitchStateListener
+ */
+ public void addSwitchStateListener(ISwitchStateListener listener);
+
+ /**
+ * Allows application to stop receiving switch state change events.
+ * @param listener The object that implements the ISwitchStateListener
+ */
+ public void removeSwitchStateListener(ISwitchStateListener listener);
+
+ /**
+ * Returns a map containing all the OF switches that are currently connected to the Controller.
+ * @return Map of ISwitch
+ */
+ public Map<Long, ISwitch> getSwitches();
+
+ /**
+ * Returns the ISwitch of the given switchId.
+ *
+ * @param switchId The switch ID
+ * @return ISwitch if present, null otherwise
+ */
+ public ISwitch getSwitch(Long switchId);
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core;
+
+import org.openflow.protocol.OFMessage;
+
+/**
+ * Interface to be implemented by applications that want to receive OF messages.
+ *
+ */
+public interface IMessageListener {
+ /**
+ * This method is called by the Controller when a message is received from a switch.
+ * Application who is interested in receiving OF Messages needs to implement this method.
+ * @param sw The ISwitch which sent the OF message
+ * @param msg The OF message
+ */
+ public void receive(ISwitch sw, OFMessage msg);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core;
+
+import java.util.List;
+
+import org.openflow.protocol.OFMessage;
+
+/**
+ * This interface defines low level routines to read/write messages on an open
+ * socket channel. If secure communication is desired, these methods also perform
+ * encryption and decryption of the network data.
+ */
+public interface IMessageReadWrite {
+ /**
+ * Sends the OF message out over the socket channel. For secure
+ * communication, the data will be encrypted.
+ *
+ * @param msg OF message to be sent
+ * @throws Exception
+ */
+ public void asyncSend(OFMessage msg) throws Exception;
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ * @throws Exception
+ */
+ public void resumeSend() throws Exception;
+
+ /**
+ * Reads the incoming network data from the socket and retrieves the OF
+ * messages. For secure communication, the data will be decrypted first.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ public List<OFMessage> readMessages() throws Exception;
+
+ /**
+ * Proper clean up when the switch connection is closed
+ *
+ * @return
+ * @throws Exception
+ */
+ public void stop() throws Exception;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
+import org.openflow.protocol.OFStatisticsRequest;
+
+/**
+ * This interface defines an abstraction of an Open Flow Switch.
+ *
+ */
+public interface ISwitch {
+ /**
+ * Gets a unique XID.
+ *
+ * @return XID
+ */
+ public int getNextXid();
+
+ /**
+ * Returns the Switch's ID.
+ *
+ * @return the Switch's ID
+ */
+ public Long getId();
+
+ /**
+ * Returns the Switch's table numbers supported by datapath
+ *
+ * @return the tables
+ */
+ public Byte getTables();
+
+ /**
+ * Returns the Switch's bitmap of supported ofp_action_type
+ *
+ * @return the actions
+ */
+ public Integer getActions();
+
+ /**
+ * Returns the Switch's bitmap of supported ofp_capabilities
+ *
+ * @return the capabilities
+ */
+ public Integer getCapabilities();
+
+ /**
+ * Returns the Switch's buffering capacity in Number of Pkts
+ *
+ * @return the buffers
+ */
+ public Integer getBuffers();
+
+ /**
+ * Returns the Date when the switch was connected.
+ *
+ * @return Date The date when the switch was connected
+ */
+ public Date getConnectedDate();
+
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. An unique XID is generated automatically and
+ * inserted into the message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ public Integer asyncSend(OFMessage msg);
+
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. The specified XID is inserted into the message.
+ *
+ * @param msg
+ * The OF message to be Sent
+ * @param xid
+ * The XID to be used in the message
+ * @return The XID used
+ */
+ public Integer asyncSend(OFMessage msg, int xid);
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. An unique XID is generated automatically and inserted into the
+ * message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ public Integer asyncFastSend(OFMessage msg);
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. The specified XID is inserted into the message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ public Integer asyncFastSend(OFMessage msg, int xid);
+
+ /**
+ * Sends the OF message followed by a Barrier Request with a unique XID
+ * which is automatically generated, and waits for a result from the switch.
+ *
+ * @param msg
+ * The message to be sent
+ * @return An Object which has one of the followings instances/values:
+ * Boolean with value true to indicate the message has been
+ * successfully processed and acknowledged by the switch; Boolean
+ * with value false to indicate the message has failed to be
+ * processed by the switch within a period of time or OFError to
+ * indicate that the message has been denied by the switch which
+ * responded with OFError.
+ */
+ public Object syncSend(OFMessage msg);
+
+ /**
+ * Returns a map containing all OFPhysicalPorts of this switch.
+ *
+ * @return The Map of OFPhysicalPort
+ */
+ public Map<Short, OFPhysicalPort> getPhysicalPorts();
+
+ /**
+ * Returns a Set containing all port IDs of this switch.
+ *
+ * @return The Set of port ID
+ */
+ public Set<Short> getPorts();
+
+ /**
+ * Returns OFPhysicalPort of the specified portNumber of this switch.
+ *
+ * @param portNumber
+ * The port ID
+ * @return OFPhysicalPort for the specified PortNumber
+ */
+ public OFPhysicalPort getPhysicalPort(Short portNumber);
+
+ /**
+ * Returns the bandwidth of the specified portNumber of this switch.
+ *
+ * @param portNumber
+ * the port ID
+ * @return bandwidth
+ */
+ public Integer getPortBandwidth(Short portNumber);
+
+ /**
+ * Returns True if the port is enabled,
+ *
+ * @param portNumber
+ * @return True if the port is enabled
+ */
+ public boolean isPortEnabled(short portNumber);
+
+ /**
+ * Returns True if the port is enabled.
+ *
+ * @param port
+ * @return True if the port is enabled
+ */
+ public boolean isPortEnabled(OFPhysicalPort port);
+
+ /**
+ * Returns a list containing all enabled ports of this switch.
+ *
+ * @return: List containing all enabled ports of this switch
+ */
+ public List<OFPhysicalPort> getEnabledPorts();
+
+ /**
+ * Sends OFStatisticsRequest with a unique XID generated automatically and
+ * waits for a result from the switch.
+ *
+ * @param req
+ * the OF Statistic Request to be sent
+ * @return Object has one of the following instances/values::
+ * List<OFStatistics>, a list of statistics records received from
+ * the switch as response from the request; OFError if the switch
+ * failed handle the request or NULL if timeout has occurred while
+ * waiting for the response.
+ */
+ public Object getStatistics(OFStatisticsRequest req);
+
+ /**
+ * Returns true if the switch has reached the operational state (has sent
+ * FEATURE_REPLY to the controller).
+ *
+ * @return true if the switch is operational
+ */
+ public boolean isOperational();
+
+ /**
+ * Send Barrier message synchronously. The caller will be blocked until the
+ * Barrier reply arrives.
+ */
+ Object syncSendBarrierMessage();
+
+ /**
+ * Send Barrier message asynchronously. The caller is not blocked. The
+ * Barrier message will be sent in a transmit thread which will be blocked
+ * until the Barrier reply arrives.
+ */
+ Object asyncSendBarrierMessage();
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core;
+
+/**
+ * Interface to be implemented by applications that want to receive switch state event changes.
+ *
+ */
+public interface ISwitchStateListener {
+ /**
+ * This method is invoked by Controller when a switch has been connected to the Controller.
+ * Application who wants to receive this event needs to implement this method.
+ * @param sw The switch that has just connected.
+ */
+ public void switchAdded(ISwitch sw);
+
+ /**
+ * This method is invoked by Controller when a switch has been disconnected from the Controller.
+ * Application who wants to receive this event needs to implement this method.
+ * @param sw The switch that has just disconnected.
+ */
+ public void switchDeleted(ISwitch sw);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
+import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageListener;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.opendaylight.openflowplugin.openflow.core.ISwitchStateListener;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
+import org.openflow.util.HexString;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Controller implements IController, CommandProvider, IPluginInConnectionService {
+ private static final Logger logger = LoggerFactory
+ .getLogger(Controller.class);
+ private ControllerIO controllerIO;
+ private Thread switchEventThread;
+ private ConcurrentHashMap<Long, ISwitch> switches;
+ private BlockingQueue<SwitchEvent> switchEvents;
+ // only 1 message listener per OFType
+ private ConcurrentMap<OFType, IMessageListener> messageListeners;
+ // only 1 switch state listener
+ private ISwitchStateListener switchStateListener;
+ private AtomicInteger switchInstanceNumber;
+ private int MAXQUEUESIZE = 50000;
+
+ /*
+ * this thread monitors the switchEvents queue for new incoming events from
+ * switch
+ */
+ private class EventHandler implements Runnable {
+ @Override
+ public void run() {
+
+ while (true) {
+ try {
+ SwitchEvent ev = switchEvents.take();
+ SwitchEvent.SwitchEventType eType = ev.getEventType();
+ ISwitch sw = ev.getSwitch();
+ switch (eType) {
+ case SWITCH_ADD:
+ Long sid = sw.getId();
+ ISwitch existingSwitch = switches.get(sid);
+ if (existingSwitch != null) {
+ logger.info("Replacing existing {} with New {}",
+ existingSwitch, sw);
+ disconnectSwitch(existingSwitch);
+ }
+ switches.put(sid, sw);
+ notifySwitchAdded(sw);
+ break;
+ case SWITCH_DELETE:
+ disconnectSwitch(sw);
+ break;
+ case SWITCH_ERROR:
+ disconnectSwitch(sw);
+ break;
+ case SWITCH_MESSAGE:
+ OFMessage msg = ev.getMsg();
+ if (msg != null) {
+ IMessageListener listener = messageListeners
+ .get(msg.getType());
+ if (listener != null) {
+ listener.receive(sw, msg);
+ }
+ }
+ break;
+ default:
+ logger.error("Unknown switch event {}", eType.ordinal());
+ }
+ } catch (InterruptedException e) {
+ switchEvents.clear();
+ return;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ public void init() {
+ logger.debug("Initializing!");
+ this.switches = new ConcurrentHashMap<Long, ISwitch>();
+ this.switchEvents = new LinkedBlockingQueue<SwitchEvent>(MAXQUEUESIZE);
+ this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
+ this.switchStateListener = null;
+ this.switchInstanceNumber = new AtomicInteger(0);
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ public void start() {
+ logger.debug("Starting!");
+ /*
+ * start a thread to handle event coming from the switch
+ */
+ switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
+ switchEventThread.start();
+
+ // spawn a thread to start to listen on the open flow port
+ controllerIO = new ControllerIO(this);
+ try {
+ controllerIO.start();
+ } catch (IOException ex) {
+ logger.error("Caught exception while starting:", ex);
+ }
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ public void stop() {
+ for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
+ .hasNext();) {
+ Entry<Long, ISwitch> entry = it.next();
+ ((SwitchHandler) entry.getValue()).stop();
+ it.remove();
+ }
+ switchEventThread.interrupt();
+ try {
+ controllerIO.shutDown();
+ } catch (IOException ex) {
+ logger.error("Caught exception while stopping:", ex);
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ public void destroy() {
+ }
+
+ @Override
+ public void addMessageListener(OFType type, IMessageListener listener) {
+ IMessageListener currentListener = this.messageListeners.get(type);
+ if (currentListener != null) {
+ logger.warn("{} is already listened by {}", type,
+ currentListener);
+ }
+ this.messageListeners.put(type, listener);
+ logger.debug("{} is now listened by {}", type, listener);
+ }
+
+ @Override
+ public void removeMessageListener(OFType type, IMessageListener listener) {
+ IMessageListener currentListener = this.messageListeners.get(type);
+ if ((currentListener != null) && (currentListener == listener)) {
+ logger.debug("{} listener {} is Removed", type, listener);
+ this.messageListeners.remove(type);
+ }
+ }
+
+ @Override
+ public void addSwitchStateListener(ISwitchStateListener listener) {
+ if (this.switchStateListener != null) {
+ logger.warn("Switch events are already listened by {}",
+ this.switchStateListener);
+ }
+ this.switchStateListener = listener;
+ logger.debug("Switch events are now listened by {}", listener);
+ }
+
+ @Override
+ public void removeSwitchStateListener(ISwitchStateListener listener) {
+ if ((this.switchStateListener != null)
+ && (this.switchStateListener == listener)) {
+ logger.debug("SwitchStateListener {} is Removed", listener);
+ this.switchStateListener = null;
+ }
+ }
+
+ public void handleNewConnection(Selector selector,
+ SelectionKey serverSelectionKey) {
+ ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
+ .channel();
+ SocketChannel sc = null;
+ try {
+ sc = ssc.accept();
+ // create new switch
+ int i = this.switchInstanceNumber.addAndGet(1);
+ String instanceName = "SwitchHandler-" + i;
+ SwitchHandler switchHandler = new SwitchHandler(this, sc, instanceName);
+ switchHandler.start();
+ if (sc.isConnected()) {
+ logger.info("Switch:{} is connected to the Controller",
+ sc.socket().getRemoteSocketAddress()
+ .toString().split("/")[1]);
+ }
+
+ } catch (IOException e) {
+ return;
+ }
+ }
+
+ private void disconnectSwitch(ISwitch sw) {
+ if (((SwitchHandler) sw).isOperational()) {
+ Long sid = sw.getId();
+ if (this.switches.remove(sid, sw)) {
+ logger.warn("{} is Disconnected", sw);
+ notifySwitchDeleted(sw);
+ }
+ }
+ ((SwitchHandler) sw).stop();
+ sw = null;
+ }
+
+ private void notifySwitchAdded(ISwitch sw) {
+ if (switchStateListener != null) {
+ switchStateListener.switchAdded(sw);
+ }
+ }
+
+ private void notifySwitchDeleted(ISwitch sw) {
+ if (switchStateListener != null) {
+ switchStateListener.switchDeleted(sw);
+ }
+ }
+
+ private synchronized void addSwitchEvent(SwitchEvent event) {
+ try {
+ this.switchEvents.put(event);
+ } catch (InterruptedException e) {
+ logger.debug("SwitchEvent caught Interrupt Exception");
+ }
+ }
+
+ public void takeSwitchEventAdd(ISwitch sw) {
+ SwitchEvent ev = new SwitchEvent(
+ SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
+ addSwitchEvent(ev);
+ }
+
+ public void takeSwitchEventDelete(ISwitch sw) {
+ SwitchEvent ev = new SwitchEvent(
+ SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
+ addSwitchEvent(ev);
+ }
+
+ public void takeSwitchEventError(ISwitch sw) {
+ SwitchEvent ev = new SwitchEvent(
+ SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
+ addSwitchEvent(ev);
+ }
+
+ public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
+ if (messageListeners.get(msg.getType()) != null) {
+ SwitchEvent ev = new SwitchEvent(
+ SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
+ addSwitchEvent(ev);
+ }
+ }
+
+ @Override
+ public Map<Long, ISwitch> getSwitches() {
+ return this.switches;
+ }
+
+ @Override
+ public ISwitch getSwitch(Long switchId) {
+ return this.switches.get(switchId);
+ }
+
+ public void _controllerShowSwitches(CommandInterpreter ci) {
+ Set<Long> sids = switches.keySet();
+ StringBuffer s = new StringBuffer();
+ int size = sids.size();
+ if (size == 0) {
+ ci.print("switches: empty");
+ return;
+ }
+ Iterator<Long> iter = sids.iterator();
+ s.append("Total: " + size + " switches\n");
+ while (iter.hasNext()) {
+ Long sid = iter.next();
+ Date date = switches.get(sid).getConnectedDate();
+ String switchInstanceName = ((SwitchHandler) switches.get(sid))
+ .getInstanceName();
+ s.append(switchInstanceName + "/" + HexString.toHexString(sid)
+ + " connected since " + date.toString() + "\n");
+ }
+ ci.print(s.toString());
+ return;
+ }
+
+ public void _controllerReset(CommandInterpreter ci) {
+ ci.print("...Disconnecting the communication to all switches...\n");
+ stop();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ } finally {
+ ci.print("...start to accept connections from switches...\n");
+ start();
+ }
+ }
+
+ public void _controllerShowConnConfig(CommandInterpreter ci) {
+ String str = System.getProperty("secureChannelEnabled");
+ if ((str != null) && (str.trim().equalsIgnoreCase("true"))) {
+ ci.print("The Controller and Switch should communicate through TLS connetion.\n");
+
+ String keyStoreFile = System.getProperty("controllerKeyStore");
+ String trustStoreFile = System.getProperty("controllerTrustStore");
+ if ((keyStoreFile == null) || keyStoreFile.trim().isEmpty()) {
+ ci.print("controllerKeyStore not specified in ./configuration/config.ini\n");
+ } else {
+ ci.print("controllerKeyStore=" + keyStoreFile + "\n");
+ }
+ if ((trustStoreFile == null) || trustStoreFile.trim().isEmpty()) {
+ ci.print("controllerTrustStore not specified in ./configuration/config.ini\n");
+ } else {
+ ci.print("controllerTrustStore=" + trustStoreFile + "\n");
+ }
+ } else {
+ ci.print("The Controller and Switch should communicate through TCP connetion.\n");
+ }
+ }
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+ .getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this,
+ null);
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---Open Flow Controller---\n");
+ help.append("\t controllerShowSwitches\n");
+ help.append("\t controllerReset\n");
+ help.append("\t controllerShowConnConfig\n");
+ return help.toString();
+ }
+
+ @Override
+ public Status disconnect(Node node) {
+ ISwitch sw = getSwitch((Long) node.getID());
+ if (sw != null) disconnectSwitch(sw);
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ @Override
+ public Node connect(String connectionIdentifier, Map<ConnectionConstants, String> params) {
+ return null;
+ }
+
+ /**
+ * View Change notification
+ */
+ public void notifyClusterViewChanged() {
+ for (ISwitch sw : switches.values()) {
+ notifySwitchAdded(sw);
+ }
+ }
+
+ /**
+ * Node Disconnected from the node's master controller.
+ */
+ @Override
+ public void notifyNodeDisconnectFromMaster(Node node) {
+ ISwitch sw = switches.get((Long)node.getID());
+ if (sw != null) notifySwitchAdded(sw);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.Iterator;
+
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ControllerIO {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ControllerIO.class);
+ private static Short defaultOpenFlowPort = 6633;
+ private Short openFlowPort;
+ private SelectionKey serverSelectionKey;
+ private IController listener;
+ private ServerSocketChannel serverSocket;
+ private Selector selector;
+ private boolean running;
+ private Thread controllerIOThread;
+
+ public ControllerIO(IController l) {
+ this.listener = l;
+ this.openFlowPort = defaultOpenFlowPort;
+ String portString = System.getProperty("of.listenPort");
+ if (portString != null) {
+ try {
+ openFlowPort = Short.decode(portString).shortValue();
+ } catch (NumberFormatException e) {
+ logger.warn("Invalid port:{}, use default({})", portString,
+ openFlowPort);
+ }
+ }
+ }
+
+ public void start() throws IOException {
+ this.running = true;
+ // obtain a selector
+ this.selector = SelectorProvider.provider().openSelector();
+ // create the listening socket
+ this.serverSocket = ServerSocketChannel.open();
+ this.serverSocket.configureBlocking(false);
+ this.serverSocket.socket().bind(
+ new java.net.InetSocketAddress(openFlowPort));
+ this.serverSocket.socket().setReuseAddress(true);
+ // register this socket for accepting incoming connections
+ this.serverSelectionKey = this.serverSocket.register(selector,
+ SelectionKey.OP_ACCEPT);
+ controllerIOThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (running) {
+ try {
+ // wait for an incoming connection
+ selector.select(0);
+ Iterator<SelectionKey> selectedKeys = selector
+ .selectedKeys().iterator();
+ while (selectedKeys.hasNext()) {
+ SelectionKey skey = selectedKeys.next();
+ selectedKeys.remove();
+ if (skey.isValid() && skey.isAcceptable()) {
+ ((Controller) listener).handleNewConnection(
+ selector, serverSelectionKey);
+ }
+ }
+ } catch (Exception e) {
+ continue;
+ }
+ }
+ }
+ }, "ControllerI/O Thread");
+ controllerIOThread.start();
+ logger.info("Controller is now listening on port {}", openFlowPort);
+ }
+
+ public void shutDown() throws IOException {
+ this.running = false;
+ this.selector.wakeup();
+ this.serverSocket.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.List;
+
+import org.opendaylight.openflowplugin.openflow.core.IMessageReadWrite;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements methods to read/write messages over an established
+ * socket channel. The data exchange is in clear text format.
+ */
+public class MessageReadWriteService implements IMessageReadWrite {
+ private static final Logger logger = LoggerFactory
+ .getLogger(MessageReadWriteService.class);
+ private static final int bufferSize = 1024 * 1024;
+
+ private Selector selector;
+ private SelectionKey clientSelectionKey;
+ private SocketChannel socket;
+ private ByteBuffer inBuffer;
+ private ByteBuffer outBuffer;
+ private BasicFactory factory;
+
+ public MessageReadWriteService(SocketChannel socket, Selector selector)
+ throws ClosedChannelException {
+ this.socket = socket;
+ this.selector = selector;
+ this.factory = new BasicFactory();
+ this.inBuffer = ByteBuffer.allocateDirect(bufferSize);
+ this.outBuffer = ByteBuffer.allocateDirect(bufferSize);
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ);
+ }
+
+ /**
+ * Sends the OF message out over the socket channel.
+ *
+ * @param msg
+ * OF message to be sent
+ * @throws Exception
+ */
+ @Override
+ public void asyncSend(OFMessage msg) throws IOException {
+ synchronized (outBuffer) {
+ int msgLen = msg.getLengthU();
+ if (outBuffer.remaining() < msgLen) {
+ // increase the buffer size so that it can contain this message
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(outBuffer
+ .capacity() + msgLen);
+ outBuffer.flip();
+ newBuffer.put(outBuffer);
+ outBuffer = newBuffer;
+ }
+ }
+ synchronized (outBuffer) {
+ msg.writeTo(outBuffer);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ outBuffer.flip();
+ socket.write(outBuffer);
+ outBuffer.compact();
+ if (outBuffer.position() > 0) {
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_WRITE, this);
+ }
+ logger.trace("Message sent: {}", msg);
+ }
+ }
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ *
+ * @throws Exception
+ */
+ @Override
+ public void resumeSend() throws IOException {
+ synchronized (outBuffer) {
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ outBuffer.flip();
+ socket.write(outBuffer);
+ outBuffer.compact();
+ if (outBuffer.position() > 0) {
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_WRITE, this);
+ } else {
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ, this);
+ }
+ }
+ }
+
+ /**
+ * Reads the incoming network data from the socket and retrieves the OF
+ * messages.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ @Override
+ public List<OFMessage> readMessages() throws IOException {
+ if (!socket.isOpen()) {
+ return null;
+ }
+
+ List<OFMessage> msgs = null;
+ int bytesRead = -1;
+ bytesRead = socket.read(inBuffer);
+ if (bytesRead < 0) {
+ throw new AsynchronousCloseException();
+ }
+
+ inBuffer.flip();
+ msgs = factory.parseMessages(inBuffer);
+ if (inBuffer.hasRemaining()) {
+ inBuffer.compact();
+ } else {
+ inBuffer.clear();
+ }
+ return msgs;
+ }
+
+ @Override
+ public void stop() {
+ inBuffer = null;
+ outBuffer = null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.openflow.protocol.OFMessage;
+
+/**
+ * This class describes an OpenFlow message with priority
+ */
+class PriorityMessage {
+ OFMessage msg;
+ int priority;
+ final static AtomicLong seq = new AtomicLong();
+ final long seqNum;
+ boolean syncReply; // set to true if we want to be blocked until the response arrives
+
+ public PriorityMessage(OFMessage msg, int priority) {
+ this.msg = msg;
+ this.priority = priority;
+ this.seqNum = seq.getAndIncrement();
+ this.syncReply = false;
+ }
+
+ public PriorityMessage(OFMessage msg, int priority, boolean syncReply) {
+ this(msg, priority);
+ this.syncReply = syncReply;
+ }
+
+ public OFMessage getMsg() {
+ return msg;
+ }
+
+ public void setMsg(OFMessage msg) {
+ this.msg = msg;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((msg == null) ? 0 : msg.hashCode());
+ result = prime * result + priority;
+ result = prime * result + (int) (seqNum ^ (seqNum >>> 32));
+ result = prime * result + (syncReply ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PriorityMessage other = (PriorityMessage) obj;
+ if (msg == null) {
+ if (other.msg != null)
+ return false;
+ } else if (!msg.equals(other.msg))
+ return false;
+ if (priority != other.priority)
+ return false;
+ if (seqNum != other.seqNum)
+ return false;
+ if (syncReply != other.syncReply)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PriorityMessage [msg=" + msg + ", priority=" + priority
+ + ", seqNum=" + seqNum + ", syncReply=" + syncReply + "]";
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.List;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+
+import org.opendaylight.openflowplugin.openflow.core.IMessageReadWrite;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements methods to read/write messages over an established
+ * socket channel. The data exchange is encrypted/decrypted by SSLEngine.
+ */
+public class SecureMessageReadWriteService implements IMessageReadWrite {
+ private static final Logger logger = LoggerFactory
+ .getLogger(SecureMessageReadWriteService.class);
+
+ private Selector selector;
+ private SelectionKey clientSelectionKey;
+ private SocketChannel socket;
+ private BasicFactory factory;
+
+ private SSLEngine sslEngine;
+ private SSLEngineResult sslEngineResult; // results from sslEngine last operation
+ private ByteBuffer myAppData; // clear text message to be sent
+ private ByteBuffer myNetData; // encrypted message to be sent
+ private ByteBuffer peerAppData; // clear text message received from the
+ // switch
+ private ByteBuffer peerNetData; // encrypted message from the switch
+ private FileInputStream kfd = null, tfd = null;
+
+ public SecureMessageReadWriteService(SocketChannel socket, Selector selector)
+ throws Exception {
+ this.socket = socket;
+ this.selector = selector;
+ this.factory = new BasicFactory();
+
+ try {
+ createSecureChannel(socket);
+ createBuffers(sslEngine);
+ } catch (Exception e) {
+ stop();
+ throw e;
+ }
+ }
+
+ /**
+ * Bring up secure channel using SSL Engine
+ *
+ * @param socket
+ * TCP socket channel
+ * @throws Exception
+ */
+ private void createSecureChannel(SocketChannel socket) throws Exception {
+ String keyStoreFile = System.getProperty("controllerKeyStore");
+ String keyStorePassword = System
+ .getProperty("controllerKeyStorePassword");
+ String trustStoreFile = System.getProperty("controllerTrustStore");
+ String trustStorePassword = System
+ .getProperty("controllerTrustStorePassword");
+
+ if (keyStoreFile != null) {
+ keyStoreFile = keyStoreFile.trim();
+ }
+ if ((keyStoreFile == null) || keyStoreFile.isEmpty()) {
+ throw new FileNotFoundException(
+ "controllerKeyStore not specified in ./configuration/config.ini");
+ }
+ if (keyStorePassword != null) {
+ keyStorePassword = keyStorePassword.trim();
+ }
+ if ((keyStorePassword == null) || keyStorePassword.isEmpty()) {
+ throw new FileNotFoundException(
+ "controllerKeyStorePassword not specified in ./configuration/config.ini");
+ }
+ if (trustStoreFile != null) {
+ trustStoreFile = trustStoreFile.trim();
+ }
+ if ((trustStoreFile == null) || trustStoreFile.isEmpty()) {
+ throw new FileNotFoundException(
+ "controllerTrustStore not specified in ./configuration/config.ini");
+ }
+ if (trustStorePassword != null) {
+ trustStorePassword = trustStorePassword.trim();
+ }
+ if ((trustStorePassword == null) || trustStorePassword.isEmpty()) {
+ throw new FileNotFoundException(
+ "controllerTrustStorePassword not specified in ./configuration/config.ini");
+ }
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ KeyStore ts = KeyStore.getInstance("JKS");
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ kfd = new FileInputStream(keyStoreFile);
+ tfd = new FileInputStream(trustStoreFile);
+ ks.load(kfd, keyStorePassword.toCharArray());
+ ts.load(tfd, trustStorePassword.toCharArray());
+ kmf.init(ks, keyStorePassword.toCharArray());
+ tmf.init(ts);
+
+ SecureRandom random = new SecureRandom();
+ random.nextInt();
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random);
+ sslEngine = sslContext.createSSLEngine();
+ sslEngine.setUseClientMode(false);
+ sslEngine.setNeedClientAuth(true);
+
+ // Do initial handshake
+ doHandshake(socket, sslEngine);
+
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ);
+ }
+
+ /**
+ * Sends the OF message out over the socket channel. The message is
+ * encrypted by SSL Engine.
+ *
+ * @param msg
+ * OF message to be sent
+ * @throws Exception
+ */
+ @Override
+ public void asyncSend(OFMessage msg) throws Exception {
+ synchronized (myAppData) {
+ int msgLen = msg.getLengthU();
+ if (myAppData.remaining() < msgLen) {
+ // increase the buffer size so that it can contain this message
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(myAppData
+ .capacity() + msgLen);
+ myAppData.flip();
+ newBuffer.put(myAppData);
+ myAppData = newBuffer;
+ }
+ }
+ synchronized (myAppData) {
+ msg.writeTo(myAppData);
+ myAppData.flip();
+ sslEngineResult = sslEngine.wrap(myAppData, myNetData);
+ logger.trace("asyncSend sslEngine wrap: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ myNetData.flip();
+ socket.write(myNetData);
+ if (myNetData.hasRemaining()) {
+ myNetData.compact();
+ } else {
+ myNetData.clear();
+ }
+
+ if (myAppData.hasRemaining()) {
+ myAppData.compact();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_WRITE, this);
+ } else {
+ myAppData.clear();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ, this);
+ }
+
+ logger.trace("Message sent: {}", msg);
+ }
+ }
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ *
+ * @throws Exception
+ */
+ @Override
+ public void resumeSend() throws Exception {
+ synchronized (myAppData) {
+ myAppData.flip();
+ sslEngineResult = sslEngine.wrap(myAppData, myNetData);
+ logger.trace("resumeSend sslEngine wrap: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ myNetData.flip();
+ socket.write(myNetData);
+ if (myNetData.hasRemaining()) {
+ myNetData.compact();
+ } else {
+ myNetData.clear();
+ }
+
+ if (myAppData.hasRemaining()) {
+ myAppData.compact();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_WRITE, this);
+ } else {
+ myAppData.clear();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ, this);
+ }
+ }
+ }
+
+ /**
+ * Reads the incoming network data from the socket, decryptes them and then
+ * retrieves the OF messages.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ @Override
+ public List<OFMessage> readMessages() throws Exception {
+ if (!socket.isOpen()) {
+ return null;
+ }
+
+ List<OFMessage> msgs = null;
+ int bytesRead = -1;
+ int countDown = 50;
+
+ bytesRead = socket.read(peerNetData);
+ if (bytesRead < 0) {
+ logger.debug("Message read operation failed");
+ throw new AsynchronousCloseException();
+ }
+
+ do {
+ peerNetData.flip();
+ sslEngineResult = sslEngine.unwrap(peerNetData, peerAppData);
+ if (peerNetData.hasRemaining()) {
+ peerNetData.compact();
+ } else {
+ peerNetData.clear();
+ }
+ logger.trace("sslEngine unwrap result: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+ } while ((sslEngineResult.getStatus() == SSLEngineResult.Status.OK)
+ && peerNetData.hasRemaining() && (--countDown > 0));
+
+ if (countDown == 0) {
+ logger.trace("countDown reaches 0. peerNetData pos {} lim {}",
+ peerNetData.position(), peerNetData.limit());
+ }
+
+ peerAppData.flip();
+ msgs = factory.parseMessages(peerAppData);
+ if (peerAppData.hasRemaining()) {
+ peerAppData.compact();
+ } else {
+ peerAppData.clear();
+ }
+
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ, this);
+
+ return msgs;
+ }
+
+ /**
+ * If the result indicates that we have outstanding tasks to do, go ahead
+ * and run them in this thread.
+ */
+ private void runDelegatedTasks(SSLEngineResult result, SSLEngine engine)
+ throws Exception {
+
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ logger.debug("\trunning delegated task...");
+ runnable.run();
+ }
+ HandshakeStatus hsStatus = engine.getHandshakeStatus();
+ if (hsStatus == HandshakeStatus.NEED_TASK) {
+ throw new Exception("handshake shouldn't need additional tasks");
+ }
+ logger.debug("\tnew HandshakeStatus: {}", hsStatus);
+ }
+ }
+
+ private void doHandshake(SocketChannel socket, SSLEngine engine)
+ throws Exception {
+ SSLSession session = engine.getSession();
+ ByteBuffer myAppData = ByteBuffer.allocate(session
+ .getApplicationBufferSize());
+ ByteBuffer peerAppData = ByteBuffer.allocate(session
+ .getApplicationBufferSize());
+ ByteBuffer myNetData = ByteBuffer.allocate(session
+ .getPacketBufferSize());
+ ByteBuffer peerNetData = ByteBuffer.allocate(session
+ .getPacketBufferSize());
+
+ // Begin handshake
+ engine.beginHandshake();
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+
+ // Process handshaking message
+ while (hs != SSLEngineResult.HandshakeStatus.FINISHED
+ && hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ switch (hs) {
+ case NEED_UNWRAP:
+ // Receive handshaking data from peer
+ if (socket.read(peerNetData) < 0) {
+ throw new AsynchronousCloseException();
+ }
+
+ // Process incoming handshaking data
+ peerNetData.flip();
+ SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
+ peerNetData.compact();
+ hs = res.getHandshakeStatus();
+
+ // Check status
+ switch (res.getStatus()) {
+ case OK:
+ // Handle OK status
+ break;
+ }
+ break;
+
+ case NEED_WRAP:
+ // Empty the local network packet buffer.
+ myNetData.clear();
+
+ // Generate handshaking data
+ res = engine.wrap(myAppData, myNetData);
+ hs = res.getHandshakeStatus();
+
+ // Check status
+ switch (res.getStatus()) {
+ case OK:
+ myNetData.flip();
+
+ // Send the handshaking data to peer
+ while (myNetData.hasRemaining()) {
+ if (socket.write(myNetData) < 0) {
+ throw new AsynchronousCloseException();
+ }
+ }
+ break;
+ }
+ break;
+
+ case NEED_TASK:
+ // Handle blocking tasks
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ logger.debug("\trunning delegated task...");
+ runnable.run();
+ }
+ hs = engine.getHandshakeStatus();
+ if (hs == HandshakeStatus.NEED_TASK) {
+ throw new Exception(
+ "handshake shouldn't need additional tasks");
+ }
+ logger.debug("\tnew HandshakeStatus: {}", hs);
+ break;
+ }
+ }
+ }
+
+ private void createBuffers(SSLEngine engine) {
+ SSLSession session = engine.getSession();
+ this.myAppData = ByteBuffer
+ .allocate(session.getApplicationBufferSize());
+ this.peerAppData = ByteBuffer.allocate(session
+ .getApplicationBufferSize());
+ this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+ this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+ }
+
+ @Override
+ public void stop() throws IOException {
+ this.sslEngine = null;
+ this.sslEngineResult = null;
+ this.myAppData = null;
+ this.myNetData = null;
+ this.peerAppData = null;
+ this.peerNetData = null;
+
+ if (this.kfd != null) {
+ this.kfd.close();
+ this.kfd = null;
+ }
+ if (this.tfd != null) {
+ this.tfd.close();
+ this.tfd = null;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFStatisticsReply;
+import org.openflow.protocol.OFStatisticsRequest;
+import org.openflow.protocol.statistics.OFStatistics;
+
+public class StatisticsCollector implements Callable<Object> {
+
+ private ISwitch sw;
+ private Integer xid;
+ private OFStatisticsRequest request;
+ private CountDownLatch latch;
+ private Object result;
+ private List<OFStatistics> stats;
+
+ public StatisticsCollector(ISwitch sw, int xid, OFStatisticsRequest request) {
+ this.sw = sw;
+ this.xid = xid;
+ this.request = request;
+ latch = new CountDownLatch(1);
+ result = new Object();
+ stats = new CopyOnWriteArrayList<OFStatistics>();
+ }
+
+ /*
+ * accumulate the stats records in result
+ * Returns: true: if this is the last record
+ * false: more to come
+ */
+ public boolean collect(OFStatisticsReply reply) {
+ synchronized (result) {
+ stats.addAll(reply.getStatistics());
+ if ((reply.getFlags() & 0x01) == 0) {
+ // all stats are collected, done
+ result = stats;
+ return true;
+ } else {
+ // still waiting for more to come
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public Object call() throws Exception {
+ sw.asyncSend(request, xid);
+ // free up the request as it is no longer needed
+ request = null;
+ // wait until all stats replies are received or timeout
+ latch.await();
+ return result;
+ }
+
+ public Integer getXid() {
+ return this.xid;
+ }
+
+ public void wakeup() {
+ this.latch.countDown();
+ }
+
+ public void wakeup(OFError errorMsg) {
+
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFMessage;
+
+public class SwitchEvent {
+
+ public static enum SwitchEventType {
+ SWITCH_ADD, SWITCH_DELETE, SWITCH_ERROR, SWITCH_MESSAGE,
+ }
+
+ private SwitchEventType eventType;
+ private ISwitch sw;
+ private OFMessage msg;
+
+ public SwitchEvent(SwitchEventType type, ISwitch sw, OFMessage msg) {
+ this.eventType = type;
+ this.sw = sw;
+ this.msg = msg;
+ }
+
+ public SwitchEventType getEventType() {
+ return this.eventType;
+ }
+
+ public ISwitch getSwitch() {
+ return this.sw;
+ }
+
+ public OFMessage getMsg() {
+ return this.msg;
+ }
+
+ @Override
+ public String toString() {
+ String s;
+ switch (this.eventType) {
+ case SWITCH_ADD:
+ s = "SWITCH_ADD";
+ break;
+ case SWITCH_DELETE:
+ s = "SWITCH_DELETE";
+ break;
+ case SWITCH_ERROR:
+ s = "SWITCH_ERROR";
+ break;
+ case SWITCH_MESSAGE:
+ s = "SWITCH_MESSAGE";
+ break;
+ default:
+ s = "?" + this.eventType.ordinal() + "?";
+ }
+ return "Switch Event: " + s;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageReadWrite;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFBarrierReply;
+import org.openflow.protocol.OFBarrierRequest;
+import org.openflow.protocol.OFEchoReply;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFFeaturesReply;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFGetConfigReply;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
+import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
+import org.openflow.protocol.OFPhysicalPort.OFPortFeatures;
+import org.openflow.protocol.OFPhysicalPort.OFPortState;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFPortStatus;
+import org.openflow.protocol.OFPortStatus.OFPortReason;
+import org.openflow.protocol.OFSetConfig;
+import org.openflow.protocol.OFStatisticsReply;
+import org.openflow.protocol.OFStatisticsRequest;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.factory.BasicFactory;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SwitchHandler implements ISwitch {
+ private static final Logger logger = LoggerFactory
+ .getLogger(SwitchHandler.class);
+ private static final int SWITCH_LIVENESS_TIMER = 5000;
+ private static final int switchLivenessTimeout = getSwitchLivenessTimeout();
+ private final int MESSAGE_RESPONSE_TIMER = 2000;
+
+ private final String instanceName;
+ private final ISwitch thisISwitch;
+ private final IController core;
+ private Long sid;
+ private Integer buffers;
+ private Integer capabilities;
+ private Byte tables;
+ private Integer actions;
+ private Selector selector;
+ private final SocketChannel socket;
+ private final BasicFactory factory;
+ private final AtomicInteger xid;
+ private SwitchState state;
+ private Timer periodicTimer;
+ private final Map<Short, OFPhysicalPort> physicalPorts;
+ private final Map<Short, Integer> portBandwidth;
+ private final Date connectedDate;
+ private Long lastMsgReceivedTimeStamp;
+ private Boolean probeSent;
+ private final ExecutorService executor;
+ private final ConcurrentHashMap<Integer, Callable<Object>> messageWaitingDone;
+ private boolean running;
+ private IMessageReadWrite msgReadWriteService;
+ private Thread switchHandlerThread;
+ private Integer responseTimerValue;
+ private PriorityBlockingQueue<PriorityMessage> transmitQ;
+ private Thread transmitThread;
+
+ private enum SwitchState {
+ NON_OPERATIONAL(0), WAIT_FEATURES_REPLY(1), WAIT_CONFIG_REPLY(2), OPERATIONAL(
+ 3);
+
+ private int value;
+
+ private SwitchState(int value) {
+ this.value = value;
+ }
+
+ @SuppressWarnings("unused")
+ public int value() {
+ return this.value;
+ }
+ }
+
+ public SwitchHandler(Controller core, SocketChannel sc, String name) {
+ this.instanceName = name;
+ this.thisISwitch = this;
+ this.sid = (long) 0;
+ this.buffers = (int) 0;
+ this.capabilities = (int) 0;
+ this.tables = (byte) 0;
+ this.actions = (int) 0;
+ this.core = core;
+ this.socket = sc;
+ this.factory = new BasicFactory();
+ this.connectedDate = new Date();
+ this.lastMsgReceivedTimeStamp = connectedDate.getTime();
+ this.physicalPorts = new HashMap<Short, OFPhysicalPort>();
+ this.portBandwidth = new HashMap<Short, Integer>();
+ this.state = SwitchState.NON_OPERATIONAL;
+ this.probeSent = false;
+ this.xid = new AtomicInteger(this.socket.hashCode());
+ this.periodicTimer = null;
+ this.executor = Executors.newFixedThreadPool(4);
+ this.messageWaitingDone = new ConcurrentHashMap<Integer, Callable<Object>>();
+ this.responseTimerValue = MESSAGE_RESPONSE_TIMER;
+ String rTimer = System.getProperty("of.messageResponseTimer");
+ if (rTimer != null) {
+ try {
+ responseTimerValue = Integer.decode(rTimer);
+ } catch (NumberFormatException e) {
+ logger.warn(
+ "Invalid of.messageResponseTimer: {} use default({})",
+ rTimer, MESSAGE_RESPONSE_TIMER);
+ }
+ }
+ }
+
+ public void start() {
+ try {
+ startTransmitThread();
+ setupCommChannel();
+ sendFirstHello();
+ startHandlerThread();
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+
+ private void startHandlerThread() {
+ switchHandlerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ running = true;
+ while (running) {
+ try {
+ // wait for an incoming connection
+ selector.select(0);
+ Iterator<SelectionKey> selectedKeys = selector
+ .selectedKeys().iterator();
+ while (selectedKeys.hasNext()) {
+ SelectionKey skey = selectedKeys.next();
+ selectedKeys.remove();
+ if (skey.isValid() && skey.isWritable()) {
+ resumeSend();
+ }
+ if (skey.isValid() && skey.isReadable()) {
+ handleMessages();
+ }
+ }
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+ }
+ }, instanceName);
+ switchHandlerThread.start();
+ }
+
+ public void stop() {
+ running = false;
+ cancelSwitchTimer();
+ try {
+ selector.wakeup();
+ selector.close();
+ } catch (Exception e) {
+ }
+ try {
+ socket.close();
+ } catch (Exception e) {
+ }
+ try {
+ msgReadWriteService.stop();
+ } catch (Exception e) {
+ }
+ executor.shutdown();
+
+ msgReadWriteService = null;
+
+ if (switchHandlerThread != null) {
+ switchHandlerThread.interrupt();
+ }
+ if (transmitThread != null) {
+ transmitThread.interrupt();
+ }
+ }
+
+ @Override
+ public int getNextXid() {
+ return this.xid.incrementAndGet();
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. An unique XID is generated automatically and
+ * inserted into the message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncSend(OFMessage msg) {
+ return asyncSend(msg, getNextXid());
+ }
+
+ private Object syncSend(OFMessage msg, int xid) {
+ return syncMessageInternal(msg, xid, true);
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. The specified XID is inserted into the message.
+ *
+ * @param msg
+ * The OF message to be Sent
+ * @param xid
+ * The XID to be used in the message
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncSend(OFMessage msg, int xid) {
+ msg.setXid(xid);
+ if (transmitQ != null) {
+ transmitQ.add(new PriorityMessage(msg, 0));
+ }
+ return xid;
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. An unique XID is generated automatically and inserted into the
+ * message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncFastSend(OFMessage msg) {
+ return asyncFastSend(msg, getNextXid());
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. The specified XID is inserted into the message.
+ *
+ * @param msg
+ * The OF message to be sent
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncFastSend(OFMessage msg, int xid) {
+ msg.setXid(xid);
+ if (transmitQ != null) {
+ transmitQ.add(new PriorityMessage(msg, 1));
+ }
+ return xid;
+ }
+
+ public void resumeSend() {
+ try {
+ if (msgReadWriteService != null) {
+ msgReadWriteService.resumeSend();
+ }
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+
+ /**
+ * This method bypasses the transmit queue and sends the message over the
+ * socket directly. If the input xid is not null, the specified xid is
+ * inserted into the message. Otherwise, an unique xid is generated
+ * automatically and inserted into the message.
+ *
+ * @param msg
+ * Message to be sent
+ * @param xid
+ * Message xid
+ */
+ private void asyncSendNow(OFMessage msg, Integer xid) {
+ if (xid == null) {
+ xid = getNextXid();
+ }
+ msg.setXid(xid);
+
+ asyncSendNow(msg);
+ }
+
+ /**
+ * This method bypasses the transmit queue and sends the message over the
+ * socket directly.
+ *
+ * @param msg
+ * Message to be sent
+ */
+ private void asyncSendNow(OFMessage msg) {
+ if (msgReadWriteService == null) {
+ logger.warn(
+ "asyncSendNow: {} is not sent because Message ReadWrite Service is not available.",
+ msg);
+ return;
+ }
+
+ try {
+ msgReadWriteService.asyncSend(msg);
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+
+ public void handleMessages() {
+ List<OFMessage> msgs = null;
+
+ try {
+ if (msgReadWriteService != null) {
+ msgs = msgReadWriteService.readMessages();
+ }
+ } catch (Exception e) {
+ reportError(e);
+ }
+
+ if (msgs == null) {
+ logger.debug("{} is down", this);
+ // the connection is down, inform core
+ reportSwitchStateChange(false);
+ return;
+ }
+ for (OFMessage msg : msgs) {
+ logger.trace("Message received: {}", msg);
+ this.lastMsgReceivedTimeStamp = System.currentTimeMillis();
+ OFType type = msg.getType();
+ switch (type) {
+ case HELLO:
+ // send feature request
+ OFMessage featureRequest = factory
+ .getMessage(OFType.FEATURES_REQUEST);
+ asyncFastSend(featureRequest);
+ // delete all pre-existing flows
+ OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
+ OFFlowMod flowMod = (OFFlowMod) factory
+ .getMessage(OFType.FLOW_MOD);
+ flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE)
+ .setOutPort(OFPort.OFPP_NONE)
+ .setLength((short) OFFlowMod.MINIMUM_LENGTH);
+ asyncFastSend(flowMod);
+ this.state = SwitchState.WAIT_FEATURES_REPLY;
+ startSwitchTimer();
+ break;
+ case ECHO_REQUEST:
+ OFEchoReply echoReply = (OFEchoReply) factory
+ .getMessage(OFType.ECHO_REPLY);
+ // respond immediately
+ asyncSendNow(echoReply, msg.getXid());
+ break;
+ case ECHO_REPLY:
+ this.probeSent = false;
+ break;
+ case FEATURES_REPLY:
+ processFeaturesReply((OFFeaturesReply) msg);
+ break;
+ case GET_CONFIG_REPLY:
+ // make sure that the switch can send the whole packet to the
+ // controller
+ if (((OFGetConfigReply) msg).getMissSendLength() == (short) 0xffff) {
+ this.state = SwitchState.OPERATIONAL;
+ }
+ break;
+ case BARRIER_REPLY:
+ processBarrierReply((OFBarrierReply) msg);
+ break;
+ case ERROR:
+ processErrorReply((OFError) msg);
+ break;
+ case PORT_STATUS:
+ processPortStatusMsg((OFPortStatus) msg);
+ break;
+ case STATS_REPLY:
+ processStatsReply((OFStatisticsReply) msg);
+ break;
+ case PACKET_IN:
+ break;
+ default:
+ break;
+ } // end of switch
+ if (isOperational()) {
+ ((Controller) core).takeSwitchEventMsg(thisISwitch, msg);
+ }
+ } // end of for
+ }
+
+ private void processPortStatusMsg(OFPortStatus msg) {
+ OFPhysicalPort port = msg.getDesc();
+ if (msg.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
+ updatePhysicalPort(port);
+ } else if (msg.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
+ updatePhysicalPort(port);
+ } else if (msg.getReason() == (byte) OFPortReason.OFPPR_DELETE
+ .ordinal()) {
+ deletePhysicalPort(port);
+ }
+
+ }
+
+ private void startSwitchTimer() {
+ this.periodicTimer = new Timer();
+ this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Long now = System.currentTimeMillis();
+ if ((now - lastMsgReceivedTimeStamp) > switchLivenessTimeout) {
+ if (probeSent) {
+ // switch failed to respond to our probe, consider
+ // it down
+ logger.warn("{} is idle for too long, disconnect",
+ toString());
+ reportSwitchStateChange(false);
+ } else {
+ // send a probe to see if the switch is still alive
+ logger.debug(
+ "Send idle probe (Echo Request) to {}",
+ this);
+ probeSent = true;
+ OFMessage echo = factory
+ .getMessage(OFType.ECHO_REQUEST);
+ asyncFastSend(echo);
+ }
+ } else {
+ if (state == SwitchState.WAIT_FEATURES_REPLY) {
+ // send another features request
+ OFMessage request = factory
+ .getMessage(OFType.FEATURES_REQUEST);
+ asyncFastSend(request);
+ } else {
+ if (state == SwitchState.WAIT_CONFIG_REPLY) {
+ // send another config request
+ OFSetConfig config = (OFSetConfig) factory
+ .getMessage(OFType.SET_CONFIG);
+ config.setMissSendLength((short) 0xffff)
+ .setLengthU(OFSetConfig.MINIMUM_LENGTH);
+ asyncFastSend(config);
+ OFMessage getConfig = factory
+ .getMessage(OFType.GET_CONFIG_REQUEST);
+ asyncFastSend(getConfig);
+ }
+ }
+ }
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+ }, SWITCH_LIVENESS_TIMER, SWITCH_LIVENESS_TIMER);
+ }
+
+ private void cancelSwitchTimer() {
+ if (this.periodicTimer != null) {
+ this.periodicTimer.cancel();
+ }
+ }
+
+ private void reportError(Exception e) {
+ if (e instanceof AsynchronousCloseException
+ || e instanceof InterruptedException
+ || e instanceof SocketException || e instanceof IOException
+ || e instanceof ClosedSelectorException) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Caught exception {}", e.getMessage());
+ }
+ } else {
+ logger.warn("Caught exception ", e);
+ }
+ // notify core of this error event and disconnect the switch
+ ((Controller) core).takeSwitchEventError(this);
+ }
+
+ private void reportSwitchStateChange(boolean added) {
+ if (added) {
+ ((Controller) core).takeSwitchEventAdd(this);
+ } else {
+ ((Controller) core).takeSwitchEventDelete(this);
+ }
+ }
+
+ @Override
+ public Long getId() {
+ return this.sid;
+ }
+
+ private void processFeaturesReply(OFFeaturesReply reply) {
+ if (this.state == SwitchState.WAIT_FEATURES_REPLY) {
+ this.sid = reply.getDatapathId();
+ this.buffers = reply.getBuffers();
+ this.capabilities = reply.getCapabilities();
+ this.tables = reply.getTables();
+ this.actions = reply.getActions();
+ // notify core of this error event
+ for (OFPhysicalPort port : reply.getPorts()) {
+ updatePhysicalPort(port);
+ }
+ // config the switch to send full data packet
+ OFSetConfig config = (OFSetConfig) factory
+ .getMessage(OFType.SET_CONFIG);
+ config.setMissSendLength((short) 0xffff).setLengthU(
+ OFSetConfig.MINIMUM_LENGTH);
+ asyncFastSend(config);
+ // send config request to make sure the switch can handle the set
+ // config
+ OFMessage getConfig = factory.getMessage(OFType.GET_CONFIG_REQUEST);
+ asyncFastSend(getConfig);
+ this.state = SwitchState.WAIT_CONFIG_REPLY;
+ // inform core that a new switch is now operational
+ reportSwitchStateChange(true);
+ }
+ }
+
+ private void updatePhysicalPort(OFPhysicalPort port) {
+ Short portNumber = port.getPortNumber();
+ physicalPorts.put(portNumber, port);
+ portBandwidth
+ .put(portNumber,
+ port.getCurrentFeatures()
+ & (OFPortFeatures.OFPPF_10MB_FD.getValue()
+ | OFPortFeatures.OFPPF_10MB_HD
+ .getValue()
+ | OFPortFeatures.OFPPF_100MB_FD
+ .getValue()
+ | OFPortFeatures.OFPPF_100MB_HD
+ .getValue()
+ | OFPortFeatures.OFPPF_1GB_FD
+ .getValue()
+ | OFPortFeatures.OFPPF_1GB_HD
+ .getValue() | OFPortFeatures.OFPPF_10GB_FD
+ .getValue()));
+ }
+
+ private void deletePhysicalPort(OFPhysicalPort port) {
+ Short portNumber = port.getPortNumber();
+ physicalPorts.remove(portNumber);
+ portBandwidth.remove(portNumber);
+ }
+
+ @Override
+ public boolean isOperational() {
+ return ((this.state == SwitchState.WAIT_CONFIG_REPLY) || (this.state == SwitchState.OPERATIONAL));
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return ("Switch:"
+ + socket.socket().getRemoteSocketAddress().toString().split("/")[1]
+ + " SWID:" + (isOperational() ? HexString
+ .toHexString(this.sid) : "unknown"));
+ } catch (Exception e) {
+ return (isOperational() ? HexString.toHexString(this.sid)
+ : "unknown");
+ }
+
+ }
+
+ @Override
+ public Date getConnectedDate() {
+ return this.connectedDate;
+ }
+
+ public String getInstanceName() {
+ return instanceName;
+ }
+
+ @Override
+ public Object getStatistics(OFStatisticsRequest req) {
+ int xid = getNextXid();
+ StatisticsCollector worker = new StatisticsCollector(this, xid, req);
+ messageWaitingDone.put(xid, worker);
+ Future<Object> submit = executor.submit(worker);
+ Object result = null;
+ try {
+ result = submit.get(responseTimerValue, TimeUnit.MILLISECONDS);
+ return result;
+ } catch (Exception e) {
+ logger.warn("Timeout while waiting for {} replies", req.getType());
+ result = null; // to indicate timeout has occurred
+ return result;
+ }
+ }
+
+ @Override
+ public Object syncSend(OFMessage msg) {
+ int xid = getNextXid();
+ return syncSend(msg, xid);
+ }
+
+ /*
+ * Either a BarrierReply or a OFError is received. If this is a reply for an
+ * outstanding sync message, wake up associated task so that it can continue
+ */
+ private void processBarrierReply(OFBarrierReply msg) {
+ Integer xid = msg.getXid();
+ SynchronousMessage worker = (SynchronousMessage) messageWaitingDone
+ .remove(xid);
+ if (worker == null) {
+ return;
+ }
+ worker.wakeup();
+ }
+
+ private void processErrorReply(OFError errorMsg) {
+ OFMessage offendingMsg = errorMsg.getOffendingMsg();
+ Integer xid;
+ if (offendingMsg != null) {
+ xid = offendingMsg.getXid();
+ } else {
+ xid = errorMsg.getXid();
+ }
+ /*
+ * the error can be a reply to a synchronous message or to a statistic
+ * request message
+ */
+ Callable<?> worker = messageWaitingDone.remove(xid);
+ if (worker == null) {
+ return;
+ }
+ if (worker instanceof SynchronousMessage) {
+ ((SynchronousMessage) worker).wakeup(errorMsg);
+ } else {
+ ((StatisticsCollector) worker).wakeup(errorMsg);
+ }
+ }
+
+ private void processStatsReply(OFStatisticsReply reply) {
+ Integer xid = reply.getXid();
+ StatisticsCollector worker = (StatisticsCollector) messageWaitingDone
+ .get(xid);
+ if (worker == null) {
+ return;
+ }
+ if (worker.collect(reply)) {
+ // if all the stats records are received (collect() returns true)
+ // then we are done.
+ messageWaitingDone.remove(xid);
+ worker.wakeup();
+ }
+ }
+
+ @Override
+ public Map<Short, OFPhysicalPort> getPhysicalPorts() {
+ return this.physicalPorts;
+ }
+
+ @Override
+ public OFPhysicalPort getPhysicalPort(Short portNumber) {
+ return this.physicalPorts.get(portNumber);
+ }
+
+ @Override
+ public Integer getPortBandwidth(Short portNumber) {
+ return this.portBandwidth.get(portNumber);
+ }
+
+ @Override
+ public Set<Short> getPorts() {
+ return this.physicalPorts.keySet();
+ }
+
+ @Override
+ public Byte getTables() {
+ return this.tables;
+ }
+
+ @Override
+ public Integer getActions() {
+ return this.actions;
+ }
+
+ @Override
+ public Integer getCapabilities() {
+ return this.capabilities;
+ }
+
+ @Override
+ public Integer getBuffers() {
+ return this.buffers;
+ }
+
+ @Override
+ public boolean isPortEnabled(short portNumber) {
+ return isPortEnabled(physicalPorts.get(portNumber));
+ }
+
+ @Override
+ public boolean isPortEnabled(OFPhysicalPort port) {
+ if (port == null) {
+ return false;
+ }
+ int portConfig = port.getConfig();
+ int portState = port.getState();
+ if ((portConfig & OFPortConfig.OFPPC_PORT_DOWN.getValue()) > 0) {
+ return false;
+ }
+ if ((portState & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0) {
+ return false;
+ }
+ if ((portState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK
+ .getValue()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public List<OFPhysicalPort> getEnabledPorts() {
+ List<OFPhysicalPort> result = new ArrayList<OFPhysicalPort>();
+ synchronized (this.physicalPorts) {
+ for (OFPhysicalPort port : physicalPorts.values()) {
+ if (isPortEnabled(port)) {
+ result.add(port);
+ }
+ }
+ }
+ return result;
+ }
+
+ /*
+ * Transmit thread polls the message out of the priority queue and invokes
+ * messaging service to transmit it over the socket channel
+ */
+ class PriorityMessageTransmit implements Runnable {
+ @Override
+ public void run() {
+ running = true;
+ while (running) {
+ try {
+ PriorityMessage pmsg = transmitQ.take();
+ msgReadWriteService.asyncSend(pmsg.msg);
+ /*
+ * If syncReply is set to true, wait for the response back.
+ */
+ if (pmsg.syncReply) {
+ syncMessageInternal(pmsg.msg, pmsg.msg.getXid(), false);
+ }
+ } catch (InterruptedException ie) {
+ reportError(new InterruptedException(
+ "PriorityMessageTransmit thread interrupted"));
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+ transmitQ = null;
+ }
+ }
+
+ /*
+ * Setup and start the transmit thread
+ */
+ private void startTransmitThread() {
+ this.transmitQ = new PriorityBlockingQueue<PriorityMessage>(11,
+ new Comparator<PriorityMessage>() {
+ @Override
+ public int compare(PriorityMessage p1, PriorityMessage p2) {
+ if (p2.priority != p1.priority) {
+ return p2.priority - p1.priority;
+ } else {
+ return (p2.seqNum < p1.seqNum) ? 1 : -1;
+ }
+ }
+ });
+ this.transmitThread = new Thread(new PriorityMessageTransmit());
+ this.transmitThread.start();
+ }
+
+ /*
+ * Setup communication services
+ */
+ private void setupCommChannel() throws Exception {
+ this.selector = SelectorProvider.provider().openSelector();
+ this.socket.configureBlocking(false);
+ this.socket.socket().setTcpNoDelay(true);
+ this.msgReadWriteService = getMessageReadWriteService();
+ }
+
+ private void sendFirstHello() {
+ try {
+ OFMessage msg = factory.getMessage(OFType.HELLO);
+ asyncFastSend(msg);
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+
+ private IMessageReadWrite getMessageReadWriteService() throws Exception {
+ String str = System.getProperty("secureChannelEnabled");
+ return ((str != null) && (str.trim().equalsIgnoreCase("true"))) ? new SecureMessageReadWriteService(
+ socket, selector) : new MessageReadWriteService(socket,
+ selector);
+ }
+
+ /**
+ * Send Barrier message synchronously. The caller will be blocked until the
+ * Barrier reply is received.
+ */
+ @Override
+ public Object syncSendBarrierMessage() {
+ OFBarrierRequest barrierMsg = new OFBarrierRequest();
+ return syncSend(barrierMsg);
+ }
+
+ /**
+ * Send Barrier message asynchronously. The caller is not blocked. The
+ * Barrier message will be sent in a transmit thread which will be blocked
+ * until the Barrier reply is received.
+ */
+ @Override
+ public Object asyncSendBarrierMessage() {
+ if (transmitQ == null) {
+ return Boolean.FALSE;
+ }
+
+ OFBarrierRequest barrierMsg = new OFBarrierRequest();
+ int xid = getNextXid();
+
+ barrierMsg.setXid(xid);
+ transmitQ.add(new PriorityMessage(barrierMsg, 0, true));
+
+ return Boolean.TRUE;
+ }
+
+ /**
+ * This method returns the switch liveness timeout value. If controller did
+ * not receive any message from the switch for such a long period,
+ * controller will tear down the connection to the switch.
+ *
+ * @return The timeout value
+ */
+ private static int getSwitchLivenessTimeout() {
+ String timeout = System.getProperty("of.switchLivenessTimeout");
+ int rv = 60500;
+
+ try {
+ if (timeout != null) {
+ rv = Integer.parseInt(timeout);
+ }
+ } catch (Exception e) {
+ }
+
+ return rv;
+ }
+
+ /**
+ * This method performs synchronous operations for a given message. If
+ * syncRequest is set to true, the message will be sent out followed by a
+ * Barrier request message. Then it's blocked until the Barrier rely arrives
+ * or timeout. If syncRequest is false, it simply skips the message send and
+ * just waits for the response back.
+ *
+ * @param msg
+ * Message to be sent
+ * @param xid
+ * Message XID
+ * @param request
+ * If set to true, the message the message will be sent out
+ * followed by a Barrier request message. If set to false, it
+ * simply skips the sending and just waits for the Barrier reply.
+ * @return the result
+ */
+ private Object syncMessageInternal(OFMessage msg, int xid, boolean syncRequest) {
+ SynchronousMessage worker = new SynchronousMessage(this, xid, msg, syncRequest);
+ messageWaitingDone.put(xid, worker);
+ Object result = null;
+ Boolean status = false;
+ Future<Object> submit = executor.submit(worker);
+ try {
+ result = submit.get(responseTimerValue, TimeUnit.MILLISECONDS);
+ messageWaitingDone.remove(xid);
+ if (result == null) {
+ // if result is null, then it means the switch can handle this
+ // message successfully
+ // convert the result into a Boolean with value true
+ status = true;
+ // logger.debug("Successfully send " +
+ // msg.getType().toString());
+ result = status;
+ } else {
+ // if result is not null, this means the switch can't handle
+ // this message
+ // the result if OFError already
+ if (logger.isDebugEnabled()) {
+ logger.debug("Send {} failed --> {}", msg.getType(),
+ (result));
+ }
+ }
+ return result;
+ } catch (Exception e) {
+ logger.warn("Timeout while waiting for {} reply", msg.getType()
+ .toString());
+ // convert the result into a Boolean with value false
+ status = false;
+ result = status;
+ return result;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.core.internal;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFBarrierRequest;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFMessage;
+
+/**
+ * This class implements synchronous operations on message send to a switch. If
+ * syncRequest is set to true, it sends the requested message to the switch
+ * followed by a Barrier request message. It returns the result once it gets the
+ * reply from the switch or after a timeout. If the protocol does not dictate
+ * the switch to reply the processing status for a particular message, the
+ * Barrier request forces the switch to reply saying whether or not the message
+ * processing was successful for messages sent to the switch up to this point.
+ * If syncRequest is false, it simply skips the message send and just waits for
+ * the response back.
+ */
+public class SynchronousMessage implements Callable<Object> {
+ private ISwitch sw;
+ private Integer xid;
+ private OFMessage syncMsg;
+ protected CountDownLatch latch;
+ private Object result;
+ private boolean syncRequest;
+
+ public SynchronousMessage(ISwitch sw, Integer xid, OFMessage msg,
+ boolean syncRequest) {
+ this.sw = sw;
+ this.xid = xid;
+ syncMsg = msg;
+ latch = new CountDownLatch(1);
+ result = null;
+ this.syncRequest = syncRequest;
+ }
+
+ @Override
+ public Object call() throws Exception {
+ /*
+ * Send out message only if syncRequest is set to true. Otherwise, just
+ * wait for the Barrier response back.
+ */
+ if (syncRequest) {
+ sw.asyncSend(syncMsg, xid);
+ if (!(syncMsg instanceof OFBarrierRequest)) {
+ OFBarrierRequest barrierMsg = new OFBarrierRequest();
+ sw.asyncSend(barrierMsg, xid);
+ }
+ }
+ latch.await();
+ return result;
+ }
+
+ public Integer getXid() {
+ return this.xid;
+ }
+
+ public void wakeup() {
+ this.latch.countDown();
+ }
+
+ public void wakeup(OFError e) {
+ result = e;
+ wakeup();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
+import org.opendaylight.controller.sal.packet.IPluginInDataPacketService;
+import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService;
+import org.opendaylight.controller.sal.reader.IPluginInReadService;
+import org.opendaylight.controller.sal.reader.IPluginOutReadService;
+import org.opendaylight.controller.sal.topology.IPluginInTopologyService;
+import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.openflowplugin.openflow.IDataPacketListen;
+import org.opendaylight.openflowplugin.openflow.IDataPacketMux;
+import org.opendaylight.openflowplugin.openflow.IDiscoveryListener;
+import org.opendaylight.openflowplugin.openflow.IFlowProgrammerNotifier;
+import org.opendaylight.openflowplugin.openflow.IInventoryProvider;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimInternalListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsManager;
+import org.opendaylight.openflowplugin.openflow.IReadFilterInternalListener;
+import org.opendaylight.openflowplugin.openflow.IReadServiceFilter;
+import org.opendaylight.openflowplugin.openflow.IRefreshInternalProvider;
+import org.opendaylight.openflowplugin.openflow.ITopologyServiceShimListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageListener;
+import org.opendaylight.openflowplugin.openflow.core.internal.Controller;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Openflow protocol plugin Activator
+ *
+ *
+ */
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some initializations
+ * are done by the ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the cleanup done by
+ * ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the list of
+ * known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { TopologyServices.class, DataPacketServices.class,
+ InventoryService.class, ReadService.class,
+ FlowProgrammerNotifier.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies is
+ * required.
+ *
+ * @param c
+ * dependency manager Component object, used for configuring the
+ * dependencies exported and imported
+ * @param imp
+ * Implementation class that is being configured, needed as long
+ * as the same routine can configure multiple implementations
+ * @param containerName
+ * The containerName being configured, this allow also optional
+ * per-container different behavior if needed, usually should not
+ * be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(TopologyServices.class)) {
+ // export the service to be used by SAL
+ c.setInterface(
+ new String[] { IPluginInTopologyService.class.getName(),
+ ITopologyServiceShimListener.class.getName() }, null);
+ // Hook the services coming in from SAL, as optional in
+ // case SAL is not yet there, could happen
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IPluginOutTopologyService.class)
+ .setCallbacks("setPluginOutTopologyService",
+ "unsetPluginOutTopologyService").setRequired(false));
+ c.add(createServiceDependency()
+ .setService(IRefreshInternalProvider.class)
+ .setCallbacks("setRefreshInternalProvider",
+ "unsetRefreshInternalProvider").setRequired(false));
+ }
+
+ if (imp.equals(InventoryService.class)) {
+ // export the service
+ c.setInterface(
+ new String[] {
+ IPluginInInventoryService.class.getName(),
+ IInventoryShimInternalListener.class.getName(),
+ IInventoryProvider.class.getName() }, null);
+
+ // Now lets add a service dependency to make sure the
+ // provider of service exists
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IPluginOutInventoryService.class)
+ .setCallbacks("setPluginOutInventoryServices",
+ "unsetPluginOutInventoryServices")
+ .setRequired(false));
+ }
+
+ if (imp.equals(DataPacketServices.class)) {
+ // export the service to be used by SAL
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ // Set the protocolPluginType property which will be used
+ // by SAL
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+ c.setInterface(IPluginInDataPacketService.class.getName(), props);
+ // Hook the services coming in from SAL, as optional in
+ // case SAL is not yet there, could happen
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ // This is required for the transmission to happen properly
+ c.add(createServiceDependency().setService(IDataPacketMux.class)
+ .setCallbacks("setIDataPacketMux", "unsetIDataPacketMux")
+ .setRequired(true));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IPluginOutDataPacketService.class)
+ .setCallbacks("setPluginOutDataPacketService",
+ "unsetPluginOutDataPacketService")
+ .setRequired(false));
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ if (imp.equals(ReadService.class)) {
+ // export the service to be used by SAL
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ // Set the protocolPluginType property which will be used
+ // by SAL
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+ c.setInterface(new String[] {
+ IReadFilterInternalListener.class.getName(),
+ IPluginInReadService.class.getName() }, props);
+
+ c.add(createServiceDependency()
+ .setService(IReadServiceFilter.class)
+ .setCallbacks("setService", "unsetService")
+ .setRequired(true));
+
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IPluginOutReadService.class)
+ .setCallbacks("setPluginOutReadServices",
+ "unsetPluginOutReadServices")
+ .setRequired(false));
+
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ if (imp.equals(FlowProgrammerNotifier.class)) {
+ // export the service to be used by SAL
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ // Set the protocolPluginType property which will be used
+ // by SAL
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+ c.setInterface(IFlowProgrammerNotifier.class.getName(), props);
+
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IPluginOutFlowProgrammerService.class)
+ .setCallbacks("setPluginOutFlowProgrammerService",
+ "unsetPluginOutFlowProgrammerService")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the list of
+ * known implementations for services that are container independent.
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getGlobalImplementations() {
+ Object[] res = { Controller.class, OFStatisticsManager.class,
+ FlowProgrammerService.class, ReadServiceFilter.class,
+ DiscoveryService.class, DataPacketMuxDemux.class, InventoryService.class,
+ InventoryServiceShim.class, TopologyServiceShim.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies is
+ * required.
+ *
+ * @param c
+ * dependency manager Component object, used for configuring the
+ * dependencies exported and imported
+ * @param imp
+ * Implementation class that is being configured, needed as long
+ * as the same routine can configure multiple implementations
+ */
+ public void configureGlobalInstance(Component c, Object imp) {
+
+ if (imp.equals(Controller.class)) {
+ logger.debug("Activator configureGlobalInstance( ) is called");
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("name", "Controller");
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+ c.setInterface(new String[] { IController.class.getName(),
+ IPluginInConnectionService.class.getName()},
+ props);
+ }
+
+ if (imp.equals(FlowProgrammerService.class)) {
+ // export the service to be used by SAL
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ // Set the protocolPluginType property which will be used
+ // by SAL
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+ c.setInterface(
+ new String[] {
+ IPluginInFlowProgrammerService.class.getName(),
+ IMessageListener.class.getName(),
+ IContainerListener.class.getName(),
+ IInventoryShimExternalListener.class.getName() },
+ props);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+
+ c.add(createServiceDependency()
+ .setService(IFlowProgrammerNotifier.class)
+ .setCallbacks("setFlowProgrammerNotifier",
+ "unsetsetFlowProgrammerNotifier")
+ .setRequired(false));
+
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ if (imp.equals(ReadServiceFilter.class)) {
+
+ c.setInterface(new String[] {
+ IReadServiceFilter.class.getName(),
+ IContainerListener.class.getName(),
+ IOFStatisticsListener.class.getName() }, null);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IOFStatisticsManager.class)
+ .setCallbacks("setService", "unsetService")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IReadFilterInternalListener.class)
+ .setCallbacks("setReadFilterInternalListener",
+ "unsetReadFilterInternalListener")
+ .setRequired(false));
+ }
+
+ if (imp.equals(OFStatisticsManager.class)) {
+
+ c.setInterface(new String[] { IOFStatisticsManager.class.getName(),
+ IInventoryShimExternalListener.class.getName() }, null);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IOFStatisticsListener.class)
+ .setCallbacks("setStatisticsListener",
+ "unsetStatisticsListener").setRequired(false));
+ }
+
+ if (imp.equals(DiscoveryService.class)) {
+ // export the service
+ c.setInterface(
+ new String[] {
+ IInventoryShimExternalListener.class.getName(),
+ IDataPacketListen.class.getName(),
+ IContainerListener.class.getName() }, null);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createContainerServiceDependency(
+ GlobalConstants.DEFAULT.toString())
+ .setService(IInventoryProvider.class)
+ .setCallbacks("setInventoryProvider",
+ "unsetInventoryProvider").setRequired(true));
+ c.add(createServiceDependency().setService(IDataPacketMux.class)
+ .setCallbacks("setIDataPacketMux", "unsetIDataPacketMux")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IDiscoveryListener.class)
+ .setCallbacks("setDiscoveryListener",
+ "unsetDiscoveryListener").setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ // DataPacket mux/demux services, which is teh actual engine
+ // doing the packet switching
+ if (imp.equals(DataPacketMuxDemux.class)) {
+ c.setInterface(new String[] { IDataPacketMux.class.getName(),
+ IContainerListener.class.getName(),
+ IInventoryShimExternalListener.class.getName() }, null);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IPluginOutDataPacketService.class)
+ .setCallbacks("setPluginOutDataPacketService",
+ "unsetPluginOutDataPacketService")
+ .setRequired(false));
+ // See if there is any local packet dispatcher
+ c.add(createServiceDependency()
+ .setService(IDataPacketListen.class)
+ .setCallbacks("setIDataPacketListen",
+ "unsetIDataPacketListen").setRequired(false));
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ if (imp.equals(InventoryService.class)) {
+ // export the service
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("scope", "Global");
+
+ c.setInterface(
+ new String[] { IPluginInInventoryService.class.getName(),
+ IInventoryShimInternalListener.class.getName(),
+ IInventoryProvider.class.getName() }, props);
+
+ // Now lets add a service dependency to make sure the
+ // provider of service exists
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IPluginOutInventoryService.class, "(scope=Global)")
+ .setCallbacks("setPluginOutInventoryServices",
+ "unsetPluginOutInventoryServices")
+ .setRequired(false));
+ }
+
+ if (imp.equals(InventoryServiceShim.class)) {
+ c.setInterface(new String[] { IContainerListener.class.getName(),
+ IOFStatisticsListener.class.getName()}, null);
+
+ c.add(createServiceDependency()
+ .setService(IController.class, "(name=Controller)")
+ .setCallbacks("setController", "unsetController")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IInventoryShimInternalListener.class, "(!(scope=Global))")
+ .setCallbacks("setInventoryShimInternalListener",
+ "unsetInventoryShimInternalListener")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IInventoryShimInternalListener.class, "(scope=Global)")
+ .setCallbacks("setInventoryShimGlobalInternalListener",
+ "unsetInventoryShimGlobalInternalListener")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IInventoryShimExternalListener.class)
+ .setCallbacks("setInventoryShimExternalListener",
+ "unsetInventoryShimExternalListener")
+ .setRequired(false));
+ c.add(createServiceDependency()
+ .setService(IPluginOutConnectionService.class)
+ .setCallbacks("setIPluginOutConnectionService",
+ "unsetIPluginOutConnectionService")
+ .setRequired(false));
+ }
+
+ if (imp.equals(TopologyServiceShim.class)) {
+ c.setInterface(new String[] { IDiscoveryListener.class.getName(),
+ IContainerListener.class.getName(),
+ IRefreshInternalProvider.class.getName(),
+ IInventoryShimExternalListener.class.getName() }, null);
+ c.add(createServiceDependency()
+ .setService(ITopologyServiceShimListener.class)
+ .setCallbacks("setTopologyServiceShimListener",
+ "unsetTopologyServiceShimListener")
+ .setRequired(true));
+ c.add(createServiceDependency()
+ .setService(IOFStatisticsManager.class)
+ .setCallbacks("setStatisticsManager",
+ "unsetStatisticsManager").setRequired(false));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.openflowplugin.openflow.IDataPacketListen;
+import org.opendaylight.openflowplugin.openflow.IDataPacketMux;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageListener;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+
+public class DataPacketMuxDemux implements IContainerListener,
+ IMessageListener, IDataPacketMux, IInventoryShimExternalListener {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(DataPacketMuxDemux.class);
+ private IController controller = null;
+ private ConcurrentMap<Long, ISwitch> swID2ISwitch = new ConcurrentHashMap<Long, ISwitch>();
+ // Gives a map between a Container and all the DataPacket listeners on SAL
+ private ConcurrentMap<String, IPluginOutDataPacketService> pluginOutDataPacketServices = new ConcurrentHashMap<String, IPluginOutDataPacketService>();
+ // Gives a map between a NodeConnector and the containers to which it
+ // belongs
+ private ConcurrentMap<NodeConnector, List<String>> nc2Container = new ConcurrentHashMap<NodeConnector, List<String>>();
+ // Gives a map between a Container and the FlowSpecs on it
+ private ConcurrentMap<String, List<ContainerFlow>> container2FlowSpecs = new ConcurrentHashMap<String, List<ContainerFlow>>();
+ // Track local data packet listener
+ private List<IDataPacketListen> iDataPacketListen = new CopyOnWriteArrayList<IDataPacketListen>();
+ private IPluginOutConnectionService connectionOutService;
+
+ void setIDataPacketListen(IDataPacketListen s) {
+ if (this.iDataPacketListen != null) {
+ if (!this.iDataPacketListen.contains(s)) {
+ logger.debug("Added new IDataPacketListen");
+ this.iDataPacketListen.add(s);
+ }
+ }
+ }
+
+ void unsetIDataPacketListen(IDataPacketListen s) {
+ if (this.iDataPacketListen != null) {
+ if (this.iDataPacketListen.contains(s)) {
+ logger.debug("Removed IDataPacketListen");
+ this.iDataPacketListen.remove(s);
+ }
+ }
+ }
+
+ void setPluginOutDataPacketService(Map<String, Object> props,
+ IPluginOutDataPacketService s) {
+ if (props == null) {
+ logger.error("Didn't receive the service properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("containerName not supplied");
+ return;
+ }
+ if (this.pluginOutDataPacketServices != null) {
+ // It's expected only one SAL per container as long as the
+ // replication is done in the SAL implementation toward
+ // the different APPS
+ this.pluginOutDataPacketServices.put(containerName, s);
+ logger.debug("New outService for container: {}", containerName);
+ }
+ }
+
+ void unsetPluginOutDataPacketService(Map<String, Object> props,
+ IPluginOutDataPacketService s) {
+ if (props == null) {
+ logger.error("Didn't receive the service properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("containerName not supplied");
+ return;
+ }
+ if (this.pluginOutDataPacketServices != null) {
+ this.pluginOutDataPacketServices.remove(containerName);
+ logger.debug("Removed outService for container: {}", containerName);
+ }
+ }
+
+ void setController(IController s) {
+ logger.debug("Controller provider set in DATAPACKET SERVICES");
+ this.controller = s;
+ }
+
+ void unsetController(IController s) {
+ if (this.controller == s) {
+ logger.debug("Controller provider UNset in DATAPACKET SERVICES");
+ this.controller = null;
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ this.controller.addMessageListener(OFType.PACKET_IN, this);
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ this.controller.removeMessageListener(OFType.PACKET_IN, this);
+
+ // Clear state that may need to be reused on component restart
+ this.pluginOutDataPacketServices.clear();
+ this.nc2Container.clear();
+ this.container2FlowSpecs.clear();
+ this.controller = null;
+ this.swID2ISwitch.clear();
+ }
+
+ @Override
+ public void receive(ISwitch sw, OFMessage msg) {
+ if (sw == null || msg == null
+ || this.pluginOutDataPacketServices == null) {
+ // Something fishy, we cannot do anything
+ logger.debug(
+ "sw: {} and/or msg: {} and/or pluginOutDataPacketServices: {} is null!",
+ new Object[] { sw, msg, this.pluginOutDataPacketServices });
+ return;
+ }
+
+ Long ofSwitchID = Long.valueOf(sw.getId());
+ try {
+ Node n = new Node(Node.NodeIDType.OPENFLOW, ofSwitchID);
+ if (!connectionOutService.isLocal(n)) {
+ logger.debug("Connection service refused DataPacketMuxDemux receive {} {}", sw, msg);
+ return;
+ }
+ }
+ catch (Exception e) {
+ return;
+ }
+
+ if (msg instanceof OFPacketIn) {
+ OFPacketIn ofPacket = (OFPacketIn) msg;
+ Short ofPortID = Short.valueOf(ofPacket.getInPort());
+
+ try {
+ Node n = new Node(Node.NodeIDType.OPENFLOW, ofSwitchID);
+ NodeConnector p = PortConverter.toNodeConnector(ofPortID, n);
+ RawPacket dataPacket = new RawPacket(ofPacket.getPacketData());
+ dataPacket.setIncomingNodeConnector(p);
+
+ // Try to dispatch the packet locally, in here we will
+ // pass the parsed packet simply because once the
+ // packet infra is settled all the packets will passed
+ // around as parsed and read-only
+ for (int i = 0; i < this.iDataPacketListen.size(); i++) {
+ IDataPacketListen s = this.iDataPacketListen.get(i);
+ if (s.receiveDataPacket(dataPacket).equals(
+ PacketResult.CONSUME)) {
+ logger.trace("Consumed locally data packet");
+ return;
+ }
+ }
+
+ // Now dispatch the packet toward SAL at least for
+ // default container, we need to revisit this in a general
+ // slicing architecture refresh
+ IPluginOutDataPacketService defaultOutService = this.pluginOutDataPacketServices
+ .get(GlobalConstants.DEFAULT.toString());
+ if (defaultOutService != null) {
+ defaultOutService.receiveDataPacket(dataPacket);
+ if (logger.isTraceEnabled()) {
+ logger.trace(
+ "Dispatched to apps a frame of size: {} on " +
+ "container: {}: {}", new Object[] {
+ ofPacket.getPacketData().length,
+ GlobalConstants.DEFAULT.toString(),
+ HexEncode.bytesToHexString(dataPacket
+ .getPacketData()) });
+ }
+ }
+ // Now check the mapping between nodeConnector and
+ // Container and later on optimally filter based on
+ // flowSpec
+ List<String> containersRX = this.nc2Container.get(p);
+ if (containersRX != null) {
+ for (int i = 0; i < containersRX.size(); i++) {
+ String container = containersRX.get(i);
+ IPluginOutDataPacketService s = this.pluginOutDataPacketServices
+ .get(container);
+ if (s != null) {
+ // TODO add filtering on a per-flowSpec base
+ s.receiveDataPacket(dataPacket);
+ if (logger.isTraceEnabled()) {
+ logger.trace(
+ "Dispatched to apps a frame of size: {}" +
+ " on container: {}: {}", new Object[] {
+ ofPacket.getPacketData().length,
+ container,
+ HexEncode.bytesToHexString(dataPacket
+ .getPacketData()) });
+ }
+ }
+ }
+ }
+
+ // This is supposed to be the catch all for all the
+ // DataPacket hence we will assume it has been handled
+ return;
+ } catch (ConstructionException cex) {
+ }
+
+ // If we reach this point something went wrong.
+ return;
+ } else {
+ // We don't care about non-data packets
+ return;
+ }
+ }
+
+ @Override
+ public void transmitDataPacket(RawPacket outPkt) {
+ // Sanity check area
+ if (outPkt == null) {
+ logger.debug("outPkt is null!");
+ return;
+ }
+
+ NodeConnector outPort = outPkt.getOutgoingNodeConnector();
+ if (outPort == null) {
+ logger.debug("outPort is null! outPkt: {}", outPkt);
+ return;
+ }
+
+ if (!connectionOutService.isLocal(outPort.getNode())) {
+ logger.debug("data packets will not be sent to {} in a non-master controller", outPort.toString());
+ return;
+ }
+
+
+ if (!outPort.getType().equals(
+ NodeConnector.NodeConnectorIDType.OPENFLOW)) {
+ // The output Port is not of type OpenFlow
+ logger.debug("outPort is not OF Type! outPort: {}", outPort);
+ return;
+ }
+
+ Short port = (Short) outPort.getID();
+ Long swID = (Long) outPort.getNode().getID();
+ ISwitch sw = this.swID2ISwitch.get(swID);
+
+ if (sw == null) {
+ // If we cannot get the controller descriptor we cannot even
+ // send out the frame
+ logger.debug("swID: {} - sw is null!", swID);
+ return;
+ }
+
+ byte[] data = outPkt.getPacketData();
+ // build action
+ OFActionOutput action = new OFActionOutput().setPort(port);
+ // build packet out
+ OFPacketOut po = new OFPacketOut()
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setInPort(OFPort.OFPP_NONE)
+ .setActions(Collections.singletonList((OFAction) action))
+ .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
+
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength()
+ + data.length);
+ po.setPacketData(data);
+
+ // send PACKET_OUT at high priority
+ sw.asyncFastSend(po);
+ logger.trace("Transmitted a frame of size: {}", data.length);
+ }
+
+ public void addNode(Node node, Set<Property> props) {
+ if (node == null) {
+ logger.debug("node is null!");
+ return;
+ }
+
+ long sid = (Long) node.getID();
+ ISwitch sw = controller.getSwitches().get(sid);
+ if (sw == null) {
+ logger.debug("sid: {} - sw is null!", sid);
+ return;
+ }
+ this.swID2ISwitch.put(sw.getId(), sw);
+ }
+
+ public void removeNode(Node node) {
+ if (node == null) {
+ logger.debug("node is null!");
+ return;
+ }
+
+ long sid = (Long) node.getID();
+ ISwitch sw = controller.getSwitches().get(sid);
+ if (sw == null) {
+ logger.debug("sid: {} - sw is null!", sid);
+ return;
+ }
+ this.swID2ISwitch.remove(sw.getId());
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag,
+ short newTag, UpdateType t) {
+ // Do nothing
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName,
+ ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
+ if (this.container2FlowSpecs == null) {
+ logger.error("container2FlowSpecs is NULL");
+ return;
+ }
+ List<ContainerFlow> fSpecs = this.container2FlowSpecs
+ .get(containerName);
+ if (fSpecs == null) {
+ fSpecs = new CopyOnWriteArrayList<ContainerFlow>();
+ }
+ switch (t) {
+ case ADDED:
+ if (!fSpecs.contains(previousFlow)) {
+ fSpecs.add(previousFlow);
+ }
+ break;
+ case REMOVED:
+ if (fSpecs.contains(previousFlow)) {
+ fSpecs.remove(previousFlow);
+ }
+ break;
+ case CHANGED:
+ break;
+ }
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector p,
+ UpdateType t) {
+ if (this.nc2Container == null) {
+ logger.error("nc2Container is NULL");
+ return;
+ }
+ List<String> containers = this.nc2Container.get(p);
+ if (containers == null) {
+ containers = new CopyOnWriteArrayList<String>();
+ }
+ boolean updateMap = false;
+ switch (t) {
+ case ADDED:
+ if (!containers.contains(containerName)) {
+ containers.add(containerName);
+ updateMap = true;
+ }
+ break;
+ case REMOVED:
+ if (containers.contains(containerName)) {
+ containers.remove(containerName);
+ updateMap = true;
+ }
+ break;
+ case CHANGED:
+ break;
+ }
+ if (updateMap) {
+ if (containers.isEmpty()) {
+ // Do cleanup to reduce memory footprint if no
+ // elements to be tracked
+ this.nc2Container.remove(p);
+ } else {
+ this.nc2Container.put(p, containers);
+ }
+ }
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+ // do nothing
+ }
+
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ switch (type) {
+ case ADDED:
+ addNode(node, props);
+ break;
+ case REMOVED:
+ removeNode(node);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props) {
+ // do nothing
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.IPluginInDataPacketService;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.openflowplugin.openflow.IDataPacketMux;
+
+public class DataPacketServices implements IPluginInDataPacketService {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(DataPacketServices.class);
+ private IDataPacketMux iDataPacketMux = null;
+ private IPluginOutConnectionService connectionOutService;
+
+ void setIDataPacketMux(IDataPacketMux s) {
+ this.iDataPacketMux = s;
+ }
+
+ void unsetIDataPacketMux(IDataPacketMux s) {
+ if (this.iDataPacketMux == s) {
+ this.iDataPacketMux = null;
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+ @Override
+ public void transmitDataPacket(RawPacket outPkt) {
+ NodeConnector nc = outPkt.getOutgoingNodeConnector();
+ if (connectionOutService != null && connectionOutService.isLocal(nc.getNode())) {
+ this.iDataPacketMux.transmitDataPacket(outPkt);
+ } else {
+ logger.debug("{} is dropped in the controller "+outPkt);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.openflow.protocol.statistics.OFDescriptionStatistics;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for converting openflow description statistics into SAL
+ * NodeDescription object
+ */
+public class DescStatisticsConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(DescStatisticsConverter.class);
+ NodeDescription hwDesc;
+ OFDescriptionStatistics ofDesc;
+
+ public DescStatisticsConverter(List<OFStatistics> statsList) {
+ this.hwDesc = null;
+ this.ofDesc = (statsList == null || statsList.isEmpty())?
+ null : (OFDescriptionStatistics) statsList.get(0);
+ }
+ public DescStatisticsConverter(OFDescriptionStatistics desc) {
+ this.hwDesc = null;
+ this.ofDesc = desc;
+ }
+
+ public NodeDescription getHwDescription() {
+ if (hwDesc == null && ofDesc != null) {
+ hwDesc = new NodeDescription();
+ hwDesc.setManufacturer(ofDesc.getManufacturerDescription());
+ hwDesc.setHardware(ofDesc.getHardwareDescription());
+ hwDesc.setSoftware(ofDesc.getSoftwareDescription());
+ hwDesc.setDescription(ofDesc.getDatapathDescription());
+ hwDesc.setSerialNumber(ofDesc.getSerialNumber());
+ }
+ log.trace("OFDescriptionStatistics: {}", ofDesc);
+ log.trace("NodeDescription: {}", hwDesc);
+ return hwDesc;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.openflow.protocol.OFPhysicalPort;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.State;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LLDPTLV;
+import org.opendaylight.controller.sal.packet.LinkEncap;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.openflowplugin.openflow.IDataPacketListen;
+import org.opendaylight.openflowplugin.openflow.IDataPacketMux;
+import org.opendaylight.openflowplugin.openflow.IDiscoveryListener;
+import org.opendaylight.openflowplugin.openflow.IInventoryProvider;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+
+/**
+ * The class describes neighbor discovery service for an OpenFlow network.
+ */
+public class DiscoveryService implements IInventoryShimExternalListener, IDataPacketListen, IContainerListener,
+ CommandProvider {
+ private static Logger logger = LoggerFactory.getLogger(DiscoveryService.class);
+ private IController controller = null;
+ private IDiscoveryListener discoveryListener = null;
+ private IInventoryProvider inventoryProvider = null;
+ private IDataPacketMux iDataPacketMux = null;
+ // High priority list containing newly added ports which will be served first
+ private List<NodeConnector> readyListHi = null;
+ // List containing all the ports which will be served periodically
+ private List<NodeConnector> readyListLo = null;
+ // Staging area during quiet period
+ private List<NodeConnector> stagingList = null;
+ // Wait for next discovery packet. The map contains the time elapsed since
+ // the last received LLDP frame on each node connector
+ private ConcurrentMap<NodeConnector, Integer> holdTime = null;
+ // Allow one more retry for newly added ports. This map contains the time
+ // period elapsed since last discovery pkt transmission on the port.
+ private ConcurrentMap<NodeConnector, Integer> elapsedTime = null;
+ // OpenFlow edges keyed by head connector
+ private ConcurrentMap<NodeConnector, Edge> edgeMap = null;
+ // The map contains aging entry keyed by head connector of Production edge
+ private ConcurrentMap<NodeConnector, Integer> agingMap = null;
+ // Production edges keyed by head connector
+ private ConcurrentMap<NodeConnector, Edge> prodMap = null;
+
+ private Timer discoveryTimer;
+ private DiscoveryTimerTask discoveryTimerTask;
+ private final static long discoveryTimerTick = 2L * 1000; // per tick in msec
+ private int discoveryTimerTickCount = 0; // main tick counter
+ // Max # of ports handled in one batch
+ private int discoveryBatchMaxPorts;
+ // Periodically restart batching process
+ private int discoveryBatchRestartTicks;
+ private int discoveryBatchPausePeriod = 2;
+ // Pause after this point
+ private int discoveryBatchPauseTicks;
+ private int discoveryTimeoutTicks;
+ private int discoveryThresholdTicks;
+ private int discoveryAgeoutTicks;
+ // multiple of discoveryBatchRestartTicks
+ private int discoveryConsistencyCheckMultiple = 2;
+ // CC tick counter
+ private int discoveryConsistencyCheckTickCount;
+ // # of times CC gets called
+ private int discoveryConsistencyCheckCallingTimes = 0;
+ // # of cases CC corrected
+ private int discoveryConsistencyCheckCorrected = 0;
+ // Enable or disable CC
+ private boolean discoveryConsistencyCheckEnabled = true;
+ // Enable or disable aging
+ private boolean discoveryAgingEnabled = true;
+ // Global flag to enable or disable LLDP snooping
+ private boolean discoverySnoopingEnabled = true;
+ // The list of ports that will not do LLDP snooping
+ private List<NodeConnector> discoverySnoopingDisableList;
+ private BlockingQueue<NodeConnector> transmitQ;
+ private Thread transmitThread;
+ private Boolean throttling = false; // if true, no more batching.
+ private volatile Boolean shuttingDown = false;
+
+ private LLDPTLV chassisIdTlv, portIdTlv, ttlTlv, customTlv;
+ private IPluginOutConnectionService connectionOutService;
+
+ class DiscoveryTransmit implements Runnable {
+ private final BlockingQueue<NodeConnector> transmitQ;
+
+ DiscoveryTransmit(BlockingQueue<NodeConnector> transmitQ) {
+ this.transmitQ = transmitQ;
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ NodeConnector nodeConnector = transmitQ.take();
+ RawPacket outPkt = createDiscoveryPacket(nodeConnector);
+ sendDiscoveryPacket(nodeConnector, outPkt);
+ nodeConnector = null;
+ } catch (InterruptedException e1) {
+ logger.warn("DiscoveryTransmit interupted", e1.getMessage());
+ if (shuttingDown) {
+ return;
+ }
+ } catch (Exception e2) {
+ logger.error("", e2);
+ }
+ }
+ }
+ }
+
+ class DiscoveryTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ checkTimeout();
+ checkAging();
+ doConsistencyCheck();
+ doDiscovery();
+ }
+ }
+
+ public enum DiscoveryPeriod {
+ INTERVAL (300),
+ AGEOUT (120),
+ THRESHOLD (30);
+
+ private int time; // sec
+ private int tick; // tick
+
+ DiscoveryPeriod(int time) {
+ this.time = time;
+ this.tick = time2Tick(time);
+ }
+
+ public int getTime() {
+ return time;
+ }
+
+ public void setTime(int time) {
+ this.time = time;
+ this.tick = time2Tick(time);
+ }
+
+ public int getTick() {
+ return tick;
+ }
+
+ public void setTick(int tick) {
+ this.time = tick2Time(tick);
+ this.tick = tick;
+ }
+
+ private int time2Tick(int time) {
+ return (int) (time / (discoveryTimerTick / 1000));
+ }
+
+ private int tick2Time(int tick) {
+ return (int) (tick * (discoveryTimerTick / 1000));
+ }
+ }
+
+ private RawPacket createDiscoveryPacket(NodeConnector nodeConnector) {
+ String nodeId = HexEncode.longToHexString((Long) nodeConnector.getNode().getID());
+
+ // Create LLDP ChassisID TLV
+ byte[] cidValue = LLDPTLV.createChassisIDTLVValue(nodeId);
+ chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()).setLength((short) cidValue.length)
+ .setValue(cidValue);
+
+ // Create LLDP PortID TLV
+ String portId = nodeConnector.getNodeConnectorIDString();
+ byte[] pidValue = LLDPTLV.createPortIDTLVValue(portId);
+ portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()).setLength((short) pidValue.length).setValue(pidValue);
+
+ // Create LLDP Custom TLV
+ byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnector.toString());
+ customTlv.setType(LLDPTLV.TLVType.Custom.getValue()).setLength((short) customValue.length)
+ .setValue(customValue);
+
+ // Create LLDP Custom Option list
+ List<LLDPTLV> customList = new ArrayList<LLDPTLV>();
+ customList.add(customTlv);
+
+ // Create discovery pkt
+ LLDP discoveryPkt = new LLDP();
+ discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setOptionalTLVList(customList);
+
+ RawPacket rawPkt = null;
+ try {
+ // Create ethernet pkt
+ byte[] sourceMac = getSourceMACFromNodeID(nodeId);
+ Ethernet ethPkt = new Ethernet();
+ ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(LLDP.LLDPMulticastMac)
+ .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
+
+ byte[] data = ethPkt.serialize();
+ rawPkt = new RawPacket(data);
+ rawPkt.setOutgoingNodeConnector(nodeConnector);
+ } catch (ConstructionException cex) {
+ logger.warn("RawPacket creation caught exception {}", cex.getMessage());
+ } catch (Exception e) {
+ logger.error("Failed to serialize the LLDP packet: " + e);
+ }
+
+ return rawPkt;
+ }
+
+ private void sendDiscoveryPacket(NodeConnector nodeConnector, RawPacket outPkt) {
+ if (nodeConnector == null) {
+ logger.debug("Can not send discovery packet out since nodeConnector is null");
+ return;
+ }
+
+ if (!connectionOutService.isLocal(nodeConnector.getNode())) {
+ logger.debug("Discoery packets will not be sent to {} in a non-master controller", nodeConnector.toString());
+ return;
+ }
+
+ if (outPkt == null) {
+ logger.debug("Can not send discovery packet out since outPkt is null");
+ return;
+ }
+
+ long sid = (Long) nodeConnector.getNode().getID();
+ ISwitch sw = controller.getSwitches().get(sid);
+
+ if (sw == null) {
+ logger.debug("Can not send discovery packet out since switch {} is null", sid);
+ return;
+ }
+
+ if (!sw.isOperational()) {
+ logger.debug("Can not send discovery packet out since switch {} is not operational", sw);
+ return;
+ }
+
+ if (this.iDataPacketMux == null) {
+ logger.debug("Can not send discovery packet out since DataPacket service is not available");
+ return;
+ }
+
+ logger.trace("Sending topology discovery pkt thru {}", nodeConnector);
+ this.iDataPacketMux.transmitDataPacket(outPkt);
+ }
+
+ @Override
+ public PacketResult receiveDataPacket(RawPacket inPkt) {
+ if (inPkt == null) {
+ logger.debug("Ignoring null packet");
+ return PacketResult.IGNORED;
+ }
+
+ byte[] data = inPkt.getPacketData();
+ if (data.length <= 0) {
+ logger.trace("Ignoring zero length packet");
+ return PacketResult.IGNORED;
+ }
+
+ if (!inPkt.getEncap().equals(LinkEncap.ETHERNET)) {
+ logger.trace("Ignoring non ethernet packet");
+ return PacketResult.IGNORED;
+ }
+
+ NodeConnector nodeConnector = inPkt.getIncomingNodeConnector();
+ if (((Short) nodeConnector.getID()).equals(NodeConnector.SPECIALNODECONNECTORID)) {
+ logger.trace("Ignoring ethernet packet received on special port: "
+ + inPkt.getIncomingNodeConnector().toString());
+ return PacketResult.IGNORED;
+ }
+
+ if (!connectionOutService.isLocal(nodeConnector.getNode())) {
+ logger.debug("Discoery packets will not be processed from {} in a non-master controller", nodeConnector.toString());
+ return PacketResult.IGNORED;
+ }
+
+ Ethernet ethPkt = new Ethernet();
+ try {
+ ethPkt.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+ } catch (Exception e) {
+ logger.warn("Failed to decode LLDP packet from {}: {}", inPkt.getIncomingNodeConnector(), e);
+ return PacketResult.IGNORED;
+ }
+
+ if (ethPkt.getPayload() instanceof LLDP) {
+ NodeConnector dst = inPkt.getIncomingNodeConnector();
+ if (isEnabled(dst)) {
+ if (!processDiscoveryPacket(dst, ethPkt)) {
+ // Snoop the discovery pkt if not generated from us
+ snoopDiscoveryPacket(dst, ethPkt);
+ }
+ return PacketResult.CONSUME;
+ }
+ }
+ return PacketResult.IGNORED;
+ }
+
+ /*
+ * Snoop incoming discovery frames generated by the production network
+ * neighbor switch
+ */
+ private void snoopDiscoveryPacket(NodeConnector dstNodeConnector, Ethernet ethPkt) {
+ if (!this.discoverySnoopingEnabled || discoverySnoopingDisableList.contains(dstNodeConnector)) {
+ logger.trace("Discarded received discovery packet on {} since snooping is turned off", dstNodeConnector);
+ return;
+ }
+
+ if ((dstNodeConnector == null) || (ethPkt == null)) {
+ logger.trace("Quit snooping discovery packet: Null node connector or packet");
+ return;
+ }
+
+ LLDP lldp = (LLDP) ethPkt.getPayload();
+
+ try {
+ String nodeId = LLDPTLV.getHexStringValue(lldp.getChassisId().getValue(), lldp.getChassisId().getLength());
+ String portId = LLDPTLV.getStringValue(lldp.getPortId().getValue(), lldp.getPortId().getLength());
+ byte[] systemNameBytes = null;
+ // get system name if present in the LLDP pkt
+ for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
+ if (lldptlv.getType() == LLDPTLV.TLVType.SystemName.getValue()) {
+ systemNameBytes = lldptlv.getValue();
+ break;
+ }
+ }
+ String nodeName = (systemNameBytes == null) ? nodeId
+ : new String(systemNameBytes, Charset.defaultCharset());
+ Node srcNode = new Node(Node.NodeIDType.PRODUCTION, nodeName);
+ NodeConnector srcNodeConnector = NodeConnectorCreator.createNodeConnector(
+ NodeConnector.NodeConnectorIDType.PRODUCTION, portId, srcNode);
+
+ Edge edge = null;
+ Set<Property> props = null;
+ edge = new Edge(srcNodeConnector, dstNodeConnector);
+ props = getProps(dstNodeConnector);
+
+ updateProdEdge(edge, props);
+ } catch (Exception e) {
+ logger.warn("Caught exception ", e);
+ }
+ }
+
+ /*
+ * Handle discovery frames generated by our controller
+ *
+ * @return true if it's a success
+ */
+ private boolean processDiscoveryPacket(NodeConnector dstNodeConnector, Ethernet ethPkt) {
+ if ((dstNodeConnector == null) || (ethPkt == null)) {
+ logger.trace("Ignoring processing of discovery packet: Null node connector or packet");
+ return false;
+ }
+
+ logger.trace("Handle discovery packet {} from {}", ethPkt, dstNodeConnector);
+
+ LLDP lldp = (LLDP) ethPkt.getPayload();
+
+ List<LLDPTLV> optionalTLVList = lldp.getOptionalTLVList();
+ if (optionalTLVList == null) {
+ logger.info("The discovery packet with null custom option from {}", dstNodeConnector);
+ return false;
+ }
+
+ Node srcNode = null;
+ NodeConnector srcNodeConnector = null;
+ for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
+ if (lldptlv.getType() == LLDPTLV.TLVType.Custom.getValue()) {
+ String ncString = LLDPTLV.getCustomString(lldptlv.getValue(), lldptlv.getLength());
+ srcNodeConnector = NodeConnector.fromString(ncString);
+ if (srcNodeConnector != null) {
+ srcNode = srcNodeConnector.getNode();
+ }
+ }
+ }
+
+ if ((srcNode == null) || (srcNodeConnector == null)) {
+ logger.trace("Received non-controller generated discovery packet from {}", dstNodeConnector);
+ return false;
+ }
+
+ // push it out to Topology
+ Edge edge = null;
+ Set<Property> props = null;
+ try {
+ edge = new Edge(srcNodeConnector, dstNodeConnector);
+ props = getProps(dstNodeConnector);
+ } catch (ConstructionException e) {
+ logger.error("Caught exception ", e);
+ }
+ addEdge(edge, props);
+
+ logger.trace("Received discovery packet for Edge {}", edge);
+
+ return true;
+ }
+
+ public Map<String, Property> getPropMap(NodeConnector nodeConnector) {
+ if (nodeConnector == null) {
+ return null;
+ }
+
+ if (inventoryProvider == null) {
+ return null;
+ }
+
+ Map<NodeConnector, Map<String, Property>> props = inventoryProvider.getNodeConnectorProps(false);
+ if (props == null) {
+ return null;
+ }
+
+ return props.get(nodeConnector);
+ }
+
+ public Property getProp(NodeConnector nodeConnector, String propName) {
+ Map<String, Property> propMap = getPropMap(nodeConnector);
+ if (propMap == null) {
+ return null;
+ }
+
+ Property prop = propMap.get(propName);
+ return prop;
+ }
+
+ public Set<Property> getProps(NodeConnector nodeConnector) {
+ Map<String, Property> propMap = getPropMap(nodeConnector);
+ if (propMap == null) {
+ return null;
+ }
+
+ Set<Property> props = new HashSet<Property>(propMap.values());
+ return props;
+ }
+
+ private boolean isEnabled(NodeConnector nodeConnector) {
+ if (nodeConnector == null) {
+ return false;
+ }
+
+ Config config = (Config) getProp(nodeConnector, Config.ConfigPropName);
+ State state = (State) getProp(nodeConnector, State.StatePropName);
+ return ((config != null) && (config.getValue() == Config.ADMIN_UP) && (state != null) && (state.getValue() == State.EDGE_UP));
+ }
+
+ private boolean isTracked(NodeConnector nodeConnector) {
+ if (readyListHi.contains(nodeConnector)) {
+ return true;
+ }
+
+ if (readyListLo.contains(nodeConnector)) {
+ return true;
+ }
+
+ if (holdTime.keySet().contains(nodeConnector)) {
+ return true;
+ }
+
+ if (stagingList.contains(nodeConnector)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private Set<NodeConnector> getWorkingSet() {
+ Set<NodeConnector> workingSet = new HashSet<NodeConnector>();
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+
+ for (NodeConnector nodeConnector : readyListHi) {
+ if (isOverLimit(workingSet.size())) {
+ break;
+ }
+
+ workingSet.add(nodeConnector);
+ removeSet.add(nodeConnector);
+
+ // Put it in the map and start the timer. It may need retry.
+ elapsedTime.put(nodeConnector, 0);
+ }
+ readyListHi.removeAll(removeSet);
+
+ removeSet.clear();
+ for (NodeConnector nodeConnector : readyListLo) {
+ if (isOverLimit(workingSet.size())) {
+ break;
+ }
+
+ workingSet.add(nodeConnector);
+ removeSet.add(nodeConnector);
+ }
+ readyListLo.removeAll(removeSet);
+
+ return workingSet;
+ }
+
+ private Boolean isOverLimit(int size) {
+ return ((size >= discoveryBatchMaxPorts) && !throttling);
+ }
+
+ private void addDiscovery() {
+ Map<Long, ISwitch> switches = controller.getSwitches();
+ Set<Long> sidSet = switches.keySet();
+ if (sidSet == null) {
+ return;
+ }
+ for (Long sid : sidSet) {
+ Node node = NodeCreator.createOFNode(sid);
+ addDiscovery(node);
+ }
+ }
+
+ private void addDiscovery(Node node) {
+ Map<Long, ISwitch> switches = controller.getSwitches();
+ ISwitch sw = switches.get(node.getID());
+ List<OFPhysicalPort> ports = sw.getEnabledPorts();
+ if (ports == null) {
+ return;
+ }
+ for (OFPhysicalPort port : ports) {
+ NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
+ if (!readyListHi.contains(nodeConnector)) {
+ readyListHi.add(nodeConnector);
+ }
+ }
+ }
+
+ private void addDiscovery(NodeConnector nodeConnector) {
+ if (isTracked(nodeConnector)) {
+ return;
+ }
+
+ readyListHi.add(nodeConnector);
+ }
+
+ private Set<NodeConnector> getRemoveSet(Collection<NodeConnector> c, Node node) {
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+ if (c == null) {
+ return removeSet;
+ }
+ for (NodeConnector nodeConnector : c) {
+ if (node.equals(nodeConnector.getNode())) {
+ Edge edge1 = edgeMap.get(nodeConnector);
+ if (edge1 != null) {
+ removeSet.add(nodeConnector);
+
+ // check reverse direction
+ Edge edge2 = edgeMap.get(edge1.getTailNodeConnector());
+ if ((edge2 != null) && node.equals(edge2.getTailNodeConnector().getNode())) {
+ removeSet.add(edge2.getHeadNodeConnector());
+ }
+ }
+ }
+ }
+ return removeSet;
+ }
+
+ private void removeDiscovery(Node node) {
+ Set<NodeConnector> removeSet;
+
+ removeSet = getRemoveSet(readyListHi, node);
+ readyListHi.removeAll(removeSet);
+
+ removeSet = getRemoveSet(readyListLo, node);
+ readyListLo.removeAll(removeSet);
+
+ removeSet = getRemoveSet(stagingList, node);
+ stagingList.removeAll(removeSet);
+
+ removeSet = getRemoveSet(holdTime.keySet(), node);
+ for (NodeConnector nodeConnector : removeSet) {
+ holdTime.remove(nodeConnector);
+ }
+
+ removeSet = getRemoveSet(edgeMap.keySet(), node);
+ for (NodeConnector nodeConnector : removeSet) {
+ removeEdge(nodeConnector, false);
+ }
+
+ removeSet = getRemoveSet(prodMap.keySet(), node);
+ for (NodeConnector nodeConnector : removeSet) {
+ removeProdEdge(nodeConnector);
+ }
+ }
+
+ private void removeDiscovery(NodeConnector nodeConnector) {
+ readyListHi.remove(nodeConnector);
+ readyListLo.remove(nodeConnector);
+ stagingList.remove(nodeConnector);
+ holdTime.remove(nodeConnector);
+ removeEdge(nodeConnector, false);
+ removeProdEdge(nodeConnector);
+ }
+
+ private void checkTimeout() {
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+ int ticks;
+
+ Set<NodeConnector> monitorSet = holdTime.keySet();
+ if (monitorSet != null) {
+ for (NodeConnector nodeConnector : monitorSet) {
+ ticks = holdTime.get(nodeConnector);
+ holdTime.put(nodeConnector, ++ticks);
+ if (ticks >= discoveryTimeoutTicks) {
+ // timeout the edge
+ removeSet.add(nodeConnector);
+ logger.trace("Discovery timeout {}", nodeConnector);
+ }
+ }
+ }
+
+ for (NodeConnector nodeConnector : removeSet) {
+ removeEdge(nodeConnector);
+ }
+
+ Set<NodeConnector> retrySet = new HashSet<NodeConnector>();
+ Set<NodeConnector> ncSet = elapsedTime.keySet();
+ if ((ncSet != null) && (ncSet.size() > 0)) {
+ for (NodeConnector nodeConnector : ncSet) {
+ ticks = elapsedTime.get(nodeConnector);
+ elapsedTime.put(nodeConnector, ++ticks);
+ if (ticks >= discoveryThresholdTicks) {
+ retrySet.add(nodeConnector);
+ }
+ }
+
+ for (NodeConnector nodeConnector : retrySet) {
+ // Allow one more retry
+ readyListLo.add(nodeConnector);
+ elapsedTime.remove(nodeConnector);
+ if (connectionOutService.isLocal(nodeConnector.getNode())) {
+ transmitQ.add(nodeConnector);
+ }
+ }
+ }
+ }
+
+ private void checkAging() {
+ if (!discoveryAgingEnabled) {
+ return;
+ }
+
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+ int ticks;
+
+ Set<NodeConnector> agingSet = agingMap.keySet();
+ if (agingSet != null) {
+ for (NodeConnector nodeConnector : agingSet) {
+ ticks = agingMap.get(nodeConnector);
+ agingMap.put(nodeConnector, ++ticks);
+ if (ticks > discoveryAgeoutTicks) {
+ // age out the edge
+ removeSet.add(nodeConnector);
+ logger.trace("Discovery age out {}", nodeConnector);
+ }
+ }
+ }
+
+ for (NodeConnector nodeConnector : removeSet) {
+ removeProdEdge(nodeConnector);
+ }
+ }
+
+ private void doDiscovery() {
+ if (++discoveryTimerTickCount <= discoveryBatchPauseTicks) {
+ for (NodeConnector nodeConnector : getWorkingSet()) {
+ if (connectionOutService.isLocal(nodeConnector.getNode())) {
+ transmitQ.add(nodeConnector);
+ // Move to staging area after it's served
+ if (!stagingList.contains(nodeConnector)) {
+ stagingList.add(nodeConnector);
+ }
+ }
+ }
+ } else if (discoveryTimerTickCount >= discoveryBatchRestartTicks) {
+ discoveryTimerTickCount = 0;
+ for (NodeConnector nodeConnector : stagingList) {
+ if (!readyListLo.contains(nodeConnector)) {
+ readyListLo.add(nodeConnector);
+ }
+ }
+ stagingList.removeAll(readyListLo);
+ }
+ }
+
+ private void doConsistencyCheck() {
+ if (!discoveryConsistencyCheckEnabled) {
+ return;
+ }
+
+ if (++discoveryConsistencyCheckTickCount % getDiscoveryConsistencyCheckInterval() != 0) {
+ return;
+ }
+
+ discoveryConsistencyCheckCallingTimes++;
+
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+ Set<NodeConnector> ncSet = edgeMap.keySet();
+ if (ncSet == null) {
+ return;
+ }
+ for (NodeConnector nodeConnector : ncSet) {
+ if (!isEnabled(nodeConnector)) {
+ removeSet.add(nodeConnector);
+ discoveryConsistencyCheckCorrected++;
+ logger.debug("ConsistencyChecker: remove disabled {}", nodeConnector);
+ continue;
+ }
+
+ if (!isTracked(nodeConnector)) {
+ stagingList.add(nodeConnector);
+ discoveryConsistencyCheckCorrected++;
+ logger.debug("ConsistencyChecker: add back untracked {}", nodeConnector);
+ continue;
+ }
+ }
+
+ for (NodeConnector nodeConnector : removeSet) {
+ removeEdge(nodeConnector, false);
+ }
+
+ // remove stale entries
+ removeSet.clear();
+ for (NodeConnector nodeConnector : stagingList) {
+ if (!isEnabled(nodeConnector)) {
+ removeSet.add(nodeConnector);
+ discoveryConsistencyCheckCorrected++;
+ logger.debug("ConsistencyChecker: remove disabled {}", nodeConnector);
+ }
+ }
+ stagingList.removeAll(removeSet);
+
+ // Get a snapshot of all the existing switches
+ Map<Long, ISwitch> switches = this.controller.getSwitches();
+ for (ISwitch sw : switches.values()) {
+ for (OFPhysicalPort port : sw.getEnabledPorts()) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
+ if (!isTracked(nodeConnector)) {
+ stagingList.add(nodeConnector);
+ discoveryConsistencyCheckCorrected++;
+ logger.debug("ConsistencyChecker: add back untracked {}", nodeConnector);
+ }
+ }
+ }
+ }
+
+ private void addEdge(Edge edge, Set<Property> props) {
+ if (edge == null) {
+ return;
+ }
+
+ NodeConnector src = edge.getTailNodeConnector();
+ NodeConnector dst = edge.getHeadNodeConnector();
+ if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ holdTime.put(dst, 0);
+ } else {
+ agingMap.put(dst, 0);
+ }
+ elapsedTime.remove(src);
+
+ // notify
+ updateEdge(edge, UpdateType.ADDED, props);
+ logger.trace("Add edge {}", edge);
+ }
+
+ /**
+ * Update Production Edge
+ *
+ * @param edge
+ * The Production Edge
+ * @param props
+ * Properties associated with the edge
+ */
+ private void updateProdEdge(Edge edge, Set<Property> props) {
+ NodeConnector edgePort = edge.getHeadNodeConnector();
+
+ /* Do not update in case there is an existing OpenFlow link */
+ if (edgeMap.get(edgePort) != null) {
+ logger.trace("Discarded edge {} since there is an existing OF link {}", edge, edgeMap.get(edgePort));
+ return;
+ }
+
+ /* Look for any existing Production Edge */
+ Edge oldEdge = prodMap.get(edgePort);
+ if (oldEdge == null) {
+ /* Let's add a new one */
+ addEdge(edge, props);
+ } else if (!edge.equals(oldEdge)) {
+ /* Remove the old one first */
+ removeProdEdge(oldEdge.getHeadNodeConnector());
+ /* Then add the new one */
+ addEdge(edge, props);
+ } else {
+ /* o/w, just reset the aging timer */
+ NodeConnector dst = edge.getHeadNodeConnector();
+ agingMap.put(dst, 0);
+ }
+ }
+
+ /**
+ * Remove Production Edge for a given edge port
+ *
+ * @param edgePort
+ * The OF edge port
+ */
+ private void removeProdEdge(NodeConnector edgePort) {
+ agingMap.remove(edgePort);
+
+ Edge edge = null;
+ Set<NodeConnector> prodKeySet = prodMap.keySet();
+ if ((prodKeySet != null) && (prodKeySet.contains(edgePort))) {
+ edge = prodMap.get(edgePort);
+ prodMap.remove(edgePort);
+ }
+
+ // notify Topology
+ if (this.discoveryListener != null) {
+ this.discoveryListener.notifyEdge(edge, UpdateType.REMOVED, null);
+ }
+ logger.trace("Remove edge {}", edge);
+ }
+
+ /*
+ * Remove OpenFlow edge
+ */
+ private void removeEdge(NodeConnector nodeConnector, boolean stillEnabled) {
+ holdTime.remove(nodeConnector);
+ readyListLo.remove(nodeConnector);
+ readyListHi.remove(nodeConnector);
+
+ if (stillEnabled) {
+ // keep discovering
+ if (!stagingList.contains(nodeConnector)) {
+ stagingList.add(nodeConnector);
+ }
+ } else {
+ // stop it
+ stagingList.remove(nodeConnector);
+ }
+
+ Edge edge = null;
+ Set<NodeConnector> edgeKeySet = edgeMap.keySet();
+ if ((edgeKeySet != null) && (edgeKeySet.contains(nodeConnector))) {
+ edge = edgeMap.get(nodeConnector);
+ edgeMap.remove(nodeConnector);
+ }
+
+ // notify Topology
+ if (this.discoveryListener != null) {
+ this.discoveryListener.notifyEdge(edge, UpdateType.REMOVED, null);
+ }
+ logger.trace("Remove {}", nodeConnector);
+ }
+
+ private void removeEdge(NodeConnector nodeConnector) {
+ removeEdge(nodeConnector, isEnabled(nodeConnector));
+ }
+
+ private void updateEdge(Edge edge, UpdateType type, Set<Property> props) {
+ if (discoveryListener == null) {
+ return;
+ }
+
+ this.discoveryListener.notifyEdge(edge, type, props);
+
+ NodeConnector src = edge.getTailNodeConnector(), dst = edge.getHeadNodeConnector();
+ if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ if (type == UpdateType.ADDED) {
+ edgeMap.put(dst, edge);
+ } else {
+ edgeMap.remove(dst);
+ }
+ } else {
+ /*
+ * Save Production edge into different DB keyed by the Edge port
+ */
+ if (type == UpdateType.ADDED) {
+ prodMap.put(dst, edge);
+ } else {
+ prodMap.remove(dst);
+ }
+ }
+ }
+
+ private void moveToReadyListHi(NodeConnector nodeConnector) {
+ if (readyListLo.contains(nodeConnector)) {
+ readyListLo.remove(nodeConnector);
+ } else if (stagingList.contains(nodeConnector)) {
+ stagingList.remove(nodeConnector);
+ }
+ readyListHi.add(nodeConnector);
+ }
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this, null);
+ }
+
+ private int getDiscoveryConsistencyCheckInterval() {
+ return discoveryConsistencyCheckMultiple * discoveryBatchRestartTicks;
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---Topology Discovery---\n");
+ help.append("\t prlh - Print readyListHi entries\n");
+ help.append("\t prll - Print readyListLo entries\n");
+ help.append("\t psl - Print stagingList entries\n");
+ help.append("\t pht - Print hold time\n");
+ help.append("\t pet - Print elapsed time\n");
+ help.append("\t ptick - Print tick time in msec\n");
+ help.append("\t pcc - Print CC info\n");
+ help.append("\t psize - Print sizes of all the lists\n");
+ help.append("\t ptm - Print timeout info\n");
+ help.append("\t ecc - Enable CC\n");
+ help.append("\t dcc - Disable CC\n");
+ help.append("\t scc [multiple] - Set/show CC multiple and interval\n");
+ help.append("\t sports [ports] - Set/show max ports per batch\n");
+ help.append("\t spause [ticks] - Set/show pause period\n");
+ help.append("\t sdi [ticks] - Set/show discovery interval in ticks\n");
+ help.append("\t addsw <swid> - Add a switch\n");
+ help.append("\t remsw <swid> - Remove a switch\n");
+ help.append("\t page - Print aging info\n");
+ help.append("\t sage - Set/Show aging time limit\n");
+ help.append("\t eage - Enable aging\n");
+ help.append("\t dage - Disable aging\n");
+ help.append("\t pthrot - Print throttling\n");
+ help.append("\t ethrot - Enable throttling\n");
+ help.append("\t dthrot - Disable throttling\n");
+ help.append("\t psnp - Print LLDP snooping\n");
+ help.append("\t esnp <all|nodeConnector> - Enable LLDP snooping\n");
+ help.append("\t dsnp <all|nodeConnector> - Disable LLDP snooping\n");
+ return help.toString();
+ }
+
+ private List<NodeConnector> sortList(Collection<NodeConnector> ncs) {
+ List<String> ncStrArray = new ArrayList<String>();
+ for (NodeConnector nc : ncs) {
+ ncStrArray.add(nc.toString());
+ }
+ Collections.sort(ncStrArray);
+
+ List<NodeConnector> sortedNodeConnectors = new ArrayList<NodeConnector>();
+ for (String ncStr : ncStrArray) {
+ sortedNodeConnectors.add(NodeConnector.fromString(ncStr));
+ }
+
+ return sortedNodeConnectors;
+ }
+
+ public void _prlh(CommandInterpreter ci) {
+ ci.println("readyListHi\n");
+ for (NodeConnector nodeConnector : sortList(readyListHi)) {
+ if (nodeConnector == null) {
+ continue;
+ }
+ ci.println(nodeConnector);
+ }
+ ci.println("Total number of Node Connectors: " + readyListHi.size());
+ }
+
+ public void _prll(CommandInterpreter ci) {
+ ci.println("readyListLo\n");
+ for (NodeConnector nodeConnector : sortList(readyListLo)) {
+ if (nodeConnector == null) {
+ continue;
+ }
+ ci.println(nodeConnector);
+ }
+ ci.println("Total number of Node Connectors: " + readyListLo.size());
+ }
+
+ public void _psl(CommandInterpreter ci) {
+ ci.println("stagingList\n");
+ for (NodeConnector nodeConnector : sortList(stagingList)) {
+ if (nodeConnector == null) {
+ continue;
+ }
+ ci.println(nodeConnector);
+ }
+ ci.println("Total number of Node Connectors: " + stagingList.size());
+ }
+
+ public void _pht(CommandInterpreter ci) {
+ ci.println(" NodeConnector Last rx LLDP (sec)");
+ for (ConcurrentMap.Entry<NodeConnector, Integer> entry: holdTime.entrySet()) {
+ ci.println(entry.getKey() + "\t\t" + entry.getValue() * (discoveryTimerTick / 1000));
+ }
+ ci.println("\nSize: " + holdTime.size() + "\tTimeout: " + discoveryTimeoutTicks * (discoveryTimerTick / 1000)
+ + " sec");
+ }
+
+ public void _pet(CommandInterpreter ci) {
+ ci.println(" NodeConnector Elapsed Time (sec)");
+ for (ConcurrentMap.Entry<NodeConnector, Integer> entry: elapsedTime.entrySet()) {
+ ci.println(entry.getKey() + "\t\t" + entry.getValue() * (discoveryTimerTick / 1000));
+ }
+ ci.println("\nSize: " + elapsedTime.size() + "\tThreshold: " + DiscoveryPeriod.THRESHOLD.getTime() + " sec");
+ }
+
+ public void _ptick(CommandInterpreter ci) {
+ ci.println("Current timer is " + discoveryTimerTick + " msec per tick");
+ }
+
+ public void _pcc(CommandInterpreter ci) {
+ if (discoveryConsistencyCheckEnabled) {
+ ci.println("ConsistencyChecker is currently enabled");
+ } else {
+ ci.println("ConsistencyChecker is currently disabled");
+ }
+ ci.println("Interval " + getDiscoveryConsistencyCheckInterval());
+ ci.println("Multiple " + discoveryConsistencyCheckMultiple);
+ ci.println("Number of times called " + discoveryConsistencyCheckCallingTimes);
+ ci.println("Corrected count " + discoveryConsistencyCheckCorrected);
+ }
+
+ public void _ptm(CommandInterpreter ci) {
+ ci.println("Timeout " + discoveryTimeoutTicks + " ticks, " + discoveryTimerTick / 1000 + " sec per tick.");
+ }
+
+ public void _psize(CommandInterpreter ci) {
+ ci.println("readyListLo size " + readyListLo.size() + "\n" + "readyListHi size " + readyListHi.size() + "\n"
+ + "stagingList size " + stagingList.size() + "\n" + "holdTime size " + holdTime.size() + "\n"
+ + "edgeMap size " + edgeMap.size() + "\n" + "prodMap size " + prodMap.size() + "\n" + "agingMap size "
+ + agingMap.size() + "\n" + "elapsedTime size " + elapsedTime.size());
+ }
+
+ public void _page(CommandInterpreter ci) {
+ if (this.discoveryAgingEnabled) {
+ ci.println("Aging is enabled");
+ } else {
+ ci.println("Aging is disabled");
+ }
+ ci.println("Current aging time limit " + this.discoveryAgeoutTicks);
+ ci.println("\n");
+ ci.println(" Edge Aging ");
+ Collection<Edge> prodSet = prodMap.values();
+ if (prodSet == null) {
+ return;
+ }
+ for (Edge edge : prodSet) {
+ Integer aging = agingMap.get(edge.getHeadNodeConnector());
+ if (aging != null) {
+ ci.println(edge + " " + aging);
+ }
+ }
+ ci.println("\n");
+ ci.println(" NodeConnector Edge ");
+ Set<NodeConnector> keySet = prodMap.keySet();
+ if (keySet == null) {
+ return;
+ }
+ for (NodeConnector nc : keySet) {
+ ci.println(nc + " " + prodMap.get(nc));
+ }
+ return;
+ }
+
+ public void _sage(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ if (val == null) {
+ ci.println("Please enter aging time limit. Current value " + this.discoveryAgeoutTicks);
+ return;
+ }
+ try {
+ this.discoveryAgeoutTicks = Integer.parseInt(val);
+ } catch (Exception e) {
+ ci.println("Please enter a valid number");
+ }
+ return;
+ }
+
+ public void _eage(CommandInterpreter ci) {
+ this.discoveryAgingEnabled = true;
+ ci.println("Aging is enabled");
+ return;
+ }
+
+ public void _dage(CommandInterpreter ci) {
+ this.discoveryAgingEnabled = false;
+ ci.println("Aging is disabled");
+ return;
+ }
+
+ public void _scc(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ if (val == null) {
+ ci.println("Please enter CC multiple. Current multiple " + discoveryConsistencyCheckMultiple
+ + " (interval " + getDiscoveryConsistencyCheckInterval() + ") calling times "
+ + discoveryConsistencyCheckCallingTimes);
+ return;
+ }
+ try {
+ discoveryConsistencyCheckMultiple = Integer.parseInt(val);
+ } catch (Exception e) {
+ ci.println("Please enter a valid number");
+ }
+ return;
+ }
+
+ public void _ecc(CommandInterpreter ci) {
+ this.discoveryConsistencyCheckEnabled = true;
+ ci.println("ConsistencyChecker is enabled");
+ return;
+ }
+
+ public void _dcc(CommandInterpreter ci) {
+ this.discoveryConsistencyCheckEnabled = false;
+ ci.println("ConsistencyChecker is disabled");
+ return;
+ }
+
+ public void _psnp(CommandInterpreter ci) {
+ if (this.discoverySnoopingEnabled) {
+ ci.println("Discovery snooping is globally enabled");
+ } else {
+ ci.println("Discovery snooping is globally disabled");
+ }
+
+ ci.println("\nDiscovery snooping is locally disabled on these ports");
+ for (NodeConnector nodeConnector : discoverySnoopingDisableList) {
+ ci.println(nodeConnector);
+ }
+ return;
+ }
+
+ public void _esnp(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+
+ if (val == null) {
+ ci.println("Usage: esnp <all|nodeConnector>");
+ } else if (val.equalsIgnoreCase("all")) {
+ this.discoverySnoopingEnabled = true;
+ ci.println("Discovery snooping is globally enabled");
+ } else {
+ NodeConnector nodeConnector = NodeConnector.fromString(val);
+ if (nodeConnector != null) {
+ discoverySnoopingDisableList.remove(nodeConnector);
+ ci.println("Discovery snooping is locally enabled on port " + nodeConnector);
+ } else {
+ ci.println("Entered invalid NodeConnector " + val);
+ }
+ }
+ return;
+ }
+
+ public void _dsnp(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+
+ if (val == null) {
+ ci.println("Usage: dsnp <all|nodeConnector>");
+ } else if (val.equalsIgnoreCase("all")) {
+ this.discoverySnoopingEnabled = false;
+ ci.println("Discovery snooping is globally disabled");
+ } else {
+ NodeConnector nodeConnector = NodeConnector.fromString(val);
+ if (nodeConnector != null) {
+ discoverySnoopingDisableList.add(nodeConnector);
+ ci.println("Discovery snooping is locally disabled on port " + nodeConnector);
+ } else {
+ ci.println("Entered invalid NodeConnector " + val);
+ }
+ }
+ return;
+ }
+
+ public void _spause(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ String out = "Please enter pause period less than " + discoveryBatchRestartTicks + ". Current pause period is "
+ + discoveryBatchPausePeriod + " ticks, pause at " + discoveryBatchPauseTicks + " ticks, "
+ + discoveryTimerTick / 1000 + " sec per tick.";
+
+ if (val != null) {
+ try {
+ int pause = Integer.parseInt(val);
+ if (pause < discoveryBatchRestartTicks) {
+ discoveryBatchPausePeriod = pause;
+ discoveryBatchPauseTicks = getDiscoveryPauseInterval();
+ return;
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ ci.println(out);
+ }
+
+ public void _sdi(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ String out = "Please enter discovery interval in ticks. Current value is " + discoveryBatchRestartTicks + " ticks, "
+ + discoveryTimerTick / 1000 + " sec per tick.";
+
+ if (val != null) {
+ try {
+ int ticks = Integer.parseInt(val);
+ DiscoveryPeriod.INTERVAL.setTick(ticks);
+ discoveryBatchRestartTicks = getDiscoveryInterval();
+ discoveryBatchPauseTicks = getDiscoveryPauseInterval();
+ discoveryTimeoutTicks = getDiscoveryTimeout();
+ return;
+ } catch (Exception e) {
+ }
+ }
+ ci.println(out);
+ }
+
+ public void _sports(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ if (val == null) {
+ ci.println("Please enter max ports per batch. Current value is " + discoveryBatchMaxPorts);
+ return;
+ }
+ try {
+ discoveryBatchMaxPorts = Integer.parseInt(val);
+ } catch (Exception e) {
+ ci.println("Please enter a valid number");
+ }
+ return;
+ }
+
+ public void _addsw(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ Long sid;
+ try {
+ sid = Long.parseLong(val);
+ Node node = NodeCreator.createOFNode(sid);
+ addDiscovery(node);
+ } catch (Exception e) {
+ ci.println("Please enter a valid number");
+ }
+ return;
+ }
+
+ public void _remsw(CommandInterpreter ci) {
+ String val = ci.nextArgument();
+ Long sid;
+ try {
+ sid = Long.parseLong(val);
+ Node node = NodeCreator.createOFNode(sid);
+ removeDiscovery(node);
+ } catch (Exception e) {
+ ci.println("Please enter a valid number");
+ }
+ return;
+ }
+
+ public void _pthrot(CommandInterpreter ci) {
+ if (this.throttling) {
+ ci.println("Throttling is enabled");
+ } else {
+ ci.println("Throttling is disabled");
+ }
+ }
+
+ public void _ethrot(CommandInterpreter ci) {
+ this.throttling = true;
+ ci.println("Throttling is enabled");
+ return;
+ }
+
+ public void _dthrot(CommandInterpreter ci) {
+ this.throttling = false;
+ ci.println("Throttling is disabled");
+ return;
+ }
+
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ switch (type) {
+ case ADDED:
+ addNode(node, props);
+ break;
+ case REMOVED:
+ removeNode(node);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
+ Config config = null;
+ State state = null;
+ boolean enabled = false;
+
+ for (Property prop : props) {
+ if (prop.getName().equals(Config.ConfigPropName)) {
+ config = (Config) prop;
+ } else if (prop.getName().equals(State.StatePropName)) {
+ state = (State) prop;
+ }
+ }
+ enabled = ((config != null) && (config.getValue() == Config.ADMIN_UP) && (state != null) && (state.getValue() == State.EDGE_UP));
+
+ switch (type) {
+ case ADDED:
+ if (enabled) {
+ addDiscovery(nodeConnector);
+ logger.trace("ADDED enabled {}", nodeConnector);
+ } else {
+ logger.trace("ADDED disabled {}", nodeConnector);
+ }
+ break;
+ case CHANGED:
+ if (enabled) {
+ addDiscovery(nodeConnector);
+ logger.trace("CHANGED enabled {}", nodeConnector);
+ } else {
+ removeDiscovery(nodeConnector);
+ logger.trace("CHANGED disabled {}", nodeConnector);
+ }
+ break;
+ case REMOVED:
+ removeDiscovery(nodeConnector);
+ logger.trace("REMOVED enabled {}", nodeConnector);
+ break;
+ default:
+ return;
+ }
+ }
+
+ public void addNode(Node node, Set<Property> props) {
+ if (node == null) {
+ return;
+ }
+
+ addDiscovery(node);
+ }
+
+ public void removeNode(Node node) {
+ if (node == null) {
+ return;
+ }
+
+ removeDiscovery(node);
+ }
+
+ void setController(IController s) {
+ this.controller = s;
+ }
+
+ void unsetController(IController s) {
+ if (this.controller == s) {
+ this.controller = null;
+ }
+ }
+
+ public void setInventoryProvider(IInventoryProvider service) {
+ this.inventoryProvider = service;
+ }
+
+ public void unsetInventoryProvider(IInventoryProvider service) {
+ this.inventoryProvider = null;
+ }
+
+ public void setIDataPacketMux(IDataPacketMux service) {
+ this.iDataPacketMux = service;
+ }
+
+ public void unsetIDataPacketMux(IDataPacketMux service) {
+ if (this.iDataPacketMux == service) {
+ this.iDataPacketMux = null;
+ }
+ }
+
+ void setDiscoveryListener(IDiscoveryListener s) {
+ this.discoveryListener = s;
+ }
+
+ void unsetDiscoveryListener(IDiscoveryListener s) {
+ if (this.discoveryListener == s) {
+ this.discoveryListener = null;
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+ private void initDiscoveryPacket() {
+ // Create LLDP ChassisID TLV
+ chassisIdTlv = new LLDPTLV();
+ chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
+
+ // Create LLDP PortID TLV
+ portIdTlv = new LLDPTLV();
+ portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
+
+ // Create LLDP TTL TLV
+ byte[] ttl = new byte[] { (byte) 0, (byte) 120 };
+ ttlTlv = new LLDPTLV();
+ ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl);
+
+ customTlv = new LLDPTLV();
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ logger.trace("Init called");
+
+ transmitQ = new LinkedBlockingQueue<NodeConnector>();
+
+ readyListHi = new CopyOnWriteArrayList<NodeConnector>();
+ readyListLo = new CopyOnWriteArrayList<NodeConnector>();
+ stagingList = new CopyOnWriteArrayList<NodeConnector>();
+ holdTime = new ConcurrentHashMap<NodeConnector, Integer>();
+ elapsedTime = new ConcurrentHashMap<NodeConnector, Integer>();
+ edgeMap = new ConcurrentHashMap<NodeConnector, Edge>();
+ agingMap = new ConcurrentHashMap<NodeConnector, Integer>();
+ prodMap = new ConcurrentHashMap<NodeConnector, Edge>();
+ discoverySnoopingDisableList = new CopyOnWriteArrayList<NodeConnector>();
+
+ discoveryBatchRestartTicks = getDiscoveryInterval();
+ discoveryBatchPauseTicks = getDiscoveryPauseInterval();
+ discoveryTimeoutTicks = getDiscoveryTimeout();
+ discoveryThresholdTicks = getDiscoveryThreshold();
+ discoveryAgeoutTicks = getDiscoveryAgeout();
+ discoveryConsistencyCheckTickCount = discoveryBatchPauseTicks;
+ discoveryBatchMaxPorts = getDiscoveryBatchMaxPorts();
+
+ discoveryTimer = new Timer("DiscoveryService");
+ discoveryTimerTask = new DiscoveryTimerTask();
+
+ transmitThread = new Thread(new DiscoveryTransmit(transmitQ));
+
+ initDiscoveryPacket();
+
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ transmitQ = null;
+ readyListHi = null;
+ readyListLo = null;
+ stagingList = null;
+ holdTime = null;
+ edgeMap = null;
+ agingMap = null;
+ prodMap = null;
+ discoveryTimer = null;
+ discoveryTimerTask = null;
+ transmitThread = null;
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ discoveryTimer.schedule(discoveryTimerTask, discoveryTimerTick, discoveryTimerTick);
+ transmitThread.start();
+ }
+
+ /**
+ * Function called after registering the service in OSGi service registry.
+ */
+ void started() {
+ /* get a snapshot of all the existing switches */
+ addDiscovery();
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ shuttingDown = true;
+ discoveryTimer.cancel();
+ transmitThread.interrupt();
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName, ContainerFlow previousFlow, ContainerFlow currentFlow,
+ UpdateType t) {
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType t) {
+ switch (t) {
+ case ADDED:
+ moveToReadyListHi(p);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+ // do nothing
+ }
+
+ private byte[] getSourceMACFromNodeID(String nodeId) {
+ byte[] cid = HexEncode.bytesFromHexString(nodeId);
+ byte[] sourceMac = new byte[6];
+ int pos = cid.length - sourceMac.length;
+
+ if (pos >= 0) {
+ System.arraycopy(cid, pos, sourceMac, 0, sourceMac.length);
+ }
+
+ return sourceMac;
+ }
+
+ private int getDiscoveryTicks(DiscoveryPeriod dp, String val) {
+ if (dp == null) {
+ return 0;
+ }
+
+ if (val != null) {
+ try {
+ dp.setTime(Integer.parseInt(val));
+ } catch (Exception e) {
+ }
+ }
+
+ return dp.getTick();
+ }
+
+ /**
+ * This method returns the interval which determines how often the discovery
+ * packets will be sent.
+ *
+ * @return The discovery interval in ticks
+ */
+ private int getDiscoveryInterval() {
+ String intvl = System.getProperty("of.discoveryInterval");
+ return getDiscoveryTicks(DiscoveryPeriod.INTERVAL, intvl);
+ }
+
+ /**
+ * This method returns the timeout value in receiving subsequent discovery packets on a port.
+ *
+ * @return The discovery timeout in ticks
+ */
+ private int getDiscoveryTimeout() {
+ String val = System.getProperty("of.discoveryTimeoutMultiple");
+ int multiple = 2;
+
+ if (val != null) {
+ try {
+ multiple = Integer.parseInt(val);
+ } catch (Exception e) {
+ }
+ }
+ return getDiscoveryInterval() * multiple + 3;
+ }
+
+ /**
+ * This method returns the user configurable threshold value
+ *
+ * @return The discovery threshold value in ticks
+ */
+ private int getDiscoveryThreshold() {
+ String val = System.getProperty("of.discoveryThreshold");
+ return getDiscoveryTicks(DiscoveryPeriod.THRESHOLD, val);
+ }
+
+ /**
+ * This method returns the discovery entry aging time in ticks.
+ *
+ * @return The aging time in ticks
+ */
+ private int getDiscoveryAgeout() {
+ return getDiscoveryTicks(DiscoveryPeriod.AGEOUT, null);
+ }
+
+ /**
+ * This method returns the pause interval
+ *
+ * @return The pause interval in ticks
+ */
+ private int getDiscoveryPauseInterval() {
+ if (discoveryBatchRestartTicks > discoveryBatchPausePeriod) {
+ return discoveryBatchRestartTicks - discoveryBatchPausePeriod;
+ } else {
+ return discoveryBatchRestartTicks - 1;
+ }
+ }
+
+ /**
+ * This method returns the user configurable maximum number of ports handled
+ * in one discovery batch.
+ *
+ * @return The maximum number of ports
+ */
+ private int getDiscoveryBatchMaxPorts() {
+ String val = System.getProperty("of.discoveryBatchMaxPorts");
+ int ports = 1024;
+
+ if (val != null) {
+ try {
+ ports = Integer.parseInt(val);
+ } catch (Exception e) {
+ }
+ }
+ return ports;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Drop;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
+import org.opendaylight.controller.sal.action.HwPath;
+import org.opendaylight.controller.sal.action.Loopback;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.SetDlDst;
+import org.opendaylight.controller.sal.action.SetDlSrc;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
+import org.opendaylight.controller.sal.action.SetNwTos;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.action.SetVlanId;
+import org.opendaylight.controller.sal.action.SetVlanPcp;
+import org.opendaylight.controller.sal.action.SwPath;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchField;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6FlowMod;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6Match;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFVendor;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionDataLayer;
+import org.openflow.protocol.action.OFActionDataLayerDestination;
+import org.openflow.protocol.action.OFActionDataLayerSource;
+import org.openflow.protocol.action.OFActionNetworkLayerAddress;
+import org.openflow.protocol.action.OFActionNetworkLayerDestination;
+import org.openflow.protocol.action.OFActionNetworkLayerSource;
+import org.openflow.protocol.action.OFActionNetworkTypeOfService;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.protocol.action.OFActionStripVirtualLan;
+import org.openflow.protocol.action.OFActionTransportLayer;
+import org.openflow.protocol.action.OFActionTransportLayerDestination;
+import org.openflow.protocol.action.OFActionTransportLayerSource;
+import org.openflow.protocol.action.OFActionVirtualLanIdentifier;
+import org.openflow.protocol.action.OFActionVirtualLanPriorityCodePoint;
+import org.openflow.util.U16;
+import org.openflow.util.U32;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for converting a SAL Flow into the OF flow and vice-versa
+ */
+public class FlowConverter {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(FlowConverter.class);
+
+ /*
+ * The value 0xffff (OFP_VLAN_NONE) is used to indicate
+ * that no VLAN ID is set for OF Flow.
+ */
+ private static final short OFP_VLAN_NONE = (short) 0xffff;
+
+ private Flow flow; // SAL Flow
+ private OFMatch ofMatch; // OF 1.0 match or OF 1.0 + IPv6 extension match
+ private List<OFAction> actionsList; // OF 1.0 actions
+ private int actionsLength;
+ private boolean isIPv6;
+
+ public FlowConverter(OFMatch ofMatch, List<OFAction> actionsList) {
+ this.ofMatch = ofMatch;
+ this.actionsList = actionsList;
+ this.actionsLength = 0;
+ this.flow = null;
+ this.isIPv6 = ofMatch instanceof V6Match;
+ }
+
+ public FlowConverter(Flow flow) {
+ this.ofMatch = null;
+ this.actionsList = null;
+ this.actionsLength = 0;
+ this.flow = flow;
+ this.isIPv6 = flow.isIPv6();
+ }
+
+ /**
+ * Returns the match in OF 1.0 (OFMatch) form or OF 1.0 + IPv6 extensions
+ * form (V6Match)
+ *
+ * @return
+ */
+ public OFMatch getOFMatch() {
+ if (ofMatch == null) {
+ Match match = flow.getMatch();
+ ofMatch = (isIPv6) ? new V6Match() : new OFMatch();
+
+ int wildcards = OFMatch.OFPFW_ALL;
+ if (match.isPresent(MatchType.IN_PORT)) {
+ short port = (Short) ((NodeConnector) match.getField(
+ MatchType.IN_PORT).getValue()).getID();
+ if (!isIPv6) {
+ ofMatch.setInputPort(port);
+ wildcards &= ~OFMatch.OFPFW_IN_PORT;
+ } else {
+ ((V6Match) ofMatch).setInputPort(port, (short) 0);
+ }
+ }
+ if (match.isPresent(MatchType.DL_SRC)) {
+ byte[] srcMac = (byte[]) match.getField(MatchType.DL_SRC)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setDataLayerSource(srcMac.clone());
+ wildcards &= ~OFMatch.OFPFW_DL_SRC;
+ } else {
+ ((V6Match) ofMatch).setDataLayerSource(srcMac, null);
+ }
+ }
+ if (match.isPresent(MatchType.DL_DST)) {
+ byte[] dstMac = (byte[]) match.getField(MatchType.DL_DST)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setDataLayerDestination(dstMac.clone());
+ wildcards &= ~OFMatch.OFPFW_DL_DST;
+ } else {
+ ((V6Match) ofMatch).setDataLayerDestination(dstMac, null);
+ }
+ }
+ if (match.isPresent(MatchType.DL_VLAN)) {
+ short vlan = (Short) match.getField(MatchType.DL_VLAN)
+ .getValue();
+ if (vlan == MatchType.DL_VLAN_NONE) {
+ vlan = OFP_VLAN_NONE;
+ }
+ if (!isIPv6) {
+ ofMatch.setDataLayerVirtualLan(vlan);
+ wildcards &= ~OFMatch.OFPFW_DL_VLAN;
+ } else {
+ ((V6Match) ofMatch).setDataLayerVirtualLan(vlan, (short) 0);
+ }
+ }
+ if (match.isPresent(MatchType.DL_VLAN_PR)) {
+ byte vlanPr = (Byte) match.getField(MatchType.DL_VLAN_PR)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setDataLayerVirtualLanPriorityCodePoint(vlanPr);
+ wildcards &= ~OFMatch.OFPFW_DL_VLAN_PCP;
+ } else {
+ ((V6Match) ofMatch)
+ .setDataLayerVirtualLanPriorityCodePoint(vlanPr,
+ (byte) 0);
+ }
+ }
+ if (match.isPresent(MatchType.DL_TYPE)) {
+ short ethType = (Short) match.getField(MatchType.DL_TYPE)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setDataLayerType(ethType);
+ wildcards &= ~OFMatch.OFPFW_DL_TYPE;
+ } else {
+ ((V6Match) ofMatch).setDataLayerType(ethType, (short) 0);
+ }
+ }
+ if (match.isPresent(MatchType.NW_TOS)) {
+ /*
+ * OF 1.0 switch expects the TOS as the 6 msb in the byte. it is
+ * actually the DSCP field followed by a zero ECN
+ */
+ byte tos = (Byte) match.getField(MatchType.NW_TOS).getValue();
+ byte dscp = (byte) (tos << 2);
+ if (!isIPv6) {
+ ofMatch.setNetworkTypeOfService(dscp);
+ wildcards &= ~OFMatch.OFPFW_NW_TOS;
+ } else {
+ ((V6Match) ofMatch).setNetworkTypeOfService(dscp, (byte) 0);
+ }
+ }
+ if (match.isPresent(MatchType.NW_PROTO)) {
+ byte proto = (Byte) match.getField(MatchType.NW_PROTO)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setNetworkProtocol(proto);
+ wildcards &= ~OFMatch.OFPFW_NW_PROTO;
+ } else {
+ ((V6Match) ofMatch).setNetworkProtocol(proto, (byte) 0);
+ }
+ }
+ if (match.isPresent(MatchType.NW_SRC)) {
+ InetAddress address = (InetAddress) match.getField(MatchType.NW_SRC).getValue();
+ InetAddress mask = (InetAddress) match.getField(MatchType.NW_SRC).getMask();
+ if (!isIPv6) {
+ ofMatch.setNetworkSource(NetUtils.byteArray4ToInt(address.getAddress()));
+ int maskLength = (mask == null) ? 32 : NetUtils.getSubnetMaskLength(mask);
+ wildcards = (wildcards & ~OFMatch.OFPFW_NW_SRC_MASK) | ((32 - maskLength) << OFMatch.OFPFW_NW_SRC_SHIFT);
+ } else {
+ ((V6Match) ofMatch).setNetworkSource(address, mask);
+ }
+ }
+ if (match.isPresent(MatchType.NW_DST)) {
+ InetAddress address = (InetAddress) match.getField(MatchType.NW_DST).getValue();
+ InetAddress mask = (InetAddress) match.getField(MatchType.NW_DST).getMask();
+ if (!isIPv6) {
+ ofMatch.setNetworkDestination(NetUtils.byteArray4ToInt(address.getAddress()));
+ int maskLength = (mask == null) ? 32 : NetUtils.getSubnetMaskLength(mask);
+ wildcards = (wildcards & ~OFMatch.OFPFW_NW_DST_MASK) | ((32 - maskLength) << OFMatch.OFPFW_NW_DST_SHIFT);
+ } else {
+ ((V6Match) ofMatch).setNetworkDestination(address, mask);
+ }
+ }
+ if (match.isPresent(MatchType.TP_SRC)) {
+ short port = (Short) match.getField(MatchType.TP_SRC)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setTransportSource(port);
+ wildcards &= ~OFMatch.OFPFW_TP_SRC;
+ } else {
+ ((V6Match) ofMatch).setTransportSource(port, (short) 0);
+ }
+ }
+ if (match.isPresent(MatchType.TP_DST)) {
+ short port = (Short) match.getField(MatchType.TP_DST)
+ .getValue();
+ if (!isIPv6) {
+ ofMatch.setTransportDestination(port);
+ wildcards &= ~OFMatch.OFPFW_TP_DST;
+ } else {
+ ((V6Match) ofMatch)
+ .setTransportDestination(port, (short) 0);
+ }
+ }
+
+ if (!isIPv6) {
+ ofMatch.setWildcards(U32.t(Long.valueOf(wildcards)));
+ }
+ }
+ logger.trace("SAL Match: {} Openflow Match: {}", flow.getMatch(),
+ ofMatch);
+ return ofMatch;
+ }
+
+ /**
+ * Returns the list of actions in OF 1.0 form
+ *
+ * @return
+ */
+ public List<OFAction> getOFActions() {
+ if (this.actionsList == null) {
+ actionsList = new ArrayList<OFAction>();
+ for (Action action : flow.getActions()) {
+ if (action.getType() == ActionType.OUTPUT) {
+ Output a = (Output) action;
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setMaxLength((short) 0xffff);
+ ofAction.setPort(PortConverter.toOFPort(a.getPort()));
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.DROP) {
+ continue;
+ }
+ if (action.getType() == ActionType.LOOPBACK) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_IN_PORT.getValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.FLOOD) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_FLOOD.getValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.FLOOD_ALL) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_ALL.getValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.CONTROLLER) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_CONTROLLER.getValue());
+ // We want the whole frame hitting the match be sent to the
+ // controller
+ ofAction.setMaxLength((short) 0xffff);
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SW_PATH) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_LOCAL.getValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.HW_PATH) {
+ OFActionOutput ofAction = new OFActionOutput();
+ ofAction.setPort(OFPort.OFPP_NORMAL.getValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionOutput.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_VLAN_ID) {
+ SetVlanId a = (SetVlanId) action;
+ OFActionVirtualLanIdentifier ofAction = new OFActionVirtualLanIdentifier();
+ ofAction.setVirtualLanIdentifier((short) a.getVlanId());
+ actionsList.add(ofAction);
+ actionsLength += OFActionVirtualLanIdentifier.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_VLAN_PCP) {
+ SetVlanPcp a = (SetVlanPcp) action;
+ OFActionVirtualLanPriorityCodePoint ofAction = new OFActionVirtualLanPriorityCodePoint();
+ ofAction.setVirtualLanPriorityCodePoint(Integer.valueOf(
+ a.getPcp()).byteValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionVirtualLanPriorityCodePoint.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.POP_VLAN) {
+ OFActionStripVirtualLan ofAction = new OFActionStripVirtualLan();
+ actionsList.add(ofAction);
+ actionsLength += OFActionStripVirtualLan.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_DL_SRC) {
+ SetDlSrc a = (SetDlSrc) action;
+ OFActionDataLayerSource ofAction = new OFActionDataLayerSource();
+ ofAction.setDataLayerAddress(a.getDlAddress());
+ actionsList.add(ofAction);
+ actionsLength += OFActionDataLayer.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_DL_DST) {
+ SetDlDst a = (SetDlDst) action;
+ OFActionDataLayerDestination ofAction = new OFActionDataLayerDestination();
+ ofAction.setDataLayerAddress(a.getDlAddress());
+ actionsList.add(ofAction);
+ actionsLength += OFActionDataLayer.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_NW_SRC) {
+ SetNwSrc a = (SetNwSrc) action;
+ OFActionNetworkLayerSource ofAction = new OFActionNetworkLayerSource();
+ ofAction.setNetworkAddress(NetUtils.byteArray4ToInt(a
+ .getAddress().getAddress()));
+ actionsList.add(ofAction);
+ actionsLength += OFActionNetworkLayerAddress.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_NW_DST) {
+ SetNwDst a = (SetNwDst) action;
+ OFActionNetworkLayerDestination ofAction = new OFActionNetworkLayerDestination();
+ ofAction.setNetworkAddress(NetUtils.byteArray4ToInt(a
+ .getAddress().getAddress()));
+ actionsList.add(ofAction);
+ actionsLength += OFActionNetworkLayerAddress.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_NW_TOS) {
+ SetNwTos a = (SetNwTos) action;
+ OFActionNetworkTypeOfService ofAction = new OFActionNetworkTypeOfService();
+ ofAction.setNetworkTypeOfService(Integer.valueOf(
+ a.getNwTos()).byteValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionNetworkTypeOfService.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_TP_SRC) {
+ SetTpSrc a = (SetTpSrc) action;
+ OFActionTransportLayerSource ofAction = new OFActionTransportLayerSource();
+ ofAction.setTransportPort(Integer.valueOf(a.getPort())
+ .shortValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionTransportLayer.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_TP_DST) {
+ SetTpDst a = (SetTpDst) action;
+ OFActionTransportLayerDestination ofAction = new OFActionTransportLayerDestination();
+ ofAction.setTransportPort(Integer.valueOf(a.getPort())
+ .shortValue());
+ actionsList.add(ofAction);
+ actionsLength += OFActionTransportLayer.MINIMUM_LENGTH;
+ continue;
+ }
+ if (action.getType() == ActionType.SET_NEXT_HOP) {
+ logger.info("Unsupported action: {}", action);
+ continue;
+ }
+ }
+ }
+ logger.trace("SAL Actions: {} Openflow Actions: {}", flow.getActions(),
+ actionsList);
+ return actionsList;
+ }
+
+ /**
+ * Utility to convert a SAL flow to an OF 1.0 (OFFlowMod) or to an OF 1.0 +
+ * IPv6 extension (V6FlowMod) Flow modifier Message
+ *
+ * @param sw
+ * @param command
+ * @param port
+ * @return
+ */
+ public OFMessage getOFFlowMod(short command, OFPort port) {
+ OFMessage fm = (isIPv6) ? new V6FlowMod() : new OFFlowMod();
+ if (this.ofMatch == null) {
+ getOFMatch();
+ }
+ if (this.actionsList == null) {
+ getOFActions();
+ }
+ if (!isIPv6) {
+ ((OFFlowMod) fm).setMatch(this.ofMatch);
+ ((OFFlowMod) fm).setActions(this.actionsList);
+ ((OFFlowMod) fm).setPriority(flow.getPriority());
+ ((OFFlowMod) fm).setCookie(flow.getId());
+ ((OFFlowMod) fm).setBufferId(OFPacketOut.BUFFER_ID_NONE);
+ ((OFFlowMod) fm).setLength(U16.t(OFFlowMod.MINIMUM_LENGTH
+ + actionsLength));
+ ((OFFlowMod) fm).setIdleTimeout(flow.getIdleTimeout());
+ ((OFFlowMod) fm).setHardTimeout(flow.getHardTimeout());
+ ((OFFlowMod) fm).setCommand(command);
+ if (port != null) {
+ ((OFFlowMod) fm).setOutPort(port);
+ }
+ if (command == OFFlowMod.OFPFC_ADD
+ || command == OFFlowMod.OFPFC_MODIFY
+ || command == OFFlowMod.OFPFC_MODIFY_STRICT) {
+ if (flow.getIdleTimeout() != 0 || flow.getHardTimeout() != 0) {
+ // Instruct switch to let controller know when flow expires
+ ((OFFlowMod) fm).setFlags((short) 1);
+ }
+ }
+ } else {
+ ((V6FlowMod) fm).setVendor();
+ ((V6FlowMod) fm).setMatch((V6Match) ofMatch);
+ ((V6FlowMod) fm).setActions(this.actionsList);
+ ((V6FlowMod) fm).setPriority(flow.getPriority());
+ ((V6FlowMod) fm).setCookie(flow.getId());
+ ((V6FlowMod) fm).setLength(U16.t(OFVendor.MINIMUM_LENGTH
+ + ((V6Match) ofMatch).getIPv6ExtMinHdrLen()
+ + ((V6Match) ofMatch).getIPv6MatchLen()
+ + ((V6Match) ofMatch).getPadSize() + actionsLength));
+ ((V6FlowMod) fm).setIdleTimeout(flow.getIdleTimeout());
+ ((V6FlowMod) fm).setHardTimeout(flow.getHardTimeout());
+ ((V6FlowMod) fm).setCommand(command);
+ if (port != null) {
+ ((V6FlowMod) fm).setOutPort(port);
+ }
+ if (command == OFFlowMod.OFPFC_ADD
+ || command == OFFlowMod.OFPFC_MODIFY
+ || command == OFFlowMod.OFPFC_MODIFY_STRICT) {
+ if (flow.getIdleTimeout() != 0 || flow.getHardTimeout() != 0) {
+ // Instruct switch to let controller know when flow expires
+ ((V6FlowMod) fm).setFlags((short) 1);
+ }
+ }
+ }
+ logger.trace("Openflow Match: {} Openflow Actions: {}", ofMatch,
+ actionsList);
+ logger.trace("Openflow Mod Message: {}", fm);
+ return fm;
+ }
+
+ public Flow getFlow(Node node) {
+ if (this.flow == null) {
+ Match salMatch = new Match();
+
+ /*
+ * Installed flow may not have a Match defined like in case of a
+ * drop all flow
+ */
+ if (ofMatch != null) {
+ if (!isIPv6) {
+ // Compute OF1.0 Match
+ if (ofMatch.getInputPort() != 0 && ofMatch.getInputPort() != OFPort.OFPP_LOCAL.getValue()) {
+ salMatch.setField(new MatchField(MatchType.IN_PORT,
+ NodeConnectorCreator.createNodeConnector(
+ ofMatch.getInputPort(), node)));
+ }
+ if (ofMatch.getDataLayerSource() != null
+ && !NetUtils
+ .isZeroMAC(ofMatch.getDataLayerSource())) {
+ byte srcMac[] = ofMatch.getDataLayerSource();
+ salMatch.setField(new MatchField(MatchType.DL_SRC,
+ srcMac.clone()));
+ }
+ if (ofMatch.getDataLayerDestination() != null
+ && !NetUtils.isZeroMAC(ofMatch
+ .getDataLayerDestination())) {
+ byte dstMac[] = ofMatch.getDataLayerDestination();
+ salMatch.setField(new MatchField(MatchType.DL_DST,
+ dstMac.clone()));
+ }
+ if (ofMatch.getDataLayerType() != 0) {
+ salMatch.setField(new MatchField(MatchType.DL_TYPE,
+ ofMatch.getDataLayerType()));
+ }
+ short vlan = ofMatch.getDataLayerVirtualLan();
+ if (vlan != 0) {
+ if (vlan == OFP_VLAN_NONE) {
+ vlan = MatchType.DL_VLAN_NONE;
+ }
+ salMatch.setField(new MatchField(MatchType.DL_VLAN,
+ vlan));
+ }
+ if (ofMatch.getDataLayerVirtualLanPriorityCodePoint() != 0) {
+ salMatch.setField(MatchType.DL_VLAN_PR, ofMatch
+ .getDataLayerVirtualLanPriorityCodePoint());
+ }
+ if (ofMatch.getNetworkSource() != 0) {
+ salMatch.setField(MatchType.NW_SRC, NetUtils
+ .getInetAddress(ofMatch.getNetworkSource()),
+ NetUtils.getInetNetworkMask(
+ ofMatch.getNetworkSourceMaskLen(),
+ false));
+ }
+ if (ofMatch.getNetworkDestination() != 0) {
+ salMatch.setField(MatchType.NW_DST,
+ NetUtils.getInetAddress(ofMatch
+ .getNetworkDestination()),
+ NetUtils.getInetNetworkMask(
+ ofMatch.getNetworkDestinationMaskLen(),
+ false));
+ }
+ if (ofMatch.getNetworkTypeOfService() != 0) {
+ int dscp = NetUtils.getUnsignedByte(ofMatch
+ .getNetworkTypeOfService());
+ byte tos = (byte) (dscp >> 2);
+ salMatch.setField(MatchType.NW_TOS, tos);
+ }
+ if (ofMatch.getNetworkProtocol() != 0) {
+ salMatch.setField(MatchType.NW_PROTO,
+ ofMatch.getNetworkProtocol());
+ }
+ if (ofMatch.getTransportSource() != 0) {
+ salMatch.setField(MatchType.TP_SRC,
+ ofMatch.getTransportSource());
+ }
+ if (ofMatch.getTransportDestination() != 0) {
+ salMatch.setField(MatchType.TP_DST,
+ ofMatch.getTransportDestination());
+ }
+ } else {
+ // Compute OF1.0 + IPv6 extensions Match
+ V6Match v6Match = (V6Match) ofMatch;
+ if (v6Match.getInputPort() != 0 && v6Match.getInputPort() != OFPort.OFPP_LOCAL.getValue()) {
+ // Mask on input port is not defined
+ salMatch.setField(new MatchField(MatchType.IN_PORT,
+ NodeConnectorCreator.createOFNodeConnector(
+ v6Match.getInputPort(), node)));
+ }
+ if (v6Match.getDataLayerSource() != null
+ && !NetUtils
+ .isZeroMAC(ofMatch.getDataLayerSource())) {
+ byte srcMac[] = v6Match.getDataLayerSource();
+ salMatch.setField(new MatchField(MatchType.DL_SRC,
+ srcMac.clone()));
+ }
+ if (v6Match.getDataLayerDestination() != null
+ && !NetUtils.isZeroMAC(ofMatch
+ .getDataLayerDestination())) {
+ byte dstMac[] = v6Match.getDataLayerDestination();
+ salMatch.setField(new MatchField(MatchType.DL_DST,
+ dstMac.clone()));
+ }
+ if (v6Match.getDataLayerType() != 0) {
+ salMatch.setField(new MatchField(MatchType.DL_TYPE,
+ v6Match.getDataLayerType()));
+ }
+ short vlan = v6Match.getDataLayerVirtualLan();
+ if (vlan != 0) {
+ if (vlan == OFP_VLAN_NONE) {
+ vlan = MatchType.DL_VLAN_NONE;
+ }
+ salMatch.setField(new MatchField(MatchType.DL_VLAN,
+ vlan));
+ }
+ if (v6Match.getDataLayerVirtualLanPriorityCodePoint() != 0) {
+ salMatch.setField(MatchType.DL_VLAN_PR, v6Match
+ .getDataLayerVirtualLanPriorityCodePoint());
+ }
+ // V6Match may carry IPv4 address
+ if (v6Match.getNetworkSrc() != null) {
+ salMatch.setField(MatchType.NW_SRC,
+ v6Match.getNetworkSrc(),
+ v6Match.getNetworkSourceMask());
+ } else if (v6Match.getNetworkSource() != 0) {
+ salMatch.setField(MatchType.NW_SRC, NetUtils
+ .getInetAddress(v6Match.getNetworkSource()),
+ NetUtils.getInetNetworkMask(
+ v6Match.getNetworkSourceMaskLen(),
+ false));
+ }
+ // V6Match may carry IPv4 address
+ if (v6Match.getNetworkDest() != null) {
+ salMatch.setField(MatchType.NW_DST,
+ v6Match.getNetworkDest(),
+ v6Match.getNetworkDestinationMask());
+ } else if (v6Match.getNetworkDestination() != 0) {
+ salMatch.setField(MatchType.NW_DST,
+ NetUtils.getInetAddress(v6Match
+ .getNetworkDestination()),
+ NetUtils.getInetNetworkMask(
+ v6Match.getNetworkDestinationMaskLen(),
+ false));
+ }
+ if (v6Match.getNetworkTypeOfService() != 0) {
+ int dscp = NetUtils.getUnsignedByte(v6Match
+ .getNetworkTypeOfService());
+ byte tos = (byte) (dscp >> 2);
+ salMatch.setField(MatchType.NW_TOS, tos);
+ }
+ if (v6Match.getNetworkProtocol() != 0) {
+ salMatch.setField(MatchType.NW_PROTO,
+ v6Match.getNetworkProtocol());
+ }
+ if (v6Match.getTransportSource() != 0) {
+ salMatch.setField(MatchType.TP_SRC,
+ (v6Match.getTransportSource()));
+ }
+ if (v6Match.getTransportDestination() != 0) {
+ salMatch.setField(MatchType.TP_DST,
+ (v6Match.getTransportDestination()));
+ }
+ }
+ }
+
+ // Convert actions
+ Action salAction = null;
+ List<Action> salActionList = new ArrayList<Action>();
+ if (actionsList == null) {
+ salActionList.add(new Drop());
+ } else {
+ for (OFAction ofAction : actionsList) {
+ if (ofAction instanceof OFActionOutput) {
+ short ofPort = ((OFActionOutput) ofAction).getPort();
+ if (ofPort == OFPort.OFPP_CONTROLLER.getValue()) {
+ salAction = new Controller();
+ } else if (ofPort == OFPort.OFPP_NONE.getValue()) {
+ salAction = new Drop();
+ } else if (ofPort == OFPort.OFPP_IN_PORT.getValue()) {
+ salAction = new Loopback();
+ } else if (ofPort == OFPort.OFPP_FLOOD.getValue()) {
+ salAction = new Flood();
+ } else if (ofPort == OFPort.OFPP_ALL.getValue()) {
+ salAction = new FloodAll();
+ } else if (ofPort == OFPort.OFPP_LOCAL.getValue()) {
+ salAction = new SwPath();
+ } else if (ofPort == OFPort.OFPP_NORMAL.getValue()) {
+ salAction = new HwPath();
+ } else if (ofPort == OFPort.OFPP_TABLE.getValue()) {
+ salAction = new HwPath(); // TODO: we do not handle
+ // table in sal for now
+ } else {
+ salAction = new Output(
+ NodeConnectorCreator.createOFNodeConnector(
+ ofPort, node));
+ }
+ } else if (ofAction instanceof OFActionVirtualLanIdentifier) {
+ salAction = new SetVlanId(
+ ((OFActionVirtualLanIdentifier) ofAction)
+ .getVirtualLanIdentifier());
+ } else if (ofAction instanceof OFActionStripVirtualLan) {
+ salAction = new PopVlan();
+ } else if (ofAction instanceof OFActionVirtualLanPriorityCodePoint) {
+ salAction = new SetVlanPcp(
+ ((OFActionVirtualLanPriorityCodePoint) ofAction)
+ .getVirtualLanPriorityCodePoint());
+ } else if (ofAction instanceof OFActionDataLayerSource) {
+ salAction = new SetDlSrc(
+ ((OFActionDataLayerSource) ofAction)
+ .getDataLayerAddress().clone());
+ } else if (ofAction instanceof OFActionDataLayerDestination) {
+ salAction = new SetDlDst(
+ ((OFActionDataLayerDestination) ofAction)
+ .getDataLayerAddress().clone());
+ } else if (ofAction instanceof OFActionNetworkLayerSource) {
+ byte addr[] = BigInteger.valueOf(
+ ((OFActionNetworkLayerSource) ofAction)
+ .getNetworkAddress()).toByteArray();
+ InetAddress ip = null;
+ try {
+ ip = InetAddress.getByAddress(addr);
+ } catch (UnknownHostException e) {
+ logger.error("", e);
+ }
+ salAction = new SetNwSrc(ip);
+ } else if (ofAction instanceof OFActionNetworkLayerDestination) {
+ byte addr[] = BigInteger.valueOf(
+ ((OFActionNetworkLayerDestination) ofAction)
+ .getNetworkAddress()).toByteArray();
+ InetAddress ip = null;
+ try {
+ ip = InetAddress.getByAddress(addr);
+ } catch (UnknownHostException e) {
+ logger.error("", e);
+ }
+ salAction = new SetNwDst(ip);
+ } else if (ofAction instanceof OFActionNetworkTypeOfService) {
+ salAction = new SetNwTos(
+ ((OFActionNetworkTypeOfService) ofAction)
+ .getNetworkTypeOfService());
+ } else if (ofAction instanceof OFActionTransportLayerSource) {
+ Short port = ((OFActionTransportLayerSource) ofAction)
+ .getTransportPort();
+ int intPort = NetUtils.getUnsignedShort(port);
+ salAction = new SetTpSrc(intPort);
+ } else if (ofAction instanceof OFActionTransportLayerDestination) {
+ Short port = ((OFActionTransportLayerDestination) ofAction)
+ .getTransportPort();
+ int intPort = NetUtils.getUnsignedShort(port);
+ salAction = new SetTpDst(intPort);
+ }
+ salActionList.add(salAction);
+ }
+ }
+ // Create Flow
+ flow = new Flow(salMatch, salActionList);
+ }
+ logger.trace("Openflow Match: {} Openflow Actions: {}", ofMatch,
+ actionsList);
+ logger.trace("SAL Flow: {}", flow);
+ return flow;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.openflowplugin.openflow.IFlowProgrammerNotifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Flow Programmer Notifier class for relaying asynchronous messages received
+ * from the network node to the listeners on the proper container
+ */
+public class FlowProgrammerNotifier implements IFlowProgrammerNotifier {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(FlowProgrammerNotifier.class);
+ private IPluginOutFlowProgrammerService salNotifier;
+ private IPluginOutConnectionService connectionOutService;
+
+ public FlowProgrammerNotifier() {
+ salNotifier = null;
+ }
+
+ void init(Component c) {
+ logger.debug("INIT called!");
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ logger.debug("DESTROY called!");
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ logger.debug("START called!");
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ logger.debug("STOP called!");
+ }
+
+ public void setPluginOutFlowProgrammerService(
+ IPluginOutFlowProgrammerService s) {
+ this.salNotifier = s;
+ }
+
+ public void unsetPluginOutFlowProgrammerService(
+ IPluginOutFlowProgrammerService s) {
+ if (this.salNotifier == s) {
+ this.salNotifier = null;
+ }
+ }
+
+ @Override
+ public void flowRemoved(Node node, Flow flow) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("flow removed will not be notified in a non-master controller for node "+node);
+ return;
+ }
+
+ if (salNotifier != null) {
+ salNotifier.flowRemoved(node, flow);
+ } else {
+ logger.warn("Unable to relay switch message to upper layer");
+ }
+ }
+
+ @Override
+ public void flowErrorReported(Node node, long rid, Object err) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("flow error will not be notified in a non-master controller for node "+node);
+ return;
+ }
+
+ if (salNotifier != null) {
+ salNotifier.flowErrorReported(node, rid, err);
+ } else {
+ logger.warn("Unable to relay switch error message to upper layer");
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.openflowplugin.openflow.IFlowProgrammerNotifier;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageListener;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFFlowRemoved;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents the openflow plugin component in charge of programming the flows
+ * the flow programming and relay them to functional modules above SAL.
+ */
+public class FlowProgrammerService implements IPluginInFlowProgrammerService,
+ IMessageListener, IContainerListener, IInventoryShimExternalListener,
+ CommandProvider {
+ private static final Logger log = LoggerFactory
+ .getLogger(FlowProgrammerService.class);
+ private IController controller;
+ private ConcurrentMap<String, IFlowProgrammerNotifier> flowProgrammerNotifiers;
+ private Map<String, Set<NodeConnector>> containerToNc;
+ private ConcurrentMap<Long, Map<Integer, Long>> xid2rid;
+ private int barrierMessagePriorCount = getBarrierMessagePriorCount();
+ private IPluginOutConnectionService connectionOutService;
+
+ public FlowProgrammerService() {
+ controller = null;
+ flowProgrammerNotifiers = new ConcurrentHashMap<String, IFlowProgrammerNotifier>();
+ containerToNc = new HashMap<String, Set<NodeConnector>>();
+ xid2rid = new ConcurrentHashMap<Long, Map<Integer, Long>>();
+ }
+
+ public void setController(IController core) {
+ this.controller = core;
+ }
+
+ public void unsetController(IController core) {
+ if (this.controller == core) {
+ this.controller = null;
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+ public void setFlowProgrammerNotifier(Map<String, ?> props,
+ IFlowProgrammerNotifier s) {
+ if (props == null || props.get("containerName") == null) {
+ log.error("Didn't receive the service correct properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ this.flowProgrammerNotifiers.put(containerName, s);
+ }
+
+ public void unsetFlowProgrammerNotifier(Map<String, ?> props,
+ IFlowProgrammerNotifier s) {
+ if (props == null || props.get("containerName") == null) {
+ log.error("Didn't receive the service correct properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (this.flowProgrammerNotifiers != null
+ && this.flowProgrammerNotifiers.containsKey(containerName)
+ && this.flowProgrammerNotifiers.get(containerName) == s) {
+ this.flowProgrammerNotifiers.remove(containerName);
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ this.controller.addMessageListener(OFType.FLOW_REMOVED, this);
+ this.controller.addMessageListener(OFType.ERROR, this);
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ }
+
+ @Override
+ public Status addFlow(Node node, Flow flow) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Add flow will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return addFlowInternal(node, flow, 0);
+ }
+
+ @Override
+ public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Modify flow will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return modifyFlowInternal(node, oldFlow, newFlow, 0);
+ }
+
+ @Override
+ public Status removeFlow(Node node, Flow flow) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Remove flow will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return removeFlowInternal(node, flow, 0);
+ }
+
+ @Override
+ public Status addFlowAsync(Node node, Flow flow, long rid) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Add flow Async will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return addFlowInternal(node, flow, rid);
+ }
+
+ @Override
+ public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow,
+ long rid) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Modify flow async will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return modifyFlowInternal(node, oldFlow, newFlow, rid);
+ }
+
+ @Override
+ public Status removeFlowAsync(Node node, Flow flow, long rid) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Remove flow async will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return removeFlowInternal(node, flow, rid);
+ }
+
+ private Status addFlowInternal(Node node, Flow flow, long rid) {
+ String action = "add";
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
+ action, "Invalid node type"));
+ }
+
+ if (controller != null) {
+ ISwitch sw = controller.getSwitch((Long) node.getID());
+ if (sw != null) {
+ FlowConverter x = new FlowConverter(flow);
+ OFMessage msg = x.getOFFlowMod(OFFlowMod.OFPFC_ADD, null);
+
+ Object result;
+ if (rid == 0) {
+ /*
+ * Synchronous message send. Each message is followed by a
+ * Barrier message.
+ */
+ result = sw.syncSend(msg);
+ } else {
+ /*
+ * Message will be sent asynchronously. A Barrier message
+ * will be inserted automatically to synchronize the
+ * progression.
+ */
+ result = asyncMsgSend(node, sw, msg, rid);
+ }
+ return getStatusInternal(result, action, rid);
+ } else {
+ return new Status(StatusCode.GONE, errorString("send", action,
+ "Switch is not available"));
+ }
+ }
+ return new Status(StatusCode.INTERNALERROR, errorString("send", action,
+ "Internal plugin error"));
+ }
+
+ private Status modifyFlowInternal(Node node, Flow oldFlow, Flow newFlow, long rid) {
+ String action = "modify";
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
+ action, "Invalid node type"));
+ }
+ if (controller != null) {
+ ISwitch sw = controller.getSwitch((Long) node.getID());
+ if (sw != null) {
+ OFMessage msg1 = null, msg2 = null;
+
+ // If priority and match portion are the same, send a
+ // modification message
+ if (oldFlow.getPriority() != newFlow.getPriority()
+ || !oldFlow.getMatch().equals(newFlow.getMatch())) {
+ msg1 = new FlowConverter(oldFlow).getOFFlowMod(
+ OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
+ msg2 = new FlowConverter(newFlow).getOFFlowMod(
+ OFFlowMod.OFPFC_ADD, null);
+ } else {
+ msg1 = new FlowConverter(newFlow).getOFFlowMod(
+ OFFlowMod.OFPFC_MODIFY_STRICT, null);
+ }
+ /*
+ * Synchronous message send
+ */
+ action = (msg2 == null) ? "modify" : "delete";
+ Object result;
+ if (rid == 0) {
+ /*
+ * Synchronous message send. Each message is followed by a
+ * Barrier message.
+ */
+ result = sw.syncSend(msg1);
+ } else {
+ /*
+ * Message will be sent asynchronously. A Barrier message
+ * will be inserted automatically to synchronize the
+ * progression.
+ */
+ result = asyncMsgSend(node, sw, msg1, rid);
+ }
+
+ Status rv = getStatusInternal(result, action, rid);
+ if ((msg2 == null) || !rv.isSuccess()) {
+ return rv;
+ }
+
+ action = "add";
+ if (rid == 0) {
+ /*
+ * Synchronous message send. Each message is followed by a
+ * Barrier message.
+ */
+ result = sw.syncSend(msg2);
+ } else {
+ /*
+ * Message will be sent asynchronously. A Barrier message
+ * will be inserted automatically to synchronize the
+ * progression.
+ */
+ result = asyncMsgSend(node, sw, msg2, rid);
+ }
+ return getStatusInternal(result, action, rid);
+ } else {
+ return new Status(StatusCode.GONE, errorString("send", action,
+ "Switch is not available"));
+ }
+ }
+ return new Status(StatusCode.INTERNALERROR, errorString("send", action,
+ "Internal plugin error"));
+ }
+
+ private Status removeFlowInternal(Node node, Flow flow, long rid) {
+ String action = "remove";
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
+ action, "Invalid node type"));
+ }
+ if (controller != null) {
+ ISwitch sw = controller.getSwitch((Long) node.getID());
+ if (sw != null) {
+ OFMessage msg = new FlowConverter(flow).getOFFlowMod(
+ OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
+ Object result;
+ if (rid == 0) {
+ /*
+ * Synchronous message send. Each message is followed by a
+ * Barrier message.
+ */
+ result = sw.syncSend(msg);
+ } else {
+ /*
+ * Message will be sent asynchronously. A Barrier message
+ * will be inserted automatically to synchronize the
+ * progression.
+ */
+ result = asyncMsgSend(node, sw, msg, rid);
+ }
+ return getStatusInternal(result, action, rid);
+ } else {
+ return new Status(StatusCode.GONE, errorString("send", action,
+ "Switch is not available"));
+ }
+ }
+ return new Status(StatusCode.INTERNALERROR, errorString("send", action,
+ "Internal plugin error"));
+ }
+
+ @Override
+ public Status removeAllFlows(Node node) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Remove all flows will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private String errorString(String phase, String action, String cause) {
+ return "Failed to "
+ + ((phase != null) ? phase + " the " + action
+ + " flow message: " : action + " the flow: ") + cause;
+ }
+
+ @Override
+ public void receive(ISwitch sw, OFMessage msg) {
+ if (msg instanceof OFFlowRemoved) {
+ handleFlowRemovedMessage(sw, (OFFlowRemoved) msg);
+ } else if (msg instanceof OFError) {
+ handleErrorMessage(sw, (OFError) msg);
+ }
+ }
+
+ private void handleFlowRemovedMessage(ISwitch sw, OFFlowRemoved msg) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ Flow flow = new FlowConverter(msg.getMatch(),
+ new ArrayList<OFAction>(0)).getFlow(node);
+ flow.setPriority(msg.getPriority());
+ flow.setIdleTimeout(msg.getIdleTimeout());
+ flow.setId(msg.getCookie());
+
+ Match match = flow.getMatch();
+ NodeConnector inPort = match.isPresent(MatchType.IN_PORT) ? (NodeConnector) match
+ .getField(MatchType.IN_PORT).getValue() : null;
+
+ for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
+ .entrySet()) {
+ String container = containerNotifier.getKey();
+ IFlowProgrammerNotifier notifier = containerNotifier.getValue();
+ /*
+ * Switch only provide us with the match information. For now let's
+ * try to identify the container membership only from the input port
+ * match field. In any case, upper layer consumers can derive
+ * whether the notification was not for them. More sophisticated
+ * filtering can be added later on.
+ */
+ if (inPort == null
+ || container.equals(GlobalConstants.DEFAULT.toString())
+ || this.containerToNc.get(container).contains(inPort)) {
+ notifier.flowRemoved(node, flow);
+ }
+ }
+ }
+
+ private void handleErrorMessage(ISwitch sw, OFError errorMsg) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ OFMessage offendingMsg = errorMsg.getOffendingMsg();
+ Integer xid;
+ if (offendingMsg != null) {
+ xid = offendingMsg.getXid();
+ } else {
+ xid = errorMsg.getXid();
+ }
+
+ Long rid = getMessageRid(sw.getId(), xid);
+ /*
+ * Null or zero requestId indicates that the error message is meant for
+ * a sync message. It will be handled by the sync message worker thread.
+ * Hence we are done here.
+ */
+ if ((rid == null) || (rid == 0)) {
+ return;
+ }
+
+ /*
+ * Notifies the caller that error has been reported for a previous flow
+ * programming request
+ */
+ for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
+ .entrySet()) {
+ IFlowProgrammerNotifier notifier = containerNotifier.getValue();
+ notifier.flowErrorReported(node, rid, errorMsg);
+ }
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag,
+ short newTag, UpdateType t) {
+
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName,
+ ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector p,
+ UpdateType type) {
+ Set<NodeConnector> target = null;
+
+ switch (type) {
+ case ADDED:
+ if (!containerToNc.containsKey(containerName)) {
+ containerToNc.put(containerName, new HashSet<NodeConnector>());
+ }
+ containerToNc.get(containerName).add(p);
+ break;
+ case CHANGED:
+ break;
+ case REMOVED:
+ target = containerToNc.get(containerName);
+ if (target != null) {
+ target.remove(p);
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+
+ }
+
+ @Override
+ public Status syncSendBarrierMessage(Node node) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("Sync Send Barrier will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ return new Status(StatusCode.NOTACCEPTABLE,
+ "The node does not support Barrier message.");
+ }
+
+ if (controller != null) {
+ long swid = (Long) node.getID();
+ ISwitch sw = controller.getSwitch(swid);
+ if (sw != null) {
+ sw.syncSendBarrierMessage();
+ clearXid2Rid(swid);
+ return (new Status(StatusCode.SUCCESS));
+ } else {
+ return new Status(StatusCode.GONE,
+ "The node does not have a valid Switch reference.");
+ }
+ }
+ return new Status(StatusCode.INTERNALERROR,
+ "Failed to send Barrier message.");
+ }
+
+ @Override
+ public Status asyncSendBarrierMessage(Node node) {
+ if (!connectionOutService.isLocal(node)) {
+ log.debug("ASync Send Barrier will not be processed in a non-master controller for node " + node);
+ return new Status(StatusCode.NOTALLOWED, "This is not the master controller for " + node);
+ }
+
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ return new Status(StatusCode.NOTACCEPTABLE,
+ "The node does not support Barrier message.");
+ }
+
+ if (controller != null) {
+ long swid = (Long) node.getID();
+ ISwitch sw = controller.getSwitch(swid);
+ if (sw != null) {
+ sw.asyncSendBarrierMessage();
+ clearXid2Rid(swid);
+ return (new Status(StatusCode.SUCCESS));
+ } else {
+ return new Status(StatusCode.GONE,
+ "The node does not have a valid Switch reference.");
+ }
+ }
+ return new Status(StatusCode.INTERNALERROR,
+ "Failed to send Barrier message.");
+ }
+
+ /**
+ * This method sends the message asynchronously until the number of messages
+ * sent reaches a threshold. Then a Barrier message is sent automatically
+ * for sync purpose. An unique Request ID associated with the message is
+ * passed down by the caller. The Request ID will be returned to the caller
+ * when an error message is received from the switch.
+ *
+ * @param node
+ * The node
+ * @param msg
+ * The switch
+ * @param msg
+ * The OF message to be sent
+ * @param rid
+ * The Request Id
+ * @return result
+ */
+ private Object asyncMsgSend(Node node, ISwitch sw, OFMessage msg, long rid) {
+ Object result = Boolean.TRUE;
+ long swid = (Long) node.getID();
+ int xid;
+
+ xid = sw.asyncSend(msg);
+ addXid2Rid(swid, xid, rid);
+
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+ if (swxid2rid == null) {
+ return result;
+ }
+
+ int size = swxid2rid.size();
+ if (size % barrierMessagePriorCount == 0) {
+ result = asyncSendBarrierMessage(node);
+ }
+
+ return result;
+ }
+
+ /**
+ * A number of async messages are sent followed by a synchronous Barrier
+ * message. This method returns the maximum async messages that can be sent
+ * before the Barrier message.
+ *
+ * @return The max count of async messages sent prior to Barrier message
+ */
+ private int getBarrierMessagePriorCount() {
+ String count = System.getProperty("of.barrierMessagePriorCount");
+ int rv = 100;
+
+ if (count != null) {
+ try {
+ rv = Integer.parseInt(count);
+ } catch (Exception e) {
+ }
+ }
+
+ return rv;
+ }
+
+ /**
+ * This method returns the message Request ID previously assigned by the
+ * caller for a given OF message xid
+ *
+ * @param swid
+ * The switch id
+ * @param xid
+ * The OF message xid
+ * @return The Request ID
+ */
+ private Long getMessageRid(long swid, Integer xid) {
+ Long rid = null;
+
+ if (xid == null) {
+ return rid;
+ }
+
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+ if (swxid2rid != null) {
+ rid = swxid2rid.get(xid);
+ }
+ return rid;
+ }
+
+ /**
+ * This method returns a copy of outstanding xid to rid mappings.for a given
+ * switch
+ *
+ * @param swid
+ * The switch id
+ * @return a copy of xid2rid mappings
+ */
+ public Map<Integer, Long> getSwXid2Rid(long swid) {
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+
+ if (swxid2rid != null) {
+ return new HashMap<Integer, Long>(swxid2rid);
+ } else {
+ return new HashMap<Integer, Long>();
+ }
+ }
+
+ /**
+ * Adds xid to rid mapping to the local DB
+ *
+ * @param swid
+ * The switch id
+ * @param xid
+ * The OF message xid
+ * @param rid
+ * The message Request ID
+ */
+ private void addXid2Rid(long swid, int xid, long rid) {
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+ if (swxid2rid != null) {
+ swxid2rid.put(xid, rid);
+ }
+ }
+
+ /**
+ * When an Error message is received, this method will be invoked to remove
+ * the offending xid from the local DB.
+ *
+ * @param swid
+ * The switch id
+ * @param xid
+ * The OF message xid
+ */
+ private void removeXid2Rid(long swid, int xid) {
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+ if (swxid2rid != null) {
+ swxid2rid.remove(xid);
+ }
+ }
+
+ /**
+ * Convert various result into Status
+ *
+ * @param result
+ * The returned result from previous action
+ * @param action
+ * add/modify/delete flow action
+ * @param rid
+ * The Request ID associated with the flow message
+ * @return Status
+ */
+ private Status getStatusInternal(Object result, String action, long rid) {
+ if (result instanceof Boolean) {
+ return ((Boolean) result == Boolean.TRUE) ? new Status(
+ StatusCode.SUCCESS, rid) : new Status(
+ StatusCode.TIMEOUT, errorString(null, action,
+ "Request Timed Out"));
+ } else if (result instanceof Status) {
+ return (Status) result;
+ } else if (result instanceof OFError) {
+ OFError res = (OFError) result;
+ return new Status(StatusCode.INTERNALERROR, errorString(
+ "program", action, Utils.getOFErrorString(res)));
+ } else {
+ return new Status(StatusCode.INTERNALERROR, errorString(
+ "send", action, "Internal Error"));
+ }
+ }
+
+ /**
+ * When a Barrier reply is received, this method will be invoked to clear
+ * the local DB
+ *
+ * @param swid
+ * The switch id
+ */
+ private void clearXid2Rid(long swid) {
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
+ if (swxid2rid != null) {
+ swxid2rid.clear();
+ }
+ }
+
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ long swid = (Long)node.getID();
+
+ switch (type) {
+ case ADDED:
+ Map<Integer, Long> swxid2rid = new HashMap<Integer, Long>();
+ this.xid2rid.put(swid, swxid2rid);
+ break;
+ case CHANGED:
+ break;
+ case REMOVED:
+ this.xid2rid.remove(swid);
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props) {
+ }
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+ .getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this,
+ null);
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("-- Flow Programmer Service --\n");
+ help.append("\t px2r <node id> - Print outstanding xid2rid mappings for a given node id\n");
+ help.append("\t px2rc - Print max num of async msgs prior to the Barrier\n");
+ return help.toString();
+ }
+
+ public void _px2r(CommandInterpreter ci) {
+ String st = ci.nextArgument();
+ if (st == null) {
+ ci.println("Please enter a valid node id");
+ return;
+ }
+
+ long sid;
+ try {
+ sid = HexEncode.stringToLong(st);
+ } catch (NumberFormatException e) {
+ ci.println("Please enter a valid node id");
+ return;
+ }
+
+ Map<Integer, Long> swxid2rid = this.xid2rid.get(sid);
+ if (swxid2rid == null) {
+ ci.println("The node id entered does not exist");
+ return;
+ }
+
+ ci.println("xid rid");
+
+ Set<Integer> xidSet = swxid2rid.keySet();
+ if (xidSet == null) {
+ return;
+ }
+
+ for (Integer xid : xidSet) {
+ ci.println(xid + " " + swxid2rid.get(xid));
+ }
+ }
+
+ public void _px2rc(CommandInterpreter ci) {
+ ci.println("Max num of async messages sent prior to the Barrier message is "
+ + barrierMessagePriorCount);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6StatsReply;
+import org.openflow.protocol.statistics.OFFlowStatisticsReply;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts an openflow list of flow statistics in a SAL list of FlowOnNode
+ * objects
+ *
+ *
+ *
+ */
+public class FlowStatisticsConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(FlowStatisticsConverter.class);
+ private List<OFStatistics> ofStatsList;
+ private List<FlowOnNode> flowOnNodeList;
+
+ public FlowStatisticsConverter(List<OFStatistics> statsList) {
+ if (statsList == null) {// || statsList.isEmpty()) {
+ this.ofStatsList = new ArrayList<OFStatistics>(1); // dummy list
+ } else {
+ this.ofStatsList = statsList; // new
+ // ArrayList<OFStatistics>(statsList);
+ }
+ this.flowOnNodeList = null;
+ }
+
+ public List<FlowOnNode> getFlowOnNodeList(Node node) {
+ if (ofStatsList != null && flowOnNodeList == null) {
+ flowOnNodeList = new ArrayList<FlowOnNode>();
+ FlowConverter flowConverter = null;
+ OFFlowStatisticsReply ofFlowStat;
+ V6StatsReply v6StatsReply;
+ for (OFStatistics ofStat : ofStatsList) {
+ FlowOnNode flowOnNode = null;
+ if (ofStat instanceof OFFlowStatisticsReply) {
+ ofFlowStat = (OFFlowStatisticsReply) ofStat;
+ flowConverter = new FlowConverter(ofFlowStat.getMatch(),
+ ofFlowStat.getActions());
+ Flow flow = flowConverter.getFlow(node);
+ flow.setPriority(ofFlowStat.getPriority());
+ flow.setIdleTimeout(ofFlowStat.getIdleTimeout());
+ flow.setHardTimeout(ofFlowStat.getHardTimeout());
+ flowOnNode = new FlowOnNode(flow);
+ flowOnNode.setByteCount(ofFlowStat.getByteCount());
+ flowOnNode.setPacketCount(ofFlowStat.getPacketCount());
+ flowOnNode.setDurationSeconds(ofFlowStat
+ .getDurationSeconds());
+ flowOnNode.setDurationNanoseconds(ofFlowStat
+ .getDurationNanoseconds());
+ } else if (ofStat instanceof V6StatsReply) {
+ v6StatsReply = (V6StatsReply) ofStat;
+ flowConverter = new FlowConverter(v6StatsReply.getMatch(),
+ v6StatsReply.getActions());
+ Flow flow = flowConverter.getFlow(node);
+ flow.setPriority(v6StatsReply.getPriority());
+ flow.setIdleTimeout(v6StatsReply.getIdleTimeout());
+ flow.setHardTimeout(v6StatsReply.getHardTimeout());
+ flowOnNode = new FlowOnNode(flow);
+ flowOnNode.setByteCount(v6StatsReply.getByteCount());
+ flowOnNode.setPacketCount(v6StatsReply.getPacketCount());
+ flowOnNode.setDurationSeconds(v6StatsReply
+ .getDurationSeconds());
+ flowOnNode.setDurationNanoseconds(v6StatsReply
+ .getDurationNanoseconds());
+ } else {
+ continue;
+ }
+ flowOnNodeList.add(flowOnNode);
+ }
+ }
+ log.trace("OFStatistics: {} FlowOnNode: {}", ofStatsList,
+ flowOnNodeList);
+ return flowOnNodeList;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.openflowplugin.openflow.IInventoryProvider;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimInternalListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The class describes inventory service protocol plugin. One instance per
+ * container of the network. Each instance gets container specific inventory
+ * events from InventoryServiceShim. It interacts with SAL to pass inventory
+ * data to the upper application.
+ *
+ *
+ */
+public class InventoryService implements IInventoryShimInternalListener,
+ IPluginInInventoryService, IInventoryProvider {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(InventoryService.class);
+ private Set<IPluginOutInventoryService> pluginOutInventoryServices;
+ private IController controller = null;
+ private ConcurrentMap<Node, Map<String, Property>> nodeProps; // properties are maintained in global container only
+ private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps; // properties are maintained in global container only
+ private boolean isDefaultContainer = false;
+ private String containerName = null;
+
+ void setController(IController s) {
+ this.controller = s;
+ }
+
+ void unsetController(IController s) {
+ if (this.controller == s) {
+ this.controller = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ @SuppressWarnings("rawtypes")
+ void init(Component c) {
+ logger.trace("INIT called!");
+
+ Dictionary props = c.getServiceProperties();
+ if (props != null) {
+ containerName = (String) props.get("containerName");
+ if (containerName != null) {
+ isDefaultContainer = containerName.equals(GlobalConstants.DEFAULT
+ .toString());
+ }
+ }
+
+ nodeProps = new ConcurrentHashMap<Node, Map<String, Property>>();
+ nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Map<String, Property>>();
+ pluginOutInventoryServices = new CopyOnWriteArraySet<IPluginOutInventoryService>();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ logger.trace("DESTROY called!");
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ logger.trace("START called!");
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ logger.trace("STOP called!");
+ }
+
+ public void setPluginOutInventoryServices(IPluginOutInventoryService service) {
+ logger.trace("Got a service set request {}", service);
+ if (this.pluginOutInventoryServices != null) {
+ this.pluginOutInventoryServices.add(service);
+ }
+ }
+
+ public void unsetPluginOutInventoryServices(
+ IPluginOutInventoryService service) {
+ logger.trace("Got a service UNset request");
+ if (this.pluginOutInventoryServices != null) {
+ this.pluginOutInventoryServices.remove(service);
+ }
+ }
+
+ /**
+ * Retrieve nodes from openflow
+ */
+ @Override
+ public ConcurrentMap<Node, Map<String, Property>> getNodeProps() {
+ logger.debug("getNodePros for container {}", containerName);
+ return nodeProps;
+ }
+
+ @Override
+ public ConcurrentMap<NodeConnector, Map<String, Property>> getNodeConnectorProps(
+ Boolean refresh) {
+ if (nodeConnectorProps == null) {
+ return null;
+ }
+
+ if (isDefaultContainer && refresh) {
+ Map<Long, ISwitch> switches = controller.getSwitches();
+ for (ISwitch sw : switches.values()) {
+ Map<NodeConnector, Set<Property>> ncProps = InventoryServiceHelper
+ .OFSwitchToProps(sw);
+ for (Map.Entry<NodeConnector, Set<Property>> entry : ncProps
+ .entrySet()) {
+ updateNodeConnector(entry.getKey(), UpdateType.ADDED,
+ entry.getValue());
+ }
+ }
+ }
+
+ return nodeConnectorProps;
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props) {
+ logger.trace("updateNodeConnector {} type {}", nodeConnector,
+ type.getName());
+ if (nodeConnectorProps == null) {
+ logger.trace("nodeConnectorProps is null");
+ return;
+ }
+
+ Map<String, Property> propMap = nodeConnectorProps.get(nodeConnector);
+ switch (type) {
+ case ADDED:
+ case CHANGED:
+ if (propMap == null) {
+ propMap = new HashMap<String, Property>();
+ }
+ if (props != null) {
+ for (Property prop : props) {
+ propMap.put(prop.getName(), prop);
+ }
+ }
+ nodeConnectorProps.put(nodeConnector, propMap);
+ break;
+ case REMOVED:
+ nodeConnectorProps.remove(nodeConnector);
+ break;
+ default:
+ return;
+ }
+
+ // update sal and discovery
+ for (IPluginOutInventoryService service : pluginOutInventoryServices) {
+ service.updateNodeConnector(nodeConnector, type, props);
+ }
+
+ }
+
+ private void addNode(Node node, Set<Property> props) {
+ if (nodeProps == null) {
+ return;
+ }
+
+ logger.trace("addNode: {} added, props: {} for container {}",
+ new Object[] { node, props, containerName });
+
+ // update local cache
+ Map<String, Property> propMap = nodeProps.get(node);
+ if (propMap == null) {
+ propMap = new HashMap<String, Property>();
+ }
+
+ if (props != null) {
+ for (Property prop : props) {
+ propMap.put(prop.getName(), prop);
+ }
+ }
+ nodeProps.put(node, propMap);
+
+ // update sal
+ for (IPluginOutInventoryService service : pluginOutInventoryServices) {
+ service.updateNode(node, UpdateType.ADDED, props);
+ }
+ }
+
+ private void removeNode(Node node) {
+ logger.trace("{} removed", node);
+ if (nodeProps == null)
+ return;
+
+ // update local cache
+ nodeProps.remove(node);
+
+ Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
+ for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+ if (nodeConnector.getNode().equals(node)) {
+ removeSet.add(nodeConnector);
+ }
+ }
+ for (NodeConnector nodeConnector : removeSet) {
+ nodeConnectorProps.remove(nodeConnector);
+ }
+
+ // update sal
+ for (IPluginOutInventoryService service : pluginOutInventoryServices) {
+ service.updateNode(node, UpdateType.REMOVED, null);
+ }
+ }
+
+ private void updateNode(Node node, Set<Property> properties) {
+ logger.trace("{} updated, props: {}", node, properties);
+ if (nodeProps == null || !nodeProps.containsKey(node) ||
+ properties == null || properties.isEmpty()) {
+ return;
+ }
+
+ // Update local cache with new properties
+ Set<Property> newProperties = new HashSet<Property>(properties.size());
+ Map<String, Property> propertyMap = nodeProps.get(node);
+ for (Property property : properties) {
+ String name = property.getName();
+ Property currentProperty = propertyMap.get(name);
+ if (!property.equals(currentProperty)) {
+ propertyMap.put(name, property);
+ newProperties.add(property);
+ }
+ }
+
+ // Update SAL if we got new properties
+ if (!newProperties.isEmpty()) {
+ for (IPluginOutInventoryService service : pluginOutInventoryServices) {
+ service.updateNode(node, UpdateType.CHANGED, newProperties);
+ }
+ }
+ }
+
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ switch (type) {
+ case ADDED:
+ addNode(node, props);
+ break;
+ case REMOVED:
+ removeNode(node);
+ break;
+ case CHANGED:
+ updateNode(node, props);
+ break;
+ default:
+ break;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.AdvertisedBandwidth;
+import org.opendaylight.controller.sal.core.SupportedBandwidth;
+import org.opendaylight.controller.sal.core.PeerBandwidth;
+import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.Name;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.State;
+
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.openflow.protocol.OFPhysicalPort;
+import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
+import org.openflow.protocol.OFPhysicalPort.OFPortFeatures;
+import org.openflow.protocol.OFPhysicalPort.OFPortState;
+
+/**
+ * The class provides helper functions to retrieve inventory properties from
+ * OpenFlow messages
+ */
+public class InventoryServiceHelper {
+ /*
+ * Returns BandWidth property from OpenFlow OFPhysicalPort features
+ */
+ public static Bandwidth OFPortToBandWidth(int portFeatures) {
+ Bandwidth bw = null;
+ int value = portFeatures
+ & (OFPortFeatures.OFPPF_10MB_FD.getValue()
+ | OFPortFeatures.OFPPF_10MB_HD.getValue()
+ | OFPortFeatures.OFPPF_100MB_FD.getValue()
+ | OFPortFeatures.OFPPF_100MB_HD.getValue()
+ | OFPortFeatures.OFPPF_1GB_FD.getValue()
+ | OFPortFeatures.OFPPF_1GB_HD.getValue() | OFPortFeatures.OFPPF_10GB_FD
+ .getValue());
+
+ switch (value) {
+ case 1:
+ case 2:
+ bw = new Bandwidth(Bandwidth.BW10Mbps);
+ break;
+ case 4:
+ case 8:
+ bw = new Bandwidth(Bandwidth.BW100Mbps);
+ break;
+ case 16:
+ case 32:
+ bw = new Bandwidth(Bandwidth.BW1Gbps);
+ break;
+ case 64:
+ bw = new Bandwidth(Bandwidth.BW10Gbps);
+ break;
+ default:
+ break;
+ }
+ return bw;
+ }
+
+ /*
+ * Returns Config property from OpenFLow OFPhysicalPort config
+ */
+ public static Config OFPortToConfig(int portConfig) {
+ Config config;
+ if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & portConfig) != 0)
+ config = new Config(Config.ADMIN_DOWN);
+ else
+ config = new Config(Config.ADMIN_UP);
+ return config;
+ }
+
+ /*
+ * Returns State property from OpenFLow OFPhysicalPort state
+ */
+ public static State OFPortToState(int portState) {
+ State state;
+ if ((OFPortState.OFPPS_LINK_DOWN.getValue() & portState) != 0)
+ state = new State(State.EDGE_DOWN);
+ else
+ state = new State(State.EDGE_UP);
+ return state;
+ }
+
+ /*
+ * Returns set of properties from OpenFLow OFPhysicalPort
+ */
+ public static Set<Property> OFPortToProps(OFPhysicalPort port) {
+ Set<Property> props = new HashSet<Property>();
+ Bandwidth bw = InventoryServiceHelper.OFPortToBandWidth(port
+ .getCurrentFeatures());
+ if (bw != null) {
+ props.add(bw);
+ }
+
+ Bandwidth abw = InventoryServiceHelper.OFPortToBandWidth(port.getAdvertisedFeatures());
+ if (abw != null) {
+ AdvertisedBandwidth a = new AdvertisedBandwidth(abw.getValue());
+ if (a != null) {
+ props.add(a);
+ }
+ }
+ Bandwidth sbw = InventoryServiceHelper.OFPortToBandWidth(port.getSupportedFeatures());
+ if (sbw != null) {
+ SupportedBandwidth s = new SupportedBandwidth(sbw.getValue());
+ if (s != null) {
+ props.add(s);
+ }
+ }
+ Bandwidth pbw = InventoryServiceHelper.OFPortToBandWidth(port.getPeerFeatures());
+ if (pbw != null) {
+ PeerBandwidth p = new PeerBandwidth(pbw.getValue());
+ if (p != null) {
+ props.add(p);
+ }
+ }
+ props.add(new Name(port.getName()));
+ props.add(InventoryServiceHelper.OFPortToConfig(port.getConfig()));
+ props.add(InventoryServiceHelper.OFPortToState(port.getState()));
+ return props;
+ }
+
+ /*
+ * Returns set of properties for each nodeConnector in an OpenFLow switch
+ */
+ public static Map<NodeConnector, Set<Property>> OFSwitchToProps(ISwitch sw) {
+ Map<NodeConnector, Set<Property>> ncProps = new HashMap<NodeConnector, Set<Property>>();
+
+ if (sw == null) {
+ return ncProps;
+ }
+
+ Node node = NodeCreator.createOFNode(sw.getId());
+ if (node == null) {
+ return ncProps;
+ }
+
+ Set<Property> props;
+ NodeConnector nodeConnector;
+ OFPhysicalPort port;
+ Map<Short, OFPhysicalPort> ports = sw.getPhysicalPorts();
+ for (Map.Entry<Short, OFPhysicalPort> entry : ports.entrySet()) {
+ nodeConnector = PortConverter.toNodeConnector(entry.getKey(), node);
+ port = entry.getValue();
+ props = InventoryServiceHelper.OFPortToProps(port);
+ ncProps.put(nodeConnector, props);
+ }
+
+ return ncProps;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Actions;
+import org.opendaylight.controller.sal.core.Buffers;
+import org.opendaylight.controller.sal.core.Capabilities;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Description;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.MacAddress;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.Tables;
+import org.opendaylight.controller.sal.core.TimeStamp;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimInternalListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsListener;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.IMessageListener;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.opendaylight.openflowplugin.openflow.core.ISwitchStateListener;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPortStatus;
+import org.openflow.protocol.OFPortStatus.OFPortReason;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.statistics.OFDescriptionStatistics;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The class describes a shim layer that bridges inventory events from Openflow
+ * core to various listeners. The notifications are filtered based on container
+ * configurations.
+ *
+ *
+ */
+public class InventoryServiceShim implements IContainerListener,
+ IMessageListener, ISwitchStateListener, IOFStatisticsListener {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(InventoryServiceShim.class);
+ private IController controller = null;
+ private final ConcurrentMap<String, IInventoryShimInternalListener> inventoryShimInternalListeners = new ConcurrentHashMap<String, IInventoryShimInternalListener>();
+ private final Set<IInventoryShimInternalListener> globalInventoryShimInternalListeners = new HashSet<IInventoryShimInternalListener>();
+ private final List<IInventoryShimExternalListener> inventoryShimExternalListeners = new CopyOnWriteArrayList<IInventoryShimExternalListener>();
+ private final ConcurrentMap<NodeConnector, Set<String>> nodeConnectorContainerMap = new ConcurrentHashMap<NodeConnector, Set<String>>();
+ private final ConcurrentMap<Node, Set<String>> nodeContainerMap = new ConcurrentHashMap<Node, Set<String>>();
+ private final ConcurrentMap<NodeConnector, Set<Property>> nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Set<Property>>();
+ private final ConcurrentMap<Node, Set<Property>> nodeProps = new ConcurrentHashMap<Node, Set<Property>>();
+ private IPluginOutConnectionService connectionOutService;
+
+ void setController(IController s) {
+ this.controller = s;
+ }
+
+ void unsetController(IController s) {
+ if (this.controller == s) {
+ this.controller = null;
+ }
+ }
+
+ void setInventoryShimGlobalInternalListener(Map<?, ?> props,
+ IInventoryShimInternalListener s) {
+ if ((this.globalInventoryShimInternalListeners != null)) {
+ this.globalInventoryShimInternalListeners.add(s);
+ }
+ }
+
+ void unsetInventoryShimGlobalInternalListener(Map<?, ?> props,
+ IInventoryShimInternalListener s) {
+ if ((this.globalInventoryShimInternalListeners != null)) {
+ this.globalInventoryShimInternalListeners.remove(s);
+ }
+ }
+
+ void setInventoryShimInternalListener(Map<?, ?> props,
+ IInventoryShimInternalListener s) {
+ if (props == null) {
+ logger.error("setInventoryShimInternalListener property is null");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("setInventoryShimInternalListener containerName not supplied");
+ return;
+ }
+ if ((this.inventoryShimInternalListeners != null)
+ && !this.inventoryShimInternalListeners.containsValue(s)) {
+ this.inventoryShimInternalListeners.put(containerName, s);
+ logger.trace(
+ "Added inventoryShimInternalListener for container {}",
+ containerName);
+ }
+ }
+
+ void unsetInventoryShimInternalListener(Map<?, ?> props,
+ IInventoryShimInternalListener s) {
+ if (props == null) {
+ logger.error("unsetInventoryShimInternalListener property is null");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("setInventoryShimInternalListener containerName not supplied");
+ return;
+ }
+ if ((this.inventoryShimInternalListeners != null)
+ && this.inventoryShimInternalListeners.get(containerName) != null
+ && this.inventoryShimInternalListeners.get(containerName)
+ .equals(s)) {
+ this.inventoryShimInternalListeners.remove(containerName);
+ logger.trace(
+ "Removed inventoryShimInternalListener for container {}",
+ containerName);
+ }
+ }
+
+ void setInventoryShimExternalListener(IInventoryShimExternalListener s) {
+ logger.trace("Set inventoryShimExternalListener");
+ if ((this.inventoryShimExternalListeners != null)
+ && !this.inventoryShimExternalListeners.contains(s)) {
+ this.inventoryShimExternalListeners.add(s);
+ }
+ }
+
+ void unsetInventoryShimExternalListener(IInventoryShimExternalListener s) {
+ if ((this.inventoryShimExternalListeners != null)
+ && this.inventoryShimExternalListeners.contains(s)) {
+ this.inventoryShimExternalListeners.remove(s);
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ this.controller.addMessageListener(OFType.PORT_STATUS, this);
+ this.controller.addSwitchStateListener(this);
+ }
+
+ /**
+ * Function called after registering the service in OSGi service registry.
+ */
+ void started() {
+ /* Start with existing switches */
+ startService();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ this.controller.removeMessageListener(OFType.PACKET_IN, this);
+ this.controller.removeSwitchStateListener(this);
+
+ this.inventoryShimInternalListeners.clear();
+ this.nodeConnectorContainerMap.clear();
+ this.nodeContainerMap.clear();
+ this.globalInventoryShimInternalListeners.clear();
+ this.controller = null;
+ }
+
+ @Override
+ public void receive(ISwitch sw, OFMessage msg) {
+ if (msg instanceof OFPortStatus) {
+ handlePortStatusMessage(sw, (OFPortStatus) msg);
+ }
+ return;
+ }
+
+ protected void handlePortStatusMessage(ISwitch sw, OFPortStatus m) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ NodeConnector nodeConnector = PortConverter.toNodeConnector(
+ m.getDesc().getPortNumber(), node);
+ // get node connector properties
+ Set<Property> props = InventoryServiceHelper.OFPortToProps(m.getDesc());
+
+ UpdateType type = null;
+ if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
+ type = UpdateType.ADDED;
+ nodeConnectorProps.put(nodeConnector, props);
+ } else if (m.getReason() == (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
+ type = UpdateType.REMOVED;
+ nodeConnectorProps.remove(nodeConnector);
+ } else if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
+ type = UpdateType.CHANGED;
+ nodeConnectorProps.put(nodeConnector, props);
+ }
+
+ logger.trace("handlePortStatusMessage {} type {}", nodeConnector, type);
+
+ if (type != null) {
+ notifyInventoryShimListener(nodeConnector, type, props);
+ }
+ }
+
+ @Override
+ public void switchAdded(ISwitch sw) {
+ if (sw == null) {
+ return;
+ }
+
+ // Add all the nodeConnectors of this switch
+ Map<NodeConnector, Set<Property>> ncProps = InventoryServiceHelper
+ .OFSwitchToProps(sw);
+ for (Map.Entry<NodeConnector, Set<Property>> entry : ncProps.entrySet()) {
+ Set<Property> props = new HashSet<Property>();
+ Set<Property> prop = entry.getValue();
+ if (prop != null) {
+ props.addAll(prop);
+ }
+ nodeConnectorProps.put(entry.getKey(), props);
+ notifyInventoryShimListener(entry.getKey(), UpdateType.ADDED,
+ entry.getValue());
+ }
+
+ // Add this node
+ addNode(sw);
+ }
+
+ @Override
+ public void switchDeleted(ISwitch sw) {
+ if (sw == null) {
+ return;
+ }
+
+ removeNode(sw);
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+ // do nothing
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag,
+ short newTag, UpdateType t) {
+ logger.debug("tagUpdated: {} type {} for container {}", new Object[] {
+ n, t, containerName });
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName,
+ ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector nc, UpdateType t) {
+ logger.debug("nodeConnectorUpdated: {} type {} for container {}", new Object[] { nc, t, containerName });
+ Node node = nc.getNode();
+ Set<String> ncContainers = this.nodeConnectorContainerMap.get(nc);
+ Set<String> nodeContainers = this.nodeContainerMap.get(node);
+ if (ncContainers == null) {
+ ncContainers = new CopyOnWriteArraySet<String>();
+ }
+ if (nodeContainers == null) {
+ nodeContainers = new CopyOnWriteArraySet<String>();
+ }
+ boolean notifyNodeUpdate = false;
+
+ switch (t) {
+ case ADDED:
+ if (ncContainers.add(containerName)) {
+ this.nodeConnectorContainerMap.put(nc, ncContainers);
+ }
+ if (nodeContainers.add(containerName)) {
+ this.nodeContainerMap.put(node, nodeContainers);
+ notifyNodeUpdate = true;
+ }
+ break;
+ case REMOVED:
+ if (ncContainers.remove(containerName)) {
+ if (ncContainers.isEmpty()) {
+ // Do cleanup to reduce memory footprint if no
+ // elements to be tracked
+ this.nodeConnectorContainerMap.remove(nc);
+ } else {
+ this.nodeConnectorContainerMap.put(nc, ncContainers);
+ }
+ }
+ boolean nodeContainerUpdate = true;
+ for (NodeConnector ncContainer : nodeConnectorContainerMap.keySet()) {
+ if ((ncContainer.getNode().equals(node)) && (nodeConnectorContainerMap.get(ncContainer).contains(containerName))) {
+ nodeContainerUpdate = false;
+ break;
+ }
+ }
+ if (nodeContainerUpdate) {
+ nodeContainers.remove(containerName);
+ notifyNodeUpdate = true;
+ if (nodeContainers.isEmpty()) {
+ this.nodeContainerMap.remove(node);
+ } else {
+ this.nodeContainerMap.put(node, nodeContainers);
+ }
+ }
+ break;
+ case CHANGED:
+ break;
+ }
+
+ Set<Property> nodeProp = nodeProps.get(node);
+ if (nodeProp == null) {
+ return;
+ }
+ Set<Property> ncProp = nodeConnectorProps.get(nc);
+ // notify InventoryService
+ notifyInventoryShimInternalListener(containerName, nc, t, ncProp);
+
+ if (notifyNodeUpdate) {
+ notifyInventoryShimInternalListener(containerName, node, t, nodeProp);
+ }
+ }
+
+ private void notifyInventoryShimExternalListener(Node node,
+ UpdateType type, Set<Property> props) {
+ for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) {
+ s.updateNode(node, type, props);
+ }
+ }
+
+ private void notifyInventoryShimExternalListener(
+ NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
+ for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) {
+ s.updateNodeConnector(nodeConnector, type, props);
+ }
+ }
+
+ private void notifyInventoryShimInternalListener(String container,
+ NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
+ IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners
+ .get(container);
+ if (inventoryShimInternalListener != null) {
+ inventoryShimInternalListener.updateNodeConnector(nodeConnector,
+ type, props);
+ logger.trace(
+ "notifyInventoryShimInternalListener {} type {} for container {}",
+ new Object[] { nodeConnector, type, container });
+ }
+ }
+
+ /*
+ * Notify all internal and external listeners
+ */
+ private void notifyInventoryShimListener(NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
+ notifyGlobalInventoryShimInternalListener(nodeConnector, type, props);
+ /*
+ * isLocal is intentionally moved after the GlobalInventory listener call.
+ * The above notification to GlobalInventory will make sure that the connectionOutService be ready
+ * to reply to isLocal query.
+ */
+ if (!connectionOutService.isLocal(nodeConnector.getNode())) {
+ logger.debug("Connection service dropped the inventory notification for {} {}", nodeConnector.toString(), type);
+ return;
+ } else {
+ logger.debug("Connection service accepted the inventory notification for {} {}", nodeConnector.toString(), type);
+ }
+
+ // notify other containers
+ Set<String> containers = (nodeConnectorContainerMap.get(nodeConnector) == null) ? new HashSet<String>()
+ : new HashSet<String>(nodeConnectorContainerMap.get(nodeConnector));
+ containers.add(GlobalConstants.DEFAULT.toString());
+ for (String container : containers) {
+ notifyInventoryShimInternalListener(container, nodeConnector, type, props);
+ }
+
+ // Notify DiscoveryService
+ notifyInventoryShimExternalListener(nodeConnector, type, props);
+ }
+
+ /*
+ * Notify all internal and external listeners
+ */
+ private void notifyInventoryShimListener(Node node, UpdateType type, Set<Property> props) {
+ notifyGlobalInventoryShimInternalListener(node, type, props);
+ /*
+ * isLocal is intentionally moved after the GlobalInventory listener call.
+ * The above notification to GlobalInventory will make sure that the connectionOutService be ready
+ * to reply to isLocal query.
+ */
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("Connection service dropped the inventory notification for {} {}", node.toString(), type);
+ return;
+ } else {
+ logger.debug("Connection service accepted the inventory notification for {} {}", node.toString(), type);
+ }
+ // Now notify other containers
+ Set<String> containers = (nodeContainerMap.get(node) == null) ? new HashSet<String>() : new HashSet<String>(
+ nodeContainerMap.get(node));
+ containers.add(GlobalConstants.DEFAULT.toString());
+ for (String container : containers) {
+ notifyInventoryShimInternalListener(container, node, type, props);
+ }
+
+ // Notify external listener
+ notifyInventoryShimExternalListener(node, type, props);
+ }
+
+ private void notifyGlobalInventoryShimInternalListener(Node node, UpdateType type, Set<Property> props) {
+ for (IInventoryShimInternalListener globalListener : globalInventoryShimInternalListeners) {
+ globalListener.updateNode(node, type, props);
+ logger.trace(
+ "notifyGlobalInventoryShimInternalListener {} type {}",
+ new Object[] { node, type });
+ }
+ }
+
+ private void notifyGlobalInventoryShimInternalListener(NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
+ for (IInventoryShimInternalListener globalListener : globalInventoryShimInternalListeners) {
+ globalListener.updateNodeConnector(nodeConnector, type, props);
+ logger.trace(
+ "notifyGlobalInventoryShimInternalListener {} type {}",
+ new Object[] { nodeConnector, type });
+ }
+ }
+
+ private void notifyInventoryShimInternalListener(String container,
+ Node node, UpdateType type, Set<Property> props) {
+ IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners
+ .get(container);
+ if (inventoryShimInternalListener != null) {
+ inventoryShimInternalListener.updateNode(node, type, props);
+ logger.trace(
+ "notifyInventoryShimInternalListener {} type {} for container {}",
+ new Object[] { node, type, container });
+ }
+ }
+
+ private void addNode(ISwitch sw) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ UpdateType type = UpdateType.ADDED;
+
+ Set<Property> props = new HashSet<Property>();
+ Long sid = (Long) node.getID();
+
+ Date connectedSince = controller.getSwitches().get(sid)
+ .getConnectedDate();
+ Long connectedSinceTime = (connectedSince == null) ? 0 : connectedSince
+ .getTime();
+ props.add(new TimeStamp(connectedSinceTime, "connectedSince"));
+ props.add(new MacAddress(deriveMacAddress(sid)));
+
+ byte tables = sw.getTables();
+ Tables t = new Tables(tables);
+ if (t != null) {
+ props.add(t);
+ }
+ int cap = sw.getCapabilities();
+ Capabilities c = new Capabilities(cap);
+ if (c != null) {
+ props.add(c);
+ }
+ int act = sw.getActions();
+ Actions a = new Actions(act);
+ if (a != null) {
+ props.add(a);
+ }
+ int buffers = sw.getBuffers();
+ Buffers b = new Buffers(buffers);
+ if (b != null) {
+ props.add(b);
+ }
+
+ nodeProps.put(node, props);
+ // Notify all internal and external listeners
+ notifyInventoryShimListener(node, type, props);
+ }
+
+ private void removeNode(ISwitch sw) {
+ Node node = NodeCreator.createOFNode(sw.getId());
+ if(node == null) {
+ return;
+ }
+ removeNodeConnectorProps(node);
+ nodeProps.remove(node);
+ UpdateType type = UpdateType.REMOVED;
+ // Notify all internal and external listeners
+ notifyInventoryShimListener(node, type, null);
+ }
+
+ private void startService() {
+ // Get a snapshot of all the existing switches
+ Map<Long, ISwitch> switches = this.controller.getSwitches();
+ for (ISwitch sw : switches.values()) {
+ switchAdded(sw);
+ }
+ }
+
+ private void removeNodeConnectorProps(Node node) {
+ List<NodeConnector> ncList = new ArrayList<NodeConnector>();
+ for (NodeConnector nc : nodeConnectorProps.keySet()) {
+ if (nc.getNode().equals(node)) {
+ ncList.add(nc);
+ }
+ }
+ for (NodeConnector nc : ncList) {
+ nodeConnectorProps.remove(nc);
+ }
+ }
+
+ @Override
+ public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> descriptionStats) {
+ Node node = NodeCreator.createOFNode(switchId);
+ Set<Property> properties = new HashSet<Property>(1);
+ OFDescriptionStatistics ofDesc = (OFDescriptionStatistics) descriptionStats.get(0);
+ Description desc = new Description(ofDesc.getDatapathDescription());
+ properties.add(desc);
+
+ // Notify all internal and external listeners
+ notifyInventoryShimListener(node, UpdateType.CHANGED, properties);
+ }
+
+ private byte[] deriveMacAddress(long dpid) {
+ byte[] mac = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ for (short i = 0; i < 6; i++) {
+ mac[5 - i] = (byte) dpid;
+ dpid >>= 8;
+ }
+
+ return mac;
+ }
+
+ @Override
+ public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
+ // Nothing to do
+ }
+
+ @Override
+ public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
+ // Nothing to do
+ }
+
+ @Override
+ public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables) {
+ // Nothing to do
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsManager;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.opendaylight.openflowplugin.openflow.core.ISwitch;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6Match;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6StatsReply;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6StatsRequest;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFStatisticsRequest;
+import org.openflow.protocol.statistics.OFAggregateStatisticsRequest;
+import org.openflow.protocol.statistics.OFFlowStatisticsReply;
+import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
+import org.openflow.protocol.statistics.OFPortStatisticsReply;
+import org.openflow.protocol.statistics.OFPortStatisticsRequest;
+import org.openflow.protocol.statistics.OFQueueStatisticsRequest;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+import org.openflow.protocol.statistics.OFTableStatistics;
+import org.openflow.protocol.statistics.OFVendorStatistics;
+import org.openflow.util.HexString;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * It periodically polls the different OF statistics from the OF switches and
+ * caches them for quick retrieval for the above layers' modules It also
+ * provides an API to directly query the switch about the statistics
+ */
+public class OFStatisticsManager implements IOFStatisticsManager,
+IInventoryShimExternalListener, CommandProvider {
+ private static final Logger log = LoggerFactory.getLogger(OFStatisticsManager.class);
+ private static final int INITIAL_SIZE = 64;
+ private static final long FLOW_STATS_PERIOD = 10000;
+ private static final long DESC_STATS_PERIOD = 60000;
+ private static final long PORT_STATS_PERIOD = 5000;
+ private static final long TABLE_STATS_PERIOD = 10000;
+ private static final long TICK = 1000;
+ private static short statisticsTickNumber = (short) (FLOW_STATS_PERIOD / TICK);
+ private static short descriptionTickNumber = (short) (DESC_STATS_PERIOD / TICK);
+ private static short portTickNumber = (short) (PORT_STATS_PERIOD / TICK);
+ private static short tableTickNumber = (short) (TABLE_STATS_PERIOD / TICK);
+ private static short factoredSamples = (short) 2;
+ private static short counter = 1;
+ private IController controller = null;
+ private ConcurrentMap<Long, List<OFStatistics>> flowStatistics;
+ private ConcurrentMap<Long, List<OFStatistics>> descStatistics;
+ private ConcurrentMap<Long, List<OFStatistics>> portStatistics;
+ private ConcurrentMap<Long, List<OFStatistics>> tableStatistics;
+ private List<OFStatistics> dummyList;
+ private ConcurrentMap<Long, StatisticsTicks> statisticsTimerTicks;
+ protected BlockingQueue<StatsRequest> pendingStatsRequests;
+ protected BlockingQueue<Long> switchPortStatsUpdated;
+ private Thread statisticsCollector;
+ private Thread txRatesUpdater;
+ private Timer statisticsTimer;
+ private TimerTask statisticsTimerTask;
+ private ConcurrentMap<Long, Boolean> switchSupportsVendorExtStats;
+ // Per port sampled (every portStatsPeriod) transmit rate
+ private Map<Long, Map<Short, TxRates>> txRates;
+ private Set<IOFStatisticsListener> statisticsListeners;
+
+ /**
+ * The object containing the latest factoredSamples tx rate samples for a
+ * given switch port
+ */
+ protected class TxRates {
+ // contains the latest factoredSamples sampled transmitted bytes
+ Deque<Long> sampledTxBytes;
+
+ public TxRates() {
+ sampledTxBytes = new LinkedBlockingDeque<Long>();
+ }
+
+ public void update(Long txBytes) {
+ /*
+ * Based on how many samples our average works on, we might have to
+ * remove the oldest sample
+ */
+ if (sampledTxBytes.size() == factoredSamples) {
+ sampledTxBytes.removeLast();
+ }
+
+ // Add the latest sample to the top of the queue
+ sampledTxBytes.addFirst(txBytes);
+ }
+
+ /**
+ * Returns the average transmit rate in bps
+ *
+ * @return the average transmit rate [bps]
+ */
+ public long getAverageTxRate() {
+ long average = 0;
+ /*
+ * If we cannot provide the value for the time window length set
+ */
+ if (sampledTxBytes.size() < factoredSamples) {
+ return average;
+ }
+ long increment = sampledTxBytes.getFirst() - sampledTxBytes
+ .getLast();
+ long timePeriod = factoredSamples * PORT_STATS_PERIOD / TICK;
+ average = (8L * increment) / timePeriod;
+ return average;
+ }
+ }
+
+ public void setController(IController core) {
+ this.controller = core;
+ }
+
+ public void unsetController(IController core) {
+ if (this.controller == core) {
+ this.controller = null;
+ }
+ }
+
+ private short getStatsQueueSize() {
+ String statsQueueSizeStr = System.getProperty("of.statsQueueSize");
+ short statsQueueSize = INITIAL_SIZE;
+ if (statsQueueSizeStr != null) {
+ try {
+ statsQueueSize = Short.parseShort(statsQueueSizeStr);
+ if (statsQueueSize <= 0) {
+ statsQueueSize = INITIAL_SIZE;
+ }
+ } catch (Exception e) {
+ }
+ }
+ return statsQueueSize;
+ }
+
+ IPluginOutConnectionService connectionPluginOutService;
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionPluginOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionPluginOutService == s) {
+ connectionPluginOutService = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ flowStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
+ descStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
+ portStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
+ tableStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
+ dummyList = new ArrayList<OFStatistics>(1);
+ pendingStatsRequests = new LinkedBlockingQueue<StatsRequest>(getStatsQueueSize());
+ statisticsTimerTicks = new ConcurrentHashMap<Long, StatisticsTicks>(INITIAL_SIZE);
+ switchPortStatsUpdated = new LinkedBlockingQueue<Long>(INITIAL_SIZE);
+ switchSupportsVendorExtStats = new ConcurrentHashMap<Long, Boolean>(INITIAL_SIZE);
+ txRates = new HashMap<Long, Map<Short, TxRates>>(INITIAL_SIZE);
+ statisticsListeners = new CopyOnWriteArraySet<IOFStatisticsListener>();
+
+ configStatsPollIntervals();
+
+ // Initialize managed timers
+ statisticsTimer = new Timer();
+ statisticsTimerTask = new TimerTask() {
+ @Override
+ public void run() {
+ decrementTicks();
+ }
+ };
+
+ // Initialize Statistics collector thread
+ statisticsCollector = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ StatsRequest req = pendingStatsRequests.take();
+ queryStatisticsInternal(req.switchId, req.type);
+ } catch (InterruptedException e) {
+ log.warn("Flow Statistics Collector thread "
+ + "interrupted", e);
+ }
+ }
+ }
+ }, "Statistics Collector");
+
+ // Initialize Tx Rate Updater thread
+ txRatesUpdater = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ long switchId = switchPortStatsUpdated.take();
+ updatePortsTxRate(switchId);
+ } catch (InterruptedException e) {
+ log.warn("TX Rate Updater thread interrupted", e);
+ }
+ }
+ }
+ }, "TX Rate Updater");
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ // Start managed timers
+ statisticsTimer.scheduleAtFixedRate(statisticsTimerTask, 0, TICK);
+
+ // Start statistics collector thread
+ statisticsCollector.start();
+
+ // Start bandwidth utilization computer thread
+ txRatesUpdater.start();
+
+ // OSGI console
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ // Stop managed timers
+ statisticsTimer.cancel();
+ }
+
+ public void setStatisticsListener(IOFStatisticsListener s) {
+ this.statisticsListeners.add(s);
+ }
+
+ public void unsetStatisticsListener(IOFStatisticsListener s) {
+ if (s != null) {
+ this.statisticsListeners.remove(s);
+ }
+ }
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this, null);
+ }
+
+ private static class StatsRequest {
+ protected Long switchId;
+ protected OFStatisticsType type;
+
+ public StatsRequest(Long d, OFStatisticsType t) {
+ switchId = d;
+ type = t;
+ }
+
+ @Override
+ public String toString() {
+ return "SReq = {switchId=" + switchId + ", type=" + type + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((switchId == null) ? 0 : switchId.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.ordinal());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ StatsRequest other = (StatsRequest) obj;
+ if (switchId == null) {
+ if (other.switchId != null) {
+ return false;
+ }
+ } else if (!switchId.equals(other.switchId)) {
+ return false;
+ }
+ if (type != other.type) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private void addStatisticsTicks(Long switchId) {
+ switchSupportsVendorExtStats.put(switchId, Boolean.TRUE); // Assume
+ // switch
+ // supports
+ // Vendor
+ // extension
+ // stats
+ statisticsTimerTicks.put(switchId, new StatisticsTicks(true));
+ log.debug("Added Switch {} to target pool",
+ HexString.toHexString(switchId.longValue()));
+ }
+
+ protected static class StatisticsTicks {
+ private short flowStatisticsTicks;
+ private short descriptionTicks;
+ private short portStatisticsTicks;
+ private short tableStatisticsTicks;
+
+ public StatisticsTicks(boolean scattered) {
+ if (scattered) {
+ // scatter bursts by statisticsTickPeriod
+ if (++counter < 0) {
+ counter = 0;
+ } // being paranoid here
+ flowStatisticsTicks = (short) (1 + counter
+ % statisticsTickNumber);
+ descriptionTicks = (short) (1 + counter % descriptionTickNumber);
+ portStatisticsTicks = (short) (1 + counter % portTickNumber);
+ tableStatisticsTicks = (short) (1 + counter % tableTickNumber);
+ } else {
+ flowStatisticsTicks = statisticsTickNumber;
+ descriptionTicks = descriptionTickNumber;
+ portStatisticsTicks = portTickNumber;
+ tableStatisticsTicks = tableTickNumber;
+ }
+ }
+
+ public boolean decrementFlowTicksIsZero() {
+ // Please ensure no code is inserted between the if check and the
+ // flowStatisticsTicks reset
+ if (--flowStatisticsTicks == 0) {
+ flowStatisticsTicks = statisticsTickNumber;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean decrementDescTicksIsZero() {
+ // Please ensure no code is inserted between the if check and the
+ // descriptionTicks reset
+ if (--descriptionTicks == 0) {
+ descriptionTicks = descriptionTickNumber;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean decrementPortTicksIsZero() {
+ // Please ensure no code is inserted between the if check and the
+ // descriptionTicks reset
+ if (--portStatisticsTicks == 0) {
+ portStatisticsTicks = portTickNumber;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean decrementTableTicksIsZero() {
+ // Please ensure no code is inserted between the if check and the
+ // descriptionTicks reset
+ if(--tableStatisticsTicks == 0) {
+ tableStatisticsTicks = tableTickNumber;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{fT=" + flowStatisticsTicks + ",dT=" + descriptionTicks
+ + ",pT=" + portStatisticsTicks + ",tT=" + tableStatisticsTicks + "}";
+ }
+ }
+
+ private void printInfoMessage(String type, StatsRequest request) {
+ log.info("{} stats request not inserted for switch: {}. Queue size: {}. Collector state: {}.",
+ new Object[] {type, HexString.toHexString(request.switchId), pendingStatsRequests.size(),
+ statisticsCollector.getState().toString() });
+ }
+
+ protected void decrementTicks() {
+ StatsRequest request = null;
+ for (Map.Entry<Long, StatisticsTicks> entry : statisticsTimerTicks
+ .entrySet()) {
+ StatisticsTicks clock = entry.getValue();
+ Long switchId = entry.getKey();
+ if (clock.decrementFlowTicksIsZero()) {
+ request = (switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) ?
+ new StatsRequest(switchId, OFStatisticsType.VENDOR) :
+ new StatsRequest(switchId, OFStatisticsType.FLOW);
+ // If a request for this switch is already in the queue, skip to
+ // add this new request
+ if (!pendingStatsRequests.contains(request)
+ && false == pendingStatsRequests.offer(request)) {
+ printInfoMessage("Flow", request);
+ }
+ }
+
+ if (clock.decrementDescTicksIsZero()) {
+ request = new StatsRequest(switchId, OFStatisticsType.DESC);
+ // If a request for this switch is already in the queue, skip to
+ // add this new request
+ if (!pendingStatsRequests.contains(request)
+ && false == pendingStatsRequests.offer(request)) {
+ printInfoMessage("Description", request);
+ }
+ }
+
+ if (clock.decrementPortTicksIsZero()) {
+ request = new StatsRequest(switchId, OFStatisticsType.PORT);
+ // If a request for this switch is already in the queue, skip to
+ // add this new request
+ if (!pendingStatsRequests.contains(request)
+ && false == pendingStatsRequests.offer(request)) {
+ printInfoMessage("Port", request);
+ }
+ }
+
+ if(clock.decrementTableTicksIsZero()) {
+ request = new StatsRequest(switchId, OFStatisticsType.TABLE);
+ // If a request for this switch is already in the queue, skip to
+ // add this new request
+ if (!pendingStatsRequests.contains(request)
+ && false == pendingStatsRequests.offer(request)) {
+ printInfoMessage("Table", request);
+ }
+ }
+ }
+ }
+
+ private void removeStatsRequestTasks(Long switchId) {
+ log.debug("Cleaning Statistics database for switch {}",
+ HexEncode.longToHexString(switchId));
+ // To be safe, let's attempt removal of both VENDOR and FLOW request. It
+ // does not hurt
+ pendingStatsRequests.remove(new StatsRequest(switchId,
+ OFStatisticsType.VENDOR));
+ pendingStatsRequests.remove(new StatsRequest(switchId,
+ OFStatisticsType.FLOW));
+ pendingStatsRequests.remove(new StatsRequest(switchId,
+ OFStatisticsType.DESC));
+ pendingStatsRequests.remove(new StatsRequest(switchId,
+ OFStatisticsType.PORT));
+ pendingStatsRequests.remove(new StatsRequest(switchId,
+ OFStatisticsType.TABLE));
+ // Take care of the TX rate databases
+ switchPortStatsUpdated.remove(switchId);
+ txRates.remove(switchId);
+ }
+
+ private void clearFlowStatsAndTicks(Long switchId) {
+ statisticsTimerTicks.remove(switchId);
+ removeStatsRequestTasks(switchId);
+ flowStatistics.remove(switchId);
+ log.debug("Statistics removed for switch {}",
+ HexString.toHexString(switchId));
+ }
+
+ private void queryStatisticsInternal(Long switchId, OFStatisticsType statType) {
+
+ // Query the switch on all matches
+ List<OFStatistics> values = this.fetchStatisticsFromSwitch(switchId, statType, null);
+
+ // If got a valid response update local cache and notify listeners
+ if (values != null && !values.isEmpty()) {
+ switch (statType) {
+ case FLOW:
+ case VENDOR:
+ flowStatistics.put(switchId, values);
+ notifyFlowUpdate(switchId, values);
+ break;
+ case DESC:
+ // Overwrite cache
+ descStatistics.put(switchId, values);
+ // Notify who may be interested in a description change
+ notifyDescriptionUpdate(switchId, values);
+ break;
+ case PORT:
+ // Overwrite cache with new port statistics for this switch
+ portStatistics.put(switchId, values);
+
+ // Wake up the thread which maintains the TX byte counters for
+ // each port
+ switchPortStatsUpdated.offer(switchId);
+ notifyPortUpdate(switchId, values);
+ break;
+ case TABLE:
+ // Overwrite cache
+ tableStatistics.put(switchId, values);
+ notifyTableUpdate(switchId, values);
+ break;
+ default:
+ }
+ }
+ }
+
+ private void notifyDescriptionUpdate(Long switchId, List<OFStatistics> values) {
+ for (IOFStatisticsListener l : this.statisticsListeners) {
+ l.descriptionStatisticsRefreshed(switchId, values);
+ }
+ }
+
+ private void notifyFlowUpdate(Long switchId, List<OFStatistics> values) {
+ if (values.get(0) instanceof OFVendorStatistics) {
+ values = this.v6StatsListToOFStatsList(values);
+ }
+
+ for (IOFStatisticsListener l : this.statisticsListeners) {
+ l.flowStatisticsRefreshed(switchId, values);
+ }
+
+ }
+
+ private void notifyPortUpdate(Long switchId, List<OFStatistics> values) {
+ for (IOFStatisticsListener l : this.statisticsListeners) {
+ l.portStatisticsRefreshed(switchId, values);
+ }
+ }
+
+ private void notifyTableUpdate(Long switchId, List<OFStatistics> values) {
+ for (IOFStatisticsListener l : this.statisticsListeners) {
+ l.tableStatisticsRefreshed(switchId, values);
+ }
+ }
+
+ /*
+ * Generic function to get the statistics form an OF switch
+ */
+ @SuppressWarnings("unchecked")
+ private List<OFStatistics> fetchStatisticsFromSwitch(Long switchId,
+ OFStatisticsType statsType, Object target) {
+ List<OFStatistics> values = null;
+ String type = null;
+ ISwitch sw = controller.getSwitch(switchId);
+
+ if (sw != null) {
+ OFStatisticsRequest req = new OFStatisticsRequest();
+ req.setStatisticType(statsType);
+ int requestLength = req.getLengthU();
+
+ if (statsType == OFStatisticsType.FLOW) {
+ OFMatch match = null;
+ if (target == null) {
+ // All flows request
+ match = new OFMatch();
+ match.setWildcards(0xffffffff);
+ } else if (!(target instanceof OFMatch)) {
+ // Malformed request
+ log.warn("Invalid target type for Flow stats request: {}",
+ target.getClass());
+ return null;
+ } else {
+ // Specific flow request
+ match = (OFMatch) target;
+ }
+ OFFlowStatisticsRequest specificReq = new OFFlowStatisticsRequest();
+ specificReq.setMatch(match);
+ specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
+ specificReq.setTableId((byte) 0xff);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ type = "FLOW";
+ } else if (statsType == OFStatisticsType.VENDOR) {
+ V6StatsRequest specificReq = new V6StatsRequest();
+ specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
+ specificReq.setTableId((byte) 0xff);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ type = "VENDOR";
+ } else if (statsType == OFStatisticsType.AGGREGATE) {
+ OFAggregateStatisticsRequest specificReq = new OFAggregateStatisticsRequest();
+ OFMatch match = new OFMatch();
+ match.setWildcards(0xffffffff);
+ specificReq.setMatch(match);
+ specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
+ specificReq.setTableId((byte) 0xff);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ type = "AGGREGATE";
+ } else if (statsType == OFStatisticsType.PORT) {
+ short targetPort;
+ if (target == null) {
+ // All ports request
+ targetPort = OFPort.OFPP_NONE.getValue();
+ } else if (!(target instanceof Short)) {
+ // Malformed request
+ log.warn("Invalid target type for Port stats request: {}",
+ target.getClass());
+ return null;
+ } else {
+ // Specific port request
+ targetPort = (Short) target;
+ }
+ OFPortStatisticsRequest specificReq = new OFPortStatisticsRequest();
+ specificReq.setPortNumber(targetPort);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ type = "PORT";
+ } else if (statsType == OFStatisticsType.QUEUE) {
+ OFQueueStatisticsRequest specificReq = new OFQueueStatisticsRequest();
+ specificReq.setPortNumber(OFPort.OFPP_ALL.getValue());
+ specificReq.setQueueId(0xffffffff);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ type = "QUEUE";
+ } else if (statsType == OFStatisticsType.DESC) {
+ type = "DESC";
+ } else if (statsType == OFStatisticsType.TABLE) {
+ if(target != null){
+ if (!(target instanceof Byte)) {
+ // Malformed request
+ log.warn("Invalid table id for table stats request: {}",
+ target.getClass());
+ return null;
+ }
+ byte targetTable = (Byte) target;
+ OFTableStatistics specificReq = new OFTableStatistics();
+ specificReq.setTableId(targetTable);
+ req.setStatistics(Collections
+ .singletonList((OFStatistics) specificReq));
+ requestLength += specificReq.getLength();
+ }
+ type = "TABLE";
+ }
+ req.setLengthU(requestLength);
+ Object result = sw.getStatistics(req);
+
+ if (result == null) {
+ log.warn("Request Timed Out for ({}) from switch {}", type,
+ HexString.toHexString(switchId));
+ } else if (result instanceof OFError) {
+ log.warn("Switch {} failed to handle ({}) stats request: {}",
+ new Object[] { HexString.toHexString(switchId), type,
+ Utils.getOFErrorString((OFError) result) });
+ if (this.switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) {
+ log.warn(
+ "Switching back to regular Flow stats requests for switch {}",
+ HexString.toHexString(switchId));
+ this.switchSupportsVendorExtStats.put(switchId,
+ Boolean.FALSE);
+ }
+ } else {
+ values = (List<OFStatistics>) result;
+ }
+ }
+ return values;
+ }
+
+ @Override
+ public List<OFStatistics> getOFFlowStatistics(Long switchId) {
+ List<OFStatistics> list = flowStatistics.get(switchId);
+
+ /*
+ * Check on emptiness as interference between add and get is still
+ * possible on the inner list (the concurrentMap entry's value)
+ */
+ return (list == null || list.isEmpty()) ? this.dummyList
+ : (list.get(0) instanceof OFVendorStatistics) ? this
+ .v6StatsListToOFStatsList(list) : list;
+ }
+
+ @Override
+ public List<OFStatistics> getOFFlowStatistics(Long switchId, OFMatch ofMatch, short priority) {
+ List<OFStatistics> statsList = flowStatistics.get(switchId);
+
+ /*
+ * Check on emptiness as interference between add and get is still
+ * possible on the inner list (the concurrentMap entry's value)
+ */
+ if (statsList == null || statsList.isEmpty()) {
+ return this.dummyList;
+ }
+
+ if (statsList.get(0) instanceof OFVendorStatistics) {
+ /*
+ * Caller could provide regular OF match when we instead pull the
+ * vendor statistics from this node Caller is not supposed to know
+ * whether this switch supports vendor extensions statistics
+ * requests
+ */
+ V6Match targetMatch = (ofMatch instanceof V6Match) ? (V6Match) ofMatch
+ : new V6Match(ofMatch);
+
+ List<OFStatistics> targetList = v6StatsListToOFStatsList(statsList);
+ for (OFStatistics stats : targetList) {
+ V6StatsReply v6Stats = (V6StatsReply) stats;
+ V6Match v6Match = v6Stats.getMatch();
+ if (v6Stats.getPriority() == priority && v6Match.equals(targetMatch)) {
+ List<OFStatistics> list = new ArrayList<OFStatistics>();
+ list.add(stats);
+ return list;
+ }
+ }
+ } else {
+ for (OFStatistics stats : statsList) {
+ OFFlowStatisticsReply flowStats = (OFFlowStatisticsReply) stats;
+ if (flowStats.getPriority() == priority && flowStats.getMatch().equals(ofMatch)) {
+ List<OFStatistics> list = new ArrayList<OFStatistics>();
+ list.add(stats);
+ return list;
+ }
+ }
+ }
+ return this.dummyList;
+ }
+
+ /*
+ * Converts the v6 vendor statistics to the OFStatistics
+ */
+ private List<OFStatistics> v6StatsListToOFStatsList(
+ List<OFStatistics> statistics) {
+ List<OFStatistics> v6statistics = new ArrayList<OFStatistics>();
+ if (statistics != null && !statistics.isEmpty()) {
+ for (OFStatistics stats : statistics) {
+ if (stats instanceof OFVendorStatistics) {
+ List<OFStatistics> r = getV6ReplyStatistics((OFVendorStatistics) stats);
+ if (r != null) {
+ v6statistics.addAll(r);
+ }
+ }
+ }
+ }
+ return v6statistics;
+ }
+
+ private static List<OFStatistics> getV6ReplyStatistics(
+ OFVendorStatistics stat) {
+ int length = stat.getLength();
+ List<OFStatistics> results = new ArrayList<OFStatistics>();
+ if (length < 12)
+ return null; // Nicira Hdr is 12 bytes. We need atleast that much
+ ByteBuffer data = ByteBuffer.allocate(length);
+ stat.writeTo(data);
+ data.rewind();
+ if (log.isTraceEnabled()) {
+ log.trace("getV6ReplyStatistics: Buffer BYTES ARE {}",
+ HexString.toHexString(data.array()));
+ }
+
+ int vendor = data.getInt(); // first 4 bytes is vendor id.
+ if (vendor != V6StatsRequest.NICIRA_VENDOR_ID) {
+ log.warn("Unexpected vendor id: 0x{}", Integer.toHexString(vendor));
+ return null;
+ } else {
+ // go ahead by 8 bytes which is 8 bytes of 0
+ data.getLong(); // should be all 0's
+ length -= 12; // 4 bytes Nicira Hdr + 8 bytes from above line have
+ // been consumed
+ }
+
+ V6StatsReply v6statsreply;
+ int min_len;
+ while (length > 0) {
+ v6statsreply = new V6StatsReply();
+ min_len = v6statsreply.getLength();
+ if (length < v6statsreply.getLength())
+ break;
+ v6statsreply.setActionFactory(stat.getActionFactory());
+ v6statsreply.readFrom(data);
+ if (v6statsreply.getLength() < min_len)
+ break;
+ v6statsreply.setVendorId(vendor);
+ log.trace("V6StatsReply: {}", v6statsreply);
+ length -= v6statsreply.getLength();
+ results.add(v6statsreply);
+ }
+ return results;
+ }
+
+ @Override
+ public List<OFStatistics> queryStatistics(Long switchId,
+ OFStatisticsType statType, Object target) {
+ /*
+ * Caller does not know and it is not supposed to know whether this
+ * switch supports vendor extension. We adjust the target for him
+ */
+ if (statType == OFStatisticsType.FLOW) {
+ if (switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) {
+ statType = OFStatisticsType.VENDOR;
+ }
+ }
+
+ List<OFStatistics> list = this.fetchStatisticsFromSwitch(switchId, statType,
+ target);
+
+ return (list == null) ? null :
+ (statType == OFStatisticsType.VENDOR) ? v6StatsListToOFStatsList(list) : list;
+ }
+
+ @Override
+ public List<OFStatistics> getOFDescStatistics(Long switchId) {
+ if (!descStatistics.containsKey(switchId))
+ return this.dummyList;
+
+ return descStatistics.get(switchId);
+ }
+
+ @Override
+ public List<OFStatistics> getOFPortStatistics(Long switchId) {
+ if (!portStatistics.containsKey(switchId)) {
+ return this.dummyList;
+ }
+
+ return portStatistics.get(switchId);
+ }
+
+ @Override
+ public List<OFStatistics> getOFPortStatistics(Long switchId, short portId) {
+ if (!portStatistics.containsKey(switchId)) {
+ return this.dummyList;
+ }
+ List<OFStatistics> list = new ArrayList<OFStatistics>(1);
+ for (OFStatistics stats : portStatistics.get(switchId)) {
+ if (((OFPortStatisticsReply) stats).getPortNumber() == portId) {
+ list.add(stats);
+ break;
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public List<OFStatistics> getOFTableStatistics(Long switchId) {
+ if (!tableStatistics.containsKey(switchId)) {
+ return this.dummyList;
+ }
+
+ return tableStatistics.get(switchId);
+ }
+
+ @Override
+ public List<OFStatistics> getOFTableStatistics(Long switchId, Byte tableId) {
+ if (!tableStatistics.containsKey(switchId)) {
+ return this.dummyList;
+ }
+
+ List<OFStatistics> list = new ArrayList<OFStatistics>(1);
+ for (OFStatistics stats : tableStatistics.get(switchId)) {
+ if (((OFTableStatistics) stats).getTableId() == tableId) {
+ list.add(stats);
+ break;
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public int getFlowsNumber(long switchId) {
+ return this.flowStatistics.get(switchId).size();
+ }
+
+ /*
+ * InventoryShim replay for us all the switch addition which happened before
+ * we were brought up
+ */
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ Long switchId = (Long) node.getID();
+ switch (type) {
+ case ADDED:
+ addStatisticsTicks(switchId);
+ break;
+ case REMOVED:
+ clearFlowStatsAndTicks(switchId);
+ default:
+ }
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props) {
+ // No action
+ }
+
+ /**
+ * Update the cached port rates for this switch with the latest retrieved
+ * port transmit byte count
+ *
+ * @param switchId
+ */
+ private synchronized void updatePortsTxRate(long switchId) {
+ List<OFStatistics> newPortStatistics = this.portStatistics.get(switchId);
+ if (newPortStatistics == null) {
+ return;
+ }
+ Map<Short, TxRates> rates = this.txRates.get(switchId);
+ if (rates == null) {
+ // First time rates for this switch are added
+ rates = new HashMap<Short, TxRates>();
+ txRates.put(switchId, rates);
+ }
+ for (OFStatistics stats : newPortStatistics) {
+ OFPortStatisticsReply newPortStat = (OFPortStatisticsReply) stats;
+ short port = newPortStat.getPortNumber();
+ TxRates portRatesHolder = rates.get(port);
+ if (portRatesHolder == null) {
+ // First time rates for this port are added
+ portRatesHolder = new TxRates();
+ rates.put(port, portRatesHolder);
+ }
+ // Get and store the number of transmitted bytes for this port
+ // And handle the case where agent does not support the counter
+ long transmitBytes = newPortStat.getTransmitBytes();
+ long value = (transmitBytes < 0) ? 0 : transmitBytes;
+ portRatesHolder.update(value);
+ }
+ }
+
+ @Override
+ public synchronized long getTransmitRate(Long switchId, Short port) {
+ long average = 0;
+ if (switchId == null || port == null) {
+ return average;
+ }
+ Map<Short, TxRates> perSwitch = txRates.get(switchId);
+ if (perSwitch == null) {
+ return average;
+ }
+ TxRates portRates = perSwitch.get(port);
+ if (portRates == null) {
+ return average;
+ }
+ return portRates.getAverageTxRate();
+ }
+
+ /*
+ * Manual switch name configuration code
+ */
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---OF Statistics Manager utilities---\n");
+ help.append("\t ofdumpstatsmgr - "
+ + "Print Internal Stats Mgr db\n");
+ help.append("\t ofstatsmgrintervals <fP> <pP> <dP> <tP> (all in seconds) - "
+ + "Set/Show flow/port/dedscription stats poll intervals\n");
+ return help.toString();
+ }
+
+ private boolean isValidSwitchId(String switchId) {
+ String regexDatapathID = "^([0-9a-fA-F]{1,2}[:-]){7}[0-9a-fA-F]{1,2}$";
+ String regexDatapathIDLong = "^[0-9a-fA-F]{1,16}$";
+
+ return (switchId != null && (switchId.matches(regexDatapathID) || switchId
+ .matches(regexDatapathIDLong)));
+ }
+
+ public long getSwitchIDLong(String switchId) {
+ int radix = 16;
+ String switchString = "0";
+
+ if (isValidSwitchId(switchId)) {
+ if (switchId.contains(":")) {
+ // Handle the 00:00:AA:BB:CC:DD:EE:FF notation
+ switchString = switchId.replace(":", "");
+ } else if (switchId.contains("-")) {
+ // Handle the 00-00-AA-BB-CC-DD-EE-FF notation
+ switchString = switchId.replace("-", "");
+ } else {
+ // Handle the 0123456789ABCDEF notation
+ switchString = switchId;
+ }
+ }
+ return Long.parseLong(switchString, radix);
+ }
+
+ /*
+ * Internal information dump code
+ */
+ private String prettyPrintSwitchMap(ConcurrentMap<Long, StatisticsTicks> map) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("{");
+ for (Entry<Long, StatisticsTicks> entry : map.entrySet()) {
+ buffer.append(HexString.toHexString(entry.getKey()) + "="
+ + entry.getValue().toString() + " ");
+ }
+ buffer.append("}");
+ return buffer.toString();
+ }
+
+ public void _ofdumpstatsmgr(CommandInterpreter ci) {
+ ci.println("Global Counter: " + counter);
+ ci.println("Timer Ticks: " + prettyPrintSwitchMap(statisticsTimerTicks));
+ ci.println("PendingStatsQueue: " + pendingStatsRequests);
+ ci.println("PendingStatsQueue size: " + pendingStatsRequests.size());
+ ci.println("Stats Collector alive: " + statisticsCollector.isAlive());
+ ci.println("Stats Collector State: "
+ + statisticsCollector.getState().toString());
+ ci.println("StatsTimer: " + statisticsTimer.toString());
+ ci.println("Flow Stats Period: " + statisticsTickNumber + " s");
+ ci.println("Desc Stats Period: " + descriptionTickNumber + " s");
+ ci.println("Port Stats Period: " + portTickNumber + " s");
+ ci.println("Table Stats Period: " + tableTickNumber + " s");
+ }
+
+ public void _resetSwitchCapability(CommandInterpreter ci) {
+ String sidString = ci.nextArgument();
+ Long sid = null;
+ if (sidString == null) {
+ ci.println("Insert the switch id (numeric value)");
+ return;
+ }
+ try {
+ sid = Long.valueOf(sidString);
+ this.switchSupportsVendorExtStats.put(sid, Boolean.TRUE);
+ ci.println("Vendor capability for switch " + sid + " set to "
+ + this.switchSupportsVendorExtStats.get(sid));
+ } catch (NumberFormatException e) {
+ ci.println("Invalid switch id. Has to be numeric.");
+ }
+
+ }
+
+ public void _ofbw(CommandInterpreter ci) {
+ String sidString = ci.nextArgument();
+ Long sid = null;
+ if (sidString == null) {
+ ci.println("Insert the switch id (numeric value)");
+ return;
+ }
+ try {
+ sid = Long.valueOf(sidString);
+ } catch (NumberFormatException e) {
+ ci.println("Invalid switch id. Has to be numeric.");
+ }
+ if (sid != null) {
+ Map<Short, TxRates> thisSwitchRates = txRates.get(sid);
+ ci.println("Bandwidth utilization (" + factoredSamples
+ * portTickNumber + " sec average) for switch "
+ + HexEncode.longToHexString(sid) + ":");
+ if (thisSwitchRates == null) {
+ ci.println("Not available");
+ } else {
+ for (Entry<Short, TxRates> entry : thisSwitchRates.entrySet()) {
+ ci.println("Port: " + entry.getKey() + ": "
+ + entry.getValue().getAverageTxRate() + " bps");
+ }
+ }
+ }
+ }
+
+ public void _txratewindow(CommandInterpreter ci) {
+ String averageWindow = ci.nextArgument();
+ short seconds = 0;
+ if (averageWindow == null) {
+ ci.println("Insert the length in seconds of the median "
+ + "window for tx rate");
+ ci.println("Current: " + factoredSamples * portTickNumber + " secs");
+ return;
+ }
+ try {
+ seconds = Short.valueOf(averageWindow);
+ } catch (NumberFormatException e) {
+ ci.println("Invalid period.");
+ }
+ OFStatisticsManager.factoredSamples = (short) (seconds / portTickNumber);
+ ci.println("New: " + factoredSamples * portTickNumber + " secs");
+ }
+
+ public void _ofstatsmgrintervals(CommandInterpreter ci) {
+ String flowStatsInterv = ci.nextArgument();
+ String portStatsInterv = ci.nextArgument();
+ String descStatsInterv = ci.nextArgument();
+ String tableStatsInterv = ci.nextArgument();
+
+ if (flowStatsInterv == null || portStatsInterv == null
+ || descStatsInterv == null) {
+ ci.println("Usage: ofstatsmgrintervals <fP> <pP> <dP> <tP> (all in seconds)");
+ ci.println("Current Values: fP=" + statisticsTickNumber + "sec pP="
+ + portTickNumber + "sec dP=" + descriptionTickNumber + "sec tP=" + tableTickNumber + " sec");
+ return;
+ }
+ Short fP, pP, dP, tP;
+ try {
+ fP = Short.parseShort(flowStatsInterv);
+ pP = Short.parseShort(portStatsInterv);
+ dP = Short.parseShort(descStatsInterv);
+ tP = Short.parseShort(tableStatsInterv);
+ } catch (Exception e) {
+ ci.println("Invalid format values: " + e.getMessage());
+ return;
+ }
+
+ if (pP <= 1 || fP <= 1 || dP <= 1 || tP <= 1) {
+ ci.println("Invalid values. fP, pP, dP, tP have to be greater than 1.");
+ return;
+ }
+
+ statisticsTickNumber = fP;
+ portTickNumber = pP;
+ descriptionTickNumber = dP;
+ tableTickNumber = tP;
+
+ ci.println("New Values: fP=" + statisticsTickNumber + "s pP="
+ + portTickNumber + "s dP=" + descriptionTickNumber + "s tP="
+ + tableTickNumber + "s");
+ }
+
+ /**
+ * This method retrieves user configurations from config.ini and updates
+ * statisticsTickNumber/portTickNumber/descriptionTickNumber accordingly.
+ */
+ private void configStatsPollIntervals() {
+ String fsStr = System.getProperty("of.flowStatsPollInterval");
+ String psStr = System.getProperty("of.portStatsPollInterval");
+ String dsStr = System.getProperty("of.descStatsPollInterval");
+ String tsStr = System.getProperty("of.tableStatsPollInterval");
+ Short fs, ps, ds, ts;
+
+ if (fsStr != null) {
+ try {
+ fs = Short.parseShort(fsStr);
+ if (fs > 0) {
+ statisticsTickNumber = fs;
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ if (psStr != null) {
+ try {
+ ps = Short.parseShort(psStr);
+ if (ps > 0) {
+ portTickNumber = ps;
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ if (dsStr != null) {
+ try {
+ ds = Short.parseShort(dsStr);
+ if (ds > 0) {
+ descriptionTickNumber = ds;
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ if (tsStr != null) {
+ try{
+ ts = Short.parseShort(tsStr);
+ if (ts > 0) {
+ tableTickNumber = ts;
+ }
+ } catch (Exception e) {
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.openflow.protocol.OFPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class which provides the utilities for converting the Openflow port
+ * number to the equivalent NodeConnector and vice versa
+ *
+ *
+ *
+ */
+public abstract class PortConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(PortConverter.class);
+ private static final int maxOFPhysicalPort = NetUtils
+ .getUnsignedShort(OFPort.OFPP_MAX.getValue());
+
+ /**
+ * Converts the Openflow port number to the equivalent NodeConnector.
+ */
+ public static NodeConnector toNodeConnector(short ofPort, Node node) {
+ // Restore original OF unsigned 16 bits value for the comparison
+ int unsignedOFPort = NetUtils.getUnsignedShort(ofPort);
+ log.trace("Openflow port number signed: {} unsigned: {}", ofPort,
+ unsignedOFPort);
+ if (unsignedOFPort > maxOFPhysicalPort) {
+ if (ofPort == OFPort.OFPP_LOCAL.getValue()) {
+ return NodeConnectorCreator.createNodeConnector(
+ NodeConnectorIDType.SWSTACK,
+ NodeConnector.SPECIALNODECONNECTORID, node);
+ } else if (ofPort == OFPort.OFPP_NORMAL.getValue()) {
+ return NodeConnectorCreator.createNodeConnector(
+ NodeConnectorIDType.HWPATH,
+ NodeConnector.SPECIALNODECONNECTORID, node);
+ } else if (ofPort == OFPort.OFPP_CONTROLLER.getValue()) {
+ return NodeConnectorCreator.createNodeConnector(
+ NodeConnectorIDType.CONTROLLER,
+ NodeConnector.SPECIALNODECONNECTORID, node);
+ }
+ }
+ return NodeConnectorCreator.createNodeConnector(ofPort, node);
+ }
+
+ /**
+ * Converts the NodeConnector to the equivalent Openflow port number
+ */
+ public static short toOFPort(NodeConnector salPort) {
+ log.trace("SAL Port", salPort);
+ if (salPort.getType().equals(NodeConnectorIDType.SWSTACK)) {
+ return OFPort.OFPP_LOCAL.getValue();
+ } else if (salPort.getType().equals(NodeConnectorIDType.HWPATH)) {
+ return OFPort.OFPP_NORMAL.getValue();
+ } else if (salPort.getType().equals(NodeConnectorIDType.CONTROLLER)) {
+ return OFPort.OFPP_CONTROLLER.getValue();
+ }
+ return (Short) salPort.getID();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.openflow.protocol.statistics.OFPortStatisticsReply;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts an openflow list of port statistics in a SAL list of
+ * NodeConnectorStatistics objects
+ *
+ *
+ *
+ */
+public class PortStatisticsConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(PortStatisticsConverter.class);
+ private long switchId;
+ private List<OFStatistics> ofStatsList;
+ private List<NodeConnectorStatistics> ncStatsList;
+
+ public PortStatisticsConverter(long switchId, List<OFStatistics> statsList) {
+ this.switchId = switchId;
+ if (statsList == null || statsList.isEmpty()) {
+ this.ofStatsList = new ArrayList<OFStatistics>(1); // dummy list
+ } else {
+ this.ofStatsList = new ArrayList<OFStatistics>(statsList);
+ }
+ this.ncStatsList = null;
+ }
+
+ public List<NodeConnectorStatistics> getNodeConnectorStatsList() {
+ if (this.ofStatsList != null && this.ncStatsList == null) {
+ this.ncStatsList = new ArrayList<NodeConnectorStatistics>();
+ OFPortStatisticsReply ofPortStat;
+ Node node = NodeCreator.createOFNode(switchId);
+ for (OFStatistics ofStat : this.ofStatsList) {
+ ofPortStat = (OFPortStatisticsReply) ofStat;
+ NodeConnectorStatistics NCStat = new NodeConnectorStatistics();
+ NCStat.setNodeConnector(PortConverter.toNodeConnector(
+ ofPortStat.getPortNumber(), node));
+ NCStat.setReceivePacketCount(ofPortStat.getreceivePackets());
+ NCStat.setTransmitPacketCount(ofPortStat.getTransmitPackets());
+ NCStat.setReceiveByteCount(ofPortStat.getReceiveBytes());
+ NCStat.setTransmitByteCount(ofPortStat.getTransmitBytes());
+ NCStat.setReceiveDropCount(ofPortStat.getReceiveDropped());
+ NCStat.setTransmitDropCount(ofPortStat.getTransmitDropped());
+ NCStat.setReceiveErrorCount(ofPortStat.getreceiveErrors());
+ NCStat.setTransmitErrorCount(ofPortStat.getTransmitErrors());
+ NCStat.setReceiveFrameErrorCount(ofPortStat
+ .getReceiveFrameErrors());
+ NCStat.setReceiveOverRunErrorCount(ofPortStat
+ .getReceiveOverrunErrors());
+ NCStat.setReceiveCRCErrorCount(ofPortStat.getReceiveCRCErrors());
+ NCStat.setCollisionCount(ofPortStat.getCollisions());
+ this.ncStatsList.add(NCStat);
+ }
+ }
+ log.trace("OFStatistics: {} NodeConnectorStatistics: {}", ofStatsList,
+ ncStatsList);
+ return this.ncStatsList;
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeTable;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.controller.sal.reader.IPluginInReadService;
+import org.opendaylight.controller.sal.reader.IPluginOutReadService;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+import org.opendaylight.openflowplugin.openflow.IReadFilterInternalListener;
+import org.opendaylight.openflowplugin.openflow.IReadServiceFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Container Instance of IPluginInReadService implementation class
+ */
+public class ReadService implements IPluginInReadService, IReadFilterInternalListener {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ReadService.class);
+ private IReadServiceFilter filter;
+ private Set<IPluginOutReadService> pluginOutReadServices;
+ private String containerName;
+ private IPluginOutConnectionService connectionOutService;
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ @SuppressWarnings("unchecked")
+ void init(Component c) {
+ Dictionary<Object, Object> props = c.getServiceProperties();
+ containerName = (props != null) ? (String) props.get("containerName")
+ : null;
+ pluginOutReadServices = new CopyOnWriteArraySet<IPluginOutReadService>();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one
+ * dependency become unsatisfied or when the component is shutting
+ * down because for example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called
+ * and after the services provided by the class are registered in
+ * the service registry
+ *
+ */
+ void start() {
+ }
+
+ /**
+ * Function called by the dependency manager before the services
+ * exported by the component are unregistered, this will be
+ * followed by a "destroy ()" calls
+ *
+ */
+ void stop() {
+ }
+
+ public void setService(IReadServiceFilter filter) {
+ this.filter = filter;
+ }
+
+ public void unsetService(IReadServiceFilter filter) {
+ this.filter = null;
+ }
+
+ public void setPluginOutReadServices(IPluginOutReadService service) {
+ logger.trace("Got a service set request {}", service);
+ if (this.pluginOutReadServices != null) {
+ this.pluginOutReadServices.add(service);
+ }
+ }
+
+ public void unsetPluginOutReadServices(
+ IPluginOutReadService service) {
+ logger.trace("Got a service UNset request");
+ if (this.pluginOutReadServices != null) {
+ this.pluginOutReadServices.remove(service);
+ }
+ }
+ @Override
+ public FlowOnNode readFlow(Node node, Flow flow, boolean cached) {
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for the node : " + node);
+ return null;
+ }
+ return filter.readFlow(containerName, node, flow, cached);
+ }
+
+ @Override
+ public List<FlowOnNode> readAllFlow(Node node, boolean cached) {
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for the node : " + node);
+ return null;
+ }
+
+ return filter.readAllFlow(containerName, node, cached);
+ }
+
+ @Override
+ public NodeDescription readDescription(Node node, boolean cached) {
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for the node : " + node);
+ return null;
+ }
+
+ return filter.readDescription(node, cached);
+ }
+
+ @Override
+ public NodeConnectorStatistics readNodeConnector(NodeConnector connector,
+ boolean cached) {
+ if (!connector.getNode().getType()
+ .equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(connector.getNode())) {
+ logger.debug("This Controller is not the master for connector : "+connector);
+ return null;
+ }
+
+ return filter.readNodeConnector(containerName, connector, cached);
+ }
+
+ @Override
+ public List<NodeConnectorStatistics> readAllNodeConnector(Node node,
+ boolean cached) {
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return null;
+ }
+
+ return filter.readAllNodeConnector(containerName, node, cached);
+ }
+
+ @Override
+ public long getTransmitRate(NodeConnector connector) {
+ if (!connector.getNode().getType()
+ .equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return 0;
+ }
+
+ if (!connectionOutService.isLocal(connector.getNode())) {
+ logger.debug("This Controller is not the master for connector : "+connector);
+ return 0;
+ }
+
+ return filter.getTransmitRate(containerName, connector);
+ }
+
+ @Override
+ public NodeTableStatistics readNodeTable(NodeTable table, boolean cached) {
+ if (!table.getNode().getType()
+ .equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(table.getNode())) {
+ logger.debug("This Controller is not the master for connector : "+table);
+ return null;
+ }
+
+ return filter.readNodeTable(containerName, table, cached);
+ }
+
+ @Override
+ public List<NodeTableStatistics> readAllNodeTable(Node node, boolean cached) {
+ if (!node.getType().equals(NodeIDType.OPENFLOW)) {
+ logger.error("Invalid node type");
+ return null;
+ }
+
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return null;
+ }
+
+ return filter.readAllNodeTable(containerName, node, cached);
+ }
+
+ @Override
+ public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return;
+ }
+ for (IPluginOutReadService service : pluginOutReadServices) {
+ service.nodeFlowStatisticsUpdated(node, flowStatsList);
+ }
+ }
+
+ @Override
+ public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return;
+ }
+ for (IPluginOutReadService service : pluginOutReadServices) {
+ service.nodeConnectorStatisticsUpdated(node, ncStatsList);
+ }
+ }
+
+ @Override
+ public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return;
+ }
+ for (IPluginOutReadService service : pluginOutReadServices) {
+ service.nodeTableStatisticsUpdated(node, tableStatsList);
+ }
+ }
+
+ @Override
+ public void nodeDescriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
+ if (!connectionOutService.isLocal(node)) {
+ logger.debug("This Controller is not the master for node : " + node);
+ return;
+ }
+ for (IPluginOutReadService service : pluginOutReadServices) {
+ service.descriptionStatisticsUpdated(node, nodeDescription);
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeTable;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.reader.FlowOnNode;
+import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
+import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.NodeTableCreator;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsManager;
+import org.opendaylight.openflowplugin.openflow.IReadFilterInternalListener;
+import org.opendaylight.openflowplugin.openflow.IReadServiceFilter;
+import org.opendaylight.openflowplugin.openflow.core.IController;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.statistics.OFFlowStatisticsReply;
+import org.openflow.protocol.statistics.OFPortStatisticsReply;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+import org.openflow.protocol.statistics.OFTableStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * Read Service shim layer which is in charge of filtering the flow statistics
+ * based on container. It is a Global instance.
+ */
+public class ReadServiceFilter implements IReadServiceFilter, IContainerListener, IOFStatisticsListener {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ReadServiceFilter.class);
+ private IController controller = null;
+ private IOFStatisticsManager statsMgr = null;
+ private ConcurrentMap<String, Set<NodeConnector>> containerToNc;
+ private ConcurrentMap<String, Set<Node>> containerToNode;
+ private ConcurrentMap<String, Set<NodeTable>> containerToNt;
+ private ConcurrentMap<String, Set<ContainerFlow>> containerFlows;
+ private ConcurrentMap<String, IReadFilterInternalListener> readFilterInternalListeners;
+
+ public void setController(IController core) {
+ this.controller = core;
+ }
+
+ public void unsetController(IController core) {
+ if (this.controller == core) {
+ this.controller = null;
+ }
+ }
+
+ public void setReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
+ if (props == null) {
+ logger.error("Failed setting Read Filter Listener, property map is null.");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("Failed setting Read Filter Listener, container name not supplied.");
+ return;
+ }
+ if ((this.readFilterInternalListeners != null) && !this.readFilterInternalListeners.containsValue(s)) {
+ this.readFilterInternalListeners.put(containerName, s);
+ logger.trace("Added Read Filter Listener for container {}", containerName);
+ }
+ }
+
+ public void unsetReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
+ if (props == null) {
+ logger.error("Failed unsetting Read Filter Listener, property map is null.");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("Failed unsetting Read Filter Listener, containerName not supplied");
+ return;
+ }
+ if ((this.readFilterInternalListeners != null) && this.readFilterInternalListeners.get(containerName) != null
+ && this.readFilterInternalListeners.get(containerName).equals(s)) {
+ this.readFilterInternalListeners.remove(containerName);
+ logger.trace("Removed Read Filter Listener for container {}", containerName);
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ containerToNc = new ConcurrentHashMap<String, Set<NodeConnector>>();
+ containerToNt = new ConcurrentHashMap<String, Set<NodeTable>>();
+ containerToNode = new ConcurrentHashMap<String, Set<Node>>();
+ containerFlows = new ConcurrentHashMap<String, Set<ContainerFlow>>();
+ readFilterInternalListeners = new ConcurrentHashMap<String, IReadFilterInternalListener>();
+ }
+
+ /**
+ * Function called by the dependency manager when at least one
+ * dependency become unsatisfied or when the component is shutting
+ * down because for example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called
+ * and after the services provided by the class are registered in
+ * the service registry
+ *
+ */
+ void start() {
+ }
+
+ /**
+ * Function called by the dependency manager before the services
+ * exported by the component are unregistered, this will be
+ * followed by a "destroy ()" calls
+ *
+ */
+ void stop() {
+ }
+
+ public void setService(IOFStatisticsManager service) {
+ this.statsMgr = service;
+ }
+
+ public void unsetService(IOFStatisticsManager service) {
+ this.statsMgr = null;
+ }
+
+ IPluginOutConnectionService connectionPluginOutService;
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionPluginOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionPluginOutService == s) {
+ connectionPluginOutService = null;
+ }
+ }
+
+ @Override
+ public FlowOnNode readFlow(String container, Node node, Flow flow, boolean cached) {
+
+ if (controller == null) {
+ // Avoid to provide cached statistics if controller went down.
+ // They are not valid anymore anyway
+ logger.error("Internal plugin error");
+ return null;
+ }
+
+ long sid = (Long) node.getID();
+ OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
+ List<OFStatistics> ofList;
+ if (cached == true){
+ ofList = statsMgr.getOFFlowStatistics(sid, ofMatch, flow.getPriority());
+ } else {
+ ofList = statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, ofMatch);
+ for (OFStatistics ofStat : ofList) {
+ if (((OFFlowStatisticsReply)ofStat).getPriority() == flow.getPriority()){
+ ofList = new ArrayList<OFStatistics>(1);
+ ofList.add(ofStat);
+ break;
+ }
+ }
+ }
+
+ // Convert and filter the statistics per container
+ List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
+ List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+ return (filteredList == null || filteredList.isEmpty()) ? null : filteredList.get(0);
+ }
+
+ @Override
+ public List<FlowOnNode> readAllFlow(String container, Node node,
+ boolean cached) {
+
+ long sid = (Long) node.getID();
+ List<OFStatistics> ofList = (cached == true) ? statsMgr
+ .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
+ OFStatisticsType.FLOW, null);
+
+ // Convert and filter the statistics per container
+ List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
+ List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+ return (filteredList == null) ? null : filteredList;
+
+ }
+
+ @Override
+ public NodeDescription readDescription(Node node, boolean cached) {
+
+ if (controller == null) {
+ logger.error("Internal plugin error");
+ return null;
+ }
+
+ long sid = (Long) node.getID();
+ List<OFStatistics> ofList = (cached == true) ? statsMgr
+ .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
+ OFStatisticsType.DESC, null);
+
+ return new DescStatisticsConverter(ofList).getHwDescription();
+ }
+
+ /**
+ * Filters a list of FlowOnNode elements based on the container
+ *
+ * @param container
+ * @param nodeId
+ * @param list
+ * @return
+ */
+ public List<FlowOnNode> filterFlowListPerContainer(String container,
+ Node nodeId, List<FlowOnNode> list) {
+ if (list == null) {
+ return null;
+ }
+
+ // Create new filtered list of flows
+ List<FlowOnNode> newList = new ArrayList<FlowOnNode>();
+
+ for (FlowOnNode target : list) {
+ // Check whether the described flow (match + actions) belongs to this container
+ if (flowBelongToContainer(container, nodeId, target.getFlow())) {
+ newList.add(target);
+ }
+ }
+
+ return newList;
+ }
+
+ /**
+ * Filters a list of OFStatistics elements based on the container
+ *
+ * @param container
+ * @param nodeId
+ * @param list
+ * @return
+ */
+ public List<OFStatistics> filterPortListPerContainer(String container, long switchId, List<OFStatistics> list) {
+ if (list == null) {
+ return null;
+ }
+
+ // Create new filtered list of flows
+ List<OFStatistics> newList = new ArrayList<OFStatistics>();
+
+ for (OFStatistics stat : list) {
+ OFPortStatisticsReply target = (OFPortStatisticsReply) stat;
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(
+ target.getPortNumber(), NodeCreator.createOFNode(switchId));
+ if (containerOwnsNodeConnector(container, nc)) {
+ newList.add(target);
+ }
+ }
+
+ return newList;
+ }
+
+
+ public List<OFStatistics> filterTableListPerContainer(
+ String container, long switchId, List<OFStatistics> list) {
+ if (list == null) {
+ return null;
+ }
+
+ // Create new filtered list of node tables
+ List<OFStatistics> newList = new ArrayList<OFStatistics>();
+
+ for (OFStatistics stat : list) {
+ OFTableStatistics target = (OFTableStatistics) stat;
+ NodeTable nt = NodeTableCreator.createOFNodeTable(target.getTableId(), NodeCreator.createOFNode(switchId));
+ if (containerOwnsNodeTable(container, nt)) {
+ newList.add(target);
+ }
+ }
+
+ return newList;
+ }
+
+ /**
+ * Returns whether the specified flow (flow match + actions)
+ * belongs to the container
+ *
+ * @param container
+ * @param node
+ * @param flow
+ * @return true if it belongs
+ */
+ public boolean flowBelongToContainer(String container, Node node, Flow flow) {
+ // All flows belong to the default container
+ if (container.equals(GlobalConstants.DEFAULT.toString())) {
+ return true;
+ }
+ return (flowPortsBelongToContainer(container, node, flow) &&
+ flowVlanBelongsToContainer(container, node, flow) &&
+ isFlowAllowedByContainer(container, flow));
+ }
+
+ /**
+ * Returns whether the passed NodeConnector belongs to the container
+ *
+ * @param container container name
+ * @param p node connector to test
+ * @return true if belongs false otherwise
+ */
+ public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
+ // All node connectors belong to the default container
+ if (container.equals(GlobalConstants.DEFAULT.toString())) {
+ return true;
+ }
+ Set<NodeConnector> portSet = containerToNc.get(container);
+ return (portSet == null) ? false : portSet.contains(p);
+ }
+
+ /**
+ * Returns whether the passed NodeConnector belongs to the container
+ *
+ * @param container container name
+ * @param table node table to test
+ * @return true if belongs false otherwise
+ */
+ public boolean containerOwnsNodeTable(String container, NodeTable table) {
+ // All node table belong to the default container
+ if (container.equals(GlobalConstants.DEFAULT.toString())) {
+ return true;
+ }
+ Set<NodeTable> tableSet = containerToNt.get(container);
+ return (tableSet == null) ? false : tableSet.contains(table);
+ }
+
+ /**
+ * Returns whether the container flows allow the passed flow
+ *
+ * @param container
+ * @param match
+ * @return
+ */
+ private boolean isFlowAllowedByContainer(String container, Flow flow) {
+ Set<ContainerFlow> cFlowSet = this.containerFlows.get(container);
+ if (cFlowSet == null || cFlowSet.isEmpty()) {
+ return true;
+ }
+ for (ContainerFlow cFlow : cFlowSet) {
+ if (cFlow.allowsFlow(flow)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the vlan field in the flow match is the same
+ * of the static vlan configured for the container
+ *
+ * @param container
+ * @param node
+ * @param flow
+ * @return
+ */
+ private boolean flowVlanBelongsToContainer(String container, Node node, Flow flow) {
+ return true; // Always true for now
+ }
+
+ /**
+ * Check whether the ports in the flow match and flow actions for
+ * the specified node belong to the container
+ *
+ * @param container
+ * @param node
+ * @param flow
+ * @return
+ */
+ private boolean flowPortsBelongToContainer(String container, Node node,
+ Flow flow) {
+ Match m = flow.getMatch();
+ if (m.isPresent(MatchType.IN_PORT)) {
+ NodeConnector inPort = (NodeConnector) m.getField(MatchType.IN_PORT).getValue();
+ // If the incoming port is specified, check if it belongs to
+ if (!containerOwnsNodeConnector(container, inPort)) {
+ return false;
+ }
+ }
+
+ // If an outgoing port is specified, it must belong to this container
+ for (Action action : flow.getActions()) {
+ if (action.getType() == ActionType.OUTPUT) {
+ NodeConnector outPort = ((Output) action).getPort();
+ if (!containerOwnsNodeConnector(container, outPort)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName, ContainerFlow previousFlow,
+ ContainerFlow currentFlow, UpdateType t) {
+ Set<ContainerFlow> cFlowSet = containerFlows.get(containerName);
+ switch (t) {
+ case ADDED:
+ if (cFlowSet == null) {
+ cFlowSet = new HashSet<ContainerFlow>();
+ containerFlows.put(containerName, cFlowSet);
+ }
+ cFlowSet.add(currentFlow);
+ case CHANGED:
+ break;
+ case REMOVED:
+ if (cFlowSet != null) {
+ cFlowSet.remove(currentFlow);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType type) {
+
+ switch (type) {
+ case ADDED:
+ if (!containerToNc.containsKey(containerName)) {
+ containerToNc.put(containerName,
+ Collections.newSetFromMap(new ConcurrentHashMap<NodeConnector,Boolean>()));
+ }
+ containerToNc.get(containerName).add(p);
+ if (!containerToNode.containsKey(containerName)) {
+ containerToNode.put(containerName, new HashSet<Node>());
+ }
+ containerToNode.get(containerName).add(p.getNode());
+ break;
+ case REMOVED:
+ Set<NodeConnector> ncSet = containerToNc.get(containerName);
+ if (ncSet != null) {
+ //remove this nc from container map
+ ncSet.remove(p);
+
+ //check if there are still ports of this node in this container
+ //and if not, remove its mapping
+ boolean nodeInContainer = false;
+ Node node = p.getNode();
+ for (NodeConnector nodeConnector : ncSet) {
+ if (nodeConnector.getNode().equals(node)){
+ nodeInContainer = true;
+ break;
+ }
+ }
+ if (! nodeInContainer) {
+ Set<Node> nodeSet = containerToNode.get(containerName);
+ if (nodeSet != null) {
+ nodeSet.remove(node);
+ }
+ }
+ }
+ break;
+ case CHANGED:
+ default:
+ }
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
+ // Not interested in this event
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+ // Not interested in this event
+ }
+
+ @Override
+ public NodeConnectorStatistics readNodeConnector(String containerName, NodeConnector connector, boolean cached) {
+ if (!containerOwnsNodeConnector(containerName, connector)) {
+ return null;
+ }
+ Node node = connector.getNode();
+ long sid = (Long) node.getID();
+ short portId = (Short) connector.getID();
+ List<OFStatistics> ofList = (cached == true) ? statsMgr
+ .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
+ sid, OFStatisticsType.PORT, portId);
+
+ List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(sid, ofList)
+ .getNodeConnectorStatsList();
+ return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics() : ncStatistics.get(0);
+ }
+
+ @Override
+ public List<NodeConnectorStatistics> readAllNodeConnector(String containerName, Node node, boolean cached) {
+
+ long sid = (Long) node.getID();
+ List<OFStatistics> ofList = (cached == true) ? statsMgr
+ .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
+ OFStatisticsType.FLOW, null);
+
+ List<OFStatistics> filteredList = filterPortListPerContainer(containerName, sid, ofList);
+
+ return new PortStatisticsConverter(sid, filteredList).getNodeConnectorStatsList();
+ }
+
+ @Override
+ public long getTransmitRate(String containerName, NodeConnector connector) {
+ if (!containerOwnsNodeConnector(containerName, connector)) {
+ return 0;
+ }
+
+ long switchId = (Long) connector.getNode().getID();
+ short port = (Short) connector.getID();
+
+ return statsMgr.getTransmitRate(switchId, port);
+ }
+
+ @Override
+ public NodeTableStatistics readNodeTable(String containerName,
+ NodeTable table, boolean cached) {
+ if (!containerOwnsNodeTable(containerName, table)) {
+ return null;
+ }
+ Node node = table.getNode();
+ long sid = (Long) node.getID();
+ Byte tableId = (Byte) table.getID();
+ List<OFStatistics> ofList = (cached == true) ? statsMgr.getOFTableStatistics(sid, tableId) :
+ statsMgr.queryStatistics(sid, OFStatisticsType.TABLE, tableId);
+
+ List<NodeTableStatistics> ntStatistics = new TableStatisticsConverter(sid, ofList).getNodeTableStatsList();
+
+ return (ntStatistics.isEmpty()) ? new NodeTableStatistics() : ntStatistics.get(0);
+ }
+
+ @Override
+ public List<NodeTableStatistics> readAllNodeTable(String containerName, Node node, boolean cached) {
+ long sid = (Long) node.getID();
+ List<OFStatistics> ofList = (cached == true) ?
+ statsMgr.getOFTableStatistics(sid) : statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, null);
+
+ List<OFStatistics> filteredList = filterTableListPerContainer(containerName, sid, ofList);
+
+ return new TableStatisticsConverter(sid, filteredList).getNodeTableStatsList();
+ }
+
+ @Override
+ public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description) {
+ String container;
+ IReadFilterInternalListener listener;
+ Node node = NodeCreator.createOFNode(switchId);
+ NodeDescription nodeDescription = new DescStatisticsConverter(description).getHwDescription();
+ for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+ container = l.getKey();
+ listener = l.getValue();
+ if (container == GlobalConstants.DEFAULT.toString()
+ || (containerToNode.containsKey(container) && containerToNode.get(container).contains(node))) {
+ listener.nodeDescriptionStatisticsUpdated(node, nodeDescription);
+ }
+ }
+ }
+
+ @Override
+ public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
+ String container;
+ IReadFilterInternalListener listener;
+ Node node = NodeCreator.createOFNode(switchId);
+ for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+ container = l.getKey();
+ listener = l.getValue();
+
+ // Convert and filter the statistics per container
+ List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(flows).getFlowOnNodeList(node);
+ flowOnNodeList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+ // notify listeners
+ listener.nodeFlowStatisticsUpdated(node, flowOnNodeList);
+ }
+ }
+
+ @Override
+ public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
+ String container;
+ IReadFilterInternalListener listener;
+ Node node = NodeCreator.createOFNode(switchId);
+ for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+ container = l.getKey();
+ listener = l.getValue();
+
+ // Convert and filter the statistics per container
+ List<OFStatistics> filteredPorts = filterPortListPerContainer(container, switchId, ports);
+ List<NodeConnectorStatistics> ncStatsList = new PortStatisticsConverter(switchId, filteredPorts)
+ .getNodeConnectorStatsList();
+
+ // notify listeners
+ listener.nodeConnectorStatisticsUpdated(node, ncStatsList);
+ }
+ }
+
+ @Override
+ public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables) {
+ String container;
+ Node node = NodeCreator.createOFNode(switchId);
+ for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+ container = l.getKey();
+
+ // Convert and filter the statistics per container
+ List<OFStatistics> filteredList = filterTableListPerContainer(container, switchId, tables);
+ List<NodeTableStatistics> tableStatsList = new TableStatisticsConverter(switchId, filteredList)
+ .getNodeTableStatsList();
+
+ // notify listeners
+ l.getValue().nodeTableStatisticsUpdated(node, tableStatsList);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeTable;
+import org.opendaylight.controller.sal.utils.NodeTableCreator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TableConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(TableConverter.class);
+
+ public static NodeTable toNodeTable(byte tableId, Node node) {
+ log.trace("Openflow table ID: {}", Byte.toString(tableId));
+ return NodeTableCreator.createNodeTable(tableId, node);
+ }
+
+ public static byte toOFTable(NodeTable salTable) {
+ log.trace("SAL Table: {}", salTable);
+ return (Byte) salTable.getID();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFTableStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts an openflow list of table statistics in a SAL list of
+ * NodeTableStatistics objects
+ */
+public class TableStatisticsConverter {
+ private static final Logger log = LoggerFactory
+ .getLogger(TableStatisticsConverter.class);
+
+ private final long switchId;
+ private List<OFStatistics> ofStatsList;
+ private List<NodeTableStatistics> ntStatsList;
+
+ public TableStatisticsConverter(long switchId, List<OFStatistics> statsList) {
+ this.switchId = switchId;
+ if (statsList == null || statsList.isEmpty()) {
+ this.ofStatsList = new ArrayList<OFStatistics>(1); // dummy list
+ } else {
+ this.ofStatsList = new ArrayList<OFStatistics>(statsList);
+ }
+ this.ntStatsList = null;
+ }
+
+ public List<NodeTableStatistics> getNodeTableStatsList() {
+ if (this.ofStatsList != null && this.ntStatsList == null) {
+ this.ntStatsList = new ArrayList<NodeTableStatistics>();
+ OFTableStatistics ofTableStat;
+ Node node = NodeCreator.createOFNode(switchId);
+ for (OFStatistics ofStat : this.ofStatsList) {
+ ofTableStat = (OFTableStatistics) ofStat;
+ NodeTableStatistics ntStat = new NodeTableStatistics();
+ ntStat.setNodeTable(TableConverter.toNodeTable(
+ ofTableStat.getTableId(), node));
+ ntStat.setActiveCount(ofTableStat.getActiveCount());
+ ntStat.setLookupCount(ofTableStat.getLookupCount());
+ ntStat.setMatchedCount(ofTableStat.getMatchedCount());
+ this.ntStatsList.add(ntStat);
+ }
+ }
+ log.trace("OFStatistics: {} NodeTableStatistics: {}", ofStatsList,
+ ntStatsList);
+ return this.ntStatsList;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.State;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.openflowplugin.openflow.IDiscoveryListener;
+import org.opendaylight.openflowplugin.openflow.IInventoryShimExternalListener;
+import org.opendaylight.openflowplugin.openflow.IOFStatisticsManager;
+import org.opendaylight.openflowplugin.openflow.IRefreshInternalProvider;
+import org.opendaylight.openflowplugin.openflow.ITopologyServiceShimListener;
+
+/**
+ * The class describes a shim layer that relays the topology events from
+ * OpenFlow core to various listeners. The notifications are filtered based on
+ * container configurations.
+ */
+public class TopologyServiceShim implements IDiscoveryListener,
+ IContainerListener, CommandProvider, IRefreshInternalProvider,
+ IInventoryShimExternalListener {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(TopologyServiceShim.class);
+ private ConcurrentMap<String, ITopologyServiceShimListener> topologyServiceShimListeners = new ConcurrentHashMap<String, ITopologyServiceShimListener>();
+ private ConcurrentMap<NodeConnector, List<String>> containerMap = new ConcurrentHashMap<NodeConnector, List<String>>();
+ private ConcurrentMap<String, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>> edgeMap = new ConcurrentHashMap<String, ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>>>();
+
+ private BlockingQueue<NotifyEntry> notifyQ;
+ private Thread notifyThread;
+ private BlockingQueue<String> bulkNotifyQ;
+ private Thread ofPluginTopoBulkUpdate;
+ private volatile Boolean shuttingDown = false;
+ private IOFStatisticsManager statsMgr;
+ private Timer pollTimer;
+ private TimerTask txRatePoller;
+ private Thread bwUtilNotifyThread;
+ private BlockingQueue<UtilizationUpdate> bwUtilNotifyQ;
+ private List<NodeConnector> connectorsOverUtilized;
+ private float bwThresholdFactor = (float) 0.8; // Threshold = 80% of link
+ // bandwidth
+
+ class NotifyEntry {
+ String container;
+ List<TopoEdgeUpdate> teuList;
+
+ public NotifyEntry(String container, TopoEdgeUpdate teu) {
+ this.container = container;
+ this.teuList = new ArrayList<TopoEdgeUpdate>();
+ if (teu != null) {
+ this.teuList.add(teu);
+ }
+ }
+
+ public NotifyEntry(String container, List<TopoEdgeUpdate> teuList) {
+ this.container = container;
+ this.teuList = new ArrayList<TopoEdgeUpdate>();
+ if (teuList != null) {
+ this.teuList.addAll(teuList);
+ }
+ }
+ }
+
+ class TopologyNotify implements Runnable {
+ private final BlockingQueue<NotifyEntry> notifyQ;
+ private NotifyEntry entry;
+ private Map<String, List<TopoEdgeUpdate>> teuMap = new HashMap<String, List<TopoEdgeUpdate>>();
+ private List<TopoEdgeUpdate> teuList;
+ private boolean notifyListeners;
+
+ TopologyNotify(BlockingQueue<NotifyEntry> notifyQ) {
+ this.notifyQ = notifyQ;
+ }
+
+ public void run() {
+ while (true) {
+ try {
+ teuMap.clear();
+ notifyListeners = false;
+ while (!notifyQ.isEmpty()) {
+ entry = notifyQ.take();
+ teuList = teuMap.get(entry.container);
+ if (teuList == null) {
+ teuList = new ArrayList<TopoEdgeUpdate>();
+ }
+ // group all the updates together
+ teuList.addAll(entry.teuList);
+ teuMap.put(entry.container, teuList);
+ notifyListeners = true;
+ }
+
+ if (notifyListeners) {
+ for (String container : teuMap.keySet()) {
+ // notify the listener
+ topologyServiceShimListeners.get(container)
+ .edgeUpdate(teuMap.get(container));
+ }
+ }
+
+ Thread.sleep(100);
+ } catch (InterruptedException e1) {
+ logger.warn("TopologyNotify interrupted {}",
+ e1.getMessage());
+ if (shuttingDown) {
+ return;
+ }
+ } catch (Exception e2) {
+ logger.error("", e2);
+ }
+ }
+ }
+ }
+
+ class UtilizationUpdate {
+ NodeConnector connector;
+ UpdateType type;
+
+ UtilizationUpdate(NodeConnector connector, UpdateType type) {
+ this.connector = connector;
+ this.type = type;
+ }
+ }
+
+ class BwUtilizationNotify implements Runnable {
+ private final BlockingQueue<UtilizationUpdate> notifyQ;
+
+ BwUtilizationNotify(BlockingQueue<UtilizationUpdate> notifyQ) {
+ this.notifyQ = notifyQ;
+ }
+
+ public void run() {
+ while (true) {
+ try {
+ UtilizationUpdate update = notifyQ.take();
+ NodeConnector connector = update.connector;
+ Set<String> containerList = edgeMap.keySet();
+ for (String container : containerList) {
+ Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
+ .get(container);
+ Edge edge = edgePropsMap.get(connector).getLeft();
+ if (edge.getTailNodeConnector().equals(connector)) {
+ ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners
+ .get(container);
+ if (update.type == UpdateType.ADDED) {
+ topologServiceShimListener
+ .edgeOverUtilized(edge);
+ } else {
+ topologServiceShimListener
+ .edgeUtilBackToNormal(edge);
+ }
+ }
+ }
+ } catch (InterruptedException e1) {
+ logger.warn(
+ "Edge Bandwidth Utilization Notify Thread interrupted {}",
+ e1.getMessage());
+ if (shuttingDown) {
+ return;
+ }
+ } catch (Exception e2) {
+ logger.error("", e2);
+ }
+ }
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ logger.trace("Init called");
+ connectorsOverUtilized = new ArrayList<NodeConnector>();
+ notifyQ = new LinkedBlockingQueue<NotifyEntry>();
+ notifyThread = new Thread(new TopologyNotify(notifyQ));
+ bwUtilNotifyQ = new LinkedBlockingQueue<UtilizationUpdate>();
+ bwUtilNotifyThread = new Thread(new BwUtilizationNotify(bwUtilNotifyQ));
+ bulkNotifyQ = new LinkedBlockingQueue<String>();
+ ofPluginTopoBulkUpdate = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ String containerName = bulkNotifyQ.take();
+ logger.debug("Bulk Notify container:{}", containerName);
+ TopologyBulkUpdate(containerName);
+ } catch (InterruptedException e) {
+ logger.warn("Topology Bulk update thread interrupted");
+ if (shuttingDown) {
+ return;
+ }
+ }
+ }
+ }
+ }, "Topology Bulk Update");
+
+ // Initialize node connector tx bit rate poller timer
+ pollTimer = new Timer();
+ txRatePoller = new TimerTask() {
+ @Override
+ public void run() {
+ pollTxBitRates();
+ }
+ };
+
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Continuously polls the transmit bit rate for all the node connectors from
+ * statistics manager and trigger the warning notification upward when the
+ * transmit rate is above a threshold which is a percentage of the edge
+ * bandwidth
+ */
+ protected void pollTxBitRates() {
+ Map<NodeConnector, Pair<Edge, Set<Property>>> globalContainerEdges = edgeMap
+ .get(GlobalConstants.DEFAULT.toString());
+ if (globalContainerEdges == null) {
+ return;
+ }
+
+ for (NodeConnector connector : globalContainerEdges.keySet()) {
+ // Skip if node connector belongs to production switch
+ if (connector.getType().equals(
+ NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ continue;
+ }
+
+ // Get edge for which this node connector is head
+ Pair<Edge, Set<Property>> props = this.edgeMap.get(
+ GlobalConstants.DEFAULT.toString()).get(connector);
+ // On switch mgr restart the props get reset
+ if (props == null) {
+ continue;
+ }
+ Set<Property> propSet = props.getRight();
+ if (propSet == null) {
+ continue;
+ }
+
+ float bw = 0;
+ for (Property prop : propSet) {
+ if (prop instanceof Bandwidth) {
+ bw = ((Bandwidth) prop).getValue();
+ break;
+ }
+ }
+
+ // Skip if agent did not provide a bandwidth info for the edge
+ if (bw == 0) {
+ continue;
+ }
+
+ // Compare bandwidth usage
+ Long switchId = (Long) connector.getNode().getID();
+ Short port = (Short) connector.getID();
+ float rate = statsMgr.getTransmitRate(switchId, port);
+ if (rate > bwThresholdFactor * bw) {
+ if (!connectorsOverUtilized.contains(connector)) {
+ connectorsOverUtilized.add(connector);
+ this.bwUtilNotifyQ.add(new UtilizationUpdate(connector,
+ UpdateType.ADDED));
+ }
+ } else {
+ if (connectorsOverUtilized.contains(connector)) {
+ connectorsOverUtilized.remove(connector);
+ this.bwUtilNotifyQ.add(new UtilizationUpdate(connector,
+ UpdateType.REMOVED));
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ logger.trace("DESTROY called!");
+ notifyQ = null;
+ notifyThread = null;
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ logger.trace("START called!");
+ notifyThread.start();
+ bwUtilNotifyThread.start();
+ ofPluginTopoBulkUpdate.start();
+ pollTimer.scheduleAtFixedRate(txRatePoller, 10000, 5000);
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ logger.trace("STOP called!");
+ shuttingDown = true;
+ notifyThread.interrupt();
+ }
+
+ void setTopologyServiceShimListener(Map<?, ?> props,
+ ITopologyServiceShimListener s) {
+ if (props == null) {
+ logger.error("Didn't receive the service properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("containerName not supplied");
+ return;
+ }
+ if ((this.topologyServiceShimListeners != null)
+ && !this.topologyServiceShimListeners
+ .containsKey(containerName)) {
+ this.topologyServiceShimListeners.put(containerName, s);
+ logger.trace("Added topologyServiceShimListener for container: {}",
+ containerName);
+ }
+ }
+
+ void unsetTopologyServiceShimListener(Map<?, ?> props,
+ ITopologyServiceShimListener s) {
+ if (props == null) {
+ logger.error("Didn't receive the service properties");
+ return;
+ }
+ String containerName = (String) props.get("containerName");
+ if (containerName == null) {
+ logger.error("containerName not supplied");
+ return;
+ }
+ if ((this.topologyServiceShimListeners != null)
+ && this.topologyServiceShimListeners.containsKey(containerName)
+ && this.topologyServiceShimListeners.get(containerName).equals(
+ s)) {
+ this.topologyServiceShimListeners.remove(containerName);
+ logger.trace(
+ "Removed topologyServiceShimListener for container: {}",
+ containerName);
+ }
+ }
+
+ void setStatisticsManager(IOFStatisticsManager s) {
+ this.statsMgr = s;
+ }
+
+ void unsetStatisticsManager(IOFStatisticsManager s) {
+ if (this.statsMgr == s) {
+ this.statsMgr = null;
+ }
+ }
+
+ IPluginOutConnectionService connectionPluginOutService;
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionPluginOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionPluginOutService == s) {
+ connectionPluginOutService = null;
+ }
+ }
+
+ private void removeNodeConnector(String container,
+ NodeConnector nodeConnector) {
+ List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
+ Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
+ .get(container);
+ if (edgePropsMap == null) {
+ return;
+ }
+
+ // Remove edge in one direction
+ Pair<Edge, Set<Property>> edgeProps = edgePropsMap.get(nodeConnector);
+ if (edgeProps == null) {
+ return;
+ }
+ teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null,
+ UpdateType.REMOVED));
+
+ // Remove edge in another direction
+ edgeProps = edgePropsMap
+ .get(edgeProps.getLeft().getHeadNodeConnector());
+ if (edgeProps == null) {
+ return;
+ }
+ teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), null,
+ UpdateType.REMOVED));
+
+ // Update in one shot
+ notifyEdge(container, teuList);
+ }
+
+ /**
+ * Update local cache and return true if it needs to notify upper layer
+ * Topology listeners.
+ *
+ * @param container
+ * The network container
+ * @param edge
+ * The edge
+ * @param type
+ * The update type
+ * @param props
+ * The edge properties
+ * @return true if it needs to notify upper layer Topology listeners
+ */
+ private boolean updateLocalEdgeMap(String container, Edge edge,
+ UpdateType type, Set<Property> props) {
+ ConcurrentMap<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
+ .get(container);
+ NodeConnector src = edge.getTailNodeConnector();
+ Pair<Edge, Set<Property>> edgeProps = new ImmutablePair<Edge, Set<Property>>(
+ edge, props);
+ boolean rv = false;
+
+ switch (type) {
+ case ADDED:
+ case CHANGED:
+ if (edgePropsMap == null) {
+ edgePropsMap = new ConcurrentHashMap<NodeConnector, Pair<Edge, Set<Property>>>();
+ rv = true;
+ } else {
+ if (edgePropsMap.containsKey(src)
+ && edgePropsMap.get(src).equals(edgeProps)) {
+ // Entry already exists. No update.
+ rv = false;
+ } else {
+ rv = true;
+ }
+ }
+ if (rv) {
+ edgePropsMap.put(src, edgeProps);
+ edgeMap.put(container, edgePropsMap);
+ }
+ break;
+ case REMOVED:
+ if ((edgePropsMap != null) && edgePropsMap.containsKey(src)) {
+ edgePropsMap.remove(src);
+ if (edgePropsMap.isEmpty()) {
+ edgeMap.remove(container);
+ } else {
+ edgeMap.put(container, edgePropsMap);
+ }
+ rv = true;
+ }
+ break;
+ default:
+ logger.debug(
+ "notifyLocalEdgeMap: invalid {} for Edge {} in container {}",
+ new Object[] { type.getName(), edge, container });
+ }
+
+ if (rv) {
+ logger.debug(
+ "notifyLocalEdgeMap: {} for Edge {} in container {}",
+ new Object[] { type.getName(), edge, container });
+ }
+
+ return rv;
+ }
+
+ private void notifyEdge(String container, Edge edge, UpdateType type,
+ Set<Property> props) {
+ boolean notifyListeners;
+
+ // Update local cache
+ notifyListeners = updateLocalEdgeMap(container, edge, type, props);
+
+ // Prepare to update TopologyService
+ if (notifyListeners) {
+ notifyQ.add(new NotifyEntry(container, new TopoEdgeUpdate(edge, props,
+ type)));
+ logger.debug("notifyEdge: {} Edge {} in container {}",
+ new Object[] { type.getName(), edge, container });
+ }
+ }
+
+ private void notifyEdge(String container, List<TopoEdgeUpdate> etuList) {
+ if (etuList == null) {
+ return;
+ }
+
+ Edge edge;
+ UpdateType type;
+ List<TopoEdgeUpdate> etuNotifyList = new ArrayList<TopoEdgeUpdate>();
+ boolean notifyListeners = false, rv;
+
+ for (TopoEdgeUpdate etu : etuList) {
+ edge = etu.getEdge();
+ type = etu.getUpdateType();
+
+ // Update local cache
+ rv = updateLocalEdgeMap(container, edge, type, etu.getProperty());
+ if (rv) {
+ if (!notifyListeners) {
+ notifyListeners = true;
+ }
+ etuNotifyList.add(etu);
+ logger.debug(
+ "notifyEdge(TopoEdgeUpdate): {} Edge {} in container {}",
+ new Object[] { type.getName(), edge, container });
+ }
+ }
+
+ // Prepare to update TopologyService
+ if (notifyListeners) {
+ notifyQ.add(new NotifyEntry(container, etuNotifyList));
+ logger.debug("notifyEdge(TopoEdgeUpdate): add notifyQ");
+ }
+ }
+
+ @Override
+ public void notifyEdge(Edge edge, UpdateType type, Set<Property> props) {
+ if ((edge == null) || (type == null)) {
+ return;
+ }
+
+ // Notify default container
+ notifyEdge(GlobalConstants.DEFAULT.toString(), edge, type, props);
+
+ // Notify the corresponding containers
+ List<String> containers = getEdgeContainers(edge);
+ if (containers != null) {
+ for (String container : containers) {
+ notifyEdge(container, edge, type, props);
+ }
+ }
+ }
+
+ /*
+ * Return a list of containers the edge associated with
+ */
+ private List<String> getEdgeContainers(Edge edge) {
+ NodeConnector src = edge.getTailNodeConnector(), dst = edge
+ .getHeadNodeConnector();
+
+ if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ /* Find the common containers for both ends */
+ List<String> srcContainers = this.containerMap.get(src), dstContainers = this.containerMap
+ .get(dst), cmnContainers = null;
+ if ((srcContainers != null) && (dstContainers != null)) {
+ cmnContainers = new ArrayList<String>(srcContainers);
+ cmnContainers.retainAll(dstContainers);
+ }
+ return cmnContainers;
+ } else {
+ /*
+ * If the neighbor is part of a monitored production network, get
+ * the containers that the edge port belongs to
+ */
+ return this.containerMap.get(dst);
+ }
+ }
+
+ @Override
+ public void tagUpdated(String containerName, Node n, short oldTag,
+ short newTag, UpdateType t) {
+ }
+
+ @Override
+ public void containerFlowUpdated(String containerName,
+ ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
+ }
+
+ @Override
+ public void nodeConnectorUpdated(String containerName, NodeConnector p,
+ UpdateType t) {
+ if (this.containerMap == null) {
+ logger.error("containerMap is NULL");
+ return;
+ }
+ List<String> containers = this.containerMap.get(p);
+ if (containers == null) {
+ containers = new CopyOnWriteArrayList<String>();
+ }
+ boolean updateMap = false;
+ switch (t) {
+ case ADDED:
+ if (!containers.contains(containerName)) {
+ containers.add(containerName);
+ updateMap = true;
+ }
+ break;
+ case REMOVED:
+ if (containers.contains(containerName)) {
+ containers.remove(containerName);
+ updateMap = true;
+ removeNodeConnector(containerName, p);
+ }
+ break;
+ case CHANGED:
+ break;
+ }
+ if (updateMap) {
+ if (containers.isEmpty()) {
+ // Do cleanup to reduce memory footprint if no
+ // elements to be tracked
+ this.containerMap.remove(p);
+ } else {
+ this.containerMap.put(p, containers);
+ }
+ }
+ }
+
+ @Override
+ public void containerModeUpdated(UpdateType t) {
+ // do nothing
+ }
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+ .getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this,
+ null);
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---Topology Service Shim---\n");
+ help.append("\t pem [container] - Print edgeMap entries");
+ help.append(" for a given container\n");
+ return help.toString();
+ }
+
+ public void _pem(CommandInterpreter ci) {
+ String container = ci.nextArgument();
+ if (container == null) {
+ container = GlobalConstants.DEFAULT.toString();
+ }
+
+ ci.println("Container: " + container);
+ ci.println(" Edge Bandwidth");
+
+ Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropsMap = edgeMap
+ .get(container);
+ if (edgePropsMap == null) {
+ return;
+ }
+ int count = 0;
+ for (Pair<Edge, Set<Property>> edgeProps : edgePropsMap.values()) {
+ if (edgeProps == null) {
+ continue;
+ }
+
+ long bw = 0;
+ Set<Property> props = edgeProps.getRight();
+ if (props != null) {
+ for (Property prop : props) {
+ if (prop.getName().equals(Bandwidth.BandwidthPropName)) {
+ bw = ((Bandwidth) prop).getValue();
+ }
+ }
+ }
+ count++;
+ ci.println(edgeProps.getLeft() + " " + bw);
+ }
+ ci.println("Total number of Edges: " + count);
+ }
+
+ public void _bwfactor(CommandInterpreter ci) {
+ String factorString = ci.nextArgument();
+ if (factorString == null) {
+ ci.println("Bw threshold: " + this.bwThresholdFactor);
+ ci.println("Insert a non null bw threshold");
+ return;
+ }
+ bwThresholdFactor = Float.parseFloat(factorString);
+ ci.println("New Bw threshold: " + this.bwThresholdFactor);
+ }
+
+ /**
+ * This method will trigger topology updates to be sent toward SAL. SAL then
+ * pushes the updates to ALL the applications that have registered as
+ * listeners for this service. SAL has no way of knowing which application
+ * requested for the refresh.
+ *
+ * As an example of this case, is stopping and starting the Topology
+ * Manager. When the topology Manager is stopped, and restarted, it will no
+ * longer have the latest topology. Hence, a request is sent here.
+ *
+ * @param containerName
+ * @return void
+ */
+ @Override
+ public void requestRefresh(String containerName) {
+ // wake up a bulk update thread and exit
+ // the thread will execute the bulkUpdate()
+ bulkNotifyQ.add(containerName);
+ }
+
+ /**
+ * Reading the current topology database, the method will replay all the
+ * edge updates for the ITopologyServiceShimListener instance in the given
+ * container, which will in turn publish them toward SAL.
+ *
+ * @param containerName
+ */
+ private void TopologyBulkUpdate(String containerName) {
+ Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropMap = null;
+
+ logger.debug("Try bulk update for container:{}", containerName);
+ edgePropMap = edgeMap.get(containerName);
+ if (edgePropMap == null) {
+ logger.debug("No edges known for container:{}", containerName);
+ return;
+ }
+ ITopologyServiceShimListener topologServiceShimListener = topologyServiceShimListeners
+ .get(containerName);
+ if (topologServiceShimListener == null) {
+ logger.debug("No topology service shim listener for container:{}",
+ containerName);
+ return;
+ }
+ int i = 0;
+ List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
+ for (Pair<Edge, Set<Property>> edgeProps : edgePropMap.values()) {
+ if (edgeProps != null) {
+ i++;
+ teuList.add(new TopoEdgeUpdate(edgeProps.getLeft(), edgeProps
+ .getRight(), UpdateType.ADDED));
+ logger.trace("Add edge {}", edgeProps.getLeft());
+ }
+ }
+ if (i > 0) {
+ topologServiceShimListener.edgeUpdate(teuList);
+ }
+ logger.debug("Sent {} updates", i);
+ }
+
+ @Override
+ public void updateNode(Node node, UpdateType type, Set<Property> props) {
+ }
+
+ @Override
+ public void updateNodeConnector(NodeConnector nodeConnector,
+ UpdateType type, Set<Property> props) {
+ List<String> containers = new ArrayList<String>();
+ List<String> conList = this.containerMap.get(nodeConnector);
+
+ containers.add(GlobalConstants.DEFAULT.toString());
+ if (conList != null) {
+ containers.addAll(conList);
+ }
+
+ switch (type) {
+ case ADDED:
+ break;
+ case CHANGED:
+ if (props == null) {
+ break;
+ }
+
+ boolean rmEdge = false;
+ for (Property prop : props) {
+ if (((prop instanceof Config) && (((Config) prop).getValue() != Config.ADMIN_UP))
+ || ((prop instanceof State) && (((State) prop)
+ .getValue() != State.EDGE_UP))) {
+ /*
+ * If port admin down or link down, remove the edges
+ * associated with the port
+ */
+ rmEdge = true;
+ break;
+ }
+ }
+
+ if (rmEdge) {
+ for (String cName : containers) {
+ removeNodeConnector(cName, nodeConnector);
+ }
+ }
+ break;
+ case REMOVED:
+ for (String cName : containers) {
+ removeNodeConnector(cName, nodeConnector);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.util.Dictionary;
+import java.util.List;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.topology.IPluginInTopologyService;
+import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
+import org.opendaylight.openflowplugin.openflow.IRefreshInternalProvider;
+import org.opendaylight.openflowplugin.openflow.ITopologyServiceShimListener;
+
+public class TopologyServices implements ITopologyServiceShimListener,
+ IPluginInTopologyService {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(TopologyServices.class);
+ private IPluginOutTopologyService salTopoService = null;
+ private IRefreshInternalProvider topoRefreshService = null;
+ private IPluginOutConnectionService connectionOutService;
+ private String containerName;
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ @SuppressWarnings("unchecked")
+ void init(Component c) {
+ logger.trace("INIT called!");
+ Dictionary<Object, Object> props = c.getServiceProperties();
+ containerName = (props != null) ? (String) props.get("containerName")
+ : null;
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ logger.trace("DESTROY called!");
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ logger.trace("START called!");
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ logger.trace("STOP called!");
+ }
+
+ /**
+ * Retrieve SAL service IPluginOutTopologyService
+ *
+ * @param s
+ * Called by Dependency Manager as soon as the SAL service is
+ * available
+ */
+ public void setPluginOutTopologyService(IPluginOutTopologyService s) {
+ logger.trace("Setting IPluginOutTopologyService to: {}", s);
+ this.salTopoService = s;
+ }
+
+ /**
+ * called when SAL service IPluginOutTopologyService is no longer available
+ *
+ * @param s
+ * Called by Dependency Manager as soon as the SAL service is
+ * unavailable
+ */
+ public void unsetPluginOutTopologyService(IPluginOutTopologyService s) {
+ if (this.salTopoService == s) {
+ logger.trace("UNSetting IPluginOutTopologyService from: {}", s);
+ this.salTopoService = null;
+ }
+ }
+
+ /**
+ * Retrieve OF protocol_plugin service IRefreshInternalProvider
+ *
+ * @param s
+ * Called by Dependency Manager as soon as the SAL service is
+ * available
+ */
+ public void setRefreshInternalProvider(IRefreshInternalProvider s) {
+ logger.trace("Setting IRefreshInternalProvider to: {}", s);
+ this.topoRefreshService = s;
+ }
+
+ /**
+ * called when OF protocol_plugin service IRefreshInternalProvider is no
+ * longer available
+ *
+ * @param s
+ * Called by Dependency Manager as soon as the SAL service is
+ * unavailable
+ */
+ public void unsetRefreshInternalProvider(IRefreshInternalProvider s) {
+ if (this.topoRefreshService == s) {
+ logger.trace("UNSetting IRefreshInternalProvider from: {}", s);
+ this.topoRefreshService = null;
+ }
+ }
+
+ @Override
+ public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
+ if (this.salTopoService != null) {
+ this.salTopoService.edgeUpdate(topoedgeupdateList);
+ }
+ }
+
+ @Override
+ public void sollicitRefresh() {
+ logger.debug("Got a request to refresh topology");
+ topoRefreshService.requestRefresh(containerName);
+ }
+
+ @Override
+ public void edgeOverUtilized(Edge edge) {
+ if (this.salTopoService != null) {
+ this.salTopoService.edgeOverUtilized(edge);
+ }
+ }
+
+ @Override
+ public void edgeUtilBackToNormal(Edge edge) {
+ if (this.salTopoService != null) {
+ this.salTopoService.edgeUtilBackToNormal(edge);
+ }
+ }
+
+ void setIPluginOutConnectionService(IPluginOutConnectionService s) {
+ connectionOutService = s;
+ }
+
+ void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
+ if (connectionOutService == s) {
+ connectionOutService = null;
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.internal;
+
+import java.nio.ByteBuffer;
+
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6Error;
+import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFError.OFBadActionCode;
+import org.openflow.protocol.OFError.OFBadRequestCode;
+import org.openflow.protocol.OFError.OFErrorType;
+import org.openflow.protocol.OFError.OFFlowModFailedCode;
+import org.openflow.protocol.OFError.OFHelloFailedCode;
+import org.openflow.protocol.OFError.OFPortModFailedCode;
+import org.openflow.protocol.OFError.OFQueueOpFailedCode;
+
+public final class Utils {
+
+ private Utils() { //prevent instantiation
+ throw new AssertionError();
+ }
+ static String getOFErrorString(OFError error) {
+ // Handle VENDOR extension errors here
+ if (error.getErrorType() == V6Error.NICIRA_VENDOR_ERRORTYPE) {
+ V6Error er = new V6Error(error);
+ byte[] b = error.getError();
+ ByteBuffer bb = ByteBuffer.allocate(b.length);
+ bb.put(b);
+ bb.rewind();
+ er.readFrom(bb);
+ return er.toString();
+ }
+
+ // Handle OF1.0 errors here
+ OFErrorType et = OFErrorType.values()[0xffff & error.getErrorType()];
+ String errorStr = "Error : " + et.toString();
+ switch (et) {
+ case OFPET_HELLO_FAILED:
+ OFHelloFailedCode hfc = OFHelloFailedCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + hfc.toString();
+ break;
+ case OFPET_BAD_REQUEST:
+ OFBadRequestCode brc = OFBadRequestCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + brc.toString();
+ break;
+ case OFPET_BAD_ACTION:
+ OFBadActionCode bac = OFBadActionCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + bac.toString();
+ break;
+ case OFPET_FLOW_MOD_FAILED:
+ OFFlowModFailedCode fmfc = OFFlowModFailedCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + fmfc.toString();
+ break;
+ case OFPET_PORT_MOD_FAILED:
+ OFPortModFailedCode pmfc = OFPortModFailedCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + pmfc.toString();
+ break;
+ case OFPET_QUEUE_OP_FAILED:
+ OFQueueOpFailedCode qofc = OFQueueOpFailedCode.values()[0xffff & error
+ .getErrorCode()];
+ errorStr += " " + qofc.toString();
+ break;
+ default:
+ break;
+ }
+ return errorStr;
+ }
+}
--- /dev/null
+package org.opendaylight.openflowplugin.openflow.vendorextension.v6extension;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.openflow.protocol.OFError;
+
+public class V6Error extends OFError {
+ private static final long serialVersionUID = 1L;
+ public static int MINIMUM_LENGTH = 20;//OfHdr(8) + NXET_VENDOR(2) + NXEC_VENDOR_ERROR(2) + struct nx_vendor_error(8)
+ public static final short NICIRA_VENDOR_ERRORTYPE = (short)0xb0c2;
+ protected int V6VendorId;
+ protected short V6VendorErrorType;
+ protected short V6VendorErrorCode;
+ protected byte[] V6ErrorData;
+
+ public V6Error(OFError e) {
+ this.length = (short)e.getLengthU();
+ this.errorType = e.getErrorType();
+ this.errorCode = e.getErrorCode();
+ this.xid = e.getXid();
+ }
+
+ @Override
+ public void readFrom(ByteBuffer data) {
+ this.V6VendorId = data.getInt();
+ this.V6VendorErrorType = data.getShort();
+ this.V6VendorErrorCode = data.getShort();
+ int dataLength = this.getLengthU() - MINIMUM_LENGTH;
+ if (dataLength > 0) {
+ this.V6ErrorData = new byte[dataLength];
+ data.get(this.V6ErrorData);
+ }
+ }
+
+ /**
+ * @return the V6VendorId
+ */
+ public int getVendorId() {
+ return V6VendorId;
+ }
+
+ /**
+ * @return the V6VendorErrorType
+ */
+ public short getVendorErrorType() {
+ return V6VendorErrorType;
+ }
+
+ /**
+ * @return the VendorErrorType
+ */
+ public short getVendorErrorCode() {
+ return V6VendorErrorCode;
+ }
+
+ /**
+ * @return the Error Bytes
+ */
+ public byte[] getError() {
+ return V6ErrorData;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + Arrays.hashCode(V6ErrorData);
+ result = prime * result + V6VendorErrorCode;
+ result = prime * result + V6VendorErrorType;
+ result = prime * result + V6VendorId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "V6Error [V6VendorId=" + V6VendorId + ", V6VendorErrorType="
+ + V6VendorErrorType + ", V6VendorErrorCode="
+ + V6VendorErrorCode + ", V6ErrorData="
+ + Arrays.toString(V6ErrorData) + ", errorType=" + errorType
+ + ", errorCode=" + errorCode + ", factory=" + factory
+ + ", error=" + Arrays.toString(error) + ", errorIsAscii="
+ + errorIsAscii + ", version=" + version + ", type=" + type
+ + ", length=" + length + ", xid=" + xid + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ V6Error other = (V6Error) obj;
+ if (!Arrays.equals(V6ErrorData, other.V6ErrorData))
+ return false;
+ if (V6VendorErrorCode != other.V6VendorErrorCode)
+ return false;
+ if (V6VendorErrorType != other.V6VendorErrorType)
+ return false;
+ if (V6VendorId != other.V6VendorId)
+ return false;
+ return true;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.vendorextension.v6extension;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFVendor;
+import org.openflow.protocol.action.OFAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to create IPv6 Vendor Extension messages. Specfically, It
+ * defines the methods used in creation of Vendor specific IPv6 Flow Mod message.
+ *
+ *
+ */
+public class V6FlowMod extends OFVendor implements Cloneable {
+ private static final Logger logger = LoggerFactory
+ .getLogger(V6FlowMod.class);
+ private static final long serialVersionUID = 1L;
+ protected V6Match match;
+ protected long cookie;
+ protected short command;
+ protected short idleTimeout;
+ protected short hardTimeout;
+ protected short priority;
+ protected int bufferId;
+ protected short outPort;
+ protected short flags;
+ protected List<OFAction> actions;
+ short match_len;
+ short actions_len;
+ short pad_size;
+
+ private static int IPV6EXT_ADD_FLOW_MSG_TYPE = 13;
+ private static int IPV6_EXT_MIN_HDR_LEN = 36;
+
+ /**
+ * Constructor for the V6FlowMod class. Initializes OFVendor (parent class)
+ * fields by calling the parent class' constructor.
+ */
+ public V6FlowMod() {
+ super();
+ }
+
+ /**
+ * This method sets the match fields of V6FlowMod object
+ * @param match V6Match object for this V6FlowMod message
+ */
+ public void setMatch(V6Match match) {
+ this.match = match;
+ }
+
+ /**
+ * Sets the list of actions V6FlowMod message
+ * @param actions a list of ordered OFAction objects
+ */
+ public void setActions(List<OFAction> actions) {
+ this.actions = actions;
+ }
+
+ /**
+ * Sets the priority field of V6FlowMod message
+ * @param priority Priority of the message
+ */
+ public void setPriority(short priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Sets the cookie field of V6FlowMod message
+ * @param cookie Cookie of the message
+ */
+ public void setCookie(long cookie) {
+ this.cookie = cookie;
+ }
+
+ /**
+ * Sets the command field of V6FlowMod message
+ * @param command Command type of the message (ADD or DELETE)
+ */
+ public V6FlowMod setCommand(short command) {
+ this.command = command;
+ return this;
+ }
+
+ /**
+ * Sets the outPort field of V6FlowMod message
+ * @param outPort outPort of the message
+ */
+ public V6FlowMod setOutPort(OFPort port) {
+ this.outPort = port.getValue();
+ return this;
+ }
+
+ /**
+ * Sets the idle_timeout of V6FlowMod message
+ * @param idleTimeout idle timeout for this message
+ */
+ public void setIdleTimeout(short idleTimeout) {
+ this.idleTimeout = idleTimeout;
+ }
+
+ /**
+ * Sets the hardTimeout field of V6FlowMod message
+ * @param hardTimeout hard timeout of the message
+ */
+ public void setHardTimeout(short hardTimeout) {
+ this.hardTimeout = hardTimeout;
+ }
+
+ /**
+ * Returns the Flow Mod message subtype for V6FlowMod message
+ * @return message subtype
+ */
+ private int getIPv6ExtensionFlowModAddSubType() {
+ return IPV6EXT_ADD_FLOW_MSG_TYPE;
+ }
+
+ /**
+ * Returns the minimum header size for V6Flow Message type
+ * @return minimum header size
+ */
+
+ public int getV6FlowModMinHdrSize() {
+ return IPV6_EXT_MIN_HDR_LEN;
+ }
+
+ /**
+ * Sets the Vendor type in OFVendor message
+ */
+
+ public void setVendor() {
+ super.setVendor(V6StatsRequest.NICIRA_VENDOR_ID);
+ }
+
+ /**
+ * Get flags
+ * @return
+ */
+ public short getFlags() {
+ return flags;
+ }
+
+ /**
+ * Set flags
+ * @param flags
+ */
+ public void setFlags(short flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * This method forms the Vendor extension IPv6 Flow Mod message.It uses the
+ * fields in V6FlowMod class, and writes the data according to vendor
+ * extension format. The fields include flow properties (cookie, timeout,
+ * priority, etc), flow match, and action list. It also takes care of
+ * required padding.
+ */
+
+ @Override
+ public void writeTo(ByteBuffer data) {
+ super.writeTo(data);
+ data.putInt(getIPv6ExtensionFlowModAddSubType());
+ data.putLong(this.cookie);
+ data.putShort(command); /* should be OFPFC_ADD, OFPFC_DELETE_STRICT, etc*/
+ data.putShort(this.idleTimeout);
+ data.putShort(this.hardTimeout);
+ data.putShort(this.priority);
+ data.putInt(OFPacketOut.BUFFER_ID_NONE);
+ data.putShort(outPort); /* output_port */
+ data.putShort(flags); /* flags */
+ match_len = this.match.getIPv6MatchLen();
+ data.putShort(match_len);
+ byte[] pad = new byte[6];
+ data.put(pad);
+ this.match.writeTo(data);
+
+ pad_size = (short) (((match_len + 7) / 8) * 8 - match_len);
+
+ /*
+ * action list should be preceded by a padding of 0 to 7 bytes based upon
+ * above formula.
+ */
+
+ byte[] pad2 = new byte[pad_size];
+ data.put(pad2);
+ if (actions != null) {
+ for (OFAction action : actions) {
+ actions_len += action.getLength();
+ action.writeTo(data);
+ }
+ }
+ logger.trace("{}", this);
+ }
+
+ /**
+ * Forms the clone of V6FlowMod Object. If Object is returned
+ * successfully, then returns the cloned object. Throws an
+ * exception if cloning is not supported.
+ */
+ @Override
+ public V6FlowMod clone() {
+ try {
+ V6Match neoMatch = match.clone();
+ V6FlowMod v6flowMod = (V6FlowMod) super.clone();
+ v6flowMod.setMatch(neoMatch);
+ List<OFAction> neoActions = new LinkedList<OFAction>();
+ for (OFAction action : this.actions)
+ neoActions.add((OFAction) action.clone());
+ v6flowMod.setActions(neoActions);
+ return v6flowMod;
+ } catch (CloneNotSupportedException e) {
+ // Won't happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "V6FlowMod [match=" + match + ", cookie=" + cookie
+ + ", command=" + command + ", idleTimeout=" + idleTimeout
+ + ", hardTimeout=" + hardTimeout + ", priority=" + priority
+ + ", bufferId=" + bufferId + ", outPort=" + outPort
+ + ", flags=" + flags + ", actions=" + actions + ", match_len="
+ + match_len + ", actions_len=" + actions_len + ", pad_size="
+ + pad_size + "]";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.vendorextension.v6extension;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.openflow.protocol.OFMatch;
+import org.openflow.util.U16;
+import org.openflow.util.U8;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This Class forms the vendor specific IPv6 Flow Match messages as well as
+ * processes the vendor specific IPv6 Stats Reply message.
+ *
+ * For message creation, it parses the user entered IPv6 match fields, creates a
+ * sub-message for each field which are later used to form the complete message.
+ *
+ * For message processing, it parses the incoming message and reads each field
+ * of the message and stores in appropriate field of V6Match object.
+ *
+ *
+ */
+public class V6Match extends OFMatch implements Cloneable {
+ private static final Logger logger = LoggerFactory.getLogger(V6Match.class);
+ private static final long serialVersionUID = 1L;
+ protected Inet6Address nwSrc;
+ protected Inet6Address nwDst;
+ protected short inputPortMask;
+ protected byte[] dataLayerSourceMask;
+ protected byte[] dataLayerDestinationMask;
+ protected int dataLayerVirtualLanTCIMask;
+ protected short dataLayerTypeMask;
+ protected byte networkTypeOfServiceMask;
+ protected byte networkProtocolMask;
+ protected short transportSourceMask;
+ protected short transportDestinationMask;
+ protected short srcIPv6SubnetMaskbits;
+ protected short dstIPv6SubnetMaskbits;
+
+ protected MatchFieldState inputPortState;
+ protected MatchFieldState dlSourceState;
+ protected MatchFieldState dlDestState;
+ protected MatchFieldState dlVlanIDState;
+ protected MatchFieldState dlVlanPCPState;
+ protected MatchFieldState dlVlanTCIState;
+ protected MatchFieldState ethTypeState;
+ protected MatchFieldState nwTosState;
+ protected MatchFieldState nwProtoState;
+ protected MatchFieldState nwSrcState;
+ protected MatchFieldState nwDstState;
+ protected MatchFieldState tpSrcState;
+ protected MatchFieldState tpDstState;
+ protected short match_len = 0;
+ protected short pad_size = 0;
+
+ private static int IPV6_EXT_MIN_HDR_LEN = 36;
+
+ private enum MatchFieldState {
+ MATCH_ABSENT, MATCH_FIELD_ONLY, MATCH_FIELD_WITH_MASK
+ }
+
+ private enum OF_Match_Types {
+ MATCH_OF_IN_PORT(0), MATCH_OF_ETH_DST(1), MATCH_OF_ETH_SRC(2), MATCH_OF_ETH_TYPE(
+ 3), MATCH_OF_VLAN_TCI(4), MATCH_OF_IP_TOS(5), MATCH_OF_IP_PROTO(
+ 6), MATCH_OF_IP_SRC(7), MATCH_OF_IP_DST(8), MATCH_OF_TCP_SRC(9), MATCH_OF_TCP_DST(
+ 10), MATCH_OF_UDP_SRC(11), MATCH_OF_UDP_DST(12), MATCH_OF_ICMTP_TYPE(
+ 13), MATCH_OF_ICMP_CODE(14), MATCH_OF_ARP_OP(15);
+
+ private int value;
+
+ private OF_Match_Types(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+ }
+
+ private enum IPv6Extension_Match_Types {
+ MATCH_IPV6EXT_TUN_ID(16), MATCH_IPV6EXT_ARP_SHA(17), MATCH_IPV6EXT_ARP_THA(
+ 18), MATCH_IPV6EXT_IPV6_SRC(19), MATCH_IPV6EXT_IPV6_DST(20);
+
+ private int value;
+
+ private IPv6Extension_Match_Types(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ public enum Extension_Types {
+ OF_10(0), IPV6EXT(1);
+
+ protected int value;
+
+ private Extension_Types(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ public V6Match() {
+ super();
+
+ this.nwSrc = null;
+ this.nwDst = null;
+
+ this.inputPortMask = 0;
+ this.dataLayerSourceMask = null;
+ this.dataLayerDestinationMask = null;
+ this.dataLayerTypeMask = 0;
+ this.dataLayerVirtualLanTCIMask = 0;
+ this.networkTypeOfServiceMask = 0;
+ this.networkProtocolMask = 0;
+ this.transportSourceMask = 0;
+ this.transportDestinationMask = 0;
+
+ this.inputPortState = MatchFieldState.MATCH_ABSENT;
+ this.dlSourceState = MatchFieldState.MATCH_ABSENT;
+ this.dlDestState = MatchFieldState.MATCH_ABSENT;
+ this.dlVlanIDState = MatchFieldState.MATCH_ABSENT;
+ this.dlVlanPCPState = MatchFieldState.MATCH_ABSENT;
+ this.dlVlanTCIState = MatchFieldState.MATCH_ABSENT;
+ this.ethTypeState = MatchFieldState.MATCH_ABSENT;
+ this.nwTosState = MatchFieldState.MATCH_ABSENT;
+ this.nwProtoState = MatchFieldState.MATCH_ABSENT;
+ this.nwSrcState = MatchFieldState.MATCH_ABSENT;
+ this.nwDstState = MatchFieldState.MATCH_ABSENT;
+ this.tpSrcState = MatchFieldState.MATCH_ABSENT;
+ this.tpDstState = MatchFieldState.MATCH_ABSENT;
+
+ this.match_len = 0;
+ this.pad_size = 0;
+ }
+
+ public V6Match(OFMatch match) {
+ super();
+ this.match_len = 0;
+ this.pad_size = 0;
+
+ if (match.getNetworkSource() != 0) {
+ InetAddress address = NetUtils.getInetAddress(match.getNetworkSource());
+ InetAddress mask = NetUtils.getInetNetworkMask(match.getNetworkSourceMaskLen(), false);
+ this.setNetworkSource(address, mask);
+ } else {
+ this.nwSrcState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ if (match.getNetworkDestination() != 0) {
+ InetAddress address = NetUtils.getInetAddress(match.getNetworkDestination());
+ InetAddress mask = NetUtils.getInetNetworkMask(match.getNetworkDestinationMaskLen(), false);
+ this.setNetworkDestination(address, mask);
+ } else {
+ this.nwDstState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.inputPortMask = 0;
+ if (match.getInputPort() != 0) {
+ this.setInputPort(match.getInputPort(), (short) 0);
+ } else {
+ this.inputPortMask = 0;
+ this.inputPortState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.dataLayerSourceMask = null;
+ if (match.getDataLayerSource() != null
+ && !NetUtils.isZeroMAC(match.getDataLayerSource())) {
+ this.setDataLayerSource(match.getDataLayerSource(), null);
+ } else {
+ this.dlSourceState = MatchFieldState.MATCH_ABSENT;
+ }
+ this.dataLayerDestinationMask = null;
+ if (match.getDataLayerDestination() != null
+ && !NetUtils.isZeroMAC(match.getDataLayerDestination())) {
+ this.setDataLayerDestination(match.getDataLayerDestination(), null);
+ } else {
+ this.dlDestState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.dataLayerTypeMask = 0;
+ if (match.getDataLayerType() != 0) {
+ this.setDataLayerType(match.getDataLayerType(), (short) 0);
+ } else {
+ this.dataLayerType = 0;
+ this.ethTypeState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.dataLayerVirtualLanTCIMask = 0;
+ this.dlVlanTCIState = MatchFieldState.MATCH_ABSENT;
+ if (match.getDataLayerVirtualLan() != 0) {
+ this.setDataLayerVirtualLan(match.getDataLayerVirtualLan(),
+ (short) 0);
+ } else {
+ this.dataLayerVirtualLan = 0;
+ this.dlVlanIDState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ if (match.getDataLayerVirtualLanPriorityCodePoint() != 0) {
+ this.setDataLayerVirtualLanPriorityCodePoint(
+ match.getDataLayerVirtualLanPriorityCodePoint(), (byte) 0);
+ } else {
+ this.dataLayerVirtualLanPriorityCodePoint = 0;
+ this.dlVlanPCPState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.networkProtocolMask = 0;
+ if (match.getNetworkProtocol() != 0) {
+ this.setNetworkProtocol(
+ this.networkProtocol = match.getNetworkProtocol(), (byte) 0);
+ } else {
+ this.networkProtocol = 0;
+ this.nwProtoState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.networkTypeOfServiceMask = 0;
+ if (match.getNetworkTypeOfService() != 0) {
+ this.setNetworkTypeOfService(
+ this.networkTypeOfService = match.getNetworkTypeOfService(),
+ (byte) 0);
+ } else {
+ this.networkTypeOfService = match.getNetworkTypeOfService();
+ this.nwTosState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.transportSourceMask = 0;
+ if (match.getTransportSource() != 0) {
+ this.setTransportSource(match.getTransportSource(), (short) 0);
+ } else {
+ this.transportSource = 0;
+ this.tpSrcState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.transportDestinationMask = 0;
+ if (match.getTransportDestination() != 0) {
+ this.setTransportDestination(match.getTransportDestination(),
+ (short) 0);
+ } else {
+ this.transportDestination = 0;
+ this.tpDstState = MatchFieldState.MATCH_ABSENT;
+ }
+
+ this.setWildcards(match.getWildcards());
+ }
+
+ private enum IPProtocols {
+ ICMP(1), TCP(6), UDP(17), ICMPV6(58);
+
+ private int protocol;
+
+ private IPProtocols(int value) {
+ this.protocol = value;
+ }
+
+ private byte getValue() {
+ return (byte) this.protocol;
+ }
+ }
+
+ public short getIPv6MatchLen() {
+ return match_len;
+ }
+
+ public int getIPv6ExtMinHdrLen() {
+ return IPV6_EXT_MIN_HDR_LEN;
+ }
+
+ public short getPadSize() {
+ return (short) (((match_len + 7) / 8) * 8 - match_len);
+ }
+
+ private int getIPv6ExtensionMatchHeader(Extension_Types extType, int field,
+ int has_mask, int length) {
+ return (((extType.getValue() & 0x0000ffff) << 16)
+ | ((field & 0x0000007f) << 9) | ((has_mask & 0x00000001) << 8) | (length & 0x000000ff));
+ }
+
+ private byte[] getIPv6ExtensionPortMatchMsg(short port) {
+ ByteBuffer ipv6ext_port_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_IN_PORT.getValue(), 0, 2);
+ ipv6ext_port_msg.putInt(nxm_header);
+ ipv6ext_port_msg.putShort(port);
+ return (ipv6ext_port_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionDestMacMatchMsg(byte[] destMac) {
+ ByteBuffer ipv6ext_destmac_msg = ByteBuffer.allocate(10);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_ETH_DST.getValue(), 0, 6);
+ ipv6ext_destmac_msg.putInt(nxm_header);
+ ipv6ext_destmac_msg.put(destMac);
+ return (ipv6ext_destmac_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionSrcMacMatchMsg(byte[] srcMac) {
+ ByteBuffer ipv6ext_srcmac_msg = ByteBuffer.allocate(10);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_ETH_SRC.getValue(), 0, 6);
+ ipv6ext_srcmac_msg.putInt(nxm_header);
+ ipv6ext_srcmac_msg.put(srcMac);
+ return (ipv6ext_srcmac_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionEtherTypeMatchMsg(short EtherType) {
+ ByteBuffer ipv6ext_etype_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_ETH_TYPE.getValue(), 0, 2);
+ ipv6ext_etype_msg.putInt(nxm_header);
+ ipv6ext_etype_msg.putShort(EtherType);
+ return (ipv6ext_etype_msg.array());
+ }
+
+ private byte[] getVlanTCI(short dataLayerVirtualLanID,
+ byte dataLayerVirtualLanPriorityCodePoint) {
+ ByteBuffer vlan_tci = ByteBuffer.allocate(2);
+ int cfi = 1 << 12; // the cfi bit is in position 12
+ int pcp = dataLayerVirtualLanPriorityCodePoint << 13; // the pcp fields
+ // have to move by
+ // 13
+ int vlan_tci_int = pcp + cfi + dataLayerVirtualLanID;
+ vlan_tci.put((byte) (vlan_tci_int >> 8)); // bits 8 to 15
+ vlan_tci.put((byte) vlan_tci_int); // bits 0 to 7
+ return vlan_tci.array();
+ }
+
+ private byte[] getIPv6ExtensionVlanTCIMatchMsg(short dataLayerVirtualLanID,
+ byte dataLayerVirtualLanPriorityCodePoint) {
+ ByteBuffer ipv6ext_vlan_tci_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_VLAN_TCI.getValue(), 0, 2);
+ ipv6ext_vlan_tci_msg.putInt(nxm_header);
+ ipv6ext_vlan_tci_msg.put(getVlanTCI(dataLayerVirtualLanID,
+ dataLayerVirtualLanPriorityCodePoint));
+ return (ipv6ext_vlan_tci_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionVlanTCIMatchWithMaskMsg(
+ short dataLayerVirtualLan,
+ byte dataLayerVirtualLanPriorityCodePoint,
+ int dataLayerVirtualLanTCIMask) {
+ ByteBuffer ipv6ext_vlan_tci_msg = ByteBuffer.allocate(8);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_VLAN_TCI.getValue(), 1, 4);
+ ipv6ext_vlan_tci_msg.putInt(nxm_header);
+ ipv6ext_vlan_tci_msg.put(getVlanTCI(dataLayerVirtualLan,
+ dataLayerVirtualLanPriorityCodePoint));
+ ipv6ext_vlan_tci_msg.put((byte) (dataLayerVirtualLanTCIMask >> 8)); // bits
+ // 8
+ // to
+ // 15
+ ipv6ext_vlan_tci_msg.put((byte) (dataLayerVirtualLanTCIMask)); // bits 0
+ // to 7
+ return (ipv6ext_vlan_tci_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionSrcIPv6MatchMsg(byte[] srcIpv6) {
+ ByteBuffer ipv6ext_ipv6_msg = ByteBuffer.allocate(20);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.IPV6EXT,
+ IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_SRC.getValue(), 0,
+ 16);
+ ipv6ext_ipv6_msg.putInt(nxm_header);
+ ipv6ext_ipv6_msg.put(srcIpv6);
+ return (ipv6ext_ipv6_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionSrcIPv6MatchwithMaskMsg(byte[] srcIpv6,
+ short masklen) {
+ ByteBuffer ipv6ext_ipv6_msg = ByteBuffer.allocate(36);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.IPV6EXT,
+ IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_SRC.getValue(), 1,
+ 32);
+ ipv6ext_ipv6_msg.putInt(nxm_header);
+ ipv6ext_ipv6_msg.put(srcIpv6);
+ byte[] ipv6_mask = getIPv6NetworkMaskinBytes(masklen);
+ ipv6ext_ipv6_msg.put(ipv6_mask);
+ return (ipv6ext_ipv6_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionDstIPv6MatchMsg(byte[] dstIpv6) {
+ ByteBuffer ipv6ext_ipv6_msg = ByteBuffer.allocate(20);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.IPV6EXT,
+ IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_DST.getValue(), 0,
+ 16);
+ ipv6ext_ipv6_msg.putInt(nxm_header);
+ ipv6ext_ipv6_msg.put(dstIpv6);
+ return (ipv6ext_ipv6_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionDstIPv6MatchwithMaskMsg(byte[] dstIpv6,
+ short masklen) {
+ ByteBuffer ipv6ext_ipv6_msg = ByteBuffer.allocate(36);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.IPV6EXT,
+ IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_DST.getValue(), 1,
+ 32);
+ ipv6ext_ipv6_msg.putInt(nxm_header);
+ ipv6ext_ipv6_msg.put(dstIpv6);
+ byte[] ipv6_mask = getIPv6NetworkMaskinBytes(masklen);
+ ipv6ext_ipv6_msg.put(ipv6_mask);
+ return (ipv6ext_ipv6_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionProtocolMatchMsg(byte protocol) {
+ ByteBuffer ipv6ext_proto_msg = ByteBuffer.allocate(5);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_IP_PROTO.getValue(), 0, 1);
+ if (protocol == 0) {
+ return null;
+ }
+ ipv6ext_proto_msg.putInt(nxm_header);
+ if (protocol == IPProtocols.ICMP.getValue()) {
+ /*
+ * The front end passes the same protocol type values for IPv4 and
+ * IPv6 flows. For the Protocol types we allow in our GUI (ICMP,
+ * TCP, UDP), ICMP is the only one which is different for IPv6. It
+ * is 1 for v4 and 58 for v6 Therefore, we overwrite it here.
+ */
+ protocol = IPProtocols.ICMPV6.getValue();
+ }
+ ipv6ext_proto_msg.put(protocol);
+ return (ipv6ext_proto_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionTOSMatchMsg(byte tos) {
+ ByteBuffer ipv6ext_tos_msg = ByteBuffer.allocate(5);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_IP_TOS.getValue(), 0, 1);
+ ipv6ext_tos_msg.putInt(nxm_header);
+ ipv6ext_tos_msg.put(tos);
+ return (ipv6ext_tos_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionTCPSrcPortMatchMsg(short src_port) {
+ ByteBuffer ipv6ext_tcp_srcport_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_TCP_SRC.getValue(), 0, 2);
+ ipv6ext_tcp_srcport_msg.putInt(nxm_header);
+ ipv6ext_tcp_srcport_msg.putShort(src_port);
+ return (ipv6ext_tcp_srcport_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionTCPDstPortMatchMsg(short dst_port) {
+ ByteBuffer ipv6ext_tcp_dstport_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_TCP_DST.getValue(), 0, 2);
+ ipv6ext_tcp_dstport_msg.putInt(nxm_header);
+ ipv6ext_tcp_dstport_msg.putShort(dst_port);
+ return (ipv6ext_tcp_dstport_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionUDPSrcPortMatchMsg(short src_port) {
+ ByteBuffer ipv6ext_udp_srcport_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_UDP_SRC.getValue(), 0, 2);
+ ipv6ext_udp_srcport_msg.putInt(nxm_header);
+ ipv6ext_udp_srcport_msg.putShort(src_port);
+ return (ipv6ext_udp_srcport_msg.array());
+ }
+
+ private byte[] getIPv6ExtensionUDPDstPortMatchMsg(short dst_port) {
+ ByteBuffer ipv6ext_udp_dstport_msg = ByteBuffer.allocate(6);
+ int nxm_header = getIPv6ExtensionMatchHeader(Extension_Types.OF_10,
+ OF_Match_Types.MATCH_OF_UDP_DST.getValue(), 0, 2);
+ ipv6ext_udp_dstport_msg.putInt(nxm_header);
+ ipv6ext_udp_dstport_msg.putShort(dst_port);
+ return (ipv6ext_udp_dstport_msg.array());
+ }
+
+ /**
+ * Sets this (V6Match) object's member variables based on a comma-separated
+ * key=value pair similar to OFMatch's fromString.
+ *
+ * @param match
+ * a key=value comma separated string.
+ */
+ @Override
+ public void fromString(String match) throws IllegalArgumentException {
+ if (match.equals("") || match.equalsIgnoreCase("any")
+ || match.equalsIgnoreCase("all") || match.equals("[]")) {
+ match = "OFMatch[]";
+ }
+ String[] tokens = match.split("[\\[,\\]]");
+ String[] values;
+ int initArg = 0;
+ if (tokens[0].equals("OFMatch")) {
+ initArg = 1;
+ }
+ this.wildcards = OFPFW_ALL;
+ int i;
+ for (i = initArg; i < tokens.length; i++) {
+ values = tokens[i].split("=");
+ if (values.length != 2) {
+ throw new IllegalArgumentException("Token " + tokens[i]
+ + " does not have form 'key=value' parsing " + match);
+ }
+ values[0] = values[0].toLowerCase(); // try to make this case insens
+ if (values[0].equals(STR_IN_PORT) || values[0].equals("input_port")) {
+ this.inputPort = U16.t(Integer.valueOf(values[1]));
+ inputPortState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 6;
+ } else if (values[0].equals(STR_DL_DST)
+ || values[0].equals("eth_dst")) {
+ this.dataLayerDestination = HexEncode
+ .bytesFromHexString(values[1]);
+ dlDestState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 10;
+ } else if (values[0].equals(STR_DL_SRC)
+ || values[0].equals("eth_src")) {
+ this.dataLayerSource = HexEncode.bytesFromHexString(values[1]);
+ dlSourceState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 10;
+ this.wildcards &= ~OFPFW_DL_SRC;
+ } else if (values[0].equals(STR_DL_TYPE)
+ || values[0].equals("eth_type")) {
+ if (values[1].startsWith("0x")) {
+ this.dataLayerType = U16.t(Integer.valueOf(values[1]
+ .replaceFirst("0x", ""), 16));
+ } else {
+
+ this.dataLayerType = U16.t(Integer.valueOf(values[1]));
+ }
+ ethTypeState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 6;
+ } else if (values[0].equals(STR_DL_VLAN)) {
+ this.dataLayerVirtualLan = U16.t(Integer.valueOf(values[1]));
+ this.dlVlanIDState = MatchFieldState.MATCH_FIELD_ONLY;
+ // the variable dlVlanIDState is not really used as a flag
+ // for serializing and deserializing. Rather it is used as a
+ // flag
+ // to check if the vlan id is being set so that we can set the
+ // dlVlanTCIState appropriately.
+ if (this.dlVlanPCPState != MatchFieldState.MATCH_ABSENT) {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len -= 2;
+ } else {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.dataLayerVirtualLanTCIMask = 0x1fff;
+ match_len += 8;
+ }
+ this.wildcards &= ~OFPFW_DL_VLAN;
+ } else if (values[0].equals(STR_DL_VLAN_PCP)) {
+ this.dataLayerVirtualLanPriorityCodePoint = U8.t(Short
+ .valueOf(values[1]));
+ this.dlVlanPCPState = MatchFieldState.MATCH_FIELD_ONLY;
+ // the variable dlVlanPCPState is not really used as a flag
+ // for serializing and deserializing. Rather it is used as a
+ // flag
+ // to check if the vlan pcp is being set so that we can set the
+ // dlVlanTCIState appropriately.
+ if (this.dlVlanIDState != MatchFieldState.MATCH_ABSENT) {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len -= 2;
+ } else {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.dataLayerVirtualLanTCIMask = 0xf000;
+ match_len += 8;
+ }
+ this.wildcards &= ~OFPFW_DL_VLAN_PCP;
+ } else if (values[0].equals(STR_NW_DST)
+ || values[0].equals("ip_dst")) {
+ try {
+ InetAddress address = null;
+ InetAddress mask = null;
+ if (values[1].contains("/")) {
+ String addressString[] = values[1].split("/");
+ address = InetAddress.getByName(addressString[0]);
+ int masklen = Integer.valueOf(addressString[1]);
+ mask = NetUtils.getInetNetworkMask(masklen, address instanceof Inet6Address);
+ } else {
+ address = InetAddress.getByName(values[1]);
+ }
+ this.setNetworkDestination(address, mask);
+ } catch (UnknownHostException e) {
+ logger.error("", e);
+ }
+ } else if (values[0].equals(STR_NW_SRC)
+ || values[0].equals("ip_src")) {
+ try {
+ InetAddress address = null;
+ InetAddress mask = null;
+ if (values[1].contains("/")) {
+ String addressString[] = values[1].split("/");
+ address = InetAddress.getByName(addressString[0]);
+ int masklen = Integer.valueOf(addressString[1]);
+ mask = NetUtils.getInetNetworkMask(masklen, address instanceof Inet6Address);
+ } else {
+ address = InetAddress.getByName(values[1]);
+ }
+ this.setNetworkSource(address, mask);
+ } catch (UnknownHostException e) {
+ logger.error("", e);
+ }
+ } else if (values[0].equals(STR_NW_PROTO)) {
+ this.networkProtocol = U8.t(Short.valueOf(values[1]));
+ if (!(this.networkProtocol == 0)) {
+ /*
+ * if user selects proto 0, don't use it
+ */
+ nwProtoState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 5;
+ }
+ } else if (values[0].equals(STR_NW_TOS)) {
+ this.networkTypeOfService = U8.t(Short.valueOf(values[1]));
+ nwTosState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 5;
+ } else if (values[0].equals(STR_TP_DST)) {
+ this.transportDestination = U16.t(Integer.valueOf(values[1]));
+ tpDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 6;
+ } else if (values[0].equals(STR_TP_SRC)) {
+ this.transportSource = U16.t(Integer.valueOf(values[1]));
+ tpSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 6;
+ } else {
+ throw new IllegalArgumentException("unknown token " + tokens[i]
+ + " parsing " + match);
+ }
+ }
+
+ /*
+ * In a V6 extension message action list should be preceded by a padding
+ * of 0 to 7 bytes based upon following formula.
+ */
+
+ pad_size = (short) (((match_len + 7) / 8) * 8 - match_len);
+
+ }
+
+ /**
+ * Write this message's binary format to the specified ByteBuffer
+ *
+ * @param data
+ */
+ @Override
+ public void writeTo(ByteBuffer data) {
+ if (inputPortState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_ingress_port_msg = getIPv6ExtensionPortMatchMsg(this.inputPort);
+ data.put(ipv6ext_ingress_port_msg);
+ }
+ if (ethTypeState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_ether_type_msg = getIPv6ExtensionEtherTypeMatchMsg(this.dataLayerType);
+ data.put(ipv6ext_ether_type_msg);
+ }
+ if (dlDestState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_destmac_msg = getIPv6ExtensionDestMacMatchMsg(this.dataLayerDestination);
+ data.put(ipv6ext_destmac_msg);
+ }
+ if (dlSourceState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_srcmac_msg = getIPv6ExtensionSrcMacMatchMsg(this.dataLayerSource);
+ data.put(ipv6ext_srcmac_msg);
+ }
+ if (dlVlanTCIState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_vlan_tci_msg = getIPv6ExtensionVlanTCIMatchMsg(
+ this.dataLayerVirtualLan,
+ this.dataLayerVirtualLanPriorityCodePoint);
+ data.put(ipv6ext_vlan_tci_msg);
+ } else if (dlVlanTCIState == MatchFieldState.MATCH_FIELD_WITH_MASK) {
+ byte[] ipv6ext_vlan_tci_msg_with_mask = getIPv6ExtensionVlanTCIMatchWithMaskMsg(
+ this.dataLayerVirtualLan,
+ this.dataLayerVirtualLanPriorityCodePoint,
+ this.dataLayerVirtualLanTCIMask);
+ data.put(ipv6ext_vlan_tci_msg_with_mask);
+ }
+ if (nwSrcState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_src_ipv6_msg = getIPv6ExtensionSrcIPv6MatchMsg(this.nwSrc
+ .getAddress());
+ data.put(ipv6ext_src_ipv6_msg);
+ } else if (nwSrcState == MatchFieldState.MATCH_FIELD_WITH_MASK) {
+ byte[] ipv6ext_src_ipv6_with_mask_msg = getIPv6ExtensionSrcIPv6MatchwithMaskMsg(
+ this.nwSrc.getAddress(), this.srcIPv6SubnetMaskbits);
+ data.put(ipv6ext_src_ipv6_with_mask_msg);
+ }
+ if (nwDstState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_dst_ipv6_msg = getIPv6ExtensionDstIPv6MatchMsg(this.nwDst
+ .getAddress());
+ data.put(ipv6ext_dst_ipv6_msg);
+ } else if (nwDstState == MatchFieldState.MATCH_FIELD_WITH_MASK) {
+ byte[] ipv6ext_dst_ipv6_with_mask_msg = getIPv6ExtensionDstIPv6MatchwithMaskMsg(
+ this.nwDst.getAddress(), this.dstIPv6SubnetMaskbits);
+ data.put(ipv6ext_dst_ipv6_with_mask_msg);
+ }
+ if (nwProtoState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_protocol_msg = getIPv6ExtensionProtocolMatchMsg(this.networkProtocol);
+ if (ipv6ext_protocol_msg != null) {
+ data.put(ipv6ext_protocol_msg);
+ }
+ }
+ if (nwTosState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_tos_msg = getIPv6ExtensionTOSMatchMsg(this.networkTypeOfService);
+ data.put(ipv6ext_tos_msg);
+ }
+ if (tpSrcState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_srcport_msg = null;
+ if (this.networkProtocol == IPProtocols.TCP.getValue()) {
+ ipv6ext_srcport_msg = getIPv6ExtensionTCPSrcPortMatchMsg(this.transportSource);
+ } else if (this.networkProtocol == IPProtocols.UDP.getValue()) {
+ ipv6ext_srcport_msg = getIPv6ExtensionUDPSrcPortMatchMsg(this.transportSource);
+ }
+ if (ipv6ext_srcport_msg != null) {
+ data.put(ipv6ext_srcport_msg);
+ }
+ }
+ if (tpDstState == MatchFieldState.MATCH_FIELD_ONLY) {
+ byte[] ipv6ext_dstport_msg = null;
+ if (this.networkProtocol == IPProtocols.TCP.getValue()) {
+ ipv6ext_dstport_msg = getIPv6ExtensionTCPDstPortMatchMsg(this.transportDestination);
+ } else if (this.networkProtocol == IPProtocols.UDP.getValue()) {
+ ipv6ext_dstport_msg = getIPv6ExtensionUDPDstPortMatchMsg(this.transportDestination);
+ }
+ if (ipv6ext_dstport_msg != null) {
+ data.put(ipv6ext_dstport_msg);
+ }
+ }
+ logger.trace("{}", this);
+ }
+
+ private void readInPort(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ /*
+ * mask is not allowed for inport port
+ */
+ return;
+ }
+ super.setInputPort(data.getShort());
+ this.inputPortState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.wildcards ^= (1 << 0); // Sync with 0F 1.0 Match
+ this.match_len += 6;
+ }
+
+ private void readDataLinkDestination(ByteBuffer data, int nxmLen,
+ boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 6) || (data.remaining() < 2 * 6)) {
+ return;
+ } else {
+ byte[] bytes = new byte[6];
+ data.get(bytes);
+ super.setDataLayerDestination(bytes);
+ this.dataLayerDestinationMask = new byte[6];
+ data.get(this.dataLayerDestinationMask);
+ this.dlDestState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 16;
+ }
+ } else {
+ if ((nxmLen != 6) || (data.remaining() < 6)) {
+ return;
+ } else {
+ byte[] bytes = new byte[6];
+ data.get(bytes);
+ super.setDataLayerDestination(bytes);
+ this.dlDestState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 10;
+ }
+ }
+ this.wildcards ^= (1 << 3); // Sync with 0F 1.0 Match
+ }
+
+ private void readDataLinkSource(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in data link source
+ */
+ if ((nxmLen != 6) || (data.remaining() < 6) || (hasMask)) {
+ return;
+ }
+ byte[] bytes = new byte[6];
+ data.get(bytes);
+ super.setDataLayerSource(bytes);
+ this.dlSourceState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 10;
+ this.wildcards ^= (1 << 2); // Sync with 0F 1.0 Match
+ }
+
+ private void readEtherType(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in ethertype
+ */
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ return;
+ }
+ super.setDataLayerType(data.getShort());
+ this.ethTypeState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.wildcards ^= (1 << 4); // Sync with 0F 1.0 Match
+ this.match_len += 6;
+ }
+
+ private short getVlanID(byte firstByte, byte secondByte) {
+ short vlan_id_mask_firstByte = 0x0f;// this is the mask for the first
+ // byte
+ short vlan_id_mask_secondByte = 0xff;// this is the mask for the second
+ // byte
+ int vlanPart1 = (firstByte & vlan_id_mask_firstByte) << 8;
+ int vlanPart2 = secondByte & vlan_id_mask_secondByte;
+ return (short) (vlanPart1 + vlanPart2);
+ }
+
+ private byte getVlanPCP(byte pcpByte) {
+ short vlan_pcp_mask = 0xe0;// this is the vlan pcp mask
+ int pcp_int = pcpByte & vlan_pcp_mask;
+ return (byte) (pcp_int >> 5);
+ }
+
+ private void readVlanTci(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 2) || (data.remaining() < 2 * 2)) {
+ return;
+ }
+ else {
+ byte firstByte = data.get();
+ byte secondByte = data.get();
+ this.dataLayerVirtualLanTCIMask = data.getShort() & 0xffff; // we
+ // need
+ // the
+ // last
+ // 16
+ // bits
+ // check the mask now
+ if ((this.dataLayerVirtualLanTCIMask & 0x0fff) != 0) {
+ // if its a vlan id mask
+ // extract the vlan id
+ super.setDataLayerVirtualLan(getVlanID(firstByte,
+ secondByte));
+ } else {
+ this.wildcards ^= (1 << 1); // Sync with 0F 1.0 Match
+ }
+ if ((this.dataLayerVirtualLanTCIMask & 0xe000) != 0) {
+ // else if its a vlan pcp mask
+ // extract the vlan pcp
+ super.setDataLayerVirtualLanPriorityCodePoint(getVlanPCP(firstByte));
+ } else {
+ this.wildcards ^= (1 << 20);
+ }
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 8;
+ }
+ } else {
+ if ((nxmLen != 2) || (data.remaining() < 2)) {
+ return;
+ }
+ else {
+ // get the vlan pcp
+ byte firstByte = data.get();
+ byte secondByte = data.get();
+ super.setDataLayerVirtualLanPriorityCodePoint(getVlanPCP(firstByte));
+ super.setDataLayerVirtualLan(getVlanID(firstByte, secondByte));
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ }
+ }
+ }
+
+ private void readIpTos(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in IP TOS
+ */
+ if ((nxmLen != 1) || (data.remaining() < 1) || (hasMask)) {
+ return;
+ }
+ super.setNetworkTypeOfService(data.get());
+ this.nwTosState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 5;
+ this.wildcards ^= (1 << 21); // Sync with 0F 1.0 Match
+ }
+
+ private void readIpProto(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in IP protocol
+ */
+ if ((nxmLen != 1) || (data.remaining() < 1) || (hasMask)) {
+ return;
+ }
+ super.setNetworkProtocol(data.get());
+ this.nwProtoState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 5;
+ this.wildcards ^= (1 << 5); // Sync with 0F 1.0 Match
+ }
+
+ private void readIpv4Src(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 4) || (data.remaining() < 2 * 4)) {
+ return;
+ } else {
+ byte[] sbytes = new byte[4];
+ data.get(sbytes);
+ // For compatibility, let's set the IPv4 in the parent OFMatch
+ int address = NetUtils.byteArray4ToInt(sbytes);
+ super.setNetworkSource(address);
+ byte[] mbytes = new byte[4];
+ data.get(mbytes);
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 12;
+ int prefixlen = getNetworkMaskPrefixLength(mbytes);
+ this.wildcards ^= (((1 << 6) - 1) << 8); // Sync with 0F 1.0 Match
+ this.wildcards |= ((32 - prefixlen) << 8); // Sync with 0F 1.0 Match
+ }
+ } else {
+ if ((nxmLen != 4) || (data.remaining() < 4)) {
+ return;
+ } else {
+ byte[] sbytes = new byte[4];
+ data.get(sbytes);
+ // For compatibility, let's also set the IPv4 in the parent OFMatch
+ int address = NetUtils.byteArray4ToInt(sbytes);
+ super.setNetworkSource(address);
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 8;
+ this.wildcards ^= (((1 << 6) - 1) << 8); // Sync with 0F 1.0
+ // Match
+ }
+ }
+ }
+
+ private void readIpv4Dst(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 4) || (data.remaining() < 2 * 4)) {
+ return;
+ } else {
+ byte[] dbytes = new byte[4];
+ data.get(dbytes);
+ // For compatibility, let's also set the IPv4 in the parent OFMatch
+ int address = NetUtils.byteArray4ToInt(dbytes);
+ super.setNetworkDestination(address);
+ byte[] mbytes = new byte[4];
+ data.get(mbytes);
+ this.nwDstState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 12;
+ int prefixlen = getNetworkMaskPrefixLength(mbytes);
+ this.wildcards ^= (((1 << 6) - 1) << 14); // Sync with 0F 1.0
+ // Match
+ this.wildcards |= ((32 - prefixlen) << 14); // Sync with 0F 1.0
+ // Match
+ }
+ } else {
+ if ((nxmLen != 4) || (data.remaining() < 4)) {
+ return;
+ } else {
+ byte[] dbytes = new byte[4];
+ data.get(dbytes);
+ int address = NetUtils.byteArray4ToInt(dbytes);
+ super.setNetworkDestination(address);
+ this.nwDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.wildcards ^= (((1 << 6) - 1) << 14); // Sync with 0F 1.0
+ // Match
+ this.match_len += 8;
+ }
+ }
+ }
+
+ private void readTcpSrc(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in TCP SRC
+ */
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ return;
+ }
+ super.setTransportSource(data.getShort());
+ this.tpSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ this.wildcards ^= (1 << 6); // Sync with 0F 1.0 Match
+ }
+
+ private void readTcpDst(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in TCP DST
+ */
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ return;
+ }
+ super.setTransportDestination(data.getShort());
+ this.tpDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ this.wildcards ^= (1 << 7); // Sync with 0F 1.0 Match
+ }
+
+ private void readUdpSrc(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in UDP SRC
+ */
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ return;
+ }
+ super.setTransportSource(data.getShort());
+ this.tpSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ this.wildcards ^= (1 << 6); // Sync with 0F 1.0 Match
+ }
+
+ private void readUdpDst(ByteBuffer data, int nxmLen, boolean hasMask) {
+ /*
+ * mask is not allowed in UDP DST
+ */
+ if ((nxmLen != 2) || (data.remaining() < 2) || (hasMask)) {
+ return;
+ }
+ super.setTransportDestination(data.getShort());
+ this.tpDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ this.wildcards ^= (1 << 7); // Sync with 0F 1.0 Match
+ }
+
+ private void readIpv6Src(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 16) || (data.remaining() < 2 * 16)) {
+ return;
+ } else {
+ byte[] sbytes = new byte[16];
+ data.get(sbytes);
+ try {
+ this.nwSrc = (Inet6Address) InetAddress.getByAddress(sbytes);
+ } catch (UnknownHostException e) {
+ return;
+ }
+ byte[] mbytes = new byte[16];
+ data.get(mbytes);
+ this.srcIPv6SubnetMaskbits = (short)NetUtils.getSubnetMaskLength(mbytes);
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 36;
+ }
+ } else {
+ if ((nxmLen != 16) || (data.remaining() < 16)) {
+ return;
+ } else {
+ byte[] sbytes = new byte[16];
+ data.get(sbytes);
+ try {
+ this.nwSrc = (Inet6Address) InetAddress.getByAddress(sbytes);
+ } catch (UnknownHostException e) {
+ return;
+ }
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 20;
+ }
+ }
+ }
+
+ private void readIpv6Dst(ByteBuffer data, int nxmLen, boolean hasMask) {
+ if (hasMask) {
+ if ((nxmLen != 2 * 16) || (data.remaining() < 2 * 16)) {
+ return;
+ } else {
+ byte[] dbytes = new byte[16];
+ data.get(dbytes);
+ try {
+ this.nwDst = (Inet6Address) InetAddress.getByAddress(dbytes);
+ } catch (UnknownHostException e) {
+ return;
+ }
+ byte[] mbytes = new byte[16];
+ data.get(mbytes);
+ this.dstIPv6SubnetMaskbits = (short)NetUtils.getSubnetMaskLength(mbytes);
+ this.nwDstState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 36;
+ }
+ } else {
+ if ((nxmLen != 16) || (data.remaining() < 16)) {
+ return;
+ } else {
+ byte[] dbytes = new byte[16];
+ data.get(dbytes);
+ try {
+ this.nwDst = (Inet6Address) InetAddress.getByAddress(dbytes);
+ } catch (UnknownHostException e) {
+ return;
+ }
+ this.nwDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 20;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "V6Match [nwSrc=" + nwSrc + ", nwDst=" + nwDst
+ + ", inputPortMask=" + inputPortMask + ", dataLayerSourceMask="
+ + HexEncode.bytesToHexStringFormat(dataLayerSourceMask)
+ + ", dataLayerDestinationMask="
+ + HexEncode.bytesToHexStringFormat(dataLayerDestinationMask)
+ + ", dataLayerVirtualLanTCIMask=" + dataLayerVirtualLanTCIMask
+ + ", dataLayerTypeMask=" + dataLayerTypeMask
+ + ", networkTypeOfServiceMask=" + networkTypeOfServiceMask
+ + ", networkProtocolMask=" + networkProtocolMask
+ + ", transportSourceMask=" + transportSourceMask
+ + ", transportDestinationMask=" + transportDestinationMask
+ + ", srcIPv6SubnetMaskbits=" + srcIPv6SubnetMaskbits
+ + ", dstIPv6SubnetMaskbits=" + dstIPv6SubnetMaskbits
+ + ", inputPortState=" + inputPortState + ", dlSourceState="
+ + dlSourceState + ", dlDestState=" + dlDestState
+ + ", dlVlanTCIState=" + dlVlanTCIState + ", ethTypeState="
+ + ethTypeState + ", nwTosState=" + nwTosState
+ + ", nwProtoState=" + nwProtoState + ", nwSrcState="
+ + nwSrcState + ", nwDstState=" + nwDstState + ", tpSrcState="
+ + tpSrcState + ", tpDstState=" + tpDstState + ", match_len="
+ + match_len + ", pad_size=" + pad_size + "]";
+ }
+
+ /**
+ * Read the data corresponding to the match field (received from the wire)
+ * Input: data: match field(s). Since match field is of variable length, the
+ * whole data that are passed in are assumed to fem0tbd.be the match fields.
+ *
+ * @param data
+ */
+ @Override
+ public void readFrom(ByteBuffer data) {
+ readFromInternal(data);
+ postprocessWildCardInfo();
+ }
+
+ private void readFromInternal(ByteBuffer data) {
+ this.match_len = 0;
+ while (data.remaining() > 0) {
+ if (data.remaining() < 4) {
+ /*
+ * at least 4 bytes for each match header
+ */
+ logger.error("Invalid Vendor Extension Header. Size {}",
+ data.remaining());
+ return;
+ }
+ /*
+ * read the 4 byte match header
+ */
+ int nxmVendor = data.getShort();
+ int b = data.get();
+ int nxmField = b >> 1;
+ boolean hasMask = ((b & 0x01) == 1) ? true : false;
+ int nxmLen = data.get();
+ if (nxmVendor == Extension_Types.OF_10.getValue()) {
+ if (nxmField == OF_Match_Types.MATCH_OF_IN_PORT.getValue()) {
+ readInPort(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_ETH_DST
+ .getValue()) {
+ readDataLinkDestination(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_ETH_SRC
+ .getValue()) {
+ readDataLinkSource(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_ETH_TYPE
+ .getValue()) {
+ readEtherType(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_VLAN_TCI
+ .getValue()) {
+ readVlanTci(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_IP_TOS
+ .getValue()) {
+ readIpTos(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_IP_PROTO
+ .getValue()) {
+ readIpProto(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_IP_SRC
+ .getValue()) {
+ readIpv4Src(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_IP_DST
+ .getValue()) {
+ readIpv4Dst(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_TCP_SRC
+ .getValue()) {
+ readTcpSrc(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_TCP_DST
+ .getValue()) {
+ readTcpDst(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_UDP_SRC
+ .getValue()) {
+ readUdpSrc(data, nxmLen, hasMask);
+ } else if (nxmField == OF_Match_Types.MATCH_OF_UDP_DST
+ .getValue()) {
+ readUdpDst(data, nxmLen, hasMask);
+ } else {
+ // unexpected nxmField
+ return;
+ }
+ } else if (nxmVendor == Extension_Types.IPV6EXT.getValue()) {
+ if (nxmField == IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_SRC
+ .getValue()) {
+ readIpv6Src(data, nxmLen, hasMask);
+ } else if (nxmField == IPv6Extension_Match_Types.MATCH_IPV6EXT_IPV6_DST
+ .getValue()) {
+ readIpv6Dst(data, nxmLen, hasMask);
+ } else {
+ // unexpected nxmField
+ return;
+ }
+ } else {
+ // invalid nxmVendor
+ return;
+ }
+ }
+ }
+
+ private void postprocessWildCardInfo() {
+ // Sync with 0F 1.0 Match
+ if (super.getDataLayerType() == 0x800) {
+ if (((this.wildcards >> 8) & 0x3f) == 0x3f) {
+ // ipv4 src processing
+ this.wildcards ^= (((1 << 5) - 1) << 8);
+ }
+ if (((this.wildcards >> 14) & 0x3f) == 0x3f) {
+ // ipv4 dest processing
+ this.wildcards ^= (((1 << 5) - 1) << 14);
+ }
+ } else {
+ this.wildcards = 0;
+ }
+ }
+
+ @Override
+ public V6Match clone() {
+
+ V6Match ret = (V6Match) super.clone();
+ try {
+ if (this.nwSrc != null) {
+ ret.nwSrc = (Inet6Address) InetAddress.getByAddress(this.nwSrc.getAddress());
+ }
+ if (this.nwDst != null) {
+ ret.nwDst = (Inet6Address) InetAddress.getByAddress(this.nwDst.getAddress());
+ }
+ return ret;
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get nw_dst
+ *
+ * @return
+ */
+
+ public Inet6Address getNetworkDest() {
+ return this.nwDst;
+ }
+
+ /**
+ * Set nw_src
+ *
+ * @return
+ */
+
+ public Inet6Address getNetworkSrc() {
+ return this.nwSrc;
+ }
+
+ private int getNetworkMaskPrefixLength(byte[] netMask) {
+ ByteBuffer nm = ByteBuffer.wrap(netMask);
+ int trailingZeros = Integer.numberOfTrailingZeros(nm.getInt());
+ return 32 - trailingZeros;
+ }
+
+ public short getInputPortMask() {
+ return inputPortMask;
+ }
+
+ public void setInputPort(short port, short mask) {
+ super.inputPort = port;
+ this.inputPortState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 6;
+ // Looks like mask is not allowed for input port. Will discard it
+ }
+
+ public byte[] getDataLayerSourceMask() {
+ return dataLayerSourceMask;
+ }
+
+ public void setDataLayerSource(byte[] mac, byte[] mask) {
+ if (mac != null) {
+ System.arraycopy(mac, 0, super.dataLayerSource, 0, mac.length);
+ }
+ if (mask == null) {
+ this.dlSourceState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 10;
+ } else {
+ if (this.dataLayerSourceMask == null) {
+ this.dataLayerSourceMask = new byte[mask.length];
+ }
+ System.arraycopy(mask, 0, this.dataLayerSourceMask, 0, mask.length);
+ this.dlSourceState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 16;
+ }
+ }
+
+ public byte[] getDataLayerDestinationMask() {
+ return dataLayerDestinationMask;
+ }
+
+ public void setDataLayerDestination(byte[] mac, byte[] mask) {
+ if (mac != null) {
+ System.arraycopy(mac, 0, super.dataLayerDestination, 0, mac.length);
+ }
+ if (mask == null) {
+ this.dlDestState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 10;
+ } else {
+ if (this.dataLayerDestinationMask == null) {
+ this.dataLayerDestinationMask = new byte[mask.length];
+ }
+ System.arraycopy(mask, 0, this.dataLayerDestinationMask, 0,
+ mask.length);
+ this.dlDestState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 16;
+ }
+ }
+
+ public void setDataLayerVirtualLan(short vlan, short mask) {
+ // mask is ignored as the code sets the appropriate mask
+ super.dataLayerVirtualLan = vlan;
+ this.dlVlanIDState = MatchFieldState.MATCH_FIELD_ONLY;
+ // the variable dlVlanIDState is not really used as a flag
+ // for serializing and deserializing. Rather it is used as a flag
+ // to check if the vlan id is being set so that we can set the
+ // dlVlanTCIState appropriately.
+ if (this.dlVlanPCPState != MatchFieldState.MATCH_ABSENT) {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len -= 2;
+ } else {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.dataLayerVirtualLanTCIMask = 0x1fff;
+ match_len += 8;
+ }
+ }
+
+ public void setDataLayerVirtualLanPriorityCodePoint(byte pcp, byte mask) {
+ // mask is ignored as the code sets the appropriate mask
+ super.dataLayerVirtualLanPriorityCodePoint = pcp;
+ this.dlVlanPCPState = MatchFieldState.MATCH_FIELD_ONLY;
+ // the variable dlVlanPCPState is not really used as a flag
+ // for serializing and deserializing. Rather it is used as a flag
+ // to check if the vlan pcp is being set so that we can set the
+ // dlVlanTCIState appropriately.
+ if (this.dlVlanIDState != MatchFieldState.MATCH_ABSENT) {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len -= 2;
+ } else {
+ this.dlVlanTCIState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.dataLayerVirtualLanTCIMask = 0xf000;
+ match_len += 8;
+ }
+ }
+
+ public void setDataLayerType(short ethType, short mask) {
+ // mask not allowed
+ super.dataLayerType = ethType;
+ this.ethTypeState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ }
+
+ public void setNetworkTypeOfService(byte tos, byte mask) {
+ // mask not allowed
+ super.networkTypeOfService = tos;
+ this.nwTosState = MatchFieldState.MATCH_FIELD_ONLY;
+ match_len += 5;
+ }
+
+ public void setNetworkProtocol(byte ipProto, byte mask) {
+ // mask not allowed
+ super.networkProtocol = ipProto;
+ this.nwProtoState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 5;
+ }
+
+ public Inet6Address getNetworkSourceMask() {
+ return (this.nwSrcState == MatchFieldState.MATCH_FIELD_WITH_MASK) ? (Inet6Address) NetUtils.getInetNetworkMask(
+ this.srcIPv6SubnetMaskbits, true) : null;
+ }
+
+ public void setNetworkSource(InetAddress address, InetAddress mask) {
+ if (address instanceof Inet6Address) {
+ this.nwSrc = (Inet6Address) address;
+ if (mask == null) {
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += (address instanceof Inet6Address) ? 20 : 8;
+ } else {
+ this.srcIPv6SubnetMaskbits = (short)NetUtils.getSubnetMaskLength(mask);
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += (address instanceof Inet6Address) ? 36 : 12;
+ }
+ } else {
+ super.setNetworkSource(NetUtils.byteArray4ToInt(address.getAddress()));
+ this.wildcards ^= (((1 << 6) - 1) << 8);
+ if (mask == null) {
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 8;
+ } else {
+ this.nwSrcState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 12;
+ this.wildcards |= ((32 - NetUtils.getSubnetMaskLength(mask)) << 8);
+ }
+ }
+ }
+
+ public Inet6Address getNetworkDestinationMask() {
+ return (this.nwDstState == MatchFieldState.MATCH_FIELD_WITH_MASK) ? (Inet6Address) NetUtils.getInetNetworkMask(
+ this.dstIPv6SubnetMaskbits, true) : null;
+ }
+
+ public void setNetworkDestination(InetAddress address, InetAddress mask) {
+ if (address instanceof Inet6Address) {
+ this.nwDst = (Inet6Address) address;
+ if (mask == null) {
+ this.nwDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += (address instanceof Inet6Address) ? 20 : 8;
+ } else {
+ this.dstIPv6SubnetMaskbits = (short)NetUtils.getSubnetMaskLength(mask);
+ this.nwDstState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += (address instanceof Inet6Address) ? 36 : 12;
+ }
+ } else {
+ this.setNetworkDestination(NetUtils.byteArray4ToInt(address.getAddress()));
+ this.wildcards ^= (((1 << 6) - 1) << 14);
+ if (mask == null) {
+ this.nwDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 8;
+ } else {
+ this.nwDstState = MatchFieldState.MATCH_FIELD_WITH_MASK;
+ this.match_len += 12;
+ this.wildcards |= ((32 - NetUtils.getSubnetMaskLength(mask)) << 14);
+ }
+ }
+ }
+
+ public void setTransportSource(short tpSrc, short mask) {
+ // mask not allowed
+ super.transportSource = tpSrc;
+ this.tpSrcState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ }
+
+ public short getTransportDestinationMask() {
+ return transportDestinationMask;
+ }
+
+ public void setTransportDestination(short tpDst, short mask) {
+ // mask not allowed
+ super.transportDestination = tpDst;
+ this.tpDstState = MatchFieldState.MATCH_FIELD_ONLY;
+ this.match_len += 6;
+ }
+
+ private byte[] getIPv6NetworkMaskinBytes(short num) {
+ byte[] nbytes = new byte[16];
+ int quot = num / 8;
+ int bits = num % 8;
+ int i;
+
+ for (i = 0; i < quot; i++) {
+ nbytes[i] = (byte) 0xff;
+ }
+
+ if (bits > 0) {
+ nbytes[i] = (byte) 0xff;
+ nbytes[i] <<= 8 - bits;
+ }
+ return nbytes;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + Arrays.hashCode(dataLayerDestinationMask);
+ result = prime * result + Arrays.hashCode(dataLayerSourceMask);
+ result = prime * result + dataLayerTypeMask;
+ result = prime * result + dataLayerVirtualLanTCIMask;
+ result = prime * result
+ + ((dlDestState == null) ? 0 : dlDestState.hashCode());
+ result = prime * result
+ + ((dlSourceState == null) ? 0 : dlSourceState.hashCode());
+ result = prime * result
+ + ((dlVlanTCIState == null) ? 0 : dlVlanTCIState.hashCode());
+ result = prime * result + dstIPv6SubnetMaskbits;
+ result = prime * result
+ + ((ethTypeState == null) ? 0 : ethTypeState.hashCode());
+ result = prime * result + inputPortMask;
+ result = prime * result
+ + ((inputPortState == null) ? 0 : inputPortState.hashCode());
+ result = prime * result + match_len;
+ result = prime * result + networkProtocolMask;
+ result = prime * result + networkTypeOfServiceMask;
+ result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
+ result = prime * result
+ + ((nwDstState == null) ? 0 : nwDstState.hashCode());
+ result = prime * result
+ + ((nwProtoState == null) ? 0 : nwProtoState.hashCode());
+ result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
+ result = prime * result
+ + ((nwSrcState == null) ? 0 : nwSrcState.hashCode());
+ result = prime * result
+ + ((nwTosState == null) ? 0 : nwTosState.hashCode());
+ result = prime * result + pad_size;
+ result = prime * result + srcIPv6SubnetMaskbits;
+ result = prime * result
+ + ((tpDstState == null) ? 0 : tpDstState.hashCode());
+ result = prime * result
+ + ((tpSrcState == null) ? 0 : tpSrcState.hashCode());
+ result = prime * result + transportDestinationMask;
+ result = prime * result + transportSourceMask;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ V6Match other = (V6Match) obj;
+ if (!Arrays.equals(dataLayerDestinationMask, other.dataLayerDestinationMask)) {
+ return false;
+ }
+ if (!Arrays.equals(dataLayerSourceMask, other.dataLayerSourceMask)) {
+ return false;
+ }
+ if (dataLayerTypeMask != other.dataLayerTypeMask) {
+ return false;
+ }
+ if (dataLayerVirtualLanTCIMask != other.dataLayerVirtualLanTCIMask) {
+ return false;
+ }
+ if (dlVlanTCIState != other.dlVlanTCIState) {
+ return false;
+ }
+ if (dlSourceState != other.dlSourceState) {
+ return false;
+ }
+ if (dstIPv6SubnetMaskbits != other.dstIPv6SubnetMaskbits) {
+ return false;
+ }
+ if (ethTypeState != other.ethTypeState) {
+ return false;
+ }
+ if (inputPortMask != other.inputPortMask) {
+ return false;
+ }
+ if (inputPortState != other.inputPortState) {
+ return false;
+ }
+ if (match_len != other.match_len) {
+ return false;
+ }
+ if (networkProtocolMask != other.networkProtocolMask) {
+ return false;
+ }
+ if (networkTypeOfServiceMask != other.networkTypeOfServiceMask) {
+ return false;
+ }
+ if (nwDst == null) {
+ if (other.nwDst != null) {
+ return false;
+ }
+ } else if (!nwDst.equals(other.nwDst)) {
+ return false;
+ }
+ if (nwDstState != other.nwDstState) {
+ return false;
+ }
+ if (nwProtoState != other.nwProtoState) {
+ return false;
+ }
+ if (nwSrc == null) {
+ if (other.nwSrc != null) {
+ return false;
+ }
+ } else if (!nwSrc.equals(other.nwSrc)) {
+ return false;
+ }
+ if (nwSrcState != other.nwSrcState) {
+ return false;
+ }
+ if (nwTosState != other.nwTosState) {
+ return false;
+ }
+ if (pad_size != other.pad_size) {
+ return false;
+ }
+ if (srcIPv6SubnetMaskbits != other.srcIPv6SubnetMaskbits) {
+ return false;
+ }
+ if (tpDstState != other.tpDstState) {
+ return false;
+ }
+ if (tpSrcState != other.tpSrcState) {
+ return false;
+ }
+ if (transportDestinationMask != other.transportDestinationMask) {
+ return false;
+ }
+ if (transportSourceMask != other.transportSourceMask) {
+ return false;
+ }
+ return true;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.vendorextension.v6extension;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.statistics.OFVendorStatistics;
+import org.openflow.util.U16;
+
+/**
+ * This Class processes the OpenFlow Vendor Extension Reply message of a Stats
+ * Request. It parses the reply message and initializes fields of V6StatsReply
+ * object. Multiple instances of this class objects are created and used by
+ * OpenDaylight's Troubleshooting Application.
+ *
+ */
+
+public class V6StatsReply extends OFVendorStatistics {
+ private static final long serialVersionUID = 1L;
+
+ public static int MINIMUM_LENGTH = 48; //48 for nx_flow_stats
+
+ protected short length = (short) MINIMUM_LENGTH;
+ protected byte tableId;
+ protected int durationSeconds;
+ protected int durationNanoseconds;
+ protected short priority;
+ protected short idleTimeout;
+ protected short hardTimeout;
+ protected short match_len;
+ protected short idleAge;
+ protected short hardAge;
+ protected long cookie;
+ protected long packetCount;
+ protected long byteCount;
+ protected V6Match match;
+ protected List<OFAction> actions;
+
+ /**
+ * @return vendor id
+ */
+ public int getVendorId() {
+ return vendor;
+ }
+
+ /**
+ * @param vendor the vendor to set
+ */
+ public void setVendorId(int vendor) {
+ this.vendor = vendor;
+ }
+
+ /**
+ * @return the tableId
+ */
+ public byte getTableId() {
+ return tableId;
+ }
+
+ /**
+ * @param tableId the tableId to set
+ */
+ public void setTableId(byte tableId) {
+ this.tableId = tableId;
+ }
+
+ /**
+ * @return the durationSeconds
+ */
+ public int getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ /**
+ * @param durationSeconds the durationSeconds to set
+ */
+ public void setDurationSeconds(int durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ }
+
+ /**
+ * @return the durationNanoseconds
+ */
+ public int getDurationNanoseconds() {
+ return durationNanoseconds;
+ }
+
+ /**
+ * @param durationNanoseconds the durationNanoseconds to set
+ */
+ public void setDurationNanoseconds(int durationNanoseconds) {
+ this.durationNanoseconds = durationNanoseconds;
+ }
+
+ /**
+ * @return the priority
+ */
+ public short getPriority() {
+ return priority;
+ }
+
+ /**
+ * @param priority the priority to set
+ */
+ public void setPriority(short priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * @return the idleTimeout
+ */
+ public short getIdleTimeout() {
+ return idleTimeout;
+ }
+
+ /**
+ * @param idleTimeout the idleTimeout to set
+ */
+ public void setIdleTimeout(short idleTimeout) {
+ this.idleTimeout = idleTimeout;
+ }
+
+ /**
+ * @return the hardTimeout
+ */
+ public short getHardTimeout() {
+ return hardTimeout;
+ }
+
+ /**
+ * @param hardTimeout the hardTimeout to set
+ */
+ public void setHardTimeout(short hardTimeout) {
+ this.hardTimeout = hardTimeout;
+ }
+
+ /**
+ * @param match_len the match_len to set
+ */
+ public void setMatchLen(short match_len) {
+ this.match_len = match_len;
+ }
+
+ /**
+ * @return the match_len
+ */
+ public short getMatchLen() {
+ return match_len;
+ }
+
+ /**
+ * @return the idleAge
+ */
+ public short getIdleAge() {
+ return idleAge;
+ }
+
+ /**
+ * @param idleAge the idleAge to set
+ */
+ public void setIdleAge(short idleAge) {
+ this.idleAge = idleAge;
+ }
+
+ /**
+ * @return the hardAge
+ */
+ public short getHardAge() {
+ return hardAge;
+ }
+
+ /**
+ * @param hardAge the hardAge to set
+ */
+ public void setHardAge(short hardAge) {
+ this.hardAge = hardAge;
+ }
+
+ /**
+ * @return the cookie
+ */
+ public long getCookie() {
+ return cookie;
+ }
+
+ /**
+ * @param cookie the cookie to set
+ */
+ public void setCookie(long cookie) {
+ this.cookie = cookie;
+ }
+
+ /**
+ * @return the packetCount
+ */
+ public long getPacketCount() {
+ return packetCount;
+ }
+
+ /**
+ * @param packetCount the packetCount to set
+ */
+ public void setPacketCount(long packetCount) {
+ this.packetCount = packetCount;
+ }
+
+ /**
+ * @return the byteCount
+ */
+ public long getByteCount() {
+ return byteCount;
+ }
+
+ /**
+ * @param byteCount the byteCount to set
+ */
+ public void setByteCount(long byteCount) {
+ this.byteCount = byteCount;
+ }
+
+ /**
+ * @param length the length to set
+ */
+ public void setLength(short length) {
+ this.length = length;
+ }
+
+ @Override
+ public int getLength() {
+ return U16.f(length);
+ }
+
+ /**
+ * @return the match
+ */
+ public V6Match getMatch() {
+ return match;
+ }
+
+ /**
+ * @return the actions
+ */
+ public List<OFAction> getActions() {
+ return actions;
+ }
+
+ /**
+ * @param actions the actions to set
+ */
+ public void setActions(List<OFAction> actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void readFrom(ByteBuffer data) {
+ short i;
+ this.length = data.getShort();
+ if (length < MINIMUM_LENGTH)
+ return; //TBD - Spurious Packet?
+ this.tableId = data.get();
+ data.get(); // pad
+ this.durationSeconds = data.getInt();
+ this.durationNanoseconds = data.getInt();
+ this.priority = data.getShort();
+ this.idleTimeout = data.getShort();
+ this.hardTimeout = data.getShort();
+ this.match_len = data.getShort();
+ this.idleAge = data.getShort();
+ this.hardAge = data.getShort();
+ this.cookie = data.getLong();
+ this.packetCount = data.getLong();
+ this.byteCount = data.getLong();
+ if (this.length == MINIMUM_LENGTH) {
+ return; //TBD - can this happen??
+ }
+ if (this.match == null)
+ this.match = new V6Match();
+ ByteBuffer mbuf = ByteBuffer.allocate(match_len);
+ for (i = 0; i < match_len; i++) {
+ mbuf.put(data.get());
+ }
+ mbuf.rewind();
+ this.match.readFrom(mbuf);
+ if (this.actionFactory == null)
+ throw new RuntimeException("OFActionFactory not set");
+ /*
+ * action list may be preceded by a padding of 0 to 7 bytes based upon this:
+ */
+ short pad_size = (short) (((match_len + 7) / 8) * 8 - match_len);
+ for (i = 0; i < pad_size; i++)
+ data.get();
+ int action_len = this.length - MINIMUM_LENGTH - (match_len + pad_size);
+ if (action_len > 0)
+ this.actions = this.actionFactory.parseActions(data, action_len);
+ }
+
+ @Override
+ public void writeTo(ByteBuffer data) {
+ super.writeTo(data);//TBD. This Fn needs work. Should never get called though.
+
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((actions == null) ? 0 : actions.hashCode());
+ result = prime * result + (int) (byteCount ^ (byteCount >>> 32));
+ result = prime * result + (int) (cookie ^ (cookie >>> 32));
+ result = prime * result + durationNanoseconds;
+ result = prime * result + durationSeconds;
+ result = prime * result + hardAge;
+ result = prime * result + hardTimeout;
+ result = prime * result + idleAge;
+ result = prime * result + idleTimeout;
+ result = prime * result + length;
+ result = prime * result + ((match == null) ? 0 : match.hashCode());
+ result = prime * result + match_len;
+ result = prime * result + (int) (packetCount ^ (packetCount >>> 32));
+ result = prime * result + priority;
+ result = prime * result + tableId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "V6StatsReply [length=" + length + ", tableId=" + tableId
+ + ", durationSeconds=" + durationSeconds
+ + ", durationNanoseconds=" + durationNanoseconds
+ + ", priority=" + priority + ", idleTimeout=" + idleTimeout
+ + ", hardTimeout=" + hardTimeout + ", match_len=" + match_len
+ + ", idleAge=" + idleAge + ", hardAge=" + hardAge + ", cookie="
+ + cookie + ", packetCount=" + packetCount + ", byteCount="
+ + byteCount + ", match=" + match + ", actions=" + actions + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ V6StatsReply other = (V6StatsReply) obj;
+ if (actions == null) {
+ if (other.actions != null)
+ return false;
+ } else if (!actions.equals(other.actions))
+ return false;
+ if (byteCount != other.byteCount)
+ return false;
+ if (cookie != other.cookie)
+ return false;
+ if (durationNanoseconds != other.durationNanoseconds)
+ return false;
+ if (durationSeconds != other.durationSeconds)
+ return false;
+ if (hardAge != other.hardAge)
+ return false;
+ if (hardTimeout != other.hardTimeout)
+ return false;
+ if (idleAge != other.idleAge)
+ return false;
+ if (idleTimeout != other.idleTimeout)
+ return false;
+ if (length != other.length)
+ return false;
+ if (match == null) {
+ if (other.match != null)
+ return false;
+ } else if (!match.equals(other.match))
+ return false;
+ if (match_len != other.match_len)
+ return false;
+ if (packetCount != other.packetCount)
+ return false;
+ if (priority != other.priority)
+ return false;
+ if (tableId != other.tableId)
+ return false;
+ return true;
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowplugin.openflow.vendorextension.v6extension;
+
+import java.nio.ByteBuffer;
+
+import org.openflow.protocol.statistics.OFVendorStatistics;
+
+
+/**
+ * This Class creates the OpenFlow Vendor Extension IPv6 Flow Stats Request
+ * messages and also reads the Reply of a stats request message.
+ *
+ */
+
+public class V6StatsRequest extends OFVendorStatistics {
+ private static final long serialVersionUID = 1L;
+ protected int msgsubtype;
+ protected short outPort;
+ protected short match_len;
+ protected byte tableId;
+
+ public static final int NICIRA_VENDOR_ID = 0x00002320; //Nicira ID
+ private static final int NXST_FLOW = 0x0; //Nicira Flow Stats Request Id
+
+ public V6StatsRequest() {
+ this.vendor = NICIRA_VENDOR_ID;
+ this.msgsubtype = NXST_FLOW;
+ this.match_len = 0;
+ }
+
+ /**
+ * @param None. Being set with local variable (TBD).
+ */
+ public void setVendorId() {
+ this.vendor = NICIRA_VENDOR_ID;
+ }
+
+ /**
+ * @return vendor id
+ */
+ public int getVendorId() {
+ return vendor;
+ }
+
+ /**
+ * @param None. Being set with local variable (TBD).
+ */
+ public void setMsgtype() {
+ this.msgsubtype = NXST_FLOW;
+ }
+
+ /**
+ * @return vendor_msgtype
+ */
+ public int getMsgtype() {
+ return msgsubtype;
+ }
+
+ /**
+ * @param outPort the outPort to set
+ */
+ public void setOutPort(short outPort) {
+ this.outPort = outPort;
+ }
+
+ /**
+ * @return the outPort
+ */
+ public short getOutPort() {
+ return outPort;
+ }
+
+ /**
+ * @param match_len the match_len to set
+ */
+ public void setMatchLen(short match_len) {
+ this.match_len = match_len;
+ }
+
+ /**
+ * @return the match_len
+ */
+ public short getMatchLen() {
+ return match_len;
+ }
+
+ /**
+ * @param tableId the tableId to set
+ */
+ public void setTableId(byte tableId) {
+ this.tableId = tableId;
+ }
+
+ /**
+ * @return the tableId
+ */
+ public byte getTableId() {
+ return tableId;
+ }
+
+ @Override
+ public int getLength() {
+ return 20;// 4(vendor)+4(msgsubtype)+4(pad)+2(outPort)+2(match_len)+1(tableid)+3(pad)
+ }
+
+ @Override
+ public void readFrom(ByteBuffer data) {
+ this.vendor = data.getInt();
+ this.msgsubtype = data.getInt();
+ data.getInt();//pad 4 bytes
+ this.outPort = data.getShort();
+ this.match_len = data.getShort();
+ this.tableId = data.get();
+ for (int i = 0; i < 3; i++)
+ data.get();//pad byte
+
+ }
+
+ @Override
+ public void writeTo(ByteBuffer data) {
+ data.putInt(this.vendor);
+ data.putInt(this.msgsubtype);
+ data.putInt((int) 0x0);//pad0
+ data.putShort(this.outPort);
+ data.putShort(this.match_len);
+ data.put(this.tableId);
+ for (int i = 0; i < 3; i++)
+ data.put((byte) 0x0);//pad byte
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + match_len;
+ result = prime * result + msgsubtype;
+ result = prime * result + outPort;
+ result = prime * result + tableId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "V6StatsRequest [msgsubtype=" + msgsubtype + ", outPort="
+ + outPort + ", match_len=" + match_len + ", tableId=" + tableId
+ + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ V6StatsRequest other = (V6StatsRequest) obj;
+ if (match_len != other.match_len)
+ return false;
+ if (msgsubtype != other.msgsubtype)
+ return false;
+ if (outPort != other.outPort)
+ return false;
+ if (tableId != other.tableId)
+ return false;
+ return true;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.protocol_plugin.openflow.internal;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.action.OFAction;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
+import org.opendaylight.controller.sal.action.HwPath;
+import org.opendaylight.controller.sal.action.Loopback;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.SetDlDst;
+import org.opendaylight.controller.sal.action.SetDlSrc;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
+import org.opendaylight.controller.sal.action.SetNwTos;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.action.SetVlanId;
+import org.opendaylight.controller.sal.action.SwPath;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.openflowplugin.openflow.internal.FlowConverter;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6Match;
+
+public class FlowProgrammerServiceTest {
+
+ @Test
+ public void testSALtoOFFlowConverter() throws UnknownHostException {
+ Node node = NodeCreator.createOFNode(1000l);
+ NodeConnector port = NodeConnectorCreator.createNodeConnector(
+ (short) 24, node);
+ NodeConnector oport = NodeConnectorCreator.createNodeConnector(
+ (short) 30, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+ (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+ (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress.getByName("172.28.30.50");
+ InetAddress dstIP = InetAddress.getByName("171.71.9.52");
+ InetAddress ipMask = InetAddress.getByName("255.255.255.0");
+ short ethertype = EtherTypes.IPv4.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.TCP.byteValue();
+ short src = (short) 55000;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(MatchType.IN_PORT, port);
+ match.setField(MatchType.DL_SRC, srcMac);
+ match.setField(MatchType.DL_DST, dstMac);
+ match.setField(MatchType.DL_TYPE, ethertype);
+ match.setField(MatchType.DL_VLAN, vlan);
+ match.setField(MatchType.DL_VLAN_PR, vlanPr);
+ match.setField(MatchType.NW_SRC, srcIP, ipMask);
+ match.setField(MatchType.NW_DST, dstIP, ipMask);
+ match.setField(MatchType.NW_TOS, tos);
+ match.setField(MatchType.NW_PROTO, proto);
+ match.setField(MatchType.TP_SRC, src);
+ match.setField(MatchType.TP_DST, dst);
+
+ Assert.assertTrue(match.isIPv4());
+
+ List<Action> actions = new ArrayList<Action>();
+ // Setting all the actions supported by of
+ actions.add(new PopVlan());
+ actions.add(new Output(oport));
+ actions.add(new Flood());
+ actions.add(new FloodAll());
+ actions.add(new SwPath());
+ actions.add(new HwPath());
+ actions.add(new Loopback());
+ byte mac[] = { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5 };
+ actions.add(new SetDlSrc(mac));
+ actions.add(new SetDlDst(mac));
+ actions.add(new SetNwSrc(dstIP));
+ actions.add(new SetNwDst(srcIP));
+ actions.add(new SetNwTos(3));
+ actions.add(new SetTpSrc(10));
+ actions.add(new SetTpDst(20));
+ actions.add(new SetVlanId(200));
+
+ Flow aFlow = new Flow(match, actions);
+
+ /*
+ * Convert the SAL aFlow to OF Flow
+ */
+ FlowConverter salToOF = new FlowConverter(aFlow);
+ OFMatch ofMatch = salToOF.getOFMatch();
+ List<OFAction> ofActions = salToOF.getOFActions();
+
+ /*
+ * Convert the OF Flow to SAL Flow bFlow
+ */
+ FlowConverter ofToSal = new FlowConverter(ofMatch, ofActions);
+ Flow bFlow = ofToSal.getFlow(node);
+ Match bMatch = bFlow.getMatch();
+ List<Action> bActions = bFlow.getActions();
+
+ /*
+ * Verify the converted SAL flow bFlow is equivalent to the original SAL Flow
+ */
+ Assert.assertTrue(((NodeConnector) match.getField(MatchType.IN_PORT)
+ .getValue()).equals(((NodeConnector) bMatch.getField(
+ MatchType.IN_PORT).getValue())));
+ Assert.assertTrue(Arrays.equals((byte[]) match.getField(
+ MatchType.DL_SRC).getValue(), (byte[]) bMatch.getField(
+ MatchType.DL_SRC).getValue()));
+ Assert.assertTrue(Arrays.equals((byte[]) match.getField(
+ MatchType.DL_DST).getValue(), (byte[]) bMatch.getField(
+ MatchType.DL_DST).getValue()));
+ Assert
+ .assertTrue(((Short) match.getField(MatchType.DL_TYPE)
+ .getValue()).equals((Short) bMatch.getField(
+ MatchType.DL_TYPE).getValue()));
+ Assert
+ .assertTrue(((Short) match.getField(MatchType.DL_VLAN)
+ .getValue()).equals((Short) bMatch.getField(
+ MatchType.DL_VLAN).getValue()));
+ Assert.assertTrue(((Byte) match.getField(MatchType.DL_VLAN_PR)
+ .getValue()).equals((Byte) bMatch
+ .getField(MatchType.DL_VLAN_PR).getValue()));
+ Assert.assertTrue(((InetAddress) match.getField(MatchType.NW_SRC)
+ .getValue()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_SRC).getValue()));
+ Assert.assertTrue(((InetAddress) match.getField(MatchType.NW_SRC)
+ .getMask()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_SRC).getMask()));
+ Assert.assertTrue(((InetAddress) match.getField(MatchType.NW_DST)
+ .getValue()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_DST).getValue()));
+ Assert.assertTrue(((InetAddress) match.getField(MatchType.NW_DST)
+ .getMask()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_DST).getMask()));
+ Assert
+ .assertTrue(((Byte) match.getField(MatchType.NW_PROTO)
+ .getValue()).equals((Byte) bMatch.getField(
+ MatchType.NW_PROTO).getValue()));
+ Assert.assertTrue(((Byte) match.getField(MatchType.NW_TOS).getValue())
+ .equals((Byte) bMatch.getField(MatchType.NW_TOS).getValue()));
+ Assert.assertTrue(((Short) match.getField(MatchType.TP_SRC).getValue())
+ .equals((Short) bMatch.getField(MatchType.TP_SRC).getValue()));
+ Assert.assertTrue(((Short) match.getField(MatchType.TP_DST).getValue())
+ .equals((Short) bMatch.getField(MatchType.TP_DST).getValue()));
+
+ // FlowConverter parses and sets the actions in the same order for sal match and of match
+ for (short i = 0; i < actions.size(); i++) {
+ Assert.assertTrue(actions.get(i).equals(bActions.get(i)));
+ }
+ }
+
+ @Test
+ public void testVlanNoneIdFlowConversion() throws Exception {
+ Node node = NodeCreator.createOFNode(1000l);
+
+ /*
+ * The value 0 is used to indicate that no VLAN ID is set
+ * for SAL Flow.
+ */
+ short vlan = (short) 0;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(MatchType.DL_VLAN, vlan);
+
+ List<Action> actions = new ArrayList<Action>();
+
+ Flow aFlow = new Flow(match, actions);
+
+ /*
+ * Convert the SAL aFlow to OF Flow
+ */
+ FlowConverter salToOF = new FlowConverter(aFlow);
+ OFMatch ofMatch = salToOF.getOFMatch();
+ List<OFAction> ofActions = salToOF.getOFActions();
+
+ /*
+ * The value 0xffff (OFP_VLAN_NONE) is used to indicate
+ * that no VLAN ID is set for OF Flow.
+ */
+ Assert.assertEquals((short) 0xffff, ofMatch.getDataLayerVirtualLan());
+
+ /*
+ * Convert the OF Flow to SAL Flow bFlow
+ */
+ FlowConverter ofToSal = new FlowConverter(ofMatch, ofActions);
+ Flow bFlow = ofToSal.getFlow(node);
+ Match bMatch = bFlow.getMatch();
+
+ /*
+ * Verify the converted SAL flow bFlow is equivalent to the original SAL Flow
+ */
+ Assert
+ .assertTrue(((Short) match.getField(MatchType.DL_VLAN)
+ .getValue()).equals((Short) bMatch.getField(
+ MatchType.DL_VLAN).getValue()));
+ }
+
+ @Test
+ public void testV6toSALFlowConversion() throws Exception {
+ Node node = NodeCreator.createOFNode(12l);
+ NodeConnector port = NodeConnectorCreator.createNodeConnector(
+ (short) 34, node);
+ NodeConnector oport = NodeConnectorCreator.createNodeConnector(
+ (short) 30, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+ (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+ (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress
+ .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress
+ .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMask = InetAddress
+ .getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.TCP.byteValue();
+ short src = (short) 55000;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match aMatch = new Match();
+
+ aMatch.setField(MatchType.IN_PORT, port);
+ aMatch.setField(MatchType.DL_SRC, srcMac);
+ aMatch.setField(MatchType.DL_DST, dstMac);
+ aMatch.setField(MatchType.DL_TYPE, ethertype);
+ aMatch.setField(MatchType.DL_VLAN, vlan);
+ aMatch.setField(MatchType.DL_VLAN_PR, vlanPr);
+ aMatch.setField(MatchType.NW_SRC, srcIP, ipMask);
+ aMatch.setField(MatchType.NW_DST, dstIP, ipMask);
+ aMatch.setField(MatchType.NW_TOS, tos);
+ aMatch.setField(MatchType.NW_PROTO, proto);
+ aMatch.setField(MatchType.TP_SRC, src);
+ aMatch.setField(MatchType.TP_DST, dst);
+
+ Assert.assertTrue(aMatch.isIPv6());
+
+ List<Action> actions = new ArrayList<Action>();
+ // Setting all the actions supported by of for v6
+ actions.add(new PopVlan());
+ actions.add(new Output(oport));
+ actions.add(new Flood());
+ actions.add(new FloodAll());
+ actions.add(new SwPath());
+ actions.add(new HwPath());
+ actions.add(new Loopback());
+ byte mac[] = { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5 };
+ actions.add(new SetDlSrc(mac));
+ actions.add(new SetDlDst(mac));
+ //actions.add(new SetNwSrc(dstIP)); Nicira extensions do not provide IPv6 match addresses change
+ //actions.add(new SetNwDst(srcIP));
+ actions.add(new SetNwTos(3));
+ actions.add(new SetTpSrc(10));
+ actions.add(new SetTpDst(65535));
+ actions.add(new SetVlanId(200));
+
+ Flow aFlow = new Flow(aMatch, actions);
+
+ /*
+ * Convert the SAL aFlow to OF Flow
+ */
+ FlowConverter salToOF = new FlowConverter(aFlow);
+ V6Match v6Match = (V6Match) salToOF.getOFMatch();
+ List<OFAction> ofActions = salToOF.getOFActions();
+
+ /*
+ * Convert the OF Flow to SAL Flow bFlow
+ */
+ FlowConverter ofToSal = new FlowConverter(v6Match, ofActions);
+ Flow bFlow = ofToSal.getFlow(node);
+ Match bMatch = bFlow.getMatch();
+ List<Action> bActions = bFlow.getActions();
+
+ /*
+ * Verify the converted SAL flow bFlow is equivalent to the original SAL Flow
+ */
+ Assert.assertTrue(((NodeConnector) aMatch.getField(MatchType.IN_PORT)
+ .getValue()).equals(((NodeConnector) bMatch.getField(
+ MatchType.IN_PORT).getValue())));
+ Assert.assertTrue(Arrays.equals((byte[]) aMatch.getField(
+ MatchType.DL_SRC).getValue(), (byte[]) bMatch.getField(
+ MatchType.DL_SRC).getValue()));
+ Assert.assertTrue(Arrays.equals((byte[]) aMatch.getField(
+ MatchType.DL_DST).getValue(), (byte[]) bMatch.getField(
+ MatchType.DL_DST).getValue()));
+ Assert.assertTrue(((Short) aMatch.getField(MatchType.DL_TYPE)
+ .getValue()).equals((Short) bMatch.getField(MatchType.DL_TYPE)
+ .getValue()));
+ Assert.assertTrue(((Short) aMatch.getField(MatchType.DL_VLAN)
+ .getValue()).equals((Short) bMatch.getField(MatchType.DL_VLAN)
+ .getValue()));
+ Assert.assertTrue(((Byte) aMatch.getField(MatchType.DL_VLAN_PR)
+ .getValue()).equals((Byte) bMatch
+ .getField(MatchType.DL_VLAN_PR).getValue()));
+ Assert.assertTrue(((InetAddress) aMatch.getField(MatchType.NW_SRC)
+ .getValue()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_SRC).getValue()));
+ Assert.assertTrue(((InetAddress) aMatch.getField(MatchType.NW_SRC)
+ .getMask()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_SRC).getMask()));
+ Assert.assertTrue(((InetAddress) aMatch.getField(MatchType.NW_DST)
+ .getValue()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_DST).getValue()));
+ Assert.assertTrue(((InetAddress) aMatch.getField(MatchType.NW_DST)
+ .getMask()).equals((InetAddress) bMatch.getField(
+ MatchType.NW_DST).getMask()));
+ Assert.assertTrue(((Byte) aMatch.getField(MatchType.NW_PROTO)
+ .getValue()).equals((Byte) bMatch.getField(MatchType.NW_PROTO)
+ .getValue()));
+ Assert.assertTrue(((Byte) aMatch.getField(MatchType.NW_TOS).getValue())
+ .equals((Byte) bMatch.getField(MatchType.NW_TOS).getValue()));
+ Assert
+ .assertTrue(((Short) aMatch.getField(MatchType.TP_SRC)
+ .getValue()).equals((Short) bMatch.getField(
+ MatchType.TP_SRC).getValue()));
+ Assert
+ .assertTrue(((Short) aMatch.getField(MatchType.TP_DST)
+ .getValue()).equals((Short) bMatch.getField(
+ MatchType.TP_DST).getValue()));
+
+ // FlowConverter parses and sets the actions in the same order for sal match and of match
+ for (short i = 0; i < actions.size(); i++) {
+ Assert.assertTrue(actions.get(i).equals(bActions.get(i)));
+ }
+ }
+
+ @Test
+ public void testV6MatchToSALMatchToV6MatchConversion()
+ throws UnknownHostException {
+ NodeConnector port = NodeConnectorCreator.createNodeConnector(
+ (short) 24, NodeCreator.createOFNode(6l));
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+ (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+ (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress
+ .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress
+ .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMask = null;//InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.TCP.byteValue();
+ short src = (short) 55000;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match aMatch = new Match();
+
+ aMatch.setField(MatchType.IN_PORT, port);
+ aMatch.setField(MatchType.DL_SRC, srcMac);
+ aMatch.setField(MatchType.DL_DST, dstMac);
+ aMatch.setField(MatchType.DL_TYPE, ethertype);
+ aMatch.setField(MatchType.DL_VLAN, vlan);
+ aMatch.setField(MatchType.DL_VLAN_PR, vlanPr);
+ aMatch.setField(MatchType.NW_SRC, srcIP, ipMask);
+ aMatch.setField(MatchType.NW_DST, dstIP, ipMask);
+ aMatch.setField(MatchType.NW_TOS, tos);
+ aMatch.setField(MatchType.NW_PROTO, proto);
+ aMatch.setField(MatchType.TP_SRC, src);
+ aMatch.setField(MatchType.TP_DST, dst);
+
+ Assert.assertTrue(aMatch.isIPv6());
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension;
+
+import static org.junit.Assert.fail;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.openflowplugin.openflow.vendorextension.v6extension.V6Match;
+import org.openflow.protocol.OFMatch;
+
+public class V6ExtensionTest {
+
+ @Test
+ public void testFromString() throws UnknownHostException {
+ // This tests creating V6Match using fromString and OFMatch by comparing
+ // the results to each other
+ V6Match match = new V6Match();
+ V6Match match2 = new V6Match();
+
+ OFMatch ofm = new OFMatch();
+ V6Match match4 = new V6Match(ofm);
+
+ match.fromString("");
+ Assert.assertTrue(match.equals(match2));
+ match.fromString("any");
+ Assert.assertTrue(match.equals(match2));
+ Assert.assertTrue(match.equals(match4));
+ try {
+ match.fromString("invalidArgument");
+
+ fail("Did not throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // passed test for throwing exception.
+ }
+ try {
+ match.fromString("invalidParameter=abcdefg");
+ fail("Did not throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // passed test for throwing exception.
+ }
+
+ match.fromString("input_port=1");
+ match.fromString("dl_dst=20:A0:11:10:00:99");
+ match.fromString("dl_src=00:10:08:22:12:75");
+
+ match.fromString("ip_src=10.1.1.1");
+ match.fromString("ip_dst=1.2.3.4");
+ match.fromString("eth_type=0x800");
+ match.fromString("dl_vlan=10");
+ match.fromString("dl_vpcp=1");
+ match.fromString("nw_proto=6");
+ match.fromString("nw_tos=100");
+ match.fromString("tp_dst=8080");
+ match.fromString("tp_src=60");
+
+ Assert.assertTrue(match.getInputPort() == 1);
+ // Assert.assertTrue(match.getIPv6MatchLen()==6);
+
+ ofm.setInputPort((short) 1);
+ // V6Match is meant for IPv6, but if using OFMatch, it will be set to
+ // IPv4 values, as OF1.0 doesn't support IPv6.
+ InetAddress addr = InetAddress.getByName("10.1.1.1");
+ int ipsrc = ByteBuffer.wrap(addr.getAddress()).getInt();
+ ofm.setNetworkSource(ipsrc);
+
+ addr = InetAddress.getByName("1.2.3.4");
+ int ipdst = ByteBuffer.wrap(addr.getAddress()).getInt();
+ ofm.setNetworkDestination(ipdst);
+
+ byte[] macSrc = { 0x00, 0x10, 0x08, 0x22, 0x12, 0x75 };
+ ofm.setDataLayerSource(macSrc);
+ byte[] macDst = { 0x20, (byte) 0xA0, 0x11, 0x10, 0x00, (byte) 0x99 };
+ ofm.setDataLayerDestination(macDst);
+ ofm.setDataLayerType((short) 0x800);
+ ofm.setDataLayerVirtualLan((short) 10);
+ ofm.setDataLayerVirtualLanPriorityCodePoint((byte) 1);
+ ofm.setNetworkProtocol((byte) 6);
+ ofm.setNetworkTypeOfService((byte) 100);
+ ofm.setTransportSource((short) 60);
+ ofm.setTransportDestination((short) 8080);
+
+ V6Match match3 = new V6Match(ofm);
+
+ Assert.assertTrue(match.getInputPort() == match3.getInputPort());
+ Assert.assertTrue(Arrays.equals(match.getDataLayerSource(),
+ match3.getDataLayerSource()));
+ Assert.assertTrue(Arrays.equals(match.getDataLayerDestination(),
+ match3.getDataLayerDestination()));
+ Assert.assertNull(match.getNetworkSrc());
+ Assert.assertNull(match3.getNetworkSrc());
+ Assert.assertNull(match.getNetworkDest());
+ Assert.assertNull(match3.getNetworkDest());
+ Assert.assertTrue(match.getDataLayerVirtualLan() == match3
+ .getDataLayerVirtualLan());
+ Assert.assertTrue(match.getDataLayerVirtualLanPriorityCodePoint() == match3
+ .getDataLayerVirtualLanPriorityCodePoint());
+ Assert.assertTrue(match.getNetworkProtocol() == match3
+ .getNetworkProtocol());
+ Assert.assertTrue(match.getNetworkTypeOfService() == match3
+ .getNetworkTypeOfService());
+ Assert.assertTrue(match.getTransportSource() == match3
+ .getTransportSource());
+ Assert.assertTrue(match.getTransportDestination() == match3
+ .getTransportDestination());
+ Assert.assertTrue(match.getWildcards() == match3.getWildcards());
+
+ }
+
+ @Test
+ public void testReadWriteBuffer() {
+ V6Match match = new V6Match();
+ match.fromString("input_port=1");
+ match.fromString("dl_dst=20:A0:11:10:00:99");
+ match.fromString("dl_src=00:10:08:22:12:75");
+ // writeTo(ByteBuffer) will only write IPv6
+ match.fromString("ip_src=2001:ddd:3e1:1234:0000:1111:2222:3333/64");
+ match.fromString("ip_dst=2001:123:222:abc:111:aaa:1111:2222/64");
+ match.fromString("dl_vlan=10");
+ match.fromString("dl_vpcp=1");
+ match.fromString("nw_proto=6");
+ match.fromString("nw_tos=100");
+ match.fromString("tp_dst=8080");
+ match.fromString("tp_src=60");
+ match.fromString("dl_type=0x800");
+
+ ByteBuffer data = ByteBuffer.allocateDirect(10000);
+ match.writeTo(data);
+ data.flip();
+ V6Match match2 = new V6Match();
+ match2.readFrom(data);
+ Assert.assertTrue(match.getInputPort() == match2.getInputPort());
+ Assert.assertTrue(Arrays.equals(match.getDataLayerSource(),
+ match2.getDataLayerSource()));
+ Assert.assertTrue(Arrays.equals(match.getDataLayerDestination(),
+ match2.getDataLayerDestination()));
+
+ Assert.assertTrue(match.getNetworkSrc().equals(match2.getNetworkSrc()));
+ Assert.assertTrue(match.getNetworkDest()
+ .equals(match2.getNetworkDest()));
+
+ Assert.assertTrue(match.getDataLayerVirtualLan() == match2
+ .getDataLayerVirtualLan());
+ Assert.assertTrue(match.getDataLayerVirtualLanPriorityCodePoint() == match2
+ .getDataLayerVirtualLanPriorityCodePoint());
+ Assert.assertTrue(match.getNetworkProtocol() == match2
+ .getNetworkProtocol());
+ Assert.assertTrue(match.getNetworkTypeOfService() == match2
+ .getNetworkTypeOfService());
+ Assert.assertTrue(match.getTransportSource() == match2
+ .getTransportSource());
+ Assert.assertTrue(match.getTransportDestination() == match2
+ .getTransportDestination());
+
+ }
+
+ @Test
+ public void testClone() {
+ V6Match match = new V6Match();
+ match.fromString("input_port=1");
+ match.fromString("dl_dst=20:A0:11:10:00:99");
+ match.fromString("dl_src=00:10:08:22:12:75");
+ match.fromString("ip_src=2001:ddd:3e1:1234:0000:1111:2222:3333/64");
+ match.fromString("ip_dst=2001:123:222:abc:111:aaa:1111:2222/64");
+ match.fromString("dl_vlan=10");
+ match.fromString("dl_vpcp=1");
+ match.fromString("nw_proto=6");
+ match.fromString("nw_tos=100");
+ match.fromString("tp_dst=8080");
+ match.fromString("tp_src=60");
+ match.fromString("dl_type=0x800");
+
+ V6Match match2 = match.clone();
+ Assert.assertTrue(match.getInputPort() == match2.getInputPort());
+ Assert.assertTrue(Arrays.equals(match.getDataLayerSource(),
+ match2.getDataLayerSource()));
+ Assert.assertTrue(Arrays.equals(match.getDataLayerDestination(),
+ match2.getDataLayerDestination()));
+ Assert.assertTrue(match.getNetworkSrc().equals(match2.getNetworkSrc()));
+ Assert.assertTrue(match.getNetworkDest()
+ .equals(match2.getNetworkDest()));
+ Assert.assertTrue(match.getDataLayerVirtualLan() == match2
+ .getDataLayerVirtualLan());
+ Assert.assertTrue(match.getDataLayerVirtualLanPriorityCodePoint() == match2
+ .getDataLayerVirtualLanPriorityCodePoint());
+ Assert.assertTrue(match.getNetworkProtocol() == match2
+ .getNetworkProtocol());
+ Assert.assertTrue(match.getNetworkTypeOfService() == match2
+ .getNetworkTypeOfService());
+ Assert.assertTrue(match.getTransportSource() == match2
+ .getTransportSource());
+ Assert.assertTrue(match.getTransportDestination() == match2
+ .getTransportDestination());
+ Assert.assertTrue(match.getWildcards() == match2.getWildcards());
+ }
+
+ @Test
+ public void testPadding() {
+ // testing that matchlen+pad keeps the 8byte alignment
+ V6Match match = new V6Match();
+
+ match.fromString("input_port=1");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("dl_dst=20:A0:11:10:00:99");
+ match.fromString("dl_src=00:10:08:22:12:75");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("ip_src=2001:ddd:3e1:1234:0000:1111:2222:3333");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("ip_dst=2001:123:222:abc:111:aaa:1111:2222");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("dl_vlan=10");
+ match.fromString("dl_vpcp=1");
+ match.fromString("nw_proto=6");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("nw_tos=100");
+ match.fromString("tp_dst=8080");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ match.fromString("tp_src=60");
+ Assert.assertTrue((match.getPadSize() + match.getIPv6MatchLen()) % 8 == 0);
+ }
+}