Rusty Eddy
Committed by Gerrit Code Review

Blew away old version of PIM to restructure. And:

1) Added packetService to register for PIM packets.
2) Added PIMPacketHandler to process PIM packets.
3) Added NetworkConfig Listener
4) Added PIMInterfaceService / PIMInterfaceManager
5) Added Process incoming hello packets to PIMInterfaceManager
6) Code Review inspired changes

Change-Id: I753880c954b9a6a91544903b613305ff9aa78cd0
/*
* 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.pim.impl.PIMInterface;
import org.onosproject.pim.impl.PIMInterfaces;
import org.onosproject.pim.impl.PIMInterfacesCodec;
import java.util.Collection;
@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 PIMInterfaces class.
@Override
protected void execute() {
// grab connect point neighbors hash map to send in to json encoder.
Collection<PIMInterface> pimIntfs = PIMInterfaces.getInstance().getInterfaces();
if (outputJson()) {
print("%s", json(pimIntfs));
} else {
print(PIMInterfaces.getInstance().printInterfaces());
}
}
private JsonNode json(Collection<PIMInterface> pimIntfs) {
return new PIMInterfacesCodec().encode(pimIntfs, this);
}
}
\ 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.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.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;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
/**
* The main PIM controller class.
*/
@Component(immediate = true)
public class PIMApplication {
private final Logger log = getLogger(getClass());
// Used to get the appId
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
// Our application ID
private static ApplicationId appId;
// Register to receive PIM packets, used to send packets as well
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
// Use the MulticastRouteService to manage incoming PIM Join/Prune state as well as
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MulticastRouteService ms;
// 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;
/**
* Activate the PIM component.
*/
@Activate
public void activate() {
// Get our application ID
appId = coreService.registerApplication("org.onosproject.pim");
// Build the traffic selector for PIM packets
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchIPProtocol(IPv4.PROTOCOL_PIM);
// Use the traffic selector to tell the packet service which packets we want.
// PIMPacketService is an inner class defined below
PIMPacketProcessor processor = new PIMPacketProcessor();
packetService.addProcessor(processor, PacketProcessor.director(5));
// 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");
}
/**
* Deactivate the PIM component.
*/
@Deactivate
public void deactivate() {
log.info("Stopped");
}
/**
* The class that will receive PIM packets, sanitize them, determine the PIMInterface
* they arrived on, then forward them on to be processed by the appropriate entity.
*/
public class PIMPacketProcessor implements PacketProcessor {
private final Logger log = getLogger(getClass());
@Override
public void process(PacketContext context) {
// return if this packet has already been handled
if (context.isHandled()) {
return;
}
// get the inbound packet
InboundPacket pkt = context.inPacket();
if (pkt == null) {
// problem getting the inbound pkt. Log it debug to avoid spamming log file
log.debug("Could not retrieve packet from context");
return;
}
// Get the ethernet header
Ethernet eth = pkt.parsed();
if (eth == null) {
// problem getting the ethernet pkt. Log it debug to avoid spamming log file
log.debug("Could not retrieve ethnernet packet from the parsed packet");
return;
}
// Get the PIM Interface the packet was received on.
PIMInterface pimi = pimInterfaceManager.getPIMInterface(pkt.receivedFrom());
if (pimi == null) {
log.debug("We received PIM packet from a non PIM interface: " + pkt.receivedFrom().toString());
return;
}
/*
* Pass the packet processing off to the PIMInterface for processing.
*
* TODO: Is it possible that PIM interface processing should move to the
* PIMInterfaceManager directly?
*/
PIMPacketHandler ph = new PIMPacketHandler();
ph.processPacket(eth, pimi);
}
}
/*
* 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;
}
}
}
}
/*
* 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.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
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 (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;
// 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");
// Initialize the Packet Handler class
pimPacketHandler = PIMPacketHandler.getInstance();
pimPacketHandler.initialize(packetService, appId);
// Initialize the Interface class
pimInterfaces = PIMInterfaces.getInstance();
pimInterfaces.initialize(configService, interfaceService);
log.info("Started");
}
@Deactivate
public void deactivate() {
PIMPacketHandler.getInstance().stop();
log.info("Stopped");
}
}
/*
* 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.google.common.collect.Maps;
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.apache.felix.scr.annotations.Service;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import java.util.Map;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages PIMInterfaces.
*
* TODO: Do we need to add a ServiceListener?
*/
@Component(immediate = true)
@Service
public class PIMInterfaceManager implements PIMInterfaceService {
private final Logger log = getLogger(getClass());
// Create ourselves a provider ID
private static final ProviderId PID = new ProviderId("pim", "org.onosproject.pim");
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected InterfaceService interfaceService;
// Store PIM Interfaces in a map key'd by ConnectPoint
private final Map<ConnectPoint, PIMInterface> pimInterfaces = Maps.newConcurrentMap();
@Activate
public void activate() {
// Query the Interface service to see if Interfaces already exist.
log.info("Started");
// Create PIM Interfaces for each of the existing ONOS Interfaces.
for (Interface intf : interfaceService.getInterfaces()) {
pimInterfaces.put(intf.connectPoint(), new PIMInterface(intf));
}
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
/**
* Update the ONOS Interface with the new Interface. If the PIMInterface does
* not exist we'll create a new one and store it.
*
* @param intf ONOS Interface.
*/
@Override
public void updateInterface(Interface intf) {
ConnectPoint cp = intf.connectPoint();
log.debug("Updating Interface for " + intf.connectPoint().toString());
pimInterfaces.compute(cp, (k, v) -> (v == null) ?
new PIMInterface(intf) :
v.setInterface(intf));
}
/**
* Delete the PIM Interface to the corresponding ConnectPoint.
*
* @param cp The connect point associated with this interface we want to delete
*/
@Override
public void deleteInterface(ConnectPoint cp) {
PIMInterface pi = pimInterfaces.remove(cp);
if (pi == null) {
log.warn("We've been asked to remove an interface we3 don't have: " + cp.toString());
return;
}
}
/**
* Return the PIMInterface that corresponds to 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.
*/
@Override
public PIMInterface getPIMInterface(ConnectPoint cp) {
PIMInterface pi = pimInterfaces.getOrDefault(cp, null);
if (pi == null) {
log.warn("We have been asked for an Interface we don't have: " + cp.toString());
}
return pi;
}
}
/*
* 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.onosproject.incubator.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
/**
* Define the PIMInterfaceService. PIM will use ONOS Interfaces to
* define PIM Interfaces. The PIM Application signed up as a Netconfig
* listener.
*
* TODO: Do we need a PIMInterfaceListenerService? Who sould listen to Interfaces changes?
*/
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);
}
/*
* 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) {
PIMInterfaces.this.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
/*
* 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 java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* PIM neighbors Codec.
*/
public class PIMInterfacesCodec extends JsonCodec<Collection<PIMInterface>> {
// 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(Collection<PIMInterface> cpn, CodecContext context) {
checkNotNull(cpn, "Pim Neighbors cannot be null");
ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode();
ArrayNode cpnList = context.mapper().createArrayNode();
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.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.getNeighbors()) {
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()));
}
}
/*
* 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;
// A back pointer the neighbors list this neighbor belongs to.
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 pimInterface The PIMInterface of this neighbor
*/
public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, PIMInterface pimInterface) {
this.macAddress = macaddr;
this.primaryAddr = ipaddr;
this.pimInterface = pimInterface;
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 PIMInterface getPimInterface() {
return pimInterface;
}
/**
* 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.
*
* 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);
boolean reelect = false;
for (PIMHelloOption opt : hello.getOptions().values()) {
int len = opt.getOptLength();
ByteBuffer bb = ByteBuffer.wrap(opt.getValue());
switch (opt.getOptType()) {
case PIMHelloOption.OPT_GENID:
int newid = bb.getInt();
if (this.genId != newid) {
// We have a newly rebooted neighbor, this is where we would
// 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 || pimInterface.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.
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) {
pimInterface.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 {
log.debug("PIM Neighbor {} has timed out: ", nbr.toString());
nbr.pimInterface.removeNeighbor(nbr);
}
}
/**
* 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
......@@ -17,213 +17,74 @@ 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.
* This class will process 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 class 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));
}
private final Logger log = getLogger(getClass());
/**
* Shutdown the packet handling service.
* Constructor for this class.
*/
public void stop() {
packetService.removeProcessor(processor);
processor = null;
public PIMPacketHandler() {
}
/**
* Packet processor responsible for handling IGMP packets.
* Sanitize and process the packet.
* TODO: replace ConnectPoint with PIMInterface when PIMInterface has been added.
*
* @param ethPkt the packet starting with the Ethernet header.
* @param pimi the PIM Interface the packet arrived on.
*/
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;
}
public void processPacket(Ethernet ethPkt, PIMInterface pimi) {
checkNotNull(ethPkt);
checkNotNull(pimi);
// Sanitize the ethernet header to ensure it is IPv4. IPv6 we'll deal with later
if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
log.debug("Recieved a non IPv4 packet");
return;
}
/**
* 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);
// Get the IP header
IPv4 ip = (IPv4) ethPkt.getPayload();
if (ip.getProtocol() != IPv4.PROTOCOL_PIM) {
log.debug("Received a non PIM IP packet");
return;
}
}
// Create an ethernet header and serialize then send
public void sendPacket(PIM pim, PIMInterface pimIntf) {
// Get the address of our the neighbor that sent this packet to us.
IpAddress nbraddr = IpAddress.valueOf(ip.getDestinationAddress());
log.debug("Packet " + nbraddr.toString() + " received on port " + pimi.toString());
Interface theInterface = pimIntf.getInterface();
// Get the PIM header
PIM pim = (PIM) ip.getPayload();
checkNotNull(pim);
// 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);
// Process the pim packet
switch (pim.getPimMsgType()) {
// Now set pim
ip.setPayload(pim);
pim.setParent(ip);
case PIM.TYPE_HELLO:
pimi.processHello(ethPkt);
log.debug("Received a PIM hello packet");
break;
ConnectPoint cp = theInterface.connectPoint();
checkNotNull(cp);
case PIM.TYPE_JOIN_PRUNE_REQUEST:
pimi.processJoinPrune(ethPkt);
log.debug("Received a PIM Join/Prune message");
break;
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);
default:
log.debug("Recieved unsupported PIM 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.jboss.netty.util.HashedWheelTimer;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* 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;
}
}
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();
}
}
......@@ -53,7 +53,6 @@ import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides implementation of the meter service APIs.
*/
......