Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
index 5285418..246c39e 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
@@ -16,7 +16,7 @@
 public class ClusterLinksCommand extends ClustersListCommand {
 
     @Argument(index = 0, name = "id", description = "Cluster ID",
-              required = false, multiValued = false)
+              required = true, multiValued = false)
     String id = null;
 
     @Override
@@ -30,5 +30,4 @@
         return null;
     }
 
-
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
index 6c3952d..e62fb05 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
@@ -33,10 +33,10 @@
     }
 
     /**
-     * Returns a formated string representing the gien link.
+     * Returns a formatted string representing the given link.
      *
      * @param link infrastructure link
-     * @return formated link string
+     * @return formatted link string
      */
     public static String linkString(Link link) {
         return String.format(FMT, link.src().deviceId(), link.src().port(),
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/PathListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/PathListCommand.java
new file mode 100644
index 0000000..114d837
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/PathListCommand.java
@@ -0,0 +1,43 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.Path;
+
+import java.util.Set;
+
+import static org.onlab.onos.net.DeviceId.deviceId;
+
+/**
+ * Lists all shortest-paths paths between the specified source and
+ * destination devices.
+ */
+@Command(scope = "onos", name = "paths",
+         description = "Lists all shortest-paths paths between the specified source and destination devices")
+public class PathListCommand extends TopologyCommand {
+
+    private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s";
+
+    @Argument(index = 0, name = "src", description = "Source device ID",
+              required = true, multiValued = false)
+    String src = null;
+
+    @Argument(index = 0, name = "dst", description = "Destination device ID",
+              required = true, multiValued = false)
+    String dst = null;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        init();
+        Set<Path> paths = service.getPaths(topology, deviceId(src), deviceId(dst));
+        for (Path path : paths) {
+            print(pathString(path));
+        }
+        return null;
+    }
+
+    private String pathString(Path path) {
+        return path.toString();
+    }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
index a390025..6c141ae 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
@@ -12,6 +12,7 @@
          description = "Lists summary of the current topology")
 public class TopologyCommand extends AbstractShellCommand {
 
+    // TODO: format the time-stamp
     private static final String FMT =
             "time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
index d5fc5f2..93ec91d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
+++ b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
@@ -9,8 +9,17 @@
  */
 public final class PortNumber {
 
+    // TODO: revisit the max and the logical port value assignments
+
     private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
 
+    public static final PortNumber FLOOD = new PortNumber(-1);
+    public static final PortNumber IN_PORT = new PortNumber(-2);
+    public static final PortNumber TABLE = new PortNumber(-3);
+    public static final PortNumber NORMAL = new PortNumber(-4);
+    public static final PortNumber ALL = new PortNumber(-4);
+    public static final PortNumber LOCAL = new PortNumber(-5);
+
     private final long number;
 
     // Public creation is prohibited
@@ -39,6 +48,16 @@
     }
 
     /**
+     * Indicates whether or not this port number is a reserved logical one or
+     * whether it corresponds to a normal physical port of a device or NIC.
+     *
+     * @return true if logical port number
+     */
+    public boolean isLogical() {
+        return number < 0 || number > MAX_NUMBER;
+    }
+
+    /**
      * Returns the backing long value.
      *
      * @return port number as long
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultInboundPacket.java b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultInboundPacket.java
new file mode 100644
index 0000000..8c57adc
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultInboundPacket.java
@@ -0,0 +1,73 @@
+package org.onlab.onos.net.packet;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.packet.Ethernet;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default implementation of an immutable inbound packet.
+ */
+public class DefaultInboundPacket implements InboundPacket {
+
+    private final ConnectPoint receivedFrom;
+    private final Ethernet parsed;
+    private final ByteBuffer unparsed;
+
+    /**
+     * Creates an immutable inbound packet.
+     *
+     * @param receivedFrom connection point where received
+     * @param parsed       parsed ethernet frame
+     * @param unparsed     unparsed raw bytes
+     */
+    public DefaultInboundPacket(ConnectPoint receivedFrom, Ethernet parsed,
+                                ByteBuffer unparsed) {
+        this.receivedFrom = receivedFrom;
+        this.parsed = parsed;
+        this.unparsed = unparsed;
+    }
+
+    @Override
+    public ConnectPoint receivedFrom() {
+        return receivedFrom;
+    }
+
+    @Override
+    public Ethernet parsed() {
+        return parsed;
+    }
+
+    @Override
+    public ByteBuffer unparsed() {
+        // FIXME: figure out immutability here
+        return unparsed;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(receivedFrom, parsed, unparsed);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof InboundPacket) {
+            final DefaultInboundPacket other = (DefaultInboundPacket) obj;
+            return Objects.equals(this.receivedFrom, other.receivedFrom) &&
+                    Objects.equals(this.parsed, other.parsed) &&
+                    Objects.equals(this.unparsed, other.unparsed);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("receivedFrom", receivedFrom)
+                .add("parsed", parsed)
+                .toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultOutboundPacket.java b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultOutboundPacket.java
new file mode 100644
index 0000000..7a55f32
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultOutboundPacket.java
@@ -0,0 +1,55 @@
+package org.onlab.onos.net.packet;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import org.onlab.onos.net.DeviceId;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Default implementation of an immutable outbound packet.
+ */
+public class DefaultOutboundPacket implements OutboundPacket {
+    private final DeviceId sendThrough;
+    private final List<Treatment> treatments;
+    private final ByteBuffer data;
+
+    /**
+     * Creates an immutable outbound packet.
+     *
+     * @param sendThrough identifier through which to send the packet
+     * @param treatments  list of packet treatments
+     * @param data        raw packet data
+     */
+    public DefaultOutboundPacket(DeviceId sendThrough,
+                                 List<Treatment> treatments, ByteBuffer data) {
+        this.sendThrough = sendThrough;
+        this.treatments = ImmutableList.copyOf(treatments);
+        this.data = data;
+    }
+
+    @Override
+    public DeviceId sendThrough() {
+        return sendThrough;
+    }
+
+    @Override
+    public List<Treatment> treatments() {
+        return treatments;
+    }
+
+    @Override
+    public ByteBuffer data() {
+        // FIXME: figure out immutability here
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("sendThrough", sendThrough)
+                .add("treatments", treatments)
+                .toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/InboundPacket.java b/core/api/src/main/java/org/onlab/onos/net/packet/InboundPacket.java
new file mode 100644
index 0000000..2931c3e
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/InboundPacket.java
@@ -0,0 +1,35 @@
+package org.onlab.onos.net.packet;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.packet.Ethernet;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Represents a data packet intercepted from an infrastructure device.
+ */
+public interface InboundPacket {
+
+    /**
+     * Returns the device and port from where the packet was received.
+     *
+     * @return connection point where received
+     */
+    ConnectPoint receivedFrom();
+
+    /**
+     * Returns the parsed form of the packet.
+     *
+     * @return parsed Ethernet frame; null if the packet is not an Ethernet
+     * frame or one for which there is no parser
+     */
+    Ethernet parsed();
+
+    /**
+     * Unparsed packet data.
+     *
+     * @return raw packet bytes
+     */
+    ByteBuffer unparsed();
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/OutboundPacket.java b/core/api/src/main/java/org/onlab/onos/net/packet/OutboundPacket.java
new file mode 100644
index 0000000..5307f00
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/OutboundPacket.java
@@ -0,0 +1,36 @@
+package org.onlab.onos.net.packet;
+
+import org.onlab.onos.net.DeviceId;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Represents an outbound data packet that is to be emitted to network via
+ * an infrastructure device.
+ */
+public interface OutboundPacket {
+
+    /**
+     * Returns the identity of a device through which this packet should be
+     * sent.
+     *
+     * @return device identity
+     */
+    DeviceId sendThrough();
+
+    /**
+     * Returns list of treatments for the outbound packet.
+     *
+     * @return output treatment
+     */
+    List<Treatment> treatments();
+
+    /**
+     * Returns the raw data to be sent.
+     *
+     * @return data to emit
+     */
+    ByteBuffer data();
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java
new file mode 100644
index 0000000..a9af312
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketContext.java
@@ -0,0 +1,55 @@
+package org.onlab.onos.net.packet;
+
+/**
+ * Represents context for processing an inbound packet, and (optionally)
+ * emitting a corresponding outbound packet.
+ */
+public interface PacketContext {
+
+    /**
+     * Returns the time when the packet was received.
+     *
+     * @return the time in millis since start of epoch
+     */
+    long time();
+
+    /**
+     * Returns the inbound packet being processed.
+     *
+     * @return inbound packet
+     */
+    InboundPacket inPacket();
+
+    /**
+     * Returns the view of the outbound packet.
+     *
+     * @return outbound packet
+     */
+    OutboundPacket outPacket();
+
+    /**
+     * Appends a new treatment to be applied to the outbound packet.
+     *
+     * @param treatment output treatment
+     */
+    void appendTreatment(Treatment treatment);
+
+    /**
+     * Triggers the outbound packet to be sent.
+     */
+    void send();
+
+    /**
+     * Blocks the outbound packet from being sent from this point onward.
+     */
+    void block();
+
+    /**
+     * Indicates whether the packet has already been handled, i.e. sent or
+     * blocked.
+     *
+     * @return true if sent or blocked
+     */
+    boolean isHandled();
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketProcessor.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProcessor.java
new file mode 100644
index 0000000..a53a08c
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProcessor.java
@@ -0,0 +1,15 @@
+package org.onlab.onos.net.packet;
+
+/**
+ * Abstraction of an inbound packet processor.
+ */
+public interface PacketProcessor {
+
+    /**
+     * Processes the inbound packet as specified in the given context.
+     *
+     * @param context packet processing context
+     */
+    void process(PacketContext context);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketProvider.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProvider.java
new file mode 100644
index 0000000..14597ee
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProvider.java
@@ -0,0 +1,15 @@
+package org.onlab.onos.net.packet;
+
+/**
+ * Abstraction of a packet provider capable of emitting packets.
+ */
+public interface PacketProvider {
+
+    /**
+     * Emits the specified outbound packet onto the network.
+     *
+     * @param packet outbound packet
+     */
+    void emit(OutboundPacket packet);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketProviderService.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProviderService.java
new file mode 100644
index 0000000..1b58f3e
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketProviderService.java
@@ -0,0 +1,16 @@
+package org.onlab.onos.net.packet;
+
+/**
+ * Entity capable of processing inbound packets.
+ */
+public interface PacketProviderService {
+
+    /**
+     * Submits inbound packet context for processing. This processing will be
+     * done synchronously, i.e. run-to-completion.
+     *
+     * @param context inbound packet context
+     */
+    void processPacket(PacketContext context);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/PacketService.java b/core/api/src/main/java/org/onlab/onos/net/packet/PacketService.java
new file mode 100644
index 0000000..5debf1b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/PacketService.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.net.packet;
+
+/**
+ * Service for intercepting data plane packets and for emitting synthetic
+ * outbound packets.
+ */
+public interface PacketService {
+
+    // TODO: ponder better ordering scheme that does not require absolute numbers
+
+    /**
+     * Adds the specified processor to the list of packet processors.
+     * It will be added into the list in the order of priority. The higher
+     * numbers will be processing the packets after the lower numbers.
+     *
+     * @param processor processor to be added
+     * @param priority  priority in the reverse natural order
+     * @throws java.lang.IllegalArgumentException if a processor with the
+     *                                            given priority already exists
+     */
+    void addProcessor(PacketProcessor processor, long priority);
+
+    /**
+     * Removes the specified processor from the processing pipeline.
+     *
+     * @param processor packet processor
+     */
+    void removeProcessor(PacketProcessor processor);
+
+    /**
+     * Emits the specified outbound packet onto the network.
+     *
+     * @param packet outbound packet
+     */
+    void emit(OutboundPacket packet);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/Treatment.java b/core/api/src/main/java/org/onlab/onos/net/packet/Treatment.java
new file mode 100644
index 0000000..411ec38
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/Treatment.java
@@ -0,0 +1,21 @@
+package org.onlab.onos.net.packet;
+
+import org.onlab.onos.net.PortNumber;
+
+/**
+ * Abstraction of different kinds of treatment that can be applied to an
+ * outbound packet.
+ */
+public interface Treatment {
+
+    // TODO: implement these later: modifications, group
+    // TODO: elsewhere provide factory methods for some default treatments
+
+    /**
+     * Returns the port number where the packet should be emitted.
+     *
+     * @return output port number
+     */
+    PortNumber output();
+
+}