Jonathan Hart

Added configuration for PIM interfaces.

Now the PIM application requires PIM Interface configuration for each interface
that will have PIM enabled (no longer uses all ONOS interfaces). The
interface-specific PIM parameters can be tuned.

Change-Id: Ibc284fdbe1b3aa4da48097b3e92470bce4f349a7
......@@ -82,37 +82,4 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
org.slf4j,
org.osgi.framework,
com.google.common.*,
org.apache.karaf.shell.commands,
org.apache.karaf.shell.console,
org.onlab.packet.*,
org.onosproject.*,
</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
......
/*
* Copyright 2016 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 org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.pim.impl.PIMInterface;
import org.onosproject.pim.impl.PIMInterfaceService;
import java.util.Set;
/**
* Lists the interfaces where PIM is enabled.
*/
@Command(scope = "onos", name = "pim-interfaces",
description = "Lists the interfaces where PIM is enabled")
public class PimInterfacesListCommand extends AbstractShellCommand {
private static final String FORMAT = "interfaceName=%s, holdTime=%s, priority=%s, genId=%s";
@Override
protected void execute() {
PIMInterfaceService interfaceService = get(PIMInterfaceService.class);
Set<PIMInterface> interfaces = interfaceService.getPimInterfaces();
interfaces.forEach(
pimIntf -> print(FORMAT, pimIntf.getInterface().name(),
pimIntf.getHoldtime(), pimIntf.getPriority(),
pimIntf.getGenerationId()));
}
}
......@@ -24,14 +24,6 @@ import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
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 org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.mcast.MulticastRouteService;
......@@ -43,7 +35,6 @@ import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import java.util.Optional;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -72,17 +63,6 @@ public class PIMApplication {
// Create an instance of the PIM packet handler
protected PIMPacketHandler pimPacketHandler;
// 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;
// Internal class used to listen for network configuration changes
private InternalConfigListener configListener = new InternalConfigListener();
// Provide interfaces to the pimInterface manager as a result of Netconfig updates.
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PIMInterfaceService pimInterfaceManager;
......@@ -94,7 +74,6 @@ public class PIMApplication {
*/
@Activate
public void activate() {
// Get our application ID
appId = coreService.registerApplication("org.onosproject.pim");
......@@ -109,15 +88,9 @@ public class PIMApplication {
packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
appId, Optional.empty());
// Register for notifications from the Network config & Interface services.
// We'll use these services to represent "PIMInterfaces"
// Get a copy of the PIM Packet Handler
pimPacketHandler = new PIMPacketHandler();
// Listen for network configuration changes
configService.addListener(configListener);
log.info("Started");
}
......@@ -180,57 +153,4 @@ public class PIMApplication {
}
}
/*
* This class receives all events from the network config services, then hands the
* event off to the PIMInterfaceManager for proper handling.
*
* TODO: should this move to PIMInterfaceManager?
*/
private class InternalConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
log.debug(event.toString());
switch (event.type()) {
case CONFIG_ADDED:
case CONFIG_UPDATED:
if (event.configClass() == InterfaceConfig.class) {
InterfaceConfig config = configService.getConfig(
(ConnectPoint) event.subject(),
InterfaceConfig.class);
log.debug("Got a network configuration event");
// Walk the interfaces and feed them to the PIMInterfaceManager
Set<Interface> intfs;
try {
intfs = config.getInterfaces();
for (Interface intf : intfs) {
pimInterfaceManager.updateInterface(intf);
}
} catch (ConfigException e) {
log.error(e.toString());
return;
}
}
break;
case CONFIG_REMOVED:
if (event.configClass() == InterfaceConfig.class) {
ConnectPoint cp = (ConnectPoint) event.subject();
//assertNotNull(cp);
pimInterfaceManager.deleteInterface(cp);
}
break;
case CONFIG_REGISTERED:
case CONFIG_UNREGISTERED:
default:
log.debug("\tWe are not handling this event type");
break;
}
}
}
}
......
......@@ -34,8 +34,10 @@ import org.slf4j.Logger;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -43,7 +45,7 @@ import static org.slf4j.LoggerFactory.getLogger;
* PIM Interface represents an ONOS Interface with IP and MAC addresses for
* a given ConnectPoint.
*/
public class PIMInterface {
public final class PIMInterface {
private final Logger log = getLogger(getClass());
......@@ -62,10 +64,10 @@ public class PIMInterface {
private int priority = PIMHelloOption.DEFAULT_PRIORITY;
// Our current genid
private int genid = PIMHelloOption.DEFAULT_GENID; // Needs to be assigned.
private final int generationId;
// The IP address of the DR
IpAddress drIpaddress;
private IpAddress drIpaddress;
// A map of all our PIM neighbors keyed on our neighbors IP address
private Map<IpAddress, PIMNeighbor> pimNeighbors = new HashMap<>();
......@@ -74,14 +76,28 @@ public class PIMInterface {
* Create a PIMInterface from an ONOS Interface.
*
* @param intf the ONOS Interface.
* @param holdTime hold time
* @param priority priority
* @param propagationDelay propagation delay
* @param overrideInterval override interval
* @param packetService reference to the packet service
*/
public PIMInterface(Interface intf, PacketService packetService) {
private PIMInterface(Interface intf,
short holdTime,
int priority,
short propagationDelay,
short overrideInterval,
PacketService packetService) {
onosInterface = intf;
outputTreatment = createOutputTreatment();
this.holdtime = holdTime;
this.packetService = packetService;
IpAddress ourIp = getIpAddress();
MacAddress mac = intf.mac();
generationId = new Random().nextInt();
// Create a PIM Neighbor to represent ourselves for DR election.
PIMNeighbor us = new PIMNeighbor(ourIp, mac);
......@@ -178,8 +194,8 @@ public class PIMInterface {
*
* @return our generation ID
*/
public int getGenid() {
return genid;
public int getGenerationId() {
return generationId;
}
/**
......@@ -188,7 +204,6 @@ public class PIMInterface {
* result of a newly created interface.
*/
public void sendHello() {
// Create the base PIM Packet and mark it a hello packet
PIMPacket pimPacket = new PIMPacket(PIM.TYPE_HELLO);
......@@ -199,6 +214,9 @@ public class PIMInterface {
// Create the hello message with options
PIMHello hello = new PIMHello();
hello.createDefaultOptions();
hello.addOption(PIMHelloOption.createHoldTime(holdtime));
hello.addOption(PIMHelloOption.createPriority(priority));
hello.addOption(PIMHelloOption.createGenID(generationId));
// Now set the hello option payload
pimPacket.setPIMPayload(hello);
......@@ -266,7 +284,7 @@ public class PIMInterface {
nbr.refreshTimestamp();
/*
* the election method will frist determine if an election
* the election method will first determine if an election
* needs to be run, if so it will run the election. The
* IP address of the DR will be returned. If the IP address
* of the DR is different from what we already have we know a
......@@ -280,17 +298,17 @@ public class PIMInterface {
}
// Run an election if we need to. Return the elected IP address.
private IpAddress election(PIMNeighbor nbr, IpAddress drip, int drpri) {
private IpAddress election(PIMNeighbor nbr, IpAddress drIp, int drPriority) {
IpAddress nbrip = nbr.getIpaddr();
if (nbr.getPriority() > drpri) {
return nbrip;
IpAddress nbrIp = nbr.getIpaddr();
if (nbr.getPriority() > drPriority) {
return nbrIp;
}
if (nbrip.compareTo(drip) > 0) {
return nbrip;
if (nbrIp.compareTo(drIp) > 0) {
return nbrIp;
}
return drip;
return drIp;
}
/**
......@@ -301,4 +319,105 @@ public class PIMInterface {
public void processJoinPrune(Ethernet ethPkt) {
// TODO: add Join/Prune processing code.
}
/**
* Returns a builder for a PIM interface.
*
* @return PIM interface builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder for a PIM interface.
*/
public static class Builder {
private Interface intf;
private PacketService packetService;
private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
private int priority = PIMHelloOption.DEFAULT_PRIORITY;
private short propagationDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
private short overrideInterval = PIMHelloOption.DEFAULT_OVERRIDEINTERVAL;
/**
* Uses the specified ONOS interface.
*
* @param intf ONOS interface
* @return this PIM interface builder
*/
public Builder withInterface(Interface intf) {
this.intf = checkNotNull(intf);
return this;
}
/**
* Sets the reference to the packet service.
*
* @param packetService packet service
* @return this PIM interface builder
*/
public Builder withPacketService(PacketService packetService) {
this.packetService = checkNotNull(packetService);
return this;
}
/**
* Uses the specified hold time.
*
* @param holdTime hold time in seconds
* @return this PIM interface builder
*/
public Builder withHoldTime(short holdTime) {
this.holdtime = holdTime;
return this;
}
/**
* Uses the specified DR priority.
*
* @param priority DR priority
* @return this PIM interface builder
*/
public Builder withPriority(int priority) {
this.priority = priority;
return this;
}
/**
* Uses the specified propagation delay.
*
* @param propagationDelay propagation delay in ms
* @return this PIM interface builder
*/
public Builder withPropagationDelay(short propagationDelay) {
this.propagationDelay = propagationDelay;
return this;
}
/**
* Uses the specified override interval.
*
* @param overrideInterval override interval in ms
* @return this PIM interface builder
*/
public Builder withOverrideInterval(short overrideInterval) {
this.overrideInterval = overrideInterval;
return this;
}
/**
* Builds the PIM interface.
*
* @return PIM interface
*/
public PIMInterface build() {
checkArgument(intf != null, "Must provide an interface");
checkArgument(packetService != null, "Must provide a packet service");
return new PIMInterface(intf, holdtime, priority, propagationDelay,
overrideInterval, packetService);
}
}
}
......
......@@ -15,9 +15,10 @@
*/
package org.onosproject.pim.impl;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
import java.util.Set;
/**
* Define the PIMInterfaceService. PIM will use ONOS Interfaces to
* define PIM Interfaces. The PIM Application signed up as a Netconfig
......@@ -28,25 +29,17 @@ import org.onosproject.net.ConnectPoint;
public interface PIMInterfaceService {
/**
* Update the corresponding PIMInterface. If the PIMInterface
* does not exist it will be created.
*
* @param intf ONOS Interface.
*/
public void updateInterface(Interface intf);
/**
* Delete the PIMInterface that corresponds to the given ConnectPoint.
*
* @param cp The connect point associated with this interface.
*/
public void deleteInterface(ConnectPoint cp);
/**
* Return the PIMInterface associated with the given ConnectPoint.
*
* @param cp The ConnectPoint we want to get the PIMInterface for.
* @return the PIMInterface if it exists, NULL if it does not exist.
*/
public PIMInterface getPIMInterface(ConnectPoint cp);
/**
* Retrieves the set of all interfaces running PIM.
*
* @return set of PIM interfaces
*/
Set<PIMInterface> getPimInterfaces();
}
......
/*
* Copyright 2016 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.onosproject.net.ConnectPoint;
import org.onosproject.net.config.Config;
import java.util.Optional;
/**
* Configuration for a PIM interface.
*/
public class PimInterfaceConfig extends Config<ConnectPoint> {
private static final String INTERFACE_NAME = "interfaceName";
private static final String ENABLED = "enabled";
private static final String HOLD_TIME = "holdTime";
private static final String PRIORITY = "priority";
private static final String PROPAGATION_DELAY = "propagationDelay";
private static final String OVERRIDE_INTERVAL = "overrideInterval";
/**
* Gets the name of the interface. This links the PIM configuration with
* an existing ONOS interface.
*
* @return interface name
*/
public String getInterfaceName() {
return node.path(INTERFACE_NAME).asText();
}
/**
* Returns whether PIM is enabled on the interface or not.
*
* @return true if PIM is enabled, otherwise false
*/
public boolean isEnabled() {
return node.path(ENABLED).asBoolean(false);
}
/**
* Gets the HELLO hold time of the interface.
*
* @return hold time
*/
public Optional<Short> getHoldTime() {
if (node.path(HOLD_TIME).isMissingNode()) {
return Optional.empty();
}
return Optional.of(Short.parseShort(node.path(HOLD_TIME).asText()));
}
/**
* Gets the priority of the interface.
*
* @return priority
*/
public Optional<Integer> getPriority() {
if (node.path(PRIORITY).isMissingNode()) {
return Optional.empty();
}
return Optional.of(node.path(PRIORITY).asInt());
}
/**
* Gets the propagation delay of the interface.
*
* @return propagation delay
*/
public Optional<Short> getPropagationDelay() {
if (node.path(PROPAGATION_DELAY).isMissingNode()) {
return Optional.empty();
}
return Optional.of(Short.parseShort(node.path(PROPAGATION_DELAY).asText()));
}
/**
* Gets the override interval of the interface.
*
* @return override interval
*/
public Optional<Short> getOverrideInterval() {
if (node.path(OVERRIDE_INTERVAL).isMissingNode()) {
return Optional.empty();
}
return Optional.of(Short.parseShort(node.path(OVERRIDE_INTERVAL).asText()));
}
}
......@@ -17,7 +17,7 @@
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
<action class="org.onosproject.pim.cli.PIMShowCommand"/>
<action class="org.onosproject.pim.cli.PimInterfacesListCommand"/>
</command>
</command-bundle>
......
......@@ -23,29 +23,43 @@ import java.text.MessageFormat;
import static org.onlab.packet.PacketUtils.checkBufferLength;
import static org.onlab.packet.PacketUtils.checkInput;
/**
* PIM HELLO option.
*/
public class PIMHelloOption {
/**
/*
* PIM Option types.
*/
public static final short OPT_HOLDTIME = 1;
public static final short HOLDTIME_LENGTH = 2;
public static final short DEFAULT_HOLDTIME = 105;
public static final short OPT_PRUNEDELAY = 2;
public static final short OPT_PRIORITY = 19;
public static final short OPT_GENID = 20;
public static final short OPT_ADDRLIST = 24;
public static final short PRUNEDELAY_LENGTH = 4;
public static final short DEFAULT_PRUNEDELAY = 500; // 500 ms
public static final short DEFAULT_OVERRIDEINTERVAL = 2500; // 2500 ms
public static final short DEFAULT_HOLDTIME = 105;
public static final int DEFAULT_PRUNEDELAY = 2000; // 2,000 ms
public static final short OPT_PRIORITY = 19;
public static final short PRIORITY_LENGTH = 4;
public static final int DEFAULT_PRIORITY = 1;
public static final short OPT_GENID = 20;
public static final short GENID_LENGTH = 4;
public static final int DEFAULT_GENID = 0;
public static final short OPT_ADDRLIST = 24;
public static final int MINIMUM_OPTION_LEN_BYTES = 4;
// Values for this particular hello option.
private short optType;
private short optLength;
private short optType = 0;
private short optLength = 0;
private byte[] optValue;
/**
* Constructs a new hello option with no fields set.
*/
public PIMHelloOption() {
}
......@@ -59,25 +73,25 @@ public class PIMHelloOption {
this.optType = type;
switch (type) {
case OPT_HOLDTIME:
this.optLength = 2;
this.optLength = HOLDTIME_LENGTH;
this.optValue = new byte[optLength];
ByteBuffer.wrap(this.optValue).putShort(PIMHelloOption.DEFAULT_HOLDTIME);
break;
case OPT_PRUNEDELAY:
this.optLength = 4;
this.optLength = PRUNEDELAY_LENGTH;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_PRUNEDELAY);
break;
case OPT_PRIORITY:
this.optLength = 4;
this.optLength = PRIORITY_LENGTH;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_PRIORITY);
break;
case OPT_GENID:
this.optLength = 4;
this.optLength = GENID_LENGTH;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_GENID);
break;
......@@ -109,17 +123,85 @@ public class PIMHelloOption {
return this.optLength;
}
public void setValue(ByteBuffer bb) throws DeserializationException {
public void setValue(ByteBuffer bb) {
this.optValue = new byte[this.optLength];
bb.get(this.optValue, 0, this.optLength);
}
public void setValue(byte[] value) {
this.optValue = value;
}
public byte[] getValue() {
return this.optValue;
}
/**
* Creates a new PIM Hello option with the specified values.
*
* @param type hello option type
* @param length option length
* @param value option value
* @return new PIM Hello option
*/
public static PIMHelloOption create(short type, short length, ByteBuffer value) {
PIMHelloOption option = new PIMHelloOption();
option.setOptType(type);
option.setOptLength(length);
value.rewind();
option.setValue(value);
return option;
}
/**
* Creates a new priority option.
*
* @param priority priority
* @return priority option
*/
public static PIMHelloOption createPriority(int priority) {
return create(OPT_PRIORITY, PRIORITY_LENGTH,
ByteBuffer.allocate(PRIORITY_LENGTH).putInt(priority));
}
/**
* Creates a new hold time option.
*
* @param holdTime hold time
* @return hold time option
*/
public static PIMHelloOption createHoldTime(short holdTime) {
return create(OPT_HOLDTIME, HOLDTIME_LENGTH,
ByteBuffer.allocate(HOLDTIME_LENGTH).putShort(holdTime));
}
/**
* Creates a new generation ID option with a particular generation ID.
*
* @param genId generation ID value
* @return generation ID option
*/
public static PIMHelloOption createGenID(int genId) {
return create(OPT_GENID, GENID_LENGTH,
ByteBuffer.allocate(GENID_LENGTH).putInt(genId));
}
/**
* Creates a new LAN Prune Delay option.
*
* @param propagationDelay prune delay
* @param overrideInterval override interval
* @return prune delay option
*/
public static PIMHelloOption createPruneDelay(short propagationDelay, short overrideInterval) {
return create(OPT_PRUNEDELAY, PRUNEDELAY_LENGTH,
ByteBuffer.allocate(PRUNEDELAY_LENGTH)
.putShort(propagationDelay)
.putShort(overrideInterval));
}
public static PIMHelloOption deserialize(ByteBuffer bb) throws DeserializationException {
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), 4);
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), MINIMUM_OPTION_LEN_BYTES);
PIMHelloOption opt = new PIMHelloOption();
opt.setOptType(bb.getShort());
......@@ -132,7 +214,7 @@ public class PIMHelloOption {
}
public byte[] serialize() {
int len = 4 + this.optLength;
int len = MINIMUM_OPTION_LEN_BYTES + this.optLength;
ByteBuffer bb = ByteBuffer.allocate(len);
bb.putShort(this.optType);
bb.putShort(this.optLength);
......