From 6a07d2d622eaa06953f3353e39c080984076e8de Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Fri, 9 Oct 2015 18:32:44 -0700 Subject: Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986 Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f --- framework/src/onos/apps/pim/pom.xml | 122 +++++++ .../org/onosproject/pim/cli/PIMShowCommand.java | 47 +++ .../java/org/onosproject/pim/cli/package-info.java | 4 + .../org/onosproject/pim/impl/PIMComponent.java | 153 ++++++++ .../java/org/onosproject/pim/impl/PIMNeighbor.java | 340 ++++++++++++++++++ .../org/onosproject/pim/impl/PIMNeighbors.java | 395 +++++++++++++++++++++ .../onosproject/pim/impl/PIMNeighborsCodec.java | 97 +++++ .../java/org/onosproject/pim/impl/PIMTimer.java | 53 +++ .../org/onosproject/pim/impl/package-info.java | 20 ++ .../resources/OSGI-INF.blueprint/shell-config.xml | 24 ++ 10 files changed, 1255 insertions(+) create mode 100644 framework/src/onos/apps/pim/pom.xml create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java create mode 100644 framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml (limited to 'framework/src/onos/apps/pim') diff --git a/framework/src/onos/apps/pim/pom.xml b/framework/src/onos/apps/pim/pom.xml new file mode 100644 index 00000000..83a366b6 --- /dev/null +++ b/framework/src/onos/apps/pim/pom.xml @@ -0,0 +1,122 @@ + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-pim + bundle + + Protocol Independent Multicast Emulation + + + org.onosproject.pim + + + + + org.onosproject + onos-api + ${project.version} + + + + org.onosproject + onos-cli + ${project.version} + + + + + org.onosproject + onlab-osgi + ${project.version} + + + + org.onosproject + onlab-junit + test + + + + + org.osgi + org.osgi.compendium + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + + org.apache.felix + org.apache.felix.scr.annotations + 1.9.8 + provided + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.groupId}.${project.artifactId} + + + org.slf4j, + org.osgi.framework, + org.apache.commons.lang.math.*, + com.google.common.*, + org.apache.karaf.shell.commands, + org.apache.karaf.shell.console, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onosproject.mfwd.impl.*; + org.onlab.util.*, + org.jboss.netty.util.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.8 + 1.8 + + + + + diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java new file mode 100644 index 00000000..0ef7e389 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.cli; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.ConnectPoint; +import org.onosproject.pim.impl.PIMNeighbors; +import org.onosproject.pim.impl.PIMNeighborsCodec; + +import java.util.HashMap; + +@Command(scope = "onos", name = "pim-neighbors", description = "Displays the pim neighbors") +public class PIMShowCommand extends AbstractShellCommand { + + // prints either the json or cli version of the hash map connect point + // neighbors from the PIMNeighbors class. + @Override + protected void execute() { + // grab connect point neighbors hash map to send in to json encoder. + HashMap pimNbrs = PIMNeighbors.getConnectPointNeighbors(); + if (outputJson()) { + print("%s", json(pimNbrs)); + } else { + print(PIMNeighbors.printPimNeighbors()); + } + } + + private JsonNode json(HashMap pimNbrs) { + return new PIMNeighborsCodec().encode(pimNbrs, this); + } + +} \ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java new file mode 100644 index 00000000..954dacbb --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java @@ -0,0 +1,4 @@ +/** + * PIM Multicast forwarding framework using intents. + */ +package org.onosproject.pim.cli; \ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java new file mode 100644 index 00000000..bd5e1486 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java @@ -0,0 +1,153 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.PIM; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.slf4j.Logger; + +/** + * Protocol Independent Multicast Emulation. + */ +@Component(immediate = true) +public class PIMComponent { + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + private PIMPacketProcessor processor = new PIMPacketProcessor(); + private static ApplicationId appId; + + @Activate + public void activate() { + appId = coreService.registerApplication("org.onosproject.pim"); + + packetService.addProcessor(processor, PacketProcessor.director(1)); + + // Build a traffic selector for all multicast traffic + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPProtocol(IPv4.PROTOCOL_PIM); + packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + packetService.removeProcessor(processor); + processor = null; + log.info("Stopped"); + } + + /** + * Packet processor responsible for handling IGMP packets. + */ + private class PIMPacketProcessor implements PacketProcessor { + + @Override + public void process(PacketContext context) { + // Stop processing if the packet has been handled, since we + // can't do any more to it. + if (context.isHandled()) { + return; + } + + InboundPacket pkt = context.inPacket(); + if (pkt == null) { + return; + } + + Ethernet ethPkt = pkt.parsed(); + if (ethPkt == null) { + return; + } + + /* + * IPv6 MLD packets are handled by ICMP6. We'll only deal + * with IPv4. + */ + if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) { + return; + } + + IPv4 ip = (IPv4) ethPkt.getPayload(); + IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress()); + IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress()); + log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() + + "\tingress port: " + context.inPacket().receivedFrom().toString()); + + if (ip.getProtocol() != IPv4.PROTOCOL_PIM) { + log.debug("PIM Picked up a non PIM packet: IP protocol: " + ip.getProtocol()); + return; + } + + // TODO: check incoming to be PIM.PIM_ADDRESS or "Our" address. + IpPrefix spfx = IpPrefix.valueOf(saddr, 32); + IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32); + + PIM pim = (PIM) ip.getPayload(); + switch (pim.getPimMsgType()) { + + case PIM.TYPE_HELLO: + PIMNeighbors.processHello(ethPkt, context.inPacket().receivedFrom()); + break; + + case PIM.TYPE_JOIN_PRUNE_REQUEST: + // Create the function + break; + + case PIM.TYPE_ASSERT: + case PIM.TYPE_BOOTSTRAP: + case PIM.TYPE_CANDIDATE_RP_ADV: + case PIM.TYPE_GRAFT: + case PIM.TYPE_GRAFT_ACK: + case PIM.TYPE_REGISTER: + case PIM.TYPE_REGISTER_STOP: + log.debug("Unsupported PIM message type: " + pim.getPimMsgType()); + break; + + default: + log.debug("Unkown PIM message type: " + pim.getPimMsgType()); + break; + } + } + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java new file mode 100644 index 00000000..1a96138f --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java @@ -0,0 +1,340 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in reliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.pim.PIMHello; +import org.onlab.packet.pim.PIMHelloOption; +import org.onosproject.net.ConnectPoint; +import org.slf4j.Logger; + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * PIMNeighbor represents all the PIM routers that have sent us + * hello messages, or that possibly have been statically configured. + */ +public class PIMNeighbor { + private final Logger log = getLogger(getClass()); + + // The primary address of this PIM neighbor + private IpAddress primaryAddr; + + // The MacAddress of this neighbor + private MacAddress macAddress; + + // The ConnectPoint this PIM neighbor is connected to. + private ConnectPoint connectPoint; + + // Is this neighbor us? + private boolean isThisUs = false; + + // The option values this neighbor has sent us. + private int priority = 0; + private int genId = 0; + private short holdtime = 0; + + // Is this pim neighbor the DR? + private boolean isDr = false; + + // Timeout for this neighbor + private volatile Timeout timeout; + + private boolean reelect = false; + + // A back pointer the neighbors list this neighbor belongs to. + private PIMNeighbors neighbors; + + /** + * Construct this neighbor from the address and connect point. + * + * @param ipaddr IP Address of neighbor + * @param macaddr MAC Address of the neighbor + * @param cp The ConnectPoint of this neighbor + */ + public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) { + this.macAddress = macaddr; + this.primaryAddr = ipaddr; + this.connectPoint = cp; + this.resetTimeout(); + } + + /** + * Get the primary address of this neighbor. + * + * @return the primary IP address. + */ + public IpAddress getPrimaryAddr() { + return primaryAddr; + } + + /** + * Set the primary address of this neighbor. + * + * @param primaryAddr the address we'll use when sending hello messages + */ + public void setPrimaryAddr(IpAddress primaryAddr) { + this.primaryAddr = primaryAddr; + } + + /** + * Get the priority this neighbor has advertised to us. + * + * @return the priority + */ + public int getPriority() { + return priority; + } + + /** + * Set the priority for this neighbor. + * + * @param priority This neighbors priority. + */ + public void setPriority(int priority) { + this.priority = priority; + } + + /** + * Get the generation ID. + * + * @return the generation ID. + */ + public int getGenId() { + return genId; + } + + /** + * Set the generation ID. + * + * @param genId the generation ID. + */ + public void setGenId(int genId) { + this.genId = genId; + } + + /** + * Get the holdtime for this neighbor. + * + * @return the holdtime + */ + public short getHoldtime() { + return holdtime; + } + + /** + * Set the holdtime for this neighbor. + * + * @param holdtime the holdtime. + */ + public void setholdtime(short holdtime) { + this.holdtime = holdtime; + } + + /** + * Is this neighbor the designated router on this connect point? + * + * @return true if so, false if not. + */ + public boolean isDr() { + return isDr; + } + + /** + * Set this router as the designated router on this connect point. + * + * @param isDr True is this neighbor is the DR false otherwise + */ + public void setIsDr(boolean isDr) { + this.isDr = isDr; + } + + /** + * The ConnectPoint this neighbor is connected to. + * + * @return the ConnectPoint + */ + public ConnectPoint getConnectPoint() { + return connectPoint; + } + + /** + * Set the ConnectPoint this router is connected to. + * + * @param connectPoint the ConnectPoint this router is connected to. + */ + public void setConnectPoint(ConnectPoint connectPoint) { + this.connectPoint = connectPoint; + } + + /** + * Set a back pointer to the neighbors list this neighbor is a member of. + * + * @param neighbors the neighbor list this neighbor belongs to + */ + public void setNeighbors(PIMNeighbors neighbors) { + this.neighbors = neighbors; + } + + /** + * We have received a fresh hello from a neighbor, now we need to process it. + * Depending on the values received in the the hello options may force a + * re-election process. + * + * We will also refresh the timeout for this neighbor. + * + * @param hello copy of the hello we'll be able to extract options from. + */ + public void refresh(PIMHello hello) { + checkNotNull(hello); + + for (PIMHelloOption opt : hello.getOptions().values()) { + + int len = opt.getOptLength(); + byte [] value = new byte[len]; + ByteBuffer bb = ByteBuffer.wrap(value); + + switch (opt.getOptType()) { + case PIMHelloOption.OPT_GENID: + int newid = bb.getInt(); + if (this.genId != newid) { + // TODO: we have a newly rebooted neighbor. Send them our joins. + this.genId = newid; + } + break; + + case PIMHelloOption.OPT_PRIORITY: + int newpri = bb.getInt(); + if (this.priority != newpri) { + + // The priorities have changed. We may need to re-elect a new DR? + if (this.isDr || this.neighbors.getDesignatedRouter().getPriority() < priority) { + reelect = true; + } + this.priority = newpri; + } + break; + + case PIMHelloOption.OPT_HOLDTIME: + short holdtime = bb.getShort(); + if (this.holdtime != holdtime) { + this.holdtime = holdtime; + if (holdtime == 0) { + // We have a neighbor going down. We can remove all joins + // we have learned from them. + // TODO: What else do we need to do when a neighbor goes down? + + log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString()); + return; + } + } + break; + + case PIMHelloOption.OPT_PRUNEDELAY: + case PIMHelloOption.OPT_ADDRLIST: + // TODO: implement prune delay and addr list. Fall through for now. + + default: + log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType()); + break; + } + } + + if (reelect) { + this.neighbors.electDR(this); + } + + // Reset the next timeout timer + this.resetTimeout(); + } + + /* --------------------------------------- Timer functions -------------------------- */ + + /** + * Restart the timeout task for this neighbor. + */ + private void resetTimeout() { + + if (this.holdtime == 0) { + + // Prepare to die. + log.debug("shutting down timer for nbr {}", this.primaryAddr.toString()); + if (this.timeout != null) { + this.timeout.cancel(); + this.timeout = null; + } + return; + } + + // Cancel the existing timeout and start a fresh new one. + if (this.timeout != null) { + this.timeout.cancel(); + } + + this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS); + } + + /** + * The task to run when a neighbor timeout expires. + */ + private final class NeighborTimeoutTask implements TimerTask { + PIMNeighbor nbr; + + NeighborTimeoutTask(PIMNeighbor nbr) { + this.nbr = nbr; + } + + @Override + public void run(Timeout timeout) throws Exception { + + // TODO: log.debug; + PIMNeighbors neighbors = nbr.neighbors; + neighbors.removeNeighbor(nbr.getPrimaryAddr()); + } + } + + /** + * Stop the timeout timer. + * + * This happens when we remove the neighbor. + */ + private final void stopTimeout() { + this.timeout.cancel(); + this.timeout = null; + } + + @Override + public String toString() { + String out = ""; + if (this.isDr) { + out += "*NBR:"; + } else { + out += "NBR:"; + } + out += "\tIP: " + this.primaryAddr.toString(); + out += "\tPr: " + String.valueOf(this.priority); + out += "\tHoldTime: " + String.valueOf(this.holdtime); + out += "\tGenID: " + String.valueOf(this.genId) + "\n"; + return out; + } +} \ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java new file mode 100644 index 00000000..cad90768 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java @@ -0,0 +1,395 @@ + +package org.onosproject.pim.impl; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.PIM; +import org.onlab.packet.pim.PIMHello; +import org.onosproject.net.ConnectPoint; +import java.util.HashMap; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * PIMNeighbors is a collection of all neighbors we have received + * PIM hello messages from. The main structure is a HashMap indexed + * by ConnectPoint with another HashMap indexed on the PIM neighbors + * IPAddress, it contains all PIM neighbors attached on that ConnectPoint. + */ +public final class PIMNeighbors { + + private static Logger log = LoggerFactory.getLogger("PIMNeighbors"); + + /** + * This is the global container for all PIM neighbors indexed by ConnectPoints. + * + * NOTE: We'll have a problem if the same neighbor can show up on two interfaces + * but that should never happen. + */ + private static HashMap 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 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 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 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/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java new file mode 100644 index 00000000..ee62eb79 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.net.ConnectPoint; + +import java.util.HashMap; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * PIM neighbors Codec. + */ +public class PIMNeighborsCodec extends JsonCodec> { + // JSON field names + //Return Name + private static final String CPNBRLIST = "connect_point_list"; + + // PIM Neightbors Fields + private static final String IP = "ip"; + private static final String PRIORITY = "priority"; + private static final String NBRLIST = "neighbor_list"; + + // PIM neighbor Files + private static final String DR = "designated"; + private static final String NBR_IP = "ip"; + private static final String PR = "priority"; + private static final String HOLDTIME = "hold_time"; + + /** + * Encode the PIM Neighbors. + * + * @param cpn ConnectPoint neighbors + * @param context encoding context + * + * @return Encoded neighbors used by CLI and REST + */ + @Override + public ObjectNode encode(HashMap cpn, CodecContext context) { + checkNotNull(cpn, "Pim Neighbors cannot be null"); + + ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode(); + ArrayNode cpnList = context.mapper().createArrayNode(); + + for (PIMNeighbors pn: cpn.values()) { + // get the PimNeighbors Obj, contains Neighbors list + // create the json object for a single Entry in the Neighbors list + ObjectNode cp = context.mapper().createObjectNode(); + cp.put(IP, pn.getOurIpAddress().toString()); + cp.put(PRIORITY, String.valueOf(pn.getOurPriority())); + + // create the array for the neighbors list + ArrayNode nbrsList = context.mapper().createArrayNode(); + for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) { + nbrsList.add(neighbor(nbr, context)); + } + // adds pim neighbor to list + cp.set(NBRLIST, nbrsList); + // adds to arraynode which will represent the connect point neighbors hash map. + cpnList.add(cp); + } + pimNbrJsonCodec.set(CPNBRLIST, cpnList); + return pimNbrJsonCodec; + } + + /** + * Encode a single PIM Neighbor. + * + * @param nbr the neighbor to be encoded + * @param context encoding context + * @return the encoded neighbor + */ + private ObjectNode neighbor(PIMNeighbor nbr, CodecContext context) { + return context.mapper().createObjectNode() + .put(DR, Boolean.toString(nbr.isDr())) + .put(NBR_IP, nbr.getPrimaryAddr().toString()) + .put(PR, String.valueOf(nbr.getPriority())) + .put(HOLDTIME, String.valueOf(nbr.getHoldtime())); + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java new file mode 100644 index 00000000..c131a53b --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import org.jboss.netty.util.HashedWheelTimer; + +/** + * PIM Timer used for PIM Neighbors. + */ +public final class PIMTimer { + + private static volatile HashedWheelTimer timer; + + // Ban public construction + private PIMTimer() { + } + + /** + * Returns the singleton hashed-wheel timer. + * + * @return hashed-wheel timer + */ + public static HashedWheelTimer getTimer() { + if (PIMTimer.timer == null) { + initTimer(); + } + return PIMTimer.timer; + } + + // Start the PIM timer. + private static synchronized void initTimer() { + if (PIMTimer.timer == null) { + + // Create and start a new hashed wheel timer, if it does not exist. + HashedWheelTimer hwTimer = new HashedWheelTimer(); + hwTimer.start(); + PIMTimer.timer = hwTimer; + } + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java new file mode 100644 index 00000000..29d1ce4e --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * PIM Emulation speak hello messages and listen to Join/Prunes. + */ +package org.onosproject.pim.impl; diff --git a/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml new file mode 100644 index 00000000..c30e3792 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml @@ -0,0 +1,24 @@ + + + + + + + + + + -- cgit 1.2.3-korg