Julian Lawrence
Committed by Gerrit Code Review

Refactored Mfwd to use new mcastroutemanager

Change-Id: I7aca7f118221ed505aeb7fcace0ef9dccb468a34
......@@ -22,7 +22,7 @@
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -15,10 +15,15 @@
*/
package org.onosproject.mfwd.cli;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.mfwd.impl.McastRouteTable;
import org.onosproject.mfwd.impl.McastForwarding;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.mcast.McastRoute;
import org.onosproject.net.mcast.MulticastRouteService;
/**
* Deletes a multicast route.
......@@ -27,6 +32,9 @@ import org.onosproject.mfwd.impl.McastRouteTable;
description = "Delete a multicast route flow")
public class McastDeleteCommand extends AbstractShellCommand {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
MulticastRouteService mcastRouteManager = AbstractShellCommand.get(MulticastRouteService.class);
@Argument(index = 0, name = "sAddr",
description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
required = true, multiValued = false)
......@@ -46,23 +54,16 @@ public class McastDeleteCommand extends AbstractShellCommand {
@Override
protected void execute() {
boolean deleted = false;
McastRouteTable mrib = McastRouteTable.getInstance();
McastRoute mRoute = McastForwarding.createStaticRoute(sAddr, gAddr);
if (egressList == null) {
mrib.removeRoute(sAddr, gAddr);
deleted = true;
mcastRouteManager.remove(mRoute);
} else {
// check list for validity before we begin to delete.
for (String egress : egressList) {
deleted = mrib.removeEgress(sAddr, gAddr, egress);
}
ConnectPoint eCp = ConnectPoint.deviceConnectPoint(egress);
mcastRouteManager.removeSink(mRoute, eCp);
}
if (deleted) {
print("Successful delete");
} else {
print("Failed to delete");
}
}
}
......
......@@ -15,13 +15,15 @@
*/
package org.onosproject.mfwd.cli;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.mfwd.impl.McastConnectPoint;
import org.onosproject.mfwd.impl.McastRouteBase;
import org.onosproject.mfwd.impl.McastRouteTable;
import org.onosproject.mfwd.impl.McastForwarding;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.mcast.McastRoute;
import org.onosproject.net.mcast.MulticastRouteService;
/**
* Installs a source, multicast group flow.
......@@ -30,6 +32,9 @@ import org.onosproject.mfwd.impl.McastRouteTable;
description = "Installs a source, multicast group flow")
public class McastJoinCommand extends AbstractShellCommand {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
MulticastRouteService mcastRouteManager = AbstractShellCommand.get(MulticastRouteService.class);
@Argument(index = 0, name = "sAddr",
description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
required = true, multiValued = false)
......@@ -41,31 +46,29 @@ public class McastJoinCommand extends AbstractShellCommand {
String gAddr = null;
@Argument(index = 2, name = "ingressPort",
description = "Ingress port and Egress ports",
description = "Ingress port of:XXXXXXXXXX/XX",
required = false, multiValued = false)
String ingressPort = null;
@Argument(index = 3, name = "ports",
description = "Ingress port and Egress ports",
description = "Egress ports of:XXXXXXXXXX/XX...",
required = false, multiValued = true)
String[] ports = null;
@Override
protected void execute() {
McastRouteTable mrib = McastRouteTable.getInstance();
McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
// Port format "of:0000000000000023/4"
if (ingressPort != null) {
String inCP = ingressPort;
log.debug("Ingress port provided: " + inCP);
mr.addIngressPoint(inCP);
}
McastRoute mRoute = McastForwarding.createStaticRoute(sAddr, gAddr);
mcastRouteManager.add(mRoute);
for (int i = 0; i < ports.length; i++) {
String egCP = ports[i];
ConnectPoint ingress = ConnectPoint.deviceConnectPoint(ingressPort);
mcastRouteManager.addSource(mRoute, ingress);
for (String egCP : ports) {
log.debug("Egress port provided: " + egCP);
mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
ConnectPoint egress = ConnectPoint.deviceConnectPoint(egCP);
mcastRouteManager.addSink(mRoute, egress);
}
print("Added the mcast route");
}
......
......@@ -15,15 +15,13 @@
*/
package org.onosproject.mfwd.cli;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.karaf.shell.commands.Command;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.mfwd.impl.McastRouteTable;
import org.onosproject.mfwd.impl.MRibCodec;
import org.onosproject.net.mcast.MulticastRouteService;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -33,30 +31,15 @@ import static org.slf4j.LoggerFactory.getLogger;
@Command(scope = "onos", name = "mcast-show", description = "Displays the source, multicast group flows")
public class McastShowCommand extends AbstractShellCommand {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
MulticastRouteService mcastRouteManager = AbstractShellCommand.get(MulticastRouteService.class);
private final Logger log = getLogger(getClass());
private static final String MCAST_GROUP = "mcastgroup";
@Override
protected void execute() {
McastRouteTable mrt = McastRouteTable.getInstance();
if (outputJson()) {
print("%s", json(mrt));
} else {
printMrib4(mrt);
}
}
public JsonNode json(McastRouteTable mrt) {
ObjectNode pushContent = new MRibCodec().encode(mrt , this);
return pushContent;
//TODO
}
/**
* Displays multicast route table entries.
*
* @param mrt Mutlicast Route Table
*/
protected void printMrib4(McastRouteTable mrt) {
print(mrt.printMcastRouteTable());
}
}
......
/*
* 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.mfwd.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onlab.packet.IpPrefix;
import java.util.Set;
import java.util.Map;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Encode and Decode the Multicast Route Table in JSON for CLI and REST commands.
*/
public class MRibCodec extends JsonCodec<McastRouteTable> {
private final Logger log = getLogger(getClass());
private static final String SOURCE_ADDRESS = "sourceAddress";
private static final String GROUP_ADDRESS = "groupAddress";
private static final String INGRESS_POINT = "ingressPoint";
private static final String EGRESS_POINT = "egressPoint";
private static final String MCASTCONNECTPOINT = "McastConnectPoint";
private static final String ELEMENTID = "elementId";
private static final String PORTNUMBER = "portNumber";
private static final String MCAST_GROUP = "mcastGroup";
/**
* Encode the MRIB into json format.
*
* @param mcastRouteTable McastRouteTable
* @param context CodecContext
* @return result ObjectNode
*/
@Override
public ObjectNode encode(McastRouteTable mcastRouteTable, CodecContext context) {
final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
final ObjectNode macastRouteTabNode = nodeFactory.objectNode();
ArrayNode mcastGroupNode = context.mapper().createArrayNode();
Optional<McastRouteTable> mcastRouteTabOpt = Optional.ofNullable(mcastRouteTable);
//checking whether the McastRouteTable is present.
if (mcastRouteTabOpt.isPresent()) {
Map<IpPrefix, McastRouteGroup> mrib4 = mcastRouteTabOpt.get().getMrib4();
Optional<Map<IpPrefix, McastRouteGroup>> mrib4Opt = Optional.ofNullable(mrib4);
//checking whether the mrib4 is present.
if (mrib4Opt.isPresent()) {
for (McastRouteGroup mg : mrib4Opt.get().values()) {
Collection<McastRouteSource> mcastRoute = mg.getSources().values();
Optional<Collection<McastRouteSource>> mcastRouteOpt = Optional.ofNullable(mcastRoute);
//checking whether the McastRouteSource List is present.
if (mcastRouteOpt.isPresent()) {
for (McastRouteSource mcastRouteSource : mcastRouteOpt.get()) {
mcastGroupNode.add(createMcastGroupNode(mcastRouteSource, context));
}
macastRouteTabNode.put(MCAST_GROUP, mcastGroupNode);
}
}
}
}
return macastRouteTabNode;
}
/**
* Method for creating the McastGroup object node.
*
* @param mcastRouteSource McastRouteSource
*/
private ObjectNode createMcastGroupNode(McastRouteSource mcastRouteSource, CodecContext context) {
final ObjectNode mcastGroupNode = context.mapper().createObjectNode();
final ObjectNode ingressNode = context.mapper().createObjectNode();
final ObjectNode egressNode = context.mapper().createObjectNode();
final ArrayNode jsonLabelIds = context.mapper().createArrayNode();
final String sAddr = mcastRouteSource.getSaddr().toString();
final String gAddr = mcastRouteSource.getGaddr().toString();
Optional<String> saddrOpt = Optional.ofNullable(sAddr);
Optional<String> gaddrOpt = Optional.ofNullable(gAddr);
//checking source address and group address are present.
if (saddrOpt.isPresent() && gaddrOpt.isPresent()) {
mcastGroupNode.put(SOURCE_ADDRESS, saddrOpt.get().toString());
mcastGroupNode.put(GROUP_ADDRESS, gaddrOpt.get().toString());
McastConnectPoint mcastIngCP = mcastRouteSource.getIngressPoint();
Optional<McastConnectPoint> mcastIngCPOpt = Optional.ofNullable(mcastIngCP);
//checking whether the ingress connection point is present.
if (mcastIngCPOpt.isPresent()) {
ingressNode.put(MCASTCONNECTPOINT, mcastConnectPoint(mcastIngCPOpt.get(), context));
}
mcastGroupNode.put(INGRESS_POINT , ingressNode);
Set<McastConnectPoint> mcastEgCPSet = mcastRouteSource.getEgressPoints();
Optional<Set<McastConnectPoint>> mcastEgCPOpt = Optional.ofNullable(mcastEgCPSet);
//checking whether the egress connection points are present.
if (mcastEgCPOpt.isPresent()) {
for (final McastConnectPoint mcastConnectPoint : mcastEgCPOpt.get()) {
jsonLabelIds.add(mcastConnectPoint(mcastConnectPoint, context));
}
}
egressNode.put(MCASTCONNECTPOINT , jsonLabelIds);
mcastGroupNode.put(EGRESS_POINT , egressNode);
}
return mcastGroupNode;
}
/**
* Method for creating the McastConnectPoint object node.
*
* @param mcastConnectPoint McastConnectPoint
* @param context CodecContext
* @return mcastCpNode ObjectNode
*/
private ObjectNode mcastConnectPoint(McastConnectPoint mcastConnectPoint, CodecContext context) {
final ObjectNode mcastCpNode = context.mapper().createObjectNode();
mcastCpNode.put(ELEMENTID , mcastConnectPoint.getConnectPoint().elementId().toString());
mcastCpNode.put(PORTNUMBER , mcastConnectPoint.getConnectPoint().port().toLong());
return mcastCpNode;
}
/**
* Decode json format and insert into the flow table.
*
* @param json ObjectNode
* @param context CodecContext
* @return mr McastRouteBase
*/
@Override
public McastRouteTable decode(ObjectNode json, CodecContext context) {
String macAddr = null;
String portNo = null;
String sAddr = json.path(SOURCE_ADDRESS).asText();
String gAddr = json.path(GROUP_ADDRESS).asText();
JsonNode inPntObjNode = (JsonNode) json.path(INGRESS_POINT);
JsonNode egPntArrNode = (JsonNode) json.path(EGRESS_POINT);
log.debug("sAddr :" + sAddr + " gAddr :" + gAddr + " inPntObjNode :" + inPntObjNode);
log.debug("egPntArrNode :" + egPntArrNode.toString());
McastRouteTable mrib = McastRouteTable.getInstance();
McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
Optional<JsonNode> inPntOpt = Optional.ofNullable(inPntObjNode);
if (inPntOpt.isPresent()) {
JsonNode inMcastCP = inPntOpt.get().path(MCASTCONNECTPOINT);
Optional<JsonNode> inCpOpt = Optional.ofNullable(inMcastCP);
if (inCpOpt.isPresent()) {
macAddr = inCpOpt.get().path(ELEMENTID).asText();
portNo = inCpOpt.get().path(PORTNUMBER).asText();
mr.addIngressPoint(macAddr + "/" + Long.parseLong(portNo));
}
}
Optional<JsonNode> egPntOpt = Optional.ofNullable(egPntArrNode);
if (egPntOpt.isPresent()) {
JsonNode egMcastCP = egPntOpt.get().path(MCASTCONNECTPOINT);
Optional<JsonNode> egMcCpOpt = Optional.ofNullable(egMcastCP);
if (egMcCpOpt.isPresent()) {
Iterator<JsonNode> egCpIt = egMcCpOpt.get().elements();
while (egCpIt.hasNext()) {
JsonNode egMcastCPObj = egCpIt.next();
Optional<JsonNode> egMcCpObOpt = Optional.ofNullable(egMcastCPObj);
if (egMcCpObOpt.isPresent()) {
macAddr = egMcCpObOpt.get().path(ELEMENTID).asText();
portNo = egMcCpObOpt.get().path(PORTNUMBER).asText();
log.debug("macAddr egPort : " + macAddr + " portNo egPort :" + portNo);
mr.addEgressPoint(macAddr + "/" + Long.parseLong(portNo), McastConnectPoint.JoinSource.STATIC);
}
}
}
}
return mrib;
}
}
/*
* 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.mfwd.impl;
import org.onosproject.net.ConnectPoint;
import java.util.EnumSet;
import java.util.Set;
/**
* Mulitcast ConnectPoint adds a variable to track the usage
* of these multicast endpoints.
*/
public class McastConnectPoint {
private ConnectPoint connectPoint;
public enum JoinSource {
STATIC, IGMP, PIM;
}
public EnumSet<JoinSource> interest = EnumSet.noneOf(JoinSource.class);
public McastConnectPoint(ConnectPoint cp) {
this.connectPoint = cp;
}
public McastConnectPoint(ConnectPoint cp, JoinSource src) {
this.connectPoint = cp;
interest.add(src);
}
public McastConnectPoint(String connectPoint, JoinSource src) {
ConnectPoint cp = ConnectPoint.deviceConnectPoint(connectPoint);
this.connectPoint = cp;
this.interest.add(src);
}
/**
* Get the connect point.
*
* @return connectPoint
*/
public ConnectPoint getConnectPoint() {
return connectPoint;
}
/**
* Get the sources of interest for this egressPoint.
*
* @return interest flags
*/
public Set<JoinSource> getInterest() {
return interest;
}
}
......@@ -15,6 +15,7 @@
*/
package org.onosproject.mfwd.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import org.apache.felix.scr.annotations.Activate;
......@@ -35,6 +36,8 @@ 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.mcast.MulticastRouteService;
import org.onosproject.net.mcast.McastRoute;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
......@@ -44,8 +47,12 @@ import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import java.util.ArrayList;
/**
* WORK-IN-PROGRESS: The multicast forwarding application using intent framework.
* The multicast forwarding component. This component is responsible for
* handling live multicast traffic by modifying multicast state and forwarding
* packets that do not yet have state installed.
*/
@Component(immediate = true)
public class McastForwarding {
......@@ -59,8 +66,12 @@ public class McastForwarding {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MulticastRouteService mcastRouteManager;
private ReactivePacketProcessor processor = new ReactivePacketProcessor();
private McastRouteTable mrib;
private static ApplicationId appId;
/**
......@@ -76,9 +87,9 @@ public class McastForwarding {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchIPDst(mcast);
packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
mrib = McastRouteTable.getInstance();
log.info("Started");
}
......@@ -137,8 +148,8 @@ public class McastForwarding {
}
IPv4 ip = (IPv4) ethPkt.getPayload();
IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
log.debug("Packet ({}, {}) has been punted\n" +
"\tingress port: {}\n",
......@@ -146,70 +157,37 @@ public class McastForwarding {
gaddr.toString(),
context.inPacket().receivedFrom().toString());
if (!mcast.contains(gaddr)) {
// Yikes, this is a bad group address
return;
}
if (mcast.contains(saddr)) {
// Yikes, the source address is multicast
// Don't allow PIM/IGMP packets to be handled here.
byte proto = ip.getProtocol();
if (proto == IPv4.PROTOCOL_PIM || proto == IPv4.PROTOCOL_IGMP) {
return;
}
IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
/*
* Do a best match lookup on the (s, g) of the packet. If an entry does
* not exist create one and store it's incoming connect point.
*
* The connect point is deviceId / portId that the packet entered
* the SDN network. This differs from traditional mcast where the
* ingress port would be a specific device.
*/
McastRoute entry = mrib.findBestMatch(spfx, gpfx);
if (entry == null || entry.getSaddr().address().isZero()) {
// TODO do we want to add a type for Mcast?
McastRoute mRoute = new McastRoute(spfx, gpfx, McastRoute.Type.STATIC);
/*
* Create an entry that we can fast drop.
*/
entry = mrib.addRoute(spfx, gpfx);
entry.addIngressPoint(context.inPacket().receivedFrom());
}
ConnectPoint ingress = mcastRouteManager.fetchSource(mRoute);
/*
* TODO: If we do not have an ingress or any egress connect points we
* should set up a fast drop entry.
*/
if (entry.getIngressPoint() == null) {
// An ingress port already exists. Log error.
if (ingress != null) {
log.error(McastForwarding.class.getSimpleName() + " received packet which already has a route.");
return;
} else {
//add ingress port
mcastRouteManager.addSource(mRoute, pkt.receivedFrom());
}
if (entry.getEgressPoints().isEmpty()) {
return;
}
/*
* This is odd, we should not have received a punted packet if an
* intent was installed unless the intent was not installed
* correctly. However, we are seeing packets get punted after
* the intent has been installed.
*
* Therefore we are going to forward the packets even if they
* should have already been forwarded by the intent fabric.
*/
if (entry.getIntentKey() != null) {
ArrayList<ConnectPoint> egressList = (ArrayList<ConnectPoint>) mcastRouteManager.fetchSinks(mRoute);
//If there are no egress ports set return, otherwise forward the packets to their expected port.
if (egressList.size() == 0) {
return;
}
entry.setIntent();
McastIntentManager im = McastIntentManager.getInstance();
im.setIntent(entry);
entry.incrementPuntCount();
// Send the pack out each of the egress devices & port
forwardPacketToDst(context, entry);
forwardPacketToDst(context, egressList);
}
}
......@@ -217,12 +195,12 @@ public class McastForwarding {
* Forward the packet to it's multicast destinations.
*
* @param context The packet context
* @param entry The multicast route entry matching this packet
* @param egressList The list of egress ports which the multicast packet is intended for.
*/
private void forwardPacketToDst(PacketContext context, McastRoute entry) {
private void forwardPacketToDst(PacketContext context, ArrayList<ConnectPoint> egressList) {
// Send the pack out each of the respective egress ports
for (ConnectPoint egress : entry.getEgressConnectPoints()) {
for (ConnectPoint egress : egressList) {
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(egress.port()).build();
......@@ -234,4 +212,19 @@ public class McastForwarding {
packetService.emit(packet);
}
}
public static McastRoute createStaticRoute(String source, String group) {
checkNotNull(source, "Must provide a source");
checkNotNull(group, "Must provide a group");
IpPrefix ipSource = IpPrefix.valueOf(source);
IpPrefix ipGroup = IpPrefix.valueOf(group);
return createStaticcreateRoute(ipSource, ipGroup);
}
public static McastRoute createStaticcreateRoute(IpPrefix source, IpPrefix group) {
checkNotNull(source, "Must provide a source");
checkNotNull(group, "Must provide a group");
McastRoute.Type type = McastRoute.Type.STATIC;
return new McastRoute(source, group, type);
}
}
......
/*
* 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.mfwd.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
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.onosproject.net.intent.Intent;
import org.onosproject.net.intent.SinglePointToMultiPointIntent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
@Component(immediate = true)
@Service(value = org.onosproject.mfwd.impl.McastIntentManager.class)
public class McastIntentManager {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
private static McastIntentManager instance;
public McastIntentManager() {
instance = this;
}
/**
* Active this component.
*/
@Activate
public void activate() { }
/**
* Deactivate this component.
*/
@Deactivate
public void deactivate() {
withdrawAllIntents();
}
/**
* Get instance of this intentManager.
*
* @return the instance of this intent manager.
*/
public static McastIntentManager getInstance() {
if (instance == null) {
instance = new McastIntentManager();
}
return instance;
}
/**
* Install the PointToMultipoint forwarding intent.
*
* @param mroute multicast route entry
* @return the intent that has been set or null otherwise
*/
public SinglePointToMultiPointIntent setIntent(McastRoute mroute) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
if (mroute.getIngressPoint() == null) {
return null;
}
/*
* Match the group AND source addresses. We will also check ether type to
* determine if we are doing ipv4 or ipv6.
*
* If we really wanted to be pendantic we could put in a
* condition to make sure the ethernet MAC address was also
* mcast.
*/
selector.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(mroute.getGaddr())
.matchIPSrc(mroute.getSaddr());
SinglePointToMultiPointIntent.Builder builder = SinglePointToMultiPointIntent.builder()
.appId(McastForwarding.getAppId())
.selector(selector.build())
.treatment(treatment)
.ingressPoint(mroute.getIngressPoint().getConnectPoint());
// allowing intent to be pushed without egress points means we can drop packets.
if (!mroute.getEgressPoints().isEmpty()) {
builder.egressPoints(mroute.getEgressConnectPoints());
}
SinglePointToMultiPointIntent intent = builder.build();
intentService.submit(intent);
mroute.setDirty(false);
return intent;
}
/**
* Withdraw the intent represented by this route.
*
* @param mroute the mcast route whose intent we want to remove
*/
public void withdrawIntent(McastRoute mroute) {
Intent intent = intentService.getIntent(mroute.getIntentKey());
intentService.withdraw(intent);
mroute.setDirty(false);
}
/**
* Withdraw all intents.
*
* This will be called from the deactivate method so we don't leave
* a mess behind us after we leave.
*/
public void withdrawAllIntents() {
for (Intent intent : intentService.getIntents()) {
intentService.withdraw(intent);
}
}
}
/*
* 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.mfwd.impl;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.SinglePointToMultiPointIntent;
import java.util.Set;
/**
* This McastRouteBase interface is implemented by the McastRouteBase class which
* in turn acts as the base class for both the McastRouteGroup and McastRouteSource.
*/
interface McastRoute {
/**
* Gets the group addresses.
*
* @return group address
*/
public IpPrefix getGaddr();
/**
* Gets the source address.
*
* @return the source address
*/
public IpPrefix getSaddr();
/**
* Determines if this is an IPv4 multicast route.
*
* @return true if it is an IPv4 route
*/
public boolean isIp4();
/**
* Determines if this is an IPv6 multicast route.
*
* @return true if it is an IPv6 route
*/
public boolean isIp6();
/**
* Get the dirty state.
*
* @return whether this route is dirty or not.
*/
public boolean getDirty();
/**
* Set the dirty state to indicate that something changed.
* This may require an update to the flow tables (intents).
*
* @param dirty set the dirty bit
*/
public void setDirty(boolean dirty);
/**
* Add the ingress ConnectPoint.
*
* @param cpstr string representing a ConnectPoint
* @return whether ingress has been added, only add if ingressPoint is null
*/
public boolean addIngressPoint(String cpstr);
/**
* Add the ingress ConnectPoint.
*
* @param cp the ConnectPoint of incoming traffic.
* @return whether ingress has been added, only add if ingressPoint is null
*/
public boolean addIngressPoint(ConnectPoint cp);
/**
* Get the ingress connect point.
*
* @return the ingress connect point
*/
public McastConnectPoint getIngressPoint();
/**
* Add an egress connect point.
*
* @param cp the egress McastConnectPoint to be added
* @return return the McastConnectPoint
*/
public McastConnectPoint addEgressPoint(ConnectPoint cp);
/**
* Add an egress connect point.
*
* @param connectPoint deviceId/portNum
* @return return the McastConnectPoint
*/
public McastConnectPoint addEgressPoint(String connectPoint);
/**
* Add an egress connect point.
*
* @param cp the egress McastConnectPoint to be added
* @param interest the protocol that has shown interest in this route
* @return return the McastConnectPoint
*/
public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest);
/**
* Add an egress connect point.
*
* @param connectPoint deviceId/portNum
* @param interest the protocol that has shown interest in this route
* @return return the McastConnectPoint
*/
public McastConnectPoint addEgressPoint(String connectPoint, McastConnectPoint.JoinSource interest);
/**
* Get the egress connect points.
*
* @return a set of egress connect points
*/
public Set<McastConnectPoint> getEgressPoints();
/**
* Get the egress connect points.
*
* @return a set of egress connect points
*/
public Set<ConnectPoint> getEgressConnectPoints();
/**
* Find the egress connect point if it exists.
*
* @param cp ConnectPoint to search for
* @return the connect point when found, null otherwise.
*/
public McastConnectPoint findEgressConnectPoint(ConnectPoint cp);
/**
* remove Interest from a McastConnectPoint.
*
* @param mcp connect point.
* @param interest the protocol interested in this multicast stream
* @return whether or not interest was removed
*/
public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest);
/**
* Increment the punt count.
*/
public void incrementPuntCount();
/**
* Get the punt count.
*
* @return the punt count
*/
public int getPuntCount();
/**
* Have the McastIntentManager create an intent, attempt to
* install the intent and then save the key.
*/
public void setIntent();
/**
* Set the Intent key.
*
* @param intent intent
*/
public void setIntent(SinglePointToMultiPointIntent intent);
/**
* Withdraw the intent if it has been installed.
*/
public void withdrawIntent();
/**
* Get the intent key.
*
* @return the intentKey
*/
public Key getIntentKey();
/**
* Pretty print the the route.
*
* @return a pretty string
*/
public String toString();
}
\ 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.mfwd.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.intent.SinglePointToMultiPointIntent;
import org.onosproject.net.intent.Key;
import java.util.Set;
import java.util.HashSet;
/**
* McastRouteBase base class for McastRouteGroup and McastRouteSource.
*/
public class McastRouteBase implements McastRoute {
protected final IpPrefix gaddr;
protected final IpPrefix saddr;
protected McastConnectPoint ingressPoint;
protected Set<McastConnectPoint> egressPoints;
protected boolean isGroup = false;
protected boolean dirty = false;
/**
* How may times has this packet been punted.
*/
private int puntCount = 0;
/**
* If the intentKey is null that means no intent has
* been installed.
*/
protected Key intentKey = null;
/**
* Create a multicast route. This is the parent class for both the Group
* and the source.
*
* @param saddr source address
* @param gaddr multicast group address
*/
public McastRouteBase(String saddr, String gaddr) {
this.gaddr = IpPrefix.valueOf(checkNotNull(gaddr));
if (saddr == null || saddr.equals("*")) {
this.saddr = IpPrefix.valueOf(0, 0);
} else {
this.saddr = IpPrefix.valueOf(checkNotNull(gaddr));
}
this.init();
}
/**
* Create a multicast group table entry.
* @param gaddr multicast group address
*/
public McastRouteBase(String gaddr) {
this("*", gaddr);
}
/**
* Set the source and group address value of a (*, G) group.
*
* @param gpfx the group prefix address
*/
public McastRouteBase(IpPrefix gpfx) {
this(IpPrefix.valueOf(0, 0), gpfx);
}
/**
* Create a multicast route constructor.
*
* @param saddr source address
* @param gaddr group address
*/
public McastRouteBase(IpPrefix saddr, IpPrefix gaddr) {
this.saddr = checkNotNull(saddr);
this.gaddr = checkNotNull(gaddr);
this.init();
}
private void init() {
this.isGroup = (this.saddr.prefixLength() == 0);
this.ingressPoint = null;
this.egressPoints = new HashSet();
}
/**
* Get the multicast group address.
*
* @return the multicast group address
*/
@Override
public IpPrefix getGaddr() {
return gaddr;
}
/**
* Get the multicast source address.
*
* @return the multicast source address
*/
@Override
public IpPrefix getSaddr() {
return saddr;
}
/**
* Is this an IPv4 multicast route.
*
* @return true if it is an IPv4 route
*/
@Override
public boolean isIp4() {
return gaddr.isIp4();
}
/**
* Is this an IPv6 multicast route.
*
* @return true if it is an IPv6 route
*/
@Override
public boolean isIp6() {
return gaddr.isIp6();
}
/**
* Is this a multicast group route?
*
* @return true if it is a multicast group route.
*/
public boolean isGroup() {
return isGroup;
}
/**
* @return true if this is (S, G) false if it (*, G).
*/
public boolean isSource() {
return (!isGroup);
}
/**
* Get the dirty state.
*
* @return whether this route is dirty or not.
*/
public boolean getDirty() {
return this.dirty;
}
/**
* Set the dirty state to indicate that something changed.
* This may require an update to the flow tables (intents).
*
* @param dirty set the dirty bit
*/
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
/**
* Add an ingress point to this route.
*
* @param ingress incoming connect point
* @return whether ingress has been added, only add if ingressPoint is null
*/
public boolean addIngressPoint(ConnectPoint ingress) {
// Do NOT add the ingressPoint if it is not null.
if (this.ingressPoint != null) {
// TODO: Log an warning.
return false;
}
this.ingressPoint = new McastConnectPoint(checkNotNull(ingress));
setDirty(true);
return true;
}
/**
* Add or modify the ingress connect point.
*
* @param connectPoint string switch device Id
* @return whether ingress has been added, only add if ingressPoint is null
*/
public boolean addIngressPoint(String connectPoint) {
if (this.ingressPoint != null) {
// TODO: log a warning.
return false;
}
ConnectPoint cp = ConnectPoint.deviceConnectPoint(checkNotNull(connectPoint));
return this.addIngressPoint(cp);
}
/**
* Get the ingress McastConnectPoint.
*
* @return the ingress McastConnectPoint
*/
public McastConnectPoint getIngressPoint() {
return this.ingressPoint;
}
/**
* Add an egress McastConnectPoint.
*
* @param cp egress connect point
* @return return the McastConnectPoint
*/
public McastConnectPoint addEgressPoint(ConnectPoint cp) {
McastConnectPoint mcp = this.findEgressConnectPoint(cp);
if (mcp == null) {
mcp = new McastConnectPoint(checkNotNull(cp));
egressPoints.add(mcp);
setDirty(true);
}
return mcp;
}
/**
* Add an egress connect point from a string.
*
* @param connectPoint string representing a connect point
* @return the MulticastConnectPoint
*/
public McastConnectPoint addEgressPoint(String connectPoint) {
checkNotNull(connectPoint);
return this.addEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
}
/**
* Add an egress McastConnectPoint.
*
* @param cp the egress connect point
* @param interest the source of interest for mcast traffic
*/
public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest) {
checkNotNull(cp);
checkNotNull(interest);
McastConnectPoint mcp = this.addEgressPoint(cp);
if (mcp != null) {
mcp.interest.add(interest);
setDirty(true);
}
return mcp;
}
/**
* Remove an egress from McastConnectPoint.
*
* @param connectPoint the egress connect point
* @return boolean result of removal
*/
public boolean removeEgressPoint(String connectPoint) {
checkNotNull(connectPoint);
return this.removeEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
}
/**
* Remove an egress from McastConnectPoint.
*
* @param cp the egress connect point
* @return boolean result of removal
*/
public boolean removeEgressPoint(ConnectPoint cp) {
boolean removed = false;
McastConnectPoint mcp = this.findEgressConnectPoint(checkNotNull(cp));
if (mcp != null) {
removed = egressPoints.remove(mcp);
setDirty(true);
}
return removed;
}
/**
* Add an egress McastConnectPoint.
*
* @param cpstr deviceId/port of the connect point
*/
public McastConnectPoint addEgressPoint(String cpstr, McastConnectPoint.JoinSource interest) {
checkNotNull(cpstr);
checkNotNull(interest);
return this.addEgressPoint(ConnectPoint.deviceConnectPoint(cpstr), interest);
}
/**
* Get egress connect points for the route.
*
* @return Set of egress connect points
*/
public Set<McastConnectPoint> getEgressPoints() {
return egressPoints;
}
/**
* Get egress McastConnectPoints points as ConnectPoints for intent system.
*
* @return Set of egress ConnectPoints
*/
public Set<ConnectPoint> getEgressConnectPoints() {
Set<ConnectPoint> cps = new HashSet<ConnectPoint>();
for (McastConnectPoint mcp : egressPoints) {
cps.add(mcp.getConnectPoint());
}
return cps;
}
/**
* Find the Multicast Connect Point that contains the ConnectPoint.
*
* @param cp the regular ConnectPoint to match
* @return the McastConnectPoint that contains cp or null if not found.
*/
public McastConnectPoint findEgressConnectPoint(ConnectPoint cp) {
for (McastConnectPoint mcp : this.egressPoints) {
if (mcp.getConnectPoint().equals(cp)) {
return mcp;
}
}
return null;
}
/**
* Remove specified interest from the given ConnectPoint.
*
* @param mcp connect point.
* @param interest the protocol interested in this multicast stream
* @return true if removed, false otherwise
*/
public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest) {
checkNotNull(mcp);
if (mcp.interest.contains(interest)) {
mcp.interest.remove(interest);
setDirty(true);
return true;
}
return false;
}
/**
* Get the number of times the packet has been punted.
*
* @return the punt count
*/
@Override
public int getPuntCount() {
return puntCount;
}
/**
* Increment the punt count.
*
* TODO: we need to handle wrapping.
*/
@Override
public void incrementPuntCount() {
puntCount++;
}
/**
* Have the McastIntentManager create and set the intent, then save the intent key.
*
* If we already have an intent, we will first withdraw the existing intent and
* replace it with a new one. This will support the case where the ingress connectPoint
* or group of egress connectPoints change.
*/
@Override
public void setIntent() {
if (this.intentKey != null) {
this.withdrawIntent();
}
McastIntentManager im = McastIntentManager.getInstance();
SinglePointToMultiPointIntent intent = im.setIntent(this);
this.intentKey = intent.key();
}
/**
* Set the Intent key.
*
* @param intent the multicast intent
*/
@Override
public void setIntent(SinglePointToMultiPointIntent intent) {
intentKey = intent.key();
}
/**
* Get the intent key represented by this route.
*
* @return intentKey
*/
@Override
public Key getIntentKey() {
return this.intentKey;
}
/**
* Withdraw the intent and set the key to null.
*/
@Override
public void withdrawIntent() {
if (intentKey == null) {
// nothing to withdraw
return;
}
McastIntentManager im = McastIntentManager.getInstance();
im.withdrawIntent(this);
this.intentKey = null;
}
/**
* Pretty Print this Multicast Route. Works for McastRouteSource and McastRouteGroup.
*
* @return pretty string of the multicast route
*/
@Override
public String toString() {
String out = String.format("(%s, %s)\n\t",
saddr.toString(), gaddr.toString());
out += "intent: ";
out += (intentKey == null) ? "not installed" : this.intentKey.toString();
out += "\n\tingress: ";
out += (ingressPoint == null) ? "NULL" : ingressPoint.getConnectPoint().toString();
out += "\n\tegress: {\n";
if (egressPoints != null && !egressPoints.isEmpty()) {
for (McastConnectPoint eg : egressPoints) {
out += "\t\t" + eg.getConnectPoint().toString() + "\n";
}
}
out += ("\t}\n");
out += ("\tpunted: " + this.getPuntCount() + "\n");
return out;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.mfwd.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.HashMap;
import org.onlab.packet.IpPrefix;
/**
* The McastRouteGroup extends the McastRouteBase class and serves two purposes:
* first it represents a (*, G) multicast route entry. Second it serves
* as a container for all (S, G) multicast route entries that belong
* to the same group address.
*/
public class McastRouteGroup extends McastRouteBase {
private HashMap<IpPrefix, McastRouteSource> sources;
/**
* Class constructor.
*
* @param gaddr - String representation of group address.
*/
public McastRouteGroup(String gaddr) {
super(checkNotNull(gaddr));
this.init();
}
/**
* Create a multicast group.
*
* @param gpfx - Group address
*/
public McastRouteGroup(IpPrefix gpfx) {
super(checkNotNull(gpfx));
this.init();
}
/**
* Common initialization used by constructors.
*/
private void init() {
this.sources = new HashMap();
super.isGroup = true;
}
/**
* Find a specific multicast source address for this group.
*
* @param saddr the source address
* @return the multicast source route or null if it does not exist
*/
public McastRouteSource findSource(IpPrefix saddr) {
return this.sources.get(checkNotNull(saddr));
}
/**
* Return the entire set of multicast sources for this group.
*
* @return the set of multicast sources
*/
public HashMap<IpPrefix, McastRouteSource> getSources() {
return this.sources;
}
/**
* Add a new McastRouteSource to this group.
*
* @param src the multicast source
*/
public void addSource(McastRouteSource src) {
checkNotNull(src);
this.sources.put(src.getSaddr(), src);
}
/**
* Remove the source with this specific IpPrefix from this group entry.
*
* @param spfx IP Prefix of the source to be removed
* @return the source route that was just removed
*/
public McastRouteSource removeSource(IpPrefix spfx) {
McastRouteSource src = this.sources.remove(spfx);
src.withdrawIntent();
return src;
}
/**
* Remove all sources from this.
*/
public void removeSources() {
for (McastRouteSource src : this.sources.values()) {
src.withdrawIntent();
this.sources.remove(src.getSaddr());
}
}
}
/*
* 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.mfwd.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import org.onlab.packet.IpPrefix;
/**
* This class represents and specific multicast senders source address. Objects from
* this class will belong to the sources collection of the multicast group.
*/
public class McastRouteSource extends McastRouteBase {
// A reference to our parent group
private McastRouteGroup group;
/**
* Create a multicast source with IpPrefixes.
*
* @param source the source address
* @param group the group address
*/
public McastRouteSource(IpPrefix source, IpPrefix group) {
super(checkNotNull(source), checkNotNull(group));
}
/**
* Set our parent multicast group.
*
* @param group the group this source belongs to
*/
public void setGroup(McastRouteGroup group) {
this.group = group;
}
}
\ 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.mfwd.impl;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.IpPrefix;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The Mcast Route Table holds all multicast state for the controller.
*
* State for IPv4 and IPv6 are maintained. The tables are sets of McastRouteGroup
* structures that represent (*, G) state with a series of egress ConnectPoints.
* Each (*, G) may also have a set of (S, G) that may have there own set of
* ingress and egress ConnectPoints.
*
* TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively.
*/
@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class)
public final class McastRouteTable {
/*
* Create a map of the McastGroups indexed by the multicast group prefix.
* We may choose to change the map data structure in to some form a radix trie
* depending on the type of real world usage we see.
*/
private final Map<IpPrefix, McastRouteGroup> mrib4;
private final Map<IpPrefix, McastRouteGroup> mrib6;
private static McastRouteTable instance = null;
private Boolean ipv6Enabled = false;
/**
* Create the two v4 & v6 tables.
*/
private McastRouteTable() {
mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
if (ipv6Enabled) {
mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
} else {
mrib6 = null;
}
}
/**
* Get the single instance of this multicast group address.
*
* @return the multicast route table
*/
public static McastRouteTable getInstance() {
if (instance == null) {
instance = new McastRouteTable();
}
return instance;
}
/**
* Get the IPv4 MRIB.
*
* @return the IPv4 MRIB
*/
public Map<IpPrefix, McastRouteGroup> getMrib4() {
return mrib4;
}
/**
* Get the IPv6 MRIB.
*
* @return Return the set of prefix keyed McastGroups
*/
public Map<IpPrefix, McastRouteGroup> getMrib6() {
return mrib6;
}
/**
* Save the McastRouteGroup in the address family appropriate mrib.
*
* @param group The McastRouteGroup to save
*/
private void storeGroup(McastRouteGroup group) {
if (group.isIp4()) {
mrib4.put(group.getGaddr(), group);
} else if (group.isIp6() && ipv6Enabled) {
mrib6.put(group.getGaddr(), group);
}
}
/**
* Remove the group.
*
* @param group the group to be removed
*/
private void removeGroup(McastRouteGroup group) {
IpPrefix gpfx = group.getGaddr();
if (gpfx.isIp4()) {
mrib4.remove(gpfx);
} else if (gpfx.isIp6() && ipv6Enabled) {
mrib6.remove(gpfx);
}
}
/**
* Add a multicast route to the MRIB. This function will.
*
* @param saddr source address * or x.x.x.x or x.x.x.x/y
* @param gaddr group address x.x.x.x or x.x.x.x/y
* @return the multicast route
*/
public McastRouteBase addRoute(String saddr, String gaddr) {
IpPrefix gpfx = IpPrefix.valueOf(gaddr);
IpPrefix spfx = IpPrefix.valueOf(0, 0);
if (saddr != null && !saddr.equals("*")) {
spfx = IpPrefix.valueOf(saddr);
}
return addRoute(spfx, gpfx);
}
/**
* Add a multicast route to the MRIB. This function will store either
* (S, G) or (*, G) in the mrib if an entry does not already exist. If
* an entry does exist it is returned to the caller.
*
* Every (S, G) is stored as part of it's parent group entry which also represents
* (*, G) routes. In the case of a (S, G) we will also create the (*, G) entry if needed
* then save the (S, G) to the (*, G).
*
* @param spfx the source prefix
* @param gpfx the group prefix
* @return the resulting McastRouteSource or McastRouteGroup accordingly.
*/
public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) {
/**
* If a group route (*, g) does not exist we will need to make so we
* can start attaching our sources to the group entry.
*/
McastRouteGroup group = findMcastGroup(gpfx);
if (group == null) {
group = new McastRouteGroup(gpfx);
// Save it for later
if (gpfx.isIp4()) {
this.mrib4.put(gpfx, group);
} else if (gpfx.isIp6() && ipv6Enabled) {
this.mrib6.put(gpfx, group);
}
}
/**
* If the source prefix length is 0 then we have our (*, g) entry, we can
* just return now.
*/
if (spfx.prefixLength() == 0) {
return group;
}
// See if the source already exists. If so just return it.
McastRouteSource source = group.findSource(spfx);
if (source != null) {
return source;
}
/**
* We have the group but no source. We need to create the source then add it
* to the group.
*/
source = new McastRouteSource(spfx, gpfx);
// Have the source save it's parent
source.setGroup(group);
// Save this source as part of this group
group.addSource(source);
return source;
}
/**
* Delete a specific egress from the MRIB.
*
* @param saddr source address * or x.x.x.x or x.x.x.x/y
* @param gaddr group address x.x.x.x or x.x.x.x/y
* @param egress group address x.x.x.x or x.x.x.x/y
* @return boolean if egress was deleted
*/
public boolean removeEgress(String saddr, String gaddr, String egress) {
IpPrefix gpfx = IpPrefix.valueOf(gaddr);
IpPrefix spfx = IpPrefix.valueOf(0, 0);
if (saddr != null && !saddr.equals("*")) {
spfx = IpPrefix.valueOf(saddr);
}
McastRouteSource src = (McastRouteSource) findBestMatch(spfx, gpfx);
boolean removed = src.removeEgressPoint(egress);
if (removed) {
src.setIntent();
}
return removed;
}
/**
* Delete a multicast route from the MRIB.
*
* @param saddr source address * or x.x.x.x or x.x.x.x/y
* @param gaddr group address x.x.x.x or x.x.x.x/y
*/
public void removeRoute(String saddr, String gaddr) {
IpPrefix gpfx = IpPrefix.valueOf(gaddr);
IpPrefix spfx = IpPrefix.valueOf(0, 0);
if (saddr != null && !saddr.equals("*")) {
spfx = IpPrefix.valueOf(saddr);
}
removeRoute(spfx, gpfx);
}
/**
* Remove a multicast route.
*
* @param spfx the source prefix
* @param gpfx the group prefix
*/
public void removeRoute(IpPrefix spfx, IpPrefix gpfx) {
/**
* If a group route (*, g) does not exist we will need to make so we
* can start attaching our sources to the group entry.
*/
McastRouteGroup group = findMcastGroup(gpfx);
if (group == null) {
// The group does not exist, we can't remove it.
return;
}
/**
* If the source prefix length is 0 then we have a (*, g) entry, which
* means we will remove this group and all of it's sources. We will
* also withdraw it's intent if need be.
*/
if (spfx.prefixLength() > 0) {
group.removeSource(spfx);
/*
* Now a little house keeping. If this group has no more sources
* nor egress connectPoints git rid of it.
*/
if (group.getSources().size() == 0 &&
group.getEgressPoints().size() == 0) {
removeGroup(group);
}
} else {
// Group remove has been explicitly requested.
group.removeSources();
group.withdrawIntent();
removeGroup(group);
}
}
/**
* Find the specific multicast group entry.
*
* @param group the group address
* @return McastRouteGroup the multicast (*, G) group route
*/
public McastRouteGroup findMcastGroup(IpPrefix group) {
McastRouteGroup g = null;
if (group.isIp4()) {
g = mrib4.get(group);
} else if (group.isIp6() && ipv6Enabled) {
g = mrib6.get(group);
}
return g;
}
/**
* Find the multicast (S, G) entry if it exists.
*
* @param saddr the source address
* @param gaddr the group address
* @return The multicast source route entry if it exists, null if it does not.
*/
public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) {
McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr));
if (grp == null) {
return null;
}
return grp.findSource(saddr);
}
/**
* This will first look up a Group entry. If no group entry was found null will
* be returned. If the group entry has been found we will then look up the (s, g) entry.
* If the (s, g) entry has been found, that will be returned. If no (s, g) was found
* the (*, g) group entry will be returned.
*
* @param saddr the source address
* @param gaddr the group address
* @return return the best matching McastRouteSource or McastRouteGroup
*/
public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) {
McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr));
if (grp == null) {
return null;
}
// Found a group now look for a source
McastRouteSource src = grp.findSource(checkNotNull(saddr));
if (src == null) {
return grp;
}
return src;
}
/**
* Print out the multicast route table in it's entirety.
*
* TODO: Eventually we will have to implement paging and how to handle large tables.
* @return String
*/
public String printMcastRouteTable() {
String out = this.toString() + "\n";
for (McastRouteGroup grp : mrib4.values()) {
out += grp.toString() + "\n";
for (McastRouteSource src : grp.getSources().values()) {
out += src.toString() + "\n";
}
}
return out;
}
/**
* Print out a summary of groups in the MRIB.
*
* @return String
*/
public String toString() {
String out = "Mcast Route Table: ";
out += mrib4.size() + " IPv4 Multicast Groups\n";
if (ipv6Enabled) {
out += mrib6.size() + " IPv6 Multicast Groups\n";
}
return out;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.mfwd.rest;
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.onosproject.mfwd.impl.McastConnectPoint;
import org.onosproject.mfwd.impl.McastRouteTable;
import org.onosproject.mfwd.impl.McastRouteBase;
import org.onosproject.mfwd.impl.MRibCodec;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Rest API for Multicast Forwarding.
*/
@Path("mcast")
public class McastResource extends AbstractWebResource {
private final Logger log = getLogger(getClass());
private static final String SOURCE_ADDRESS = "sourceAddress";
private static final String GROUP_ADDRESS = "groupAddress";
private static final String INGRESS_POINT = "ingressPoint";
private static final String EGRESS_POINT = "egressPoint";
private static final String MCAST_GROUP = "mcastGroup";
/**
* Retrieve the multicast route table.
*
* @return the multicast route table.
* @throws IOException if an error occurs
*/
@Path("show")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response showAll() throws IOException {
McastRouteTable mrt = McastRouteTable.getInstance();
ObjectNode pushContent = new MRibCodec().encode(mrt , this);
return ok(pushContent.toString()).build();
}
/**
* Static join a multicast flow.
*
* @param sAddr source address to join
* @param gAddr group address to join
* @param ports ingress and egress ConnectPoints to join
* @return the Result of the join
* @throws IOException if something failed with the join command
*/
@Path("/join")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public Response join(@QueryParam("src") String sAddr,
@QueryParam("grp") String gAddr,
@DefaultValue("") @QueryParam("ports") String ports)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
log.debug("Source IP Address: " + sAddr);
log.debug("Destination IP Address: " + gAddr);
log.debug("Ingress and Egress ports: " + ports);
String output = "Insertion Faild";
if (sAddr != null && gAddr != null && ports != null) {
String[] portArr = ports.split(",");
log.debug("Port Array Length: " + portArr.length);
McastRouteTable mrt = McastRouteTable.getInstance();
McastRouteBase mr = mrt.addRoute(sAddr, gAddr);
// Port format "of:0000000000000023/4"
log.debug("checking inside outer if: " + portArr.length);
if (mr != null && portArr != null && portArr.length > 0) {
String inCP = portArr[0];
log.debug("Ingress port provided: " + inCP);
mr.addIngressPoint(inCP);
for (int i = 1; i < portArr.length; i++) {
String egCP = portArr[i];
log.debug("Egress port provided: " + egCP);
mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
}
mrt.printMcastRouteTable();
output = "Successfully Inserted";
}
} else {
output = "Please Insert the rest uri correctly";
}
return Response.ok(output).build();
}
/**
* Delete multicast state.
*
* @param src address to be deleted
* @param grp address to be deleted
* @return status of delete if successful
*/
@Path("/delete")
@DELETE
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public Response removeMcastFlow(@QueryParam("src") String src,
@QueryParam("grp") String grp) {
String resp = "Failed to delete";
log.info("Source IP Address to delete: " + src);
log.info("Destination IP Address to delete: " + grp);
McastRouteTable mrt = McastRouteTable.getInstance();
if (src != null && grp != null) {
mrt.removeRoute(src, grp);
resp = "Deleted flow for src " + src + " and grp " + grp;
}
return Response.ok(resp).build();
}
}
/*
* 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.
*/
/**
* REST API for multicase forwarding.
*/
package org.onosproject.mfwd.rest;