blob: cad907689977366b498bddecc9576012a23fe28c [file] [log] [blame]
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;
}
}