Adding mfwd, igmp and mfwd apps

Change-Id: Ie7187716db36b754e4cd687a8f9de004e27c7825

adding mfwd, pim, igmp apps

Change-Id: Iddd2dcee24dc905d5ff0efe1d1d798fc83a7c736
diff --git a/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java b/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
new file mode 100644
index 0000000..5effe45
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
@@ -0,0 +1,47 @@
+/*
+ * 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.onosproject.pim.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.pim.impl.PIMNeighbors;
+import org.onosproject.pim.impl.PIMNeighborsCodec;
+
+import java.util.HashMap;
+
+@Command(scope = "onos", name = "pim-show", description = "Displays the pim neighbors")
+public class PIMShowCommand extends AbstractShellCommand {
+
+    // prints either the json or cli version of the hash map connect point
+    // neighbors from the PIMNeighbors class.
+    @Override
+    protected  void execute() {
+        // grab connect point neighbors hash map to send in to json encoder.
+        HashMap<ConnectPoint, PIMNeighbors> pimNbrs = PIMNeighbors.getConnectPointNeighbors();
+        if (outputJson()) {
+            print("%s", json(pimNbrs));
+        } else {
+            print(PIMNeighbors.printPimNeighbors());
+        }
+    }
+
+    private JsonNode json(HashMap<ConnectPoint, PIMNeighbors> pimNbrs) {
+        return new PIMNeighborsCodec().encode(pimNbrs, this);
+    }
+
+}
\ No newline at end of file
diff --git a/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java b/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java
new file mode 100644
index 0000000..954dacb
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * PIM Multicast forwarding framework using intents.
+ */
+package org.onosproject.pim.cli;
\ No newline at end of file
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
new file mode 100644
index 0000000..bd5e148
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
@@ -0,0 +1,153 @@
+/*
+ * 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.onosproject.pim.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.PIM;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * Protocol Independent Multicast Emulation.
+ */
+@Component(immediate = true)
+public class PIMComponent {
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    private PIMPacketProcessor processor = new PIMPacketProcessor();
+    private static ApplicationId appId;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onosproject.pim");
+
+        packetService.addProcessor(processor, PacketProcessor.director(1));
+
+        // Build a traffic selector for all multicast traffic
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPProtocol(IPv4.PROTOCOL_PIM);
+        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("Stopped");
+    }
+
+    /**
+     * Packet processor responsible for handling IGMP packets.
+     */
+    private class PIMPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            if (pkt == null) {
+                return;
+            }
+
+            Ethernet ethPkt = pkt.parsed();
+            if (ethPkt == null) {
+                return;
+            }
+
+            /*
+             * IPv6 MLD packets are handled by ICMP6. We'll only deal
+             * with IPv4.
+             */
+            if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
+                return;
+            }
+
+            IPv4 ip = (IPv4) ethPkt.getPayload();
+            IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+            IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+            log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() +
+                    "\tingress port: " + context.inPacket().receivedFrom().toString());
+
+            if (ip.getProtocol() != IPv4.PROTOCOL_PIM) {
+                log.debug("PIM Picked up a non PIM packet: IP protocol: " + ip.getProtocol());
+                return;
+            }
+
+            // TODO: check incoming to be PIM.PIM_ADDRESS or "Our" address.
+            IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+            IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+            PIM pim = (PIM) ip.getPayload();
+            switch (pim.getPimMsgType()) {
+
+                case PIM.TYPE_HELLO:
+                    PIMNeighbors.processHello(ethPkt, context.inPacket().receivedFrom());
+                    break;
+
+                case PIM.TYPE_JOIN_PRUNE_REQUEST:
+                    // Create the function
+                    break;
+
+                case PIM.TYPE_ASSERT:
+                case PIM.TYPE_BOOTSTRAP:
+                case PIM.TYPE_CANDIDATE_RP_ADV:
+                case PIM.TYPE_GRAFT:
+                case PIM.TYPE_GRAFT_ACK:
+                case PIM.TYPE_REGISTER:
+                case PIM.TYPE_REGISTER_STOP:
+                    log.debug("Unsupported PIM message type: " + pim.getPimMsgType());
+                    break;
+
+                default:
+                    log.debug("Unkown PIM message type: " + pim.getPimMsgType());
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
new file mode 100644
index 0000000..1a96138
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in reliance 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.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.pim.PIMHello;
+import org.onlab.packet.pim.PIMHelloOption;
+import org.onosproject.net.ConnectPoint;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * PIMNeighbor represents all the PIM routers that have sent us
+ * hello messages, or that possibly have been statically configured.
+ */
+public class PIMNeighbor {
+    private final Logger log = getLogger(getClass());
+
+    // The primary address of this PIM neighbor
+    private IpAddress primaryAddr;
+
+    // The MacAddress of this neighbor
+    private MacAddress macAddress;
+
+    // The ConnectPoint this PIM neighbor is connected to.
+    private ConnectPoint connectPoint;
+
+    // Is this neighbor us?
+    private boolean isThisUs = false;
+
+    // The option values this neighbor has sent us.
+    private int priority = 0;
+    private int genId = 0;
+    private short holdtime = 0;
+
+    // Is this pim neighbor the DR?
+    private boolean isDr = false;
+
+    // Timeout for this neighbor
+    private volatile Timeout timeout;
+
+    private boolean reelect = false;
+
+    // A back pointer the neighbors list this neighbor belongs to.
+    private PIMNeighbors neighbors;
+
+    /**
+     * Construct this neighbor from the address and connect point.
+     *
+     * @param ipaddr IP Address of neighbor
+     * @param macaddr MAC Address of the neighbor
+     * @param cp The ConnectPoint of this neighbor
+     */
+    public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) {
+        this.macAddress = macaddr;
+        this.primaryAddr = ipaddr;
+        this.connectPoint = cp;
+        this.resetTimeout();
+    }
+
+    /**
+     * Get the primary address of this neighbor.
+     *
+     * @return the primary IP address.
+     */
+    public IpAddress getPrimaryAddr() {
+        return primaryAddr;
+    }
+
+    /**
+     * Set the primary address of this neighbor.
+     *
+     * @param primaryAddr the address we'll use when sending hello messages
+     */
+    public void setPrimaryAddr(IpAddress primaryAddr) {
+        this.primaryAddr = primaryAddr;
+    }
+
+    /**
+     * Get the priority this neighbor has advertised to us.
+     *
+     * @return the priority
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Set the priority for this neighbor.
+     *
+     * @param priority This neighbors priority.
+     */
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    /**
+     * Get the generation ID.
+     *
+     * @return the generation ID.
+     */
+    public int getGenId() {
+        return genId;
+    }
+
+    /**
+     * Set the generation ID.
+     *
+     * @param genId the generation ID.
+     */
+    public void setGenId(int genId) {
+        this.genId = genId;
+    }
+
+    /**
+     * Get the holdtime for this neighbor.
+     *
+     * @return the holdtime
+     */
+    public short getHoldtime() {
+        return holdtime;
+    }
+
+    /**
+     * Set the holdtime for this neighbor.
+     *
+     * @param holdtime the holdtime.
+     */
+    public void setholdtime(short holdtime) {
+        this.holdtime = holdtime;
+    }
+
+    /**
+     * Is this neighbor the designated router on this connect point?
+     *
+     * @return true if so, false if not.
+     */
+    public boolean isDr() {
+        return isDr;
+    }
+
+    /**
+     * Set this router as the designated router on this connect point.
+     *
+     * @param isDr True is this neighbor is the DR false otherwise
+     */
+    public void setIsDr(boolean isDr) {
+        this.isDr = isDr;
+    }
+
+    /**
+     * The ConnectPoint this neighbor is connected to.
+     *
+     * @return the ConnectPoint
+     */
+    public ConnectPoint getConnectPoint() {
+        return connectPoint;
+    }
+
+    /**
+     * Set the ConnectPoint this router is connected to.
+     *
+     * @param connectPoint the ConnectPoint this router is connected to.
+     */
+    public void setConnectPoint(ConnectPoint connectPoint) {
+        this.connectPoint = connectPoint;
+    }
+
+    /**
+     * Set a back pointer to the neighbors list this neighbor is a member of.
+     *
+     * @param neighbors the neighbor list this neighbor belongs to
+     */
+    public void setNeighbors(PIMNeighbors neighbors) {
+        this.neighbors = neighbors;
+    }
+
+    /**
+     * We have received a fresh hello from a neighbor, now we need to process it.
+     * Depending on the values received in the the hello options may force a
+     * re-election process.
+     *
+     * We will also refresh the timeout for this neighbor.
+     *
+     * @param hello copy of the hello we'll be able to extract options from.
+     */
+    public void refresh(PIMHello hello) {
+        checkNotNull(hello);
+
+        for (PIMHelloOption opt : hello.getOptions().values()) {
+
+            int len = opt.getOptLength();
+            byte [] value = new byte[len];
+            ByteBuffer bb = ByteBuffer.wrap(value);
+
+            switch (opt.getOptType()) {
+                case PIMHelloOption.OPT_GENID:
+                    int newid = bb.getInt();
+                    if (this.genId != newid) {
+                        // TODO: we have a newly rebooted neighbor.  Send them our joins.
+                        this.genId = newid;
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_PRIORITY:
+                    int newpri = bb.getInt();
+                    if (this.priority != newpri) {
+
+                        // The priorities have changed.  We may need to re-elect a new DR?
+                        if (this.isDr || this.neighbors.getDesignatedRouter().getPriority() < priority) {
+                            reelect = true;
+                        }
+                        this.priority = newpri;
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_HOLDTIME:
+                    short holdtime = bb.getShort();
+                    if (this.holdtime != holdtime) {
+                        this.holdtime = holdtime;
+                        if (holdtime == 0) {
+                            // We have a neighbor going down.  We can remove all joins
+                            // we have learned from them.
+                            // TODO: What else do we need to do when a neighbor goes down?
+
+                            log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString());
+                            return;
+                        }
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_PRUNEDELAY:
+                case PIMHelloOption.OPT_ADDRLIST:
+                    // TODO: implement prune delay and addr list.  Fall through for now.
+
+                default:
+                    log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType());
+                    break;
+            }
+        }
+
+        if (reelect) {
+            this.neighbors.electDR(this);
+        }
+
+        // Reset the next timeout timer
+        this.resetTimeout();
+    }
+
+    /* --------------------------------------- Timer functions -------------------------- */
+
+    /**
+     * Restart the timeout task for this neighbor.
+     */
+    private void resetTimeout() {
+
+        if (this.holdtime == 0) {
+
+            // Prepare to die.
+            log.debug("shutting down timer for nbr {}", this.primaryAddr.toString());
+            if (this.timeout != null) {
+                this.timeout.cancel();
+                this.timeout = null;
+            }
+            return;
+        }
+
+        // Cancel the existing timeout and start a fresh new one.
+        if (this.timeout != null) {
+            this.timeout.cancel();
+        }
+
+        this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS);
+    }
+
+    /**
+     * The task to run when a neighbor timeout expires.
+     */
+    private final class NeighborTimeoutTask implements TimerTask {
+        PIMNeighbor nbr;
+
+        NeighborTimeoutTask(PIMNeighbor nbr) {
+            this.nbr = nbr;
+        }
+
+        @Override
+        public void run(Timeout timeout) throws Exception {
+
+            // TODO: log.debug;
+            PIMNeighbors neighbors = nbr.neighbors;
+            neighbors.removeNeighbor(nbr.getPrimaryAddr());
+        }
+    }
+
+    /**
+     * Stop the timeout timer.
+     *
+     * This happens when we remove the neighbor.
+     */
+    private final void stopTimeout() {
+        this.timeout.cancel();
+        this.timeout = null;
+    }
+
+    @Override
+    public String toString() {
+        String out = "";
+        if (this.isDr) {
+            out += "*NBR:";
+        } else {
+            out += "NBR:";
+        }
+        out += "\tIP: " + this.primaryAddr.toString();
+        out += "\tPr: " + String.valueOf(this.priority);
+        out += "\tHoldTime: " + String.valueOf(this.holdtime);
+        out += "\tGenID: " + String.valueOf(this.genId) + "\n";
+        return out;
+    }
+}
\ No newline at end of file
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
new file mode 100644
index 0000000..cad9076
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
@@ -0,0 +1,395 @@
+
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.PIM;
+import org.onlab.packet.pim.PIMHello;
+import org.onosproject.net.ConnectPoint;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIMNeighbors is a collection of all neighbors we have received
+ * PIM hello messages from.  The main structure is a HashMap indexed
+ * by ConnectPoint with another HashMap indexed on the PIM neighbors
+ * IPAddress, it contains all PIM neighbors attached on that ConnectPoint.
+ */
+public final class PIMNeighbors {
+
+    private static Logger log = LoggerFactory.getLogger("PIMNeighbors");
+
+    /**
+     * This is the global container for all PIM neighbors indexed by ConnectPoints.
+     *
+     * NOTE: We'll have a problem if the same neighbor can show up on two interfaces
+     * but that should never happen.
+     */
+    private static HashMap<ConnectPoint, PIMNeighbors> connectPointNeighbors = new HashMap<>();
+
+    // The connect point these neighbors are connected to.
+    private ConnectPoint connectPoint;
+
+    // Pointer to the current designated router on this ConnectPoint.
+    private PIMNeighbor designatedRouter;
+
+    // The list of neighbors we have learned on this ConnectPoint.
+    private HashMap<IpAddress, PIMNeighbor> neighbors = new HashMap<>();
+
+    /*
+     * TODO: turn ourIpAddress, ourPriority and OurHoldTime into config options.
+     */
+    // The IP address we are using to source our PIM hello messages on this connect Point.
+    private IpAddress ourIpAddress;
+
+    // The priority we use on this ConnectPoint.
+    private int ourPriority = 1;
+
+    // The holdtime we are sending out.
+    private int ourHoldtime = 105;
+
+    // Then generation ID we are sending out. 0 means we need to generate a new random ID
+    private int ourGenid = 0;
+
+    // Hello Timer for sending hello messages per ConnectPoint with neighbors.
+    private volatile Timeout helloTimer;
+
+    // The period of which we will be sending out PIM hello messages.
+    private final int defaultPimHelloInterval = 30; // seconds
+
+    /**
+     * Create PIMNeighbors object per ConnectPoint.
+     *
+     * @param cp the ConnectPoint.
+     * @return PIMNeighbors structure
+     */
+    public static PIMNeighbors getConnectPointNeighbors(ConnectPoint cp) {
+        return connectPointNeighbors.get(cp);
+    }
+
+    /**
+     * Process incoming hello message, we will need the Macaddress and IP address of the sender.
+     *
+     * @param ethPkt the ethernet header
+     * @param receivedFrom the connect point we recieved this message from
+     */
+    public static void processHello(Ethernet ethPkt, ConnectPoint receivedFrom) {
+        checkNotNull(ethPkt);
+        checkNotNull(ethPkt);
+
+        MacAddress srcmac = ethPkt.getSourceMAC();
+        IPv4 ip = (IPv4) ethPkt.getPayload();
+        Ip4Address srcip = Ip4Address.valueOf(ip.getSourceAddress());
+
+        PIM pim = (PIM) ip.getPayload();
+        checkNotNull(pim);
+
+        PIMHello hello = (PIMHello) pim.getPayload();
+        checkNotNull(hello);
+
+        PIMNeighbor nbr = PIMNeighbors.findOrCreate(srcip, srcmac, receivedFrom);
+        if (nbr == null) {
+            log.error("Could not create a neighbor for: {1}", srcip.toString());
+            return;
+        }
+
+        nbr.setConnectPoint(receivedFrom);
+        nbr.refresh(hello);
+    }
+
+    /**
+     * Create a PIM Neighbor.
+     *
+     * @param cp The ConnectPoint this neighbor was found on
+     */
+    public PIMNeighbors(ConnectPoint cp) {
+        this.connectPoint = cp;
+
+        // TODO: use network config to assign address.
+        this.ourIpAddress = IpAddress.valueOf("10.2.2.2");
+        this.addIpAddress(this.ourIpAddress);
+    }
+
+    /**
+     * Create a PIM neighbor.
+     *
+     * @param cp the ConnectPoint this neighbor was found on
+     * @param ourIp the IP address of this neighbor
+     */
+    public PIMNeighbors(ConnectPoint cp, IpAddress ourIp) {
+        this.connectPoint = cp;
+        this.addIpAddress(ourIp);
+    }
+
+    /**
+     * Start the hello timer when we have been given an IP address.
+     *
+     * @param ourIp our IP address.
+     */
+    public void addIpAddress(IpAddress ourIp) {
+        this.startHelloTimer();
+
+        // Kick off the first pim hello packet
+        this.sendHelloPacket();
+    }
+
+    /**
+     * Getter for our IP address.
+     *
+     * @return our IP address.
+     */
+    public IpAddress getOurIpAddress() {
+        return this.ourIpAddress;
+    }
+
+    /**
+     * Get our priority.
+     *
+     * @return our priority.
+     */
+    public int getOurPriority() {
+        return this.ourPriority;
+    }
+
+    /**
+     * Get the neighbor list for this specific connectPoint.
+     *
+     * @return PIM neighbors on this ConnectPoint
+     */
+    public HashMap<IpAddress, PIMNeighbor> getOurNeighborsList() {
+        return this.neighbors;
+    }
+
+    /**
+     * Get the designated router on this connection.
+     *
+     * @return the PIMNeighbor representing the DR
+     */
+    public PIMNeighbor getDesignatedRouter() {
+        return designatedRouter;
+    }
+
+    /**
+     * Are we the DR on this CP?
+     *
+     * @return true if we are, false if not
+     */
+    public boolean weAreTheDr() {
+        return (designatedRouter != null &&
+                designatedRouter.getPrimaryAddr().equals(ourIpAddress));
+    }
+
+    /**
+     * Find the neighbor with the given IP address on this CP.
+     *
+     * @param ipaddr the IP address of the neighbor we are interested in
+     * @return the pim neighbor if it exists
+     */
+    public PIMNeighbor findNeighbor(IpAddress ipaddr) {
+        PIMNeighbor nbr = neighbors.get(ipaddr);
+        return nbr;
+    }
+
+    /**
+     * Add a new PIM neighbor to this list.
+     *
+     * @param nbr the neighbor to be added.
+     */
+    public void addNeighbor(PIMNeighbor nbr) {
+        if (neighbors.containsKey(nbr.getPrimaryAddr())) {
+
+            // TODO: Hmmm, how should this be handled?
+            log.debug("We are adding a neighbor that already exists: {}", nbr.toString());
+            neighbors.remove(nbr.getPrimaryAddr(), nbr);
+        }
+        nbr.setNeighbors(this);
+        neighbors.put(nbr.getPrimaryAddr(), nbr);
+    }
+
+    /**
+     * Remove the neighbor from our neighbor list.
+     *
+     * @param ipaddr the IP address of the neighbor to remove
+     */
+    public void removeNeighbor(IpAddress ipaddr) {
+
+        boolean reelect = (designatedRouter == null || designatedRouter.getPrimaryAddr().equals(ipaddr));
+        if (neighbors.containsKey(ipaddr)) {
+            neighbors.remove(ipaddr);
+        }
+        this.electDR();
+    }
+
+    /**
+     * Remove the given neighbor from the neighbor list.
+     *
+     * @param nbr the nbr to be removed.
+     */
+    public void removeNeighbor(PIMNeighbor nbr) {
+
+        boolean reelect = (designatedRouter == null || nbr.isDr());
+        neighbors.remove(nbr.getPrimaryAddr(), nbr);
+        this.electDR();
+    }
+
+    /**
+     * Elect a new DR on this ConnectPoint.
+     *
+     * @return the PIM Neighbor that wins
+     */
+    public PIMNeighbor electDR() {
+
+        for (PIMNeighbor nbr : this.neighbors.values()) {
+            if (this.designatedRouter == null) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+
+            if (nbr.getPriority() > this.designatedRouter.getPriority()) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+
+            // We could sort in ascending order
+            if (this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+        }
+
+        return this.designatedRouter;
+    }
+
+    /**
+     * Elect a new DR given the new neighbor.
+     *
+     * @param nbr the new neighbor to use in DR election.
+     * @return the PIM Neighbor that wins DR election
+     */
+    public PIMNeighbor electDR(PIMNeighbor nbr) {
+
+        // Make sure I have
+        if (this.designatedRouter == null ||
+                this.designatedRouter.getPriority() < nbr.getPriority() ||
+                this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+            this.designatedRouter = nbr;
+        }
+        return this.designatedRouter;
+    }
+
+    /**
+     * Find or create a pim neighbor with a given ip address and connect point.
+     *
+     * @param ipaddr of the pim neighbor
+     * @param mac The mac address of our sending neighbor
+     * @param cp the connect point the neighbor was learned from
+     * @return an existing or new PIM neighbor
+     */
+    public static PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac, ConnectPoint cp) {
+        PIMNeighbors neighbors = connectPointNeighbors.get(cp);
+        if (neighbors == null) {
+            neighbors = new PIMNeighbors(cp);
+            connectPointNeighbors.put(cp, neighbors);
+        }
+
+        PIMNeighbor nbr = neighbors.findNeighbor(ipaddr);
+        if (nbr == null) {
+            nbr = new PIMNeighbor(ipaddr, mac, cp);
+            neighbors.addNeighbor(nbr);
+            neighbors.electDR(nbr);
+        }
+        return nbr;
+    }
+
+    // Returns the connect point neighbors hash map
+    public static HashMap<ConnectPoint, PIMNeighbors> getConnectPointNeighbors() {
+        return connectPointNeighbors;
+    }
+
+    /* ---------------------------------- PIM Hello Timer ----------------------------------- */
+
+    /**
+     * Start a new hello timer for this ConnectPoint.
+     */
+    private void startHelloTimer() {
+        this.helloTimer = PIMTimer.getTimer().newTimeout(
+                new HelloTimer(this),
+                this.defaultPimHelloInterval,
+                TimeUnit.SECONDS);
+
+        log.trace("Started Hello Timer: " + this.ourIpAddress.toString());
+    }
+
+    /**
+     * This inner class handles transmitting a PIM hello message on this ConnectPoint.
+     */
+    private final class HelloTimer implements TimerTask {
+        PIMNeighbors neighbors;
+
+        HelloTimer(PIMNeighbors neighbors) {
+            this.neighbors = neighbors;
+        }
+
+        @Override
+        public void run(Timeout timeout) throws Exception {
+
+            // Send off a hello packet
+            sendHelloPacket();
+
+            // restart the hello timer
+            neighbors.startHelloTimer();
+        }
+    }
+
+    private void sendHelloPacket() {
+        PIMHello hello = new PIMHello();
+
+        // TODO: we will need to implement the network config service to assign ip addresses & options
+        /*
+        hello.createDefaultOptions();
+
+        Ethernet eth = hello.createPIMHello(this.ourIpAddress);
+        hello.sendPacket(this.connectPoint);
+        */
+    }
+
+    /**
+     * prints the connectPointNeighbors list with each neighbor list.
+     *
+     * @return string of neighbors.
+     */
+    public static String printPimNeighbors() {
+        String out = "PIM Neighbors Table: \n";
+
+        for (PIMNeighbors pn: connectPointNeighbors.values()) {
+
+            out += "CP:\n " + pn.toString();
+            for (PIMNeighbor nbr : pn.neighbors.values()) {
+                out += "\t" + nbr.toString();
+            }
+        }
+        return out;
+    }
+
+    @Override
+    public String toString() {
+        String out = "PIM Neighbors: ";
+        if (this.ourIpAddress != null) {
+            out += "IP: " + this.ourIpAddress.toString();
+        } else {
+            out += "IP: *Null*";
+        }
+        out += "\tPR: " + String.valueOf(this.ourPriority) + "\n";
+        return out;
+    }
+}
\ No newline at end of file
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
new file mode 100644
index 0000000..ee62eb7
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.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.onosproject.pim.impl;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.HashMap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIM neighbors Codec.
+ */
+public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighbors>> {
+    // JSON field names
+    //Return Name
+    private static final String CPNBRLIST = "connect_point_list";
+
+    // PIM Neightbors Fields
+    private static final String IP = "ip";
+    private static final String PRIORITY = "priority";
+    private static final String NBRLIST = "neighbor_list";
+
+    // PIM neighbor Files
+    private static final String DR = "designated";
+    private static final String NBR_IP = "ip";
+    private static final String PR = "priority";
+    private static final String HOLDTIME = "hold_time";
+
+    /**
+     * Encode the PIM Neighbors.
+     *
+     * @param cpn ConnectPoint neighbors
+     * @param context encoding context
+     *
+     * @return Encoded neighbors used by CLI and REST
+     */
+    @Override
+    public ObjectNode encode(HashMap<ConnectPoint, PIMNeighbors> cpn, CodecContext context) {
+        checkNotNull(cpn, "Pim Neighbors cannot be null");
+
+        ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode();
+        ArrayNode cpnList = context.mapper().createArrayNode();
+
+        for (PIMNeighbors pn: cpn.values()) {
+            // get the PimNeighbors Obj, contains Neighbors list
+            // create the json object for a single Entry in the Neighbors list
+            ObjectNode cp = context.mapper().createObjectNode();
+            cp.put(IP, pn.getOurIpAddress().toString());
+            cp.put(PRIORITY, String.valueOf(pn.getOurPriority()));
+
+            // create the array for the neighbors list
+            ArrayNode nbrsList = context.mapper().createArrayNode();
+            for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) {
+                nbrsList.add(neighbor(nbr, context));
+            }
+            // adds pim neighbor to list
+            cp.set(NBRLIST, nbrsList);
+            // adds to arraynode which will represent the connect point neighbors hash map.
+            cpnList.add(cp);
+        }
+        pimNbrJsonCodec.set(CPNBRLIST, cpnList);
+        return pimNbrJsonCodec;
+    }
+
+    /**
+     * Encode a single PIM Neighbor.
+     *
+     * @param nbr the neighbor to be encoded
+     * @param context encoding context
+     * @return the encoded neighbor
+     */
+    private ObjectNode neighbor(PIMNeighbor nbr, CodecContext context) {
+        return context.mapper().createObjectNode()
+                .put(DR, Boolean.toString(nbr.isDr()))
+                .put(NBR_IP, nbr.getPrimaryAddr().toString())
+                .put(PR, String.valueOf(nbr.getPriority()))
+                .put(HOLDTIME, String.valueOf(nbr.getHoldtime()));
+    }
+}
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
new file mode 100644
index 0000000..c131a53
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.onosproject.pim.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+
+/**
+ * PIM Timer used for PIM Neighbors.
+ */
+public final class PIMTimer {
+
+    private static volatile HashedWheelTimer timer;
+
+    // Ban public construction
+    private PIMTimer() {
+    }
+
+    /**
+     * Returns the singleton hashed-wheel timer.
+     *
+     * @return hashed-wheel timer
+     */
+    public static HashedWheelTimer getTimer() {
+        if (PIMTimer.timer == null) {
+            initTimer();
+        }
+        return PIMTimer.timer;
+    }
+
+    // Start the PIM timer.
+    private static synchronized  void initTimer() {
+        if (PIMTimer.timer == null) {
+
+            // Create and start a new hashed wheel timer, if it does not exist.
+            HashedWheelTimer hwTimer = new HashedWheelTimer();
+            hwTimer.start();
+            PIMTimer.timer = hwTimer;
+        }
+    }
+}
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java b/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java
new file mode 100644
index 0000000..29d1ce4
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * PIM Emulation speak hello messages and listen to Join/Prunes.
+ */
+package org.onosproject.pim.impl;