aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/misc/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/misc/src/main/java')
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractEdge.java76
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java316
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AdjacencyListsGraph.java122
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java60
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java79
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultMutablePath.java136
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultPath.java103
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java183
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java97
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java128
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java.orig169
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Edge.java39
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/EdgeWeight.java31
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAOrganism.java38
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAPopulation.java75
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Graph.java59
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java87
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphSearch.java43
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Heap.java211
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java286
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java160
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableGraph.java59
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutablePath.java62
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Path.java45
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SRLGGraphSearch.java253
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SuurballeGraphSearch.java193
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java212
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Vertex.java22
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/graph/package-info.java20
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java118
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java60
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java36
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java41
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java304
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java178
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsUtil.java56
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/package-info.java20
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java439
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java127
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java86
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java632
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java136
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java116
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java132
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java32
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java36
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java264
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java199
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java159
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java625
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java223
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java366
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java335
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java98
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java158
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java202
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java89
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java731
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java384
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java174
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java104
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java152
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java93
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java559
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java303
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java102
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java300
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java225
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java165
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java147
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java217
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java74
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java185
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java84
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java423
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java142
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java462
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java104
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java306
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java102
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java300
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java260
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java29
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java188
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java253
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java29
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java37
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java291
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java20
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java278
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java281
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java192
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java225
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java325
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java155
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java21
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java21
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/AbstractAccumulator.java214
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Accumulator.java49
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Bandwidth.java138
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/BlockingBoolean.java97
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java176
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/ByteArraySizeHashPrinter.java69
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Counter.java139
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Frequency.java183
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/GroupedThreadFactory.java88
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexString.java112
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/ItemNotFoundException.java46
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java437
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java47
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java48
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/RetryingFunction.java60
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/RichComparable.java45
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutorService.java138
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutors.java123
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/SlidingWindowCounter.java129
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Spectrum.java51
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Timer.java52
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java557
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/TriConsumer.java35
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/util/package-info.java20
121 files changed, 20127 insertions, 0 deletions
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractEdge.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractEdge.java
new file mode 100644
index 00000000..3e8960e6
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractEdge.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstract graph edge implementation.
+ */
+public abstract class AbstractEdge<V extends Vertex> implements Edge<V> {
+
+ private final V src;
+ private final V dst;
+
+ /**
+ * Creates a new edge between the specified source and destination vertexes.
+ *
+ * @param src source vertex
+ * @param dst destination vertex
+ */
+ public AbstractEdge(V src, V dst) {
+ this.src = checkNotNull(src, "Source vertex cannot be null");
+ this.dst = checkNotNull(dst, "Destination vertex cannot be null");
+ }
+
+ @Override
+ public V src() {
+ return src;
+ }
+
+ @Override
+ public V dst() {
+ return dst;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(src, dst);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AbstractEdge) {
+ final AbstractEdge other = (AbstractEdge) obj;
+ return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("src", src)
+ .add("dst", dst)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
new file mode 100644
index 00000000..e7e2c40d
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Basis for various graph path search algorithm implementations.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public abstract class AbstractGraphPathSearch<V extends Vertex, E extends Edge<V>>
+ implements GraphPathSearch<V, E> {
+
+ private double samenessThreshold = Double.MIN_VALUE;
+
+ /**
+ * Sets a new sameness threshold for comparing cost values; default is
+ * is {@link Double#MIN_VALUE}.
+ *
+ * @param threshold fractional double value
+ */
+ public void setSamenessThreshold(double threshold) {
+ samenessThreshold = threshold;
+ }
+
+ /**
+ * Returns the current sameness threshold for comparing cost values.
+ *
+ * @return current threshold
+ */
+ public double samenessThreshold() {
+ return samenessThreshold;
+ }
+
+ /**
+ * Default path search result that uses the DefaultPath to convey paths
+ * in a graph.
+ */
+ protected class DefaultResult implements Result<V, E> {
+
+ private final V src;
+ private final V dst;
+ protected final Set<Path<V, E>> paths = new HashSet<>();
+ protected final Map<V, Double> costs = new HashMap<>();
+ protected final Map<V, Set<E>> parents = new HashMap<>();
+ protected final int maxPaths;
+
+ /**
+ * Creates the result of a single-path search.
+ *
+ * @param src path source
+ * @param dst optional path destination
+ */
+ public DefaultResult(V src, V dst) {
+ this(src, dst, 1);
+ }
+
+ /**
+ * Creates the result of path search.
+ *
+ * @param src path source
+ * @param dst optional path destination
+ * @param maxPaths optional limit of number of paths;
+ * {@link GraphPathSearch#ALL_PATHS} if no limit
+ */
+ public DefaultResult(V src, V dst, int maxPaths) {
+ checkNotNull(src, "Source cannot be null");
+ this.src = src;
+ this.dst = dst;
+ this.maxPaths = maxPaths;
+ }
+
+ @Override
+ public V src() {
+ return src;
+ }
+
+ @Override
+ public V dst() {
+ return dst;
+ }
+
+ @Override
+ public Set<Path<V, E>> paths() {
+ return paths;
+ }
+
+ @Override
+ public Map<V, Double> costs() {
+ return costs;
+ }
+
+ @Override
+ public Map<V, Set<E>> parents() {
+ return parents;
+ }
+
+ /**
+ * Indicates whether or not the given vertex has a cost yet.
+ *
+ * @param v vertex to test
+ * @return true if the vertex has cost already
+ */
+ boolean hasCost(V v) {
+ return costs.get(v) != null;
+ }
+
+ /**
+ * Returns the current cost to reach the specified vertex.
+ *
+ * @param v vertex to reach
+ * @return cost to reach the vertex
+ */
+ double cost(V v) {
+ Double c = costs.get(v);
+ return c == null ? Double.MAX_VALUE : c;
+ }
+
+ /**
+ * Updates the cost of the vertex using its existing cost plus the
+ * cost to traverse the specified edge. If the search is in single
+ * path mode, only one path will be accrued.
+ *
+ * @param vertex vertex to update
+ * @param edge edge through which vertex is reached
+ * @param cost current cost to reach the vertex from the source
+ * @param replace true to indicate that any accrued edges are to be
+ * cleared; false to indicate that the edge should be
+ * added to the previously accrued edges as they yield
+ * the same cost
+ */
+ void updateVertex(V vertex, E edge, double cost, boolean replace) {
+ costs.put(vertex, cost);
+ if (edge != null) {
+ Set<E> edges = parents.get(vertex);
+ if (edges == null) {
+ edges = new HashSet<>();
+ parents.put(vertex, edges);
+ }
+ if (replace) {
+ edges.clear();
+ }
+ if (maxPaths == ALL_PATHS || edges.size() < maxPaths) {
+ edges.add(edge);
+ }
+ }
+ }
+
+ /**
+ * Removes the set of parent edges for the specified vertex.
+ *
+ * @param v vertex
+ */
+ void removeVertex(V v) {
+ parents.remove(v);
+ }
+
+ /**
+ * If possible, relax the specified edge using the supplied base cost
+ * and edge-weight function.
+ *
+ * @param edge edge to be relaxed
+ * @param cost base cost to reach the edge destination vertex
+ * @param ew optional edge weight function
+ * @param forbidNegatives if true negative values will forbid the link
+ * @return true if the edge was relaxed; false otherwise
+ */
+ boolean relaxEdge(E edge, double cost, EdgeWeight<V, E> ew,
+ boolean... forbidNegatives) {
+ V v = edge.dst();
+ double oldCost = cost(v);
+ double hopCost = ew == null ? 1.0 : ew.weight(edge);
+ if (hopCost < 0 && forbidNegatives.length == 1 && forbidNegatives[0]) {
+ return false;
+ }
+
+ double newCost = cost + hopCost;
+ boolean relaxed = newCost < oldCost;
+ boolean same = Math.abs(newCost - oldCost) <= samenessThreshold;
+ if (same || relaxed) {
+ updateVertex(v, edge, newCost, !same);
+ }
+ return relaxed;
+ }
+
+ /**
+ * Builds a set of paths for the specified src/dst vertex pair.
+ */
+ protected void buildPaths() {
+ Set<V> destinations = new HashSet<>();
+ if (dst == null) {
+ destinations.addAll(costs.keySet());
+ } else {
+ destinations.add(dst);
+ }
+
+ // Build all paths between the source and all requested destinations.
+ for (V v : destinations) {
+ // Ignore the source, if it is among the destinations.
+ if (!v.equals(src)) {
+ buildAllPaths(this, src, v, maxPaths);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Builds a set of all paths between the source and destination using the
+ * graph search result by applying breadth-first search through the parent
+ * edges and vertex costs.
+ *
+ * @param result graph search result
+ * @param src source vertex
+ * @param dst destination vertex
+ * @param maxPaths limit on the number of paths built;
+ * {@link GraphPathSearch#ALL_PATHS} if no limit
+ */
+ private void buildAllPaths(DefaultResult result, V src, V dst, int maxPaths) {
+ DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>();
+ basePath.setCost(result.cost(dst));
+
+ Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>();
+ pendingPaths.add(basePath);
+
+ while (!pendingPaths.isEmpty() &&
+ (maxPaths == ALL_PATHS || result.paths.size() < maxPaths)) {
+ Set<DefaultMutablePath<V, E>> frontier = new HashSet<>();
+
+ for (DefaultMutablePath<V, E> path : pendingPaths) {
+ // For each pending path, locate its first vertex since we
+ // will be moving backwards from it.
+ V firstVertex = firstVertex(path, dst);
+
+ // If the first vertex is our expected source, we have reached
+ // the beginning, so add the this path to the result paths.
+ if (firstVertex.equals(src)) {
+ path.setCost(result.cost(dst));
+ result.paths.add(new DefaultPath<>(path.edges(), path.cost()));
+
+ } else {
+ // If we have not reached the beginning, i.e. the source,
+ // fetch the set of edges leading to the first vertex of
+ // this pending path; if there are none, abandon processing
+ // this path for good.
+ Set<E> firstVertexParents = result.parents.get(firstVertex);
+ if (firstVertexParents == null || firstVertexParents.isEmpty()) {
+ break;
+ }
+
+ // Now iterate over all the edges and for each of them
+ // cloning the current path and then insert that edge to
+ // the path and then add that path to the pending ones.
+ // When processing the last edge, modify the current
+ // pending path rather than cloning a new one.
+ Iterator<E> edges = firstVertexParents.iterator();
+ while (edges.hasNext()) {
+ E edge = edges.next();
+ boolean isLast = !edges.hasNext();
+ DefaultMutablePath<V, E> pendingPath = isLast ? path : new DefaultMutablePath<>(path);
+ pendingPath.insertEdge(edge);
+ frontier.add(pendingPath);
+ }
+ }
+ }
+
+ // All pending paths have been scanned so promote the next frontier
+ pendingPaths = frontier;
+ }
+ }
+
+ // Returns the first vertex of the specified path. This is either the source
+ // of the first edge or, if there are no edges yet, the given destination.
+ private V firstVertex(Path<V, E> path, V dst) {
+ return path.edges().isEmpty() ? dst : path.edges().get(0).src();
+ }
+
+ /**
+ * Checks the specified path search arguments for validity.
+ *
+ * @param graph graph; must not be null
+ * @param src source vertex; must not be null and belong to graph
+ * @param dst optional target vertex; must belong to graph
+ */
+ protected void checkArguments(Graph<V, E> graph, V src, V dst) {
+ checkNotNull(graph, "Graph cannot be null");
+ checkNotNull(src, "Source cannot be null");
+ Set<V> vertices = graph.getVertexes();
+ checkArgument(vertices.contains(src), "Source not in the graph");
+ checkArgument(dst == null || vertices.contains(dst),
+ "Destination not in graph");
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AdjacencyListsGraph.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AdjacencyListsGraph.java
new file mode 100644
index 00000000..3890dcf8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/AdjacencyListsGraph.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable graph implemented using adjacency lists.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public class AdjacencyListsGraph<V extends Vertex, E extends Edge<V>>
+ implements Graph<V, E> {
+
+ private final Set<V> vertexes;
+ private final Set<E> edges;
+
+ private final ImmutableSetMultimap<V, E> sources;
+ private final ImmutableSetMultimap<V, E> destinations;
+
+ /**
+ * Creates a graph comprising of the specified vertexes and edges.
+ *
+ * @param vertexes set of graph vertexes
+ * @param edges set of graph edges
+ */
+ public AdjacencyListsGraph(Set<V> vertexes, Set<E> edges) {
+ checkNotNull(vertexes, "Vertex set cannot be null");
+ checkNotNull(edges, "Edge set cannot be null");
+
+ // Record ingress/egress edges for each vertex.
+ ImmutableSetMultimap.Builder<V, E> srcMap = ImmutableSetMultimap.builder();
+ ImmutableSetMultimap.Builder<V, E> dstMap = ImmutableSetMultimap.builder();
+
+ // Also make sure that all edge end-points are added as vertexes
+ ImmutableSet.Builder<V> actualVertexes = ImmutableSet.builder();
+ actualVertexes.addAll(vertexes);
+
+ for (E edge : edges) {
+ srcMap.put(edge.src(), edge);
+ actualVertexes.add(edge.src());
+ dstMap.put(edge.dst(), edge);
+ actualVertexes.add(edge.dst());
+ }
+
+ // Make an immutable copy of the edge and vertex sets
+ this.edges = ImmutableSet.copyOf(edges);
+ this.vertexes = actualVertexes.build();
+
+ // Build immutable copies of sources and destinations edge maps
+ sources = srcMap.build();
+ destinations = dstMap.build();
+ }
+
+ @Override
+ public Set<V> getVertexes() {
+ return vertexes;
+ }
+
+ @Override
+ public Set<E> getEdges() {
+ return edges;
+ }
+
+ @Override
+ public Set<E> getEdgesFrom(V src) {
+ return sources.get(src);
+ }
+
+ @Override
+ public Set<E> getEdgesTo(V dst) {
+ return destinations.get(dst);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AdjacencyListsGraph) {
+ AdjacencyListsGraph that = (AdjacencyListsGraph) obj;
+ return this.getClass() == that.getClass() &&
+ Objects.equals(this.vertexes, that.vertexes) &&
+ Objects.equals(this.edges, that.edges);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(vertexes, edges);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("vertexes", vertexes)
+ .add("edges", edges)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
new file mode 100644
index 00000000..dc741f73
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Bellman-Ford graph search algorithm for locating shortest-paths in
+ * directed graphs that may contain negative cycles.
+ */
+public class BellmanFordGraphSearch<V extends Vertex, E extends Edge<V>>
+ extends AbstractGraphPathSearch<V, E> {
+
+ @Override
+ public Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+ checkArguments(graph, src, dst);
+
+ // Prepare the graph search result.
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
+
+ // The source vertex has cost 0, of course.
+ result.updateVertex(src, null, 0.0, true);
+
+ int max = graph.getVertexes().size() - 1;
+ for (int i = 0; i < max; i++) {
+ // Relax, if possible, all egress edges of the current vertex.
+ for (E edge : graph.getEdges()) {
+ if (result.hasCost(edge.src())) {
+ result.relaxEdge(edge, result.cost(edge.src()), weight);
+ }
+ }
+ }
+
+ // Remove any vertexes reached by traversing edges with negative weights.
+ for (E edge : graph.getEdges()) {
+ if (result.hasCost(edge.src())) {
+ if (result.relaxEdge(edge, result.cost(edge.src()), weight)) {
+ result.removeVertex(edge.dst());
+ }
+ }
+ }
+
+ // Finally, but the paths on the search result and return.
+ result.buildPaths();
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
new file mode 100644
index 00000000..40c8735b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Implementation of the BFS algorithm.
+ */
+public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>>
+ extends AbstractGraphPathSearch<V, E> {
+
+ @Override
+ public Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+ checkArguments(graph, src, dst);
+
+ // Prepare the graph result.
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
+
+ // Setup the starting frontier with the source as the sole vertex.
+ Set<V> frontier = new HashSet<>();
+ result.updateVertex(src, null, 0.0, true);
+ frontier.add(src);
+
+ boolean reachedEnd = false;
+ while (!reachedEnd && !frontier.isEmpty()) {
+ // Prepare the next frontier.
+ Set<V> next = new HashSet<>();
+
+ // Visit all vertexes in the current frontier.
+ for (V vertex : frontier) {
+ double cost = result.cost(vertex);
+
+ // Visit all egress edges of the current frontier vertex.
+ for (E edge : graph.getEdgesFrom(vertex)) {
+ V nextVertex = edge.dst();
+ if (!result.hasCost(nextVertex)) {
+ // If this vertex has not been visited yet, update it.
+ double newCost = cost + (weight == null ? 1.0 : weight.weight(edge));
+ result.updateVertex(nextVertex, edge, newCost, true);
+ // If we have reached our intended destination, bail.
+ if (nextVertex.equals(dst)) {
+ reachedEnd = true;
+ break;
+ }
+ next.add(nextVertex);
+ }
+
+ if (reachedEnd) {
+ break;
+ }
+ }
+ }
+
+ // Promote the next frontier.
+ frontier = next;
+ }
+
+ // Finally, but the paths on the search result and return.
+ result.buildPaths();
+ return result;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultMutablePath.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultMutablePath.java
new file mode 100644
index 00000000..ad7e8402
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultMutablePath.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Simple concrete implementation of a directed graph path.
+ */
+public class DefaultMutablePath<V extends Vertex, E extends Edge<V>> implements MutablePath<V, E> {
+
+ private final List<E> edges = new ArrayList<>();
+ private double cost = 0.0;
+
+ /**
+ * Creates a new empty path.
+ */
+ public DefaultMutablePath() {
+ }
+
+ /**
+ * Creates a new path as a copy of another path.
+ *
+ * @param path path to be copied
+ */
+ public DefaultMutablePath(Path<V, E> path) {
+ checkNotNull(path, "Path cannot be null");
+ this.cost = path.cost();
+ edges.addAll(path.edges());
+ }
+
+ @Override
+ public V src() {
+ return edges.isEmpty() ? null : edges.get(0).src();
+ }
+
+ @Override
+ public V dst() {
+ return edges.isEmpty() ? null : edges.get(edges.size() - 1).dst();
+ }
+
+ @Override
+ public double cost() {
+ return cost;
+ }
+
+ @Override
+ public List<E> edges() {
+ return ImmutableList.copyOf(edges);
+ }
+
+ @Override
+ public void setCost(double cost) {
+ this.cost = cost;
+ }
+
+ @Override
+ public Path<V, E> toImmutable() {
+ return new DefaultPath<>(edges, cost);
+ }
+
+ @Override
+ public void insertEdge(E edge) {
+ checkNotNull(edge, "Edge cannot be null");
+ checkArgument(edges.isEmpty() || src().equals(edge.dst()),
+ "Edge destination must be the same as the current path source");
+ edges.add(0, edge);
+ }
+
+ @Override
+ public void appendEdge(E edge) {
+ checkNotNull(edge, "Edge cannot be null");
+ checkArgument(edges.isEmpty() || dst().equals(edge.src()),
+ "Edge source must be the same as the current path destination");
+ edges.add(edge);
+ }
+
+ @Override
+ public void removeEdge(E edge) {
+ checkArgument(edge.src().equals(edge.dst()) ||
+ edges.indexOf(edge) == 0 ||
+ edges.lastIndexOf(edge) == edges.size() - 1,
+ "Edge must be at start or end of path, or it must be a cyclic edge");
+ edges.remove(edge);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("src", src())
+ .add("dst", dst())
+ .add("cost", cost)
+ .add("edges", edges)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(edges, cost);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultMutablePath) {
+ final DefaultMutablePath other = (DefaultMutablePath) obj;
+ return Objects.equals(this.cost, other.cost) &&
+ Objects.equals(this.edges, other.edges);
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultPath.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultPath.java
new file mode 100644
index 00000000..816fb161
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DefaultPath.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Simple concrete implementation of a directed graph path.
+ */
+public class DefaultPath<V extends Vertex, E extends Edge<V>> implements Path<V, E> {
+
+ private final V src;
+ private final V dst;
+ private final List<E> edges;
+ private double cost = 0.0;
+
+ /**
+ * Creates a new path from the specified list of edges and cost.
+ *
+ * @param edges list of path edges
+ * @param cost path cost as a unit-less number
+ */
+ public DefaultPath(List<E> edges, double cost) {
+ checkNotNull(edges, "Edges list must not be null");
+ checkArgument(!edges.isEmpty(), "There must be at least one edge");
+ this.edges = ImmutableList.copyOf(edges);
+ this.src = edges.get(0).src();
+ this.dst = edges.get(edges.size() - 1).dst();
+ this.cost = cost;
+ }
+
+ @Override
+ public V src() {
+ return src;
+ }
+
+ @Override
+ public V dst() {
+ return dst;
+ }
+
+ @Override
+ public double cost() {
+ return cost;
+ }
+
+ @Override
+ public List<E> edges() {
+ return Collections.unmodifiableList(edges);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("src", src)
+ .add("dst", dst)
+ .add("cost", cost)
+ .add("edges", edges)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(src, dst, edges, cost);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultPath) {
+ final DefaultPath other = (DefaultPath) obj;
+ return Objects.equals(this.src, other.src) &&
+ Objects.equals(this.dst, other.dst) &&
+ Objects.equals(this.cost, other.cost) &&
+ Objects.equals(this.edges, other.edges);
+ }
+ return false;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
new file mode 100644
index 00000000..91b5108f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * DFS graph search algorithm implemented via iteration rather than recursion.
+ */
+public class DepthFirstSearch<V extends Vertex, E extends Edge<V>>
+ extends AbstractGraphPathSearch<V, E> {
+
+ /**
+ * Graph edge types as classified by the DFS algorithm.
+ */
+ public static enum EdgeType {
+ TREE_EDGE, FORWARD_EDGE, BACK_EDGE, CROSS_EDGE
+ }
+
+ @Override
+ public SpanningTreeResult search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+ checkArguments(graph, src, dst);
+
+ // Prepare the search result.
+ SpanningTreeResult result = new SpanningTreeResult(src, dst, maxPaths);
+
+ // The source vertex has cost 0, of course.
+ result.updateVertex(src, null, 0.0, true);
+
+ // Track finished vertexes and keep a stack of vertexes that have been
+ // started; start this stack with the source on it.
+ Set<V> finished = new HashSet<>();
+ Stack<V> stack = new Stack<>();
+ stack.push(src);
+
+ while (!stack.isEmpty()) {
+ V vertex = stack.peek();
+ if (vertex.equals(dst)) {
+ // If we have reached our destination, bail.
+ break;
+ }
+
+ double cost = result.cost(vertex);
+ boolean tangent = false;
+
+ // Visit all egress edges of the current vertex.
+ for (E edge : graph.getEdgesFrom(vertex)) {
+ // If we have seen the edge already, skip it.
+ if (result.isEdgeMarked(edge)) {
+ continue;
+ }
+
+ // Examine the destination of the current edge.
+ V nextVertex = edge.dst();
+ if (!result.hasCost(nextVertex)) {
+ // If this vertex have not finished this vertex yet,
+ // not started it, then start it as a tree-edge.
+ result.markEdge(edge, EdgeType.TREE_EDGE);
+ double newCost = cost + (weight == null ? 1.0 : weight.weight(edge));
+ result.updateVertex(nextVertex, edge, newCost, true);
+ stack.push(nextVertex);
+ tangent = true;
+ break;
+
+ } else if (!finished.contains(nextVertex)) {
+ // We started the vertex, but did not yet finish it, so
+ // it must be a back-edge.
+ result.markEdge(edge, EdgeType.BACK_EDGE);
+ } else {
+ // The target has been finished already, so what we have
+ // here is either a forward-edge or a cross-edge.
+ result.markEdge(edge, isForwardEdge(result, edge) ?
+ EdgeType.FORWARD_EDGE : EdgeType.CROSS_EDGE);
+ }
+ }
+
+ // If we have not been sent on a tangent search and reached the
+ // end of the current scan normally, mark the node as finished
+ // and pop it off the vertex stack.
+ if (!tangent) {
+ finished.add(vertex);
+ stack.pop();
+ }
+ }
+
+ // Finally, but the paths on the search result and return.
+ result.buildPaths();
+ return result;
+ }
+
+ /**
+ * Determines whether the specified edge is a forward edge using the
+ * accumulated set of parent edges for each vertex.
+ *
+ * @param result search result
+ * @param edge edge to be classified
+ * @return true if the edge is a forward edge
+ */
+ protected boolean isForwardEdge(DefaultResult result, E edge) {
+ // Follow the parent edges until we hit the edge source vertex
+ V target = edge.src();
+ V vertex = edge.dst();
+ Set<E> parentEdges;
+ while ((parentEdges = result.parents.get(vertex)) != null) {
+ for (E parentEdge : parentEdges) {
+ vertex = parentEdge.src();
+ if (vertex.equals(target)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Graph search result which includes edge classification for building
+ * a spanning tree.
+ */
+ public class SpanningTreeResult extends DefaultResult {
+
+ protected final Map<E, EdgeType> edges = new HashMap<>();
+
+ /**
+ * Creates a new spanning tree result.
+ *
+ * @param src search source
+ * @param dst optional search destination
+ * @param maxPaths limit on the number of paths
+ */
+ public SpanningTreeResult(V src, V dst, int maxPaths) {
+ super(src, dst, maxPaths);
+ }
+
+ /**
+ * Returns the map of edge type.
+ *
+ * @return edge to edge type bindings
+ */
+ public Map<E, EdgeType> edges() {
+ return edges;
+ }
+
+ /**
+ * Indicates whether or not the edge has been marked with type.
+ *
+ * @param edge edge to test
+ * @return true if the edge has been marked already
+ */
+ boolean isEdgeMarked(E edge) {
+ return edges.containsKey(edge);
+ }
+
+ /**
+ * Marks the edge with the specified type.
+ *
+ * @param edge edge to mark
+ * @param type edge type
+ */
+ void markEdge(E edge, EdgeType type) {
+ edges.put(edge, type);
+ }
+
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
new file mode 100644
index 00000000..b1892ee1
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Set;
+
+/**
+ * Dijkstra shortest-path graph search algorithm capable of finding not just
+ * one, but all shortest paths between the source and destinations.
+ */
+public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>>
+ extends AbstractGraphPathSearch<V, E> {
+
+ @Override
+ public Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+ checkArguments(graph, src, dst);
+
+ // Use the default result to remember cumulative costs and parent
+ // edges to each each respective vertex.
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
+
+ // Cost to reach the source vertex is 0 of course.
+ result.updateVertex(src, null, 0.0, false);
+
+ if (graph.getEdges().isEmpty()) {
+ result.buildPaths();
+ return result;
+ }
+
+ // Use the min priority queue to progressively find each nearest
+ // vertex until we reach the desired destination, if one was given,
+ // or until we reach all possible destinations.
+ Heap<V> minQueue = createMinQueue(graph.getVertexes(),
+ new PathCostComparator(result));
+ while (!minQueue.isEmpty()) {
+ // Get the nearest vertex
+ V nearest = minQueue.extractExtreme();
+ if (nearest.equals(dst)) {
+ break;
+ }
+
+ // Find its cost and use it to determine if the vertex is reachable.
+ double cost = result.cost(nearest);
+ if (cost < Double.MAX_VALUE) {
+ // If the vertex is reachable, relax all its egress edges.
+ for (E e : graph.getEdgesFrom(nearest)) {
+ result.relaxEdge(e, cost, weight, true);
+ }
+ }
+
+ // Re-prioritize the min queue.
+ minQueue.heapify();
+ }
+
+ // Now construct a set of paths from the results.
+ result.buildPaths();
+ return result;
+ }
+
+ // Compares path weights using their accrued costs; used for sorting the
+ // min priority queue.
+ private final class PathCostComparator implements Comparator<V> {
+ private final DefaultResult result;
+
+ private PathCostComparator(DefaultResult result) {
+ this.result = result;
+ }
+
+ @Override
+ public int compare(V v1, V v2) {
+ double delta = result.cost(v2) - result.cost(v1);
+ return delta < 0 ? -1 : (delta > 0 ? 1 : 0);
+ }
+ }
+
+ // Creates a min priority queue from the specified vertexes and comparator.
+ private Heap<V> createMinQueue(Set<V> vertexes, Comparator<V> comparator) {
+ return new Heap<>(new ArrayList<>(vertexes), comparator);
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java
new file mode 100644
index 00000000..b62d3b24
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.onlab.graph;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+
+public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Path<V, E> {
+ public Path<V, E> path1, path2;
+ boolean usingPath1 = true;
+
+ /**
+ * Creates a Disjoint Path Pair from two paths.
+ *
+ * @param p1 first path
+ * @param p2 second path
+ */
+ public DisjointPathPair(Path<V, E> p1, Path<V, E> p2) {
+ path1 = p1;
+ path2 = p2;
+ }
+
+ @Override
+ public V src() {
+ return path1.src();
+ }
+
+ @Override
+ public V dst() {
+ return path1.dst();
+ }
+
+ @Override
+ public double cost() {
+ if (!hasBackup()) {
+ return path1.cost();
+ }
+ return path1.cost() + path2.cost();
+ }
+
+ @Override
+ public List<E> edges() {
+ if (usingPath1 || !hasBackup()) {
+ return path1.edges();
+ } else {
+ return path2.edges();
+ }
+ }
+
+ /**
+ * Checks if this path pair contains a backup/secondary path.
+ *
+ * @return boolean representing whether it has backup
+ */
+ public boolean hasBackup() {
+ return path2 != null && path2.edges() != null;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("src", src())
+ .add("dst", dst())
+ .add("cost", cost())
+ .add("edges", edges())
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ Set<Path<V, E>> paths;
+ if (!hasBackup()) {
+ paths = of(path1);
+ } else {
+ paths = of(path1, path2);
+ }
+ return Objects.hash(paths);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DisjointPathPair) {
+ final DisjointPathPair other = (DisjointPathPair) obj;
+ return Objects.equals(this.src(), other.src()) &&
+ Objects.equals(this.dst(), other.dst()) &&
+ (Objects.equals(this.path1, other.path1) &&
+ Objects.equals(this.path2, other.path2)) ||
+ (Objects.equals(this.path1, other.path2) &&
+ Objects.equals(this.path2, other.path1));
+ }
+ return false;
+ }
+
+ /**
+ * Returns number of paths inside this path pair object.
+ *
+ * @return number of paths
+ */
+ public int size() {
+ if (hasBackup()) {
+ return 2;
+ }
+ return 1;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java.orig b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java.orig
new file mode 100644
index 00000000..1cf22b6a
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java.orig
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.onlab.graph;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+
+public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Path<V, E> {
+ public Path<V, E> path1, path2;
+ boolean usingPath1 = true;
+
+<<<<<<< HEAD
+ /**
+ * Creates a Disjoint Path Pair from two paths.
+ *
+ * @param p1 first path
+ * @param p2 second path
+ */
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public DisjointPathPair(Path<V, E> p1, Path<V, E> p2) {
+ path1 = p1;
+ path2 = p2;
+ }
+<<<<<<< HEAD
+
+ @Override
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public V src() {
+ return path1.src();
+ }
+
+<<<<<<< HEAD
+ @Override
+ public V dst() {
+ return path1.dst();
+ }
+
+ @Override
+=======
+ public V dst() {
+ return path1.dst();
+ }
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public double cost() {
+ if (!hasBackup()) {
+ return path1.cost();
+ }
+ return path1.cost() + path2.cost();
+ }
+<<<<<<< HEAD
+
+ @Override
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public List<E> edges() {
+ if (usingPath1 || !hasBackup()) {
+ return path1.edges();
+ } else {
+ return path2.edges();
+ }
+ }
+<<<<<<< HEAD
+
+ /**
+ * Checks if this path pair contains a backup/secondary path.
+ *
+ * @return boolean representing whether it has backup
+ */
+ public boolean hasBackup() {
+ return path2 != null && path2.edges() != null;
+ }
+
+ /**
+ * Switches this disjoint path pair to using its backup path, instead of
+ * using its primary.
+ */
+ public void useBackup() {
+ usingPath1 = !usingPath1;
+ }
+
+ @Override
+=======
+ public boolean hasBackup() {
+ return path2 != null && path2.edges() != null;
+ }
+ public void useBackup() {
+ usingPath1 = !usingPath1;
+ }
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public String toString() {
+ return toStringHelper(this)
+ .add("src", src())
+ .add("dst", dst())
+ .add("cost", cost())
+ .add("edges", edges())
+ .toString();
+ }
+<<<<<<< HEAD
+
+ @Override
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public int hashCode() {
+ Set<Path<V, E>> paths;
+ if (!hasBackup()) {
+ paths = of(path1);
+ } else {
+ paths = of(path1, path2);
+ }
+ return Objects.hash(paths);
+ }
+<<<<<<< HEAD
+
+ @Override
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DisjointPathPair) {
+ final DisjointPathPair other = (DisjointPathPair) obj;
+ return Objects.equals(this.src(), other.src()) &&
+ Objects.equals(this.dst(), other.dst()) &&
+ (Objects.equals(this.path1, other.path1) &&
+ Objects.equals(this.path2, other.path2)) ||
+ (Objects.equals(this.path1, other.path2) &&
+ Objects.equals(this.path2, other.path1));
+ }
+ return false;
+ }
+<<<<<<< HEAD
+
+ /**
+ * Returns number of paths inside this path pair object.
+ *
+ * @return number of paths
+ */
+=======
+>>>>>>> Disjoint Path Pairs (Suurballe) utils
+ public int size() {
+ if (hasBackup()) {
+ return 2;
+ }
+ return 1;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Edge.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Edge.java
new file mode 100644
index 00000000..1bbfbf99
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Edge.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Representation of a graph edge.
+ *
+ * @param <V> vertex type
+ */
+public interface Edge<V extends Vertex> {
+
+ /**
+ * Returns the edge source vertex.
+ *
+ * @return source vertex
+ */
+ V src();
+
+ /**
+ * Returns the edge destination vertex.
+ *
+ * @return destination vertex
+ */
+ V dst();
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/EdgeWeight.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/EdgeWeight.java
new file mode 100644
index 00000000..975b59c7
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/EdgeWeight.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Abstraction of a graph edge weight function.
+ */
+public interface EdgeWeight<V extends Vertex, E extends Edge<V>> {
+
+ /**
+ * Returns the weight of the given edge as a unit-less number.
+ *
+ * @param edge edge to be weighed
+ * @return edge weight as a unit-less number
+ */
+ double weight(E edge);
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAOrganism.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAOrganism.java
new file mode 100644
index 00000000..230609f8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAOrganism.java
@@ -0,0 +1,38 @@
+package org.onlab.graph;
+
+/**
+ * Interface representing an "organism": a specific solution
+ * to a problem where solutions can be evaluated in terms
+ * of fitness. These organisms can be used to represent any
+ * class of problem that genetic algorithms can be run on.
+ */
+interface GAOrganism {
+ /**
+ * A fitness function that determines how
+ * optimal a given organism is.
+ *
+ * @return fitness of organism
+ */
+ double fitness();
+
+ /**
+ * A method that slightly mutates an organism.
+ */
+ void mutate();
+
+ /**
+ * Creates a new random organism.
+ *
+ * @return random GAOrganism
+ */
+ GAOrganism random();
+
+ /**
+ * Returns a child organism that is the result
+ * of "crossing" this organism with another.
+ *
+ * @param other Other organism to cross with
+ * @return child organism
+ */
+ GAOrganism crossWith(GAOrganism other);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAPopulation.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAPopulation.java
new file mode 100644
index 00000000..ae7f182e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GAPopulation.java
@@ -0,0 +1,75 @@
+package org.onlab.graph;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Represents a population of GAOrganisms. This class can be used
+ * to run a genetic algorithm on the population and return the fittest solutions.
+ */
+class GAPopulation<Organism extends GAOrganism> extends ArrayList<Organism> {
+ Random r = new Random();
+
+ /**
+ * Steps the population through one generation. The 75% least fit
+ * organisms are killed off and replaced with the children of the
+ * 25% (as well as some "random" newcomers).
+ */
+ void step() {
+ Collections.sort(this, (org1, org2) -> {
+ double d = org1.fitness() - org2.fitness();
+ if (d < 0) {
+ return -1;
+ } else if (d == 0) {
+ return 0;
+ }
+ return 1;
+ });
+ int maxSize = size();
+ for (int i = size() - 1; i > maxSize / 4; i--) {
+ remove(i);
+ }
+ for (Organism org: this) {
+ if (r.nextBoolean()) {
+ org.mutate();
+ }
+ }
+ while (size() < maxSize * 4 / 5) {
+ Organism org1 = get(r.nextInt(size()));
+ Organism org2 = get(r.nextInt(size()));
+ add((Organism) org1.crossWith(org2));
+ }
+
+ while (size() < maxSize) {
+ Organism org1 = get(r.nextInt(size()));
+ add((Organism) org1.random());
+ }
+ }
+
+ /**
+ * Runs GA for the specified number of iterations, and returns
+ * a sample of the resulting population of solutions.
+ *
+ * @param generations Number of generations to run GA for
+ * @param populationSize Population size of GA
+ * @param sample Number of solutions to ask for
+ * @param template Template GAOrganism to seed the population with
+ * @return ArrayList containing sample number of organisms
+ */
+ List<Organism> runGA(int generations, int populationSize, int sample, Organism template) {
+ for (int i = 0; i < populationSize; i++) {
+ add((Organism) template.random());
+ }
+
+ for (int i = 0; i < generations; i++) {
+ step();
+ }
+ for (int i = size() - 1; i >= sample; i--) {
+ remove(i);
+ }
+ return new ArrayList<>(this);
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Graph.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Graph.java
new file mode 100644
index 00000000..bc7853ec
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Graph.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+
+import java.util.Set;
+
+/**
+ * Abstraction of a directed graph structure.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public interface Graph<V extends Vertex, E extends Edge> {
+
+ /**
+ * Returns the set of vertexes comprising the graph.
+ *
+ * @return set of vertexes
+ */
+ Set<V> getVertexes();
+
+ /**
+ * Returns the set of edges comprising the graph.
+ *
+ * @return set of edges
+ */
+ Set<E> getEdges();
+
+ /**
+ * Returns all edges leading out from the specified source vertex.
+ *
+ * @param src source vertex
+ * @return set of egress edges; empty if no such edges
+ */
+ Set<E> getEdgesFrom(V src);
+
+ /**
+ * Returns all edges leading towards the specified destination vertex.
+ *
+ * @param dst destination vertex
+ * @return set of ingress vertexes; empty if no such edges
+ */
+ Set<E> getEdgesTo(V dst);
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
new file mode 100644
index 00000000..caebce47
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Representation of a graph path search algorithm.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public interface GraphPathSearch<V extends Vertex, E extends Edge<V>> {
+
+ static int ALL_PATHS = -1;
+
+ /**
+ * Abstraction of a path search result.
+ */
+ interface Result<V extends Vertex, E extends Edge<V>> {
+
+ /**
+ * Returns the search source.
+ *
+ * @return search source
+ */
+ V src();
+
+ /**
+ * Returns the search destination, if was was given.
+ *
+ * @return optional search destination
+ */
+ V dst();
+
+ /**
+ * Returns the set of paths produced as a result of the graph search.
+ *
+ * @return set of paths
+ */
+ Set<Path<V, E>> paths();
+
+ /**
+ * Returns bindings of each vertex to its parent edges in the path.
+ *
+ * @return map of vertex to its parent edge bindings
+ */
+ Map<V, Set<E>> parents();
+
+ /**
+ * Return a bindings of each vertex to its cost in the path.
+ *
+ * @return map of vertex to path cost bindings
+ */
+ Map<V, Double> costs();
+ }
+
+ /**
+ * Searches the specified graph for paths between vertices.
+ *
+ * @param graph graph to be searched
+ * @param src optional source vertex
+ * @param dst optional destination vertex; if null paths to all vertex
+ * destinations will be searched
+ * @param weight optional edge-weight; if null cost of each edge will be
+ * assumed to be 1.0
+ * @param maxPaths limit on number of paths; {@link GraphPathSearch#ALL_PATHS} if no limit
+ * @return search results
+ */
+ Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths);
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphSearch.java
new file mode 100644
index 00000000..ca369a7d
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/GraphSearch.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Representation of a graph search algorithm and its outcome.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public interface GraphSearch<V extends Vertex, E extends Edge<V>> {
+
+ /**
+ * Notion of a graph search result.
+ */
+ interface Result<V extends Vertex, E extends Edge<V>> {
+ }
+
+ /**
+ * Searches the specified graph.
+ *
+ * @param graph graph to be searched
+ * @param weight optional edge-weight; if null cost of each edge will be
+ * assumed to be 1.0
+ *
+ * @return search results
+ */
+ Result search(Graph<V, E> graph, EdgeWeight<V, E> weight);
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Heap.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Heap.java
new file mode 100644
index 00000000..ebb1a60b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Heap.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of an array-backed heap structure whose sense of order is
+ * imposed by the provided comparator.
+ * <p>
+ * While this provides similar functionality to {@link java.util.PriorityQueue}
+ * data structure, one key difference is that external entities can control
+ * when to restore the heap property, which is done through invocation of the
+ * {@link #heapify} method.
+ * </p>
+ * <p>
+ * This class is not thread-safe and care must be taken to prevent concurrent
+ * modifications.
+ * </p>
+ *
+ * @param <T> type of the items on the heap
+ */
+public class Heap<T> {
+
+ private final List<T> data;
+ private final Comparator<T> comparator;
+
+ /**
+ * Creates a new heap backed by the specified list. In the interest of
+ * efficiency, the list should be array-backed. Also, for the same reason,
+ * the data is not copied and therefore, the caller must assure that the
+ * backing data is not altered in any way.
+ *
+ * @param data backing data list
+ * @param comparator comparator for ordering the heap items
+ */
+ public Heap(List<T> data, Comparator<T> comparator) {
+ this.data = checkNotNull(data, "Data cannot be null");
+ this.comparator = checkNotNull(comparator, "Comparator cannot be null");
+ heapify();
+ }
+
+ /**
+ * Restores the heap property by re-arranging the elements in the backing
+ * array as necessary following any heap modifications.
+ */
+ public void heapify() {
+ for (int i = data.size() / 2; i >= 0; i--) {
+ heapify(i);
+ }
+ }
+
+ /**
+ * Returns the current size of the heap.
+ *
+ * @return number of items in the heap
+ */
+ public int size() {
+ return data.size();
+ }
+
+ /**
+ * Returns true if there are no items in the heap.
+ *
+ * @return true if heap is empty
+ */
+ public boolean isEmpty() {
+ return data.isEmpty();
+ }
+
+ /**
+ * Returns the most extreme item in the heap.
+ *
+ * @return heap extreme or null if the heap is empty
+ */
+ public T extreme() {
+ return data.isEmpty() ? null : data.get(0);
+ }
+
+ /**
+ * Extracts and returns the most extreme item from the heap.
+ *
+ * @return heap extreme or null if the heap is empty
+ */
+ public T extractExtreme() {
+ if (!isEmpty()) {
+ T extreme = extreme();
+
+ data.set(0, data.get(data.size() - 1));
+ data.remove(data.size() - 1);
+ heapify();
+ return extreme;
+ }
+ return null;
+ }
+
+ /**
+ * Inserts the specified item into the heap and returns the modified heap.
+ *
+ * @param item item to be inserted
+ * @return the heap self
+ * @throws IllegalArgumentException if the heap is already full
+ */
+ public Heap<T> insert(T item) {
+ data.add(item);
+ bubbleUp();
+ return this;
+ }
+
+ /**
+ * Returns iterator to traverse the heap level-by-level. This iterator
+ * does not permit removal of items.
+ *
+ * @return non-destructive heap iterator
+ */
+ public Iterator<T> iterator() {
+ return ImmutableList.copyOf(data).iterator();
+ }
+
+ // Bubbles up the last item in the heap to its proper position to restore
+ // the heap property.
+ private void bubbleUp() {
+ int child = data.size() - 1;
+ while (child > 0) {
+ int parent = child / 2;
+ if (comparator.compare(data.get(child), data.get(parent)) < 0) {
+ break;
+ }
+ swap(child, parent);
+ child = parent;
+ }
+ }
+
+ // Restores the heap property of the specified heap layer.
+ private void heapify(int i) {
+ int left = 2 * i + 1;
+ int right = 2 * i;
+ int extreme = i;
+
+ if (left < data.size() &&
+ comparator.compare(data.get(extreme), data.get(left)) < 0) {
+ extreme = left;
+ }
+
+ if (right < data.size() &&
+ comparator.compare(data.get(extreme), data.get(right)) < 0) {
+ extreme = right;
+ }
+
+ if (extreme != i) {
+ swap(i, extreme);
+ heapify(extreme);
+ }
+ }
+
+ // Swaps two heap items identified by their respective indexes.
+ private void swap(int i, int k) {
+ T aux = data.get(i);
+ data.set(i, data.get(k));
+ data.set(k, aux);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Heap) {
+ Heap that = (Heap) obj;
+ return this.getClass() == that.getClass() &&
+ Objects.equals(this.comparator, that.comparator) &&
+ Objects.deepEquals(this.data, that.data);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(comparator, data);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("data", data)
+ .add("comparator", comparator)
+ .toString();
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
new file mode 100644
index 00000000..820e912c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.ArrayList;
+//import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+//import java.util.Map;
+//import java.util.PriorityQueue;
+import java.util.Set;
+
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
+
+/**
+ * K-shortest-path graph search algorithm capable of finding not just one,
+ * but K shortest paths with ascending order between the source and destinations.
+ */
+
+public class KshortestPathSearch<V extends Vertex, E extends Edge<V>> {
+
+ // Define class variables.
+ private Graph<V, E> immutableGraph;
+ private MutableGraph<V, E> mutableGraph;
+ private List<List<E>> pathResults = new ArrayList<List<E>>();
+ private List<List<E>> pathCandidates = new ArrayList<List<E>>();
+ private V source;
+ private V sink;
+ private int numK = 0;
+ private EdgeWeight<V, E> weight = null;
+ // private PriorityQueue<List<E>> pathCandidates = new PriorityQueue<List<E>>();
+
+ // Initialize the graph.
+ public KshortestPathSearch(Graph<V, E> graph) {
+ immutableGraph = graph;
+ mutableGraph = new MutableAdjacencyListsGraph<>(graph.getVertexes(),
+ graph.getEdges());
+ }
+
+ public List<List<E>> search(V src,
+ V dst,
+ EdgeWeight<V, E> wei,
+ int k) {
+
+ weight = wei;
+ source = src;
+ sink = dst;
+ numK = k;
+ // pathCandidates = new PriorityQueue<List<E>>();
+
+ pathResults.clear();
+ pathCandidates.clear();
+
+ // Double check the parameters
+ checkArguments(immutableGraph, src, dst, numK);
+
+ // DefaultResult result = new DefaultResult(src, dst);
+
+ searchKShortestPaths();
+
+ return pathResults;
+ }
+
+ private void checkArguments(Graph<V, E> graph, V src, V dst, int k) {
+ if (graph == null) {
+ throw new NullPointerException("graph is null");
+ }
+ if (!graph.getVertexes().contains(src)) {
+ throw new NullPointerException("source node does not exist");
+ }
+ if (!graph.getVertexes().contains(dst)) {
+ throw new NullPointerException("target node does not exist");
+ }
+ if (k <= 0) {
+ throw new NullPointerException("K is negative or 0");
+ }
+ if (weight == null) {
+ throw new NullPointerException("the cost matrix is null");
+ }
+ }
+
+ private void searchKShortestPaths() {
+ // Step 1: find the shortest path.
+ List<E> shortestPath = searchShortestPath(immutableGraph, source, sink);
+ // no path exists, exit.
+ if (shortestPath == null) {
+ return;
+ }
+
+ // Step 2: update the results.
+ pathResults.add(shortestPath);
+ // pathCandidates.add(shortestPath);
+
+ // Step 3: find the other K-1 paths.
+ while (/*pathCandidates.size() > 0 &&*/pathResults.size() < numK) {
+ // 3.1 the spur node ranges from the first node to the last node in the previous k-shortest path.
+ List<E> lastPath = pathResults.get(pathResults.size() - 1);
+ for (int i = 0; i < lastPath.size(); i++) {
+ // 3.1.1 convert the graph into mutable.
+ convertGraph();
+ // 3.1.2 transform the graph.
+ List<E> rootPath = createSpurNode(lastPath, i);
+ transformGraph(rootPath);
+ // 3.1.3 find the deviation node.
+ V devNode;
+ devNode = getDevNode(rootPath);
+ List<E> spurPath;
+ // 3.1.4 find the shortest path in the transformed graph.
+ spurPath = searchShortestPath(mutableGraph, devNode, sink);
+ // 3.1.5 update the path candidates.
+ if (spurPath != null) {
+ // totalPath = rootPath + spurPath;
+ rootPath.addAll(spurPath);
+ pathCandidates.add(rootPath);
+ }
+ }
+ // 3.2 if there is no spur path, exit.
+ if (pathCandidates.size() == 0) {
+ break;
+ }
+ // 3.3 add the path into the results.
+ addPathResult();
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
+ // Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
+ DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
+ Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight, ALL_PATHS).paths();
+ Iterator<Path> itr = paths.iterator();
+ if (!itr.hasNext()) {
+ return null;
+ }
+ // return the first shortest path only.
+ return (List<E>) itr.next().edges();
+ }
+
+ private void convertGraph() {
+ // clear the mutableGraph first
+ if (mutableGraph != null) {
+ ((MutableAdjacencyListsGraph) mutableGraph).clear();
+ }
+
+ // create a immutableGraph
+ Set<E> copyEa = immutableGraph.getEdges();
+ Set<V> copyVa = immutableGraph.getVertexes();
+ for (V vertex : copyVa) {
+ mutableGraph.addVertex(vertex);
+ }
+ for (E edge : copyEa) {
+ mutableGraph.addEdge(edge);
+ }
+ }
+
+ private V getDevNode(List<E> path) {
+ V srcA;
+ V dstB;
+
+ if (path.size() == 0) {
+ return source;
+ }
+
+ E temp1 = path.get(path.size() - 1);
+ srcA = temp1.src();
+ dstB = temp1.dst();
+
+ if (path.size() == 1) {
+ if (srcA.equals(source)) {
+ return dstB;
+ } else {
+ return srcA;
+ }
+ } else {
+ E temp2 = path.get(path.size() - 2);
+ if (srcA.equals(temp2.src()) || srcA.equals(temp2.dst())) {
+ return dstB;
+ } else {
+ return srcA;
+ }
+ }
+ }
+
+ private List<E> createSpurNode(List<E> path, int n) {
+ List<E> root = new ArrayList<E>();
+
+ for (int i = 0; i < n; i++) {
+ root.add(path.get(i));
+ }
+ return root;
+ }
+
+ private void transformGraph(List<E> rootPath) {
+ List<E> prePath;
+ //remove edges
+ for (int i = 0; i < pathResults.size(); i++) {
+ prePath = pathResults.get(i);
+ if (prePath.size() == 1) {
+ mutableGraph.removeEdge(prePath.get(0));
+ } else if (comparePath(rootPath, prePath)) {
+ for (int j = 0; j <= rootPath.size(); j++) {
+ mutableGraph.removeEdge(prePath.get(j));
+ }
+ }
+ }
+ for (int i = 0; i < pathCandidates.size(); i++) {
+ prePath = pathCandidates.get(i);
+ if (prePath.size() == 1) {
+ mutableGraph.removeEdge(prePath.get(0));
+ } else if (comparePath(rootPath, prePath)) {
+ for (int j = 0; j <= rootPath.size(); j++) {
+ mutableGraph.removeEdge(prePath.get(j));
+ }
+ }
+ }
+
+ if (rootPath.size() == 0) {
+ return;
+ }
+
+ //remove nodes
+ List<V> nodes = new ArrayList<V>();
+ nodes.add(source);
+ V pre = source;
+ V srcA;
+ V dstB;
+ for (int i = 0; i < rootPath.size() - 1; i++) {
+ E temp = rootPath.get(i);
+ srcA = temp.src();
+ dstB = temp.dst();
+
+ if (srcA.equals(pre)) {
+ nodes.add(dstB);
+ pre = dstB;
+ } else {
+ nodes.add(srcA);
+ pre = srcA;
+ }
+ }
+ for (int i = 0; i < nodes.size(); i++) {
+ mutableGraph.removeVertex(nodes.get(i));
+ }
+ }
+
+ private boolean comparePath(List<E> path1, List<E> path2) {
+ if (path1.size() > path2.size()) {
+ return false;
+ }
+ if (path1.size() == 0) {
+ return true;
+ }
+ for (int i = 0; i < path1.size(); i++) {
+ if (path1.get(i) != path2.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void addPathResult() {
+ List<E> sp;
+ sp = pathCandidates.get(0);
+ for (int i = 1; i < pathCandidates.size(); i++) {
+ if (sp.size() > pathCandidates.get(i).size()) {
+ sp = pathCandidates.get(i);
+ }
+ }
+ pathResults.add(sp);
+ // Log.info(sp.toString());
+ pathCandidates.remove(sp);
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java
new file mode 100644
index 00000000..87571c4b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableAdjacencyListsGraph.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+
+public class MutableAdjacencyListsGraph<V extends Vertex, E extends Edge<V>>
+implements MutableGraph<V, E> {
+ private Set<V> vertexes = new HashSet<V>();
+ private Set<E> edges = new HashSet<E>();
+
+ private SetMultimap<V, E> sources = HashMultimap.create();
+ private SetMultimap<V, E> destinations = HashMultimap.create();
+
+ /**
+ * Creates a graph comprising of the specified vertexes and edges.
+ *
+ * @param vertex set of graph vertexes
+ * @param edge set of graph edges
+ */
+ public MutableAdjacencyListsGraph(Set<V> vertex, Set<E> edge) {
+ vertexes.addAll(vertex);
+ edges.addAll(edge);
+ for (E e : edge) {
+ sources.put(e.src(), e);
+ vertexes.add(e.src());
+ destinations.put(e.dst(), e);
+ vertexes.add(e.dst());
+ }
+ }
+
+ @Override
+ public Set<V> getVertexes() {
+ return vertexes;
+ }
+
+ @Override
+ public Set<E> getEdges() {
+ return edges;
+ }
+
+ @Override
+ public Set<E> getEdgesFrom(V src) {
+ return sources.get(src);
+ }
+
+ @Override
+ public Set<E> getEdgesTo(V dst) {
+ return destinations.get(dst);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MutableAdjacencyListsGraph) {
+ MutableAdjacencyListsGraph that = (MutableAdjacencyListsGraph) obj;
+ return this.getClass() == that.getClass() &&
+ Objects.equals(this.vertexes, that.vertexes) &&
+ Objects.equals(this.edges, that.edges);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(vertexes, edges);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("vertexes", vertexes)
+ .add("edges", edges)
+ .toString();
+ }
+
+
+ @Override
+ public void addVertex(V vertex) {
+ if (vertexes != null) {
+ if (!vertexes.contains(vertex)) {
+ vertexes.add(vertex);
+ }
+ }
+ }
+
+ @Override
+ public void removeVertex(V vertex) {
+ if (vertexes != null && edges != null) {
+ if (vertexes.contains(vertex)) {
+ vertexes.remove(vertex);
+ Set<E> srcEdgesList = sources.get(vertex);
+ Set<E> dstEdgesList = destinations.get(vertex);
+ edges.removeAll(srcEdgesList);
+ edges.removeAll(dstEdgesList);
+ sources.remove(vertex, srcEdgesList);
+ sources.remove(vertex, dstEdgesList);
+ }
+ }
+ }
+
+ @Override
+ public void addEdge(E edge) {
+ if (edges != null) {
+ if (!edges.contains(edge)) {
+ edges.add(edge);
+ sources.put(edge.src(), edge);
+ destinations.put(edge.dst(), edge);
+ }
+ }
+ }
+
+ @Override
+ public void removeEdge(E edge) {
+ if (edges != null) {
+ if (edges.contains(edge)) {
+ edges.remove(edge);
+ sources.remove(edge.src(), edge);
+ destinations.remove(edge.dst(), edge);
+ }
+ }
+ }
+
+ @Override
+ public Graph<V, E> toImmutable() {
+ return null;
+ }
+
+ /**
+ * Clear the graph.
+ */
+ public void clear() {
+ edges.clear();
+ vertexes.clear();
+ sources.clear();
+ destinations.clear();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableGraph.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableGraph.java
new file mode 100644
index 00000000..bd2b600c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutableGraph.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Abstraction of a mutable graph that can be constructed gradually.
+ */
+public interface MutableGraph<V extends Vertex, E extends Edge> extends Graph<V, E> {
+
+ /**
+ * Adds the specified vertex to this graph.
+ *
+ * @param vertex new vertex
+ */
+ void addVertex(V vertex);
+
+ /**
+ * Removes the specified vertex from the graph.
+ *
+ * @param vertex vertex to be removed
+ */
+ void removeVertex(V vertex);
+
+ /**
+ * Adds the specified edge to this graph. If the edge vertexes are not
+ * already in the graph, they will be added as well.
+ *
+ * @param edge new edge
+ */
+ void addEdge(E edge);
+
+ /**
+ * Removes the specified edge from the graph.
+ *
+ * @param edge edge to be removed
+ */
+ void removeEdge(E edge);
+
+ /**
+ * Returns an immutable copy of this graph.
+ *
+ * @return immutable copy
+ */
+ Graph<V, E> toImmutable();
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutablePath.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutablePath.java
new file mode 100644
index 00000000..5cd8fd0e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/MutablePath.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Abstraction of a mutable path that allows gradual construction.
+ */
+public interface MutablePath<V extends Vertex, E extends Edge<V>> extends Path<V, E> {
+
+ /**
+ * Inserts a new edge at the beginning of this path. The edge must be
+ * adjacent to the prior start of the path.
+ *
+ * @param edge edge to be inserted
+ */
+ void insertEdge(E edge);
+
+ /**
+ * Appends a new edge at the end of the this path. The edge must be
+ * adjacent to the prior end of the path.
+ *
+ * @param edge edge to be inserted
+ */
+ void appendEdge(E edge);
+
+ /**
+ * Removes the specified edge. This edge must be either at the start or
+ * at the end of the path, or it must be a cyclic edge in order not to
+ * violate the contiguous path property.
+ *
+ * @param edge edge to be removed
+ */
+ void removeEdge(E edge);
+
+ /**
+ * Sets the total path cost as a unit-less double.
+ *
+ * @param cost new path cost
+ */
+ void setCost(double cost);
+
+ /**
+ * Returns an immutable copy of this path.
+ *
+ * @return immutable copy
+ */
+ Path<V, E> toImmutable();
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Path.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Path.java
new file mode 100644
index 00000000..ed9aa2c9
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Path.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.List;
+
+/**
+ * Representation of a path in a graph as a sequence of edges. Paths are
+ * assumed to be continuous, where adjacent edges must share a vertex.
+ *
+ * @param <V> vertex type
+ * @param <E> edge type
+ */
+public interface Path<V extends Vertex, E extends Edge<V>> extends Edge<V> {
+
+ /**
+ * Returns the list of edges comprising the path. Adjacent edges will
+ * share the same vertex, meaning that a source of one edge, will be the
+ * same as the destination of the prior edge.
+ *
+ * @return list of path edges
+ */
+ List<E> edges();
+
+ /**
+ * Returns the total cost of the path as a unit-less number.
+ *
+ * @return path cost as a unit-less number
+ */
+ double cost();
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SRLGGraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SRLGGraphSearch.java
new file mode 100644
index 00000000..21f687a3
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SRLGGraphSearch.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.onlab.graph;
+
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Random;
+
+
+/**
+ * SRLG Graph Search finds a pair of paths with disjoint risk groups; i.e
+ * if one path goes through an edge in risk group 1, the other path will go
+ * through no edges in risk group 1.
+ */
+public class SRLGGraphSearch<V extends Vertex, E extends Edge<V>>
+ extends AbstractGraphPathSearch<V, E> {
+
+ static final int ITERATIONS = 100;
+ static final int POPSIZE = 50;
+
+ boolean useSuurballe = false;
+
+ static final double INF = 100000000.0;
+
+ int numGroups;
+ Map<E, Integer> riskGrouping;
+
+ Graph<V, E> orig;
+ V src, dst;
+ EdgeWeight<V, E> weight;
+
+ /**
+ * Creates an SRLG graph search object with the given number
+ * of groups and given risk mapping.
+ *
+ * @param groups the number of disjoint risk groups
+ * @param grouping map linking edges to integral group assignments
+ */
+ public SRLGGraphSearch(int groups, Map<E, Integer> grouping) {
+ numGroups = groups;
+ riskGrouping = grouping;
+ }
+
+ /**
+ * Creates an SRLG graph search object from a map, inferring
+ * the number of groups and creating an integral mapping.
+ *
+ * @param grouping map linking edges to object group assignments,
+ * with same-group status linked to equality
+ */
+ public SRLGGraphSearch(Map<E, Object> grouping) {
+ if (grouping == null) {
+ useSuurballe = true;
+ return;
+ }
+ numGroups = 0;
+ HashMap<Object, Integer> tmpMap = new HashMap<>();
+ riskGrouping = new HashMap<>();
+ for (E key: grouping.keySet()) {
+ Object value = grouping.get(key);
+ if (!tmpMap.containsKey(value)) {
+ tmpMap.put(value, numGroups);
+ numGroups++;
+ }
+ riskGrouping.put(key, tmpMap.get(value));
+ }
+ }
+
+ @Override
+ public Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+ if (maxPaths == ALL_PATHS) {
+ maxPaths = POPSIZE;
+ }
+ if (useSuurballe) {
+ return new SuurballeGraphSearch<V, E>().search(graph, src, dst, weight, ALL_PATHS);
+ }
+ if (weight == null) {
+ weight = edge -> 1;
+ }
+ checkArguments(graph, src, dst);
+ orig = graph;
+ this.src = src;
+ this.dst = dst;
+ this.weight = weight;
+ List<Subset> best = new GAPopulation<Subset>()
+ .runGA(ITERATIONS, POPSIZE, maxPaths, new Subset(new boolean[numGroups]));
+ Set<DisjointPathPair> dpps = new HashSet<DisjointPathPair>();
+ for (Subset s: best) {
+ dpps.addAll(s.buildPaths());
+ }
+ Result<V, E> firstDijkstra = new DijkstraGraphSearch<V, E>()
+ .search(orig, src, dst, weight, 1);
+ return new Result<V, E>() {
+ final DefaultResult search = (DefaultResult) firstDijkstra;
+
+ public V src() {
+ return src;
+ }
+ public V dst() {
+ return dst;
+
+ }
+ public Set<Path<V, E>> paths() {
+ Set<Path<V, E>> pathsD = new HashSet<>();
+ for (DisjointPathPair<V, E> path: dpps) {
+ pathsD.add(path);
+ }
+ return pathsD;
+ }
+ public Map<V, Double> costs() {
+ return search.costs();
+
+ }
+ public Map<V, Set<E>> parents() {
+ return search.parents();
+
+ }
+ };
+ }
+
+ //finds the shortest path in the graph given a subset of edge types to use
+ private Result<V, E> findShortestPathFromSubset(boolean[] subset) {
+ Graph<V, E> graph = orig;
+ EdgeWeight<V, E> modified = new EdgeWeight<V, E>() {
+ final boolean[] subsetF = subset;
+
+ @Override
+ public double weight(E edge) {
+ if (subsetF[riskGrouping.get(edge)]) {
+ return weight.weight(edge);
+ }
+ return INF;
+ }
+ };
+
+ Result<V, E> res = new DijkstraGraphSearch<V, E>().search(graph, src, dst, modified, 1);
+ return res;
+ }
+ /**
+ * A subset is a type of GA organism that represents a subset of allowed shortest
+ * paths (and its complement). Its fitness is determined by the sum of the weights
+ * of the first two shortest paths.
+ */
+ class Subset implements GAOrganism {
+
+ boolean[] subset;
+ boolean[] not;
+ Random r = new Random();
+
+ /**
+ * Creates a Subset from the given subset array.
+ *
+ * @param sub subset array
+ */
+ public Subset(boolean[] sub) {
+ subset = sub.clone();
+ not = new boolean[subset.length];
+ for (int i = 0; i < subset.length; i++) {
+ not[i] = !subset[i];
+ }
+ }
+
+ @Override
+ public double fitness() {
+ Set<Path<V, E>> paths1 = findShortestPathFromSubset(subset).paths();
+ Set<Path<V, E>> paths2 = findShortestPathFromSubset(not).paths();
+ if (paths1.size() == 0 || paths2.size() == 0) {
+ return INF;
+ }
+ return paths1.iterator().next().cost() + paths2.iterator().next().cost();
+ }
+
+ @Override
+ public void mutate() {
+ int turns = r.nextInt((int) Math.sqrt(subset.length));
+ while (turns > 0) {
+ int choose = r.nextInt(subset.length);
+ subset[choose] = !subset[choose];
+ not[choose] = !not[choose];
+ turns--;
+ }
+ }
+
+ @Override
+ public GAOrganism crossWith(GAOrganism org) {
+ if (!(org.getClass().equals(getClass()))) {
+ return this;
+ }
+ Subset other = (Subset) (org);
+ boolean[] sub = new boolean[subset.length];
+ for (int i = 0; i < subset.length; i++) {
+ sub[i] = subset[i];
+ if (r.nextBoolean()) {
+ sub[i] = other.subset[i];
+ }
+ }
+ return new Subset(sub);
+ }
+
+ @Override
+ public GAOrganism random() {
+ boolean[] sub = new boolean[subset.length];
+ for (int i = 0; i < sub.length; i++) {
+ sub[i] = r.nextBoolean();
+ }
+ return new Subset(sub);
+ }
+
+ /**
+ * Builds the set of disjoint path pairs for a given subset
+ * using Dijkstra's algorithm on both the subset and complement
+ * and returning all pairs with one from each set.
+ *
+ * @return all shortest disjoint paths given this subset
+ */
+ public Set<DisjointPathPair> buildPaths() {
+ Set<DisjointPathPair> dpps = new HashSet<>();
+ for (Path<V, E> path1: findShortestPathFromSubset(subset).paths()) {
+ if (path1.cost() >= INF) {
+ continue;
+ }
+ for (Path<V, E> path2: findShortestPathFromSubset(not).paths()) {
+ if (path2.cost() >= INF) {
+ continue;
+ }
+ DisjointPathPair<V, E> dpp = new DisjointPathPair<>(path1, path2);
+ dpps.add(dpp);
+ }
+ }
+ return dpps;
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SuurballeGraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SuurballeGraphSearch.java
new file mode 100644
index 00000000..76591c82
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/SuurballeGraphSearch.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.graph;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.stream.Collectors;
+
+/**
+ * Suurballe shortest-path graph search algorithm capable of finding both
+ * a shortest path, as well as a backup shortest path, between a source and a destination
+ * such that the sum of the path lengths is minimized.
+ */
+public class SuurballeGraphSearch<V extends Vertex, E extends Edge<V>> extends DijkstraGraphSearch<V, E> {
+
+ @Override
+ public Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths) {
+
+ if (weight == null) {
+ weight = edge -> 1;
+ }
+
+ List<DisjointPathPair<V, E>> dpps = new ArrayList<>();
+
+ final EdgeWeight weightf = weight;
+ DefaultResult firstDijkstraS = (DefaultResult) super.search(graph, src, dst, weight, ALL_PATHS);
+ DefaultResult firstDijkstra = (DefaultResult) super.search(graph, src, null, weight, ALL_PATHS);
+
+ //choose an arbitrary shortest path to run Suurballe on
+ Path<V, E> shortPath = null;
+ if (firstDijkstraS.paths().size() == 0) {
+ return firstDijkstraS;
+ }
+ for (Path p: firstDijkstraS.paths()) {
+ shortPath = p;
+ //transforms the graph so tree edges have 0 weight
+ EdgeWeight<V, Edge<V>> modified = edge -> {
+ if (classE().isInstance(edge)) {
+ return weightf.weight((E) (edge)) + firstDijkstra.cost(edge.src())
+ - firstDijkstra.cost(edge.dst());
+ }
+ return 0;
+ };
+ EdgeWeight<V, E> modified2 = edge ->
+ weightf.weight(edge) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst());
+
+ //create a residual graph g' by removing all src vertices and reversing 0 length path edges
+ MutableGraph<V, Edge<V>> gt = mutableCopy(graph);
+
+ Map<Edge<V>, E> revToEdge = new HashMap<>();
+ graph.getEdgesTo(src).forEach(gt::removeEdge);
+ for (E edge: shortPath.edges()) {
+ gt.removeEdge(edge);
+ Edge<V> reverse = new Edge<V>() {
+ final Edge<V> orig = edge;
+ public V src() {
+ return orig.dst();
+ }
+ public V dst() {
+ return orig.src();
+ }
+ public String toString() {
+ return "ReversedEdge " + "src=" + src() + " dst=" + dst();
+ }
+ };
+ revToEdge.put(reverse, edge);
+ gt.addEdge(reverse);
+ }
+
+ //rerun dijkstra on the temporary graph to get a second path
+ Result<V, Edge<V>> secondDijkstra;
+ secondDijkstra = new DijkstraGraphSearch<V, Edge<V>>().search(gt, src, dst, modified, ALL_PATHS);
+
+ Path<V, Edge<V>> residualShortPath = null;
+ if (secondDijkstra.paths().size() == 0) {
+ dpps.add(new DisjointPathPair<V, E>(shortPath, null));
+ continue;
+ }
+
+ for (Path p2: secondDijkstra.paths()) {
+ residualShortPath = p2;
+
+ MutableGraph<V, E> roundTrip = mutableCopy(graph);
+
+ List<E> tmp = roundTrip.getEdges().stream().collect(Collectors.toList());
+
+ tmp.forEach(roundTrip::removeEdge);
+
+ shortPath.edges().forEach(roundTrip::addEdge);
+
+ if (residualShortPath != null) {
+ for (Edge<V> edge: residualShortPath.edges()) {
+ if (classE().isInstance(edge)) {
+ roundTrip.addEdge((E) edge);
+ } else {
+ roundTrip.removeEdge(revToEdge.get(edge));
+ }
+ }
+ }
+ //Actually build the final result
+ DefaultResult lastSearch = (DefaultResult) super.search(roundTrip, src, dst, weight, ALL_PATHS);
+ Path<V, E> path1 = lastSearch.paths().iterator().next();
+ path1.edges().forEach(roundTrip::removeEdge);
+
+ Set<Path<V, E>> bckpaths = super.search(roundTrip, src, dst, weight, ALL_PATHS).paths();
+ Path<V, E> backup = null;
+ if (bckpaths.size() != 0) {
+ backup = bckpaths.iterator().next();
+ }
+
+ dpps.add(new DisjointPathPair<>(path1, backup));
+ }
+ }
+
+ for (int i = dpps.size() - 1; i > 0; i--) {
+ if (dpps.get(i).size() <= 1) {
+ dpps.remove(i);
+ }
+ }
+
+ return new Result<V, E>() {
+ final DefaultResult search = firstDijkstra;
+
+ public V src() {
+ return src;
+ }
+ public V dst() {
+ return dst;
+ }
+ public Set<Path<V, E>> paths() {
+ Set<Path<V, E>> pathsD = new HashSet<>();
+ int paths = 0;
+ for (DisjointPathPair<V, E> path: dpps) {
+ pathsD.add((Path<V, E>) path);
+ paths++;
+ if (paths == maxPaths) {
+ break;
+ }
+ }
+ return pathsD;
+ }
+ public Map<V, Double> costs() {
+ return search.costs();
+ }
+ public Map<V, Set<E>> parents() {
+ return search.parents();
+ }
+ };
+ }
+
+ private Class<?> clazzV;
+
+ public Class<?> classV() {
+ return clazzV;
+ }
+
+ private Class<?> clazzE;
+
+ public Class<?> classE() {
+ return clazzE;
+ }
+ /**
+ * Creates a mutable copy of an immutable graph.
+ *
+ * @param graph immutable graph
+ * @return mutable copy
+ */
+ public MutableGraph mutableCopy(Graph<V, E> graph) {
+ clazzV = graph.getVertexes().iterator().next().getClass();
+ clazzE = graph.getEdges().iterator().next().getClass();
+ return new MutableAdjacencyListsGraph<V, E>(graph.getVertexes(), graph.getEdges());
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java
new file mode 100644
index 00000000..5bf305e6
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tarjan algorithm for searching a graph and producing results describing
+ * the graph SCC (strongly-connected components).
+ */
+public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>>
+ implements GraphSearch<V, E> {
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation produces results augmented with information on
+ * SCCs within the graph.
+ * </p>
+ * <p>
+ * To prevent traversal of an edge, the {@link EdgeWeight#weight} should
+ * return a negative value as an edge weight.
+ * </p>
+ */
+ @Override
+ public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) {
+ SCCResult<V, E> result = new SCCResult<>(graph);
+ for (V vertex : graph.getVertexes()) {
+ VertexData data = result.data(vertex);
+ if (data == null) {
+ connect(graph, vertex, weight, result);
+ }
+ }
+ return result.build();
+ }
+
+ /**
+ * Scans the specified graph, using recursion, and produces SCC results.
+ *
+ * @param graph graph to search
+ * @param vertex current vertex to scan and connect
+ * @param weight optional edge weight
+ * @param result graph search result
+ * @return augmentation vertexData for the current vertex
+ */
+ private VertexData<V> connect(Graph<V, E> graph, V vertex,
+ EdgeWeight<V, E> weight,
+ SCCResult<V, E> result) {
+ VertexData<V> data = result.addData(vertex);
+
+ // Scan through all egress edges of the current vertex.
+ for (E edge : graph.getEdgesFrom(vertex)) {
+ V nextVertex = edge.dst();
+
+ // If edge weight is negative, skip it.
+ if (weight != null && weight.weight(edge) < 0) {
+ continue;
+ }
+
+ // Attempt to get the augmentation vertexData for the next vertex.
+ VertexData<V> nextData = result.data(nextVertex);
+ if (nextData == null) {
+ // Next vertex has not been visited yet, so do this now.
+ nextData = connect(graph, nextVertex, weight, result);
+ data.lowLink = Math.min(data.lowLink, nextData.lowLink);
+
+ } else if (result.visited(nextData)) {
+ // Next vertex has been visited, which means it is in the
+ // same cluster as the current vertex.
+ data.lowLink = Math.min(data.lowLink, nextData.index);
+ }
+ }
+
+ if (data.lowLink == data.index) {
+ result.addCluster(data);
+ }
+ return data;
+ }
+
+ /**
+ * Graph search result augmented with SCC vertexData.
+ */
+ public static final class SCCResult<V extends Vertex, E extends Edge<V>>
+ implements Result {
+
+ private final Graph<V, E> graph;
+ private List<Set<V>> clusterVertexes = new ArrayList<>();
+ private List<Set<E>> clusterEdges = new ArrayList<>();
+
+ private int index = 0;
+ private final Map<V, VertexData<V>> vertexData = new HashMap<>();
+ private final List<VertexData<V>> visited = new ArrayList<>();
+
+ private SCCResult(Graph<V, E> graph) {
+ this.graph = graph;
+ }
+
+ /**
+ * Returns the number of SCC clusters in the graph.
+ *
+ * @return number of clusters
+ */
+ public int clusterCount() {
+ return clusterEdges.size();
+ }
+
+ /**
+ * Returns the list of strongly connected vertex clusters.
+ *
+ * @return list of strongly connected vertex sets
+ */
+ public List<Set<V>> clusterVertexes() {
+ return clusterVertexes;
+ }
+
+ /**
+ * Returns the list of edges linking strongly connected vertex clusters.
+ *
+ * @return list of strongly connected edge sets
+ */
+ public List<Set<E>> clusterEdges() {
+ return clusterEdges;
+ }
+
+ // Gets the augmentation vertexData for the specified vertex
+ private VertexData<V> data(V vertex) {
+ return vertexData.get(vertex);
+ }
+
+ // Adds augmentation vertexData for the specified vertex
+ private VertexData<V> addData(V vertex) {
+ VertexData<V> d = new VertexData<>(vertex, index);
+ vertexData.put(vertex, d);
+ visited.add(0, d);
+ index++;
+ return d;
+ }
+
+ // Indicates whether the given vertex has been visited
+ private boolean visited(VertexData data) {
+ return visited.contains(data);
+ }
+
+ // Adds a new cluster for the specified vertex
+ private void addCluster(VertexData data) {
+ Set<V> vertexes = findClusterVertices(data);
+ clusterVertexes.add(vertexes);
+ clusterEdges.add(findClusterEdges(vertexes));
+ }
+
+ private Set<V> findClusterVertices(VertexData data) {
+ VertexData<V> nextVertexData;
+ Set<V> vertexes = new HashSet<>();
+ do {
+ nextVertexData = visited.remove(0);
+ vertexes.add(nextVertexData.vertex);
+ } while (data != nextVertexData);
+ return Collections.unmodifiableSet(vertexes);
+ }
+
+ private Set<E> findClusterEdges(Set<V> vertexes) {
+ Set<E> edges = new HashSet<>();
+ for (V vertex : vertexes) {
+ for (E edge : graph.getEdgesFrom(vertex)) {
+ if (vertexes.contains((edge.dst()))) {
+ edges.add(edge);
+ }
+ }
+ }
+ return Collections.unmodifiableSet(edges);
+ }
+
+ public SCCResult<V, E> build() {
+ clusterVertexes = Collections.unmodifiableList(clusterVertexes);
+ clusterEdges = Collections.unmodifiableList(clusterEdges);
+ return this;
+ }
+ }
+
+ // Augments the vertex to assist in determining SCC clusters.
+ private static final class VertexData<V extends Vertex> {
+ final V vertex;
+ int index;
+ int lowLink;
+
+ private VertexData(V vertex, int index) {
+ this.vertex = vertex;
+ this.index = index;
+ this.lowLink = index;
+ }
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Vertex.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Vertex.java
new file mode 100644
index 00000000..80e3e7df
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/Vertex.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.graph;
+
+/**
+ * Representation of a graph vertex.
+ */
+public interface Vertex {
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/package-info.java
new file mode 100644
index 00000000..2ee949d2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Graph abstractions and graph path finding algorithms.
+ */
+package org.onlab.graph;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java
new file mode 100644
index 00000000..d20ca53f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+
+/**
+ * Metric measurements for events.
+ */
+public class EventMetric {
+ private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
+ private static final String METER_RATE_NAME = "Rate";
+
+ private final MetricsService metricsService;
+ private final String componentName;
+ private final String featureName;
+
+ private MetricsComponent metricsComponent;
+ private MetricsFeature metricsFeature;
+
+ private volatile long lastEventTimestampEpochMs = 0;
+ private Gauge<Long> lastEventTimestampGauge;
+ private Meter eventRateMeter;
+
+ /**
+ * Constructor.
+ *
+ * @param metricsService the Metrics Service to use for Metrics
+ * registration and deregistration
+ * @param componentName the Metrics Component Name to use for Metrics
+ * registration and deregistration
+ * @param featureName the Metrics Feature Name to use for Metrics
+ * registration and deregistration
+ */
+ public EventMetric(MetricsService metricsService, String componentName,
+ String featureName) {
+ this.metricsService = metricsService;
+ this.componentName = componentName;
+ this.featureName = featureName;
+ }
+
+ /**
+ * Registers the metrics.
+ */
+ public void registerMetrics() {
+ metricsComponent = metricsService.registerComponent(componentName);
+ metricsFeature = metricsComponent.registerFeature(featureName);
+
+ lastEventTimestampEpochMs = 0;
+ lastEventTimestampGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeature,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return lastEventTimestampEpochMs;
+ }
+ });
+
+ eventRateMeter = metricsService.createMeter(metricsComponent,
+ metricsFeature,
+ METER_RATE_NAME);
+ }
+
+ /**
+ * Removes the metrics.
+ */
+ public void removeMetrics() {
+ lastEventTimestampEpochMs = 0;
+ metricsService.removeMetric(metricsComponent,
+ metricsFeature,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeature,
+ METER_RATE_NAME);
+ }
+
+ /**
+ * Updates the metric measurements for a single event.
+ */
+ public void eventReceived() {
+ lastEventTimestampEpochMs = System.currentTimeMillis();
+ eventRateMeter.mark(1);
+ }
+
+ /**
+ * Gets the last event timestamp Gauge (ms from the Epoch).
+ *
+ * @return the last event timestamp Gauge (ms from the Epoch)
+ */
+ public Gauge<Long> lastEventTimestampGauge() {
+ return lastEventTimestampGauge;
+ }
+
+ /**
+ * Gets the event rate meter.
+ *
+ * @return the event rate meter
+ */
+ public Meter eventRateMeter() {
+ return eventRateMeter;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java
new file mode 100644
index 00000000..cc8fe83c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Components to register for metrics.
+ */
+public class MetricsComponent implements MetricsComponentRegistry {
+ private final String name;
+
+ /**
+ * Registry to hold the Features defined in this Component.
+ */
+ private final ConcurrentMap<String, MetricsFeature> featuresRegistry =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Constructs a component from a name.
+ *
+ * @param newName name of the component
+ */
+ MetricsComponent(final String newName) {
+ name = newName;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public MetricsFeature registerFeature(final String featureName) {
+ MetricsFeature feature = featuresRegistry.get(featureName);
+ if (feature == null) {
+ final MetricsFeature createdFeature =
+ new MetricsFeature(featureName);
+ feature = featuresRegistry.putIfAbsent(featureName, createdFeature);
+ if (feature == null) {
+ feature = createdFeature;
+ }
+ }
+ return feature;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java
new file mode 100644
index 00000000..89f6ec5e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+/**
+ * Registry Entry for Metrics Components.
+ */
+public interface MetricsComponentRegistry {
+ /**
+ * Fetches the name of the Component.
+ *
+ * @return name of the Component
+ */
+ String getName();
+
+ /**
+ * Registers a Feature for this component.
+ *
+ * @param featureName name of the Feature to register
+ * @return Feature object that can be used when creating Metrics
+ */
+ MetricsFeature registerFeature(String featureName);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
new file mode 100644
index 00000000..bc6753c9
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+/**
+ * Features to tag metrics.
+ */
+public class MetricsFeature {
+ private final String name;
+
+ /**
+ * Constructs a Feature from a name.
+ *
+ * @param newName name of the Feature
+ */
+ public MetricsFeature(final String newName) {
+ name = newName;
+ }
+
+ /**
+ * Fetches the name of the Feature.
+ *
+ * @return name of the Feature
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
new file mode 100644
index 00000000..6c9314ad
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+
+/**
+ * This class holds the Metrics registry for ONOS.
+ * All metrics (Counter, Histogram, Timer, Meter, Gauge) use a hierarchical
+ * string-based naming scheme: COMPONENT.FEATURE.NAME.
+ * Example: "Topology.Counters.TopologyUpdates".
+ * The COMPONENT and FEATURE names have to be registered in advance before
+ * a metric can be created. Example:
+ * <pre>
+ * <code>
+ * private final MetricsManager.MetricsComponent COMPONENT =
+ * MetricsManager.registerComponent("Topology");
+ * private final MetricsManager.MetricsFeature FEATURE =
+ * COMPONENT.registerFeature("Counters");
+ * private final Counter counterTopologyUpdates =
+ * MetricsManager.createCounter(COMPONENT, FEATURE, "TopologyUpdates");
+ * </code>
+ * </pre>
+ * Gauges are slightly different because they are not created directly in
+ * this class, but are allocated by the caller and passed in for registration:
+ * <pre>
+ * <code>
+ * private final Gauge&lt;Long&gt; gauge =
+ * new {@literal Gauge&lt;Long&gt}() {
+ * {@literal @}Override
+ * public Long getValue() {
+ * return gaugeValue;
+ * }
+ * };
+ * MetricsManager.registerMetric(COMPONENT, FEATURE, GAUGE_NAME, gauge);
+ * </code>
+ * </pre>
+ */
+public class MetricsManager implements MetricsService {
+
+ /**
+ * Registry to hold the Components defined in the system.
+ */
+ private ConcurrentMap<String, MetricsComponent> componentsRegistry =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Registry for the Metrics objects created in the system.
+ */
+ private MetricRegistry metricsRegistry = new MetricRegistry();
+
+ /**
+ * Clears the internal state.
+ */
+ protected void clear() {
+ this.componentsRegistry = new ConcurrentHashMap<>();
+ this.metricsRegistry = new MetricRegistry();
+ }
+
+ /**
+ * Registers a component.
+ *
+ * @param name name of the Component to register
+ * @return MetricsComponent object that can be used to create Metrics.
+ */
+ @Override
+ public MetricsComponent registerComponent(final String name) {
+ MetricsComponent component = componentsRegistry.get(name);
+ if (component == null) {
+ final MetricsComponent createdComponent =
+ new MetricsComponent(name);
+ component = componentsRegistry.putIfAbsent(name, createdComponent);
+ if (component == null) {
+ component = createdComponent;
+ }
+ }
+ return component;
+ }
+
+ /**
+ * Generates a name for a Metric from its component and feature.
+ *
+ * @param component component the metric is defined in
+ * @param feature feature the metric is defined in
+ * @param metricName local name of the metric
+ *
+ * @return full name of the metric
+ */
+ private String generateName(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ return MetricRegistry.name(component.getName(),
+ feature.getName(),
+ metricName);
+ }
+
+ /**
+ * Creates a Counter metric.
+ *
+ * @param component component the Counter is defined in
+ * @param feature feature the Counter is defined in
+ * @param metricName local name of the metric
+ * @return the created Counter Meteric
+ */
+ @Override
+ public Counter createCounter(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.counter(name);
+ }
+
+ /**
+ * Creates a Histogram metric.
+ *
+ * @param component component the Histogram is defined in
+ * @param feature feature the Histogram is defined in
+ * @param metricName local name of the metric
+ * @return the created Histogram Metric
+ */
+ @Override
+ public Histogram createHistogram(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.histogram(name);
+ }
+
+ /**
+ * Creates a Timer metric.
+ *
+ * @param component component the Timer is defined in
+ * @param feature feature the Timer is defined in
+ * @param metricName local name of the metric
+ * @return the created Timer Metric
+ */
+ @Override
+ public Timer createTimer(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.timer(name);
+ }
+
+ /**
+ * Creates a Meter metric.
+ *
+ * @param component component the Meter is defined in
+ * @param feature feature the Meter is defined in
+ * @param metricName local name of the metric
+ * @return the created Meter Metric
+ */
+ @Override
+ public Meter createMeter(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.meter(name);
+ }
+
+ /**
+ * Registers an already created Metric. This is used for situation where a
+ * caller needs to allocate its own Metric, but still register it with the
+ * system.
+ *
+ * @param <T> Metric type
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @param metric Metric to register
+ * @return the registered Metric
+ */
+ @Override
+ public <T extends Metric> T registerMetric(
+ final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName,
+ final T metric) {
+ final String name = generateName(component, feature, metricName);
+ metricsRegistry.register(name, metric);
+ return metric;
+ }
+
+ /**
+ * Removes the metric with the given name.
+ *
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @return true if the metric existed and was removed, otherwise false
+ */
+ @Override
+ public boolean removeMetric(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.remove(name);
+ }
+
+ /**
+ * Fetches the existing Timers.
+ *
+ * @param filter filter to use to select Timers
+ * @return a map of the Timers that match the filter, with the key as the
+ * name String to the Timer.
+ */
+ @Override
+ public Map<String, Timer> getTimers(final MetricFilter filter) {
+ return metricsRegistry.getTimers(filter);
+ }
+
+ /**
+ * Fetches the existing Gauges.
+ *
+ * @param filter filter to use to select Gauges
+ * @return a map of the Gauges that match the filter, with the key as the
+ * name String to the Gauge.
+ */
+ @Override
+ public Map<String, Gauge> getGauges(final MetricFilter filter) {
+ return metricsRegistry.getGauges(filter);
+ }
+
+ /**
+ * Fetches the existing Counters.
+ *
+ * @param filter filter to use to select Counters
+ * @return a map of the Counters that match the filter, with the key as the
+ * name String to the Counter.
+ */
+ @Override
+ public Map<String, Counter> getCounters(final MetricFilter filter) {
+ return metricsRegistry.getCounters(filter);
+ }
+
+ /**
+ * Fetches the existing Meters.
+ *
+ * @param filter filter to use to select Meters
+ * @return a map of the Meters that match the filter, with the key as the
+ * name String to the Meter.
+ */
+ @Override
+ public Map<String, Meter> getMeters(final MetricFilter filter) {
+ return metricsRegistry.getMeters(filter);
+ }
+
+ /**
+ * Fetches the existing Histograms.
+ *
+ * @param filter filter to use to select Histograms
+ * @return a map of the Histograms that match the filter, with the key as
+ * the name String to the Histogram.
+ */
+ @Override
+ public Map<String, Histogram> getHistograms(final MetricFilter filter) {
+ return metricsRegistry.getHistograms(filter);
+ }
+
+ /**
+ * Removes all Metrics that match a given filter.
+ *
+ * @param filter filter to use to select the Metrics to remove.
+ */
+ @Override
+ public void removeMatching(final MetricFilter filter) {
+ metricsRegistry.removeMatching(filter);
+ }
+
+ /**
+ * Fetches the existing Meters.
+ *
+ *
+ * @return a map of all metrics with the key as the
+ * name String to the Meter.
+ */
+ public Map<String, Metric> getMetrics() {
+ return metricsRegistry.getMetrics();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
new file mode 100644
index 00000000..4f0d67a8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.metrics;
+
+import java.util.Map;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.Timer;
+
+/**
+ * Metrics Service to collect metrics.
+ */
+public interface MetricsService {
+
+ /**
+ * Registers a component.
+ *
+ * @param name name of the Component to register
+ * @return MetricsComponent object that can be used to create Metrics.
+ */
+ MetricsComponent registerComponent(String name);
+
+ /**
+ * Creates a Counter metric.
+ *
+ * @param component component the Counter is defined in
+ * @param feature feature the Counter is defined in
+ * @param metricName local name of the metric
+ * @return the created Counter Meteric
+ */
+ Counter createCounter(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Histogram metric.
+ *
+ * @param component component the Histogram is defined in
+ * @param feature feature the Histogram is defined in
+ * @param metricName local name of the metric
+ * @return the created Histogram Metric
+ */
+ Histogram createHistogram(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Timer metric.
+ *
+ * @param component component the Timer is defined in
+ * @param feature feature the Timer is defined in
+ * @param metricName local name of the metric
+ * @return the created Timer Metric
+ */
+ Timer createTimer(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Meter metric.
+ *
+ * @param component component the Meter is defined in
+ * @param feature feature the Meter is defined in
+ * @param metricName local name of the metric
+ * @return the created Meter Metric
+ */
+ Meter createMeter(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Registers an already created Metric. This is used for situation where a
+ * caller needs to allocate its own Metric, but still register it with the
+ * system.
+ *
+ * @param <T> Metric type
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @param metric Metric to register
+ * @return the registered Metric
+ */
+ <T extends Metric> T registerMetric(
+ MetricsComponent component,
+ MetricsFeature feature,
+ String metricName,
+ T metric);
+
+ /**
+ * Removes the metric with the given name.
+ *
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @return true if the metric existed and was removed, otherwise false
+ */
+ boolean removeMetric(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Fetches the existing Timers.
+ *
+ * @param filter filter to use to select Timers
+ * @return a map of the Timers that match the filter, with the key as the
+ * name String to the Timer.
+ */
+ Map<String, Timer> getTimers(MetricFilter filter);
+
+ /**
+ * Fetches the existing Gauges.
+ *
+ * @param filter filter to use to select Gauges
+ * @return a map of the Gauges that match the filter, with the key as the
+ * name String to the Gauge.
+ */
+ Map<String, Gauge> getGauges(MetricFilter filter);
+
+ /**
+ * Fetches the existing Counters.
+ *
+ * @param filter filter to use to select Counters
+ * @return a map of the Counters that match the filter, with the key as the
+ * name String to the Counter.
+ */
+ Map<String, Counter> getCounters(MetricFilter filter);
+
+ /**
+ * Fetches the existing Meters.
+ *
+ * @param filter filter to use to select Meters
+ * @return a map of the Meters that match the filter, with the key as the
+ * name String to the Meter.
+ */
+ Map<String, Meter> getMeters(MetricFilter filter);
+
+ /**
+ * Fetches the existing Histograms.
+ *
+ * @param filter filter to use to select Histograms
+ * @return a map of the Histograms that match the filter, with the key as
+ * the name String to the Histogram.
+ */
+ Map<String, Histogram> getHistograms(MetricFilter filter);
+
+ /**
+ * Fetches the existing metrics.
+ *
+ * @return a map of the Metrics, with the key as
+ * the name String to the Histogram.
+ */
+ Map<String, Metric> getMetrics();
+
+ /**
+ * Removes all Metrics that match a given filter.
+ *
+ * @param filter filter to use to select the Metrics to remove.
+ */
+ void removeMatching(MetricFilter filter);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsUtil.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsUtil.java
new file mode 100644
index 00000000..cab6c0ea
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/MetricsUtil.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.metrics;
+
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Timer.Context;
+
+public final class MetricsUtil {
+
+ /**
+ * Starts the Metric Timer.
+ * <p>
+ * If the given timer was null, it will silently return null.
+ * </p>
+ *
+ * @param timer timer to start
+ * @return timing context, if timer was not null
+ */
+ public static Context startTimer(Timer timer) {
+ if (timer != null) {
+ return timer.time();
+ }
+ return null;
+ }
+
+ /**
+ * Stops the Metric Timer context.
+ * <p>
+ * If the given context was null, it will silently be ignored.
+ * </p>
+ *
+ * @param context timing context to stop, if not null.
+ */
+ public static void stopTimer(Context context) {
+ if (context != null) {
+ context.stop();
+ }
+ }
+
+ // avoid instantiation
+ private MetricsUtil() {}
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/package-info.java
new file mode 100644
index 00000000..0a61f353
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/metrics/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Misc utils for various performance metrics.
+ */
+package org.onlab.metrics;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java
new file mode 100644
index 00000000..dc3c07f1
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ *
+ */
+public class ARP extends BasePacket {
+ public static final short HW_TYPE_ETHERNET = 0x1;
+
+ public static final short PROTO_TYPE_IP = 0x800;
+
+ public static final short OP_REQUEST = 0x1;
+ public static final short OP_REPLY = 0x2;
+ public static final short OP_RARP_REQUEST = 0x3;
+ public static final short OP_RARP_REPLY = 0x4;
+
+ public static final short INITIAL_HEADER_LENGTH = 8;
+
+ protected short hardwareType;
+ protected short protocolType;
+ protected byte hardwareAddressLength;
+ protected byte protocolAddressLength;
+ protected short opCode;
+ protected byte[] senderHardwareAddress;
+ protected byte[] senderProtocolAddress;
+ protected byte[] targetHardwareAddress;
+ protected byte[] targetProtocolAddress;
+
+ /**
+ * @return the hardwareType
+ */
+ public short getHardwareType() {
+ return this.hardwareType;
+ }
+
+ /**
+ * @param hwType
+ * the hardwareType to set
+ * @return this
+ */
+ public ARP setHardwareType(final short hwType) {
+ this.hardwareType = hwType;
+ return this;
+ }
+
+ /**
+ * @return the protocolType
+ */
+ public short getProtocolType() {
+ return this.protocolType;
+ }
+
+ /**
+ * @param protoType
+ * the protocolType to set
+ * @return this
+ */
+ public ARP setProtocolType(final short protoType) {
+ this.protocolType = protoType;
+ return this;
+ }
+
+ /**
+ * @return the hardwareAddressLength
+ */
+ public byte getHardwareAddressLength() {
+ return this.hardwareAddressLength;
+ }
+
+ /**
+ * @param hwAddressLength
+ * the hardwareAddressLength to set
+ * @return this
+ */
+ public ARP setHardwareAddressLength(final byte hwAddressLength) {
+ this.hardwareAddressLength = hwAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the protocolAddressLength
+ */
+ public byte getProtocolAddressLength() {
+ return this.protocolAddressLength;
+ }
+
+ /**
+ * @param protoAddressLength
+ * the protocolAddressLength to set
+ * @return this
+ */
+ public ARP setProtocolAddressLength(final byte protoAddressLength) {
+ this.protocolAddressLength = protoAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the opCode
+ */
+ public short getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @param op
+ * the opCode to set
+ * @return this
+ */
+ public ARP setOpCode(final short op) {
+ this.opCode = op;
+ return this;
+ }
+
+ /**
+ * @return the senderHardwareAddress
+ */
+ public byte[] getSenderHardwareAddress() {
+ return this.senderHardwareAddress;
+ }
+
+ /**
+ * @param senderHWAddress
+ * the senderHardwareAddress to set
+ * @return this
+ */
+ public ARP setSenderHardwareAddress(final byte[] senderHWAddress) {
+ this.senderHardwareAddress = senderHWAddress;
+ return this;
+ }
+
+ /**
+ * @return the senderProtocolAddress
+ */
+ public byte[] getSenderProtocolAddress() {
+ return this.senderProtocolAddress;
+ }
+
+ /**
+ * @param senderProtoAddress
+ * the senderProtocolAddress to set
+ * @return this
+ */
+ public ARP setSenderProtocolAddress(final byte[] senderProtoAddress) {
+ this.senderProtocolAddress = senderProtoAddress;
+ return this;
+ }
+
+ public ARP setSenderProtocolAddress(final int address) {
+ this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+ .array();
+ return this;
+ }
+
+ /**
+ * @return the targetHardwareAddress
+ */
+ public byte[] getTargetHardwareAddress() {
+ return this.targetHardwareAddress;
+ }
+
+ /**
+ * @param targetHWAddress
+ * the targetHardwareAddress to set
+ * @return this
+ */
+ public ARP setTargetHardwareAddress(final byte[] targetHWAddress) {
+ this.targetHardwareAddress = targetHWAddress;
+ return this;
+ }
+
+ /**
+ * @return the targetProtocolAddress
+ */
+ public byte[] getTargetProtocolAddress() {
+ return this.targetProtocolAddress;
+ }
+
+ /**
+ * @return True if gratuitous ARP (SPA = TPA), false otherwise
+ */
+ public boolean isGratuitous() {
+ assert this.senderProtocolAddress.length == this.targetProtocolAddress.length;
+
+ int indx = 0;
+ while (indx < this.senderProtocolAddress.length) {
+ if (this.senderProtocolAddress[indx] != this.targetProtocolAddress[indx]) {
+ return false;
+ }
+ indx++;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param targetProtoAddress
+ * the targetProtocolAddress to set
+ * @return this
+ */
+ public ARP setTargetProtocolAddress(final byte[] targetProtoAddress) {
+ this.targetProtocolAddress = targetProtoAddress;
+ return this;
+ }
+
+ public ARP setTargetProtocolAddress(final int address) {
+ this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+ .array();
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ final int length = 8 + 2 * (0xff & this.hardwareAddressLength) + 2
+ * (0xff & this.protocolAddressLength);
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.putShort(this.hardwareType);
+ bb.putShort(this.protocolType);
+ bb.put(this.hardwareAddressLength);
+ bb.put(this.protocolAddressLength);
+ bb.putShort(this.opCode);
+ bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+ bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength);
+ bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+ bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength);
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.hardwareType = bb.getShort();
+ this.protocolType = bb.getShort();
+ this.hardwareAddressLength = bb.get();
+ this.protocolAddressLength = bb.get();
+ this.opCode = bb.getShort();
+ this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+ bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length);
+ this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength];
+ bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length);
+ this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+ bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length);
+ this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength];
+ bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 13121;
+ int result = super.hashCode();
+ result = prime * result + this.hardwareAddressLength;
+ result = prime * result + this.hardwareType;
+ result = prime * result + this.opCode;
+ result = prime * result + this.protocolAddressLength;
+ result = prime * result + this.protocolType;
+ result = prime * result + Arrays.hashCode(this.senderHardwareAddress);
+ result = prime * result + Arrays.hashCode(this.senderProtocolAddress);
+ result = prime * result + Arrays.hashCode(this.targetHardwareAddress);
+ result = prime * result + Arrays.hashCode(this.targetProtocolAddress);
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof ARP)) {
+ return false;
+ }
+ final ARP other = (ARP) obj;
+ if (this.hardwareAddressLength != other.hardwareAddressLength) {
+ return false;
+ }
+ if (this.hardwareType != other.hardwareType) {
+ return false;
+ }
+ if (this.opCode != other.opCode) {
+ return false;
+ }
+ if (this.protocolAddressLength != other.protocolAddressLength) {
+ return false;
+ }
+ if (this.protocolType != other.protocolType) {
+ return false;
+ }
+ if (!Arrays.equals(this.senderHardwareAddress,
+ other.senderHardwareAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.senderProtocolAddress,
+ other.senderProtocolAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetHardwareAddress,
+ other.targetHardwareAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetProtocolAddress,
+ other.targetProtocolAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "ARP [hardwareType=" + this.hardwareType + ", protocolType="
+ + this.protocolType + ", hardwareAddressLength="
+ + this.hardwareAddressLength + ", protocolAddressLength="
+ + this.protocolAddressLength + ", opCode=" + this.opCode
+ + ", senderHardwareAddress="
+ + Arrays.toString(this.senderHardwareAddress)
+ + ", senderProtocolAddress="
+ + Arrays.toString(this.senderProtocolAddress)
+ + ", targetHardwareAddress="
+ + Arrays.toString(this.targetHardwareAddress)
+ + ", targetProtocolAddress="
+ + Arrays.toString(this.targetProtocolAddress) + "]";
+ }
+
+ /**
+ * Builds an ARP reply based on a request.
+ *
+ * @param srcIp the IP address to use as the reply source
+ * @param srcMac the MAC address to use as the reply source
+ * @param request the ARP request we got
+ * @return an Ethernet frame containing the ARP reply
+ */
+ public static Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac,
+ Ethernet request) {
+
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(request.getSourceMAC());
+ eth.setSourceMACAddress(srcMac);
+ eth.setEtherType(Ethernet.TYPE_ARP);
+ eth.setVlanID(request.getVlanID());
+
+ ARP arp = new ARP();
+ arp.setOpCode(ARP.OP_REPLY);
+ arp.setProtocolType(ARP.PROTO_TYPE_IP);
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
+
+ arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
+ arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
+ arp.setSenderHardwareAddress(srcMac.toBytes());
+ arp.setTargetHardwareAddress(request.getSourceMACAddress());
+
+ arp.setTargetProtocolAddress(((ARP) request.getPayload())
+ .getSenderProtocolAddress());
+ arp.setSenderProtocolAddress(srcIp.toInt());
+
+ eth.setPayload(arp);
+ return eth;
+ }
+
+ /**
+ * Deserializer function for ARP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ARP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_LENGTH);
+
+ ARP arp = new ARP();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ arp.setHardwareType(bb.getShort());
+ arp.setProtocolType(bb.getShort());
+
+ byte hwAddressLength = bb.get();
+ arp.setHardwareAddressLength(hwAddressLength);
+
+ byte protocolAddressLength = bb.get();
+ arp.setProtocolAddressLength(protocolAddressLength);
+ arp.setOpCode(bb.getShort());
+
+ // Check we have enough space for the addresses
+ checkHeaderLength(length, INITIAL_HEADER_LENGTH +
+ 2 * hwAddressLength +
+ 2 * protocolAddressLength);
+
+ arp.senderHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.senderHardwareAddress, 0, arp.senderHardwareAddress.length);
+ arp.senderProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.senderProtocolAddress, 0, arp.senderProtocolAddress.length);
+ arp.targetHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.targetHardwareAddress, 0, arp.targetHardwareAddress.length);
+ arp.targetProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.targetProtocolAddress, 0, arp.targetProtocolAddress.length);
+
+ return arp;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
new file mode 100644
index 00000000..4aece66f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+/**
+ *
+ *
+ */
+public abstract class BasePacket implements IPacket {
+ protected IPacket parent;
+ protected IPacket payload;
+
+ /**
+ * @return the parent
+ */
+ @Override
+ public IPacket getParent() {
+ return this.parent;
+ }
+
+ /**
+ * @param parent
+ * the parent to set
+ */
+ @Override
+ public IPacket setParent(final IPacket parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ /**
+ * @return the payload
+ */
+ @Override
+ public IPacket getPayload() {
+ return this.payload;
+ }
+
+ /**
+ * @param payload
+ * the payload to set
+ */
+ @Override
+ public IPacket setPayload(final IPacket payload) {
+ this.payload = payload;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ if (this.parent != null) {
+ this.parent.resetChecksum();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 6733;
+ int result = 1;
+ result = prime * result
+ + (this.payload == null ? 0 : this.payload.hashCode());
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BasePacket)) {
+ return false;
+ }
+ final BasePacket other = (BasePacket) obj;
+ if (this.payload == null) {
+ if (other.payload != null) {
+ return false;
+ }
+ } else if (!this.payload.equals(other.payload)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public Object clone() {
+ IPacket pkt;
+ try {
+ pkt = this.getClass().newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException("Could not clone packet");
+ }
+
+ final byte[] data = this.serialize();
+ pkt.deserialize(this.serialize(), 0, data.length);
+ pkt.setParent(this.parent);
+ return pkt;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
new file mode 100644
index 00000000..23c9859f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+/**
+ * The class representing a network device chassisId.
+ * This class is immutable.
+ */
+public final class ChassisId {
+
+ private static final long UNKNOWN = 0;
+ private final long value;
+
+ /**
+ * Default constructor.
+ */
+ public ChassisId() {
+ this.value = ChassisId.UNKNOWN;
+ }
+
+ /**
+ * Constructor from a long value.
+ *
+ * @param value the value to use.
+ */
+ public ChassisId(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public ChassisId(String value) {
+ this.value = Long.parseLong(value, 16);
+ }
+
+ /**
+ * Get the value of the chassis id.
+ *
+ * @return the value of the chassis id.
+ */
+ public long value() {
+ return value;
+ }
+
+ /**
+ * Convert the Chassis Id value to a ':' separated hexadecimal string.
+ *
+ * @return the Chassis Id value as a ':' separated hexadecimal string.
+ */
+ @Override
+ public String toString() {
+ return Long.toHexString(this.value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ChassisId)) {
+ return false;
+ }
+
+ ChassisId otherChassisId = (ChassisId) other;
+
+ return value == otherChassisId.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(value);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
new file mode 100644
index 00000000..de5b43fa
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ *
+ */
+public class DHCP extends BasePacket {
+ /**
+ * Dynamic Host Configuration Protocol packet.
+ * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
+ * hops(1) | ------------------------------------------ | xid (4) |
+ * ------------------------------------------ | secs (2) | flags (2) |
+ * ------------------------------------------ | ciaddr (4) |
+ * ------------------------------------------ | yiaddr (4) |
+ * ------------------------------------------ | siaddr (4) |
+ * ------------------------------------------ | giaddr (4) |
+ * ------------------------------------------ | chaddr (16) |
+ * ------------------------------------------ | sname (64) |
+ * ------------------------------------------ | file (128) |
+ * ------------------------------------------ | options (312) |
+ * ------------------------------------------
+ *
+ */
+ // Header + magic without options
+ public static final int MIN_HEADER_LENGTH = 240;
+ public static final byte OPCODE_REQUEST = 0x1;
+ public static final byte OPCODE_REPLY = 0x2;
+
+ public static final byte HWTYPE_ETHERNET = 0x1;
+
+ public enum DHCPOptionCode {
+ OptionCode_SubnetMask((byte) 1), OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
+ OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15), OptionCode_BroadcastAddress((byte) 28),
+ OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
+ OptionCode_DHCPServerIp((byte) 54), OptionCode_RequestedParameters((byte) 55),
+ OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID((byte) 61),
+ OptionCode_END((byte) 255);
+
+ protected byte value;
+
+ private DHCPOptionCode(final byte value) {
+ this.value = value;
+ }
+
+ public byte getValue() {
+ return this.value;
+ }
+ }
+
+ protected byte opCode;
+ protected byte hardwareType;
+ protected byte hardwareAddressLength;
+ protected byte hops;
+ protected int transactionId;
+ protected short seconds;
+ protected short flags;
+ protected int clientIPAddress;
+ protected int yourIPAddress;
+ protected int serverIPAddress;
+ protected int gatewayIPAddress;
+ protected byte[] clientHardwareAddress;
+ protected String serverName;
+ protected String bootFileName;
+ protected List<DHCPOption> options = new ArrayList<DHCPOption>();
+
+ /**
+ * @return the opCode
+ */
+ public byte getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @param opCode
+ * the opCode to set
+ * @return this
+ */
+ public DHCP setOpCode(final byte opCode) {
+ this.opCode = opCode;
+ return this;
+ }
+
+ /**
+ * @return the hardwareType
+ */
+ public byte getHardwareType() {
+ return this.hardwareType;
+ }
+
+ /**
+ * @param hardwareType
+ * the hardwareType to set
+ * @return this
+ */
+ public DHCP setHardwareType(final byte hardwareType) {
+ this.hardwareType = hardwareType;
+ return this;
+ }
+
+ /**
+ * @return the hardwareAddressLength
+ */
+ public byte getHardwareAddressLength() {
+ return this.hardwareAddressLength;
+ }
+
+ /**
+ * @param hardwareAddressLength
+ * the hardwareAddressLength to set
+ * @return this
+ */
+ public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
+ this.hardwareAddressLength = hardwareAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the hops
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @param hops
+ * the hops to set
+ * @return this
+ */
+ public DHCP setHops(final byte hops) {
+ this.hops = hops;
+ return this;
+ }
+
+ /**
+ * @return the transactionId
+ */
+ public int getTransactionId() {
+ return this.transactionId;
+ }
+
+ /**
+ * @param transactionId
+ * the transactionId to set
+ * @return this
+ */
+ public DHCP setTransactionId(final int transactionId) {
+ this.transactionId = transactionId;
+ return this;
+ }
+
+ /**
+ * @return the seconds
+ */
+ public short getSeconds() {
+ return this.seconds;
+ }
+
+ /**
+ * @param seconds
+ * the seconds to set
+ * @return this
+ */
+ public DHCP setSeconds(final short seconds) {
+ this.seconds = seconds;
+ return this;
+ }
+
+ /**
+ * @return the flags
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ * @return this
+ */
+ public DHCP setFlags(final short flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * @return the clientIPAddress
+ */
+ public int getClientIPAddress() {
+ return this.clientIPAddress;
+ }
+
+ /**
+ * @param clientIPAddress
+ * the clientIPAddress to set
+ * @return this
+ */
+ public DHCP setClientIPAddress(final int clientIPAddress) {
+ this.clientIPAddress = clientIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the yourIPAddress
+ */
+ public int getYourIPAddress() {
+ return this.yourIPAddress;
+ }
+
+ /**
+ * @param yourIPAddress
+ * the yourIPAddress to set
+ * @return this
+ */
+ public DHCP setYourIPAddress(final int yourIPAddress) {
+ this.yourIPAddress = yourIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the serverIPAddress
+ */
+ public int getServerIPAddress() {
+ return this.serverIPAddress;
+ }
+
+ /**
+ * @param serverIPAddress
+ * the serverIPAddress to set
+ * @return this
+ */
+ public DHCP setServerIPAddress(final int serverIPAddress) {
+ this.serverIPAddress = serverIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the gatewayIPAddress
+ */
+ public int getGatewayIPAddress() {
+ return this.gatewayIPAddress;
+ }
+
+ /**
+ * @param gatewayIPAddress
+ * the gatewayIPAddress to set
+ * @return this
+ */
+ public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
+ this.gatewayIPAddress = gatewayIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the clientHardwareAddress
+ */
+ public byte[] getClientHardwareAddress() {
+ return this.clientHardwareAddress;
+ }
+
+ /**
+ * @param clientHardwareAddress
+ * the clientHardwareAddress to set
+ * @return this
+ */
+ public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
+ this.clientHardwareAddress = clientHardwareAddress;
+ return this;
+ }
+
+ /**
+ * Gets a specific DHCP option parameter.
+ *
+ * @param optionCode
+ * The option code to get
+ * @return The value of the option if it exists, null otherwise
+ */
+ public DHCPOption getOption(final DHCPOptionCode optionCode) {
+ for (final DHCPOption opt : this.options) {
+ if (opt.code == optionCode.value) {
+ return opt;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the options
+ */
+ public List<DHCPOption> getOptions() {
+ return this.options;
+ }
+
+ /**
+ * @param options
+ * the options to set
+ * @return this
+ */
+ public DHCP setOptions(final List<DHCPOption> options) {
+ this.options = options;
+ return this;
+ }
+
+ /**
+ * @return the packetType base on option 53
+ */
+ public DHCPPacketType getPacketType() {
+ final ListIterator<DHCPOption> lit = this.options.listIterator();
+ while (lit.hasNext()) {
+ final DHCPOption option = lit.next();
+ // only care option 53
+ if (option.getCode() == 53) {
+ return DHCPPacketType.getType(option.getData()[0]);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the serverName
+ */
+ public String getServerName() {
+ return this.serverName;
+ }
+
+ /**
+ * @param server
+ * the serverName to set
+ * @return this
+ */
+ public DHCP setServerName(final String server) {
+ this.serverName = server;
+ return this;
+ }
+
+ /**
+ * @return the bootFileName
+ */
+ public String getBootFileName() {
+ return this.bootFileName;
+ }
+
+ /**
+ * @param bootFile
+ * the bootFileName to set
+ * @return this
+ */
+ public DHCP setBootFileName(final String bootFile) {
+ this.bootFileName = bootFile;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ // not guaranteed to retain length/exact format
+ this.resetChecksum();
+
+ // minimum size 240 including magic cookie, options generally padded to
+ // 300
+ int optionsLength = 0;
+ for (final DHCPOption option : this.options) {
+ if (option.getCode() == 0 || option.getCode() == ((byte) 255)) {
+ optionsLength += 1;
+ } else {
+ optionsLength += 2 + (0xff & option.getLength());
+ }
+ }
+ int optionsPadLength = 0;
+ if (optionsLength < 60) {
+ optionsPadLength = 60 - optionsLength;
+ }
+
+ final byte[] data = new byte[240 + optionsLength + optionsPadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.opCode);
+ bb.put(this.hardwareType);
+ bb.put(this.hardwareAddressLength);
+ bb.put(this.hops);
+ bb.putInt(this.transactionId);
+ bb.putShort(this.seconds);
+ bb.putShort(this.flags);
+ bb.putInt(this.clientIPAddress);
+ bb.putInt(this.yourIPAddress);
+ bb.putInt(this.serverIPAddress);
+ bb.putInt(this.gatewayIPAddress);
+ checkArgument(this.clientHardwareAddress.length <= 16,
+ "Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
+ bb.put(this.clientHardwareAddress);
+ if (this.clientHardwareAddress.length < 16) {
+ for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ this.writeString(this.serverName, bb, 64);
+ this.writeString(this.bootFileName, bb, 128);
+ // magic cookie
+ bb.put((byte) 0x63);
+ bb.put((byte) 0x82);
+ bb.put((byte) 0x53);
+ bb.put((byte) 0x63);
+ for (final DHCPOption option : this.options) {
+ final int code = option.getCode() & 0xff;
+ bb.put((byte) code);
+ if (code != 0 && code != 255) {
+ bb.put(option.getLength());
+ bb.put(option.getData());
+ }
+ }
+ // assume the rest is padded out with zeroes
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
+ return this;
+ }
+
+ this.opCode = bb.get();
+ this.hardwareType = bb.get();
+ this.hardwareAddressLength = bb.get();
+ this.hops = bb.get();
+ this.transactionId = bb.getInt();
+ this.seconds = bb.getShort();
+ this.flags = bb.getShort();
+ this.clientIPAddress = bb.getInt();
+ this.yourIPAddress = bb.getInt();
+ this.serverIPAddress = bb.getInt();
+ this.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
+ this.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(this.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ this.serverName = this.readString(bb, 64);
+ this.bootFileName = this.readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+ // read options
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ }
+ this.options.add(option);
+ if (code == 255) {
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ break;
+ }
+ }
+
+ return this;
+ }
+
+ protected void writeString(final String string, final ByteBuffer bb,
+ final int maxLength) {
+ if (string == null) {
+ for (int i = 0; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ } else {
+ byte[] bytes = null;
+ try {
+ bytes = string.getBytes("ascii");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure encoding server name", e);
+ }
+ int writeLength = bytes.length;
+ if (writeLength > maxLength) {
+ writeLength = maxLength;
+ }
+ bb.put(bytes, 0, writeLength);
+ for (int i = writeLength; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ }
+
+ private static String readString(final ByteBuffer bb, final int maxLength) {
+ final byte[] bytes = new byte[maxLength];
+ bb.get(bytes);
+ String result = null;
+ try {
+ result = new String(bytes, "ascii").trim();
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure decoding string", e);
+ }
+ return result;
+ }
+
+ /**
+ * Deserializer function for DHCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DHCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MIN_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ DHCP dhcp = new DHCP();
+
+ dhcp.opCode = bb.get();
+ dhcp.hardwareType = bb.get();
+ dhcp.hardwareAddressLength = bb.get();
+ dhcp.hops = bb.get();
+ dhcp.transactionId = bb.getInt();
+ dhcp.seconds = bb.getShort();
+ dhcp.flags = bb.getShort();
+ dhcp.clientIPAddress = bb.getInt();
+ dhcp.yourIPAddress = bb.getInt();
+ dhcp.serverIPAddress = bb.getInt();
+ dhcp.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
+ dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(dhcp.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ dhcp.serverName = readString(bb, 64);
+ dhcp.bootFileName = readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+
+ // read options
+ boolean foundEndOptionsMarker = false;
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ dhcp.options.add(option);
+ } else {
+ throw new DeserializationException(
+ "Buffer underflow while reading DHCP option");
+ }
+ }
+ } else if (code == 255) {
+ DHCPOption end = new DHCPOption();
+ end.setCode((byte) 255);
+ dhcp.options.add(end);
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ foundEndOptionsMarker = true;
+ break;
+ }
+ }
+
+ if (!foundEndOptionsMarker) {
+ throw new DeserializationException("DHCP End options marker was missing");
+ }
+
+ return dhcp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
new file mode 100644
index 00000000..1b6c670e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.util.Arrays;
+
+/**
+ *
+ */
+public class DHCPOption {
+ protected byte code;
+ protected byte length;
+ protected byte[] data;
+
+ /**
+ * @return the code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * @param code
+ * the code to set
+ * @return this
+ */
+ public DHCPOption setCode(final byte code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public byte getLength() {
+ return this.length;
+ }
+
+ /**
+ * @param length
+ * the length to set
+ * @return this
+ */
+ public DHCPOption setLength(final byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @return the data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * @param data
+ * the data to set
+ * @return this
+ */
+ public DHCPOption setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + this.code;
+ result = prime * result + Arrays.hashCode(this.data);
+ result = prime * result + this.length;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DHCPOption)) {
+ return false;
+ }
+ final DHCPOption other = (DHCPOption) obj;
+ if (this.code != other.code) {
+ return false;
+ }
+ if (!Arrays.equals(this.data, other.data)) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "DHCPOption [code=" + this.code + ", length=" + this.length
+ + ", data=" + Arrays.toString(this.data) + "]";
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
new file mode 100644
index 00000000..8254c770
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+public enum DHCPPacketType {
+ // From RFC 1533
+ DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5), DHCPNAK(
+ 6), DHCPRELEASE(7),
+
+ // From RFC2132
+ DHCPINFORM(8),
+
+ // From RFC3203
+ DHCPFORCERENEW(9),
+
+ // From RFC4388
+ DHCPLEASEQUERY(10), DHCPLEASEUNASSIGNED(11), DHCPLEASEUNKNOWN(12), DHCPLEASEACTIVE(
+ 13);
+
+ protected int value;
+
+ private DHCPPacketType(final int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ switch (this.value) {
+ case 1:
+ return "DHCPDISCOVER";
+ case 2:
+ return "DHCPOFFER";
+ case 3:
+ return "DHCPREQUEST";
+ case 4:
+ return "DHCPDECLINE";
+ case 5:
+ return "DHCPACK";
+ case 6:
+ return "DHCPNAK";
+ case 7:
+ return "DHCPRELEASE";
+ case 8:
+ return "DHCPINFORM";
+ case 9:
+ return "DHCPFORCERENEW";
+ case 10:
+ return "DHCPLEASEQUERY";
+ case 11:
+ return "DHCPLEASEUNASSIGNED";
+ case 12:
+ return "DHCPLEASEUNKNOWN";
+ case 13:
+ return "DHCPLEASEACTIVE";
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ public static DHCPPacketType getType(final int value) {
+ switch (value) {
+ case 1:
+ return DHCPDISCOVER;
+ case 2:
+ return DHCPOFFER;
+ case 3:
+ return DHCPREQUEST;
+ case 4:
+ return DHCPDECLINE;
+ case 5:
+ return DHCPACK;
+ case 6:
+ return DHCPNAK;
+ case 7:
+ return DHCPRELEASE;
+ case 8:
+ return DHCPINFORM;
+ case 9:
+ return DHCPFORCERENEW;
+ case 10:
+ return DHCPLEASEQUERY;
+ case 11:
+ return DHCPLEASEUNASSIGNED;
+ case 12:
+ return DHCPLEASEUNKNOWN;
+ case 13:
+ return DHCPLEASEACTIVE;
+ default:
+ break;
+ }
+
+ return null;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java
new file mode 100644
index 00000000..79abcba1
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class Data extends BasePacket {
+ protected byte[] data;
+
+ /**
+ *
+ */
+ public Data() {
+ data = new byte[0];
+ }
+
+ /**
+ * @param data the data
+ */
+ public Data(final byte[] data) {
+ this.data = data;
+ }
+
+ /**
+ * @return the data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * @param data
+ * the data to set
+ * @return self
+ */
+ public Data setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return this.data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ this.data = Arrays.copyOfRange(data, offset, data.length);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 1571;
+ int result = super.hashCode();
+ result = prime * result + Arrays.hashCode(this.data);
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Data)) {
+ return false;
+ }
+ final Data other = (Data) obj;
+ if (!Arrays.equals(this.data, other.data)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for generic payload data.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Data> deserializer() {
+ return (data, offset, length) -> {
+ // Allow zero-length data for now
+ if (length == 0) {
+ return new Data();
+ }
+
+ checkInput(data, offset, length, 1);
+
+ Data dataObject = new Data();
+
+ dataObject.data = Arrays.copyOfRange(data, offset, data.length);
+
+ return dataObject;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
new file mode 100644
index 00000000..03a8aa35
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+/**
+ * Signals that an error occurred during deserialization of a packet.
+ */
+public class DeserializationException extends Exception {
+
+ /**
+ * Creates a new deserialization exception with the given message.
+ *
+ * @param message exception message
+ */
+ public DeserializationException(String message) {
+ super(message);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
new file mode 100644
index 00000000..e0ef381b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+/**
+ * Function to deserialize a packet from a byte-based input stream.
+ */
+@FunctionalInterface
+public interface Deserializer<U extends IPacket> {
+
+ /**
+ * Deserialize a packet object from a byte array.
+ *
+ * @param data input array to take packet bytes from
+ * @param offset index where this packet header begins in the byte array
+ * @param length length of the packet header
+ * @return a deserialized packet object
+ * @throws DeserializationException if the packet cannot be deserialized
+ * from the input
+ */
+ U deserialize(byte[] data, int offset, int length) throws DeserializationException;
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java
new file mode 100644
index 00000000..516938a4
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java
@@ -0,0 +1,264 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * EAP (Extensible Authentication Protocol) packet.
+ */
+public class EAP extends BasePacket {
+ private static final int HEADER_LENGTH = 4;
+
+ public static final short MIN_LEN = 0x4;
+ public static final short EAP_HDR_LEN_REQ_RESP = 5;
+ public static final short EAP_HDR_LEN_SUC_FAIL = 4;
+
+ // EAP Code
+ public static final byte REQUEST = 0x1;
+ public static final byte RESPONSE = 0x2;
+ public static final byte SUCCESS = 0x3;
+ public static final byte FAILURE = 0x4;
+
+ // EAP Attribute Type
+ public static final byte ATTR_IDENTITY = 0x1;
+ public static final byte ATTR_NOTIFICATION = 0x2;
+ public static final byte ATTR_NAK = 0x3;
+ public static final byte ATTR_MD5 = 0x4;
+ public static final byte ATTR_OTP = 0x5;
+ public static final byte ATTR_GTC = 0x6;
+ public static final byte ATTR_TLS = 0xd;
+
+ protected byte code;
+ protected byte identifier;
+ protected short length;
+ protected byte type;
+ protected byte[] data;
+
+
+ /**
+ * Gets the EAP code.
+ *
+ * @return EAP code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+
+ /**
+ * Sets the EAP code.
+ *
+ * @param code EAP code
+ * @return this
+ */
+ public EAP setCode(final byte code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * Gets the EAP identifier.
+ *
+ * @return EAP identifier
+ */
+ public byte getIdentifier() {
+ return this.identifier;
+ }
+
+ /**
+ * Sets the EAP identifier.
+ *
+ * @param identifier EAP identifier
+ * @return this
+ */
+ public EAP setIdentifier(final byte identifier) {
+ this.identifier = identifier;
+ return this;
+ }
+
+ /**
+ * Gets the get packet length.
+ *
+ * @return packet length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * Sets the packet length.
+ *
+ * @param length packet length
+ * @return this
+ */
+ public EAP setLength(final short length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Gets the data type.
+ *
+ * @return data type
+ */
+ public byte getDataType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the data type.
+ *
+ * @param type data type
+ * @return this
+ */
+ public EAP setDataType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Gets the EAP data.
+ *
+ * @return EAP data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * Sets the EAP data.
+ *
+ * @param data EAP data to be set
+ * @return this
+ */
+ public EAP setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ /**
+ * Default EAP constructor that sets the EAP code to 0.
+ */
+ public EAP() {
+ this.code = 0;
+ }
+
+ /**
+ * EAP constructor that initially sets all fields.
+ *
+ * @param code EAP code
+ * @param identifier EAP identifier
+ * @param type packet type
+ * @param data EAP data
+ */
+ public EAP(byte code, byte identifier, byte type, byte[] data) {
+ this.code = code;
+ this.identifier = identifier;
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ this.length = (short) (5 + (data == null ? 0 : data.length));
+ this.type = type;
+ } else {
+ this.length = (short) (4 + (data == null ? 0 : data.length));
+ }
+ this.data = data;
+ }
+
+ /**
+ * Deserializer for EAP packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<EAP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EAP eap = new EAP();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ eap.code = bb.get();
+ eap.identifier = bb.get();
+ eap.length = bb.getShort();
+
+ checkHeaderLength(length, eap.length);
+
+ int dataLength;
+ if (eap.code == REQUEST || eap.code == RESPONSE) {
+ eap.type = bb.get();
+ dataLength = eap.length - 5;
+ } else {
+ dataLength = eap.length - 4;
+ }
+
+ eap.data = new byte[dataLength];
+ bb.get(eap.data);
+ return eap;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[this.length];
+
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.code);
+ bb.put(this.identifier);
+ bb.putShort(this.length);
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ bb.put(this.type);
+ }
+ if (this.data != null) {
+ bb.put(this.data);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.code = bb.get();
+ this.identifier = bb.get();
+ this.length = bb.getShort();
+
+ int dataLength;
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ this.type = bb.get();
+ dataLength = this.length - 5;
+ } else {
+ dataLength = this.length - 4;
+ }
+ this.data = new byte[dataLength];
+ bb.get(this.data);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 3889;
+ int result = super.hashCode();
+ result = prime * result + this.code;
+ result = prime * result + this.identifier;
+ result = prime * result + this.length;
+ result = prime * result + this.type;
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java
new file mode 100644
index 00000000..1820cc31
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java
@@ -0,0 +1,199 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * EAPOL (Extensible Authentication Protocol over LAN) header.
+ */
+public class EAPOL extends BasePacket {
+
+ private byte version = 0x01;
+ private byte eapolType;
+ private short packetLength;
+
+ private static final int HEADER_LENGTH = 4;
+
+ // EAPOL Packet Type
+ public static final byte EAPOL_PACKET = 0x0;
+ public static final byte EAPOL_START = 0x1;
+ public static final byte EAPOL_LOGOFF = 0x2;
+ public static final byte EAPOL_KEY = 0x3;
+ public static final byte EAPOL_ASF = 0x4;
+
+ public static final MacAddress PAE_GROUP_ADDR = MacAddress.valueOf(new byte[] {
+ (byte) 0x01, (byte) 0x80, (byte) 0xc2, (byte) 0x00, (byte) 0x00, (byte) 0x03
+ });
+
+ /**
+ * Gets the version.
+ *
+ * @return version
+ */
+ public byte getVersion() {
+ return this.version;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version EAPOL version
+ * @return this
+ */
+ public EAPOL setVersion(final byte version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Gets the type.
+ *
+ * @return EAPOL type
+ */
+ public byte getEapolType() {
+ return this.eapolType;
+ }
+
+ /**
+ * Sets the EAPOL type.
+ *
+ * @param eapolType EAPOL type
+ * @return this
+ */
+ public EAPOL setEapolType(final byte eapolType) {
+ this.eapolType = eapolType;
+ return this;
+ }
+
+ /**
+ * Gets the packet length.
+ *
+ * @return packet length
+ */
+ public short getPacketLength() {
+ return this.packetLength;
+ }
+
+ /**
+ * Sets the packet length.
+ *
+ * @param packetLen packet length
+ * @return this
+ */
+ public EAPOL setPacketLength(final short packetLen) {
+ this.packetLength = packetLen;
+ return this;
+ }
+
+ /**
+ * Serializes the packet, based on the code/type using the payload
+ * to compute its length.
+ *
+ * @return this
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ // prepare the buffer to hold the version (1), packet type (1),
+ // packet length (2) and the eap payload.
+ // if there is no payload, packet length is 0
+ byte[] data = new byte[4 + this.packetLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.version);
+ bb.put(this.eapolType);
+ bb.putShort(this.packetLength);
+
+ // put the EAP payload
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 3889;
+ int result = super.hashCode();
+ result = prime * result + this.version;
+ result = prime * result + this.eapolType;
+ result = prime * result + this.packetLength;
+ return result;
+ }
+
+ /**
+ * Deserializer for EAPOL packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<EAPOL> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EAPOL eapol = new EAPOL();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ eapol.setVersion(bb.get());
+ eapol.setEapolType(bb.get());
+ eapol.setPacketLength(bb.getShort());
+
+ if (eapol.packetLength > 0) {
+ checkHeaderLength(length, HEADER_LENGTH + eapol.packetLength);
+ // deserialize the EAP Payload
+ eapol.payload = EAP.deserializer().deserialize(data,
+ bb.position(), bb.limit() - bb.position());
+
+ eapol.payload.setParent(eapol);
+ }
+
+ return eapol;
+ };
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ // deserialize the EAPOL header
+ this.version = bb.get();
+ this.eapolType = bb.get();
+ this.packetLength = bb.getShort();
+
+ if (this.packetLength > 0) {
+ // deserialize the EAP Payload
+ this.payload = new EAP();
+
+ this.payload = this.payload.deserialize(data, bb.position(), length - 4);
+ this.payload.setParent(this);
+ }
+
+ return this;
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
new file mode 100644
index 00000000..561c9307
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+/**
+ * Representation of an Ethertype.
+ */
+public class EthType {
+
+ /**
+ * A list of known ethertypes. Adding a fully defined enum here will
+ * associated the ethertype with a textual representation and a parsing
+ * class.
+ */
+ public enum EtherType {
+
+ ARP(0x806, "arp", org.onlab.packet.ARP.deserializer()),
+ RARP(0x8035, "rarp", org.onlab.packet.ARP.deserializer()),
+ IPV4(0x800, "ipv4", org.onlab.packet.IPv4.deserializer()),
+ IPV6(0x86dd, "ipv6", org.onlab.packet.IPv6.deserializer()),
+ LLDP(0x88cc, "lldp", org.onlab.packet.LLDP.deserializer()),
+ VLAN(0x8100, "vlan", null),
+ BDDP(0x8942, "bddp", org.onlab.packet.LLDP.deserializer()),
+ MPLS_UNICAST(0x8847, "mpls_unicast", org.onlab.packet.MPLS.deserializer()),
+ MPLS_MULTICAST(0x8848, "mpls_unicast", org.onlab.packet.MPLS.deserializer()),
+ EAPOL(0x888e, "eapol", org.onlab.packet.EAPOL.deserializer()),
+ UNKNOWN(0, "unknown", null);
+
+
+ private final EthType etherType;
+ private final String type;
+ private final Deserializer<?> deserializer;
+
+ /**
+ * Constucts a new ethertype.
+ *
+ * @param ethType The actual ethertype
+ * @param type it's textual representation
+ * @param deserializer a parser for this ethertype
+ */
+ EtherType(int ethType, String type, Deserializer<?> deserializer) {
+ this.etherType = new EthType(ethType);
+ this.type = type;
+ this.deserializer = deserializer;
+ }
+
+ public EthType ethType() {
+ return etherType;
+ }
+
+ @Override
+ public String toString() {
+ return type;
+ }
+
+ public Deserializer<?> deserializer() {
+ return deserializer;
+ }
+
+ public static EtherType lookup(short etherType) {
+ for (EtherType ethType : EtherType.values()) {
+ if (ethType.ethType().toShort() == etherType) {
+ return ethType;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ }
+
+
+ private final short etherType;
+
+ /**
+ * Builds the EthType.
+ *
+ * @param etherType an integer representing an ethtype
+ */
+ public EthType(int etherType) {
+ this.etherType = (short) (etherType & 0xFFFF);
+ }
+
+ /**
+ * Builds the EthType.
+ *
+ * @param etherType a short representing the ethtype
+ */
+ public EthType(short etherType) {
+ this.etherType = etherType;
+ }
+
+ /**
+ * Returns the short value for this ethtype.
+ *
+ * @return a short value
+ */
+ public short toShort() {
+ return etherType;
+ }
+
+ /**
+ * Looks up the ethertype by it's numerical representation
+ * and returns it's textual format.
+ *
+ * @param etherType the short value of the ethertype
+ * @return a textual representation
+ */
+ public EtherType lookup(short etherType) {
+ for (EtherType ethType : EtherType.values()) {
+ if (ethType.ethType().toShort() == etherType) {
+ return ethType;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ EthType ethType = (EthType) o;
+
+ if (etherType != ethType.etherType) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) etherType;
+ }
+
+ public String toString() {
+ EtherType ethType = lookup(this.etherType);
+ return (ethType == null ? String.format("0x%04x", etherType) :
+ ethType.toString());
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
new file mode 100644
index 00000000..003c1772
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ *
+ */
+public class Ethernet extends BasePacket {
+ private static final String HEXES = "0123456789ABCDEF";
+
+ public static final short TYPE_ARP = EthType.EtherType.ARP.ethType().toShort();
+ public static final short TYPE_RARP = EthType.EtherType.RARP.ethType().toShort();
+ public static final short TYPE_IPV4 = EthType.EtherType.IPV4.ethType().toShort();
+ public static final short TYPE_IPV6 = EthType.EtherType.IPV6.ethType().toShort();
+ public static final short TYPE_LLDP = EthType.EtherType.LLDP.ethType().toShort();
+ public static final short TYPE_VLAN = EthType.EtherType.VLAN.ethType().toShort();
+ public static final short TYPE_BSN = EthType.EtherType.BDDP.ethType().toShort();
+
+ public static final short MPLS_UNICAST = EthType.EtherType.MPLS_UNICAST.ethType().toShort();;
+ public static final short MPLS_MULTICAST = EthType.EtherType.MPLS_MULTICAST.ethType().toShort();
+
+
+ public static final short VLAN_UNTAGGED = (short) 0xffff;
+
+ public static final short ETHERNET_HEADER_LENGTH = 14; // bytes
+ public static final short VLAN_HEADER_LENGTH = 4; // bytes
+
+ public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
+
+ private static final Map<Short, Deserializer<? extends IPacket>> ETHERTYPE_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ for (EthType.EtherType ethType : EthType.EtherType.values()) {
+ if (ethType.deserializer() != null) {
+ ETHERTYPE_DESERIALIZER_MAP.put(ethType.ethType().toShort(), ethType.deserializer());
+ }
+ }
+ }
+
+ protected MacAddress destinationMACAddress;
+ protected MacAddress sourceMACAddress;
+ protected byte priorityCode;
+ protected short vlanID;
+ protected short etherType;
+ protected boolean pad = false;
+
+ /**
+ * By default, set Ethernet to untagged.
+ */
+ public Ethernet() {
+ super();
+ this.vlanID = Ethernet.VLAN_UNTAGGED;
+ }
+
+ /**
+ * Gets the destination MAC address.
+ *
+ * @return the destination MAC as a byte array
+ */
+ public byte[] getDestinationMACAddress() {
+ return this.destinationMACAddress.toBytes();
+ }
+
+ /**
+ * Gets the destination MAC address.
+ *
+ * @return the destination MAC
+ */
+ public MacAddress getDestinationMAC() {
+ return this.destinationMACAddress;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final MacAddress destMac) {
+ this.destinationMACAddress = checkNotNull(destMac);
+ return this;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final byte[] destMac) {
+ this.destinationMACAddress = MacAddress.valueOf(destMac);
+ return this;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final String destMac) {
+ this.destinationMACAddress = MacAddress.valueOf(destMac);
+ return this;
+ }
+
+ /**
+ * Gets the source MAC address.
+ *
+ * @return the source MACAddress as a byte array
+ */
+ public byte[] getSourceMACAddress() {
+ return this.sourceMACAddress.toBytes();
+ }
+
+ /**
+ * Gets the source MAC address.
+ *
+ * @return the source MACAddress
+ */
+ public MacAddress getSourceMAC() {
+ return this.sourceMACAddress;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
+ this.sourceMACAddress = checkNotNull(sourceMac);
+ return this;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final byte[] sourceMac) {
+ this.sourceMACAddress = MacAddress.valueOf(sourceMac);
+ return this;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final String sourceMac) {
+ this.sourceMACAddress = MacAddress.valueOf(sourceMac);
+ return this;
+ }
+
+ /**
+ * Gets the priority code.
+ *
+ * @return the priorityCode
+ */
+ public byte getPriorityCode() {
+ return this.priorityCode;
+ }
+
+ /**
+ * Sets the priority code.
+ *
+ * @param priority the priorityCode to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setPriorityCode(final byte priority) {
+ this.priorityCode = priority;
+ return this;
+ }
+
+ /**
+ * Gets the VLAN ID.
+ *
+ * @return the vlanID
+ */
+ public short getVlanID() {
+ return this.vlanID;
+ }
+
+ /**
+ * Sets the VLAN ID.
+ *
+ * @param vlan the vlanID to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setVlanID(final short vlan) {
+ this.vlanID = vlan;
+ return this;
+ }
+
+ /**
+ * Gets the Ethernet type.
+ *
+ * @return the etherType
+ */
+ public short getEtherType() {
+ return this.etherType;
+ }
+
+ /**
+ * Sets the Ethernet type.
+ *
+ * @param ethType the etherType to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setEtherType(final short ethType) {
+ this.etherType = ethType;
+ return this;
+ }
+
+ /**
+ * @return True if the Ethernet frame is broadcast, false otherwise
+ */
+ public boolean isBroadcast() {
+ assert this.destinationMACAddress.length() == 6;
+ return this.destinationMACAddress.isBroadcast();
+ }
+
+ /**
+ * @return True is the Ethernet frame is multicast, False otherwise
+ */
+ public boolean isMulticast() {
+ return this.destinationMACAddress.isMulticast();
+ }
+
+ /**
+ * Pad this packet to 60 bytes minimum, filling with zeros?
+ *
+ * @return the pad
+ */
+ public boolean isPad() {
+ return this.pad;
+ }
+
+ /**
+ * Pad this packet to 60 bytes minimum, filling with zeros?
+ *
+ * @param pd
+ * the pad to set
+ * @return this
+ */
+ public Ethernet setPad(final boolean pd) {
+ this.pad = pd;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+ int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
+ + (payloadData == null ? 0 : payloadData.length);
+ if (this.pad && length < 60) {
+ length = 60;
+ }
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.destinationMACAddress.toBytes());
+ bb.put(this.sourceMACAddress.toBytes());
+ if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
+ bb.putShort(TYPE_VLAN);
+ bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
+ }
+ bb.putShort(this.etherType);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+ if (this.pad) {
+ Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ if (length <= 0) {
+ return null;
+ }
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ if (this.destinationMACAddress == null) {
+ this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
+ }
+ final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ bb.get(dstAddr);
+ this.destinationMACAddress = MacAddress.valueOf(dstAddr);
+
+ if (this.sourceMACAddress == null) {
+ this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
+ }
+ final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ bb.get(srcAddr);
+ this.sourceMACAddress = MacAddress.valueOf(srcAddr);
+
+ short ethType = bb.getShort();
+ if (ethType == TYPE_VLAN) {
+ final short tci = bb.getShort();
+ this.priorityCode = (byte) (tci >> 13 & 0x07);
+ this.vlanID = (short) (tci & 0x0fff);
+ ethType = bb.getShort();
+ } else {
+ this.vlanID = Ethernet.VLAN_UNTAGGED;
+ }
+ this.etherType = ethType;
+
+ IPacket payload;
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+ return this;
+ }
+
+ /**
+ * Checks to see if a string is a valid MAC address.
+ *
+ * @param macAddress string to test if it is a valid MAC
+ * @return True if macAddress is a valid MAC, False otherwise
+ */
+ public static boolean isMACAddress(final String macAddress) {
+ final String[] macBytes = macAddress.split(":");
+ if (macBytes.length != 6) {
+ return false;
+ }
+ for (int i = 0; i < 6; ++i) {
+ if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
+ || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
+ 1)) == -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
+ * matter, and returns a corresponding byte[].
+ *
+ * @param macAddress
+ * The MAC address to convert into a byte array
+ * @return The macAddress as a byte array
+ */
+ public static byte[] toMACAddress(final String macAddress) {
+ return MacAddress.valueOf(macAddress).toBytes();
+ }
+
+ /**
+ * Accepts a MAC address and returns the corresponding long, where the MAC
+ * bytes are set on the lower order bytes of the long.
+ *
+ * @param macAddress MAC address as a byte array
+ * @return a long containing the mac address bytes
+ */
+ public static long toLong(final byte[] macAddress) {
+ return MacAddress.valueOf(macAddress).toLong();
+ }
+
+ /**
+ * Converts a long MAC address to a byte array.
+ *
+ * @param macAddress MAC address set on the lower order bytes of the long
+ * @return the bytes of the mac address
+ */
+ public static byte[] toByteArray(final long macAddress) {
+ return MacAddress.valueOf(macAddress).toBytes();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 7867;
+ int result = super.hashCode();
+ result = prime * result + this.destinationMACAddress.hashCode();
+ result = prime * result + this.etherType;
+ result = prime * result + this.vlanID;
+ result = prime * result + this.priorityCode;
+ result = prime * result + (this.pad ? 1231 : 1237);
+ result = prime * result + this.sourceMACAddress.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Ethernet)) {
+ return false;
+ }
+ final Ethernet other = (Ethernet) obj;
+ if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
+ return false;
+ }
+ if (this.priorityCode != other.priorityCode) {
+ return false;
+ }
+ if (this.vlanID != other.vlanID) {
+ return false;
+ }
+ if (this.etherType != other.etherType) {
+ return false;
+ }
+ if (this.pad != other.pad) {
+ return false;
+ }
+ if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString(java.lang.Object)
+ */
+ @Override
+ public String toString() {
+
+ final StringBuffer sb = new StringBuffer("\n");
+
+ final IPacket pkt = this.getPayload();
+
+ if (pkt instanceof ARP) {
+ sb.append("arp");
+ } else if (pkt instanceof LLDP) {
+ sb.append("lldp");
+ } else if (pkt instanceof ICMP) {
+ sb.append("icmp");
+ } else if (pkt instanceof IPv4) {
+ sb.append("ip");
+ } else if (pkt instanceof DHCP) {
+ sb.append("dhcp");
+ } else {
+ sb.append(this.getEtherType());
+ }
+
+ sb.append("\ndl_vlan: ");
+ if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
+ sb.append("untagged");
+ } else {
+ sb.append(this.getVlanID());
+ }
+ sb.append("\ndl_vlan_pcp: ");
+ sb.append(this.getPriorityCode());
+ sb.append("\ndl_src: ");
+ sb.append(bytesToHex(this.getSourceMACAddress()));
+ sb.append("\ndl_dst: ");
+ sb.append(bytesToHex(this.getDestinationMACAddress()));
+
+ if (pkt instanceof ARP) {
+ final ARP p = (ARP) pkt;
+ sb.append("\nnw_src: ");
+ sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
+ .getSenderProtocolAddress())));
+ sb.append("\nnw_dst: ");
+ sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
+ .getTargetProtocolAddress())));
+ } else if (pkt instanceof LLDP) {
+ sb.append("lldp packet");
+ } else if (pkt instanceof ICMP) {
+ final ICMP icmp = (ICMP) pkt;
+ sb.append("\nicmp_type: ");
+ sb.append(icmp.getIcmpType());
+ sb.append("\nicmp_code: ");
+ sb.append(icmp.getIcmpCode());
+ } else if (pkt instanceof IPv4) {
+ final IPv4 p = (IPv4) pkt;
+ sb.append("\nnw_src: ");
+ sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
+ sb.append("\nnw_dst: ");
+ sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
+ sb.append("\nnw_tos: ");
+ sb.append(p.getDiffServ());
+ sb.append("\nnw_proto: ");
+ sb.append(p.getProtocol());
+
+ if (pkt instanceof TCP) {
+ sb.append("\ntp_src: ");
+ sb.append(((TCP) pkt).getSourcePort());
+ sb.append("\ntp_dst: ");
+ sb.append(((TCP) pkt).getDestinationPort());
+
+ } else if (pkt instanceof UDP) {
+ sb.append("\ntp_src: ");
+ sb.append(((UDP) pkt).getSourcePort());
+ sb.append("\ntp_dst: ");
+ sb.append(((UDP) pkt).getDestinationPort());
+ }
+
+ if (pkt instanceof ICMP) {
+ final ICMP icmp = (ICMP) pkt;
+ sb.append("\nicmp_type: ");
+ sb.append(icmp.getIcmpType());
+ sb.append("\nicmp_code: ");
+ sb.append(icmp.getIcmpCode());
+ }
+
+ } else if (pkt instanceof DHCP) {
+ sb.append("\ndhcp packet");
+ } else if (pkt instanceof Data) {
+ sb.append("\ndata packet");
+ } else if (pkt instanceof LLC) {
+ sb.append("\nllc packet");
+ } else {
+ sb.append("\nunknown packet");
+ }
+
+ return sb.toString();
+ }
+
+ public static String bytesToHex(byte[] in) {
+ final StringBuilder builder = new StringBuilder();
+ for (byte b : in) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Deserializer function for Ethernet packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Ethernet> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ETHERNET_HEADER_LENGTH);
+
+ byte[] addressBuffer = new byte[DATALAYER_ADDRESS_LENGTH];
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ Ethernet eth = new Ethernet();
+ // Read destination MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setDestinationMACAddress(addressBuffer);
+
+ // Read source MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setSourceMACAddress(addressBuffer);
+
+ short ethType = bb.getShort();
+ if (ethType == TYPE_VLAN) {
+ checkHeaderLength(length, ETHERNET_HEADER_LENGTH + VLAN_HEADER_LENGTH);
+ final short tci = bb.getShort();
+ eth.setPriorityCode((byte) (tci >> 13 & 0x07));
+ eth.setVlanID((short) (tci & 0x0fff));
+ ethType = bb.getShort();
+ } else {
+ eth.setVlanID(Ethernet.VLAN_UNTAGGED);
+ }
+ eth.setEtherType(ethType);
+
+ IPacket payload;
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ payload.setParent(eth);
+ eth.setPayload(payload);
+
+ return eth;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java
new file mode 100644
index 00000000..d07a9ba4
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Implements ICMP packet format.
+ *
+ */
+public class ICMP extends BasePacket {
+ protected byte icmpType;
+ protected byte icmpCode;
+ protected short checksum;
+
+ public static final byte TYPE_ECHO_REQUEST = 0x08;
+ public static final byte TYPE_ECHO_REPLY = 0x00;
+ public static final byte SUBTYPE_ECHO_REPLY = 0x00;
+
+ public static final short ICMP_HEADER_LENGTH = 4;
+
+ /**
+ * @return the icmpType
+ */
+ public byte getIcmpType() {
+ return this.icmpType;
+ }
+
+ /**
+ * @param icmpType
+ * to set
+ * @return this
+ */
+ public ICMP setIcmpType(final byte icmpType) {
+ this.icmpType = icmpType;
+ return this;
+ }
+
+ /**
+ * @return the icmp code
+ */
+ public byte getIcmpCode() {
+ return this.icmpCode;
+ }
+
+ /**
+ * @param icmpCode
+ * code to set
+ * @return this
+ */
+ public ICMP setIcmpCode(final byte icmpCode) {
+ this.icmpCode = icmpCode;
+ return this;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public ICMP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ int length = 4;
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ length += payloadData.length;
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.icmpType);
+ bb.put(this.icmpCode);
+ bb.putShort(this.checksum);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_ICMP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ for (int i = 0; i < length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(2, this.checksum);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.icmpType = bb.get();
+ this.icmpCode = bb.get();
+ this.checksum = bb.getShort();
+
+ this.payload = new Data();
+ this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ this.payload.setParent(this);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.icmpType;
+ result = prime * result + this.icmpCode;
+ result = prime * result + this.checksum;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof ICMP)) {
+ return false;
+ }
+ final ICMP other = (ICMP) obj;
+ if (this.icmpType != other.icmpType) {
+ return false;
+ }
+ if (this.icmpCode != other.icmpCode) {
+ return false;
+ }
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for ICMP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ICMP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ICMP_HEADER_LENGTH);
+
+ ICMP icmp = new ICMP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ icmp.icmpType = bb.get();
+ icmp.icmpCode = bb.get();
+ icmp.checksum = bb.getShort();
+
+ icmp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ icmp.payload.setParent(icmp);
+ return icmp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java
new file mode 100644
index 00000000..c8981302
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import org.onlab.packet.ipv6.IExtensionHeader;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.packet.ndp.Redirect;
+import org.onlab.packet.ndp.RouterAdvertisement;
+import org.onlab.packet.ndp.RouterSolicitation;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 packet format. (RFC 4443)
+ */
+public class ICMP6 extends BasePacket {
+ public static final byte HEADER_LENGTH = 4; // bytes
+
+ // Type
+ /** Destination Unreachable. */
+ public static final byte DEST_UNREACH = (byte) 0x01;
+ /** Packet Too Big. */
+ public static final byte PKT_TOO_BIG = (byte) 0x02;
+ /** Time Exceeded. */
+ public static final byte TIME_EXCEED = (byte) 0x03;
+ /** Parameter Problem. */
+ public static final byte PARAM_ERR = (byte) 0x04;
+ /** Echo Request. */
+ public static final byte ECHO_REQUEST = (byte) 0x80;
+ /** Echo Reply. */
+ public static final byte ECHO_REPLY = (byte) 0x81;
+ /** Multicast Listener Query. */
+ public static final byte MCAST_QUERY = (byte) 0x82;
+ /** Multicast Listener Report. */
+ public static final byte MCAST_REPORT = (byte) 0x83;
+ /** Multicast Listener Done. */
+ public static final byte MCAST_DONE = (byte) 0x84;
+ /** Router Solicitation. */
+ public static final byte ROUTER_SOLICITATION = (byte) 0x85;
+ /** Router Advertisement. */
+ public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
+ /** Neighbor Solicitation. */
+ public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
+ /** Neighbor Advertisement. */
+ public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
+ /** Redirect Message. */
+ public static final byte REDIRECT = (byte) 0x89;
+
+ // Code for DEST_UNREACH
+ /** No route to destination. */
+ public static final byte NO_ROUTE = (byte) 0x00;
+ /** Communication with destination administratively prohibited. */
+ public static final byte COMM_PROHIBIT = (byte) 0x01;
+ /** Beyond scope of source address. */
+ public static final byte BEYOND_SCOPE = (byte) 0x02;
+ /** Address unreachable. */
+ public static final byte ADDR_UNREACH = (byte) 0x03;
+ /** Port unreachable. */
+ public static final byte PORT_UNREACH = (byte) 0x04;
+ /** Source address failed ingress/egress policy. */
+ public static final byte FAIL_POLICY = (byte) 0x05;
+ /** Reject route to destination. */
+ public static final byte REJECT_ROUTE = (byte) 0x06;
+ /** Error in Source Routing Header. */
+ public static final byte SRC_ROUTING_HEADER_ERR = (byte) 0x07;
+
+ // Code for TIME_EXCEED
+ /** Hop limit exceeded in transit. */
+ public static final byte HOP_LIMIT_EXCEED = (byte) 0x00;
+ /** Fragment reassembly time exceeded. */
+ public static final byte DEFRAG_TIME_EXCEED = (byte) 0x01;
+
+ // Code for PARAM_ERR
+ /** Erroneous header field encountered. */
+ public static final byte HDR_FIELD_ERR = (byte) 0x00;
+ /** Unrecognized Next Header type encountered. */
+ public static final byte NEXT_HEADER_ERR = (byte) 0x01;
+ /** Unrecognized IPv6 option encountered. */
+ public static final byte IPV6_OPT_ERR = (byte) 0x01;
+
+ public static final Map<Byte, Deserializer<? extends IPacket>> TYPE_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.deserializer());
+ ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.REDIRECT, Redirect.deserializer());
+ }
+
+ protected byte icmpType;
+ protected byte icmpCode;
+ protected short checksum;
+
+ private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
+
+ /**
+ * Gets ICMP6 type.
+ *
+ * @return the ICMP6 type
+ */
+ public byte getIcmpType() {
+ return this.icmpType;
+ }
+
+ /**
+ * Sets ICMP6 type.
+ *
+ * @param icmpType the ICMP type to set
+ * @return this
+ */
+ public ICMP6 setIcmpType(final byte icmpType) {
+ this.icmpType = icmpType;
+ return this;
+ }
+
+ /**
+ * Gets ICMP6 code.
+ *
+ * @return the ICMP6 code
+ */
+ public byte getIcmpCode() {
+ return this.icmpCode;
+ }
+
+ /**
+ * Sets ICMP6 code.
+ *
+ * @param icmpCode the ICMP6 code to set
+ * @return this
+ */
+ public ICMP6 setIcmpCode(final byte icmpCode) {
+ this.icmpCode = icmpCode;
+ return this;
+ }
+
+ /**
+ * Gets checksum.
+ *
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * Sets checksum.
+ *
+ * @param checksum the checksum to set
+ * @return this
+ */
+ public ICMP6 setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + payloadLength];
+ final ByteBuffer bbData = ByteBuffer.wrap(data);
+
+ // Creating ByteBuffer for checksum calculation
+ final byte[] checksumData =
+ new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
+ final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
+
+ //
+ // Creating IPv6 Pseudo Header for checksum calculation according
+ // to RFC 4443 and RFC 2460
+ //
+ IPv6 ipv6Parent = null;
+ for (IPacket p = this.parent; p != null; p = p.getParent()) {
+ if (p instanceof IPv6) {
+ ipv6Parent = (IPv6) p;
+ break;
+ }
+ }
+ if (ipv6Parent != null) {
+ bbChecksum.put(ipv6Parent.getSourceAddress());
+ bbChecksum.put(ipv6Parent.getDestinationAddress());
+ } else {
+ // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
+ bbChecksum.put(ZERO_ADDRESS);
+ bbChecksum.put(ZERO_ADDRESS);
+ }
+ bbChecksum.putInt(HEADER_LENGTH + payloadLength);
+ bbChecksum.put((byte) 0);
+ bbChecksum.put((byte) 0);
+ bbChecksum.put((byte) 0);
+ bbChecksum.put(IPv6.PROTOCOL_ICMP6);
+ bbChecksum.put(this.icmpType);
+ bbChecksum.put(this.icmpCode);
+ bbChecksum.put((byte) 0);
+ bbChecksum.put((byte) 0);
+
+ bbData.put(this.icmpType);
+ bbData.put(this.icmpCode);
+ bbData.putShort(this.checksum);
+ if (payloadData != null) {
+ bbData.put(payloadData);
+ bbChecksum.put(payloadData);
+ }
+
+ if (this.parent != null) {
+ if (this.parent instanceof IPv6) {
+ ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
+ } else if (this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
+ }
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bbData.rewind();
+ bbChecksum.rewind();
+ int accumulation = 0;
+
+ for (int i = 0; i < checksumData.length / 2; ++i) {
+ accumulation += 0xffff & bbChecksum.getShort();
+ }
+ // pad to an even number of shorts
+ if (checksumData.length % 2 > 0) {
+ accumulation += (bbChecksum.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bbData.putShort(2, this.checksum);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.icmpType = bb.get();
+ this.icmpCode = bb.get();
+ this.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmpType)) {
+ deserializer = TYPE_DESERIALIZER_MAP.get(icmpType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.icmpType;
+ result = prime * result + this.icmpCode;
+ result = prime * result + this.checksum;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof ICMP6)) {
+ return false;
+ }
+ final ICMP6 other = (ICMP6) obj;
+ if (this.icmpType != other.icmpType) {
+ return false;
+ }
+ if (this.icmpCode != other.icmpCode) {
+ return false;
+ }
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for ICMPv6 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ICMP6> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ ICMP6 icmp6 = new ICMP6();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ icmp6.icmpType = bb.get();
+ icmp6.icmpCode = bb.get();
+ icmp6.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmp6.icmpType)) {
+ deserializer = TYPE_DESERIALIZER_MAP.get(icmp6.icmpType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ icmp6.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ icmp6.payload.setParent(icmp6);
+
+ return icmp6;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java
new file mode 100644
index 00000000..e7abbd6a
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+
+/**
+ * Implements IGMP control packet format.
+ */
+public class IGMP extends BasePacket {
+ private final Logger log = getLogger(getClass());
+
+ public static final byte TYPE_IGMPV3_MEMBERSHIP_QUERY = 0x11;
+ public static final byte TYPE_IGMPV1_MEMBERSHIP_REPORT = 0x12;
+ public static final byte TYPE_IGMPV2_MEMBERSHIP_REPORT = 0x16;
+ public static final byte TYPE_IGMPV2_LEAVE_GROUP = 0x17;
+ public static final byte TYPE_IGMPV3_MEMBERSHIP_REPORT = 0x22;
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP = new HashMap<>();
+
+ public static final int MINIMUM_HEADER_LEN = 12;
+
+ List<IGMPGroup> groups = new ArrayList<>();
+
+ // Fields contained in the IGMP header
+ private byte igmpType;
+ private byte resField = 0;
+ private short checksum = 0;
+
+ private byte[] unsupportTypeData;
+
+ public IGMP() {
+ }
+
+ /**
+ * Get the IGMP message type.
+ *
+ * @return the IGMP message type
+ */
+ public byte getIgmpType() {
+ return igmpType;
+ }
+
+ /**
+ * Set the IGMP message type.
+ *
+ * @param msgType IGMP message type
+ */
+ public void setIgmpType(byte msgType) {
+ igmpType = msgType;
+ }
+
+ /**
+ * Get the checksum of this message.
+ *
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return checksum;
+ }
+
+ /**
+ * get the Max Resp Code.
+ *
+ * @return The Maximum Time allowed before before sending a responding report.
+ */
+ public byte getMaxRespField() {
+ return resField;
+ }
+
+ /**
+ * Set the Max Resp Code.
+ *
+ * @param respCode the Maximum Response Code.
+ */
+ public void setMaxRespCode(byte respCode) {
+ if (igmpType != IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY) {
+ log.debug("Requesting the max response code for an incorrect field: ");
+ }
+ this.resField = respCode;
+ }
+
+ /**
+ * Get the list of IGMPGroups. The group objects will be either IGMPQuery or IGMPMembership
+ * depending on the IGMP message type. For IGMP Query, the groups list should only be
+ * one group.
+ *
+ * @return The list of IGMP groups.
+ */
+ public List<IGMPGroup> getGroups() {
+ return groups;
+ }
+
+ /**
+ * Add a multicast group to this IGMP message.
+ *
+ * @param group the IGMPGroup will be IGMPQuery or IGMPMembership depending on the message type.
+ * @return true if group was valid and added, false otherwise.
+ */
+ public boolean addGroup(IGMPGroup group) {
+ checkNotNull(group);
+ switch (this.igmpType) {
+ case TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ if (group instanceof IGMPMembership) {
+ return false;
+ }
+
+ if (group.sources.size() > 1) {
+ return false;
+ }
+ break;
+
+ case TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ if (group instanceof IGMPMembership) {
+ return false;
+ }
+ break;
+
+ default:
+ log.debug("Warning no IGMP message type has been set");
+ }
+
+ this.groups.add(group);
+ return true;
+ }
+
+ /**
+ * Serialize this IGMP packet. This will take care
+ * of serializing IGMPv3 Queries and IGMPv3 Membership
+ * Reports.
+ *
+ * @return the serialized IGMP message
+ */
+ @Override
+ public byte[] serialize() {
+ byte [] data = new byte[8915];
+
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.getIgmpType());
+
+ // reserved or max resp code depending on type.
+ bb.put(this.resField);
+
+ // Must calculate checksum
+ bb.putShort((short) 0);
+
+ switch (this.igmpType) {
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ // reserved
+ bb.putShort((short) 0);
+ // Number of groups
+ bb.putShort((short) groups.size());
+ // Fall through
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
+
+ for (IGMPGroup grp : groups) {
+ grp.serialize(bb);
+ }
+ break;
+
+ default:
+ bb.put(this.unsupportTypeData);
+ break;
+ }
+
+ int size = bb.position();
+ bb.position(0);
+ byte [] rdata = new byte[size];
+ bb.get(rdata, 0, size);
+ return rdata;
+ }
+
+ /**
+ * Deserialize an IGMP message.
+ *
+ * @param data bytes to deserialize
+ * @param offset offset to start deserializing from
+ * @param length length of the data to deserialize
+ * @return populated IGMP object
+ */
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+
+ IGMP igmp = new IGMP();
+ try {
+ igmp = IGMP.deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ log.error(e.getStackTrace().toString());
+ return this;
+ }
+ this.igmpType = igmp.igmpType;
+ this.resField = igmp.resField;
+ this.checksum = igmp.checksum;
+ this.groups = igmp.groups;
+ return this;
+ }
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IGMP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MINIMUM_HEADER_LEN);
+
+ IGMP igmp = new IGMP();
+
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ igmp.igmpType = bb.get();
+ igmp.resField = bb.get();
+ igmp.checksum = bb.getShort();
+ int len = MINIMUM_HEADER_LEN;
+ String msg;
+
+ switch (igmp.igmpType) {
+
+ case TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ IGMPQuery qgroup = new IGMPQuery();
+ qgroup.deserialize(bb);
+ igmp.groups.add(qgroup);
+ break;
+
+ case TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ bb.getShort(); // Ignore resvd
+ int ngrps = bb.getShort();
+
+ for (; ngrps > 0; ngrps--) {
+ IGMPMembership mgroup = new IGMPMembership();
+ mgroup.deserialize(bb);
+ igmp.groups.add(mgroup);
+ }
+ break;
+
+ /*
+ * NOTE: according to the IGMPv3 spec. These previous IGMP type fields
+ * must be supported. At this time we are going to <b>assume</b> we run
+ * in a modern network where all devices are IGMPv3 capable.
+ */
+ case TYPE_IGMPV1_MEMBERSHIP_REPORT:
+ case TYPE_IGMPV2_MEMBERSHIP_REPORT:
+ case TYPE_IGMPV2_LEAVE_GROUP:
+ igmp.unsupportTypeData = bb.array(); // Is this the entire array?
+ msg = "IGMP message type: " + igmp.igmpType + " is not supported";
+ igmp.log.debug(msg);
+ break;
+
+ default:
+ msg = "IGMP message type: " + igmp.igmpType + " is not recognized";
+ igmp.unsupportTypeData = bb.array();
+ igmp.log.debug(msg);
+ break;
+ }
+ return igmp;
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof IGMP)) {
+ return false;
+ }
+ final IGMP other = (IGMP) obj;
+ if (this.igmpType != other.igmpType) {
+ return false;
+ }
+ if (this.resField != other.resField) {
+ return false;
+ }
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.groups.size() != other.groups.size()) {
+ return false;
+ }
+ // TODO: equals should be true regardless of order.
+ if (!groups.equals(other.groups)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.igmpType;
+ result = prime * result + this.groups.size();
+ result = prime * result + this.resField;
+ result = prime * result + this.checksum;
+ result = prime * result + this.groups.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java
new file mode 100644
index 00000000..70ff5563
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to represent Groups for membership query and reports.
+ */
+public abstract class IGMPGroup {
+
+ protected int auxInfo;
+ protected IpAddress gaddr;
+ protected List<IpAddress> sources = new ArrayList<>();
+
+ public IGMPGroup() {
+ }
+
+ /**
+ * Initialize this object with a multicast group address and additional info.
+ *
+ * @param gaddr: the multicast group address for this message type.
+ * @param auxInfo: additional info potentially used by IGMPQuery
+ */
+ public IGMPGroup(IpAddress gaddr, int auxInfo) {
+ this.gaddr = gaddr;
+ this.auxInfo = auxInfo;
+ }
+
+ /**
+ * Get the multicast group address.
+ *
+ * @return the group address
+ */
+ public IpAddress getGaddr() {
+ return this.gaddr;
+ }
+
+ /**
+ * Get the auxillary info.
+ *
+ * @return the auxillary info
+ */
+ public int getAuxInfo() {
+ return this.auxInfo;
+ }
+
+ /**
+ * Add a unicast source address to this message.
+ *
+ * @param saddr IPv4 unicast source address
+ */
+ public void addSource(IpAddress saddr) {
+ sources.add(saddr);
+ }
+
+ /**
+ * Return the list of source addresses.
+ *
+ * @return list of source addresses
+ */
+ public List<IpAddress> getSources() {
+ return sources;
+ }
+
+ /**
+ * Deserialize an IGMPQuery or IGMPMembership message.
+ *
+ * @param bb the ByteBuffer wrapping the serialized message. The position of the
+ * ByteBuffer should be pointing at the head of either message type.
+ * @return An object populated with the respective IGMPGroup subclass
+ * @throws DeserializationException in case deserialization goes wrong
+ */
+ public abstract IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException;
+
+ /**
+ * Serialize the IGMPGroup subclass.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return The serialized message
+ */
+ public abstract byte[] serialize(ByteBuffer bb);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java
new file mode 100644
index 00000000..495e283c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.checkBufferLength;
+
+public class IGMPMembership extends IGMPGroup {
+
+ public static final byte MODE_IS_INCLUDE = 0x1;
+ public static final byte MODE_IS_EXCLUDE = 0x2;
+ public static final byte CHANGE_TO_INCLUDE_MODE = 0x3;
+ public static final byte CHANGE_TO_EXCLUDE_MODE = 0x4;
+ public static final byte ALLOW_NEW_SOURCES = 0x5;
+ public static final byte BLOCK_OLD_SOURCES = 0x6;
+
+ private final int minGroupRecordLen = Ip4Address.BYTE_LENGTH + 4;
+
+ protected byte recordType;
+ protected byte auxDataLength = 0;
+ protected byte[] auxData;
+
+ /**
+ * Constructor initialized with a multicast group address.
+ *
+ * @param gaddr A multicast group address.
+ */
+ public IGMPMembership(Ip4Address gaddr) {
+ super(gaddr, 0);
+ }
+
+ /**
+ * Default constructor.
+ */
+ public IGMPMembership() {
+ super();
+ }
+
+ /**
+ * Serialize this Membership Report.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return serialized IGMP message.
+ */
+ @Override
+ public byte[] serialize(ByteBuffer bb) {
+
+ bb.put(recordType);
+ bb.put(auxDataLength); // reserved
+ bb.putShort((short) sources.size());
+ bb.put(gaddr.toOctets());
+ for (IpAddress ipaddr : sources) {
+ bb.put(ipaddr.toOctets());
+ }
+
+ if (auxDataLength > 0) {
+ bb.put(auxData);
+ }
+
+ return bb.array();
+ }
+
+ /**
+ * Deserialize the IGMP Membership report packet.
+ *
+ * @param bb the ByteBuffer wrapping the serialized message. The position of the
+ * ByteBuffer should be pointing at the head of either message type.
+ * @return IGMP Group
+ * @throws DeserializationException if deserialization fails
+ */
+ public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
+
+ // Make sure there is enough buffer to read the header,
+ // including the number of sources
+ checkBufferLength(bb.remaining(), 0, minGroupRecordLen);
+ recordType = bb.get();
+ auxDataLength = bb.get();
+ int nsrcs = bb.getShort();
+
+ gaddr = Ip4Address.valueOf(bb.getInt());
+
+ // Make sure we have enough buffer to hold all of these sources
+ checkBufferLength(bb.remaining(), 0, Ip4Address.BYTE_LENGTH * nsrcs);
+ for (; nsrcs > 0; nsrcs--) {
+ Ip4Address src = Ip4Address.valueOf(bb.getInt());
+ this.sources.add(src);
+ }
+
+ if (auxDataLength > 0) {
+ auxData = new byte[auxDataLength];
+ bb.get(auxData, 0, auxDataLength);
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals()
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IGMPMembership)) {
+ return false;
+ }
+ IGMPMembership other = (IGMPMembership) obj;
+
+ if (!this.gaddr.equals(other.gaddr)) {
+ return false;
+ }
+ if (this.recordType != other.recordType) {
+ return false;
+ }
+ if (this.auxDataLength != other.auxDataLength) {
+ return false;
+ }
+ if (this.sources.size() != other.sources.size()) {
+ return false;
+ }
+ // TODO: make these tolerant of order
+ if (!this.sources.equals(other.sources)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.gaddr.hashCode();
+ result = prime * result + this.recordType;
+ result = prime * result + this.auxDataLength;
+ result = prime * result + this.sources.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java
new file mode 100644
index 00000000..bb53eba5
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+public class IGMPQuery extends IGMPGroup {
+
+ // Bits and bytes after the group address
+ private byte resv = 0;
+ private boolean sbit = false;
+ private byte qrv = 2;
+ private byte qqic = 0x7d;
+
+ /**
+ * Create IGMP Query message.
+ *
+ * @param gaddr initiaze with a group address.
+ * @param auxInfo auxillary info.
+ */
+ public IGMPQuery(IpAddress gaddr, int auxInfo) {
+ super(gaddr, auxInfo);
+ }
+
+ /**
+ * Create IGMP Query message.
+ */
+ public IGMPQuery() {
+ super();
+ }
+
+ /**
+ * Is the S flag set? Telling adjacent routers to suppress normal timer updates.
+ *
+ * @return true if the flag is set, false otherwise
+ */
+ public boolean isSbit() {
+ return sbit;
+ }
+
+ /**
+ * Set the S flag. Default is false.
+ *
+ * @param sbit true or false
+ */
+ public void setSbit(boolean sbit) {
+ this.sbit = sbit;
+ }
+
+ /**
+ * Get the Querier Robustness Variable.
+ *
+ * @return querier robustness value
+ */
+ public byte getQrv() {
+ return qrv;
+ }
+
+ /**
+ * Set the Querier Robustness Variable. Default is 2.
+ *
+ * @param qrv new querier robustness value
+ */
+ public void setQrv(byte qrv) {
+ this.qrv = qrv;
+ }
+
+ /**
+ * Get the reserved field. Should be zero, but ignored regardless of it's value.
+ *
+ * @return the reserved field.
+ */
+ public byte getResv() {
+ return resv;
+ }
+
+ /**
+ * Set the reserved field. Should be 0 and ignored by receivers.
+ *
+ * @param resv the reserved field.
+ */
+ public void setResv(byte resv) {
+ this.resv = resv;
+ }
+
+ /**
+ * Serialize this IGMPQuery.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return the serialized message
+ */
+ @Override
+ public byte[] serialize(ByteBuffer bb) {
+
+ bb.put(gaddr.toOctets());
+ byte fld = (byte) (0x7 & qrv);
+ bb.put(fld);
+ bb.put(qqic);
+ bb.putShort((short) sources.size());
+ for (IpAddress ipaddr : sources) {
+ bb.put(ipaddr.toOctets());
+ }
+ return bb.array();
+ }
+
+ /**
+ * Deserialize the IGMP Query group structure.
+ *
+ * @param bb ByteBuffer pointing at the IGMP Query group address
+ * @return the IGMP Group object
+ */
+ public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
+
+ gaddr = Ip4Address.valueOf(bb.getInt());
+ byte fld = bb.get();
+
+ // Just ignore the reserved bits
+ resv = 0;
+ this.sbit = ((fld & 0x8) == 0x8);
+ qrv = (byte) (fld & 0x7);
+
+ // QQIC field
+ qqic = bb.get();
+
+ // Get the number of sources.
+ short nsrcs = bb.getShort();
+
+ // Do a sanity check on the amount of space we have in our buffer.
+ int lengthNeeded = (Ip4Address.BYTE_LENGTH * nsrcs);
+ PacketUtils.checkHeaderLength(bb.remaining(), lengthNeeded);
+
+ for (; nsrcs > 0; nsrcs--) {
+ Ip4Address ipaddr = Ip4Address.valueOf(bb.getInt());
+ this.sources.add(ipaddr);
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals()
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IGMPQuery)) {
+ return false;
+ }
+ IGMPQuery other = (IGMPQuery) obj;
+
+ if (this.sbit != other.sbit) {
+ return false;
+ }
+ if (this.qrv != other.qrv) {
+ return false;
+ }
+ if (this.qqic != other.qqic) {
+ return false;
+ }
+ if (this.sources.size() != other.sources.size()) {
+ return false;
+ }
+
+ // TODO: make these tolerant of order
+ if (!this.sources.equals(other.sources)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.gaddr.hashCode();
+ result = prime * result + this.qqic;
+ result = prime * result + this.qrv;
+ result = prime * result + this.sources.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java
new file mode 100644
index 00000000..64e6ac36
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+/**
+ *
+ */
+public interface IPacket {
+ /**
+ *
+ * @return the payload
+ */
+ IPacket getPayload();
+
+ /**
+ *
+ * @param packet new payload
+ * @return self
+ */
+ IPacket setPayload(IPacket packet);
+
+ /**
+ *
+ * @return parent packet
+ */
+ IPacket getParent();
+
+ /**
+ *
+ * @param packet new parent
+ * @return self
+ */
+ IPacket setParent(IPacket packet);
+
+ /**
+ * Reset any checksums as needed, and call resetChecksum on all parents.
+ */
+ void resetChecksum();
+
+ /**
+ * Sets all payloads parent packet if applicable, then serializes this
+ * packet and all payloads.
+ *
+ * @return a byte[] containing this packet and payloads
+ */
+ byte[] serialize();
+
+ /**
+ * Deserializes this packet layer and all possible payloads.
+ *
+ * NOTE: This method has been deprecated and will be removed in a future
+ * release. It is now recommended to use the Deserializer function provided
+ * by the deserialize() method on each packet to deserialize them. The
+ * Deserializer functions are robust to malformed input.
+ *
+ * @param data bytes to deserialize
+ * @param offset
+ * offset to start deserializing from
+ * @param length
+ * length of the data to deserialize
+ * @return the deserialized data
+ * @deprecated in Cardinal Release
+ */
+ @Deprecated
+ IPacket deserialize(byte[] data, int offset, int length);
+
+ /**
+ * Clone this packet and its payload packet but not its parent.
+ *
+ * @return the clone
+ */
+ Object clone();
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java
new file mode 100644
index 00000000..d75b50a2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class IPv4 extends BasePacket {
+ public static final byte PROTOCOL_ICMP = 0x1;
+ public static final byte PROTOCOL_IGMP = 0x2;
+ public static final byte PROTOCOL_TCP = 0x6;
+ public static final byte PROTOCOL_UDP = 0x11;
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_IGMP, IGMP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer());
+ }
+
+ private static final byte DSCP_MASK = 0x3f;
+ private static final byte DSCP_OFFSET = 2;
+ private static final byte ECN_MASK = 0x3;
+
+ private static final short HEADER_LENGTH = 20;
+
+ protected byte version;
+ protected byte headerLength;
+ protected byte diffServ;
+ protected short totalLength;
+ protected short identification;
+ protected byte flags;
+ protected short fragmentOffset;
+ protected byte ttl;
+ protected byte protocol;
+ protected short checksum;
+ protected int sourceAddress;
+ protected int destinationAddress;
+ protected byte[] options;
+
+ protected boolean isTruncated;
+
+ /**
+ * Default constructor that sets the version to 4.
+ */
+ public IPv4() {
+ super();
+ this.version = 4;
+ this.isTruncated = false;
+ }
+
+ /**
+ * @return the version
+ */
+ public byte getVersion() {
+ return this.version;
+ }
+
+ /**
+ * @param version
+ * the version to set
+ * @return this
+ */
+ public IPv4 setVersion(final byte version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * @return the headerLength
+ */
+ public byte getHeaderLength() {
+ return this.headerLength;
+ }
+
+ /**
+ * Gets the DSCP value (6 bits).
+ *
+ * @return the DSCP value (6 bits)
+ */
+ public byte getDscp() {
+ return (byte) ((this.diffServ >>> DSCP_OFFSET) & DSCP_MASK);
+ }
+
+ /**
+ * Sets the DSCP value (6 bits).
+ *
+ * @param dscp the DSCP value (6 bits)
+ * @return this
+ */
+ public IPv4 setDscp(byte dscp) {
+ this.diffServ &= ~(DSCP_MASK << DSCP_OFFSET);
+ this.diffServ |= (dscp & DSCP_MASK) << DSCP_OFFSET;
+ return this;
+ }
+
+ /**
+ * Gets the ECN value (2 bits).
+ *
+ * @return the ECN value (2 bits)
+ */
+ public byte getEcn() {
+ return (byte) (this.diffServ & ECN_MASK);
+ }
+
+ /**
+ * Sets the ECN value (2 bits).
+ *
+ * @param ecn the ECN value (2 bits)
+ * @return this
+ */
+ public IPv4 setEcn(byte ecn) {
+ this.diffServ &= ~ECN_MASK;
+ this.diffServ |= (ecn & ECN_MASK);
+ return this;
+ }
+
+ /**
+ * Gets the DiffServ octet (including the DSCP and ECN bits).
+ *
+ * @return the diffServ octet (including the DSCP and ECN bits)
+ */
+ public byte getDiffServ() {
+ return this.diffServ;
+ }
+
+ /**
+ * Sets the DiffServ octet (including the DSCP and ECN bits).
+ *
+ * @param diffServ the diffServ octet to set (including the DSCP and ECN
+ * bits)
+ * @return this
+ */
+ public IPv4 setDiffServ(final byte diffServ) {
+ this.diffServ = diffServ;
+ return this;
+ }
+
+ /**
+ * @return the totalLength
+ */
+ public short getTotalLength() {
+ return this.totalLength;
+ }
+
+ /**
+ * @return the identification
+ */
+ public short getIdentification() {
+ return this.identification;
+ }
+
+ public boolean isTruncated() {
+ return this.isTruncated;
+ }
+
+ public void setTruncated(final boolean isTruncated) {
+ this.isTruncated = isTruncated;
+ }
+
+ /**
+ * @param identification
+ * the identification to set
+ * @return this
+ */
+ public IPv4 setIdentification(final short identification) {
+ this.identification = identification;
+ return this;
+ }
+
+ /**
+ * @return the flags
+ */
+ public byte getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ * @return this
+s */
+ public IPv4 setFlags(final byte flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * @return the fragmentOffset
+ */
+ public short getFragmentOffset() {
+ return this.fragmentOffset;
+ }
+
+ /**
+ * @param fragmentOffset
+ * the fragmentOffset to set
+ * @return this
+ */
+ public IPv4 setFragmentOffset(final short fragmentOffset) {
+ this.fragmentOffset = fragmentOffset;
+ return this;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public byte getTtl() {
+ return this.ttl;
+ }
+
+ /**
+ * @param ttl
+ * the ttl to set
+ * @return this
+ */
+ public IPv4 setTtl(final byte ttl) {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * @return the protocol
+ */
+ public byte getProtocol() {
+ return this.protocol;
+ }
+
+ /**
+ * @param protocol
+ * the protocol to set
+ * @return this
+ */
+ public IPv4 setProtocol(final byte protocol) {
+ this.protocol = protocol;
+ return this;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public IPv4 setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * @return the sourceAddress
+ */
+ public int getSourceAddress() {
+ return this.sourceAddress;
+ }
+
+ /**
+ * @param sourceAddress
+ * the sourceAddress to set
+ * @return this
+ */
+ public IPv4 setSourceAddress(final int sourceAddress) {
+ this.sourceAddress = sourceAddress;
+ return this;
+ }
+
+ /**
+ * @param sourceAddress
+ * the sourceAddress to set
+ * @return this
+ */
+ public IPv4 setSourceAddress(final String sourceAddress) {
+ this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
+ return this;
+ }
+
+ /**
+ * @return the destinationAddress
+ */
+ public int getDestinationAddress() {
+ return this.destinationAddress;
+ }
+
+ /**
+ * @param destinationAddress
+ * the destinationAddress to set
+ * @return this
+ */
+ public IPv4 setDestinationAddress(final int destinationAddress) {
+ this.destinationAddress = destinationAddress;
+ return this;
+ }
+
+ /**
+ * @param destinationAddress
+ * the destinationAddress to set
+ * @return this
+ */
+ public IPv4 setDestinationAddress(final String destinationAddress) {
+ this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
+ return this;
+ }
+
+ /**
+ * @return the options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * @param options
+ * the options to set
+ * @return this
+ */
+ public IPv4 setOptions(final byte[] options) {
+ if (options != null && options.length % 4 > 0) {
+ throw new IllegalArgumentException(
+ "Options length must be a multiple of 4");
+ }
+ this.options = options;
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -headerLength : 0 -totalLength : 0
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int optionsLength = 0;
+ if (this.options != null) {
+ optionsLength = this.options.length / 4;
+ }
+ this.headerLength = (byte) (5 + optionsLength);
+
+ this.totalLength = (short) (this.headerLength * 4 + (payloadData == null ? 0
+ : payloadData.length));
+
+ final byte[] data = new byte[this.totalLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put((byte) ((this.version & 0xf) << 4 | this.headerLength & 0xf));
+ bb.put(this.diffServ);
+ bb.putShort(this.totalLength);
+ bb.putShort(this.identification);
+ bb.putShort((short) ((this.flags & 0x7) << 13 | this.fragmentOffset & 0x1fff));
+ bb.put(this.ttl);
+ bb.put(this.protocol);
+ bb.putShort(this.checksum);
+ bb.putInt(this.sourceAddress);
+ bb.putInt(this.destinationAddress);
+ if (this.options != null) {
+ bb.put(this.options);
+ }
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+ for (int i = 0; i < this.headerLength * 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(10, this.checksum);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ short sscratch;
+
+ this.version = bb.get();
+ this.headerLength = (byte) (this.version & 0xf);
+ this.version = (byte) (this.version >> 4 & 0xf);
+ this.diffServ = bb.get();
+ this.totalLength = bb.getShort();
+ this.identification = bb.getShort();
+ sscratch = bb.getShort();
+ this.flags = (byte) (sscratch >> 13 & 0x7);
+ this.fragmentOffset = (short) (sscratch & 0x1fff);
+ this.ttl = bb.get();
+ this.protocol = bb.get();
+ this.checksum = bb.getShort();
+ this.sourceAddress = bb.getInt();
+ this.destinationAddress = bb.getInt();
+
+ if (this.headerLength > 5) {
+ final int optionsLength = (this.headerLength - 5) * 4;
+ this.options = new byte[optionsLength];
+ bb.get(this.options);
+ }
+
+ if (this.totalLength != length) {
+ this.isTruncated = true;
+ } else {
+ this.isTruncated = false;
+ }
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(this.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(this.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /**
+ * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+ * returns the corresponding 32 bit integer.
+ *
+ * @param ipAddress ip address in string form
+ * @return int ip address value
+ */
+ public static int toIPv4Address(final String ipAddress) {
+ if (ipAddress == null) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+ final String[] octets = ipAddress.split("\\.");
+ if (octets.length != 4) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result |= Integer.parseInt(octets[i]) << (3 - i) * 8;
+ }
+ return result;
+ }
+
+ /**
+ * Accepts an IPv4 address in a byte array and returns the corresponding
+ * 32-bit integer value.
+ *
+ * @param ipAddress ip address in byte form
+ * @return int ip address value
+ */
+ public static int toIPv4Address(final byte[] ipAddress) {
+ int ip = 0;
+ for (int i = 0; i < 4; i++) {
+ final int t = (ipAddress[i] & 0xff) << (3 - i) * 8;
+ ip |= t;
+ }
+ return ip;
+ }
+
+ /**
+ * Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx,
+ * e.g., 192.168.0.1.
+ *
+ * @param ipAddress ip address in form
+ * @return string form of ip address
+ */
+ public static String fromIPv4Address(final int ipAddress) {
+ final StringBuffer sb = new StringBuffer();
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result = ipAddress >> (3 - i) * 8 & 0xff;
+ sb.append(result);
+ if (i != 3) {
+ sb.append(".");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Accepts a collection of IPv4 addresses as integers and returns a single
+ * String useful in toString method's containing collections of IP
+ * addresses.
+ *
+ * @param ipAddresses
+ * collection
+ * @return ip addresses in comma-separated string form
+ */
+ public static String fromIPv4AddressCollection(
+ final Collection<Integer> ipAddresses) {
+ if (ipAddresses == null) {
+ return "null";
+ }
+ final StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ for (final Integer ip : ipAddresses) {
+ sb.append(IPv4.fromIPv4Address(ip));
+ sb.append(",");
+ }
+ sb.replace(sb.length() - 1, sb.length(), "]");
+ return sb.toString();
+ }
+
+ /**
+ * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+ * returns the corresponding byte array.
+ *
+ * @param ipAddress
+ * The IP address in the form xx.xxx.xxx.xxx.
+ * @return The IP address separated into bytes
+ */
+ public static byte[] toIPv4AddressBytes(final String ipAddress) {
+ final String[] octets = ipAddress.split("\\.");
+ if (octets.length != 4) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+
+ final byte[] result = new byte[4];
+ for (int i = 0; i < 4; ++i) {
+ result[i] = Integer.valueOf(octets[i]).byteValue();
+ }
+ return result;
+ }
+
+ /**
+ * Accepts an IPv4 address in the form of an integer and returns the
+ * corresponding byte array.
+ *
+ * @param ipAddress
+ * The IP address as an integer.
+ * @return The IP address separated into bytes.
+ */
+ public static byte[] toIPv4AddressBytes(final int ipAddress) {
+ return new byte[] {(byte) (ipAddress >>> 24),
+ (byte) (ipAddress >>> 16), (byte) (ipAddress >>> 8),
+ (byte) ipAddress};
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.checksum;
+ result = prime * result + this.destinationAddress;
+ result = prime * result + this.diffServ;
+ result = prime * result + this.flags;
+ result = prime * result + this.fragmentOffset;
+ result = prime * result + this.headerLength;
+ result = prime * result + this.identification;
+ result = prime * result + Arrays.hashCode(this.options);
+ result = prime * result + this.protocol;
+ result = prime * result + this.sourceAddress;
+ result = prime * result + this.totalLength;
+ result = prime * result + this.ttl;
+ result = prime * result + this.version;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof IPv4)) {
+ return false;
+ }
+ final IPv4 other = (IPv4) obj;
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.destinationAddress != other.destinationAddress) {
+ return false;
+ }
+ if (this.diffServ != other.diffServ) {
+ return false;
+ }
+ if (this.flags != other.flags) {
+ return false;
+ }
+ if (this.fragmentOffset != other.fragmentOffset) {
+ return false;
+ }
+ if (this.headerLength != other.headerLength) {
+ return false;
+ }
+ if (this.identification != other.identification) {
+ return false;
+ }
+ if (!Arrays.equals(this.options, other.options)) {
+ return false;
+ }
+ if (this.protocol != other.protocol) {
+ return false;
+ }
+ if (this.sourceAddress != other.sourceAddress) {
+ return false;
+ }
+ if (this.totalLength != other.totalLength) {
+ return false;
+ }
+ if (this.ttl != other.ttl) {
+ return false;
+ }
+ if (this.version != other.version) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IPv4> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ IPv4 ipv4 = new IPv4();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ byte versionByte = bb.get();
+ ipv4.headerLength = (byte) (versionByte & 0xf);
+ ipv4.setVersion((byte) (versionByte >> 4 & 0xf));
+ ipv4.setDiffServ(bb.get());
+ ipv4.totalLength = bb.getShort();
+ ipv4.identification = bb.getShort();
+ short flagsFragment = bb.getShort();
+ ipv4.flags = (byte) (flagsFragment >> 13 & 0x7);
+ ipv4.fragmentOffset = (short) (flagsFragment & 0x1fff);
+ ipv4.ttl = bb.get();
+ ipv4.protocol = bb.get();
+ ipv4.checksum = bb.getShort();
+ ipv4.sourceAddress = bb.getInt();
+ ipv4.destinationAddress = bb.getInt();
+
+ if (ipv4.headerLength > 5) {
+ checkHeaderLength(length, ipv4.headerLength * 4);
+
+ int optionsLength = (ipv4.headerLength - 5) * 4;
+ ipv4.options = new byte[optionsLength];
+ bb.get(ipv4.options);
+ }
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv4.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(ipv4.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ ipv4.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ ipv4.payload.setParent(ipv4);
+
+ if (ipv4.totalLength != length) {
+ ipv4.isTruncated = true;
+ } else {
+ ipv4.isTruncated = false;
+ }
+
+ return ipv4;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java
new file mode 100644
index 00000000..2e59632a
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import org.onlab.packet.ipv6.Authentication;
+import org.onlab.packet.ipv6.DestinationOptions;
+import org.onlab.packet.ipv6.EncapSecurityPayload;
+import org.onlab.packet.ipv6.Fragment;
+import org.onlab.packet.ipv6.HopByHopOptions;
+import org.onlab.packet.ipv6.IExtensionHeader;
+import org.onlab.packet.ipv6.Routing;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements IPv6 packet format. (RFC 2460)
+ */
+public class IPv6 extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 40; // bytes
+
+ public static final byte PROTOCOL_TCP = 0x6;
+ public static final byte PROTOCOL_UDP = 0x11;
+ public static final byte PROTOCOL_ICMP6 = 0x3A;
+ public static final byte PROTOCOL_HOPOPT = 0x00;
+ public static final byte PROTOCOL_ROUTING = 0x2B;
+ public static final byte PROTOCOL_FRAG = 0x2C;
+ public static final byte PROTOCOL_ESP = 0x32;
+ public static final byte PROTOCOL_AH = 0x33;
+ public static final byte PROTOCOL_DSTOPT = 0x3C;
+
+
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_TCP, TCP.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_UDP, UDP.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_AH, Authentication.deserializer());
+ IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.deserializer());
+ }
+
+ protected byte version;
+ protected byte trafficClass;
+ protected int flowLabel;
+ protected short payloadLength;
+ protected byte nextHeader;
+ protected byte hopLimit;
+ protected byte[] sourceAddress = new byte[Ip6Address.BYTE_LENGTH];
+ protected byte[] destinationAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ /**
+ * Default constructor that sets the version to 6.
+ */
+ public IPv6() {
+ super();
+ this.version = 6;
+ }
+
+ /**
+ * Gets IP version.
+ *
+ * @return the IP version
+ */
+ public byte getVersion() {
+ return this.version;
+ }
+
+ /**
+ * Sets IP version.
+ *
+ * @param version the IP version to set
+ * @return this
+ */
+ public IPv6 setVersion(final byte version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Gets traffic class.
+ *
+ * @return the traffic class
+ */
+ public byte getTrafficClass() {
+ return this.trafficClass;
+ }
+
+ /**
+ * Sets traffic class.
+ *
+ * @param trafficClass the traffic class to set
+ * @return this
+ */
+ public IPv6 setTrafficClass(final byte trafficClass) {
+ this.trafficClass = trafficClass;
+ return this;
+ }
+
+ /**
+ * Gets flow label.
+ *
+ * @return the flow label
+ */
+ public int getFlowLabel() {
+ return this.flowLabel;
+ }
+
+ /**
+ * Sets flow label.
+ *
+ * @param flowLabel the flow label to set
+ * @return this
+ */
+ public IPv6 setFlowLabel(final int flowLabel) {
+ this.flowLabel = flowLabel;
+ return this;
+ }
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public IPv6 setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets hop limit.
+ *
+ * @return the hop limit
+ */
+ public byte getHopLimit() {
+ return this.hopLimit;
+ }
+
+ /**
+ * Sets hop limit.
+ *
+ * @param hopLimit the hop limit to set
+ * @return this
+ */
+ public IPv6 setHopLimit(final byte hopLimit) {
+ this.hopLimit = hopLimit;
+ return this;
+ }
+
+ /**
+ * Gets source address.
+ *
+ * @return the IPv6 source address
+ */
+ public byte[] getSourceAddress() {
+ return this.sourceAddress;
+ }
+
+ /**
+ * Sets source address.
+ *
+ * @param sourceAddress the IPv6 source address to set
+ * @return this
+ */
+ public IPv6 setSourceAddress(final byte[] sourceAddress) {
+ this.sourceAddress = Arrays.copyOfRange(sourceAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets destination address.
+ *
+ * @return the IPv6 destination address
+ */
+ public byte[] getDestinationAddress() {
+ return this.destinationAddress;
+ }
+
+ /**
+ * Sets destination address.
+ *
+ * @param destinationAddress the IPv6 destination address to set
+ * @return this
+ */
+ public IPv6 setDestinationAddress(final byte[] destinationAddress) {
+ this.destinationAddress = Arrays.copyOfRange(destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ this.payloadLength = 0;
+ if (payloadData != null) {
+ this.payloadLength = (short) payloadData.length;
+ }
+
+ final byte[] data = new byte[FIXED_HEADER_LENGTH + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt((this.version & 0xf) << 28 | (this.trafficClass & 0xff) << 20 | this.flowLabel & 0xfffff);
+ bb.putShort(this.payloadLength);
+ bb.put(this.nextHeader);
+ bb.put(this.hopLimit);
+ bb.put(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.put(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int iscratch;
+
+ iscratch = bb.getInt();
+ this.version = (byte) (iscratch >> 28 & 0xf);
+ this.trafficClass = (byte) (iscratch >> 20 & 0xff);
+ this.flowLabel = iscratch & 0xfffff;
+ this.payloadLength = bb.getShort();
+ this.nextHeader = bb.get();
+ this.hopLimit = bb.get();
+ bb.get(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(this.destinationAddress);
+ for (int i = 0; i < 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.trafficClass;
+ result = prime * result + this.flowLabel;
+ result = prime * result + this.hopLimit;
+ result = prime * result + this.nextHeader;
+ result = prime * result + this.payloadLength;
+ bb = ByteBuffer.wrap(this.sourceAddress);
+ for (int i = 0; i < 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.version;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof IPv6)) {
+ return false;
+ }
+ final IPv6 other = (IPv6) obj;
+ if (!Arrays.equals(this.destinationAddress, other.destinationAddress)) {
+ return false;
+ }
+ if (this.trafficClass != other.trafficClass) {
+ return false;
+ }
+ if (this.flowLabel != other.flowLabel) {
+ return false;
+ }
+ if (this.hopLimit != other.hopLimit) {
+ return false;
+ }
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.payloadLength != other.payloadLength) {
+ return false;
+ }
+ if (!Arrays.equals(this.sourceAddress, other.sourceAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for IPv6 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IPv6> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ IPv6 ipv6 = new IPv6();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int iscratch = bb.getInt();
+
+ ipv6.version = (byte) (iscratch >> 28 & 0xf);
+ ipv6.trafficClass = (byte) (iscratch >> 20 & 0xff);
+ ipv6.flowLabel = iscratch & 0xfffff;
+ ipv6.payloadLength = bb.getShort();
+ ipv6.nextHeader = bb.get();
+ ipv6.hopLimit = bb.get();
+ bb.get(ipv6.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(ipv6.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv6.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(ipv6.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ ipv6.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ ipv6.payload.setParent(ipv6);
+
+ return ipv6;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
new file mode 100644
index 00000000..114f126c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * A class representing an IPv4 address.
+ * This class is immutable.
+ */
+public final class Ip4Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ public static final int BYTE_LENGTH = IpAddress.INET_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET_BIT_LENGTH;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ private Ip4Address(byte[] value) {
+ super(VERSION, value);
+ }
+
+ /**
+ * Returns the integer value of this IPv4 address.
+ *
+ * @return the IPv4 address's value as an integer
+ */
+ public int toInt() {
+ ByteBuffer bb = ByteBuffer.wrap(super.toOctets());
+ return bb.getInt();
+ }
+
+ /**
+ * Converts an integer into an IPv4 address.
+ *
+ * @param value an integer representing an IPv4 address value
+ * @return an IPv4 address
+ */
+ public static Ip4Address valueOf(int value) {
+ byte[] bytes =
+ ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+ return new Ip4Address(bytes);
+ }
+
+ /**
+ * Converts a byte array into an IPv4 address.
+ *
+ * @param value the IPv4 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(byte[] value) {
+ return new Ip4Address(value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv4 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip4Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip4Address.valueOf(bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IPv4 address.
+ *
+ * @param inetAddress the InetAddress value to use. It must contain an IPv4
+ * address
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ return new Ip4Address(bytes);
+ }
+ if ((inetAddress instanceof Inet6Address) ||
+ (bytes.length == INET6_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv4 version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET_BYTE_LENGTH) {
+ return new Ip4Address(bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv4 string literal (e.g., "10.2.3.4") into an IP address.
+ *
+ * @param value an IPv4 address value in string form
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IPv4 network mask prefix.
+ *
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
+ * @return a new IPv4 address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip4Address(mask);
+ }
+
+ /**
+ * Creates an IPv4 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
+ * @return a new IPv4 address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static Ip4Address makeMaskedAddress(final Ip4Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip4Address.valueOf(net);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
new file mode 100644
index 00000000..fc442c3e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+/**
+ * The class representing an IPv4 network address.
+ * This class is immutable.
+ */
+public final class Ip4Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET_MASK_LENGTH;
+
+ /**
+ * Constructor for given IPv4 address, and a prefix length.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private Ip4Prefix(Ip4Address address, int prefixLength) {
+ super(address, prefixLength);
+ }
+
+ /**
+ * Returns the IPv4 address value of the prefix.
+ *
+ * @return the IPv4 address value of the prefix
+ */
+ public Ip4Address address() {
+ IpAddress a = super.address();
+ return (Ip4Address) a;
+ }
+
+ /**
+ * Converts an integer and a prefix length into an IPv4 prefix.
+ *
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(int address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IPv4 prefix.
+ *
+ * @param address the IPv4 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts an IPv4 address and a prefix length into an IPv4 prefix.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(Ip4Address address, int prefixLength) {
+ return new Ip4Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16")
+ * into an IPv4 prefix.
+ *
+ * @param address an IP prefix in string form (e.g., "10.1.0.0/16")
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip4Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv4 prefix string: " + address + ". " +
+ "Address must take form \"x.x.x.x/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ Ip4Address ipAddress = Ip4Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new Ip4Prefix(ipAddress, prefixLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
new file mode 100644
index 00000000..d353422b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * A class representing an IPv6 address.
+ * This class is immutable.
+ */
+public final class Ip6Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ public static final int BYTE_LENGTH = IpAddress.INET6_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET6_BIT_LENGTH;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ private Ip6Address(byte[] value) {
+ super(VERSION, value);
+ }
+
+ /**
+ * Converts a byte array into an IPv6 address.
+ *
+ * @param value the IPv6 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(byte[] value) {
+ return new Ip6Address(value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv6 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip6Address.valueOf(bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IPv6 address.
+ *
+ * @param inetAddress the InetAddress value to use. It must contain an IPv6
+ * address
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet6Address) {
+ return new Ip6Address(bytes);
+ }
+ if ((inetAddress instanceof Inet4Address) ||
+ (bytes.length == INET_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv6 version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET6_BYTE_LENGTH) {
+ return new Ip6Address(bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv6 string literal (e.g., "1111:2222::8888") into an IP
+ * address.
+ *
+ * @param value an IPv6 address value in string form
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IPv6 network mask prefix.
+ *
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
+ * @return a new IPv6 address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip6Address(mask);
+ }
+
+ /**
+ * Creates an IPv6 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
+ * @return a new IPv6 address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static Ip6Address makeMaskedAddress(final Ip6Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip6Address.valueOf(net);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
new file mode 100644
index 00000000..4ea9099b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+/**
+ * The class representing an IPv6 network address.
+ * This class is immutable.
+ */
+public final class Ip6Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET6_MASK_LENGTH;
+
+ /**
+ * Constructor for given IPv6 address, and a prefix length.
+ *
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private Ip6Prefix(Ip6Address address, int prefixLength) {
+ super(address, prefixLength);
+ }
+
+ /**
+ * Returns the IPv6 address value of the prefix.
+ *
+ * @return the IPv6 address value of the prefix
+ */
+ public Ip6Address address() {
+ IpAddress a = super.address();
+ return (Ip6Address) a;
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IPv6 prefix.
+ *
+ * @param address the IPv6 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip6Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip6Prefix(Ip6Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts an IPv6 address and a prefix length into an IPv6 prefix.
+ *
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip6Prefix valueOf(Ip6Address address, int prefixLength) {
+ return new Ip6Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "1111:2222::/64")
+ * into an IPv6 prefix.
+ *
+ * @param address an IP prefix in string form (e.g.,"1111:2222::/64")
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv6 prefix string: " + address + ". " +
+ "Address must take form " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ Ip6Address ipAddress = Ip6Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new Ip6Prefix(ipAddress, prefixLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
new file mode 100644
index 00000000..5fdd3276
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedBytes;
+
+
+/**
+ * A class representing an IP address.
+ * This class is immutable.
+ */
+public class IpAddress implements Comparable<IpAddress> {
+ private static final int BIT_MASK = 0x000000ff;
+
+ // IP Versions
+ public enum Version { INET, INET6 };
+
+ // lengths of address, in bytes
+ public static final int INET_BYTE_LENGTH = 4;
+ public static final int INET_BIT_LENGTH = INET_BYTE_LENGTH * Byte.SIZE;
+ public static final int INET6_BYTE_LENGTH = 16;
+ public static final int INET6_BIT_LENGTH = INET6_BYTE_LENGTH * Byte.SIZE;
+
+ private final Version version;
+ private final byte[] octets;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ protected IpAddress(Version version, byte[] value) {
+ checkArguments(version, value, 0);
+ this.version = version;
+ switch (version) {
+ case INET:
+ this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
+ break;
+ case INET6:
+ this.octets = Arrays.copyOf(value, INET6_BYTE_LENGTH);
+ break;
+ default:
+ // Should not be reached
+ this.octets = null;
+ break;
+ }
+ }
+
+ /**
+ * Returns the IP version of this address.
+ *
+ * @return the version
+ */
+ public Version version() {
+ return this.version;
+ }
+
+ /**
+ * Tests whether the IP version of this address is IPv4.
+ *
+ * @return true if the IP version of this address is IPv4, otherwise false.
+ */
+ public boolean isIp4() {
+ return (version() == Ip4Address.VERSION);
+ }
+
+ /**
+ * Tests whether the IP version of this address is IPv6.
+ *
+ * @return true if the IP version of this address is IPv6, otherwise false.
+ */
+ public boolean isIp6() {
+ return (version() == Ip6Address.VERSION);
+ }
+
+ /**
+ * Gets the {@link Ip4Address} view of the IP address.
+ *
+ * @return the {@link Ip4Address} view of the IP address if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Address getIp4Address() {
+ if (!isIp4()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Address
+ if (this instanceof Ip4Address) {
+ return (Ip4Address) this;
+ }
+ return Ip4Address.valueOf(octets);
+ }
+
+ /**
+ * Gets the {@link Ip6Address} view of the IP address.
+ *
+ * @return the {@link Ip6Address} view of the IP address if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Address getIp6Address() {
+ if (!isIp6()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Address
+ if (this instanceof Ip6Address) {
+ return (Ip6Address) this;
+ }
+ return Ip6Address.valueOf(octets);
+ }
+
+ /**
+ * Returns the IP address as a byte array.
+ *
+ * @return a byte array
+ */
+ public byte[] toOctets() {
+ return Arrays.copyOf(octets, octets.length);
+ }
+
+ /**
+ * Computes the IP address byte length for a given IP version.
+ *
+ * @param version the IP version
+ * @return the IP address byte length for the IP version
+ * @throws IllegalArgumentException if the IP version is invalid
+ */
+ public static int byteLength(Version version) {
+ switch (version) {
+ case INET:
+ return INET_BYTE_LENGTH;
+ case INET6:
+ return INET6_BYTE_LENGTH;
+ default:
+ String msg = "Invalid IP version " + version;
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ /**
+ * Converts an integer into an IPv4 address.
+ *
+ * @param value an integer representing an IPv4 address value
+ * @return an IP address
+ */
+ public static IpAddress valueOf(int value) {
+ byte[] bytes =
+ ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+ return new IpAddress(Version.INET, bytes);
+ }
+
+ /**
+ * Converts a byte array into an IP address.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IP address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress valueOf(Version version, byte[] value) {
+ return new IpAddress(version, value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IP address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param version the IP address version
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IP address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress valueOf(Version version, byte[] value,
+ int offset) {
+ checkArguments(version, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return IpAddress.valueOf(version, bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IP address.
+ *
+ * @param inetAddress the InetAddress value to use
+ * @return an IP address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static IpAddress valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ return new IpAddress(Version.INET, bytes);
+ }
+ if (inetAddress instanceof Inet6Address) {
+ return new IpAddress(Version.INET6, bytes);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET_BYTE_LENGTH) {
+ return new IpAddress(Version.INET, bytes);
+ }
+ if (bytes.length == INET6_BYTE_LENGTH) {
+ return new IpAddress(Version.INET6, bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
+ * "1111:2222::8888") into an IP address.
+ *
+ * @param value an IP address value in string form
+ * @return an IP address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static IpAddress valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IP network mask prefix.
+ *
+ * @param version the IP address version
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a new IP address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
+ byte[] mask = makeMaskPrefixArray(version, prefixLength);
+ return new IpAddress(version, mask);
+ }
+
+ /**
+ * Creates an IP address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a new IP address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static IpAddress makeMaskedAddress(final IpAddress address,
+ int prefixLength) {
+ if (address instanceof Ip4Address) {
+ Ip4Address ip4a = (Ip4Address) address;
+ return Ip4Address.makeMaskedAddress(ip4a, prefixLength);
+ } else if (address instanceof Ip6Address) {
+ Ip6Address ip6a = (Ip6Address) address;
+ return Ip6Address.makeMaskedAddress(ip6a, prefixLength);
+ } else {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return IpAddress.valueOf(address.version(), net);
+ }
+ }
+
+ /**
+ * Check if this IP address is zero.
+ *
+ * @return true if this address is zero
+ */
+ public boolean isZero() {
+ for (byte b : octets) {
+ if (b != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if this IP address is self-assigned.
+ *
+ * @return true if this address is self-assigned
+ */
+ public boolean isSelfAssigned() {
+ return isIp4() && octets[0] == (byte) 169;
+ }
+
+ @Override
+ public int compareTo(IpAddress o) {
+ // Compare first the version
+ if (this.version != o.version) {
+ return this.version.compareTo(o.version);
+ }
+
+ // Compare the bytes, one-by-one
+ for (int i = 0; i < this.octets.length; i++) {
+ if (this.octets[i] != o.octets[i]) {
+ return UnsignedBytes.compare(this.octets[i], o.octets[i]);
+ }
+ }
+ return 0; // Equal
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(version, Arrays.hashCode(octets));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (!(obj instanceof IpAddress))) {
+ return false;
+ }
+ IpAddress other = (IpAddress) obj;
+ return (version == other.version) &&
+ Arrays.equals(octets, other.octets);
+ }
+
+ @Override
+ /*
+ * (non-Javadoc)
+ * The string representation of the IP address: "x.x.x.x" for IPv4
+ * addresses, or ':' separated string for IPv6 addresses.
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ // FIXME InetAddress is super slow
+ switch (version) {
+ case INET:
+ return String.format("%d.%d.%d.%d", octets[0] & 0xff,
+ octets[1] & 0xff,
+ octets[2] & 0xff,
+ octets[3] & 0xff);
+ case INET6:
+ default:
+ return ipv6ToStringHelper();
+ }
+ }
+
+ /**
+ * Generates an IP prefix.
+ *
+ * @return the IP prefix of the IP address
+ */
+ public IpPrefix toIpPrefix() {
+
+ if (isIp4()) {
+ return IpPrefix.valueOf(new IpAddress(Version.INET, octets),
+ Ip4Address.BIT_LENGTH);
+ } else {
+ return IpPrefix.valueOf(new IpAddress(Version.INET6, octets),
+ Ip6Address.BIT_LENGTH);
+ }
+ }
+
+ /**
+ * Gets the IP address name for the IP address version.
+ *
+ * @param version the IP address version
+ * @return the IP address name for the IP address version
+ */
+ private static String addressName(Version version) {
+ switch (version) {
+ case INET:
+ return "IPv4";
+ case INET6:
+ return "IPv6";
+ default:
+ break;
+ }
+ return "UnknownIP(" + version + ")";
+ }
+
+ /**
+ * Checks whether the arguments are valid.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in a byte array
+ * @param offset the offset in bytes from the beginning of the byte
+ * array with the address
+ * @throws IllegalArgumentException if any of the arguments is invalid
+ */
+ static void checkArguments(Version version, byte[] value, int offset) {
+ // Check the offset and byte array length
+ int addrByteLength = byteLength(version);
+ if ((offset < 0) || (offset + addrByteLength > value.length)) {
+ String msg;
+ if (value.length < addrByteLength) {
+ msg = "Invalid " + addressName(version) +
+ " address array: array length: " + value.length +
+ ". Must be at least " + addrByteLength;
+ } else {
+ msg = "Invalid " + addressName(version) +
+ " address array: array offset: " + offset +
+ ". Must be in the interval [0, " +
+ (value.length - addrByteLength) + "]";
+ }
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ /**
+ * Creates a byte array for IP network mask prefix.
+ *
+ * @param version the IP address version
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ static byte[] makeMaskPrefixArray(Version version, int prefixLength) {
+ int addrByteLength = byteLength(version);
+ int addrBitLength = addrByteLength * Byte.SIZE;
+
+ // Verify the prefix length
+ if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
+ final String msg = "Invalid IP prefix length: " + prefixLength +
+ ". Must be in the interval [0, " + addrBitLength + "].";
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Number of bytes and extra bits that should be all 1s
+ int maskBytes = prefixLength / Byte.SIZE;
+ int maskBits = prefixLength % Byte.SIZE;
+ byte[] mask = new byte[addrByteLength];
+
+ // Set the bytes and extra bits to 1s
+ for (int i = 0; i < maskBytes; i++) {
+ mask[i] = (byte) 0xff; // Set mask bytes to 1s
+ }
+ for (int i = maskBytes; i < addrByteLength; i++) {
+ mask[i] = 0; // Set remaining bytes to 0s
+ }
+ if (maskBits > 0) {
+ mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
+ }
+ return mask;
+ }
+
+ /**
+ * Creates a byte array that represents an IP address masked with
+ * a network mask of given mask length.
+ *
+ * @param addr the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that represents the IP address masked with
+ * a mask prefix of the specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ static byte[] makeMaskedAddressArray(final IpAddress addr,
+ int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(addr.version(),
+ prefixLength);
+ byte[] net = new byte[mask.length];
+
+ // Mask each byte
+ for (int i = 0; i < net.length; i++) {
+ net[i] = (byte) (addr.octets[i] & mask[i]);
+ }
+ return net;
+ }
+
+ /**
+ * Creates a string based on the IPv6 recommendations for canonical representations found here:
+ * https://tools.ietf.org/html/rfc5952#section-1.
+ * @return A properly formatted IPv6 canonical representation.
+ */
+ private String ipv6ToStringHelper() {
+ //Populate a buffer with the string of the full address with leading zeros stripped
+ StringBuffer buff = new StringBuffer();
+ buff.append(String.format("%x:%x:%x:%x:%x:%x:%x:%x",
+ (((octets[0] & BIT_MASK) << 8) | (octets[1] & BIT_MASK)),
+ (((octets[2] & BIT_MASK) << 8) | (octets[3] & BIT_MASK)),
+ (((octets[4] & BIT_MASK) << 8) | (octets[5] & BIT_MASK)),
+ (((octets[6] & BIT_MASK) << 8) | (octets[7] & BIT_MASK)),
+ (((octets[8] & BIT_MASK) << 8) | (octets[9] & BIT_MASK)),
+ (((octets[10] & BIT_MASK) << 8) | (octets[11] & BIT_MASK)),
+ (((octets[12] & BIT_MASK) << 8) | (octets[13] & BIT_MASK)),
+ (((octets[14] & BIT_MASK) << 8) | (octets[15] & BIT_MASK))));
+ //Initialize variables for tracking longest zero subsequence, tiebreaking by first occurence
+ int longestSeqStart, longestSeqLen, currSeqStart, currSeqLen;
+ longestSeqStart = 0;
+ longestSeqLen = 0;
+ currSeqStart = 0;
+ currSeqLen = 0;
+
+ for (int index = 0; index < buff.length(); index++) {
+ if (buff.charAt(index) == ':') {
+ if (currSeqLen != 0 && buff.charAt(index + 1) == '0') {
+ currSeqLen += 1;
+ }
+ } else if (buff.charAt(index) == '0' && ((index == 0) || (buff.charAt(index - 1) == ':'))) {
+ if (currSeqLen == 0) {
+ currSeqStart = index;
+ }
+ currSeqLen += 1;
+ } else {
+ if (currSeqLen > longestSeqLen) {
+ longestSeqStart = currSeqStart;
+ longestSeqLen = currSeqLen;
+ }
+ currSeqLen = 0;
+ }
+ }
+
+ if (currSeqLen > longestSeqLen) {
+ longestSeqLen = currSeqLen;
+ longestSeqStart = currSeqStart;
+ }
+ if (longestSeqLen > 1) {
+ if (buff.length() == (longestSeqStart + longestSeqLen)) {
+ buff.append(':');
+ }
+
+ buff.delete(longestSeqStart, longestSeqStart + longestSeqLen);
+
+ if (longestSeqStart == 0) {
+ buff.insert(0, ':');
+ }
+ }
+
+ return buff.toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
new file mode 100644
index 00000000..14d07fed
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.util.Objects;
+
+/**
+ * A class representing an IP prefix. A prefix consists of an IP address and
+ * a subnet mask.
+ * This class is immutable.
+ * <p>
+ * NOTE: The stored IP address in the result IP prefix is masked to
+ * contain zeroes in all bits after the prefix length.
+ * </p>
+ */
+public class IpPrefix {
+ // Maximum network mask length
+ public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
+ public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
+
+ private final IpAddress address;
+ private final short prefixLength;
+
+ /**
+ * Constructor for given IP address, and a prefix length.
+ *
+ * @param address the IP address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ protected IpPrefix(IpAddress address, int prefixLength) {
+ checkPrefixLength(address.version(), prefixLength);
+ this.address = IpAddress.makeMaskedAddress(address, prefixLength);
+ this.prefixLength = (short) prefixLength;
+ }
+
+ /**
+ * Returns the IP version of the prefix.
+ *
+ * @return the IP version of the prefix
+ */
+ public IpAddress.Version version() {
+ return address.version();
+ }
+
+ /**
+ * Tests whether the IP version of this prefix is IPv4.
+ *
+ * @return true if the IP version of this prefix is IPv4, otherwise false.
+ */
+ public boolean isIp4() {
+ return address.isIp4();
+ }
+
+ /**
+ * Tests whether the IP version of this prefix is IPv6.
+ *
+ * @return true if the IP version of this prefix is IPv6, otherwise false.
+ */
+ public boolean isIp6() {
+ return address.isIp6();
+ }
+
+ /**
+ * Returns the IP address value of the prefix.
+ *
+ * @return the IP address value of the prefix
+ */
+ public IpAddress address() {
+ return address;
+ }
+
+ /**
+ * Returns the IP address prefix length.
+ *
+ * @return the IP address prefix length
+ */
+ public int prefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Gets the {@link Ip4Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Prefix getIp4Prefix() {
+ if (!isIp4()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Prefix
+ if (this instanceof Ip4Prefix) {
+ return (Ip4Prefix) this;
+ }
+ return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
+ }
+
+ /**
+ * Gets the {@link Ip6Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Prefix getIp6Prefix() {
+ if (!isIp6()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Prefix
+ if (this instanceof Ip6Prefix) {
+ return (Ip6Prefix) this;
+ }
+ return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
+ }
+
+ /**
+ * Converts an integer and a prefix length into an IPv4 prefix.
+ *
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(int address, int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IP prefix.
+ *
+ * @param version the IP address version
+ * @param address the IP address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
+ int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
+ }
+
+ /**
+ * Converts an IP address and a prefix length into an IP prefix.
+ *
+ * @param address the IP address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress address, int prefixLength) {
+ return new IpPrefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
+ * "1111:2222::/64") into an IP prefix.
+ *
+ * @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
+ * "1111:2222::/64")
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpPrefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IP prefix string: " + address + ". " +
+ "Address must take form \"x.x.x.x/y\" or " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ IpAddress ipAddress = IpAddress.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new IpPrefix(ipAddress, prefixLength);
+ }
+
+ /**
+ * Determines whether a given IP prefix is contained within this prefix.
+ *
+ * @param other the IP prefix to test
+ * @return true if the other IP prefix is contained in this prefix,
+ * otherwise false
+ */
+ public boolean contains(IpPrefix other) {
+ if (version() != other.version()) {
+ return false;
+ }
+
+ if (this.prefixLength > other.prefixLength) {
+ return false; // This prefix has smaller prefix size
+ }
+
+ //
+ // Mask the other address with my prefix length.
+ // If the other prefix is within this prefix, the masked address must
+ // be same as the address of this prefix.
+ //
+ IpAddress maskedAddr =
+ IpAddress.makeMaskedAddress(other.address, this.prefixLength);
+ return this.address.equals(maskedAddr);
+ }
+
+ /**
+ * Determines whether a given IP address is contained within this prefix.
+ *
+ * @param other the IP address to test
+ * @return true if the IP address is contained in this prefix, otherwise
+ * false
+ */
+ public boolean contains(IpAddress other) {
+ if (version() != other.version()) {
+ return false;
+ }
+
+ //
+ // Mask the other address with my prefix length.
+ // If the other prefix is within this prefix, the masked address must
+ // be same as the address of this prefix.
+ //
+ IpAddress maskedAddr =
+ IpAddress.makeMaskedAddress(other, this.prefixLength);
+ return this.address.equals(maskedAddr);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLength);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (!(obj instanceof IpPrefix))) {
+ return false;
+ }
+ IpPrefix other = (IpPrefix) obj;
+ return ((prefixLength == other.prefixLength) &&
+ address.equals(other.address));
+ }
+
+ @Override
+ /*
+ * (non-Javadoc)
+ * The format is "x.x.x.x/y" for IPv4 prefixes.
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(address.toString());
+ builder.append("/");
+ builder.append(String.format("%d", prefixLength));
+ return builder.toString();
+ }
+
+ /**
+ * Checks whether the prefix length is valid.
+ *
+ * @param version the IP address version
+ * @param prefixLength the prefix length value to check
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private static void checkPrefixLength(IpAddress.Version version,
+ int prefixLength) {
+ int maxPrefixLen = 0;
+
+ switch (version) {
+ case INET:
+ maxPrefixLen = MAX_INET_MASK_LENGTH;
+ break;
+ case INET6:
+ maxPrefixLen = MAX_INET6_MASK_LENGTH;
+ break;
+ default:
+ String msg = "Invalid IP version " + version;
+ throw new IllegalArgumentException(msg);
+ }
+
+ if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
+ String msg = "Invalid prefix length " + prefixLength + ". " +
+ "The value must be in the interval [0, " +
+ maxPrefixLen + "]";
+ throw new IllegalArgumentException(msg);
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java
new file mode 100644
index 00000000..78b4f3fa
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * This class represents an Link Local Control header that is used in Ethernet
+ * 802.3.
+ *
+ *
+ */
+public class LLC extends BasePacket {
+
+ public static final byte LLC_HEADER_LENGTH = 3;
+
+ private byte dsap = 0;
+ private byte ssap = 0;
+ private byte ctrl = 0;
+
+ public byte getDsap() {
+ return this.dsap;
+ }
+
+ public void setDsap(final byte dsap) {
+ this.dsap = dsap;
+ }
+
+ public byte getSsap() {
+ return this.ssap;
+ }
+
+ public void setSsap(final byte ssap) {
+ this.ssap = ssap;
+ }
+
+ public byte getCtrl() {
+ return this.ctrl;
+ }
+
+ public void setCtrl(final byte ctrl) {
+ this.ctrl = ctrl;
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[3];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.dsap);
+ bb.put(this.ssap);
+ bb.put(this.ctrl);
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.dsap = bb.get();
+ this.ssap = bb.get();
+ this.ctrl = bb.get();
+ return this;
+ }
+
+ /**
+ * Deserializer function for LLC packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLC> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, LLC_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLC llc = new LLC();
+
+ llc.dsap = bb.get();
+ llc.ssap = bb.get();
+ llc.ctrl = bb.get();
+
+ return llc;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java
new file mode 100644
index 00000000..ae9d7173
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class LLDP extends BasePacket {
+ public static final byte CHASSIS_TLV_TYPE = 1;
+ public static final short CHASSIS_TLV_SIZE = 7;
+ public static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+ public static final byte PORT_TLV_TYPE = 2;
+ public static final short PORT_TLV_SIZE = 5;
+ public static final byte PORT_TLV_SUBTYPE = 2;
+
+ public static final byte TTL_TLV_TYPE = 3;
+ public static final short TTL_TLV_SIZE = 2;
+
+ protected LLDPTLV chassisId;
+ protected LLDPTLV portId;
+ protected LLDPTLV ttl;
+ protected List<LLDPTLV> optionalTLVList;
+ protected short ethType;
+
+ public LLDP() {
+ this.optionalTLVList = new LinkedList<>();
+ this.ethType = Ethernet.TYPE_LLDP;
+ }
+
+ /**
+ * @return the chassisId
+ */
+ public LLDPTLV getChassisId() {
+ return this.chassisId;
+ }
+
+ /**
+ * @param chassis
+ * the chassisId to set
+ * @return this
+ */
+ public LLDP setChassisId(final LLDPTLV chassis) {
+ this.chassisId = chassis;
+ return this;
+ }
+
+ /**
+ * @return the portId
+ */
+ public LLDPTLV getPortId() {
+ return this.portId;
+ }
+
+ /**
+ * @param portId
+ * the portId to set
+ * @return this
+ */
+ public LLDP setPortId(final LLDPTLV portId) {
+ this.portId = portId;
+ return this;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public LLDPTLV getTtl() {
+ return this.ttl;
+ }
+
+ /**
+ * @param ttl
+ * the ttl to set
+ * @return this
+ */
+ public LLDP setTtl(final LLDPTLV ttl) {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * @return the optionalTLVList
+ */
+ public List<LLDPTLV> getOptionalTLVList() {
+ return this.optionalTLVList;
+ }
+
+ /**
+ * @param optionalTLVList
+ * the optionalTLVList to set
+ * @return this
+ */
+ public LLDP setOptionalTLVList(final List<LLDPTLV> optionalTLVList) {
+ this.optionalTLVList = optionalTLVList;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ int length = 2 + this.chassisId.getLength() + 2
+ + this.portId.getLength() + 2 + this.ttl.getLength() + 2;
+ for (final LLDPTLV tlv : this.optionalTLVList) {
+ length += 2 + tlv.getLength();
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.chassisId.serialize());
+ bb.put(this.portId.serialize());
+ bb.put(this.ttl.serialize());
+ for (final LLDPTLV tlv : this.optionalTLVList) {
+ bb.put(tlv.serialize());
+ }
+ bb.putShort((short) 0); // End of LLDPDU
+
+ /*
+ * if (this.parent != null && this.parent instanceof Ethernet) {
+ * ((Ethernet) this.parent).setEtherType(this.ethType); }
+ */
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLDPTLV tlv;
+ do {
+ try {
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+ } catch (DeserializationException e) {
+ break;
+ }
+
+ // if there was a failure to deserialize stop processing TLVs
+ if (tlv == null) {
+ break;
+ }
+ switch (tlv.getType()) {
+ case 0x0:
+ // can throw this one away, its just an end delimiter
+ break;
+ case 0x1:
+ this.chassisId = tlv;
+ break;
+ case 0x2:
+ this.portId = tlv;
+ break;
+ case 0x3:
+ this.ttl = tlv;
+ break;
+
+ default:
+ this.optionalTLVList.add(tlv);
+ break;
+ }
+ } while (tlv.getType() != 0 && bb.hasRemaining());
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 883;
+ int result = super.hashCode();
+ result = prime * result
+ + (this.chassisId == null ? 0 : this.chassisId.hashCode());
+ result = prime * result + this.optionalTLVList.hashCode();
+ result = prime * result
+ + (this.portId == null ? 0 : this.portId.hashCode());
+ result = prime * result + (this.ttl == null ? 0 : this.ttl.hashCode());
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof LLDP)) {
+ return false;
+ }
+ final LLDP other = (LLDP) obj;
+ if (this.chassisId == null) {
+ if (other.chassisId != null) {
+ return false;
+ }
+ } else if (!this.chassisId.equals(other.chassisId)) {
+ return false;
+ }
+ if (!this.optionalTLVList.equals(other.optionalTLVList)) {
+ return false;
+ }
+ if (this.portId == null) {
+ if (other.portId != null) {
+ return false;
+ }
+ } else if (!this.portId.equals(other.portId)) {
+ return false;
+ }
+ if (this.ttl == null) {
+ if (other.ttl != null) {
+ return false;
+ }
+ } else if (!this.ttl.equals(other.ttl)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for LLDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, 0);
+
+ LLDP lldp = new LLDP();
+
+ int currentIndex = 0;
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLDPTLV tlv;
+ do {
+ // Each new TLV must be a minimum of 2 bytes
+ // (containing the type and length fields).
+ currentIndex += 2;
+ checkHeaderLength(length, currentIndex);
+
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+
+ // if there was a failure to deserialize stop processing TLVs
+ if (tlv == null) {
+ break;
+ }
+ switch (tlv.getType()) {
+ case 0x0:
+ // can throw this one away, it's just an end delimiter
+ break;
+ case 0x1:
+ lldp.chassisId = tlv;
+ break;
+ case 0x2:
+ lldp.portId = tlv;
+ break;
+ case 0x3:
+ lldp.ttl = tlv;
+ break;
+ default:
+ lldp.optionalTLVList.add(tlv);
+ break;
+ }
+
+ currentIndex += tlv.getLength();
+ } while (tlv.getType() != 0);
+
+ return lldp;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
new file mode 100644
index 00000000..bedf439f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ **/
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * The class representing LLDP Organizationally Specific TLV.
+ *
+ */
+public class LLDPOrganizationalTLV extends LLDPTLV {
+ public static final int OUI_LENGTH = 3;
+ public static final int SUBTYPE_LENGTH = 1;
+ public static final byte ORGANIZATIONAL_TLV_TYPE = 127;
+ public static final int MAX_INFOSTRING_LENGTH = 507;
+
+ protected byte[] oui;
+ protected byte subType;
+ private byte[] infoString;
+
+ public LLDPOrganizationalTLV() {
+ this.type = LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE;
+ }
+
+ /**
+ * Set the value of OUI.
+ *
+ * @param oui
+ * The value of OUI to be set.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setOUI(final byte[] oui) {
+ if (oui.length != LLDPOrganizationalTLV.OUI_LENGTH) {
+ throw new IllegalArgumentException("The length of OUI must be "
+ + LLDPOrganizationalTLV.OUI_LENGTH + ", but it is "
+ + oui.length);
+ }
+ this.oui = Arrays.copyOf(oui, oui.length);
+ return this;
+ }
+
+ /**
+ * Returns the value of the OUI.
+ *
+ * @return The value of the OUI .
+ */
+ public byte[] getOUI() {
+ return Arrays.copyOf(this.oui, this.oui.length);
+ }
+
+ /**
+ * Set the value of sub type.
+ *
+ * @param subType
+ * The value of sub type to be set.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setSubType(final byte subType) {
+ this.subType = subType;
+ return this;
+ }
+
+ /**
+ * Returns the value of the sub type.
+ *
+ * @return The value of the sub type.
+ */
+ public byte getSubType() {
+ return this.subType;
+ }
+
+ /**
+ * Set the value of information string.
+ *
+ * @param infoString
+ * the byte array of the value of information string.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setInfoString(final byte[] infoString) {
+ if (infoString.length > LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH) {
+ throw new IllegalArgumentException(
+ "The length of infoString cannot exceed "
+ + LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH);
+ }
+ this.infoString = Arrays.copyOf(infoString, infoString.length);
+ return this;
+ }
+
+ /**
+ * Set the value of information string. The String value is automatically
+ * converted into byte array with UTF-8 encoding.
+ *
+ * @param infoString
+ * the String value of information string.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setInfoString(final String infoString) {
+ final byte[] infoStringBytes = infoString.getBytes(Charset
+ .forName("UTF-8"));
+ return this.setInfoString(infoStringBytes);
+ }
+
+ /**
+ * Returns the value of information string.
+ *
+ * @return the value of information string.
+ */
+ public byte[] getInfoString() {
+ return Arrays.copyOf(this.infoString, this.infoString.length);
+ }
+
+ @Override
+ public byte[] serialize() {
+ if (this.type != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ return super.serialize();
+ }
+ final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
+ + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
+ this.value = new byte[valueLength];
+ final ByteBuffer bb = ByteBuffer.wrap(this.value);
+ bb.put(this.oui);
+ bb.put(this.subType);
+ bb.put(this.infoString);
+ return super.serialize();
+ }
+
+ @Override
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ super.deserialize(bb);
+ if (this.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ return this;
+ }
+
+ if (this.getLength() <= OUI_LENGTH + SUBTYPE_LENGTH) {
+ throw new DeserializationException(
+ "TLV length is less than required for organizational TLV");
+ }
+
+ final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
+
+ final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
+ optionalField.get(oui);
+ this.setOUI(oui);
+
+ this.setSubType(optionalField.get());
+
+ final byte[] infoString = new byte[this.getLength()
+ - LLDPOrganizationalTLV.OUI_LENGTH
+ - LLDPOrganizationalTLV.SUBTYPE_LENGTH];
+ optionalField.get(infoString);
+ this.setInfoString(infoString);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 1423;
+ int result = 1;
+ result = prime * result + this.type;
+ result = prime * result + this.length;
+ result = prime * result + Arrays.hashCode(this.oui);
+ result = prime * result + this.subType;
+ result = prime * result + Arrays.hashCode(this.infoString);
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof LLDPOrganizationalTLV)) {
+ return false;
+ }
+
+ final LLDPOrganizationalTLV other = (LLDPOrganizationalTLV) o;
+ if (this.type != other.type) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ if (!Arrays.equals(this.oui, other.oui)) {
+ return false;
+ }
+ if (this.subType != other.subType) {
+ return false;
+ }
+ if (!Arrays.equals(this.infoString, other.infoString)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
new file mode 100644
index 00000000..77efe1b7
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ *
+ *
+ */
+public class LLDPTLV {
+ protected byte type;
+ protected short length;
+ protected byte[] value;
+
+ /**
+ * @return the type
+ */
+ public byte getType() {
+ return this.type;
+ }
+
+ /**
+ * @param type
+ * the type to set
+ * @return this
+ */
+ public LLDPTLV setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * @param length
+ * the length to set
+ * @return this
+ */
+ public LLDPTLV setLength(final short length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @return the value
+ */
+ public byte[] getValue() {
+ return this.value;
+ }
+
+ /**
+ * @param value
+ * the value to set
+ * @return this
+ */
+ public LLDPTLV setValue(final byte[] value) {
+ this.value = value;
+ return this;
+ }
+
+ public byte[] serialize() {
+ // type = 7 bits
+ // info string length 9 bits, each value == byte
+ // info string
+ final short scratch = (short) ((0x7f & this.type) << 9 | 0x1ff & this.length);
+ final byte[] data = new byte[2 + this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.putShort(scratch);
+ if (this.value != null) {
+ bb.put(this.value);
+ }
+ return data;
+ }
+
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ if (bb.remaining() < 2) {
+ throw new DeserializationException(
+ "Not enough bytes to deserialize TLV type and length");
+ }
+ short typeLength;
+ typeLength = bb.getShort();
+ this.type = (byte) (typeLength >> 9 & 0x7f);
+ this.length = (short) (typeLength & 0x1ff);
+
+ if (this.length > 0) {
+ this.value = new byte[this.length];
+
+ // if there is an underrun just toss the TLV
+ if (bb.remaining() < this.length) {
+ throw new DeserializationException(
+ "Remaining bytes are less then the length of the TLV");
+ }
+ bb.get(this.value);
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 1423;
+ int result = 1;
+ result = prime * result + this.length;
+ result = prime * result + this.type;
+ result = prime * result + Arrays.hashCode(this.value);
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof LLDPTLV)) {
+ return false;
+ }
+ final LLDPTLV other = (LLDPTLV) obj;
+ if (this.length != other.length) {
+ return false;
+ }
+ if (this.type != other.type) {
+ return false;
+ }
+ if (!Arrays.equals(this.value, other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java
new file mode 100644
index 00000000..47dbeed2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java
@@ -0,0 +1,147 @@
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+public class MPLS extends BasePacket {
+ public static final int HEADER_LENGTH = 4;
+
+ public static final byte PROTOCOL_IPV4 = 0x1;
+ public static final byte PROTOCOL_MPLS = 0x6;
+ static Map<Byte, Deserializer<? extends IPacket>> protocolDeserializerMap
+ = new HashMap<>();
+
+ static {
+ protocolDeserializerMap.put(PROTOCOL_IPV4, IPv4.deserializer());
+ protocolDeserializerMap.put(PROTOCOL_MPLS, MPLS.deserializer());
+ }
+
+ protected int label; //20bits
+ protected byte bos; //1bit
+ protected byte ttl; //8bits
+ protected byte protocol;
+
+ /**
+ * Default constructor that sets the version to 4.
+ */
+ public MPLS() {
+ super();
+ this.bos = 1;
+ this.protocol = PROTOCOL_IPV4;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (payload != null) {
+ payload.setParent(this);
+ payloadData = payload.serialize();
+ }
+
+ byte[] data = new byte[(4 + ((payloadData != null) ? payloadData.length : 0)) ];
+ ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(((this.label & 0x000fffff) << 12) | ((this.bos & 0x1) << 8 | (this.ttl & 0xff)));
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int mplsheader = bb.getInt();
+ this.label = ((mplsheader & 0xfffff000) >> 12);
+ this.bos = (byte) ((mplsheader & 0x00000100) >> 8);
+ this.bos = (byte) (mplsheader & 0x000000ff);
+ this.protocol = (this.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
+
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(this.protocol)) {
+ deserializer = protocolDeserializerMap.get(this.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /**
+ * Returns the MPLS label.
+ *
+ * @return MPLS label
+ */
+ public int getLabel() {
+ return label;
+ }
+
+ /**
+ * Sets the MPLS label.
+ *
+ * @param label MPLS label
+ */
+ public void setLabel(int label) {
+ this.label = label;
+ }
+
+ /**
+ * Returns the MPLS TTL of the packet.
+ *
+ * @return MPLS TTL of the packet
+ */
+ public byte getTtl() {
+ return ttl;
+ }
+
+ /**
+ * Sets the MPLS TTL of the packet.
+ *
+ * @param ttl MPLS TTL
+ */
+ public void setTtl(byte ttl) {
+ this.ttl = ttl;
+ }
+
+ /**
+ * Deserializer function for MPLS packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<MPLS> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ MPLS mpls = new MPLS();
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int mplsheader = bb.getInt();
+ mpls.label = ((mplsheader & 0xfffff000) >>> 12);
+ mpls.bos = (byte) ((mplsheader & 0x00000100) >> 8);
+ mpls.ttl = (byte) (mplsheader & 0x000000ff);
+ mpls.protocol = (mpls.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
+
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(mpls.protocol)) {
+ deserializer = protocolDeserializerMap.get(mpls.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ mpls.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ mpls.payload.setParent(mpls);
+
+ return mpls;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
new file mode 100644
index 00000000..89cddbae
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import java.util.Arrays;
+
+/**
+ * The class representing MAC address.
+ */
+public class MacAddress {
+
+ public static final MacAddress ZERO = valueOf("00:00:00:00:00:00");
+ public static final MacAddress BROADCAST = valueOf("ff:ff:ff:ff:ff:ff");
+
+ private static final byte[] LL = new byte[]{
+ 0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00,
+ 0x00, 0x0e, 0x03
+ };
+
+ public static final int MAC_ADDRESS_LENGTH = 6;
+ private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+
+ public MacAddress(final byte[] address) {
+ this.address = Arrays.copyOf(address, MacAddress.MAC_ADDRESS_LENGTH);
+ }
+
+ /**
+ * Returns a MAC address instance representing the value of the specified
+ * {@code String}.
+ *
+ * @param address the String representation of the MAC Address to be parsed.
+ * @return a MAC Address instance representing the value of the specified
+ * {@code String}.
+ * @throws IllegalArgumentException if the string cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final String address) {
+ final String[] elements = address.split(":");
+ if (elements.length != MacAddress.MAC_ADDRESS_LENGTH) {
+ throw new IllegalArgumentException(
+ "Specified MAC Address must contain 12 hex digits"
+ + " separated pairwise by :'s.");
+ }
+
+ final byte[] addressInBytes = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ for (int i = 0; i < MacAddress.MAC_ADDRESS_LENGTH; i++) {
+ final String element = elements[i];
+ addressInBytes[i] = (byte) Integer.parseInt(element, 16);
+ }
+
+ return new MacAddress(addressInBytes);
+ }
+
+ /**
+ * Returns a MAC address instance representing the specified {@code byte}
+ * array.
+ *
+ * @param address the byte array to be parsed.
+ * @return a MAC address instance representing the specified {@code byte}
+ * array.
+ * @throws IllegalArgumentException if the byte array cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final byte[] address) {
+ if (address.length != MacAddress.MAC_ADDRESS_LENGTH) {
+ throw new IllegalArgumentException("the length is not "
+ + MacAddress.MAC_ADDRESS_LENGTH);
+ }
+
+ return new MacAddress(address);
+ }
+
+ /**
+ * Returns a MAC address instance representing the specified {@code long}
+ * value. The lower 48 bits of the long value are used to parse as a MAC
+ * address.
+ *
+ * @param address the long value to be parsed. The lower 48 bits are used for a
+ * MAC address.
+ * @return a MAC address instance representing the specified {@code long}
+ * value.
+ * @throws IllegalArgumentException if the long value cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final long address) {
+ final byte[] addressInBytes = new byte[]{
+ (byte) (address >> 40 & 0xff), (byte) (address >> 32 & 0xff),
+ (byte) (address >> 24 & 0xff), (byte) (address >> 16 & 0xff),
+ (byte) (address >> 8 & 0xff), (byte) (address >> 0 & 0xff)};
+
+ return new MacAddress(addressInBytes);
+ }
+
+ /**
+ * Returns the length of the {@code MACAddress}.
+ *
+ * @return the length of the {@code MACAddress}.
+ */
+ public int length() {
+ return this.address.length;
+ }
+
+ /**
+ * Returns the value of the {@code MACAddress} as a {@code byte} array.
+ *
+ * @return the numeric value represented by this object after conversion to
+ * type {@code byte} array.
+ */
+ public byte[] toBytes() {
+ return Arrays.copyOf(this.address, this.address.length);
+ }
+
+ /**
+ * Returns the value of the {@code MACAddress} as a {@code long}.
+ *
+ * @return the numeric value represented by this object after conversion to
+ * type {@code long}.
+ */
+ public long toLong() {
+ long mac = 0;
+ for (int i = 0; i < 6; i++) {
+ final long t = (this.address[i] & 0xffL) << (5 - i) * 8;
+ mac |= t;
+ }
+ return mac;
+ }
+
+ /**
+ * Returns {@code true} if the MAC address is the broadcast address.
+ *
+ * @return {@code true} if the MAC address is the broadcast address.
+ */
+ public boolean isBroadcast() {
+ for (final byte b : this.address) {
+ if (b != -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if the MAC address is the multicast address.
+ *
+ * @return {@code true} if the MAC address is the multicast address.
+ */
+ public boolean isMulticast() {
+ if (this.isBroadcast()) {
+ return false;
+ }
+ return (this.address[0] & 0x01) != 0;
+ }
+
+ /**
+ * Returns true if this MAC address is link local.
+ *
+ * @return true if link local
+ */
+ public boolean isLinkLocal() {
+ return LL[0] == address[0] && LL[1] == address[1] && LL[2] == address[2] &&
+ LL[3] == address[3] && LL[4] == address[4] &&
+ (LL[5] == address[5] || LL[6] == address[5] || LL[7] == address[5]);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof MacAddress)) {
+ return false;
+ }
+
+ final MacAddress other = (MacAddress) o;
+ return Arrays.equals(this.address, other.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(toLong());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ for (final byte b : this.address) {
+ if (builder.length() > 0) {
+ builder.append(":");
+ }
+ builder.append(String.format("%02X", b & 0xFF));
+ }
+ return builder.toString();
+ }
+
+ /**
+ * @return MAC address in string representation without colons (useful for
+ * radix tree storage)
+ */
+ public String toStringNoColon() {
+ final StringBuilder builder = new StringBuilder();
+ for (final byte b : this.address) {
+ builder.append(String.format("%02X", b & 0xFF));
+ }
+ return builder.toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
new file mode 100644
index 00000000..09a939fc
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
@@ -0,0 +1,74 @@
+package org.onlab.packet;
+
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Representation of a MPLS label.
+ */
+public class MplsLabel {
+
+ private final int mplsLabel;
+
+ // An MPLS Label maximum 20 bits.
+ public static final int MAX_MPLS = 0xFFFFF;
+
+ protected MplsLabel(int value) {
+ this.mplsLabel = value;
+ }
+
+ public static MplsLabel mplsLabel(int value) {
+
+ if (value < 0 || value > MAX_MPLS) {
+ String errorMsg = "MPLS label value " + value +
+ " is not in the interval [0, 0xFFFFF]";
+ throw new IllegalArgumentException(errorMsg);
+ }
+ return new MplsLabel(value);
+ }
+
+ public int toInt() {
+ return this.mplsLabel;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof MplsLabel) {
+
+ MplsLabel other = (MplsLabel) obj;
+
+ if (this.mplsLabel == other.mplsLabel) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.mplsLabel;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(this.mplsLabel);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
new file mode 100644
index 00000000..5b3902a8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * ONOS LLDP containing organizational TLV for ONOS device dicovery.
+ */
+public class ONOSLLDP extends LLDP {
+
+ public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
+ public static final String DEFAULT_DEVICE = "INVALID";
+ public static final String DEFAULT_NAME = "ONOS Discovery";
+
+ public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
+ 0x01};
+ public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
+ (byte) 0xc2, 0x00, 0x00, 0x0e};
+ public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+
+ private static final byte NAME_SUBTYPE = 1;
+ private static final byte DEVICE_SUBTYPE = 2;
+ private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
+ private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
+ private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
+ private final LLDPOrganizationalTLV deviceTLV = new LLDPOrganizationalTLV();
+
+ // TLV constants: type, size and subtype
+ // Organizationally specific TLV also have packet offset and contents of TLV
+ // header
+ private static final byte CHASSIS_TLV_TYPE = 1;
+ private static final byte CHASSIS_TLV_SIZE = 7;
+ private static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+ private static final byte PORT_TLV_TYPE = 2;
+ private static final byte PORT_TLV_SIZE = 5;
+ private static final byte PORT_TLV_SUBTYPE = 2;
+
+ private static final byte TTL_TLV_TYPE = 3;
+
+
+ private final byte[] ttlValue = new byte[] {0, 0x78};
+
+ public ONOSLLDP() {
+ super();
+ setName(DEFAULT_NAME);
+ setDevice(DEFAULT_DEVICE);
+ setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
+ setTtl(new LLDPTLV().setType(TTL_TLV_TYPE)
+ .setLength((short) ttlValue.length)
+ .setValue(ttlValue));
+
+ }
+
+ private ONOSLLDP(LLDP lldp) {
+ this.portId = lldp.getPortId();
+ this.chassisId = lldp.getChassisId();
+ this.ttl = lldp.getTtl();
+ this.optionalTLVList = lldp.getOptionalTLVList();
+ }
+
+ public void setName(String name) {
+ nameTLV.setLength((short) (name.length() + NAME_LENGTH));
+ nameTLV.setInfoString(name);
+ nameTLV.setSubType(NAME_SUBTYPE);
+ nameTLV.setOUI(ONLAB_OUI);
+ }
+
+ public void setDevice(String device) {
+ deviceTLV.setInfoString(device);
+ deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
+ deviceTLV.setSubType(DEVICE_SUBTYPE);
+ deviceTLV.setOUI(ONLAB_OUI);
+ }
+
+ public void setChassisId(final ChassisId chassisId) {
+ MacAddress chassisMac = MacAddress.valueOf(chassisId.value());
+ byte[] chassis = ArrayUtils.addAll(new byte[] {CHASSIS_TLV_SUBTYPE},
+ chassisMac.toBytes());
+
+ LLDPTLV chassisTLV = new LLDPTLV();
+ chassisTLV.setLength(CHASSIS_TLV_SIZE);
+ chassisTLV.setType(CHASSIS_TLV_TYPE);
+ chassisTLV.setValue(chassis);
+ this.setChassisId(chassisTLV);
+ }
+
+ public void setPortId(final int portNumber) {
+ byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
+ ByteBuffer.allocate(4).putInt(portNumber).array());
+
+ LLDPTLV portTLV = new LLDPTLV();
+ portTLV.setLength(PORT_TLV_SIZE);
+ portTLV.setType(PORT_TLV_TYPE);
+ portTLV.setValue(port);
+ this.setPortId(portTLV);
+ }
+
+ public LLDPOrganizationalTLV getNameTLV() {
+ for (LLDPTLV tlv : this.getOptionalTLVList()) {
+ if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+ if (orgTLV.getSubType() == NAME_SUBTYPE) {
+ return orgTLV;
+ }
+ }
+ }
+ return null;
+ }
+
+ public LLDPOrganizationalTLV getDeviceTLV() {
+ for (LLDPTLV tlv : this.getOptionalTLVList()) {
+ if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+ if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
+ return orgTLV;
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getNameString() {
+ LLDPOrganizationalTLV tlv = getNameTLV();
+ if (tlv != null) {
+ return new String(tlv.getInfoString(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
+ public String getDeviceString() {
+ LLDPOrganizationalTLV tlv = getDeviceTLV();
+ if (tlv != null) {
+ return new String(tlv.getInfoString(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
+ public Integer getPort() {
+ ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
+ portBB.position(1);
+ return portBB.getInt();
+ }
+
+ /**
+ * Given an ethernet packet, determines if this is an LLDP from
+ * ONOS and returns the device the LLDP came from.
+ * @param eth an ethernet packet
+ * @return a the lldp packet or null
+ */
+ public static ONOSLLDP parseONOSLLDP(Ethernet eth) {
+ if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
+ eth.getEtherType() == Ethernet.TYPE_BSN) {
+ ONOSLLDP onosLldp = new ONOSLLDP((LLDP) eth.getPayload()); //(ONOSLLDP) eth.getPayload();
+ if (ONOSLLDP.DEFAULT_NAME.equals(onosLldp.getNameString())) {
+ return onosLldp;
+ }
+ }
+ return null;
+ }
+
+
+
+
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
new file mode 100644
index 00000000..c3bede2f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utilities for working with packet headers.
+ */
+public final class PacketUtils {
+
+ private PacketUtils() {
+ }
+
+ /**
+ * Check the length of the input buffer is appropriate given the offset and
+ * length parameters.
+ *
+ * @param byteLength length of the input buffer array
+ * @param offset offset given to begin reading bytes from
+ * @param length length given to read up until
+ * @throws DeserializationException if the input parameters don't match up (i.e
+ * we can't read that many bytes from the buffer at the given offest)
+ */
+ public static void checkBufferLength(int byteLength, int offset, int length)
+ throws DeserializationException {
+ boolean ok = (offset >= 0 && offset < byteLength);
+ ok = ok & (length >= 0 && offset + length <= byteLength);
+
+ if (!ok) {
+ throw new DeserializationException("Unable to read " + length + " bytes from a "
+ + byteLength + " byte array starting at offset " + offset);
+ }
+ }
+
+ /**
+ * Check that there are enough bytes in the buffer to read some number of
+ * bytes that we need to read a full header.
+ *
+ * @param givenLength given size of the buffer
+ * @param requiredLength number of bytes we need to read some header fully
+ * @throws DeserializationException if there aren't enough bytes
+ */
+ public static void checkHeaderLength(int givenLength, int requiredLength)
+ throws DeserializationException {
+ if (requiredLength > givenLength) {
+ throw new DeserializationException(requiredLength
+ + " bytes are needed to continue deserialization, however only "
+ + givenLength + " remain in buffer");
+ }
+ }
+
+ /**
+ * Check the input parameters are sane and there's enough bytes to read
+ * the required length.
+ *
+ * @param data input byte buffer
+ * @param offset offset of the start of the header
+ * @param length length given to deserialize the header
+ * @param requiredLength length needed to deserialize header
+ * @throws DeserializationException if we're unable to deserialize the
+ * packet based on the input parameters
+ */
+ public static void checkInput(byte[] data, int offset, int length, int requiredLength)
+ throws DeserializationException {
+ checkNotNull(data);
+ checkBufferLength(data.length, offset, length);
+ checkHeaderLength(length, requiredLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java
new file mode 100644
index 00000000..297fee7c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java
@@ -0,0 +1,423 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.onlab.packet;
+
+import org.slf4j.Logger;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * RADIUS packet.
+ */
+public class RADIUS extends BasePacket {
+ protected byte code;
+ protected byte identifier;
+ protected short length = RADIUS_MIN_LENGTH;
+ protected byte[] authenticator = new byte[16];
+ protected List<RADIUSAttribute> attributes = new ArrayList<>();
+
+ // RADIUS parameters
+ public static final short RADIUS_MIN_LENGTH = 20;
+ public static final short MAX_ATTR_VALUE_LENGTH = 253;
+ public static final short RADIUS_MAX_LENGTH = 4096;
+
+ // RADIUS packet types
+ public static final byte RADIUS_CODE_ACCESS_REQUEST = 0x01;
+ public static final byte RADIUS_CODE_ACCESS_ACCEPT = 0x02;
+ public static final byte RADIUS_CODE_ACCESS_REJECT = 0x03;
+ public static final byte RADIUS_CODE_ACCOUNTING_REQUEST = 0x04;
+ public static final byte RADIUS_CODE_ACCOUNTING_RESPONSE = 0x05;
+ public static final byte RADIUS_CODE_ACCESS_CHALLENGE = 0x0b;
+
+ private final Logger log = getLogger(getClass());
+
+ /**
+ * Default constructor.
+ */
+ public RADIUS() {
+ }
+
+ /**
+ * Constructs a RADIUS packet with the given code and identifier.
+ *
+ * @param code code
+ * @param identifier identifier
+ */
+ public RADIUS(byte code, byte identifier) {
+ this.code = code;
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the code.
+ *
+ * @return code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * Sets the code.
+ *
+ * @param code code
+ */
+ public void setCode(byte code) {
+ this.code = code;
+ }
+
+ /**
+ * Gets the identifier.
+ *
+ * @return identifier
+ */
+ public byte getIdentifier() {
+ return this.identifier;
+ }
+
+ /**
+ * Sets the identifier.
+ *
+ * @param identifier identifier
+ */
+ public void setIdentifier(byte identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the authenticator.
+ *
+ * @return authenticator
+ */
+ public byte[] getAuthenticator() {
+ return this.authenticator;
+ }
+
+ /**
+ * Sets the authenticator.
+ *
+ * @param authenticator authenticator
+ */
+ public void setAuthenticator(byte[] authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ /**
+ * Generates an authenticator code.
+ *
+ * @return the authenticator
+ */
+ public byte[] generateAuthCode() {
+ new SecureRandom().nextBytes(this.authenticator);
+ return this.authenticator;
+ }
+
+ /**
+ * Checks if the packet's code field is valid.
+ *
+ * @return whether the code is valid
+ */
+ public boolean isValidCode() {
+ return this.code == RADIUS_CODE_ACCESS_REQUEST ||
+ this.code == RADIUS_CODE_ACCESS_ACCEPT ||
+ this.code == RADIUS_CODE_ACCESS_REJECT ||
+ this.code == RADIUS_CODE_ACCOUNTING_REQUEST ||
+ this.code == RADIUS_CODE_ACCOUNTING_RESPONSE ||
+ this.code == RADIUS_CODE_ACCESS_CHALLENGE;
+ }
+
+ /**
+ * Adds a message authenticator to the packet based on the given key.
+ *
+ * @param key key to generate message authenticator
+ * @return the messgae authenticator RADIUS attribute
+ */
+ public RADIUSAttribute addMessageAuthenticator(String key) {
+ // Message-Authenticator = HMAC-MD5 (Type, Identifier, Length,
+ // Request Authenticator, Attributes)
+ // When the message integrity check is calculated the signature string
+ // should be considered to be sixteen octets of zero.
+ byte[] hashOutput = new byte[16];
+ Arrays.fill(hashOutput, (byte) 0);
+
+ RADIUSAttribute authAttribute = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH);
+ if (authAttribute != null) {
+ // If Message-Authenticator was already present, override it
+ this.log.warn("Attempted to add duplicate Message-Authenticator");
+ authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ } else {
+ // Else generate a new attribute padded with zeroes
+ authAttribute = this.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ }
+ // Calculate the MD5 HMAC based on the message
+ try {
+ SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
+ Mac mac = Mac.getInstance("HmacMD5");
+ mac.init(keySpec);
+ hashOutput = mac.doFinal(this.serialize());
+ // Update HMAC in Message-Authenticator
+ authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ } catch (Exception e) {
+ this.log.error("Failed to generate message authenticator: {}", e.getMessage());
+ }
+
+ return authAttribute;
+ }
+
+ /**
+ * Checks the message authenticator in the packet with one generated from
+ * the given key.
+ *
+ * @param key key to generate message authenticator
+ * @return whether the message authenticators match or not
+ */
+ public boolean checkMessageAuthenticator(String key) {
+ byte[] newHash = new byte[16];
+ Arrays.fill(newHash, (byte) 0);
+ byte[] messageAuthenticator = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH).getValue();
+ this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, newHash);
+ // Calculate the MD5 HMAC based on the message
+ try {
+ SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
+ Mac mac = Mac.getInstance("HmacMD5");
+ mac.init(keySpec);
+ newHash = mac.doFinal(this.serialize());
+ } catch (Exception e) {
+ log.error("Failed to generate message authenticator: {}", e.getMessage());
+ }
+ this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, messageAuthenticator);
+ // Compare the calculated Message-Authenticator with the one in the message
+ return Arrays.equals(newHash, messageAuthenticator);
+ }
+
+ /**
+ * Encapsulates an EAP packet in this RADIUS packet.
+ *
+ * @param message EAP message object to be embedded in the RADIUS
+ * EAP-Message attributed
+ */
+ public void encapsulateMessage(EAP message) {
+ if (message.length <= MAX_ATTR_VALUE_LENGTH) {
+ // Use the regular serialization method as it fits into one EAP-Message attribute
+ this.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+ message.serialize());
+ } else {
+ // Segment the message into chucks and embed them in several EAP-Message attributes
+ short remainingLength = message.length;
+ byte[] messageBuffer = message.serialize();
+ final ByteBuffer bb = ByteBuffer.wrap(messageBuffer);
+ while (bb.hasRemaining()) {
+ byte[] messageAttributeData;
+ if (remainingLength > MAX_ATTR_VALUE_LENGTH) {
+ // The remaining data is still too long to fit into one attribute, keep going
+ messageAttributeData = new byte[MAX_ATTR_VALUE_LENGTH];
+ bb.get(messageAttributeData, 0, MAX_ATTR_VALUE_LENGTH);
+ remainingLength -= MAX_ATTR_VALUE_LENGTH;
+ } else {
+ // The remaining data fits, this will be the last chunk
+ messageAttributeData = new byte[remainingLength];
+ bb.get(messageAttributeData, 0, remainingLength);
+ }
+ this.attributes.add(new RADIUSAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+ (byte) (messageAttributeData.length + 2), messageAttributeData));
+
+ // Adding the size of the data to the total RADIUS length
+ this.length += (short) (messageAttributeData.length & 0xFF);
+ // Adding the size of the overhead attribute type and length
+ this.length += 2;
+ }
+ }
+ }
+
+ /**
+ * Decapsulates an EAP packet from the RADIUS packet.
+ *
+ * @return An EAP object containing the reassembled EAP message
+ */
+ public EAP decapsulateMessage() {
+ EAP message = new EAP();
+ ByteArrayOutputStream messageStream = new ByteArrayOutputStream();
+ // Iterating through EAP-Message attributes to concatenate their value
+ for (RADIUSAttribute ra : this.getAttributeList(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE)) {
+ try {
+ messageStream.write(ra.getValue());
+ } catch (IOException e) {
+ log.error("Error while reassembling EAP message: {}", e.getMessage());
+ }
+ }
+ // Assembling EAP object from the concatenated stream
+ message.deserialize(messageStream.toByteArray(), 0, messageStream.size());
+ return message;
+ }
+
+ /**
+ * Gets a list of attributes from the RADIUS packet.
+ *
+ * @param attrType the type field of the required attributes
+ * @return List of the attributes that matches the type or an empty list if there is none
+ */
+ public ArrayList<RADIUSAttribute> getAttributeList(byte attrType) {
+ ArrayList<RADIUSAttribute> attrList = new ArrayList<>();
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ attrList.add(this.attributes.get(i));
+ }
+ }
+ return attrList;
+ }
+
+ /**
+ * Gets an attribute from the RADIUS packet.
+ *
+ * @param attrType the type field of the required attribute
+ * @return the first attribute that matches the type or null if does not exist
+ */
+ public RADIUSAttribute getAttribute(byte attrType) {
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ return this.attributes.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets an attribute in the RADIUS packet.
+ *
+ * @param attrType the type field of the attribute to set
+ * @param value value to be set
+ * @return reference to the attribute object
+ */
+ public RADIUSAttribute setAttribute(byte attrType, byte[] value) {
+ byte attrLength = (byte) (value.length + 2);
+ RADIUSAttribute newAttribute = new RADIUSAttribute(attrType, attrLength, value);
+ this.attributes.add(newAttribute);
+ this.length += (short) (attrLength & 0xFF);
+ return newAttribute;
+ }
+
+ /**
+ * Updates an attribute in the RADIUS packet.
+ *
+ * @param attrType the type field of the attribute to update
+ * @param value the value to update to
+ * @return reference to the attribute object
+ */
+ public RADIUSAttribute updateAttribute(byte attrType, byte[] value) {
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ this.length -= (short) (this.attributes.get(i).getLength() & 0xFF);
+ RADIUSAttribute newAttr = new RADIUSAttribute(attrType, (byte) (value.length + 2), value);
+ this.attributes.set(i, newAttr);
+ this.length += (short) (newAttr.getLength() & 0xFF);
+ return newAttr;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Deserializer for RADIUS packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<RADIUS> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, RADIUS_MIN_LENGTH);
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ RADIUS radius = new RADIUS();
+ radius.code = bb.get();
+ radius.identifier = bb.get();
+ radius.length = bb.getShort();
+ bb.get(radius.authenticator, 0, 16);
+
+ checkHeaderLength(length, radius.length);
+
+ int remainingLength = radius.length - RADIUS_MIN_LENGTH;
+ while (remainingLength > 0 && bb.hasRemaining()) {
+
+ RADIUSAttribute attr = new RADIUSAttribute();
+ attr.setType(bb.get());
+ attr.setLength(bb.get());
+ short attrLength = (short) (attr.length & 0xff);
+ attr.value = new byte[attrLength - 2];
+ bb.get(attr.value, 0, attrLength - 2);
+ radius.attributes.add(attr);
+ remainingLength -= attr.length;
+ }
+ return radius;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.code);
+ bb.put(this.identifier);
+ bb.putShort(this.length);
+ bb.put(this.authenticator);
+ for (int i = 0; i < this.attributes.size(); i++) {
+ RADIUSAttribute attr = this.attributes.get(i);
+ bb.put(attr.getType());
+ bb.put(attr.getLength());
+ bb.put(attr.getValue());
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.code = bb.get();
+ this.identifier = bb.get();
+ this.length = bb.getShort();
+ bb.get(this.authenticator, 0, 16);
+
+ int remainingLength = this.length - RADIUS_MIN_LENGTH;
+ while (remainingLength > 0 && bb.hasRemaining()) {
+ RADIUSAttribute attr = new RADIUSAttribute();
+ attr.setType(bb.get());
+ attr.setLength(bb.get());
+ short attrLength = (short) (attr.length & 0xff);
+ attr.value = new byte[attrLength - 2];
+ bb.get(attr.value, 0, attrLength - 2);
+ this.attributes.add(attr);
+ remainingLength -= attr.length;
+ }
+ return this;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java
new file mode 100644
index 00000000..9687e377
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java
@@ -0,0 +1,142 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.onlab.packet;
+
+/**
+ * An attribute in a RADIUS packet.
+ */
+public class RADIUSAttribute {
+ protected byte type;
+ protected byte length;
+ protected byte[] value;
+
+ // RADIUS attribute types
+ public static final byte RADIUS_ATTR_USERNAME = 1;
+ public static final byte RADIUS_ATTR_NAS_IP = 4;
+ public static final byte RADIUS_ATTR_NAS_PORT = 5;
+ public static final byte RADIUS_ATTR_FRAMED_MTU = 12;
+ public static final byte RADIUS_ATTR_STATE = 24;
+ public static final byte RADIUS_ATTR_VENDOR_SPECIFIC = 26;
+ public static final byte RADIUS_ATTR_CALLING_STATION_ID = 31;
+ public static final byte RADIUS_ATTR_NAS_ID = 32;
+ public static final byte RADIUS_ATTR_ACCT_SESSION_ID = 44;
+ public static final byte RADIUS_ATTR_NAS_PORT_TYPE = 61;
+ public static final byte RADIUS_ATTR_EAP_MESSAGE = 79;
+ public static final byte RADIUS_ATTR_MESSAGE_AUTH = 80;
+ public static final byte RADIUS_ATTR_NAS_PORT_ID = 87;
+
+ /**
+ * Default constructor.
+ */
+ public RADIUSAttribute() {
+ }
+
+ /**
+ * Constructs a RADIUS attribute with the give type, length and value.
+ *
+ * @param type type
+ * @param length length
+ * @param value value
+ */
+ public RADIUSAttribute(final byte type, final byte length, final byte[] value) {
+ this.type = type;
+ this.length = length;
+ this.value = value;
+ }
+
+ /**
+ * Checks if the attribute type is valid.
+ *
+ * @return whether the type is valid or not
+ */
+ public boolean isValidType() {
+ return this.type == RADIUS_ATTR_USERNAME ||
+ this.type == RADIUS_ATTR_NAS_IP ||
+ this.type == RADIUS_ATTR_NAS_PORT ||
+ this.type == RADIUS_ATTR_VENDOR_SPECIFIC ||
+ this.type == RADIUS_ATTR_CALLING_STATION_ID ||
+ this.type == RADIUS_ATTR_NAS_ID ||
+ this.type == RADIUS_ATTR_ACCT_SESSION_ID ||
+ this.type == RADIUS_ATTR_NAS_PORT_TYPE ||
+ this.type == RADIUS_ATTR_EAP_MESSAGE ||
+ this.type == RADIUS_ATTR_MESSAGE_AUTH ||
+ this.type == RADIUS_ATTR_NAS_PORT_ID;
+ }
+
+ /**
+ * Gets the attribute type.
+ *
+ * @return the type
+ */
+ public byte getType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the attribute type.
+ *
+ * @param type the code to set
+ * @return this
+ */
+ public RADIUSAttribute setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Gets the attribute length.
+ *
+ * @return the length
+ */
+ public byte getLength() {
+ return this.length;
+ }
+
+ /**
+ * Sets the attribute length.
+ *
+ * @param length the length to set
+ * @return this
+ */
+ public RADIUSAttribute setLength(final byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Gets the attribute value.
+ *
+ * @return the value
+ */
+ public byte[] getValue() {
+ return this.value;
+ }
+
+ /**
+ * Sets the attribute value.
+ *
+ * @param value the data to set
+ * @return this
+ */
+ public RADIUSAttribute setValue(final byte[] value) {
+ this.value = value;
+ return this;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java
new file mode 100644
index 00000000..e089f272
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Implements TCP packet format.
+ */
+
+public class TCP extends BasePacket {
+
+ private static final short TCP_HEADER_LENGTH = 20;
+
+ protected int sourcePort;
+ protected int destinationPort;
+ protected int sequence;
+ protected int acknowledge;
+ protected byte dataOffset;
+ protected short flags;
+ protected short windowSize;
+ protected short checksum;
+ protected short urgentPointer;
+ protected byte[] options;
+
+ /**
+ * Gets TCP source port.
+ *
+ * @return TCP source port
+ */
+ public int getSourcePort() {
+ return this.sourcePort;
+ }
+
+ /**
+ * Sets TCP source port.
+ *
+ * @param sourcePort the sourcePort to set (unsigned 16 bits integer)
+ * @return this
+ */
+ public TCP setSourcePort(final int sourcePort) {
+ this.sourcePort = sourcePort;
+ return this;
+ }
+
+ /**
+ * Gets TCP destination port.
+ *
+ * @return the destinationPort
+ */
+ public int getDestinationPort() {
+ return this.destinationPort;
+ }
+
+ /**
+ * Sets TCP destination port.
+ *
+ * @param destinationPort the destinationPort to set (unsigned 16 bits integer)
+ * @return this
+ */
+ public TCP setDestinationPort(final int destinationPort) {
+ this.destinationPort = destinationPort;
+ return this;
+ }
+
+ /**
+ * Gets checksum.
+ *
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * Sets checksum.
+ *
+ * @param checksum the checksum to set
+ * @return this
+ */
+ public TCP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ /**
+ * Gets sequence number.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets sequence number.
+ *
+ * @param seq the sequence number to set
+ * @return this
+ */
+ public TCP setSequence(final int seq) {
+ this.sequence = seq;
+ return this;
+ }
+
+ /**
+ * Gets acknowledge number.
+ *
+ * @return the acknowledge number
+ */
+ public int getAcknowledge() {
+ return this.acknowledge;
+ }
+
+ /**
+ * Sets acknowledge number.
+ *
+ * @param ack the acknowledge number to set
+ * @return this
+ */
+ public TCP setAcknowledge(final int ack) {
+ this.acknowledge = ack;
+ return this;
+ }
+
+ /**
+ * Gets offset.
+ *
+ * @return the offset
+ */
+ public byte getDataOffset() {
+ return this.dataOffset;
+ }
+
+ /**
+ * Sets offset.
+ *
+ * @param offset the offset to set
+ * @return this
+ */
+ public TCP setDataOffset(final byte offset) {
+ this.dataOffset = offset;
+ return this;
+ }
+
+ /**
+ * Gets TCP flags.
+ *
+ * @return the TCP flags
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * Sets TCP flags.
+ *
+ * @param flags the TCP flags to set
+ * @return this
+ */
+ public TCP setFlags(final short flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * Gets TCP window size.
+ *
+ * @return the TCP window size
+ */
+ public short getWindowSize() {
+ return this.windowSize;
+ }
+
+ /**
+ * Sets TCP window size.
+ *
+ * @param windowSize the TCP window size to set
+ * @return this
+ */
+ public TCP setWindowSize(final short windowSize) {
+ this.windowSize = windowSize;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * Gets urgent pointer.
+ *
+ * @return the urgent pointer
+ */
+ public short getUrgentPointer() {
+ return this.urgentPointer;
+ }
+
+ /**
+ * Sets urgent pointer.
+ *
+ * @param urgentPointer the urgent pointer to set
+ * @return this
+ */
+ public TCP setUrgentPointer(final short urgentPointer) {
+ this.urgentPointer = urgentPointer;
+ return this;
+ }
+
+ /**
+ * Gets TCP options.
+ *
+ * @return the TCP options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * Sets TCP options.
+ *
+ * @param options the options to set
+ * @return this
+ */
+ public TCP setOptions(final byte[] options) {
+ this.options = options;
+ this.dataOffset = (byte) (20 + options.length + 3 >> 2);
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ int length;
+ if (this.dataOffset == 0) {
+ this.dataOffset = 5; // default header length
+ }
+ length = this.dataOffset << 2;
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ length += payloadData.length;
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putShort((short) (this.sourcePort & 0xffff));
+ bb.putShort((short) (this.destinationPort & 0xffff));
+ bb.putInt(this.sequence);
+ bb.putInt(this.acknowledge);
+ bb.putShort((short) (this.flags | this.dataOffset << 12));
+ bb.putShort(this.windowSize);
+ bb.putShort(this.checksum);
+ bb.putShort(this.urgentPointer);
+ if (this.dataOffset > 5) {
+ int padding;
+ bb.put(this.options);
+ padding = (this.dataOffset << 2) - 20 - this.options.length;
+ for (int i = 0; i < padding; i++) {
+ bb.put((byte) 0);
+ }
+ }
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ // compute pseudo header mac
+ if (this.parent != null) {
+ if (this.parent instanceof IPv4) {
+ final IPv4 ipv4 = (IPv4) this.parent;
+ accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
+ + (ipv4.getSourceAddress() & 0xffff);
+ accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
+ + (ipv4.getDestinationAddress() & 0xffff);
+ accumulation += ipv4.getProtocol() & 0xff;
+ accumulation += length & 0xffff;
+ } else if (this.parent instanceof IPv6) {
+ final IPv6 ipv6 = (IPv6) this.parent;
+ final int bbLength =
+ Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
+ + 2 // nextHeader (with padding)
+ + 4; // length
+ final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
+ bbChecksum.put(ipv6.getSourceAddress());
+ bbChecksum.put(ipv6.getDestinationAddress());
+ bbChecksum.put((byte) 0); // padding
+ bbChecksum.put(ipv6.getNextHeader());
+ bbChecksum.putInt(length);
+ bbChecksum.rewind();
+ for (int i = 0; i < bbLength / 2; ++i) {
+ accumulation += 0xffff & bbChecksum.getShort();
+ }
+ }
+ }
+
+ for (int i = 0; i < length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(16, this.checksum);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.sourcePort = (bb.getShort() & 0xffff);
+ this.destinationPort = (bb.getShort() & 0xffff);
+ this.sequence = bb.getInt();
+ this.acknowledge = bb.getInt();
+ this.flags = bb.getShort();
+ this.dataOffset = (byte) (this.flags >> 12 & 0xf);
+ this.flags = (short) (this.flags & 0x1ff);
+ this.windowSize = bb.getShort();
+ this.checksum = bb.getShort();
+ this.urgentPointer = bb.getShort();
+ if (this.dataOffset > 5) {
+ int optLength = (this.dataOffset << 2) - 20;
+ if (bb.limit() < bb.position() + optLength) {
+ optLength = bb.limit() - bb.position();
+ }
+ try {
+ this.options = new byte[optLength];
+ bb.get(this.options, 0, optLength);
+ } catch (final IndexOutOfBoundsException e) {
+ this.options = null;
+ }
+ }
+
+ this.payload = new Data();
+ this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ this.payload.setParent(this);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.checksum;
+ result = prime * result + this.destinationPort;
+ result = prime * result + this.sourcePort;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof TCP)) {
+ return false;
+ }
+ final TCP other = (TCP) obj;
+ // May want to compare fields based on the flags set
+ return this.checksum == other.checksum
+ && this.destinationPort == other.destinationPort
+ && this.sourcePort == other.sourcePort
+ && this.sequence == other.sequence
+ && this.acknowledge == other.acknowledge
+ && this.dataOffset == other.dataOffset
+ && this.flags == other.flags
+ && this.windowSize == other.windowSize
+ && this.urgentPointer == other.urgentPointer
+ && (this.dataOffset == 5 || Arrays.equals(this.options, other.options));
+ }
+
+ /**
+ * Deserializer function for TCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<TCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, TCP_HEADER_LENGTH);
+
+ TCP tcp = new TCP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ tcp.sourcePort = (bb.getShort() & 0xffff);
+ tcp.destinationPort = (bb.getShort() & 0xffff);
+ tcp.sequence = bb.getInt();
+ tcp.acknowledge = bb.getInt();
+ tcp.flags = bb.getShort();
+ tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
+ tcp.flags = (short) (tcp.flags & 0x1ff);
+ tcp.windowSize = bb.getShort();
+ tcp.checksum = bb.getShort();
+ tcp.urgentPointer = bb.getShort();
+ if (tcp.dataOffset > 5) {
+ int optLength = (tcp.dataOffset << 2) - 20;
+ checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
+ tcp.options = new byte[optLength];
+ bb.get(tcp.options, 0, optLength);
+ }
+
+ tcp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+ tcp.payload.setParent(tcp);
+ return tcp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java
new file mode 100644
index 00000000..9b86a816
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java
@@ -0,0 +1,104 @@
+package org.onlab.packet;
+
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Representation of a transport layer port.
+ */
+public class TpPort {
+
+ private final int port;
+
+ // Transport layer port is unsigned 16 bits integer.
+ public static final int MAX_PORT = 0xFFFF;
+ public static final int MIN_PORT = 0;
+
+ /**
+ * Constructs a new TpPort.
+ *
+ * @param value the transport layer port
+ */
+ protected TpPort(int value) {
+ this.port = value;
+ }
+
+ /**
+ * Converts an integer into a TpPort.
+ *
+ * @param value an integer representing the transport layer port
+ * @return a TpPort
+ * @throws IllegalArgumentException if the value is invalid
+ */
+ public static TpPort tpPort(int value) {
+ if (value < MIN_PORT || value > MAX_PORT) {
+ throw new IllegalArgumentException(
+ "Transport layer port value " + value + "is not in the interval [0, 0xFFFF]");
+ }
+ return new TpPort(value);
+ }
+
+ /**
+ * Returns the integer value for this transport port.
+ *
+ * @return an integer value
+ */
+ public int toInt() {
+ return this.port;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof TpPort) {
+
+ TpPort other = (TpPort) obj;
+
+ if (this.port == other.port) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.port;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return String.valueOf(this.port);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java
new file mode 100644
index 00000000..a30c9a92
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Representation of a UDP packet.
+ */
+public class UDP extends BasePacket {
+ public static final Map<Integer, Deserializer<? extends IPacket>> PORT_DESERIALIZER_MAP =
+ new HashMap<>();
+ public static final int DHCP_SERVER_PORT = 67;
+ public static final int DHCP_CLIENT_PORT = 68;
+
+ private static final short UDP_HEADER_LENGTH = 8;
+
+ static {
+ /*
+ * Disable DHCP until the deserialize code is hardened to deal with
+ * garbage input
+ */
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_SERVER_PORT, DHCP.deserializer());
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_CLIENT_PORT, DHCP.deserializer());
+
+ }
+
+ protected int sourcePort;
+ protected int destinationPort;
+ protected short length;
+ protected short checksum;
+
+ /**
+ * @return the sourcePort
+ */
+ public int getSourcePort() {
+ return this.sourcePort;
+ }
+
+ /**
+ * @param sourcePort
+ * the sourcePort to set (16 bits unsigned integer)
+ * @return this
+ */
+ public UDP setSourcePort(final int sourcePort) {
+ this.sourcePort = sourcePort;
+ return this;
+ }
+
+ /**
+ * @return the destinationPort
+ */
+ public int getDestinationPort() {
+ return this.destinationPort;
+ }
+
+ /**
+ * @param destinationPort
+ * the destinationPort to set (16 bits unsigned integer)
+ * @return this
+ */
+ public UDP setDestinationPort(final int destinationPort) {
+ this.destinationPort = destinationPort;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public UDP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ this.length = (short) (8 + (payloadData == null ? 0
+ : payloadData.length));
+
+ final byte[] data = new byte[this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putShort((short) (this.sourcePort & 0xffff));
+ bb.putShort((short) (this.destinationPort & 0xffff));
+ bb.putShort(this.length);
+ bb.putShort(this.checksum);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_UDP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ // compute pseudo header mac
+ if (this.parent != null) {
+ if (this.parent instanceof IPv4) {
+ final IPv4 ipv4 = (IPv4) this.parent;
+ accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
+ + (ipv4.getSourceAddress() & 0xffff);
+ accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
+ + (ipv4.getDestinationAddress() & 0xffff);
+ accumulation += ipv4.getProtocol() & 0xff;
+ accumulation += length & 0xffff;
+ } else if (this.parent instanceof IPv6) {
+ final IPv6 ipv6 = (IPv6) this.parent;
+ final int bbLength =
+ Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
+ + 2 // nextHeader (with padding)
+ + 4; // length
+ final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
+ bbChecksum.put(ipv6.getSourceAddress());
+ bbChecksum.put(ipv6.getDestinationAddress());
+ bbChecksum.put((byte) 0); // padding
+ bbChecksum.put(ipv6.getNextHeader());
+ bbChecksum.putInt(length);
+ bbChecksum.rewind();
+ for (int i = 0; i < bbLength / 2; ++i) {
+ accumulation += 0xffff & bbChecksum.getShort();
+ }
+ }
+ }
+
+ for (int i = 0; i < this.length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (this.length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(6, this.checksum);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.sourcePort = (bb.getShort() & 0xffff);
+ this.destinationPort = (bb.getShort() & 0xffff);
+ this.length = bb.getShort();
+ this.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.sourcePort);
+ } else {
+ deserializer = Data.deserializer();
+ }
+
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.checksum;
+ result = prime * result + this.destinationPort;
+ result = prime * result + this.length;
+ result = prime * result + this.sourcePort;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof UDP)) {
+ return false;
+ }
+ final UDP other = (UDP) obj;
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.destinationPort != other.destinationPort) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ if (this.sourcePort != other.sourcePort) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for UDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<UDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, UDP_HEADER_LENGTH);
+
+ UDP udp = new UDP();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ udp.sourcePort = (bb.getShort() & 0xffff);
+ udp.destinationPort = (bb.getShort() & 0xffff);
+ udp.length = bb.getShort();
+ udp.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.sourcePort);
+ } else {
+ deserializer = Data.deserializer();
+ }
+
+ udp.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ udp.payload.setParent(udp);
+ return udp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java
new file mode 100644
index 00000000..4b38308b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet;
+
+/**
+ * Representation of a VLAN ID.
+ */
+public class VlanId {
+
+ private final short value;
+
+ // Based on convention used elsewhere? Check and change if needed
+ public static final short UNTAGGED = (short) 0xffff;
+
+ // In a traffic selector, this means that a VLAN ID must be present, but
+ // can have any value. We use the same value as OpenFlow, but this is not
+ // required.
+ public static final short ANY_VALUE = (short) 0x1000;
+
+ public static final VlanId NONE = VlanId.vlanId(UNTAGGED);
+ public static final VlanId ANY = VlanId.vlanId(ANY_VALUE);
+
+ // A VLAN ID is actually 12 bits of a VLAN tag.
+ public static final short MAX_VLAN = 4095;
+
+ protected VlanId() {
+ this.value = UNTAGGED;
+ }
+
+ protected VlanId(short value) {
+ this.value = value;
+ }
+
+ public static VlanId vlanId() {
+ return new VlanId(UNTAGGED);
+ }
+
+ public static VlanId vlanId(short value) {
+ if (value == UNTAGGED) {
+ return new VlanId();
+ }
+
+ if (value == ANY_VALUE) {
+ return new VlanId(ANY_VALUE);
+ }
+
+ if (value > MAX_VLAN) {
+ throw new IllegalArgumentException(
+ "value exceeds allowed maximum VLAN ID value (4095)");
+ }
+ return new VlanId(value);
+ }
+
+ public short toShort() {
+ return this.value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof VlanId) {
+
+ VlanId other = (VlanId) obj;
+
+ if (this.value == other.value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ if (this.value == ANY_VALUE) {
+ return "Any";
+ }
+ return String.valueOf(this.value);
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
new file mode 100644
index 00000000..ec04a812
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements IPv6 authentication extension header format. (RFC 4302)
+ */
+public class Authentication extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 12; // bytes
+ public static final byte LENGTH_UNIT = 4; // bytes per unit
+ public static final byte MINUS = 2;
+
+ protected byte nextHeader;
+ protected byte payloadLength;
+ protected int securityParamIndex;
+ protected int sequence;
+ protected byte[] integrityCheck;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Authentication setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the payload length of this header.
+ *
+ * @return the payload length
+ */
+ public byte getPayloadLength() {
+ return this.payloadLength;
+ }
+
+ /**
+ * Sets the payload length of this header.
+ *
+ * @param payloadLength the payload length to set
+ * @return this
+ */
+ public Authentication setPayloadLength(final byte payloadLength) {
+ this.payloadLength = payloadLength;
+ return this;
+ }
+
+ /**
+ * Gets the security parameter index of this header.
+ *
+ * @return the security parameter index
+ */
+ public int getSecurityParamIndex() {
+ return this.securityParamIndex;
+ }
+
+ /**
+ * Sets the security parameter index of this header.
+ *
+ * @param securityParamIndex the security parameter index to set
+ * @return this
+ */
+ public Authentication setSecurityParamIndex(final int securityParamIndex) {
+ this.securityParamIndex = securityParamIndex;
+ return this;
+ }
+
+ /**
+ * Gets the sequence number of this header.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets the sequence number of this header.
+ *
+ * @param sequence the sequence number to set
+ * @return this
+ */
+ public Authentication setSequence(final int sequence) {
+ this.sequence = sequence;
+ return this;
+ }
+
+ /**
+ * Gets the integrity check value of this header.
+ *
+ * @return the integrity check value
+ */
+ public byte[] getIntegrityCheck() {
+ return this.integrityCheck;
+ }
+
+ /**
+ * Sets the integrity check value of this header.
+ *
+ * @param integrityCheck the integrity check value to set
+ * @return this
+ */
+ public Authentication setIngegrityCheck(final byte[] integrityCheck) {
+ this.integrityCheck =
+ Arrays.copyOfRange(integrityCheck, 0, integrityCheck.length);
+ return this;
+ }
+
+ /**
+ * Gets the total length of this header.
+ * According to spec, payload length should be the total length of this AH
+ * in 4-octet unit, minus 2
+ *
+ * @return the total length
+ */
+ public int getTotalLength() {
+ return (this.payloadLength + MINUS) * LENGTH_UNIT;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + integrityCheck.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.payloadLength);
+ bb.putShort((short) 0);
+ bb.putInt(this.securityParamIndex);
+ bb.putInt(this.sequence);
+ bb.put(this.integrityCheck, 0, integrityCheck.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_AH);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.payloadLength = bb.get();
+ bb.getShort();
+ this.securityParamIndex = bb.getInt();
+ this.sequence = bb.getInt();
+ int icvLength = getTotalLength() - FIXED_HEADER_LENGTH;
+ this.integrityCheck = new byte[icvLength];
+ bb.get(this.integrityCheck, 0, icvLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.nextHeader;
+ result = prime * result + this.payloadLength;
+ result = prime * result + this.securityParamIndex;
+ result = prime * result + this.sequence;
+ for (byte b : this.integrityCheck) {
+ result = prime * result + b;
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Authentication)) {
+ return false;
+ }
+ final Authentication other = (Authentication) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.payloadLength != other.payloadLength) {
+ return false;
+ }
+ if (this.securityParamIndex != other.securityParamIndex) {
+ return false;
+ }
+ if (this.sequence != other.sequence) {
+ return false;
+ }
+ if (!Arrays.equals(this.integrityCheck, other.integrityCheck)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for authentication headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Authentication> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Authentication authentication = new Authentication();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ authentication.nextHeader = bb.get();
+ authentication.payloadLength = bb.get();
+ bb.getShort();
+ authentication.securityParamIndex = bb.getInt();
+ authentication.sequence = bb.getInt();
+ int icvLength = (authentication.payloadLength + MINUS) * LENGTH_UNIT - FIXED_HEADER_LENGTH;
+ authentication.integrityCheck = new byte[icvLength];
+ bb.get(authentication.integrityCheck, 0, icvLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(authentication.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(authentication.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ authentication.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ authentication.payload.setParent(authentication);
+
+ return authentication;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
new file mode 100644
index 00000000..f57b756e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Base class for hop-by-hop options and destination options.
+ */
+public class BaseOptions extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 2; // bytes
+ public static final byte FIXED_OPTIONS_LENGTH = 6; // bytes
+ public static final byte LENGTH_UNIT = 8; // bytes per unit
+
+ protected byte nextHeader;
+ protected byte headerExtLength;
+ protected byte[] options;
+ protected byte type;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public BaseOptions setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the extension length of this header.
+ *
+ * @return header length
+ */
+ public byte getHeaderExtLength() {
+ return this.headerExtLength;
+ }
+
+ /**
+ * Sets the extension length of this header.
+ *
+ * @param headerExtLength the header length to set
+ * @return this
+ */
+ public BaseOptions setHeaderExtLength(final byte headerExtLength) {
+ this.headerExtLength = headerExtLength;
+ return this;
+ }
+
+ /**
+ * Gets the options.
+ *
+ * @return the options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * Sets the options.
+ *
+ * @param options the options to set
+ * @return this
+ */
+ public BaseOptions setOptions(final byte[] options) {
+ this.options =
+ Arrays.copyOfRange(options, 0, options.length);
+ return this;
+ }
+
+ /**
+ * Gets the type of this option.
+ *
+ * @return the type
+ */
+ protected byte getType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the type of this option.
+ * Must be either IPv6.PROTOCOL_HOPOPT or IPv6.PROTOCOL_DSTOPT
+ *
+ * @param type the type to set
+ * @return this
+ */
+ protected BaseOptions setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + options.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.headerExtLength);
+ bb.put(this.options, 0, options.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(this.type);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.headerExtLength = bb.get();
+ int optionLength =
+ FIXED_OPTIONS_LENGTH + LENGTH_UNIT * this.headerExtLength;
+ this.options = new byte[optionLength];
+ bb.get(this.options, 0, optionLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.nextHeader;
+ result = prime * result + this.headerExtLength;
+ for (byte b : this.options) {
+ result = prime * result + b;
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof BaseOptions)) {
+ return false;
+ }
+ final BaseOptions other = (BaseOptions) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.headerExtLength != other.headerExtLength) {
+ return false;
+ }
+ if (!Arrays.equals(this.options, other.options)) {
+ return false;
+ }
+ if (this.type != other.type) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for IPv6 base options.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<BaseOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ BaseOptions baseOptions = new BaseOptions();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ baseOptions.nextHeader = bb.get();
+ baseOptions.headerExtLength = bb.get();
+ int optionLength =
+ FIXED_OPTIONS_LENGTH + LENGTH_UNIT * baseOptions.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), optionLength);
+
+ baseOptions.options = new byte[optionLength];
+ bb.get(baseOptions.options, 0, optionLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(baseOptions.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(baseOptions.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ baseOptions.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ baseOptions.payload.setParent(baseOptions);
+
+ return baseOptions;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java
new file mode 100644
index 00000000..208bdd7e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.IPv6;
+
+/**
+ * Implements IPv6 Destination Options extension header format. (RFC 2460)
+ */
+public class DestinationOptions extends BaseOptions {
+ public DestinationOptions() {
+ super();
+ this.setType(IPv6.PROTOCOL_DSTOPT);
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
new file mode 100644
index 00000000..e46a1261
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements IPv6 Encapsulating Security Payload (ESP) extension header format.
+ * (RFC 4303)
+ */
+public class EncapSecurityPayload extends BasePacket {
+ public static final byte HEADER_LENGTH = 8; // bytes
+
+ protected int securityParamIndex;
+ protected int sequence;
+ //
+ // NOTE: The remaining fields including payload data, padding length and
+ // next header are encrypted and all considered as a payload of ESP.
+ //
+
+ /**
+ * Gets the security parameter index of this header.
+ *
+ * @return the security parameter index
+ */
+ public int getSecurityParamIndex() {
+ return this.securityParamIndex;
+ }
+
+ /**
+ * Sets the security parameter index of this header.
+ *
+ * @param securityParamIndex the security parameter index to set
+ * @return this
+ */
+ public EncapSecurityPayload setSecurityParamIndex(final int securityParamIndex) {
+ this.securityParamIndex = securityParamIndex;
+ return this;
+ }
+
+ /**
+ * Gets the sequence number of this header.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets the sequence number of this header.
+ *
+ * @param sequence the sequence number to set
+ * @return this
+ */
+ public EncapSecurityPayload setSequence(final int sequence) {
+ this.sequence = sequence;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(this.securityParamIndex);
+ bb.putInt(this.sequence);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ESP);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.securityParamIndex = bb.getInt();
+ this.sequence = bb.getInt();
+
+ this.payload = new Data();
+ this.payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.securityParamIndex;
+ result = prime * result + this.sequence;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof EncapSecurityPayload)) {
+ return false;
+ }
+ final EncapSecurityPayload other = (EncapSecurityPayload) obj;
+ if (this.securityParamIndex != other.securityParamIndex) {
+ return false;
+ }
+ if (this.sequence != other.sequence) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for encapsulated security payload headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<EncapSecurityPayload> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EncapSecurityPayload encapSecurityPayload = new EncapSecurityPayload();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ encapSecurityPayload.securityParamIndex = bb.getInt();
+ encapSecurityPayload.sequence = bb.getInt();
+
+ encapSecurityPayload.payload = Data.deserializer().deserialize(
+ data, bb.position(), bb.limit() - bb.position());
+ encapSecurityPayload.payload.setParent(encapSecurityPayload);
+
+ return encapSecurityPayload;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
new file mode 100644
index 00000000..68015d31
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements IPv6 fragment extension header format. (RFC 2460)
+ */
+public class Fragment extends BasePacket implements IExtensionHeader {
+ public static final byte HEADER_LENGTH = 8; // bytes
+
+ protected byte nextHeader;
+ protected short fragmentOffset;
+ protected byte moreFragment;
+ protected int identification;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Fragment setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the fragment offset of this header.
+ *
+ * @return fragment offset
+ */
+ public short getFragmentOffset() {
+ return this.fragmentOffset;
+ }
+
+ /**
+ * Sets the fragment offset of this header.
+ *
+ * @param fragmentOffset the fragment offset to set
+ * @return this
+ */
+ public Fragment setFragmentOffset(final short fragmentOffset) {
+ this.fragmentOffset = fragmentOffset;
+ return this;
+ }
+
+ /**
+ * Gets the more fragment flag of this header.
+ *
+ * @return more fragment flag
+ */
+ public byte getMoreFragment() {
+ return this.moreFragment;
+ }
+
+ /**
+ * Sets the more fragment flag of this header.
+ *
+ * @param moreFragment the more fragment flag to set
+ * @return this
+ */
+ public Fragment setMoreFragment(final byte moreFragment) {
+ this.moreFragment = moreFragment;
+ return this;
+ }
+
+ /**
+ * Gets the identification of this header.
+ *
+ * @return identification
+ */
+ public int getIdentification() {
+ return this.identification;
+ }
+
+ /**
+ * Sets the identification of this header.
+ *
+ * @param identification the identification to set
+ * @return this
+ */
+ public Fragment setIdentification(final int identification) {
+ this.identification = identification;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put((byte) 0);
+ bb.putShort((short) (
+ (this.fragmentOffset & 0x1fff) << 3 |
+ this.moreFragment & 0x1
+ ));
+ bb.putInt(this.identification);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_FRAG);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ bb.get();
+ short sscratch = bb.getShort();
+ this.fragmentOffset = (short) (sscratch >> 3 & 0x1fff);
+ this.moreFragment = (byte) (sscratch & 0x1);
+ this.identification = bb.getInt();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.nextHeader;
+ result = prime * result + this.fragmentOffset;
+ result = prime * result + this.moreFragment;
+ result = prime * result + this.identification;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Fragment)) {
+ return false;
+ }
+ final Fragment other = (Fragment) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.fragmentOffset != other.fragmentOffset) {
+ return false;
+ }
+ if (this.moreFragment != other.moreFragment) {
+ return false;
+ }
+ if (this.identification != other.identification) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for fragment headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Fragment> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Fragment fragment = new Fragment();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ fragment.nextHeader = bb.get();
+ bb.get();
+ short sscratch = bb.getShort();
+ fragment.fragmentOffset = (short) (sscratch >> 3 & 0x1fff);
+ fragment.moreFragment = (byte) (sscratch & 0x1);
+ fragment.identification = bb.getInt();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(fragment.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(fragment.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ fragment.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ fragment.payload.setParent(fragment);
+
+ return fragment;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java
new file mode 100644
index 00000000..cd8c141c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.IPv6;
+
+/**
+ * Implements IPv6 Hop-by-hop Options extension header format. (RFC 2460)
+ */
+public class HopByHopOptions extends BaseOptions {
+ public HopByHopOptions() {
+ super();
+ this.setType(IPv6.PROTOCOL_HOPOPT);
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java
new file mode 100644
index 00000000..252f1a3c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+/**
+ * Interface for IPv6 extension header.
+ */
+public interface IExtensionHeader {
+ /**
+ * Gets the type of next header.
+ *
+ * @return next header
+ */
+ byte getNextHeader();
+
+ /**
+ * Sets the type of next header.
+ *
+ * @param nextHeader the next header to set
+ * @return this
+ */
+ IExtensionHeader setNextHeader(final byte nextHeader);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
new file mode 100644
index 00000000..d7d204a9
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.packet.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements IPv6 routing extension header format. (RFC 2460)
+ */
+public class Routing extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 4; // bytes
+ public static final byte FIXED_ROUTING_DATA_LENGTH = 4; // bytes
+ public static final byte LENGTH_UNIT = 8; // bytes per unit
+
+ protected byte nextHeader;
+ protected byte headerExtLength;
+ protected byte routingType;
+ protected byte segmentsLeft;
+ protected byte[] routingData;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Routing setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the extension length of this header.
+ *
+ * @return header length
+ */
+ public byte getHeaderExtLength() {
+ return this.headerExtLength;
+ }
+
+ /**
+ * Sets the extension length of this header.
+ *
+ * @param headerExtLength the header length to set
+ * @return this
+ */
+ public Routing setHeaderExtLength(final byte headerExtLength) {
+ this.headerExtLength = headerExtLength;
+ return this;
+ }
+
+ /**
+ * Gets the routing type of this header.
+ *
+ * @return routing type
+ */
+ public byte getRoutingType() {
+ return this.routingType;
+ }
+
+ /**
+ * Sets the routing type of this header.
+ *
+ * @param routingType the routing type to set
+ * @return this
+ */
+ public Routing setRoutingType(final byte routingType) {
+ this.routingType = routingType;
+ return this;
+ }
+
+ /**
+ * Gets the number of remaining route segments of this header.
+ *
+ * @return number of remaining route segments
+ */
+ public byte getSegmentsLeft() {
+ return this.segmentsLeft;
+ }
+
+ /**
+ * Sets the number of remaining route segments of this header.
+ *
+ * @param segmentsLeft the number of remaining route segments to set
+ * @return this
+ */
+ public Routing setSegmntsLeft(final byte segmentsLeft) {
+ this.segmentsLeft = segmentsLeft;
+ return this;
+ }
+
+ /**
+ * Gets the routing data.
+ *
+ * @return the routing data
+ */
+ public byte[] getRoutingData() {
+ return this.routingData;
+ }
+
+ /**
+ * Sets the routing data.
+ *
+ * @param routingData the routing data to set
+ * @return this
+ */
+ public Routing setRoutingData(final byte[] routingData) {
+ this.routingData =
+ Arrays.copyOfRange(routingData, 0, routingData.length);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + routingData.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.headerExtLength);
+ bb.put(this.routingType);
+ bb.put(this.segmentsLeft);
+ bb.put(this.routingData, 0, routingData.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ROUTING);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.headerExtLength = bb.get();
+ this.routingType = bb.get();
+ this.segmentsLeft = bb.get();
+ int dataLength =
+ FIXED_ROUTING_DATA_LENGTH + LENGTH_UNIT * this.headerExtLength;
+ this.routingData = new byte[dataLength];
+ bb.get(this.routingData, 0, dataLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = new Data().deserializer();
+ }
+ try {
+ this.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(this);
+ } catch (DeserializationException e) {
+ return this;
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.nextHeader;
+ result = prime * result + this.headerExtLength;
+ result = prime * result + this.routingType;
+ result = prime * result + this.segmentsLeft;
+ for (byte b : this.routingData) {
+ result = prime * result + b;
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Routing)) {
+ return false;
+ }
+ final Routing other = (Routing) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.headerExtLength != other.headerExtLength) {
+ return false;
+ }
+ if (this.routingType != other.routingType) {
+ return false;
+ }
+ if (this.segmentsLeft != other.segmentsLeft) {
+ return false;
+ }
+ if (!Arrays.equals(this.routingData, other.routingData)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for routing headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Routing> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Routing routing = new Routing();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ routing.nextHeader = bb.get();
+ routing.headerExtLength = bb.get();
+ routing.routingType = bb.get();
+ routing.segmentsLeft = bb.get();
+ int dataLength =
+ FIXED_ROUTING_DATA_LENGTH + LENGTH_UNIT * routing.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), dataLength);
+
+ routing.routingData = new byte[dataLength];
+ bb.get(routing.routingData, 0, dataLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(routing.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(routing.nextHeader);
+ } else {
+ deserializer = new Data().deserializer();
+ }
+ routing.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ routing.payload.setParent(routing);
+
+ return routing;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
new file mode 100644
index 00000000..714fd1b2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utilities for decoding and encoding IPv6 extension headers.
+ */
+package org.onlab.packet.ipv6;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
new file mode 100644
index 00000000..99fa0dd6
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
+ */
+public class NeighborAdvertisement extends BasePacket {
+ public static final byte HEADER_LENGTH = 20; // bytes
+
+ protected byte routerFlag;
+ protected byte solicitedFlag;
+ protected byte overrideFlag;
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets router flag.
+ *
+ * @return the router flag
+ */
+ public byte getRouterFlag() {
+ return this.routerFlag;
+ }
+
+ /**
+ * Sets router flag.
+ *
+ * @param routerFlag the router flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setRouterFlag(final byte routerFlag) {
+ this.routerFlag = routerFlag;
+ return this;
+ }
+
+ /**
+ * Gets solicited flag.
+ *
+ * @return the solicited flag
+ */
+ public byte getSolicitedFlag() {
+ return this.solicitedFlag;
+ }
+
+ /**
+ * Sets solicited flag.
+ *
+ * @param solicitedFlag the solicited flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setSolicitedFlag(final byte solicitedFlag) {
+ this.solicitedFlag = solicitedFlag;
+ return this;
+ }
+
+ /**
+ * Gets override flag.
+ *
+ * @return the override flag
+ */
+ public byte getOverrideFlag() {
+ return this.overrideFlag;
+ }
+
+ /**
+ * Sets override flag.
+ *
+ * @param overrideFlag the override flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setOverrideFlag(final byte overrideFlag) {
+ this.overrideFlag = overrideFlag;
+ return this;
+ }
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public NeighborAdvertisement setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborAdvertisement addOption(final byte type,
+ final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt((this.routerFlag & 0x1) << 31 |
+ (this.solicitedFlag & 0x1) << 30 |
+ (this.overrideFlag & 0x1) << 29);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int iscratch;
+
+ iscratch = bb.getInt();
+ this.routerFlag = (byte) (iscratch >> 31 & 0x1);
+ this.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
+ this.overrideFlag = (byte) (iscratch >> 29 & 0x1);
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ result = prime * result + this.routerFlag;
+ result = prime * result + this.solicitedFlag;
+ result = prime * result + this.overrideFlag;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof NeighborAdvertisement)) {
+ return false;
+ }
+ final NeighborAdvertisement other = (NeighborAdvertisement) obj;
+ if (this.routerFlag != other.routerFlag) {
+ return false;
+ }
+ if (this.solicitedFlag != other.solicitedFlag) {
+ return false;
+ }
+ if (this.overrideFlag != other.overrideFlag) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for neighbor advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborAdvertisement neighborAdvertisement = new NeighborAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int iscratch;
+
+ iscratch = bb.getInt();
+ neighborAdvertisement.routerFlag = (byte) (iscratch >> 31 & 0x1);
+ neighborAdvertisement.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
+ neighborAdvertisement.overrideFlag = (byte) (iscratch >> 29 & 0x1);
+ bb.get(neighborAdvertisement.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborAdvertisement.addOption(option.type(), option.data());
+ }
+ }
+
+ return neighborAdvertisement;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
new file mode 100644
index 00000000..00a26068
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Neighbor Discovery Protocol packet options.
+ */
+public class NeighborDiscoveryOptions extends BasePacket {
+ public static final byte TYPE_SOURCE_LL_ADDRESS = 1;
+ public static final byte TYPE_TARGET_LL_ADDRESS = 2;
+ public static final byte TYPE_PREFIX_INFORMATION = 3;
+ public static final byte TYPE_REDIRECTED_HEADER = 4;
+ public static final byte TYPE_MTU = 5;
+
+ public static final byte INITIAL_HEADER_REQUIRED = 2;
+
+ private static final String BUFFER_UNDERFLOW_ERROR =
+ "Not enough bytes in buffer to read option";
+
+ private final List<Option> options = new ArrayList<>();
+
+ /**
+ * Packet option.
+ */
+ public final class Option {
+ private final byte type;
+ private final byte[] data;
+
+ /**
+ * Constructor.
+ *
+ * @param type the option type
+ * @param data the option data
+ */
+ private Option(byte type, byte[] data) {
+ this.type = type;
+ this.data = Arrays.copyOfRange(data, 0, data.length);
+ }
+
+ /**
+ * Gets the option type.
+ *
+ * @return the option type
+ */
+ public byte type() {
+ return this.type;
+ }
+
+ /**
+ * Gets the option data.
+ *
+ * @return the option data
+ */
+ public byte[] data() {
+ return this.data;
+ }
+
+ /**
+ * Gets the option data length (in number of octets).
+ *
+ * @return the option data length (in number of octets)
+ */
+ public int dataLength() {
+ return data.length;
+ }
+
+ /**
+ * Gets the option length (in number of octets), including the type and
+ * length fields (one octet each).
+ *
+ * @return the option length (in number of octets), including the type
+ * and length fields
+ */
+ private int optionLength() {
+ return 2 + dataLength();
+ }
+
+ /**
+ * Gets the option length field value (in units of 8 octets).
+ *
+ * @return the option length field value (in units of 8 octets)
+ */
+ private byte optionLengthField() {
+ return (byte) ((optionLength() + 7) / 8);
+ }
+
+ /**
+ * Gets the option length on the wire (in number of octets).
+ *
+ * @return the option length on the wire (in number of octets)
+ */
+ private int optionWireLength() {
+ return 8 * optionLengthField();
+ }
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborDiscoveryOptions addOption(byte type, byte[] data) {
+ options.add(new Option(type, data));
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> options() {
+ return this.options;
+ }
+
+ /**
+ * Checks whether any options are included.
+ *
+ * @return true if options are included, otherwise false
+ */
+ public boolean hasOptions() {
+ return !this.options.isEmpty();
+ }
+
+ @Override
+ public byte[] serialize() {
+ // Compute first the total length on the wire for all options
+
+ int wireLength = 0;
+
+ for (Option option : this.options) {
+ wireLength += option.optionWireLength();
+ }
+
+ final byte[] data = new byte[wireLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ //
+ // Serialize all options
+ //
+ for (Option option : this.options) {
+ bb.put(option.type());
+ bb.put(option.optionLengthField());
+ bb.put(option.data());
+ // Add the padding
+ int paddingLength =
+ option.optionWireLength() - option.optionLength();
+ for (int i = 0; i < paddingLength; i++) {
+ bb.put((byte) 0);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ options.clear();
+
+ //
+ // Deserialize all options
+ //
+ while (bb.hasRemaining()) {
+ byte type = bb.get();
+ if (!bb.hasRemaining()) {
+ break;
+ }
+ byte lengthField = bb.get();
+ int dataLength = lengthField * 8; // The data length field is in
+ // unit of 8 octets
+
+ // Exclude the type and length fields
+ if (dataLength < 2) {
+ break;
+ }
+ dataLength -= 2;
+
+ if (bb.remaining() < dataLength) {
+ break;
+ }
+ byte[] optionData = new byte[dataLength];
+ bb.get(optionData, 0, optionData.length);
+ addOption(type, optionData);
+ }
+
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+
+ for (Option option : this.options) {
+ result = prime * result + option.type();
+ result = prime * result + Arrays.hashCode(option.data());
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof NeighborDiscoveryOptions) {
+ NeighborDiscoveryOptions other = (NeighborDiscoveryOptions) obj;
+ return this.options.equals(other.options);
+ }
+ return false;
+ }
+
+ public static Deserializer<NeighborDiscoveryOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_REQUIRED);
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ NeighborDiscoveryOptions ndo = new NeighborDiscoveryOptions();
+
+ ndo.options.clear();
+
+ //
+ // Deserialize all options
+ //
+ while (bb.hasRemaining()) {
+ byte type = bb.get();
+ if (!bb.hasRemaining()) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte lengthField = bb.get();
+ int dataLength = lengthField * 8; // The data length field is in
+ // unit of 8 octets
+
+ // Exclude the type and length fields
+ if (dataLength < 2) {
+ break;
+ }
+ dataLength -= 2;
+
+ if (bb.remaining() < dataLength) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte[] optionData = new byte[dataLength];
+ bb.get(optionData, 0, optionData.length);
+ ndo.addOption(type, optionData);
+ }
+
+ return ndo;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
new file mode 100644
index 00000000..77c119a0
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Neighbor Solicitation packet format. (RFC 4861)
+ */
+public class NeighborSolicitation extends BasePacket {
+ public static final byte HEADER_LENGTH = 20; // bytes
+
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public NeighborSolicitation setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborSolicitation addOption(final byte type,
+ final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof NeighborSolicitation)) {
+ return false;
+ }
+ final NeighborSolicitation other = (NeighborSolicitation) obj;
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for neighbor solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborSolicitation neighborSolicitation = new NeighborSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(neighborSolicitation.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborSolicitation.addOption(option.type(), option.data());
+ }
+ }
+
+ return neighborSolicitation;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
new file mode 100644
index 00000000..51256d41
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Redirect packet format. (RFC 4861)
+ */
+public class Redirect extends BasePacket {
+ public static final byte HEADER_LENGTH = 36; // bytes
+
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+ protected byte[] destinationAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public Redirect setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets destination address.
+ *
+ * @return the destination IPv6 address
+ */
+ public byte[] getDestinationAddress() {
+ return this.destinationAddress;
+ }
+
+ /**
+ * Sets destination address.
+ *
+ * @param destinationAddress the destination IPv6 address to set
+ * @return this
+ */
+ public Redirect setDestinationAddress(final byte[] destinationAddress) {
+ this.destinationAddress =
+ Arrays.copyOfRange(destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public Redirect addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.put(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ bb = ByteBuffer.wrap(this.destinationAddress);
+ for (int i = 0; i < this.destinationAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Redirect)) {
+ return false;
+ }
+ final Redirect other = (Redirect) obj;
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.destinationAddress,
+ other.destinationAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for redirect packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Redirect> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Redirect redirect = new Redirect();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ bb.get(redirect.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(redirect.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ redirect.addOption(option.type(), option.data());
+ }
+ }
+
+ return redirect;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
new file mode 100644
index 00000000..597fc9f8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Router Advertisement packet format. (RFC 4861)
+ */
+public class RouterAdvertisement extends BasePacket {
+ public static final byte HEADER_LENGTH = 12; // bytes
+
+ protected byte currentHopLimit;
+ protected byte mFlag;
+ protected byte oFlag;
+ protected short routerLifetime;
+ protected int reachableTime;
+ protected int retransmitTimer;
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets current hop limit.
+ *
+ * @return the current hop limit
+ */
+ public byte getCurrentHopLimit() {
+ return this.currentHopLimit;
+ }
+
+ /**
+ * Sets current hop limit.
+ *
+ * @param currentHopLimit the current hop limit to set
+ * @return this
+ */
+ public RouterAdvertisement setCurrentHopLimit(final byte currentHopLimit) {
+ this.currentHopLimit = currentHopLimit;
+ return this;
+ }
+
+ /**
+ * Gets managed address configuration flag.
+ *
+ * @return the managed address configuration flag
+ */
+ public byte getMFlag() {
+ return this.mFlag;
+ }
+
+ /**
+ * Sets managed address configuration flag.
+ *
+ * @param mFlag the managed address configuration flag to set
+ * @return this
+ */
+ public RouterAdvertisement setMFlag(final byte mFlag) {
+ this.mFlag = mFlag;
+ return this;
+ }
+
+ /**
+ * Gets other configuration flag.
+ *
+ * @return the other configuration flag
+ */
+ public byte getOFlag() {
+ return this.oFlag;
+ }
+
+ /**
+ * Sets other configuration flag.
+ *
+ * @param oFlag the other configuration flag to set
+ * @return this
+ */
+ public RouterAdvertisement setOFlag(final byte oFlag) {
+ this.oFlag = oFlag;
+ return this;
+ }
+
+ /**
+ * Gets router lifetime.
+ *
+ * @return the router lifetime
+ */
+ public short getRouterLifetime() {
+ return this.routerLifetime;
+ }
+
+ /**
+ * Sets router lifetime.
+ *
+ * @param routerLifetime the router lifetime to set
+ * @return this
+ */
+ public RouterAdvertisement setRouterLifetime(final short routerLifetime) {
+ this.routerLifetime = routerLifetime;
+ return this;
+ }
+
+ /**
+ * Gets reachable time.
+ *
+ * @return the reachable time
+ */
+ public int getReachableTime() {
+ return this.reachableTime;
+ }
+
+ /**
+ * Sets reachable time.
+ *
+ * @param reachableTime the reachable time to set
+ * @return this
+ */
+ public RouterAdvertisement setReachableTime(final int reachableTime) {
+ this.reachableTime = reachableTime;
+ return this;
+ }
+
+ /**
+ * Gets retransmission timer.
+ *
+ * @return the retransmission timer
+ */
+ public int getRetransmitTimer() {
+ return this.retransmitTimer;
+ }
+
+ /**
+ * Sets retransmission timer.
+ *
+ * @param retransmitTimer the retransmission timer to set
+ * @return this
+ */
+ public RouterAdvertisement setRetransmitTimer(final int retransmitTimer) {
+ this.retransmitTimer = retransmitTimer;
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public RouterAdvertisement addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.currentHopLimit);
+ bb.put((byte) ((this.mFlag & 0x1) << 7 | (this.oFlag & 0x1) << 6));
+ bb.putShort(routerLifetime);
+ bb.putInt(reachableTime);
+ bb.putInt(retransmitTimer);
+
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int bscratch;
+
+ this.currentHopLimit = bb.get();
+ bscratch = bb.get();
+ this.mFlag = (byte) ((bscratch >> 7) & 0x1);
+ this.oFlag = (byte) ((bscratch >> 6) & 0x1);
+ this.routerLifetime = bb.getShort();
+ this.reachableTime = bb.getInt();
+ this.retransmitTimer = bb.getInt();
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.currentHopLimit;
+ result = prime * result + this.mFlag;
+ result = prime * result + this.oFlag;
+ result = prime * result + this.routerLifetime;
+ result = prime * result + this.reachableTime;
+ result = prime * result + this.retransmitTimer;
+ result = prime * result + this.options.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof RouterAdvertisement)) {
+ return false;
+ }
+ final RouterAdvertisement other = (RouterAdvertisement) obj;
+ if (this.currentHopLimit != other.currentHopLimit) {
+ return false;
+ }
+ if (this.mFlag != other.mFlag) {
+ return false;
+ }
+ if (this.oFlag != other.oFlag) {
+ return false;
+ }
+ if (this.routerLifetime != other.routerLifetime) {
+ return false;
+ }
+ if (this.reachableTime != other.reachableTime) {
+ return false;
+ }
+ if (this.retransmitTimer != other.retransmitTimer) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for router advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterAdvertisement routerAdvertisement = new RouterAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int bscratch;
+
+ routerAdvertisement.currentHopLimit = bb.get();
+ bscratch = bb.get();
+ routerAdvertisement.mFlag = (byte) ((bscratch >> 7) & 0x1);
+ routerAdvertisement.oFlag = (byte) ((bscratch >> 6) & 0x1);
+ routerAdvertisement.routerLifetime = bb.getShort();
+ routerAdvertisement.reachableTime = bb.getInt();
+ routerAdvertisement.retransmitTimer = bb.getInt();
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerAdvertisement.addOption(option.type(), option.data());
+ }
+ }
+
+ return routerAdvertisement;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
new file mode 100644
index 00000000..e279a404
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.packet.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Router Solicitation packet format. (RFC 4861)
+ */
+public class RouterSolicitation extends BasePacket {
+ public static final byte HEADER_LENGTH = 4; // bytes
+
+ private final NeighborDiscoveryOptions options = new NeighborDiscoveryOptions();
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public RouterSolicitation addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ result = prime * result + this.options.hashCode();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof RouterSolicitation)) {
+ return false;
+ }
+ final RouterSolicitation other = (RouterSolicitation) obj;
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for router solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterSolicitation routerSolicitation = new RouterSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerSolicitation.addOption(option.type(), option.data());
+ }
+ }
+
+ return routerSolicitation;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java
new file mode 100644
index 00000000..c62b1fba
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utilities for decoding and encoding packets of Neighbor Discovery Protocol
+ * for IPv6 (RFC 4861).
+ */
+package org.onlab.packet.ndp;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java
new file mode 100644
index 00000000..e8e0cb5e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utilities for decoding and encoding packets of various network protocols
+ * and encapsulations.
+ */
+package org.onlab.packet;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/AbstractAccumulator.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/AbstractAccumulator.java
new file mode 100644
index 00000000..500f8d60
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/AbstractAccumulator.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import com.google.common.collect.Lists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Base implementation of an item accumulator. It allows triggering based on
+ * item inter-arrival time threshold, maximum batch life threshold and maximum
+ * batch size.
+ */
+public abstract class AbstractAccumulator<T> implements Accumulator<T> {
+
+ private Logger log = LoggerFactory.getLogger(AbstractAccumulator.class);
+
+ private final Timer timer;
+ private final int maxItems;
+ private final int maxBatchMillis;
+ private final int maxIdleMillis;
+
+ private volatile TimerTask idleTask = new ProcessorTask();
+ private volatile TimerTask maxTask = new ProcessorTask();
+
+ private List<T> items = Lists.newArrayList();
+
+ /**
+ * Creates an item accumulator capable of triggering on the specified
+ * thresholds.
+ *
+ * @param timer timer to use for scheduling check-points
+ * @param maxItems maximum number of items to accumulate before
+ * processing is triggered
+ * @param maxBatchMillis maximum number of millis allowed since the first
+ * item before processing is triggered
+ * @param maxIdleMillis maximum number millis between items before
+ * processing is triggered
+ */
+ protected AbstractAccumulator(Timer timer, int maxItems,
+ int maxBatchMillis, int maxIdleMillis) {
+ this.timer = checkNotNull(timer, "Timer cannot be null");
+
+ checkArgument(maxItems > 1, "Maximum number of items must be > 1");
+ checkArgument(maxBatchMillis > 0, "Maximum millis must be positive");
+ checkArgument(maxIdleMillis > 0, "Maximum idle millis must be positive");
+
+ this.maxItems = maxItems;
+ this.maxBatchMillis = maxBatchMillis;
+ this.maxIdleMillis = maxIdleMillis;
+ }
+
+ @Override
+ public synchronized void add(T item) {
+ idleTask = cancelIfActive(idleTask);
+ items.add(checkNotNull(item, "Item cannot be null"));
+
+ // Did we hit the max item threshold?
+ if (items.size() >= maxItems) {
+ maxTask = cancelIfActive(maxTask);
+ scheduleNow();
+ } else {
+ // Otherwise, schedule idle task and if this is a first item
+ // also schedule the max batch age task.
+ idleTask = schedule(maxIdleMillis);
+ if (items.size() == 1) {
+ maxTask = schedule(maxBatchMillis);
+ }
+ }
+ }
+
+ /**
+ * Finalizes the current batch, if ready, and schedules a new processor
+ * in the immediate future.
+ */
+ private void scheduleNow() {
+ if (isReady()) {
+ TimerTask task = new ProcessorTask(finalizeCurrentBatch());
+ timer.schedule(task, 1);
+ }
+ }
+
+ /**
+ * Schedules a new processor task given number of millis in the future.
+ * Batch finalization is deferred to time of execution.
+ */
+ private TimerTask schedule(int millis) {
+ TimerTask task = new ProcessorTask();
+ timer.schedule(task, millis);
+ return task;
+ }
+
+ /**
+ * Cancels the specified task if it is active.
+ */
+ private TimerTask cancelIfActive(TimerTask task) {
+ if (task != null) {
+ task.cancel();
+ }
+ return task;
+ }
+
+ // Task for triggering processing of accumulated items
+ private class ProcessorTask extends TimerTask {
+
+ private final List<T> items;
+
+ // Creates a new processor task with deferred batch finalization.
+ ProcessorTask() {
+ this.items = null;
+ }
+
+ // Creates a new processor task with pre-emptive batch finalization.
+ ProcessorTask(List<T> items) {
+ this.items = items;
+ }
+
+ @Override
+ public void run() {
+ synchronized (AbstractAccumulator.this) {
+ idleTask = cancelIfActive(idleTask);
+ }
+ if (isReady()) {
+ try {
+ synchronized (AbstractAccumulator.this) {
+ maxTask = cancelIfActive(maxTask);
+ }
+ List<T> batch = items != null ? items : finalizeCurrentBatch();
+ if (!batch.isEmpty()) {
+ processItems(batch);
+ }
+ } catch (Exception e) {
+ log.warn("Unable to process batch due to", e);
+ }
+ } else {
+ synchronized (AbstractAccumulator.this) {
+ idleTask = schedule(maxIdleMillis);
+ }
+ }
+ }
+ }
+
+ // Demotes and returns the current batch of items and promotes a new one.
+ private synchronized List<T> finalizeCurrentBatch() {
+ List<T> toBeProcessed = items;
+ items = Lists.newArrayList();
+ return toBeProcessed;
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ /**
+ * Returns the backing timer.
+ *
+ * @return backing timer
+ */
+ public Timer timer() {
+ return timer;
+ }
+
+ /**
+ * Returns the maximum number of items allowed to accumulate before
+ * processing is triggered.
+ *
+ * @return max number of items
+ */
+ public int maxItems() {
+ return maxItems;
+ }
+
+ /**
+ * Returns the maximum number of millis allowed to expire since the first
+ * item before processing is triggered.
+ *
+ * @return max number of millis a batch is allowed to last
+ */
+ public int maxBatchMillis() {
+ return maxBatchMillis;
+ }
+
+ /**
+ * Returns the maximum number of millis allowed to expire since the last
+ * item arrival before processing is triggered.
+ *
+ * @return max number of millis since the last item
+ */
+ public int maxIdleMillis() {
+ return maxIdleMillis;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Accumulator.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Accumulator.java
new file mode 100644
index 00000000..20b7a481
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Accumulator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.List;
+
+/**
+ * Abstraction of an accumulator capable of collecting items and at some
+ * point in time triggers processing of all previously accumulated items.
+ *
+ * @param <T> item type
+ */
+public interface Accumulator<T> {
+
+ /**
+ * Adds an item to the current batch. This operation may, or may not
+ * trigger processing of the current batch of items.
+ *
+ * @param item item to be added to the current batch
+ */
+ void add(T item);
+
+ /**
+ * Processes the specified list of accumulated items.
+ *
+ * @param items list of accumulated items
+ */
+ void processItems(List<T> items);
+
+ /**
+ * Indicates whether the accumulator is ready to process items.
+ *
+ * @return true if ready to process
+ */
+ boolean isReady();
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Bandwidth.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Bandwidth.java
new file mode 100644
index 00000000..349e660f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Bandwidth.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import com.google.common.collect.ComparisonChain;
+
+import java.util.Objects;
+
+/**
+ * Representation of bandwidth.
+ * Use the static factory method corresponding to the unit (like Kbps) you desire on instantiation.
+ */
+public final class Bandwidth implements RichComparable<Bandwidth> {
+
+ private final double bps;
+
+ /**
+ * Creates a new instance with given bandwidth.
+ *
+ * @param bps bandwidth value to be assigned
+ */
+ private Bandwidth(double bps) {
+ this.bps = bps;
+ }
+
+ // Constructor for serialization
+ private Bandwidth() {
+ this.bps = 0;
+ }
+
+ /**
+ * Creates a new instance with given bandwidth in bps.
+ *
+ * @param bps bandwidth value to be assigned
+ * @return {@link Bandwidth} instance with given bandwidth
+ */
+ public static Bandwidth bps(double bps) {
+ return new Bandwidth(bps);
+ }
+
+ /**
+ * Creates a new instance with given bandwidth in Kbps.
+ *
+ * @param kbps bandwidth value to be assigned
+ * @return {@link Bandwidth} instance with given bandwidth
+ */
+ public static Bandwidth kbps(double kbps) {
+ return bps(kbps * 1_000L);
+ }
+
+ /**
+ * Creates a new instance with given bandwidth in Mbps.
+ *
+ * @param mbps bandwidth value to be assigned
+ * @return {@link Bandwidth} instance with given bandwidth
+ */
+ public static Bandwidth mbps(double mbps) {
+ return bps(mbps * 1_000_000L);
+ }
+
+ /**
+ * Creates a new instance with given bandwidth in Gbps.
+ *
+ * @param gbps bandwidth value to be assigned
+ * @return {@link Bandwidth} instance with given bandwidth
+ */
+ public static Bandwidth gbps(double gbps) {
+ return bps(gbps * 1_000_000_000L);
+ }
+
+ /**
+ * Returns bandwidth in bps.
+ *
+ * @return bandwidth in bps.
+ */
+ public double bps() {
+ return bps;
+ }
+
+ /**
+ * Returns a Bandwidth whose value is (this + value).
+ *
+ * @param value value to be added to this Frequency
+ * @return this + value
+ */
+ public Bandwidth add(Bandwidth value) {
+ return bps(this.bps + value.bps);
+ }
+
+ /**
+ * Returns a Bandwidth whose value is (this - value).
+ *
+ * @param value value to be added to this Frequency
+ * @return this - value
+ */
+ public Bandwidth subtract(Bandwidth value) {
+ return bps(this.bps - value.bps);
+ }
+
+ @Override
+ public int compareTo(Bandwidth other) {
+ return ComparisonChain.start()
+ .compare(this.bps, other.bps)
+ .result();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Bandwidth) {
+ Bandwidth that = (Bandwidth) obj;
+ return Objects.equals(this.bps, that.bps);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(Double.doubleToLongBits(bps));
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(this.bps);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BlockingBoolean.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BlockingBoolean.java
new file mode 100644
index 00000000..f3049c31
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BlockingBoolean.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+
+/**
+ * Mutable boolean that allows threads to wait for a specified value.
+ */
+public class BlockingBoolean extends AbstractQueuedSynchronizer {
+
+ private static final int TRUE = 1;
+ private static final int FALSE = 0;
+
+ /**
+ * Creates a new blocking boolean with the specified value.
+ *
+ * @param value the starting value
+ */
+ public BlockingBoolean(boolean value) {
+ setState(value ? TRUE : FALSE);
+ }
+
+ /**
+ * Causes the current thread to wait until the boolean equals the specified
+ * value unless the thread is {@linkplain Thread#interrupt interrupted}.
+ *
+ * @param value specified value
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public void await(boolean value) throws InterruptedException {
+ acquireSharedInterruptibly(value ? TRUE : FALSE);
+ }
+
+ /**
+ * Causes the current thread to wait until the boolean equals the specified
+ * value unless the thread is {@linkplain Thread#interrupt interrupted},
+ * or the specified waiting time elapses.
+ *
+ * @param value specified value
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the {@code timeout} argument
+ * @return {@code true} if the count reached zero and {@code false}
+ * if the waiting time elapsed before the count reached zero
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean await(boolean value, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return tryAcquireSharedNanos(value ? TRUE : FALSE, unit.toNanos(timeout));
+ }
+
+ protected int tryAcquireShared(int acquires) {
+ return (getState() == acquires) ? 1 : -1;
+ }
+
+ /**
+ * Sets the value of the blocking boolean.
+ *
+ * @param value new value
+ */
+ public void set(boolean value) {
+ releaseShared(value ? TRUE : FALSE);
+ }
+
+ /**
+ * Gets the value of the blocking boolean.
+ *
+ * @return current value
+ */
+ public boolean get() {
+ return getState() == TRUE;
+ }
+
+ protected boolean tryReleaseShared(int releases) {
+ // Signal on state change only
+ int state = getState();
+ if (state == releases) {
+ return false;
+ }
+ return compareAndSetState(state, releases);
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java
new file mode 100644
index 00000000..9eef6609
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Implementation of ThreadPoolExecutor that bounds the work queue.
+ * <p>
+ * When a new job would exceed the queue bound, the job is run on the caller's
+ * thread rather than on a thread from the pool.
+ * </p>
+ */
+public final class BoundedThreadPool extends ThreadPoolExecutor {
+
+ private static final org.slf4j.Logger log = LoggerFactory.getLogger(BoundedThreadPool.class);
+
+ protected static int maxQueueSize = 80_000; //TODO tune this value
+ //private static final RejectedExecutionHandler DEFAULT_HANDLER = new CallerFeedbackPolicy();
+ private static final long STATS_INTERVAL = 5_000; //ms
+
+ private final BlockingBoolean underHighLoad;
+
+ private BoundedThreadPool(int numberOfThreads,
+ ThreadFactory threadFactory) {
+ super(numberOfThreads, numberOfThreads,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(maxQueueSize),
+ threadFactory,
+ new CallerFeedbackPolicy());
+ underHighLoad = ((CallerFeedbackPolicy) getRejectedExecutionHandler()).load();
+ }
+
+ /**
+ * Returns a single-thread, bounded executor service.
+ *
+ * @param threadFactory thread factory for the worker thread.
+ * @return the bounded thread pool
+ */
+ public static BoundedThreadPool newSingleThreadExecutor(ThreadFactory threadFactory) {
+ return new BoundedThreadPool(1, threadFactory);
+ }
+
+ /**
+ * Returns a fixed-size, bounded executor service.
+ *
+ * @param numberOfThreads number of threads in the pool
+ * @param threadFactory thread factory for the worker threads.
+ * @return the bounded thread pool
+ */
+ public static BoundedThreadPool newFixedThreadPool(int numberOfThreads, ThreadFactory threadFactory) {
+ return new BoundedThreadPool(numberOfThreads, threadFactory);
+ }
+
+ //TODO Might want to switch these to use Metrics class Meter and/or Gauge instead.
+ private final Counter submitted = new Counter();
+ private final Counter taken = new Counter();
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ submitted.add(1);
+ return super.submit(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ submitted.add(1);
+ return super.submit(task, result);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ submitted.add(1);
+ super.execute(command);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ submitted.add(1);
+ return super.submit(task);
+ }
+
+
+ @Override
+ protected void beforeExecute(Thread t, Runnable r) {
+ super.beforeExecute(t, r);
+ taken.add(1);
+ periodicallyPrintStats();
+ updateLoad();
+ }
+
+ // TODO schedule this with a fixed delay from a scheduled executor
+ private final AtomicLong lastPrinted = new AtomicLong(0L);
+
+ private void periodicallyPrintStats() {
+ long now = System.currentTimeMillis();
+ long prev = lastPrinted.get();
+ if (now - prev > STATS_INTERVAL) {
+ if (lastPrinted.compareAndSet(prev, now)) {
+ log.debug("queue size: {} jobs, submitted: {} jobs/s, taken: {} jobs/s",
+ getQueue().size(),
+ submitted.throughput(), taken.throughput());
+ submitted.reset();
+ taken.reset();
+ }
+ }
+ }
+
+ // TODO consider updating load whenever queue changes
+ private void updateLoad() {
+ underHighLoad.set(getQueue().remainingCapacity() / (double) maxQueueSize < 0.2);
+ }
+
+ /**
+ * Feedback policy that delays the caller's thread until the executor's work
+ * queue falls below a threshold, then runs the job on the caller's thread.
+ */
+ private static final class CallerFeedbackPolicy implements RejectedExecutionHandler {
+
+ private final BlockingBoolean underLoad = new BlockingBoolean(false);
+
+ public BlockingBoolean load() {
+ return underLoad;
+ }
+
+ /**
+ * Executes task r in the caller's thread, unless the executor
+ * has been shut down, in which case the task is discarded.
+ *
+ * @param r the runnable task requested to be executed
+ * @param e the executor attempting to execute this task
+ */
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+ if (!e.isShutdown()) {
+ // Wait for up to 1 second while the queue drains...
+ boolean notified = false;
+ try {
+ notified = underLoad.await(false, 1, TimeUnit.SECONDS);
+ } catch (InterruptedException exception) {
+ log.debug("Got exception waiting for notification:", exception);
+ } finally {
+ if (!notified) {
+ log.info("Waited for 1 second on {}. Proceeding with work...",
+ Thread.currentThread().getName());
+ } else {
+ log.info("FIXME we got a notice");
+ }
+ }
+ // Do the work on the submitter's thread
+ r.run();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ByteArraySizeHashPrinter.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ByteArraySizeHashPrinter.java
new file mode 100644
index 00000000..cc39ce28
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ByteArraySizeHashPrinter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.util;
+
+import java.util.Arrays;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+
+/**
+ * Helper to print byte[] length and hashCode.
+ */
+public final class ByteArraySizeHashPrinter {
+
+ private final byte[] bytes;
+
+ /**
+ * Returns ByteArraySizeHashPrinter wrapping given byte[].
+ *
+ * @param bytes bytes to wrap around
+ * @return ByteArraySizeHashPrinter
+ */
+ public static ByteArraySizeHashPrinter of(byte[] bytes) {
+ return new ByteArraySizeHashPrinter(bytes);
+ }
+
+ /**
+ * Returns ByteArraySizeHashPrinter wrapping given byte[].
+ *
+ * @param bytes bytes to wrap around
+ * @return null if {@code bytes == null}, ByteArraySizeHashPrinter otherwise
+ */
+ public static ByteArraySizeHashPrinter orNull(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ return new ByteArraySizeHashPrinter(bytes);
+ }
+
+ public ByteArraySizeHashPrinter(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public String toString() {
+ ToStringHelper helper = MoreObjects.toStringHelper("byte[]");
+ if (bytes != null) {
+ helper.add("length", bytes.length)
+ .add("hash", Arrays.hashCode(bytes));
+ } else {
+ helper.addValue(bytes);
+ }
+ return helper.toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Counter.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Counter.java
new file mode 100644
index 00000000..bde28783
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Counter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Counting mechanism capable of tracking occurrences and rates.
+ */
+public class Counter {
+
+ private long total = 0;
+ private long start = System.currentTimeMillis();
+ private long end = 0;
+
+ /**
+ * Creates a new counter.
+ */
+ public Counter() {
+ }
+
+ /**
+ * Creates a new counter in a specific state. If non-zero end time is
+ * specified, the counter will be frozen.
+ *
+ * @param start start time
+ * @param total total number of items to start with
+ * @param end end time; if non-ze
+ */
+ public Counter(long start, long total, long end) {
+ checkArgument(start <= end, "Malformed interval: start > end");
+ checkArgument(total >= 0, "Total must be non-negative");
+ this.start = start;
+ this.total = total;
+ this.end = end;
+ }
+
+ /**
+ * Resets the counter, by zeroing out the count and restarting the timer.
+ */
+ public synchronized void reset() {
+ end = 0;
+ total = 0;
+ start = System.currentTimeMillis();
+ }
+
+ /**
+ * Freezes the counter in the current state including the counts and times.
+ */
+ public synchronized void freeze() {
+ end = System.currentTimeMillis();
+ }
+
+ /**
+ * Adds the specified number of occurrences to the counter. No-op if the
+ * counter has been frozen.
+ *
+ * @param count number of occurrences
+ */
+ public synchronized void add(long count) {
+ checkArgument(count >= 0, "Count must be non-negative");
+ if (end == 0L) {
+ total += count;
+ }
+ }
+
+ /**
+ * Returns the number of occurrences per second.
+ *
+ * @return throughput in occurrences per second
+ */
+ public synchronized double throughput() {
+ return total / duration();
+ }
+
+ /**
+ * Returns the total number of occurrences counted.
+ *
+ * @return number of counted occurrences
+ */
+ public synchronized long total() {
+ return total;
+ }
+
+ /**
+ * Returns the duration expressed in fractional number of seconds.
+ *
+ * @return fractional number of seconds since the last reset
+ */
+ public synchronized double duration() {
+ // Protect against 0 return by artificially setting duration to 1ms
+ long duration = (end == 0L ? System.currentTimeMillis() : end) - start;
+ return (duration == 0 ? 1 : duration) / 1000.0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(total, start, end);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Counter) {
+ final Counter other = (Counter) obj;
+ return Objects.equals(this.total, other.total) &&
+ Objects.equals(this.start, other.start) &&
+ Objects.equals(this.end, other.end);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("total", total)
+ .add("start", start)
+ .add("end", end)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Frequency.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Frequency.java
new file mode 100644
index 00000000..5669abdc
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Frequency.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ComparisonChain;
+
+import java.util.Objects;
+
+/**
+ * Class representing frequency. This class is intended to be used for a value whose unit is Hz
+ * and its family (KHz, MHz, etc.).
+ *
+ * <p>
+ * Note: this class is mainly intended to be used for lambda, which
+ * represents THz order. Long has enough space to represent over THz frequency as Hz,
+ * and the underlying value is long as Hz. This means this class can't represent
+ * sub-Hz accuracy.
+ * </p>
+ */
+public final class Frequency implements RichComparable<Frequency> {
+
+ private static final long KHZ = 1_000L;
+ private static final long MHZ = 1_000_000L;
+ private static final long GHZ = 1_000_000_000L;
+ private static final long THZ = 1_000_000_000_000L;
+
+ private final long frequency; // frequency in Hz
+
+ /**
+ * Creates an instance representing the specified frequency in Hz.
+ *
+ * @param frequency frequency in Hz
+ */
+ private Frequency(long frequency) {
+ this.frequency = frequency;
+ }
+
+ /**
+ * Return the value this instance represents as Hz.
+ *
+ * @return frequency in Hz
+ */
+ public long asHz() {
+ return frequency;
+ }
+
+ /**
+ * Returns an instance representing the specified value in Hz.
+ *
+ * @param value frequency in Hz
+ * @return instance representing the given frequency
+ */
+ public static Frequency ofHz(long value) {
+ return new Frequency(value);
+ }
+
+ /**
+ * Returns an instance representing the specified value in KHz.
+ *
+ * @param value frequency in KHz
+ * @return instance representing the given frequency
+ */
+ public static Frequency ofKHz(double value) {
+ return new Frequency((long) (value * KHZ));
+ }
+
+ /**
+ * Returns an instance representing the specified value in MHz.
+ *
+ * @param value frequency in MHz
+ * @return instance representing the given frequency
+ */
+ public static Frequency ofMHz(double value) {
+ return new Frequency((long) (value * MHZ));
+ }
+
+ /**
+ * Returns an instance representing the specified value in GHz.
+ *
+ * @param value frequency in GHz
+ * @return instance representing the given frequency
+ */
+ public static Frequency ofGHz(double value) {
+ return new Frequency((long) (value * GHZ));
+ }
+
+ /**
+ * Returns an instance representing the specified value in THz.
+ *
+ * @param value frequency in THz
+ * @return instance representing the given frequency
+ */
+ public static Frequency ofTHz(double value) {
+ return new Frequency((long) (value * THZ));
+ }
+
+ /**
+ * Returns a Frequency whose value is (this + value).
+ *
+ * @param value value to be added to this Frequency
+ * @return this + value
+ */
+ public Frequency add(Frequency value) {
+ return new Frequency(this.frequency + value.frequency);
+ }
+
+ /**
+ * Returns a Frequency whose value is (this - value).
+ *
+ * @param value value to be subtracted from this Frequency
+ * @return this - value
+ */
+ public Frequency subtract(Frequency value) {
+ return new Frequency(this.frequency - value.frequency);
+ }
+
+ /**
+ * Returns a Frequency whose value is (this * value).
+ *
+ * @param value value to be multiplied by this Frequency
+ * @return this * value
+ */
+ public Frequency multiply(long value) {
+ return new Frequency(this.frequency * value);
+ }
+
+ /**
+ * Returns a Frequency whose value is Math.floorDiv(this, value).
+ *
+ * @param value value to be divided by this Frequency
+ * @return Math.floorDiv(this, value)
+ */
+ public Frequency floorDivision(long value) {
+ return new Frequency(Math.floorDiv(this.frequency, value));
+ }
+
+ @Override
+ public int compareTo(Frequency other) {
+ return ComparisonChain.start()
+ .compare(this.frequency, other.frequency)
+ .result();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(frequency);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Frequency)) {
+ return false;
+ }
+
+ final Frequency other = (Frequency) obj;
+ return this.frequency == other.frequency;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("frequency", frequency + "Hz")
+ .toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/GroupedThreadFactory.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/GroupedThreadFactory.java
new file mode 100644
index 00000000..9001cf5f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/GroupedThreadFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import org.apache.commons.lang3.concurrent.ConcurrentUtils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadFactory;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Thread factory for creating threads that belong to the specified thread group.
+ */
+public final class GroupedThreadFactory implements ThreadFactory {
+
+ public static final String DELIMITER = "/";
+
+ private final ThreadGroup group;
+
+ // Cache of created thread factories.
+ private static final ConcurrentHashMap<String, GroupedThreadFactory> FACTORIES =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Returns thread factory for producing threads associated with the specified
+ * group name. The group name-space is hierarchical, based on slash-delimited
+ * name segments, e.g. {@code onos/intent}.
+ *
+ * @param groupName group name
+ * @return thread factory
+ */
+ public static GroupedThreadFactory groupedThreadFactory(String groupName) {
+ GroupedThreadFactory factory = FACTORIES.get(groupName);
+ if (factory != null) {
+ return factory;
+ }
+
+ // Find the parent group or root the group hierarchy under default group.
+ int i = groupName.lastIndexOf(DELIMITER);
+ if (i > 0) {
+ String name = groupName.substring(0, i);
+ ThreadGroup parentGroup = groupedThreadFactory(name).threadGroup();
+ factory = new GroupedThreadFactory(new ThreadGroup(parentGroup, groupName));
+ } else {
+ factory = new GroupedThreadFactory(new ThreadGroup(groupName));
+ }
+
+ return ConcurrentUtils.putIfAbsent(FACTORIES, groupName, factory);
+ }
+
+ // Creates a new thread group
+ private GroupedThreadFactory(ThreadGroup group) {
+ this.group = group;
+ }
+
+ /**
+ * Returns the thread group associated with the factory.
+ *
+ * @return thread group
+ */
+ public ThreadGroup threadGroup() {
+ return group;
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(group, r);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("group", group).toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexString.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexString.java
new file mode 100644
index 00000000..a1aba93b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexString.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+public final class HexString {
+
+ private HexString() {
+
+ }
+
+ /**
+ * Convert a string of bytes to a ':' separated hex string.
+ *
+ * @param bytes string of bytes to convert
+ * @return "0f:ca:fe:de:ad:be:ef"
+ */
+ public static String toHexString(final byte[] bytes) {
+ if (bytes == null) {
+ return "(null)";
+ }
+ int i;
+ StringBuilder ret = new StringBuilder(bytes.length * 3 - 1);
+ String tmp;
+ for (i = 0; i < bytes.length; i++) {
+ if (i > 0) {
+ ret.append(':');
+ }
+ tmp = Integer.toHexString((bytes[i] & 0xff));
+ if (tmp.length() == 1) {
+ ret.append('0');
+ }
+ ret.append(tmp);
+ }
+ return ret.toString();
+ }
+
+ public static String toHexString(final long val, final int padTo) {
+ char[] arr = Long.toHexString(val).toCharArray();
+ StringBuilder ret = new StringBuilder(padTo * 3 - 1);
+ // prepend the right number of leading zeros
+ int i = 0;
+ for (; i < (padTo * 2 - arr.length); i++) {
+ ret.append('0');
+ if ((i % 2) != 0) {
+ ret.append(':');
+ }
+ }
+ for (int j = 0; j < arr.length; j++) {
+ ret.append(arr[j]);
+ if ((((i + j) % 2) != 0) && (j < (arr.length - 1))) {
+ ret.append(':');
+ }
+ }
+ return ret.toString();
+ }
+
+ public static String toHexString(final long val) {
+ return toHexString(val, 8);
+ }
+
+ /**
+ * Convert a string of hex values into a string of bytes.
+ *
+ * @param values
+ * "0f:ca:fe:de:ad:be:ef"
+ * @return [15, 5 ,2, 5, 17]
+ * @throws NumberFormatException
+ * If the string can not be parsed
+ */
+ public static byte[] fromHexString(final String values) {
+ String[] octets = values.split(":");
+ byte[] ret = new byte[octets.length];
+
+ for (int i = 0; i < octets.length; i++) {
+ if (octets[i].length() > 2) {
+ throw new NumberFormatException("Invalid octet length");
+ }
+ ret[i] = Integer.valueOf(octets[i], 16).byteValue();
+ }
+ return ret;
+ }
+
+ public static long toLong(String value) {
+ String[] octets = value.split(":");
+ if (octets.length > 8) {
+ throw new NumberFormatException("Input string is too big to fit in long: " + value);
+ }
+ long l = 0;
+ for (String octet: octets) {
+ if (octet.length() > 2) {
+ throw new NumberFormatException(
+ "Each colon-separated byte component must consist of 1 or 2 hex digits: " + value);
+ }
+ short s = Short.parseShort(octet, 16);
+ l = (l << 8) + s;
+ }
+ return l;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ItemNotFoundException.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ItemNotFoundException.java
new file mode 100644
index 00000000..01440abf
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/ItemNotFoundException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+/**
+ * Represents condition where an item is not found or not available.
+ */
+public class ItemNotFoundException extends RuntimeException {
+
+ /**
+ * Creates a new exception with no message.
+ */
+ public ItemNotFoundException() {
+ }
+
+ /**
+ * Creates a new exception with the supplied message.
+ * @param message error message
+ */
+ public ItemNotFoundException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new exception with the supplied message and cause.
+ * @param message error message
+ * @param cause cause of the error
+ */
+ public ItemNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
new file mode 100644
index 00000000..9977e35d
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.ByteBufferInput;
+import com.esotericsoftware.kryo.io.ByteBufferOutput;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.pool.KryoCallback;
+import com.esotericsoftware.kryo.pool.KryoFactory;
+import com.esotericsoftware.kryo.pool.KryoPool;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Pool of Kryo instances, with classes pre-registered.
+ */
+//@ThreadSafe
+public final class KryoNamespace implements KryoFactory, KryoPool {
+
+ /**
+ * Default buffer size used for serialization.
+ *
+ * @see #serialize(Object)
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 4096;
+ public static final int MAX_BUFFER_SIZE = 100 * 1000 * 1000;
+
+ /**
+ * ID to use if this KryoNamespace does not define registration id.
+ */
+ public static final int FLOATING_ID = -1;
+
+ /**
+ * Smallest ID free to use for user defined registrations.
+ */
+ public static final int INITIAL_ID = 11;
+
+
+ private final KryoPool pool = new KryoPool.Builder(this)
+ .softReferences()
+ .build();
+
+ private final ImmutableList<RegistrationBlock> registeredBlocks;
+
+ private final boolean registrationRequired;
+
+
+ /**
+ * KryoNamespace builder.
+ */
+ //@NotThreadSafe
+ public static final class Builder {
+
+ private int blockHeadId = INITIAL_ID;
+ private List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>();
+ private List<RegistrationBlock> blocks = new ArrayList<>();
+ private boolean registrationRequired = true;
+
+ /**
+ * Builds a {@link KryoNamespace} instance.
+ *
+ * @return KryoNamespace
+ */
+ public KryoNamespace build() {
+ if (!types.isEmpty()) {
+ blocks.add(new RegistrationBlock(this.blockHeadId, types));
+ }
+ return new KryoNamespace(blocks, registrationRequired).populate(1);
+ }
+
+ /**
+ * Sets the next Kryo registration Id for following register entries.
+ *
+ * @param id Kryo registration Id
+ * @return this
+ *
+ * @see Kryo#register(Class, Serializer, int)
+ */
+ public Builder nextId(final int id) {
+ if (!types.isEmpty()) {
+ blocks.add(new RegistrationBlock(this.blockHeadId, types));
+ types = new ArrayList<>();
+ }
+ this.blockHeadId = id;
+ return this;
+ }
+
+ /**
+ * Registers classes to be serialized using Kryo default serializer.
+ *
+ * @param expectedTypes list of classes
+ * @return this
+ */
+ public Builder register(final Class<?>... expectedTypes) {
+ for (Class<?> clazz : expectedTypes) {
+ types.add(Pair.of(clazz, null));
+ }
+ return this;
+ }
+
+ /**
+ * Registers a class and it's serializer.
+ *
+ * @param classes list of classes to register
+ * @param serializer serializer to use for the class
+ * @return this
+ */
+ public Builder register(Serializer<?> serializer, final Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ types.add(Pair.of(clazz, serializer));
+ }
+ return this;
+ }
+
+ private Builder register(RegistrationBlock block) {
+ if (block.begin() != FLOATING_ID) {
+ // flush pending types
+ nextId(block.begin());
+ blocks.add(block);
+ nextId(block.begin() + block.types().size());
+ } else {
+ // flush pending types
+ final int addedBlockBegin = blockHeadId + types.size();
+ nextId(addedBlockBegin);
+ blocks.add(new RegistrationBlock(addedBlockBegin, block.types()));
+ nextId(addedBlockBegin + block.types().size());
+ }
+ return this;
+ }
+
+ /**
+ * Registers all the class registered to given KryoNamespace.
+ *
+ * @param ns KryoNamespace
+ * @return this
+ */
+ public Builder register(final KryoNamespace ns) {
+ for (RegistrationBlock block : ns.registeredBlocks) {
+ this.register(block);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the registrationRequired flag.
+ *
+ * @param registrationRequired Kryo's registrationRequired flag
+ * @return this
+ *
+ * @see Kryo#setRegistrationRequired(boolean)
+ */
+ public Builder setRegistrationRequired(boolean registrationRequired) {
+ this.registrationRequired = registrationRequired;
+ return this;
+ }
+ }
+
+ /**
+ * Creates a new {@link KryoNamespace} builder.
+ *
+ * @return builder
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Creates a Kryo instance pool.
+ *
+ * @param registeredTypes types to register
+ * @param registrationRequired
+ */
+ private KryoNamespace(final List<RegistrationBlock> registeredTypes, boolean registrationRequired) {
+ this.registeredBlocks = ImmutableList.copyOf(registeredTypes);
+ this.registrationRequired = registrationRequired;
+ }
+
+ /**
+ * Populates the Kryo pool.
+ *
+ * @param instances to add to the pool
+ * @return this
+ */
+ public KryoNamespace populate(int instances) {
+
+ for (int i = 0; i < instances; ++i) {
+ release(create());
+ }
+ return this;
+ }
+
+ /**
+ * Serializes given object to byte array using Kryo instance in pool.
+ * <p>
+ * Note: Serialized bytes must be smaller than {@link #MAX_BUFFER_SIZE}.
+ *
+ * @param obj Object to serialize
+ * @return serialized bytes
+ */
+ public byte[] serialize(final Object obj) {
+ return serialize(obj, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Serializes given object to byte array using Kryo instance in pool.
+ *
+ * @param obj Object to serialize
+ * @param bufferSize maximum size of serialized bytes
+ * @return serialized bytes
+ */
+ public byte[] serialize(final Object obj, final int bufferSize) {
+ ByteBufferOutput out = new ByteBufferOutput(bufferSize, MAX_BUFFER_SIZE);
+ try {
+ Kryo kryo = borrow();
+ try {
+ kryo.writeClassAndObject(out, obj);
+ out.flush();
+ return out.toBytes();
+ } finally {
+ release(kryo);
+ }
+ } finally {
+ out.release();
+ }
+ }
+
+ /**
+ * Serializes given object to byte buffer using Kryo instance in pool.
+ *
+ * @param obj Object to serialize
+ * @param buffer to write to
+ */
+ public void serialize(final Object obj, final ByteBuffer buffer) {
+ ByteBufferOutput out = new ByteBufferOutput(buffer);
+ Kryo kryo = borrow();
+ try {
+ kryo.writeClassAndObject(out, obj);
+ out.flush();
+ } finally {
+ release(kryo);
+ }
+ }
+
+ /**
+ * Serializes given object to OutputStream using Kryo instance in pool.
+ *
+ * @param obj Object to serialize
+ * @param stream to write to
+ */
+ public void serialize(final Object obj, final OutputStream stream) {
+ serialize(obj, stream, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Serializes given object to OutputStream using Kryo instance in pool.
+ *
+ * @param obj Object to serialize
+ * @param stream to write to
+ * @param bufferSize size of the buffer in front of the stream
+ */
+ public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
+ ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
+ Kryo kryo = borrow();
+ try {
+ kryo.writeClassAndObject(out, obj);
+ out.flush();
+ } finally {
+ release(kryo);
+ }
+ }
+
+ /**
+ * Deserializes given byte array to Object using Kryo instance in pool.
+ *
+ * @param bytes serialized bytes
+ * @param <T> deserialized Object type
+ * @return deserialized Object
+ */
+ public <T> T deserialize(final byte[] bytes) {
+ Input in = new Input(bytes);
+ Kryo kryo = borrow();
+ try {
+ @SuppressWarnings("unchecked")
+ T obj = (T) kryo.readClassAndObject(in);
+ return obj;
+ } finally {
+ release(kryo);
+ }
+ }
+
+ /**
+ * Deserializes given byte buffer to Object using Kryo instance in pool.
+ *
+ * @param buffer input with serialized bytes
+ * @param <T> deserialized Object type
+ * @return deserialized Object
+ */
+ public <T> T deserialize(final ByteBuffer buffer) {
+ ByteBufferInput in = new ByteBufferInput(buffer);
+ Kryo kryo = borrow();
+ try {
+ @SuppressWarnings("unchecked")
+ T obj = (T) kryo.readClassAndObject(in);
+ return obj;
+ } finally {
+ release(kryo);
+ }
+ }
+
+ /**
+ * Deserializes given InputStream to an Object using Kryo instance in pool.
+ *
+ * @param stream input stream
+ * @param <T> deserialized Object type
+ * @return deserialized Object
+ */
+ public <T> T deserialize(final InputStream stream) {
+ return deserialize(stream, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Deserializes given InputStream to an Object using Kryo instance in pool.
+ *
+ * @param stream input stream
+ * @param <T> deserialized Object type
+ * @return deserialized Object
+ * @param bufferSize size of the buffer in front of the stream
+ */
+ public <T> T deserialize(final InputStream stream, final int bufferSize) {
+ ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
+ Kryo kryo = borrow();
+ try {
+ @SuppressWarnings("unchecked")
+ T obj = (T) kryo.readClassAndObject(in);
+ return obj;
+ } finally {
+ release(kryo);
+ }
+ }
+
+ /**
+ * Creates a Kryo instance.
+ *
+ * @return Kryo instance
+ */
+ @Override
+ public Kryo create() {
+ Kryo kryo = new Kryo();
+ kryo.setRegistrationRequired(registrationRequired);
+ for (RegistrationBlock block : registeredBlocks) {
+ int id = block.begin();
+ if (id == FLOATING_ID) {
+ id = kryo.getNextRegistrationId();
+ }
+ for (Pair<Class<?>, Serializer<?>> entry : block.types()) {
+ final Serializer<?> serializer = entry.getRight();
+ if (serializer == null) {
+ kryo.register(entry.getLeft(), id++);
+ } else {
+ kryo.register(entry.getLeft(), serializer, id++);
+ }
+ }
+ }
+ return kryo;
+ }
+
+ @Override
+ public Kryo borrow() {
+ return pool.borrow();
+ }
+
+ @Override
+ public void release(Kryo kryo) {
+ pool.release(kryo);
+ }
+
+ @Override
+ public <T> T run(KryoCallback<T> callback) {
+ return pool.run(callback);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("registeredBlocks", registeredBlocks)
+ .toString();
+ }
+
+ static final class RegistrationBlock {
+ private final int begin;
+ private final ImmutableList<Pair<Class<?>, Serializer<?>>> types;
+
+ public RegistrationBlock(int begin, List<Pair<Class<?>, Serializer<?>>> types) {
+ this.begin = begin;
+ this.types = ImmutableList.copyOf(types);
+ }
+
+ public int begin() {
+ return begin;
+ }
+
+ public ImmutableList<Pair<Class<?>, Serializer<?>>> types() {
+ return types;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("begin", begin)
+ .add("types", types)
+ .toString();
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
new file mode 100644
index 00000000..2d222eac
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+
+/**
+ * Creates an instance of new ConcurrentHashMap on each {@link #get()} call.
+ * <p>
+ * To be used with
+ * {@link org.apache.commons.lang3.concurrent.ConcurrentUtils#createIfAbsent}
+ * </p>
+ *
+ * @param <K> ConcurrentHashMap key type
+ * @param <V> ConcurrentHashMap value type
+ */
+public final class NewConcurrentHashMap<K, V>
+ implements ConcurrentInitializer<ConcurrentMap<K, V>> {
+
+ public static final NewConcurrentHashMap<?, ?> INSTANCE = new NewConcurrentHashMap<>();
+
+ @SuppressWarnings("unchecked")
+ public static <K, V> NewConcurrentHashMap<K, V> ifNeeded() {
+ return (NewConcurrentHashMap<K, V>) INSTANCE;
+ }
+
+ @Override
+ public ConcurrentMap<K, V> get() {
+ return new ConcurrentHashMap<>();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java
new file mode 100644
index 00000000..647e0c0d
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+/**
+ * Allows slf4j style formatting of parameters into a string.
+ */
+public final class PositionalParameterStringFormatter {
+
+ /**
+ * Hide default constructor.
+ */
+ private PositionalParameterStringFormatter() {
+ }
+
+ /**
+ * Formats a string using slf4j style positional parameter replacement.
+ * Instances of "{}" in the source string are replaced in order by the
+ * specified parameter values as strings.
+ *
+ * @param source original string to format
+ * @param parameters list of parameters that will be substituted
+ * @return formatted string
+ */
+ public static String format(String source, Object... parameters) {
+ String current = source;
+ for (Object parameter : parameters) {
+ if (!current.contains("{}")) {
+ return current;
+ }
+ current = current.replaceFirst("\\{\\}", String.valueOf(parameter));
+ }
+ return current;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RetryingFunction.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RetryingFunction.java
new file mode 100644
index 00000000..484e236f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RetryingFunction.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.function.Function;
+
+import com.google.common.base.Throwables;
+
+/**
+ * Function that retries execution on failure.
+ *
+ * @param <U> input type
+ * @param <V> output type
+ */
+public class RetryingFunction<U, V> implements Function<U, V> {
+
+ private final Function<U, V> baseFunction;
+ private final Class<? extends Throwable> exceptionClass;
+ private final int maxRetries;
+ private final int maxDelayBetweenRetries;
+
+ public RetryingFunction(Function<U, V> baseFunction,
+ Class<? extends Throwable> exceptionClass,
+ int maxRetries,
+ int maxDelayBetweenRetries) {
+ this.baseFunction = baseFunction;
+ this.exceptionClass = exceptionClass;
+ this.maxRetries = maxRetries;
+ this.maxDelayBetweenRetries = maxDelayBetweenRetries;
+ }
+
+ @Override
+ public V apply(U input) {
+ int retryAttempts = 0;
+ while (true) {
+ try {
+ return baseFunction.apply(input);
+ } catch (Throwable t) {
+ if (!exceptionClass.isAssignableFrom(t.getClass()) || retryAttempts == maxRetries) {
+ Throwables.propagate(t);
+ }
+ Tools.randomDelay(maxDelayBetweenRetries);
+ retryAttempts++;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RichComparable.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RichComparable.java
new file mode 100644
index 00000000..8a49bba9
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/RichComparable.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Extends useful methods for comparison to {@link Comparable} interface.
+ *
+ * @param <T> type of instance to be compared
+ */
+public interface RichComparable<T> extends Comparable<T> {
+ /**
+ * Compares if this object is less than the specified object.
+ *
+ * @param other the object to be compared
+ * @return true if this object is less than the specified object
+ */
+ default boolean isLessThan(T other) {
+ return compareTo(checkNotNull(other)) < 0;
+ }
+
+ /**
+ * Compares if this object is greater than the specified object.
+ *
+ * @param other the object to be compared
+ * @return true if this object is less thant the specified object
+ */
+ default boolean isGreaterThan(T other) {
+ return compareTo(checkNotNull(other)) > 0;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutorService.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutorService.java
new file mode 100644
index 00000000..051155ce
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutorService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Executor service wrapper for shared executors with safeguards on shutdown
+ * to prevent inadvertent shutdown.
+ */
+class SharedExecutorService implements ExecutorService {
+
+ private static final String NOT_ALLOWED = "Shutdown of shared executor is not allowed";
+
+ private ExecutorService executor;
+
+ /**
+ * Creates a wrapper for the given executor service.
+ *
+ * @param executor executor service to wrap
+ */
+ SharedExecutorService(ExecutorService executor) {
+ this.executor = executor;
+ }
+
+ /**
+ * Returns the backing executor service.
+ *
+ * @return backing executor service
+ */
+ ExecutorService backingExecutor() {
+ return executor;
+ }
+
+ /**
+ * Swaps the backing executor with a new one and shuts down the old one.
+ *
+ * @param executor new executor service
+ */
+ void setBackingExecutor(ExecutorService executor) {
+ ExecutorService oldExecutor = this.executor;
+ this.executor = executor;
+ oldExecutor.shutdown();
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException(NOT_ALLOWED);
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException(NOT_ALLOWED);
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return executor.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return executor.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return executor.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return executor.submit(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return executor.submit(task, result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return executor.submit(task);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return executor.invokeAll(tasks);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
+ long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return executor.invokeAll(tasks, timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return executor.invokeAny(tasks);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
+ long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return executor.invokeAny(tasks, timeout, unit);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ executor.execute(command);
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutors.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutors.java
new file mode 100644
index 00000000..0dadce85
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SharedExecutors.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.util;
+
+import java.util.Timer;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * Utility for managing a set of shared execution resources, such as a timer,
+ * single thread executor and thread pool executor for use by various parts of
+ * the platform or by applications.
+ * <p>
+ * Whenever possible, use of these shared resources is encouraged over creating
+ * separate ones.
+ * </p>
+ */
+public final class SharedExecutors {
+
+ public static final int DEFAULT_POOL_SIZE = 30;
+
+ private static SharedExecutorService singleThreadExecutor =
+ new SharedExecutorService(
+ newSingleThreadExecutor(groupedThreads("onos/shared",
+ "onos-single-executor")));
+
+ private static SharedExecutorService poolThreadExecutor =
+ new SharedExecutorService(
+ newFixedThreadPool(DEFAULT_POOL_SIZE,
+ groupedThreads("onos/shared",
+ "onos-pool-executor-%d")));
+
+ private static SharedTimer sharedTimer = new SharedTimer();
+
+ // Ban public construction
+ private SharedExecutors() {
+ }
+
+ /**
+ * Returns the shared single thread executor.
+ *
+ * @return shared single thread executor
+ */
+ public static ExecutorService getSingleThreadExecutor() {
+ return singleThreadExecutor;
+ }
+
+ /**
+ * Returns the shared thread pool executor.
+ *
+ * @return shared executor pool
+ */
+ public static ExecutorService getPoolThreadExecutor() {
+ return poolThreadExecutor;
+ }
+
+ /**
+ * Returns the shared timer.
+ *
+ * @return shared timer
+ */
+ public static Timer getTimer() {
+ return sharedTimer;
+ }
+
+ /**
+ * Sets the shared thread pool size.
+ *
+ * @param poolSize new pool size
+ */
+ public static void setPoolSize(int poolSize) {
+ checkArgument(poolSize > 0, "Shared pool size size must be greater than 0");
+ poolThreadExecutor.setBackingExecutor(
+ newFixedThreadPool(poolSize, groupedThreads("onos/shared",
+ "onos-pool-executor-%d")));
+ }
+
+ /**
+ * Shuts down all shared timers and executors and therefore should be
+ * called only by the framework.
+ */
+ public static void shutdown() {
+ sharedTimer.shutdown();
+ singleThreadExecutor.backingExecutor().shutdown();
+ poolThreadExecutor.backingExecutor().shutdown();
+ }
+
+ // Timer extension which does not allow outside cancel method.
+ private static class SharedTimer extends Timer {
+
+ public SharedTimer() {
+ super("onos-shared-timer");
+ }
+
+ @Override
+ public void cancel() {
+ throw new UnsupportedOperationException("Cancel of shared timer is not allowed");
+ }
+
+ private void shutdown() {
+ super.cancel();
+ }
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SlidingWindowCounter.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SlidingWindowCounter.java
new file mode 100644
index 00000000..4f0093c0
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/SlidingWindowCounter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Maintains a sliding window of value counts. The sliding window counter is
+ * initialized with a number of window slots. Calls to #incrementCount() will
+ * increment the value in the current window slot. Periodically the window
+ * slides and the oldest value count is dropped. Calls to #get() will get the
+ * total count for the last N window slots.
+ */
+public final class SlidingWindowCounter {
+
+ private volatile int headSlot;
+ private final int windowSlots;
+
+ private final List<AtomicLong> counters;
+
+ private final ScheduledExecutorService background;
+
+ private static final int SLIDE_WINDOW_PERIOD_SECONDS = 1;
+
+ /**
+ * Creates a new sliding window counter with the given total number of
+ * window slots.
+ *
+ * @param windowSlots total number of window slots
+ */
+ public SlidingWindowCounter(int windowSlots) {
+ checkArgument(windowSlots > 0, "Window size must be a positive integer");
+
+ this.windowSlots = windowSlots;
+ this.headSlot = 0;
+
+ // Initialize each item in the list to an AtomicLong of 0
+ this.counters = Collections.nCopies(windowSlots, 0)
+ .stream()
+ .map(AtomicLong::new)
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ background = Executors.newSingleThreadScheduledExecutor();
+ background.scheduleWithFixedDelay(this::advanceHead, 0,
+ SLIDE_WINDOW_PERIOD_SECONDS, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Releases resources used by the SlidingWindowCounter.
+ */
+ public void destroy() {
+ background.shutdownNow();
+ }
+
+ /**
+ * Increments the count of the current window slot by 1.
+ */
+ public void incrementCount() {
+ incrementCount(headSlot, 1);
+ }
+
+ /**
+ * Increments the count of the current window slot by the given value.
+ *
+ * @param value value to increment by
+ */
+ public void incrementCount(long value) {
+ incrementCount(headSlot, value);
+ }
+
+ private void incrementCount(int slot, long value) {
+ counters.get(slot).addAndGet(value);
+ }
+
+ /**
+ * Gets the total count for the last N window slots.
+ *
+ * @param slots number of slots to include in the count
+ * @return total count for last N slots
+ */
+ public long get(int slots) {
+ checkArgument(slots <= windowSlots,
+ "Requested window must be less than the total window slots");
+
+ long sum = 0;
+
+ for (int i = 0; i < slots; i++) {
+ int currentIndex = headSlot - i;
+ if (currentIndex < 0) {
+ currentIndex = counters.size() + currentIndex;
+ }
+ sum += counters.get(currentIndex).get();
+ }
+
+ return sum;
+ }
+
+ void advanceHead() {
+ counters.get(slotAfter(headSlot)).set(0);
+ headSlot = slotAfter(headSlot);
+ }
+
+ private int slotAfter(int slot) {
+ return (slot + 1) % windowSlots;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Spectrum.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Spectrum.java
new file mode 100644
index 00000000..42d76090
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Spectrum.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+/**
+ * Telecom optical wavelength bands: O, E, S, C, L and U bands.
+ *
+ * See ITU-T G-Series Recommendations, Supplement 39
+ */
+public final class Spectrum {
+
+ private Spectrum() {
+ }
+
+ // O band (original): 1260 to 1360 nm
+ public static final Frequency O_BAND_MIN = Frequency.ofTHz(220.436);
+ public static final Frequency O_BAND_MAX = Frequency.ofTHz(237.931);
+
+ // E band (extended): 1360 to 1460 nm
+ public static final Frequency E_BAND_MIN = Frequency.ofTHz(205.337);
+ public static final Frequency E_BAND_MAX = Frequency.ofTHz(220.436);
+
+ // S band (short wavelength): 1460 to 1530 nm
+ public static final Frequency S_BAND_MIN = Frequency.ofTHz(195.943);
+ public static final Frequency S_BAND_MAX = Frequency.ofTHz(205.337);
+
+ // C band (conventional): 1530 to 1565 nm
+ public static final Frequency C_BAND_MIN = Frequency.ofTHz(191.561);
+ public static final Frequency C_BAND_MAX = Frequency.ofTHz(195.943);
+
+ // L band (long wavelength): 1565 to 1625 nm
+ public static final Frequency L_BAND_MIN = Frequency.ofTHz(184.488);
+ public static final Frequency L_BAND_MAX = Frequency.ofTHz(191.561);
+
+ // U band (ultra-long wavelength): 1625 to 1675 nm
+ public static final Frequency U_BAND_MIN = Frequency.ofTHz(178.981);
+ public static final Frequency U_BAND_MAX = Frequency.ofTHz(184.488);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Timer.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Timer.java
new file mode 100644
index 00000000..fe508839
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Timer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import org.jboss.netty.util.HashedWheelTimer;
+
+/**
+ * Hashed-wheel timer singleton. Care must be taken to shutdown the timer
+ * only when the VM is ready to exit.
+ */
+public final class Timer {
+
+ private static volatile HashedWheelTimer timer;
+
+ // Ban public construction
+ private Timer() {
+ }
+
+ /**
+ * Returns the singleton hashed-wheel timer.
+ *
+ * @return hashed-wheel timer
+ */
+ public static HashedWheelTimer getTimer() {
+ if (Timer.timer == null) {
+ initTimer();
+ }
+ return Timer.timer;
+ }
+
+ private static synchronized void initTimer() {
+ if (Timer.timer == null) {
+ HashedWheelTimer hwTimer = new HashedWheelTimer();
+ hwTimer.start();
+ Timer.timer = hwTimer;
+ }
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java
new file mode 100644
index 00000000..abc48ccf
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import static java.nio.file.Files.delete;
+import static java.nio.file.Files.walkFileTree;
+import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.slf4j.Logger;
+
+import com.google.common.base.Strings;
+import com.google.common.primitives.UnsignedLongs;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * Miscellaneous utility methods.
+ */
+public abstract class Tools {
+
+ private Tools() {
+ }
+
+ private static final Logger log = getLogger(Tools.class);
+
+ private static Random random = new Random();
+
+ /**
+ * Returns a thread factory that produces threads named according to the
+ * supplied name pattern.
+ *
+ * @param pattern name pattern
+ * @return thread factory
+ */
+ public static ThreadFactory namedThreads(String pattern) {
+ return new ThreadFactoryBuilder()
+ .setNameFormat(pattern)
+ .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
+ .build();
+ }
+
+ /**
+ * Returns a thread factory that produces threads named according to the
+ * supplied name pattern and from the specified thread-group. The thread
+ * group name is expected to be specified in slash-delimited format, e.g.
+ * {@code onos/intent}. The thread names will be produced by converting
+ * the thread group name into dash-delimited format and pre-pended to the
+ * specified pattern.
+ *
+ * @param groupName group name in slash-delimited format to indicate hierarchy
+ * @param pattern name pattern
+ * @return thread factory
+ */
+ public static ThreadFactory groupedThreads(String groupName, String pattern) {
+ return new ThreadFactoryBuilder()
+ .setThreadFactory(groupedThreadFactory(groupName))
+ .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
+ .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
+ .build();
+ }
+
+ /**
+ * Returns a thread factory that produces threads with MIN_PRIORITY.
+ *
+ * @param factory backing ThreadFactory
+ * @return thread factory
+ */
+ public static ThreadFactory minPriority(ThreadFactory factory) {
+ return new ThreadFactoryBuilder()
+ .setThreadFactory(factory)
+ .setPriority(Thread.MIN_PRIORITY)
+ .build();
+ }
+
+ /**
+ * Returns true if the collection is null or is empty.
+ *
+ * @param collection collection to test
+ * @return true if null or empty; false otherwise
+ */
+ public static boolean isNullOrEmpty(Collection collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ /**
+ * Returns the specified item if that item is not null; otherwise throws
+ * not found exception.
+ *
+ * @param item item to check
+ * @param message not found message
+ * @param <T> item type
+ * @return item if not null
+ * @throws org.onlab.util.ItemNotFoundException if item is null
+ */
+ public static <T> T nullIsNotFound(T item, String message) {
+ if (item == null) {
+ throw new ItemNotFoundException(message);
+ }
+ return item;
+ }
+
+ /**
+ * Returns the specified item if that item is not null; otherwise throws
+ * bad argument exception.
+ *
+ * @param item item to check
+ * @param message not found message
+ * @param <T> item type
+ * @return item if not null
+ * @throws IllegalArgumentException if item is null
+ */
+ public static <T> T nullIsIllegal(T item, String message) {
+ if (item == null) {
+ throw new IllegalArgumentException(message);
+ }
+ return item;
+ }
+
+ /**
+ * Converts a string from hex to long.
+ *
+ * @param string hex number in string form; sans 0x
+ * @return long value
+ */
+ public static long fromHex(String string) {
+ return UnsignedLongs.parseUnsignedLong(string, 16);
+ }
+
+ /**
+ * Converts a long value to hex string; 16 wide and sans 0x.
+ *
+ * @param value long value
+ * @return hex string
+ */
+ public static String toHex(long value) {
+ return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
+ }
+
+ /**
+ * Converts a long value to hex string; 16 wide and sans 0x.
+ *
+ * @param value long value
+ * @param width string width; zero padded
+ * @return hex string
+ */
+ public static String toHex(long value, int width) {
+ return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
+ }
+
+ /**
+ * Returns a copy of the input byte array.
+ *
+ * @param original input
+ * @return copy of original
+ */
+ public static byte[] copyOf(byte[] original) {
+ return Arrays.copyOf(original, original.length);
+ }
+
+ /**
+ * Get property as a string value.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ public static String get(Dictionary<?, ?> properties, String propertyName) {
+ Object v = properties.get(propertyName);
+ String s = (v instanceof String) ? (String) v :
+ v != null ? v.toString() : null;
+ return Strings.isNullOrEmpty(s) ? null : s.trim();
+ }
+
+ /**
+ * Suspends the current thread for a specified number of millis.
+ *
+ * @param ms number of millis
+ */
+ public static void delay(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted", e);
+ }
+ }
+
+ /**
+ * Returns a function that retries execution on failure.
+ * @param base base function
+ * @param exceptionClass type of exception for which to retry
+ * @param maxRetries max number of retries before giving up
+ * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
+ * the interval (0, maxDelayBetweenRetries]
+ * @return function
+ * @param <U> type of function input
+ * @param <V> type of function output
+ */
+ public static <U, V> Function<U, V> retryable(Function<U, V> base,
+ Class<? extends Throwable> exceptionClass,
+ int maxRetries,
+ int maxDelayBetweenRetries) {
+ return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
+ }
+
+ /**
+ * Returns a Supplier that retries execution on failure.
+ * @param base base supplier
+ * @param exceptionClass type of exception for which to retry
+ * @param maxRetries max number of retries before giving up
+ * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
+ * the interval (0, maxDelayBetweenRetries]
+ * @return supplier
+ * @param <V> type of supplied result
+ */
+ public static <V> Supplier<V> retryable(Supplier<V> base,
+ Class<? extends Throwable> exceptionClass,
+ int maxRetries,
+ int maxDelayBetweenRetries) {
+ return () -> new RetryingFunction<>(v -> base.get(),
+ exceptionClass,
+ maxRetries,
+ maxDelayBetweenRetries).apply(null);
+ }
+
+ /**
+ * Suspends the current thread for a random number of millis between 0 and
+ * the indicated limit.
+ *
+ * @param ms max number of millis
+ */
+ public static void randomDelay(int ms) {
+ try {
+ Thread.sleep(random.nextInt(ms));
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted", e);
+ }
+ }
+
+ /**
+ * Suspends the current thread for a specified number of millis and nanos.
+ *
+ * @param ms number of millis
+ * @param nanos number of nanos
+ */
+ public static void delay(int ms, int nanos) {
+ try {
+ Thread.sleep(ms, nanos);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted", e);
+ }
+ }
+
+ /**
+ * Slurps the contents of a file into a list of strings, one per line.
+ *
+ * @param path file path
+ * @return file contents
+ */
+ public static List<String> slurp(File path) {
+ try {
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
+
+ List<String> lines = new ArrayList<>();
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ return lines;
+
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Purges the specified directory path.&nbsp;Use with great caution since
+ * no attempt is made to check for symbolic links, which could result in
+ * deletion of unintended files.
+ *
+ * @param path directory to be removed
+ * @throws java.io.IOException if unable to remove contents
+ */
+ public static void removeDirectory(String path) throws IOException {
+ DirectoryDeleter visitor = new DirectoryDeleter();
+ File dir = new File(path);
+ if (dir.exists() && dir.isDirectory()) {
+ walkFileTree(Paths.get(path), visitor);
+ if (visitor.exception != null) {
+ throw visitor.exception;
+ }
+ }
+ }
+
+ /**
+ * Purges the specified directory path.&nbsp;Use with great caution since
+ * no attempt is made to check for symbolic links, which could result in
+ * deletion of unintended files.
+ *
+ * @param dir directory to be removed
+ * @throws java.io.IOException if unable to remove contents
+ */
+ public static void removeDirectory(File dir) throws IOException {
+ DirectoryDeleter visitor = new DirectoryDeleter();
+ if (dir.exists() && dir.isDirectory()) {
+ walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
+ if (visitor.exception != null) {
+ throw visitor.exception;
+ }
+ }
+ }
+
+ // Auxiliary path visitor for recursive directory structure removal.
+ private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
+
+ private IOException exception;
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
+ throws IOException {
+ if (attributes.isRegularFile()) {
+ delete(file);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
+ throws IOException {
+ delete(directory);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException ioe)
+ throws IOException {
+ this.exception = ioe;
+ return FileVisitResult.TERMINATE;
+ }
+ }
+
+ /**
+ * Returns a human friendly time ago string for a specified system time.
+ *
+ * @param unixTime system time in millis
+ * @return human friendly time ago
+ */
+ public static String timeAgo(long unixTime) {
+ long deltaMillis = System.currentTimeMillis() - unixTime;
+ long secondsSince = (long) (deltaMillis / 1000.0);
+ long minsSince = (long) (deltaMillis / (1000.0 * 60));
+ long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
+ long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
+ if (daysSince > 0) {
+ return String.format("%dd ago", daysSince);
+ } else if (hoursSince > 0) {
+ return String.format("%dh ago", hoursSince);
+ } else if (minsSince > 0) {
+ return String.format("%dm ago", minsSince);
+ } else if (secondsSince > 0) {
+ return String.format("%ds ago", secondsSince);
+ } else {
+ return "just now";
+ }
+ }
+
+ /**
+ * Copies the specified directory path.&nbsp;Use with great caution since
+ * no attempt is made to check for symbolic links, which could result in
+ * copy of unintended files.
+ *
+ * @param src directory to be copied
+ * @param dst destination directory to be removed
+ * @throws java.io.IOException if unable to remove contents
+ */
+ public static void copyDirectory(String src, String dst) throws IOException {
+ walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
+ }
+
+ /**
+ * Copies the specified directory path.&nbsp;Use with great caution since
+ * no attempt is made to check for symbolic links, which could result in
+ * copy of unintended files.
+ *
+ * @param src directory to be copied
+ * @param dst destination directory to be removed
+ * @throws java.io.IOException if unable to remove contents
+ */
+ public static void copyDirectory(File src, File dst) throws IOException {
+ walkFileTree(Paths.get(src.getAbsolutePath()),
+ new DirectoryCopier(src.getAbsolutePath(),
+ dst.getAbsolutePath()));
+ }
+
+ /**
+ * Returns the future value when complete or if future
+ * completes exceptionally returns the defaultValue.
+ *
+ * @param future future
+ * @param defaultValue default value
+ * @param <T> future value type
+ * @return future value when complete or if future
+ * completes exceptionally returns the defaultValue.
+ */
+ public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return defaultValue;
+ } catch (ExecutionException e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the future value when complete or if future
+ * completes exceptionally returns the defaultValue.
+ *
+ * @param future future
+ * @param timeout time to wait for successful completion
+ * @param timeUnit time unit
+ * @param defaultValue default value
+ * @param <T> future value type
+ * @return future value when complete or if future
+ * completes exceptionally returns the defaultValue.
+ */
+ public static <T> T futureGetOrElse(Future<T> future,
+ long timeout,
+ TimeUnit timeUnit,
+ T defaultValue) {
+ try {
+ return future.get(timeout, timeUnit);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return defaultValue;
+ } catch (ExecutionException | TimeoutException e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns a future that is completed exceptionally.
+ *
+ * @param t exception
+ * @param <T> future value type
+ * @return future
+ */
+ public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
+ CompletableFuture<T> future = new CompletableFuture<>();
+ future.completeExceptionally(t);
+ return future;
+ }
+
+ /**
+ * Returns the contents of {@code ByteBuffer} as byte array.
+ * <p>
+ * WARNING: There is a performance cost due to array copy
+ * when using this method.
+ *
+ * @param buffer byte buffer
+ * @return byte array containing the byte buffer contents
+ */
+ public static byte[] byteBuffertoArray(ByteBuffer buffer) {
+ int length = buffer.remaining();
+ if (buffer.hasArray()) {
+ int offset = buffer.arrayOffset() + buffer.position();
+ return Arrays.copyOfRange(buffer.array(), offset, offset + length);
+ }
+ byte[] bytes = new byte[length];
+ buffer.duplicate().get(bytes);
+ return bytes;
+ }
+
+ /**
+ * Converts an iterable to a stream.
+ *
+ * @param it iterable to convert
+ * @param <T> type if item
+ * @return iterable as a stream
+ */
+ public static <T> Stream<T> stream(Iterable<T> it) {
+ return StreamSupport.stream(it.spliterator(), false);
+ }
+
+ // Auxiliary path visitor for recursive directory structure copying.
+ private static class DirectoryCopier extends SimpleFileVisitor<Path> {
+ private Path src;
+ private Path dst;
+ private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
+
+ DirectoryCopier(String src, String dst) {
+ this.src = Paths.get(src);
+ this.dst = Paths.get(dst);
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ Path targetPath = dst.resolve(src.relativize(dir));
+ if (!Files.exists(targetPath)) {
+ Files.createDirectory(targetPath);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/TriConsumer.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/TriConsumer.java
new file mode 100644
index 00000000..d1963c46
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/TriConsumer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+/**
+ * A consumer that accepts three arguments.
+ *
+ * @param <U> type of first argument
+ * @param <V> type of second argument
+ * @param <W> type of third argument
+ */
+public interface TriConsumer<U, V, W> {
+
+ /**
+ * Applies the given arguments to the function.
+ * @param arg1 first argument
+ * @param arg2 second argument
+ * @param arg3 third argument
+ */
+ void accept(U arg1, V arg2, W arg3);
+
+} \ No newline at end of file
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/package-info.java
new file mode 100644
index 00000000..06cc394f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Miscellaneous domain-agnostic utilities.
+ */
+package org.onlab.util;