PIM Neighbors refactored to PIMInterfaces and now used
the NetworkConfiguration service

Change-Id: Ieb0a6faee3f3399f1bba5d9614fce6b0050e8510
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
index 0ef7e38..6bd563b 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
@@ -18,30 +18,30 @@
 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 org.onosproject.pim.impl.PIMInterface;
+import org.onosproject.pim.impl.PIMInterfaces;
+import org.onosproject.pim.impl.PIMInterfacesCodec;
 
-import java.util.HashMap;
+import java.util.Collection;
 
-@Command(scope = "onos", name = "pim-neighbors", description = "Displays the pim neighbors")
+@Command(scope = "onos", name = "pim-interfaces", description = "Displays the pim interfaces")
 public class PIMShowCommand extends AbstractShellCommand {
 
     // prints either the json or cli version of the hash map connect point
-    // neighbors from the PIMNeighbors class.
+    // neighbors from the PIMInterfaces class.
     @Override
     protected  void execute() {
         // grab connect point neighbors hash map to send in to json encoder.
-        HashMap<ConnectPoint, PIMNeighbors> pimNbrs = PIMNeighbors.getConnectPointNeighbors();
+        Collection<PIMInterface> pimIntfs = PIMInterfaces.getInstance().getInterfaces();
         if (outputJson()) {
-            print("%s", json(pimNbrs));
+            print("%s", json(pimIntfs));
         } else {
-            print(PIMNeighbors.printPimNeighbors());
+            print(PIMInterfaces.getInstance().printInterfaces());
         }
     }
 
-    private JsonNode json(HashMap<ConnectPoint, PIMNeighbors> pimNbrs) {
-        return new PIMNeighborsCodec().encode(pimNbrs, this);
+    private JsonNode json(Collection<PIMInterface> pimIntfs) {
+        return new PIMInterfacesCodec().encode(pimIntfs, this);
     }
 
 }
\ 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
index bd5e148..1a2d6f5 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
@@ -22,132 +22,61 @@
 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.incubator.net.intf.InterfaceService;
+import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.packet.PacketService;
 import org.slf4j.Logger;
 
 /**
- * Protocol Independent Multicast Emulation.
+ * Protocol Independent Multicast (PIM) Emulation.  This component is responsible
+ * for reference the services this PIM module is going to need, then initializing
+ * the corresponding utility classes.
  */
 @Component(immediate = true)
 public class PIMComponent {
     private final Logger log = getLogger(getClass());
 
+    // Register to receive PIM packets, used to send packets as well
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
+    // Get the appId
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
-    private PIMPacketProcessor processor = new PIMPacketProcessor();
+    // Get the network configuration updates
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    // Access defined network (IP) interfaces
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
     private static ApplicationId appId;
 
+    private PIMInterfaces pimInterfaces;
+    private PIMPacketHandler pimPacketHandler;
+
     @Activate
     public void activate() {
         appId = coreService.registerApplication("org.onosproject.pim");
 
-        packetService.addProcessor(processor, PacketProcessor.director(1));
+        // Initialize the Packet Handler class
+        pimPacketHandler = PIMPacketHandler.getInstance();
+        pimPacketHandler.initialize(packetService, appId);
 
-        // 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);
+        // Initialize the Interface class
+        pimInterfaces = PIMInterfaces.getInstance();
+        pimInterfaces.initialize(configService, interfaceService);
 
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        packetService.removeProcessor(processor);
-        processor = null;
+        PIMPacketHandler.getInstance().stop();
         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/PIMInterface.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java
new file mode 100644
index 0000000..28d1e8b
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java
@@ -0,0 +1,339 @@
+/*
+ * 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.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.onlab.packet.pim.PIMHelloOption;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * The PIM Interface is a wrapper around a ConnectPoint and used to provide
+ * hello options values when "talking" with PIM other PIM routers.
+ */
+public class PIMInterface {
+    private static Logger log = LoggerFactory.getLogger("PIMInterfaces");
+
+    // Interface from the interface subsystem
+    private Interface theInterface;
+
+    // The list of PIM neighbors adjacent to this interface
+    private Map<IpAddress, PIMNeighbor> neighbors = new HashMap<>();
+
+    // The designatedRouter for this LAN
+    private PIMNeighbor designatedRouter;
+
+    // The priority we use on this ConnectPoint.
+    private int priority = PIMHelloOption.DEFAULT_PRIORITY;
+
+    // The holdtime we are sending out.
+    private int holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
+
+    // Then generation ID we are sending out. 0 means we need to generate a new random ID
+    private int genid = PIMHelloOption.DEFAULT_GENID;
+
+    // Our default prune delay
+    private int prunedelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
+
+    /**
+     * Create a PIMInterface.
+     */
+    public PIMInterface(Interface intf) {
+
+        log.debug("Adding an interface: " + intf.toString() + "\n");
+        this.theInterface = intf;
+
+        // Send a hello to let our neighbors know we are alive
+        sendHello();
+    }
+
+    /**
+     * Get the PIM Interface.
+     *
+     * @return the PIM Interface
+     */
+    public Interface getInterface() {
+        return theInterface;
+    }
+
+    /**
+     * Getter for our IP address.
+     *
+     * @return our IP address.
+     */
+    public IpAddress getIpAddress() {
+        if (theInterface.ipAddresses().isEmpty()) {
+            return null;
+        }
+
+        // We will just assume the first interface on the list
+        IpAddress ipaddr = null;
+        for (InterfaceIpAddress ifipaddr : theInterface.ipAddresses()) {
+            ipaddr = ifipaddr.ipAddress();
+            break;
+        }
+        return ipaddr;
+    }
+
+    /**
+     * Get our priority.
+     *
+     * @return our priority.
+     */
+    public int getPriority() {
+        return this.priority;
+    }
+
+    /**
+     * 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 areWeDr() {
+        return (designatedRouter != null &&
+                designatedRouter.getPrimaryAddr().equals(this.getIpAddress()));
+    }
+
+    /**
+     * Return a collection of PIM Neighbors.
+     *
+     * @return the collection of PIM Neighbors
+     */
+    public Collection<PIMNeighbor> getNeighbors() {
+        return this.neighbors.values();
+    }
+
+    /**
+     * 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())) {
+
+            log.debug("We are adding a neighbor that already exists: {}", nbr.toString());
+            neighbors.remove(nbr.getPrimaryAddr());
+        }
+        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) {
+
+        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) {
+
+        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
+     * @return an existing or new PIM neighbor
+     */
+    public PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac) {
+        PIMNeighbor nbr = this.findNeighbor(ipaddr);
+        if (nbr == null) {
+            nbr = new PIMNeighbor(ipaddr, mac, this);
+            this.addNeighbor(nbr);
+            this.electDR(nbr);
+        }
+        return nbr;
+    }
+
+    /**
+     * Process a hello packet received on this Interface.
+     *
+     * @param ethPkt the ethernet packet containing the hello message
+     * @param cp the ConnectPoint of this interface
+     */
+    public void processHello(Ethernet ethPkt, ConnectPoint cp) {
+        checkNotNull(ethPkt);
+        checkNotNull(cp);
+
+        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 = this.findOrCreate(srcip, srcmac);
+        if (nbr == null) {
+            log.error("Could not create a neighbor for: {1}", srcip.toString());
+            return;
+        }
+
+        ConnectPoint icp = theInterface.connectPoint();
+        checkNotNull(icp);
+        if (!cp.equals(icp)) {
+            log.error("PIM Hello message received from {} on incorrect interface {}",
+                    nbr.getPrimaryAddr(), this.toString());
+            return;
+        }
+        nbr.refresh(hello);
+    }
+
+    /**
+     * Send a hello packet from this interface.
+     */
+    public void sendHello() {
+        PIM pim = new PIM();
+        PIMHello hello = new PIMHello();
+
+        // Create a PIM Hello
+        pim = new PIM();
+        pim.setVersion((byte) 2);
+        pim.setPIMType((byte) PIM.TYPE_HELLO);
+        pim.setChecksum((short) 0);
+
+        hello = new PIMHello();
+        hello.createDefaultOptions();
+        pim.setPayload(hello);
+        hello.setParent(pim);
+
+        log.debug("Sending hello: \n");
+        PIMPacketHandler.getInstance().sendPacket(pim, this);
+    }
+
+    /**
+     * prints the connectPointNeighbors list with each neighbor list.
+     *
+     * @return string of neighbors.
+     */
+    public String printNeighbors() {
+        String out = "PIM Neighbors Table: \n";
+        for (PIMNeighbor nbr : this.neighbors.values()) {
+            out += "\t" + nbr.toString();
+        }
+        return out;
+    }
+
+    @Override
+    public String toString() {
+        IpAddress ipaddr = this.getIpAddress();
+        String out = "PIM Neighbors: ";
+        if (ipaddr != null) {
+            out += "IP: " + ipaddr.toString();
+        } else {
+            out += "IP: *Null*";
+        }
+        out += "\tPR: " + String.valueOf(this.priority) + "\n";
+        return out;
+    }
+
+}
+
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaces.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaces.java
new file mode 100644
index 0000000..e33d5aa
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaces.java
@@ -0,0 +1,260 @@
+/*
+ * 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.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.incubator.net.config.basics.InterfaceConfig;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * PIMInterfaces 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 PIMInterfaces {
+
+    private Logger log = LoggerFactory.getLogger("PIMInterfaces");
+
+    private static PIMInterfaces instance = null;
+
+    // Used to listen to network configuration changes
+    private NetworkConfigService configService;
+
+    // Used to access IP Interface definitions for our segment
+    private InterfaceService interfaceService;
+
+    // Internal class used to listen for network configuration changes
+    private InternalConfigListener configListener = new InternalConfigListener();
+
+    // This is the global container for all PIM Interfaces indexed by ConnectPoints.
+    private Map<ConnectPoint, PIMInterface> interfaces = new HashMap<>();
+
+    // Default hello message interval
+    private int helloMessageInterval = 60;
+
+    // Timer used to send hello messages on this interface
+    private Timeout helloTimer;
+
+    // Required by a utility class
+    private PIMInterfaces() {}
+
+    /**
+     * Get the instance of PIMInterfaces.  Create the instance if needed.
+     *
+     * @return PIMInterface instance
+     */
+    public static PIMInterfaces getInstance() {
+        if (null == instance) {
+            instance = new PIMInterfaces();
+        }
+        return instance;
+    }
+
+    // Initialize the services
+    public void initialize(NetworkConfigService cs, InterfaceService is) {
+        configService = cs;
+        interfaceService = is;
+
+        // Initialize interfaces if they already exist
+        initInterfaces();
+
+        // Listen for network config changes
+        configService.addListener(configListener);
+    }
+
+    /**
+     * Listener for network config events.
+     */
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        private void updateInterfaces(InterfaceConfig config) {
+            Set<Interface> intfs;
+            try {
+                intfs = config.getInterfaces();
+            } catch (ConfigException e) {
+                log.error(e.toString());
+                return;
+            }
+            for (Interface intf : intfs) {
+                addInterface(intf);
+            }
+        }
+
+        /**
+         * Remove the PIMInterface represented by the ConnectPoint. If the
+         * PIMInterface does not exist this function is a no-op.
+         *
+         * @param cp The connectPoint representing the PIMInterface to be removed.
+         */
+        private void removeInterface(ConnectPoint cp) {
+            removeInterface(cp);
+        }
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    log.debug("Config updated: " + event.toString() + "\n");
+                    if (event.configClass() == InterfaceConfig.class) {
+                        InterfaceConfig config =
+                                configService.getConfig((ConnectPoint) event.subject(), InterfaceConfig.class);
+                        updateInterfaces(config);
+                    }
+                    break;
+                case CONFIG_REMOVED:
+                    if (event.configClass() == InterfaceConfig.class) {
+                        removeInterface((ConnectPoint) event.subject());
+                    }
+                    break;
+                case CONFIG_REGISTERED:
+                case CONFIG_UNREGISTERED:
+                default:
+                    break;
+            }
+        }
+    }
+
+    // Configure interfaces if they already exist.
+    private void initInterfaces() {
+        Set<Interface> intfs = interfaceService.getInterfaces();
+        for (Interface intf : intfs) {
+            log.debug("Adding interface: " + intf.toString() + "\n");
+            addInterface(intf);
+        }
+    }
+
+    /**
+     * Create a PIM Interface and add to our interfaces list.
+     *
+     * @param intf the interface to add
+     * @return the PIMInterface
+     */
+    public PIMInterface addInterface(Interface intf) {
+        PIMInterface pif = new PIMInterface(intf);
+        interfaces.put(intf.connectPoint(), pif);
+
+        // If we have added our first interface start the hello timer.
+        if (interfaces.size() == 1) {
+            startHelloTimer();
+        }
+
+        // Return this interface
+        return pif;
+    }
+
+    /**
+     * Remove the PIMInterface from the given ConnectPoint.
+     *
+     * @param cp the ConnectPoint indexing the PIMInterface to be removed.
+     */
+    public void removeInterface(ConnectPoint cp) {
+        if (interfaces.containsKey(cp)) {
+            interfaces.remove(cp);
+        }
+
+        if (interfaces.size() == 0) {
+            PIMTimer.stop();
+        }
+    }
+
+    /**
+     * Return a collection of PIMInterfaces for use by the PIM Interface codec.
+     *
+     * @return the collection of PIMInterfaces
+     */
+    public Collection<PIMInterface> getInterfaces() {
+        return interfaces.values();
+    }
+
+    /**
+     * Get the PIM Interface indexed by the given ConnectPoint.
+     *
+     * @param cp the connect point
+     * @return the PIMInterface if it exists, NULL if not
+     */
+    public PIMInterface getInterface(ConnectPoint cp) {
+        return interfaces.get(cp);
+    }
+
+    /**
+     * Return a string of PIMInterfaces for the cli command.
+     *
+     * @return a string representing PIM interfaces
+     */
+    public String printInterfaces() {
+        String str = "";
+        for (PIMInterface pi : interfaces.values()) {
+            str += pi.toString();
+        }
+        return str;
+    }
+
+    /* ---------------------------------- PIM Hello Timer ----------------------------------- */
+
+    /**
+     * Start a new hello timer for this interface.
+     */
+    private void startHelloTimer() {
+        helloTimer = PIMTimer.getTimer().newTimeout(
+                new HelloTimer(),
+                helloMessageInterval,
+                TimeUnit.SECONDS);
+
+        log.debug("Started Hello Timer");
+    }
+
+    /**
+     * This inner class handles transmitting a PIM hello message on this ConnectPoint.
+     */
+    private final class HelloTimer implements TimerTask {
+
+        HelloTimer() {
+        }
+
+        @Override
+        public void run(Timeout timeout) throws Exception {
+
+            log.debug("Running Hello Timer\n");
+            // Technically we should not send all hello's in synch..
+            for (PIMInterface pi : interfaces.values()) {
+                pi.sendHello();
+            }
+
+            // restart the hello timer
+            if (interfaces.size() > 0) {
+                startHelloTimer();
+            }
+        }
+    }
+}
\ 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/PIMInterfacesCodec.java
similarity index 86%
rename from apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
rename to apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfacesCodec.java
index ee62eb7..ddd7a59 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfacesCodec.java
@@ -19,16 +19,15 @@
 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 java.util.Collection;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * PIM neighbors Codec.
  */
-public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighbors>> {
+public class PIMInterfacesCodec extends JsonCodec<Collection<PIMInterface>> {
     // JSON field names
     //Return Name
     private static final String CPNBRLIST = "connect_point_list";
@@ -53,22 +52,22 @@
      * @return Encoded neighbors used by CLI and REST
      */
     @Override
-    public ObjectNode encode(HashMap<ConnectPoint, PIMNeighbors> cpn, CodecContext context) {
+    public ObjectNode encode(Collection<PIMInterface> 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()) {
+        for (PIMInterface pn: cpn) {
             // 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()));
+            cp.put(IP, pn.getIpAddress().toString());
+            cp.put(PRIORITY, String.valueOf(pn.getPriority()));
 
             // create the array for the neighbors list
             ArrayNode nbrsList = context.mapper().createArrayNode();
-            for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) {
+            for (PIMNeighbor nbr : pn.getNeighbors()) {
                 nbrsList.add(neighbor(nbr, context));
             }
             // adds pim neighbor to list
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
index 1a96138..73d1598 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
@@ -60,22 +60,20 @@
     // 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;
+    private PIMInterface pimInterface;
 
     /**
      * 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
+     * @param pimInterface The PIMInterface of this neighbor
      */
-    public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) {
+    public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, PIMInterface pimInterface) {
         this.macAddress = macaddr;
         this.primaryAddr = ipaddr;
-        this.connectPoint = cp;
+        this.pimInterface = pimInterface;
         this.resetTimeout();
     }
 
@@ -174,30 +172,12 @@
      *
      * @return the ConnectPoint
      */
-    public ConnectPoint getConnectPoint() {
-        return connectPoint;
+    public PIMInterface getPimInterface() {
+        return pimInterface;
     }
 
     /**
-     * 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.
+     * We have received a fresh hello from this neighbor, now we need to process it.
      * Depending on the values received in the the hello options may force a
      * re-election process.
      *
@@ -208,17 +188,19 @@
     public void refresh(PIMHello hello) {
         checkNotNull(hello);
 
+        boolean reelect = false;
         for (PIMHelloOption opt : hello.getOptions().values()) {
 
             int len = opt.getOptLength();
-            byte [] value = new byte[len];
-            ByteBuffer bb = ByteBuffer.wrap(value);
+            ByteBuffer bb = ByteBuffer.wrap(opt.getValue());
 
             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.
+
+                        // We have a newly rebooted neighbor, this is where we would
+                        // send them our joins.
                         this.genId = newid;
                     }
                     break;
@@ -228,7 +210,7 @@
                     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) {
+                        if (this.isDr || pimInterface.getDesignatedRouter().getPriority() < priority) {
                             reelect = true;
                         }
                         this.priority = newpri;
@@ -242,7 +224,6 @@
                         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;
@@ -261,7 +242,7 @@
         }
 
         if (reelect) {
-            this.neighbors.electDR(this);
+            pimInterface.electDR(this);
         }
 
         // Reset the next timeout timer
@@ -307,9 +288,8 @@
         @Override
         public void run(Timeout timeout) throws Exception {
 
-            // TODO: log.debug;
-            PIMNeighbors neighbors = nbr.neighbors;
-            neighbors.removeNeighbor(nbr.getPrimaryAddr());
+            log.debug("PIM Neighbor {} has timed out: ", nbr.toString());
+            nbr.pimInterface.removeNeighbor(nbr);
         }
     }
 
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
deleted file mode 100644
index cad9076..0000000
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
+++ /dev/null
@@ -1,395 +0,0 @@
-
-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/PIMPacketHandler.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMPacketHandler.java
new file mode 100644
index 0000000..c1ad2cf
--- /dev/null
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMPacketHandler.java
@@ -0,0 +1,229 @@
+/*
+ * 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.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.MacAddress;
+import org.onlab.packet.PIM;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+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;
+
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handing Incoming and outgoing PIM packets.
+ */
+public final class PIMPacketHandler {
+    private final Logger log = getLogger(getClass());
+
+    private static PIMPacketHandler instance = null;
+
+    private PacketService packetService;
+    private PIMPacketProcessor processor = new PIMPacketProcessor();
+    private MacAddress pimDestinationMac = MacAddress.valueOf("01:00:5E:00:00:0d");
+
+    // Utility class
+    private PIMPacketHandler() {}
+
+    public static PIMPacketHandler getInstance() {
+        if (null == instance) {
+            instance = new PIMPacketHandler();
+        }
+        return instance;
+    }
+
+    /**
+     * Initialize the packet handling service.
+     *
+     * @param ps the packetService
+     * @param appId our application ID
+     */
+    public void initialize(PacketService ps, ApplicationId appId) {
+        packetService = ps;
+
+        // 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);
+
+        packetService.addProcessor(processor, PacketProcessor.director(1));
+    }
+
+    /**
+     * Shutdown the packet handling service.
+     */
+    public void stop() {
+        packetService.removeProcessor(processor);
+        processor = null;
+    }
+
+    /**
+     * Packet processor responsible for handling IGMP packets.
+     */
+    public class PIMPacketProcessor implements PacketProcessor {
+        private final Logger log = getLogger(getClass());
+
+        @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:
+                    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;
+            }
+        }
+
+        /**
+         * 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
+         */
+        private void processHello(Ethernet ethPkt, ConnectPoint receivedFrom) {
+            checkNotNull(ethPkt);
+            checkNotNull(receivedFrom);
+
+            // It is a problem if we don't have the
+            PIMInterfaces pintfs = PIMInterfaces.getInstance();
+            PIMInterface intf = pintfs.getInterface(receivedFrom);
+            if (intf == null) {
+                log.error("We received a PIM message on an interface we were not supposed to");
+                return;
+            }
+            intf.processHello(ethPkt, receivedFrom);
+        }
+    }
+
+    // Create an ethernet header and serialize then send
+    public void sendPacket(PIM pim, PIMInterface pimIntf) {
+
+        Interface theInterface = pimIntf.getInterface();
+
+        // Create the ethernet packet
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(pimDestinationMac);
+        eth.setSourceMACAddress(theInterface.mac());
+        eth.setEtherType(Ethernet.TYPE_IPV4);
+        if (theInterface.vlan() != VlanId.NONE) {
+            eth.setVlanID(theInterface.vlan().toShort());
+        }
+
+        // Create the IP Packet
+        IPv4 ip = new IPv4();
+        ip.setVersion((byte) 4);
+        ip.setTtl((byte) 20);
+        ip.setProtocol(IPv4.PROTOCOL_PIM);
+        ip.setChecksum((short) 0);
+        ip.setSourceAddress(checkNotNull(pimIntf.getIpAddress()).getIp4Address().toInt());
+        ip.setDestinationAddress(PIM.PIM_ADDRESS.getIp4Address().toInt());
+        eth.setPayload(ip);
+        ip.setParent(eth);
+
+        // Now set pim
+        ip.setPayload(pim);
+        pim.setParent(ip);
+
+        ConnectPoint cp = theInterface.connectPoint();
+        checkNotNull(cp);
+
+        TrafficTreatment treat = DefaultTrafficTreatment.builder().setOutput(cp.port()).build();
+        ByteBuffer bb = ByteBuffer.wrap(eth.serialize());
+        OutboundPacket packet = new DefaultOutboundPacket(cp.deviceId(), treat, bb);
+        checkNotNull(packet);
+
+        packetService.emit(packet);
+    }
+}
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
index c131a53..c2a3303 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
@@ -17,6 +17,8 @@
 
 import org.jboss.netty.util.HashedWheelTimer;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * PIM Timer used for PIM Neighbors.
  */
@@ -50,4 +52,21 @@
             PIMTimer.timer = hwTimer;
         }
     }
+
+    public static void start() {
+        if (PIMTimer.timer == null) {
+            getTimer();
+        }
+        checkNotNull(timer);
+        timer.start();
+    }
+
+    public static void stop() {
+        if (PIMTimer.timer == null) {
+            // No need to stop
+            return;
+        }
+        checkNotNull(timer);
+        timer.stop();
+    }
 }