Jonathan Hart
Committed by Gerrit Code Review

Remove old and unused Router code (incl. tests).

Added deprecated tags to a bunch of other objects that will be removed in a
future release

Change-Id: Iee80a260951070c1f280aa1a7755f06349aacb4f
......@@ -24,7 +24,10 @@ import java.util.Objects;
/**
* An entry in the Forwarding Information Base (FIB).
*
* @deprecated use RouteService instead
*/
@Deprecated
public class FibEntry {
private final IpPrefix prefix;
......
......@@ -19,7 +19,10 @@ import java.util.Collection;
/**
* A component that is able to process Forwarding Information Base (FIB) updates.
*
* @deprecated use RouteService instead
*/
@Deprecated
public interface FibListener {
/**
......
......@@ -21,7 +21,10 @@ import java.util.Objects;
/**
* Represents a change to the Forwarding Information Base (FIB).
*
* @deprecated use RouteService instead
*/
@Deprecated
public class FibUpdate {
/**
......
......@@ -25,7 +25,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents a route entry for an IP prefix.
*
* @deprecated use RouteService instead
*/
@Deprecated
public class RouteEntry {
private final IpPrefix prefix; // The IP prefix
private final IpAddress nextHop; // Next-hop IP address
......
......@@ -23,7 +23,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents a change in routing information.
*
* @deprecated use RouteService instead
*/
@Deprecated
public class RouteUpdate {
private final Type type; // The route update type
private final RouteEntry routeEntry; // The updated route entry
......
/*
* 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.routing.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
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.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.core.CoreService;
import org.onosproject.net.Host;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.routing.RouteSourceService;
import org.onosproject.routing.FibEntry;
import org.onosproject.routing.FibListener;
import org.onosproject.routing.FibUpdate;
import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RouteListener;
import org.onosproject.routing.RouteUpdate;
import org.onosproject.routing.RoutingService;
import org.onosproject.routing.config.RoutingConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.routing.RouteEntry.createBinaryString;
/**
* This class processes route updates and maintains a Routing Information Base
* (RIB). After route updates have been processed and next hops have been
* resolved, FIB updates are sent to any listening FIB components.
* <p>
* This implementation has been superseded by the RouteService and will be
* removed soon.
* </p>
*/
@Deprecated
@Component(immediate = true, enabled = false)
@Service
public class DefaultRouter implements RoutingService {
private static final Logger log = LoggerFactory.getLogger(DefaultRouter.class);
// Route entries are stored in a radix tree.
// The key in this tree is the binary string of prefix of the route.
private InvertedRadixTree<RouteEntry> ribTable4;
private InvertedRadixTree<RouteEntry> ribTable6;
// Stores all incoming route updates in a queue.
private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
new LinkedBlockingQueue<>();
// Next-hop IP address to route entry mapping for next hops pending MAC
// resolution
private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
// The IPv4 address to MAC address mapping
private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
private FibListener fibComponent;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RouteSourceService routeSourceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RoutingConfigurationService routingConfigurationService;
private ExecutorService bgpUpdatesExecutor;
private final HostListener hostListener = new InternalHostListener();
@Activate
public void activate() {
ribTable4 = new ConcurrentInvertedRadixTree<>(
new DefaultByteArrayNodeFactory());
ribTable6 = new ConcurrentInvertedRadixTree<>(
new DefaultByteArrayNodeFactory());
routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
HashMultimap.create());
coreService.registerApplication(ROUTER_APP_ID);
bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder()
.setNameFormat("rib-updates-%d").build());
}
@Deactivate
public void deactivate() {
log.debug("Stopped");
}
@Override
public void addFibListener(FibListener fibListener) {
this.fibComponent = checkNotNull(fibListener);
}
@Override
public void start() {
this.hostService.addListener(hostListener);
routeSourceService.start(new InternalRouteListener());
bgpUpdatesExecutor.execute(this::doUpdatesThread);
}
@Override
public void stop() {
routeSourceService.stop();
this.hostService.removeListener(hostListener);
// Stop the thread(s)
bgpUpdatesExecutor.shutdownNow();
synchronized (this) {
// Cleanup all local state
ribTable4 = new ConcurrentInvertedRadixTree<>(
new DefaultByteArrayNodeFactory());
ribTable6 = new ConcurrentInvertedRadixTree<>(
new DefaultByteArrayNodeFactory());
routeUpdatesQueue.clear();
routesWaitingOnArp.clear();
ip2Mac.clear();
}
}
/**
* Entry point for route updates.
*
* @param routeUpdates collection of route updates to process
*/
private void update(Collection<RouteUpdate> routeUpdates) {
try {
routeUpdatesQueue.put(routeUpdates);
} catch (InterruptedException e) {
log.error("Interrupted while putting on routeUpdatesQueue", e);
Thread.currentThread().interrupt();
}
}
/**
* Thread for handling route updates.
*/
private void doUpdatesThread() {
boolean interrupted = false;
try {
while (!interrupted) {
try {
Collection<RouteUpdate> routeUpdates =
routeUpdatesQueue.take();
processRouteUpdates(routeUpdates);
} catch (InterruptedException e) {
log.error("Interrupted while taking from updates queue", e);
interrupted = true;
} catch (Exception e) {
log.error("exception", e);
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
/**
* Gets all IPv4 routes from the RIB.
*
* @return all IPv4 routes from the RIB
*/
@Override
public Collection<RouteEntry> getRoutes4() {
Iterator<KeyValuePair<RouteEntry>> it =
ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
List<RouteEntry> routes = new LinkedList<>();
while (it.hasNext()) {
KeyValuePair<RouteEntry> entry = it.next();
routes.add(entry.getValue());
}
return routes;
}
/**
* Gets all IPv6 routes from the RIB.
*
* @return all IPv6 routes from the RIB
*/
@Override
public Collection<RouteEntry> getRoutes6() {
Iterator<KeyValuePair<RouteEntry>> it =
ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
List<RouteEntry> routes = new LinkedList<>();
while (it.hasNext()) {
KeyValuePair<RouteEntry> entry = it.next();
routes.add(entry.getValue());
}
return routes;
}
/**
* Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
* IPv6.
*
* @param prefix the prefix to use
* @return the route if found, otherwise null
*/
RouteEntry findRibRoute(IpPrefix prefix) {
String binaryString = createBinaryString(prefix);
if (prefix.isIp4()) {
// IPv4
return ribTable4.getValueForExactKey(binaryString);
}
// IPv6
return ribTable6.getValueForExactKey(binaryString);
}
/**
* Adds a route to the RIB. The route can be either IPv4 or IPv6.
*
* @param routeEntry the route entry to use
*/
void addRibRoute(RouteEntry routeEntry) {
if (routeEntry.isIp4()) {
// IPv4
ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
} else {
// IPv6
ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry);
}
}
/**
* Removes a route for a prefix from the RIB. The prefix can be either IPv4
* or IPv6.
*
* @param prefix the prefix to use
* @return true if the route was found and removed, otherwise false
*/
boolean removeRibRoute(IpPrefix prefix) {
if (prefix.isIp4()) {
// IPv4
return ribTable4.remove(createBinaryString(prefix));
}
// IPv6
return ribTable6.remove(createBinaryString(prefix));
}
/**
* Processes route updates.
*
* @param routeUpdates the route updates to process
*/
void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
synchronized (this) {
Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Collection<FibUpdate> fibUpdates = new LinkedList<>();
Collection<FibUpdate> fibWithdraws = new LinkedList<>();
for (RouteUpdate update : routeUpdates) {
switch (update.type()) {
case UPDATE:
FibEntry fib = processRouteAdd(update.routeEntry(),
withdrawPrefixes);
if (fib != null) {
fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
}
break;
case DELETE:
processRouteDelete(update.routeEntry(), withdrawPrefixes);
break;
default:
log.error("Unknown update Type: {}", update.type());
break;
}
}
withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
fibComponent.update(fibUpdates, fibWithdraws);
}
}
}
/**
* Processes adding a route entry.
* <p>
* The route entry is added to the radix tree. If there was an existing
* next hop for this prefix, but the next hop was different, then the
* old route entry is deleted.
* </p>
* <p>
* NOTE: Currently, we don't handle routes if the next hop is within the
* SDN domain.
* </p>
*
* @param routeEntry the route entry to add
* @param withdrawPrefixes the collection of accumulated prefixes whose
* intents will be withdrawn
* @return the corresponding FIB entry change, or null
*/
private FibEntry processRouteAdd(RouteEntry routeEntry,
Collection<IpPrefix> withdrawPrefixes) {
log.debug("Processing route add: {}", routeEntry);
// Find the old next-hop if we are updating an old route entry
IpAddress oldNextHop = null;
RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
if (oldRouteEntry != null) {
oldNextHop = oldRouteEntry.nextHop();
}
// Add the new route to the RIB
addRibRoute(routeEntry);
if (oldNextHop != null) {
if (oldNextHop.equals(routeEntry.nextHop())) {
return null; // No change
}
//
// Update an existing nexthop for the prefix.
// We need to remove the old flows for this prefix from the
// switches before the new flows are added.
//
withdrawPrefixes.add(routeEntry.prefix());
}
if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
// Route originated by local SDN domain
// We don't handle these here, reactive routing APP will handle
// these
log.debug("Own route {} to {}",
routeEntry.prefix(), routeEntry.nextHop());
return null;
}
//
// Find the MAC address of next hop router for this route entry.
// If the MAC address can not be found in ARP cache, then this prefix
// will be put in routesWaitingOnArp queue. Otherwise, generate
// a new route intent.
//
// Monitor the IP address for updates of the MAC address
hostService.startMonitoringIp(routeEntry.nextHop());
// Check if we know the MAC address of the next hop
MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
if (nextHopMacAddress == null) {
Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
if (!hosts.isEmpty()) {
nextHopMacAddress = hosts.iterator().next().mac();
}
if (nextHopMacAddress != null) {
ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
}
}
if (nextHopMacAddress == null) {
routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
return null;
}
return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
nextHopMacAddress);
}
/**
* Processes the deletion of a route entry.
* <p>
* The prefix for the routing entry is removed from radix tree.
* If the operation is successful, the prefix is added to the collection
* of prefixes whose intents that will be withdrawn.
* </p>
*
* @param routeEntry the route entry to delete
* @param withdrawPrefixes the collection of accumulated prefixes whose
* intents will be withdrawn
*/
private void processRouteDelete(RouteEntry routeEntry,
Collection<IpPrefix> withdrawPrefixes) {
log.debug("Processing route delete: {}", routeEntry);
boolean isRemoved = removeRibRoute(routeEntry.prefix());
if (isRemoved) {
//
// Only withdraw intents if an entry was actually removed from the
// tree. If no entry was removed, the <prefix, nexthop> wasn't
// there so it's probably already been removed and we don't
// need to do anything.
//
withdrawPrefixes.add(routeEntry.prefix());
}
routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
}
/**
* Signals the Router that the MAC to IP mapping has potentially been
* updated. This has the effect of updating the MAC address for any
* installed prefixes if it has changed, as well as installing any pending
* prefixes that were waiting for MAC resolution.
*
* @param ipAddress the IP address that an event was received for
* @param macAddress the most recently known MAC address for the IP address
*/
private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
log.debug("Received updated MAC info: {} => {}", ipAddress,
macAddress);
//
// We synchronize on "this" to prevent changes to the Radix tree
// while we're pushing intents. If the tree changes, the
// tree and the intents could get out of sync.
//
synchronized (this) {
Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Set<RouteEntry> routesToPush =
routesWaitingOnArp.removeAll(ipAddress);
for (RouteEntry routeEntry : routesToPush) {
// These will always be adds
RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
if (foundRouteEntry != null &&
foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
// We only push FIB updates if the prefix is still in the
// radix tree and the next hop is the same as our entry.
// The prefix could have been removed while we were waiting
// for the ARP, or the next hop could have changed.
submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
new FibEntry(routeEntry.prefix(),
ipAddress, macAddress)));
} else {
log.debug("{} has been revoked before the MAC was resolved",
routeEntry);
}
}
if (!submitFibEntries.isEmpty()) {
fibComponent.update(submitFibEntries, Collections.emptyList());
}
ip2Mac.put(ipAddress, macAddress);
}
}
/**
* Listener for host events.
*/
class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
log.debug("Received HostEvent {}", event);
Host host = event.subject();
switch (event.type()) {
case HOST_ADDED:
// FALLTHROUGH
case HOST_UPDATED:
for (IpAddress ipAddress : host.ipAddresses()) {
updateMac(ipAddress, host.mac());
}
break;
case HOST_REMOVED:
for (IpAddress ipAddress : host.ipAddresses()) {
ip2Mac.remove(ipAddress);
}
break;
default:
break;
}
}
}
/**
* Listener for route events.
*/
private class InternalRouteListener implements RouteListener {
@Override
public void update(Collection<RouteUpdate> routeUpdates) {
DefaultRouter.this.update(routeUpdates);
}
}
@Override
public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
RouteEntry routeEntry = null;
Iterable<RouteEntry> routeEntries;
if (ipAddress.isIp4()) {
routeEntries = ribTable4.getValuesForKeysPrefixing(
createBinaryString(
IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
} else {
routeEntries = ribTable6.getValuesForKeysPrefixing(
createBinaryString(
IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
}
if (routeEntries == null) {
return null;
}
Iterator<RouteEntry> it = routeEntries.iterator();
while (it.hasNext()) {
routeEntry = it.next();
}
return routeEntry;
}
}
/*
* Copyright 2015-present 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.routing.impl;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.routing.config.RoutingConfigurationService;
import org.onosproject.routing.impl.DefaultRouter.InternalHostListener;
import org.onosproject.routing.RouteSourceService;
import org.onosproject.routing.FibEntry;
import org.onosproject.routing.FibListener;
import org.onosproject.routing.FibUpdate;
import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RouteListener;
import org.onosproject.routing.RouteUpdate;
import java.util.Collections;
import static org.easymock.EasyMock.*;
/**
* This class tests adding a route and updating a route.
* The HostService module answers the MAC address asynchronously.
*/
public class RouterAsyncArpTest {
private HostService hostService;
private FibListener fibListener;
private RoutingConfigurationService routingConfigurationService;
private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber(1));
private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber(1));
private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber(1));
private DefaultRouter router;
private InternalHostListener internalHostListener;
@Before
public void setUp() throws Exception {
hostService = createMock(HostService.class);
routingConfigurationService =
createMock(RoutingConfigurationService.class);
RouteSourceService routeSourceService = createMock(RouteSourceService.class);
routeSourceService.start(anyObject(RouteListener.class));
routeSourceService.stop();
replay(routeSourceService);
fibListener = createMock(FibListener.class);
router = new DefaultRouter();
router.coreService = createNiceMock(CoreService.class);
router.hostService = hostService;
router.routingConfigurationService = routingConfigurationService;
router.routeSourceService = routeSourceService;
router.activate();
router.addFibListener(fibListener);
router.start();
internalHostListener = router.new InternalHostListener();
}
@After
public void tearDown() {
// Called during shutdown
reset(hostService);
hostService.removeListener(anyObject(HostListener.class));
router.stop();
}
/**
* Tests adding a route entry with asynchronous HostService replies.
*/
@Test
public void testRouteAdd() {
// Construct a route entry
IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
// Host service will reply with no hosts when asked
reset(hostService);
expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
Collections.emptySet()).anyTimes();
hostService.startMonitoringIp(IpAddress.valueOf("192.168.10.1"));
replay(hostService);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(false);
replay(routingConfigurationService);
// Initially when we add the route, no FIB update will be sent
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
verify(fibListener);
// Now when we send the event, we expect the FIB update to be sent
reset(fibListener);
FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
MacAddress.valueOf("00:00:00:00:00:01"));
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
replay(fibListener);
Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
new HostLocation(
SW1_ETH1.deviceId(),
SW1_ETH1.port(), 1),
Sets.newHashSet(IpAddress.valueOf("192.168.10.1")));
// Send in the host event
internalHostListener.event(
new HostEvent(HostEvent.Type.HOST_ADDED, host));
verify(fibListener);
}
/**
* Tests updating a route entry with asynchronous HostService replies.
*/
@Test
public void testRouteUpdate() {
// Add a route
testRouteAdd();
// Construct a route entry
IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
IpAddress nextHopIp = Ip4Address.valueOf("192.168.20.1");
RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
// Host service will reply with no hosts when asked
reset(hostService);
expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
Collections.emptySet()).anyTimes();
hostService.startMonitoringIp(IpAddress.valueOf("192.168.20.1"));
replay(hostService);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(false);
replay(routingConfigurationService);
// Initially when we add the route, the DELETE FIB update will be sent
// but the UPDATE FIB update will come later when the MAC is resolved
reset(fibListener);
fibListener.update(Collections.emptyList(), Collections.singletonList(new FibUpdate(
FibUpdate.Type.DELETE, new FibEntry(prefix, null, null))));
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
verify(fibListener);
// Now when we send the event, we expect the FIB update to be sent
reset(fibListener);
FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
MacAddress.valueOf("00:00:00:00:00:02"));
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
replay(fibListener);
Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
new HostLocation(
SW1_ETH1.deviceId(),
SW1_ETH1.port(), 1),
Sets.newHashSet(IpAddress.valueOf("192.168.20.1")));
// Send in the host event
internalHostListener.event(
new HostEvent(HostEvent.Type.HOST_ADDED, host));
verify(fibListener);
}
}
/*
* Copyright 2015-present 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.routing.impl;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.Ip6Prefix;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.routing.RouteSourceService;
import org.onosproject.routing.FibEntry;
import org.onosproject.routing.FibListener;
import org.onosproject.routing.FibUpdate;
import org.onosproject.routing.RouteEntry;
import org.onosproject.routing.RouteListener;
import org.onosproject.routing.RouteUpdate;
import org.onosproject.routing.config.RoutingConfigurationService;
import java.util.Collections;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* This class tests adding a route, updating a route, deleting a route,
* and adding a route whose next hop is the local BGP speaker.
* <p/>
* The HostService answers requests synchronously.
*/
public class RouterTest {
private HostService hostService;
private RoutingConfigurationService routingConfigurationService;
private FibListener fibListener;
private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber(1));
private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber(1));
private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber(1));
private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000004"),
PortNumber.portNumber(1));
private static final ConnectPoint SW5_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000005"),
PortNumber.portNumber(1));
private static final ConnectPoint SW6_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000006"),
PortNumber.portNumber(1));
private DefaultRouter router;
@Before
public void setUp() throws Exception {
setUpHostService();
routingConfigurationService =
createMock(RoutingConfigurationService.class);
RouteSourceService routeSourceService = createMock(RouteSourceService.class);
routeSourceService.start(anyObject(RouteListener.class));
routeSourceService.stop();
replay(routeSourceService);
fibListener = createMock(FibListener.class);
router = new DefaultRouter();
router.coreService = createNiceMock(CoreService.class);
router.hostService = hostService;
router.routingConfigurationService = routingConfigurationService;
router.routeSourceService = routeSourceService;
router.activate();
router.addFibListener(fibListener);
router.start();
}
@After
public void tearDown() {
router.stop();
}
/**
* Sets up the host service with details of some hosts.
*/
private void setUpHostService() {
hostService = createMock(HostService.class);
hostService.addListener(anyObject(HostListener.class));
expectLastCall().anyTimes();
IpAddress host1Address = IpAddress.valueOf("192.168.10.1");
Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
new HostLocation(SW1_ETH1, 1),
Sets.newHashSet(host1Address));
expect(hostService.getHostsByIp(host1Address))
.andReturn(Sets.newHashSet(host1)).anyTimes();
hostService.startMonitoringIp(host1Address);
expectLastCall().anyTimes();
IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
new HostLocation(SW2_ETH1, 1),
Sets.newHashSet(host2Address));
expect(hostService.getHostsByIp(host2Address))
.andReturn(Sets.newHashSet(host2)).anyTimes();
hostService.startMonitoringIp(host2Address);
expectLastCall().anyTimes();
// Next hop on a VLAN
IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
new HostLocation(SW3_ETH1, 1),
Sets.newHashSet(host3Address));
expect(hostService.getHostsByIp(host3Address))
.andReturn(Sets.newHashSet(host3)).anyTimes();
hostService.startMonitoringIp(host3Address);
expectLastCall().anyTimes();
IpAddress host4Address = IpAddress.valueOf("1000::1");
Host host4 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE,
new HostLocation(SW4_ETH1, 1),
Sets.newHashSet(host4Address));
expect(hostService.getHostsByIp(host4Address))
.andReturn(Sets.newHashSet(host4)).anyTimes();
hostService.startMonitoringIp(host4Address);
expectLastCall().anyTimes();
IpAddress host5Address = IpAddress.valueOf("2000::1");
Host host5 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:05"), VlanId.NONE,
new HostLocation(SW5_ETH1, 1),
Sets.newHashSet(host5Address));
expect(hostService.getHostsByIp(host5Address))
.andReturn(Sets.newHashSet(host5)).anyTimes();
hostService.startMonitoringIp(host5Address);
expectLastCall().anyTimes();
// Next hop on a VLAN
IpAddress host6Address = IpAddress.valueOf("3000::1");
Host host6 = new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:06"), VlanId.vlanId((short) 1),
new HostLocation(SW6_ETH1, 1),
Sets.newHashSet(host6Address));
expect(hostService.getHostsByIp(host6Address))
.andReturn(Sets.newHashSet(host6)).anyTimes();
hostService.startMonitoringIp(host6Address);
expectLastCall().anyTimes();
// Called during shutdown
hostService.removeListener(anyObject(HostListener.class));
replay(hostService);
}
/**
* Tests adding a IPv4 route entry.
*/
@Test
public void testIpv4RouteAdd() {
// Construct a route entry
IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
// Expected FIB entry
FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
MacAddress.valueOf("00:00:00:00:00:01"));
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
verify(fibListener);
}
/**
* Tests adding a IPv6 route entry.
*/
@Test
public void testIpv6RouteAdd() {
// Construct a route entry
IpPrefix prefix = Ip6Prefix.valueOf("4000::/64");
IpAddress nextHopIp = Ip6Address.valueOf("1000::1");
RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
// Expected FIB entry
FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
MacAddress.valueOf("00:00:00:00:00:04"));
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
verify(fibListener);
}
/**
* Tests updating a IPv4 route entry.
*/
@Test
public void testRouteUpdate() {
// Firstly add a route
testIpv4RouteAdd();
// Route entry with updated next hop for the original prefix
RouteEntry routeEntryUpdate = new RouteEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
Ip4Address.valueOf("192.168.20.1"));
// The old FIB entry will be withdrawn
FibEntry withdrawFibEntry = new FibEntry(
Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
// A new FIB entry will be added
FibEntry updateFibEntry = new FibEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
Ip4Address.valueOf("192.168.20.1"),
MacAddress.valueOf("00:00:00:00:00:02"));
reset(fibListener);
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, updateFibEntry)),
Collections.singletonList(new FibUpdate(
FibUpdate.Type.DELETE, withdrawFibEntry)));
replay(fibListener);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(false);
replay(routingConfigurationService);
router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
RouteUpdate.Type.UPDATE, routeEntryUpdate)));
verify(fibListener);
}
/**
* Tests updating a IPv6 route entry.
*/
@Test
public void testIpv6RouteUpdate() {
// Firstly add a route
testIpv6RouteAdd();
// Route entry with updated next hop for the original prefix
RouteEntry routeEntryUpdate = new RouteEntry(
Ip6Prefix.valueOf("4000::/64"),
Ip6Address.valueOf("2000::1"));
// The old FIB entry will be withdrawn
FibEntry withdrawFibEntry = new FibEntry(
Ip6Prefix.valueOf("4000::/64"), null, null);
// A new FIB entry will be added
FibEntry updateFibEntry = new FibEntry(
Ip6Prefix.valueOf("4000::/64"),
Ip6Address.valueOf("2000::1"),
MacAddress.valueOf("00:00:00:00:00:05"));
reset(fibListener);
fibListener.update(Collections.singletonList(new FibUpdate(
FibUpdate.Type.UPDATE, updateFibEntry)),
Collections.singletonList(new FibUpdate(
FibUpdate.Type.DELETE, withdrawFibEntry)));
replay(fibListener);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(false);
replay(routingConfigurationService);
router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
RouteUpdate.Type.UPDATE, routeEntryUpdate)));
verify(fibListener);
}
/**
* Tests deleting a IPv4 route entry.
*/
@Test
public void testIpv4RouteDelete() {
// Firstly add a route
testIpv4RouteAdd();
RouteEntry deleteRouteEntry = new RouteEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
Ip4Address.valueOf("192.168.10.1"));
FibEntry deleteFibEntry = new FibEntry(
Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
reset(fibListener);
fibListener.update(Collections.emptyList(), Collections.singletonList(
new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry)));
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry)));
verify(fibListener);
}
/**
* Tests deleting a IPv6 route entry.
*/
@Test
public void testIpv6RouteDelete() {
// Firstly add a route
testIpv6RouteAdd();
RouteEntry deleteRouteEntry = new RouteEntry(
Ip6Prefix.valueOf("4000::/64"),
Ip6Address.valueOf("1000::1"));
FibEntry deleteFibEntry = new FibEntry(
Ip6Prefix.valueOf("4000::/64"), null, null);
reset(fibListener);
fibListener.update(Collections.emptyList(), Collections.singletonList(
new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry)));
replay(fibListener);
router.processRouteUpdates(Collections.singletonList(
new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry)));
verify(fibListener);
}
/**
* Tests adding a IPv4 route whose next hop is the local BGP speaker.
*/
@Test
public void testIpv4LocalRouteAdd() {
// Construct a route entry, the next hop is the local BGP speaker
RouteEntry routeEntry = new RouteEntry(
Ip4Prefix.valueOf("1.1.1.0/24"),
Ip4Address.valueOf("0.0.0.0"));
// No methods on the FIB listener should be called
replay(fibListener);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(true);
replay(routingConfigurationService);
// Call the processRouteUpdates() method in Router class
RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
routeEntry);
router.processRouteUpdates(Collections.singletonList(routeUpdate));
// Verify
assertEquals(1, router.getRoutes4().size());
assertTrue(router.getRoutes4().contains(routeEntry));
verify(fibListener);
}
/**
* Tests adding a IPv6 route whose next hop is the local BGP speaker.
*/
@Test
public void testIpv6LocalRouteAdd() {
// Construct a route entry, the next hop is the local BGP speaker
RouteEntry routeEntry = new RouteEntry(
Ip6Prefix.valueOf("4000::/64"),
Ip6Address.valueOf("::"));
// No methods on the FIB listener should be called
replay(fibListener);
reset(routingConfigurationService);
expect(routingConfigurationService.isIpPrefixLocal(
anyObject(IpPrefix.class))).andReturn(true);
replay(routingConfigurationService);
// Call the processRouteUpdates() method in Router class
RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
routeEntry);
router.processRouteUpdates(Collections.singletonList(routeUpdate));
// Verify
assertEquals(1, router.getRoutes6().size());
assertTrue(router.getRoutes6().contains(routeEntry));
verify(fibListener);
}
}