CORD-1416 Implement multi-homing probing in HostLocationProvider
Also include following refactoring
- Refactor the way we generate ARP probe
- Remove some unused code
Change-Id: I96b1c47bd5731b7b38ef4d19a941d231b5d0054c
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 cce446d..4d97af7 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
@@ -15,6 +15,7 @@
*/
package org.onosproject.provider.host.impl;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -31,10 +32,10 @@
import org.onlab.packet.IPacket;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
@@ -90,6 +91,7 @@
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
+import java.util.Set;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
@@ -162,9 +164,11 @@
label = "Enable requesting packet intercepts")
private boolean requestInterceptsEnabled = true;
- protected ExecutorService eventHandler;
+ @Property(name = "multihomingEnabled", boolValue = false,
+ label = "Allow hosts to be multihomed")
+ private boolean multihomingEnabled = false;
- private static final byte[] SENDER_ADDRESS = IpAddress.valueOf("0.0.0.0").toOctets();
+ protected ExecutorService eventHandler;
/**
* Creates an OpenFlow host provider.
@@ -245,18 +249,6 @@
packetService.cancelPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
packetService.cancelPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
}
-
- // Use DHCP
- TrafficSelector dhcpServerSelector = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(IPv4.PROTOCOL_UDP)
- .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
- .build();
- TrafficSelector dhcpClientSelector = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(IPv4.PROTOCOL_UDP)
- .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
- .build();
}
/**
@@ -336,6 +328,17 @@
log.info("Configured. Request intercepts is {}",
requestInterceptsEnabled ? "enabled" : "disabled");
}
+
+ flag = Tools.isPropertyEnabled(properties, "multihomingEnabled");
+ if (flag == null) {
+ log.info("Multihoming is not configured, " +
+ "using current value of {}", multihomingEnabled);
+ } else {
+ multihomingEnabled = flag;
+ log.info("Configured. Multihoming is {}",
+ multihomingEnabled ? "enabled" : "disabled");
+ }
+
}
@Override
@@ -378,26 +381,9 @@
// This method is using source ip as 0.0.0.0 , to receive the reply even from the sub net hosts.
private Ethernet buildArpRequest(IpAddress targetIp, Host host) {
-
- ARP arp = new ARP();
- arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
- .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
- .setProtocolType(ARP.PROTO_TYPE_IP)
- .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
- .setOpCode(ARP.OP_REQUEST);
-
- arp.setSenderHardwareAddress(MacAddress.BROADCAST.toBytes())
- .setSenderProtocolAddress(SENDER_ADDRESS)
- .setTargetHardwareAddress(MacAddress.BROADCAST.toBytes())
- .setTargetProtocolAddress(targetIp.toOctets());
-
- Ethernet ethernet = new Ethernet();
- ethernet.setEtherType(Ethernet.TYPE_ARP)
- .setDestinationMACAddress(MacAddress.BROADCAST)
- .setSourceMACAddress(MacAddress.BROADCAST).setPayload(arp);
-
- ethernet.setPad(true);
- return ethernet;
+ return ARP.buildArpRequest(MacAddress.BROADCAST.toBytes(), Ip4Address.ZERO.toOctets(),
+ MacAddress.BROADCAST.toBytes(), targetIp.toOctets(),
+ MacAddress.BROADCAST.toBytes(), VlanId.NONE.toShort());
}
private class InternalHostProvider implements PacketProcessor {
@@ -414,9 +400,23 @@
private void createOrUpdateHost(HostId hid, MacAddress mac,
VlanId vlan, HostLocation hloc,
IpAddress ip) {
+ Set<HostLocation> newLocations = Sets.newHashSet(hloc);
+
+ if (multihomingEnabled) {
+ Host existingHost = hostService.getHost(hid);
+ if (existingHost != null) {
+ Set<HostLocation> prevLocations = existingHost.locations();
+ newLocations.addAll(prevLocations);
+
+ if (!existingHost.locations().contains(hloc)) {
+ probeLocations(existingHost);
+ }
+ }
+ }
+
HostDescription desc = ip == null || ip.isZero() || ip.isSelfAssigned() ?
- new DefaultHostDescription(mac, vlan, hloc) :
- new DefaultHostDescription(mac, vlan, hloc, ip);
+ new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(), false) :
+ new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(ip), false);
try {
providerService.hostDetected(hid, desc, false);
} catch (IllegalStateException e) {
@@ -425,6 +425,50 @@
}
/**
+ * 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 -> {
+ 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().toOctets(),
+ IPv6.getLinkLocalAddress(probeMac.toBytes()),
+ IPv6.getSolicitNodeAddress(ip.getIp6Address().toOctets()),
+ probeMac.toBytes(),
+ IPv6.getMCastMacAddress(ip.getIp6Address().toOctets()),
+ host.id().vlanId());
+ }
+ sendProbe(probe, location);
+ });
+ });
+ }
+
+ /**
+ * 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
@@ -433,12 +477,12 @@
private void updateHostIp(HostId hid, IpAddress ip) {
Host host = hostService.getHost(hid);
if (host == null) {
- log.debug("Fail to update IP for {}. Host does not exist");
+ log.warn("Fail to update IP for {}. Host does not exist", hid);
return;
}
HostDescription desc = new DefaultHostDescription(hid.mac(), hid.vlanId(),
- host.location(), ip);
+ host.locations(), Sets.newHashSet(ip), false);
try {
providerService.hostDetected(hid, desc, false);
} catch (IllegalStateException e) {
@@ -478,6 +522,14 @@
HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
+ MacAddress destMac = eth.getDestinationMAC();
+
+ // Receives a location probe. Invalid entry from the cache
+ if (multihomingEnabled && destMac.isOnos() && !MacAddress.NONE.equals(destMac)) {
+ log.info("Receives probe for {}/{} on {}", srcMac, vlan, heardOn);
+ providerService.removePendingHostLocation(destMac);
+ return;
+ }
// ARP: possible new hosts, update both location and IP
if (eth.getEtherType() == Ethernet.TYPE_ARP) {