Rusty Eddy
Committed by Gerrit Code Review

PIM Neighbors refactored to PIMInterfaces and now used

the NetworkConfiguration service

Change-Id: Ieb0a6faee3f3399f1bba5d9614fce6b0050e8510
......@@ -18,30 +18,30 @@ 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 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
......
......@@ -22,132 +22,61 @@ 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.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;
}
}
}
}
......
/*
* 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;
}
}
/*
* 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
......@@ -19,16 +19,15 @@ 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 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 @@ public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighb
* @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
......
......@@ -60,22 +60,20 @@ public class PIMNeighbor {
// 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 @@ public class PIMNeighbor {
*
* @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;
public PIMInterface getPimInterface() {
return pimInterface;
}
/**
* 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 class PIMNeighbor {
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 @@ public class PIMNeighbor {
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 @@ public class PIMNeighbor {
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 @@ public class PIMNeighbor {
}
if (reelect) {
this.neighbors.electDR(this);
pimInterface.electDR(this);
}
// Reset the next timeout timer
......@@ -307,9 +288,8 @@ public class PIMNeighbor {
@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);
}
}
......
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
/*
* 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);
}
}
......@@ -17,6 +17,8 @@ package org.onosproject.pim.impl;
import org.jboss.netty.util.HashedWheelTimer;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* PIM Timer used for PIM Neighbors.
*/
......@@ -50,4 +52,21 @@ public final class PIMTimer {
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();
}
}
......