Dual-homing probing improvements
(1) Active probing mechanism in the following two scenarios
(1-1) Probe all ports on the pair device within the same vlan (excluding the pair port) when the 1st location of a host is learnt
(1-2) Probe again when a device/port goes down and comes up again
* Introduce HostLocationProvingService
- DISCOVER mode: discover potential new locations
- VERIFY mode: verify old locations
* Can be enabled/disabled via component config
* Improve HostHandlerTest to test the probing behavior
(2) Fix an issue that redirection flow doesn't get installed after device re-connects
(3) Temporarily fix a race condition in HostHandler by adding a little bit delay
Change-Id: I33d3fe94a6ca491a88b8e06f65bef11447ead0bf
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 6ac25f3..1379e79 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -35,6 +35,7 @@
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.host.HostService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,6 +44,9 @@
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
@@ -51,8 +55,9 @@
* Handles host-related events.
*/
public class HostHandler {
-
private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
+ static final int HOST_MOVED_DELAY_MS = 1000;
+
protected final SegmentRoutingManager srManager;
private HostService hostService;
private FlowObjectiveService flowObjectiveService;
@@ -71,7 +76,8 @@
protected void init(DeviceId devId) {
hostService.getHosts().forEach(host ->
host.locations().stream()
- .filter(location -> location.deviceId().equals(devId))
+ .filter(location -> location.deviceId().equals(devId) ||
+ location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
.forEach(location -> processHostAddedAtLocation(host, location))
);
}
@@ -114,6 +120,10 @@
processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
ip, false));
+
+ if (srManager.activeProbing) {
+ probe(host, location, pairDeviceId, pairRemotePort);
+ }
});
}
});
@@ -162,8 +172,23 @@
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Set<HostLocation> newLocations = event.subject().locations();
Set<IpAddress> newIps = event.subject().ipAddresses();
- log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ if (prevLocations.size() == 1 && newLocations.size() == 2) {
+ log.debug("Delay event handling when host {}/{} moves from 1 to 2 locations", hostMac, hostVlanId);
+ ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.schedule(() ->
+ processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps),
+ HOST_MOVED_DELAY_MS, TimeUnit.MILLISECONDS);
+ } else {
+ processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps);
+ }
+ }
+
+ private void processHostMoved(MacAddress hostMac, VlanId hostVlanId, Set<HostLocation> prevLocations,
+ Set<IpAddress> prevIps, Set<HostLocation> newLocations, Set<IpAddress> newIps) {
+ log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
.collect(Collectors.toSet());
@@ -254,11 +279,12 @@
}
void processHostUpdatedEvent(HostEvent event) {
- MacAddress hostMac = event.subject().mac();
- VlanId hostVlanId = event.subject().vlan();
- Set<HostLocation> locations = event.subject().locations();
+ Host host = event.subject();
+ MacAddress hostMac = host.mac();
+ VlanId hostVlanId = host.vlan();
+ Set<HostLocation> locations = host.locations();
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
- Set<IpAddress> newIps = event.subject().ipAddresses();
+ Set<IpAddress> newIps = host.ipAddresses();
log.info("Host {}/{} is updated", hostMac, hostVlanId);
locations.stream().filter(srManager::isMasterOf).forEach(location -> {
@@ -273,25 +299,94 @@
// Use the pair link temporarily before the second location of a dual-homed host shows up.
// This do not affect single-homed hosts since the flow will be blocked in
// processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
- locations.forEach(location -> {
+ locations.forEach(location ->
srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
+ Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
+ Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
+
srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
// NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
// when the host is untagged
VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
- Sets.difference(prevIps, newIps).forEach(ip ->
+ ipsToRemove.forEach(ip ->
processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
);
- Sets.difference(newIps, prevIps).forEach(ip ->
+ ipsToAdd.forEach(ip ->
processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
);
+
+ if (srManager.activeProbing) {
+ probe(host, location, pairDeviceId, pairRemotePort);
+ }
});
}
- });
- });
+ })
+ );
+ }
+
+ /**
+ * When a non-pair port comes up, probe each host on the pair device if
+ * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
+ * (2) the host is untagged and the internal vlan is the same on the host port and current port.
+ *
+ * @param cp connect point
+ */
+ void processPortUp(ConnectPoint cp) {
+ if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
+ return;
+ }
+ if (srManager.activeProbing) {
+ srManager.getPairDeviceId(cp.deviceId())
+ .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
+ .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
+ .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
+ );
+ }
+ }
+
+ /**
+ * Checks if given host located on given device id matches VLAN config of current port.
+ *
+ * @param host host to check
+ * @param deviceId device id to check
+ * @param cp current connect point
+ * @return true if the host located at deviceId matches the VLAN config on cp
+ */
+ private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
+ VlanId internalVlan = srManager.getInternalVlanId(cp);
+ Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
+
+ return taggedVlan.contains(host.vlan()) ||
+ (internalVlan != null && host.locations().stream()
+ .filter(l -> l.deviceId().equals(deviceId))
+ .map(srManager::getInternalVlanId)
+ .anyMatch(internalVlan::equals));
+ }
+
+ /**
+ * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
+ *
+ * @param host host to probe
+ * @param location newly discovered host location
+ * @param pairDeviceId pair device id
+ * @param pairRemotePort pair remote port
+ */
+ private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
+ VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
+ srManager.getInternalVlanId(location) : host.vlan();
+ srManager.interfaceService.getInterfaces().stream()
+ .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
+ i.vlanUntagged().equals(vlanToProbe) ||
+ i.vlanNative().equals(vlanToProbe))
+ .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
+ .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
+ .forEach(i -> {
+ log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
+ srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
+ });
}
/**
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index d57a681..7a17d39 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.segmentrouting;
+
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
@@ -24,6 +25,8 @@
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.Modified;
+import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
@@ -35,6 +38,7 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -64,6 +68,7 @@
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostLocationProbingService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intf.Interface;
@@ -106,10 +111,12 @@
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -158,6 +165,9 @@
HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ HostLocationProbingService probingService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -187,6 +197,10 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
public InterfaceService interfaceService;
+ @Property(name = "activeProbing", boolValue = true,
+ label = "Enable active probing to discover dual-homed hosts.")
+ boolean activeProbing = true;
+
ArpHandler arpHandler = null;
IcmpHandler icmpHandler = null;
IpHandler ipHandler = null;
@@ -226,19 +240,19 @@
* Per device next objective ID store with (device id + destination set) as key.
* Used to keep track on MPLS group information.
*/
- EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
+ private EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
dsNextObjStore = null;
/**
* Per device next objective ID store with (device id + vlanid) as key.
* Used to keep track on L2 flood group information.
*/
- EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
+ private EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
vlanNextObjStore = null;
/**
* Per device next objective ID store with (device id + port + treatment + meta) as key.
* Used to keep track on L2 interface group and L3 unicast group information.
*/
- EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
+ private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
// Local store for all links seen and their present status, used for
@@ -323,7 +337,7 @@
public static final VlanId INTERNAL_VLAN = VlanId.vlanId((short) 4094);
@Activate
- protected void activate() {
+ protected void activate(ComponentContext context) {
appId = coreService.registerApplication(APP_NAME);
log.debug("Creating EC map nsnextobjectivestore");
@@ -392,6 +406,8 @@
"staleLinkAge", "15000");
compCfgService.preSetProperty("org.onosproject.net.host.impl.HostManager",
"allowDuplicateIps", "false");
+ compCfgService.registerProperties(getClass());
+ modified(context);
processor = new InternalPacketProcessor();
linkListener = new InternalLinkListener();
@@ -449,6 +465,7 @@
cfgService.unregisterConfigFactory(xConnectConfigFactory);
cfgService.unregisterConfigFactory(mcastConfigFactory);
cfgService.unregisterConfigFactory(pwaasConfigFactory);
+ compCfgService.unregisterProperties(getClass(), false);
hostService.removeListener(hostListener);
packetService.removeProcessor(processor);
@@ -472,6 +489,22 @@
log.info("Stopped");
}
+ @Modified
+ private void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ if (properties == null) {
+ return;
+ }
+
+ String strActiveProving = Tools.get(properties, "activeProbing");
+ boolean expectActiveProbing = Boolean.parseBoolean(strActiveProving);
+
+ if (expectActiveProbing != activeProbing) {
+ activeProbing = expectActiveProbing;
+ log.info("{} active probing", activeProbing ? "Enabling" : "Disabling");
+ }
+ }
+
@Override
public List<Tunnel> getTunnels() {
return tunnelHandler.getTunnels();
@@ -922,7 +955,7 @@
* true if the seen-link is up;
* false if the seen-link is down
*/
- Boolean isSeenLinkUp(Link link) {
+ private Boolean isSeenLinkUp(Link link) {
return seenLinks.get(link);
}
@@ -970,7 +1003,7 @@
* @return true if another unidirectional link exists in the reverse direction,
* has been seen-before and is up
*/
- public boolean isBidirectional(Link link) {
+ boolean isBidirectional(Link link) {
Link reverseLink = linkService.getLink(link.dst(), link.src());
if (reverseLink == null) {
return false;
@@ -989,7 +1022,7 @@
* @param link the infrastructure link being queried
* @return true if link should be avoided
*/
- public boolean avoidLink(Link link) {
+ boolean avoidLink(Link link) {
// XXX currently only avoids all pair-links. In the future can be
// extended to avoid any generic link
DeviceId src = link.src().deviceId();
@@ -1012,12 +1045,9 @@
return false;
}
- if (srcPort.equals(pairLocalPort) &&
+ return srcPort.equals(pairLocalPort) &&
link.dst().deviceId().equals(pairDev) &&
- link.dst().port().equals(pairRemotePort)) {
- return true;
- }
- return false;
+ link.dst().port().equals(pairRemotePort);
}
private class InternalPacketProcessor implements PacketProcessor {
@@ -1374,9 +1404,7 @@
private void processDeviceRemoved(Device device) {
dsNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
- .forEach(entry -> {
- dsNextObjStore.remove(entry.getKey());
- });
+ .forEach(entry -> dsNextObjStore.remove(entry.getKey()));
vlanNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> vlanNextObjStore.remove(entry.getKey()));
@@ -1456,6 +1484,7 @@
if (portUp) {
log.info("Device:EdgePort {}:{} is enabled in vlan: {}", device.id(),
port.number(), vlanId);
+ hostHandler.processPortUp(new ConnectPoint(device.id(), port.number()));
} else {
log.info("Device:EdgePort {}:{} is disabled in vlan: {}", device.id(),
port.number(), vlanId);
@@ -1509,7 +1538,7 @@
/**
* Reads network config and initializes related data structure accordingly.
*/
- public void configureNetwork() {
+ void configureNetwork() {
createOrUpdateDeviceConfiguration();
arpHandler = new ArpHandler(srManager);
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index b7d32a1..7ea430d 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -501,6 +501,20 @@
}
/**
+ * Returns all ports that has a subnet that contains any of the given IP addresses.
+ *
+ * @param ips a set of IP addresses
+ * @return a set of connect point that has a subnet that contains any of the given IP addresses
+ */
+ public Set<ConnectPoint> getPortByIps(Set<IpAddress> ips) {
+ return srManager.interfaceService.getInterfaces().stream()
+ .filter(intf -> intf.ipAddressesList().stream().anyMatch(intfAddress ->
+ ips.stream().anyMatch(ip -> intfAddress.subnetAddress().contains(ip))))
+ .map(Interface::connectPoint)
+ .collect(Collectors.toSet());
+ }
+
+ /**
* Returns the router ip address of segment router that has the
* specified ip address in its subnets.
*
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index 3b6d00a..cce5a31 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -28,6 +28,7 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.host.HostLocationProbingService;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
@@ -176,6 +177,8 @@
private static final Set<Interface> INTERFACES = Sets.newHashSet(INTF11, INTF12, INTF13, INTF21,
INTF22, INTF31, INTF32, INTF39, INTF41, INTF49);
+ private MockLocationProbingService mockLocationProbingService;
+
@Before
public void setUp() throws Exception {
// Initialize pairDevice and pairLocalPort config
@@ -207,6 +210,8 @@
srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
srManager.hostService = new MockHostService(HOSTS);
srManager.cfgService = mockNetworkConfigRegistry;
+ mockLocationProbingService = new MockLocationProbingService();
+ srManager.probingService = mockLocationProbingService;
hostHandler = new HostHandler(srManager);
@@ -336,15 +341,22 @@
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // Expect probe to be sent out on pair device
+ assertTrue(mockLocationProbingService.verifyProbe(host1, CP41, HostLocationProbingService.ProbeMode.DISCOVER));
// Add the second location of dual-homed host
// Expect: no longer use the pair link
hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
@@ -612,10 +624,16 @@
assertEquals(4, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
@@ -714,22 +732,29 @@
// Discover IP
// Expect: routing redirection should also work
- hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host1));
+ hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host1));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // Expect probe to be sent out on pair device
+ assertTrue(mockLocationProbingService.verifyProbe(host2, CP41, HostLocationProbingService.ProbeMode.DISCOVER));
// Discover location
// Expect: cancel all redirections
- hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
+ hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
index 2367b57..f8d04ca 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
@@ -39,4 +39,9 @@
return interfaces.stream().filter(intf -> cp.equals(intf.connectPoint()))
.collect(Collectors.toSet());
}
+
+ @Override
+ public Set<Interface> getInterfaces() {
+ return interfaces;
+ }
}
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java
new file mode 100644
index 0000000..b0cdf43
--- /dev/null
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.segmentrouting;
+
+import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostLocationProbingService;
+
+import java.util.List;
+import java.util.Objects;
+
+public class MockLocationProbingService implements HostLocationProbingService {
+ List<Probe> probes;
+
+ private class Probe {
+ private Host host;
+ private ConnectPoint connectPoint;
+ private ProbeMode probeMode;
+
+ Probe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ this.host = host;
+ this.connectPoint = connectPoint;
+ this.probeMode = probeMode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Probe)) {
+ return false;
+ }
+ Probe that = (Probe) o;
+ return (Objects.equals(this.host, that.host) &&
+ Objects.equals(this.connectPoint, that.connectPoint) &&
+ Objects.equals(this.probeMode, that.probeMode));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(host, connectPoint, probeMode);
+ }
+ }
+
+ MockLocationProbingService() {
+ probes = Lists.newArrayList();
+ }
+
+ boolean verifyProbe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ Probe probe = new Probe(host, connectPoint, probeMode);
+ return probes.contains(probe);
+ }
+
+ @Override
+ public void probeHostLocation(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ probes.add(new Probe(host, connectPoint, probeMode));
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostLocationProbingService.java b/core/api/src/main/java/org/onosproject/net/host/HostLocationProbingService.java
new file mode 100644
index 0000000..56d056c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/host/HostLocationProbingService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.net.host;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+
+public interface HostLocationProbingService {
+ /**
+ * Mode of host location probing.
+ */
+ enum ProbeMode {
+ /**
+ * Append probed host location if reply is received before timeout. Otherwise, do nothing.
+ * Typically used to discover secondary locations.
+ */
+ DISCOVER,
+
+ /**
+ * Remove probed host location if reply is received after timeout. Otherwise, do nothing.
+ * Typically used to verify previous locations.
+ */
+ VERIFY
+ }
+
+ /**
+ * Probes given host on given location.
+ *
+ * @param host the host to be probed
+ * @param connectPoint the location of host to be probed
+ * @param probeMode probe mode
+ */
+ void probeHostLocation(Host host, ConnectPoint connectPoint, ProbeMode probeMode);
+
+}
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java b/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java
index b324ad0..d9aeff5 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java
@@ -17,8 +17,10 @@
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.provider.ProviderService;
/**
@@ -64,10 +66,11 @@
* retrieves the unique MAC address for the probe.
*
* @param hostId ID of the host
- * @param hostLocation the host location that is under verification
+ * @param connectPoint the connect point that is under verification
+ * @param probeMode probe mode
* @return probeMac, the source MAC address ONOS uses to probe the host
*/
- default MacAddress addPendingHostLocation(HostId hostId, HostLocation hostLocation) {
+ default MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
return MacAddress.NONE;
}
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostStore.java b/core/api/src/main/java/org/onosproject/net/host/HostStore.java
index 6e98d95..96d3c31 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostStore.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostStore.java
@@ -23,6 +23,7 @@
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.Store;
@@ -65,6 +66,14 @@
HostEvent removeIp(HostId hostId, IpAddress ipAddress);
/**
+ * Append the specified location to the host entry.
+ *
+ * @param hostId host identification
+ * @param location location to be added
+ */
+ void appendLocation(HostId hostId, HostLocation location);
+
+ /**
* Removes the specified location from the host entry.
*
* @param hostId host identification
@@ -139,10 +148,11 @@
* retrieves the unique MAC address for the probe.
*
* @param hostId ID of the host
- * @param hostLocation the host location that is under verification
+ * @param connectPoint the connect point that is under verification
+ * @param probeMode probe mode
* @return probeMac, the source MAC address ONOS uses to probe the host
*/
- default MacAddress addPendingHostLocation(HostId hostId, HostLocation hostLocation) {
+ default MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
return MacAddress.NONE;
}
diff --git a/core/api/src/test/java/org/onosproject/net/host/HostStoreAdapter.java b/core/api/src/test/java/org/onosproject/net/host/HostStoreAdapter.java
index 8254de1..b79776d 100644
--- a/core/api/src/test/java/org/onosproject/net/host/HostStoreAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/host/HostStoreAdapter.java
@@ -65,6 +65,11 @@
}
@Override
+ public void appendLocation(HostId hostId, HostLocation location) {
+
+ }
+
+ @Override
public void removeLocation(HostId hostId, HostLocation location) {
}
diff --git a/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java b/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java
index e1fea28..4af7bea 100644
--- a/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java
+++ b/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java
@@ -174,6 +174,11 @@
}
@Override
+ public void appendLocation(HostId hostId, HostLocation location) {
+ hosts.get(hostId).locations().add(location);
+ }
+
+ @Override
public void removeLocation(HostId hostId, HostLocation location) {
hosts.get(hostId).locations().remove(location);
}
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
index b7a38e6..998f7df 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
@@ -29,6 +29,7 @@
import org.onlab.packet.VlanId;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.HostLocation;
@@ -455,8 +456,8 @@
}
@Override
- public MacAddress addPendingHostLocation(HostId hostId, HostLocation hostLocation) {
- return store.addPendingHostLocation(hostId, hostLocation);
+ public MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
+ return store.addPendingHostLocation(hostId, connectPoint, probeMode);
}
@Override
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
index 436ec9b..dcf58e6 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
@@ -43,6 +43,7 @@
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostStore;
import org.onosproject.net.host.HostStoreDelegate;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
@@ -60,6 +61,7 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -146,7 +148,8 @@
KryoNamespace.Builder pendingHostSerializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
- .register(PendingHostLocation.class);
+ .register(PendingHostLocation.class)
+ .register(ProbeMode.class);
pendingHostsConsistentMap = storageService.<MacAddress, PendingHostLocation>consistentMapBuilder()
.withName("onos-hosts-pending")
.withRelaxedReadConsistency()
@@ -311,7 +314,30 @@
}
@Override
+ public void appendLocation(HostId hostId, HostLocation location) {
+ log.debug("Appending location {} to host {}", location, hostId);
+ hosts.compute(hostId, (id, existingHost) -> {
+ if (existingHost != null) {
+ checkState(Objects.equals(hostId.mac(), existingHost.mac()),
+ "Existing and new MAC addresses differ.");
+ checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
+ "Existing and new VLANs differ.");
+
+ Set<HostLocation> locations = new HashSet<>(existingHost.locations());
+ locations.add(location);
+
+ return new DefaultHost(existingHost.providerId(),
+ hostId, existingHost.mac(), existingHost.vlan(),
+ locations, existingHost.ipAddresses(),
+ existingHost.configured(), existingHost.annotations());
+ }
+ return null;
+ });
+ }
+
+ @Override
public void removeLocation(HostId hostId, HostLocation location) {
+ log.debug("Removing location {} from host {}", location, hostId);
hosts.compute(hostId, (id, existingHost) -> {
if (existingHost != null) {
checkState(Objects.equals(hostId.mac(), existingHost.mac()),
@@ -384,11 +410,11 @@
}
@Override
- public MacAddress addPendingHostLocation(HostId hostId, HostLocation hostLocation) {
+ public MacAddress addPendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
// Use ONLab OUI (3 bytes) + atomic counter (3 bytes) as the source MAC of the probe
long nextIndex = storageService.getAtomicCounter("onos-hosts-probe-index").getAndIncrement();
MacAddress probeMac = MacAddress.valueOf(MacAddress.NONE.toLong() + nextIndex);
- PendingHostLocation phl = new PendingHostLocation(hostId, hostLocation);
+ PendingHostLocation phl = new PendingHostLocation(hostId, connectPoint, probeMode);
pendingHostsCache.put(probeMac, phl);
pendingHosts.put(probeMac, phl);
@@ -398,6 +424,14 @@
@Override
public void removePendingHostLocation(MacAddress probeMac) {
+ // Add the host location if probe replied in-time in DISCOVER mode
+ Optional.ofNullable(pendingHosts.get(probeMac)).ifPresent(phl -> {
+ if (phl.probeMode() == ProbeMode.DISCOVER) {
+ HostLocation newLocation = new HostLocation(phl.connectPoint(), System.currentTimeMillis());
+ appendLocation(phl.hostId(), newLocation);
+ }
+ });
+
pendingHostsCache.invalidate(probeMac);
pendingHosts.remove(probeMac);
}
@@ -502,11 +536,13 @@
case INSERT:
break;
case UPDATE:
- if (newValue.value().expired()) {
+ // Remove the host location if probe timeout in VERIFY mode
+ if (newValue.value().expired() && newValue.value().probeMode() == ProbeMode.VERIFY) {
Executor locationRemover = Executors.newSingleThreadScheduledExecutor();
locationRemover.execute(() -> {
pendingHosts.remove(event.key());
- removeLocation(newValue.value().hostId(), newValue.value().location());
+ removeLocation(newValue.value().hostId(),
+ new HostLocation(newValue.value().connectPoint(), 0L));
});
}
break;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/PendingHostLocation.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/PendingHostLocation.java
index de538f5..dc10507 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/PendingHostLocation.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/PendingHostLocation.java
@@ -16,8 +16,9 @@
package org.onosproject.store.host.impl;
+import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import java.util.Objects;
@@ -28,19 +29,22 @@
*/
class PendingHostLocation {
private HostId hostId;
- private HostLocation location;
+ private ConnectPoint connectPoint;
private boolean expired;
+ private ProbeMode probeMode;
/**
* Constructs PendingHostLocation.
*
* @param hostId Host ID
- * @param location location to be verified
+ * @param connectPoint location to be verified
+ * @param probeMode probe mode
*/
- PendingHostLocation(HostId hostId, HostLocation location) {
+ PendingHostLocation(HostId hostId, ConnectPoint connectPoint, ProbeMode probeMode) {
this.hostId = hostId;
- this.location = location;
+ this.connectPoint = connectPoint;
this.expired = false;
+ this.probeMode = probeMode;
}
/**
@@ -53,12 +57,12 @@
}
/**
- * Gets HostLocation of this entry.
+ * Gets connect point of this entry.
*
- * @return host location
+ * @return connect point
*/
- HostLocation location() {
- return location;
+ ConnectPoint connectPoint() {
+ return connectPoint;
}
/**
@@ -79,6 +83,15 @@
this.expired = expired;
}
+ /**
+ * Gets probe mode of this entry.
+ *
+ * @return probe mode
+ */
+ ProbeMode probeMode() {
+ return probeMode;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -89,20 +102,22 @@
}
PendingHostLocation that = (PendingHostLocation) o;
return (Objects.equals(this.hostId, that.hostId) &&
- Objects.equals(this.location, that.location) &&
- Objects.equals(this.expired, that.expired));
+ Objects.equals(this.connectPoint, that.connectPoint) &&
+ Objects.equals(this.expired, that.expired) &&
+ Objects.equals(this.probeMode, that.probeMode));
}
@Override
public int hashCode() {
- return Objects.hash(hostId, location, expired);
+ return Objects.hash(hostId, connectPoint, expired, probeMode);
}
@Override
public String toString() {
return toStringHelper(getClass())
.add("hostId", hostId)
- .add("location", location)
+ .add("location", connectPoint)
.add("expired", expired)
+ .add("probeMode", probeMode)
.toString();
}
}
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index 7e2feca..eadb4cf 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -23,6 +23,7 @@
import org.apache.felix.scr.annotations.Property;
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.ARP;
import org.onlab.packet.BasePacket;
import org.onlab.packet.DHCP;
@@ -52,6 +53,7 @@
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.net.host.HostLocationProbingService;
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
@@ -105,7 +107,8 @@
* hosts.
*/
@Component(immediate = true)
-public class HostLocationProvider extends AbstractProvider implements HostProvider {
+@Service
+public class HostLocationProvider extends AbstractProvider implements HostProvider, HostLocationProbingService {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -169,6 +172,8 @@
label = "Allow hosts to be multihomed")
private boolean multihomingEnabled = false;
+ private int probeInitDelayMs = 1000;
+
protected ExecutorService eventHandler;
private int probeDelayMs = 1000;
@@ -341,7 +346,48 @@
log.info("Configured. Multihoming is {}",
multihomingEnabled ? "enabled" : "disabled");
}
+ }
+ @Override
+ public void probeHostLocation(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ host.ipAddresses().stream().findFirst().ifPresent(ip -> {
+ MacAddress probeMac = providerService.addPendingHostLocation(host.id(), connectPoint, probeMode);
+ log.debug("Constructing {} probe for host {} with {}", probeMode, host.id(), ip);
+ Ethernet probe;
+ if (ip.isIp4()) {
+ probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
+ host.id().mac().toBytes(), ip.toOctets(),
+ host.id().mac().toBytes(), host.id().vlanId().toShort());
+ } else {
+ probe = NeighborSolicitation.buildNdpSolicit(
+ ip.getIp6Address(),
+ Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
+ ip.getIp6Address(),
+ probeMac,
+ host.id().mac(),
+ host.id().vlanId());
+ }
+
+ // NOTE: delay the probe a little bit to wait for the store synchronization is done
+ ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.schedule(() ->
+ sendLocationProbe(probe, connectPoint), probeInitDelayMs, TimeUnit.MILLISECONDS);
+ });
+ }
+
+ /**
+ * Send the probe packet on given port.
+ *
+ * @param probe the probe packet
+ * @param connectPoint the port we want to probe
+ */
+ private void sendLocationProbe(Ethernet probe, ConnectPoint connectPoint) {
+ log.info("Sending probe for host {} on location {} with probeMac {}",
+ probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
+ treatment, ByteBuffer.wrap(probe.serialize()));
+ packetService.emit(outboundPacket);
}
@Override
@@ -414,7 +460,8 @@
// New location is on a device that we haven't seen before
// Could be a dual-home host. Append new location and send out the probe
newLocations.addAll(prevLocations);
- probeLocations(existingHost);
+ prevLocations.forEach(prevLocation ->
+ probeHostLocation(existingHost, prevLocation, ProbeMode.VERIFY));
} else {
// Move within the same switch
// Simply replace old location that is on the same device
@@ -435,54 +482,6 @@
}
/**
- * Start verification procedure of all previous locations by sending probes.
- *
- * @param host Host to be probed
- */
- private void probeLocations(Host host) {
- host.locations().forEach(location -> {
- MacAddress probeMac = providerService.addPendingHostLocation(host.id(), location);
-
- host.ipAddresses().stream().findFirst().ifPresent(ip -> {
- log.debug("Probing host {} with {}", host.id(), ip);
- Ethernet probe;
- if (ip.isIp4()) {
- probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
- host.id().mac().toBytes(), ip.toOctets(),
- host.id().mac().toBytes(), host.id().vlanId().toShort());
- } else {
- probe = NeighborSolicitation.buildNdpSolicit(
- ip.getIp6Address(),
- Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
- ip.getIp6Address(),
- probeMac,
- host.id().mac(),
- host.id().vlanId());
- }
-
- // NOTE: delay the probe a little bit to wait for the store synchronization is done
- ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.schedule(() -> sendProbe(probe, location), probeDelayMs, TimeUnit.MILLISECONDS);
- });
- });
- }
-
- /**
- * Send the probe packet on given port.
- *
- * @param probe the probe packet
- * @param connectPoint the port we want to probe
- */
- private void sendProbe(Ethernet probe, ConnectPoint connectPoint) {
- log.info("Probing host {} on location {} with probeMac {}",
- probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
- TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
- OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
- treatment, ByteBuffer.wrap(probe.serialize()));
- packetService.emit(outboundPacket);
- }
-
- /**
* Updates IP address for an existing host.
*
* @param hid host ID