blob: 1140c3a8f7a368ad1fe7a0df61e412aa78e9687d [file] [log] [blame]
/*
* 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;
}
}