blob: 5da5c2b32bf03610fe36b8cefb40306212beb82d [file] [log] [blame]
/*
* 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.
*
* @param intf the network interface configuration
*/
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;
}
}