Jonathan Hart

Wired up HostMonitor to its dependencies and got it working.

......@@ -3,8 +3,6 @@ package org.onlab.onos.config;
import java.util.List;
import org.codehaus.jackson.annotate.JsonProperty;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
/**
* Represents a set of addresses bound to a port.
......@@ -12,8 +10,8 @@ import org.onlab.packet.MacAddress;
public class AddressEntry {
private String dpid;
private short portNumber;
private List<IpPrefix> ipAddresses;
private MacAddress macAddress;
private List<String> ipAddresses;
private String macAddress;
public String getDpid() {
return dpid;
......@@ -33,21 +31,21 @@ public class AddressEntry {
this.portNumber = portNumber;
}
public List<IpPrefix> getIpAddresses() {
public List<String> getIpAddresses() {
return ipAddresses;
}
@JsonProperty("ips")
public void setIpAddresses(List<IpPrefix> ipAddresses) {
this.ipAddresses = ipAddresses;
public void setIpAddresses(List<String> strIps) {
this.ipAddresses = strIps;
}
public MacAddress getMacAddress() {
public String getMacAddress() {
return macAddress;
}
@JsonProperty("mac")
public void setMacAddress(MacAddress macAddress) {
public void setMacAddress(String macAddress) {
this.macAddress = macAddress;
}
}
......
......@@ -5,6 +5,8 @@ import static org.slf4j.LoggerFactory.getLogger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -17,10 +19,10 @@ import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.host.HostAdminService;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.slf4j.Logger;
import com.google.common.collect.Sets;
/**
* Simple configuration module to read in supplementary network configuration
* from a file.
......@@ -51,9 +53,29 @@ public class NetworkConfigReader {
DeviceId.deviceId(dpidToUri(entry.getDpid())),
PortNumber.portNumber(entry.getPortNumber()));
Set<IpPrefix> ipAddresses = new HashSet<IpPrefix>();
for (String strIp : entry.getIpAddresses()) {
try {
IpPrefix address = IpPrefix.valueOf(strIp);
ipAddresses.add(address);
} catch (IllegalArgumentException e) {
log.warn("Bad format for IP address in config: {}", strIp);
}
}
MacAddress macAddress = null;
if (entry.getMacAddress() != null) {
try {
macAddress = MacAddress.valueOf(entry.getMacAddress());
} catch (IllegalArgumentException e) {
log.warn("Bad format for MAC address in config: {}",
entry.getMacAddress());
}
}
PortAddresses addresses = new PortAddresses(cp,
Sets.newHashSet(entry.getIpAddresses()),
entry.getMacAddress());
ipAddresses, macAddress);
hostAdminService.bindAddressesToPort(addresses);
}
......
package org.onlab.onos.net.host.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -12,6 +17,7 @@ import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.host.HostAdminService;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostEvent;
......@@ -23,6 +29,7 @@ import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.onlab.packet.IpAddress;
......@@ -31,11 +38,6 @@ import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides basic implementation of the host SB &amp; NB APIs.
*/
......@@ -59,12 +61,22 @@ public class HostManager
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
private HostMonitor monitor;
@Activate
public void activate() {
log.info("Started");
store.setDelegate(delegate);
eventDispatcher.addSink(HostEvent.class, listenerRegistry);
log.info("Started");
monitor = new HostMonitor(deviceService, packetService, this);
}
@Deactivate
......@@ -76,6 +88,8 @@ public class HostManager
@Override
protected HostProviderService createProviderService(HostProvider provider) {
monitor.registerHostProvider(provider);
return new InternalHostProviderService(provider);
}
......@@ -126,12 +140,12 @@ public class HostManager
@Override
public void startMonitoringIp(IpAddress ip) {
// TODO pass through to HostMonitor
monitor.addMonitoringFor(ip);
}
@Override
public void stopMonitoringIp(IpAddress ip) {
// TODO pass through to HostMonitor
monitor.stopMonitoring(ip);
}
@Override
......
......@@ -2,10 +2,11 @@ package org.onlab.onos.net.host.impl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.util.Timeout;
......@@ -21,19 +22,19 @@ import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions;
import org.onlab.onos.net.host.HostProvider;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
import org.onlab.onos.net.packet.OutboundPacket;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.topology.TopologyService;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Monitors hosts on the dataplane to detect changes in host data.
......@@ -43,9 +44,7 @@ import org.onlab.util.Timer;
* probe for hosts that have not yet been detected (specified by IP address).
*/
public class HostMonitor implements TimerTask {
private static final byte[] DEFAULT_MAC_ADDRESS =
MacAddress.valueOf("00:00:00:00:00:01").getAddress();
private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
private static final byte[] ZERO_MAC_ADDRESS =
MacAddress.valueOf("00:00:00:00:00:00").getAddress();
......@@ -54,59 +53,77 @@ public class HostMonitor implements TimerTask {
private static final byte[] BROADCAST_MAC =
MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
private final HostService hostService;
private final TopologyService topologyService;
private final DeviceService deviceService;
private final HostProvider hostProvider;
private final PacketService packetService;
private final HostStore hostStore;
private DeviceService deviceService;
private PacketService packetService;
private HostManager hostManager;
private final Set<IpAddress> monitoredAddresses;
private final Map<ProviderId, HostProvider> hostProviders;
private final long probeRate;
private final Timeout timeout;
public HostMonitor(HostService hostService, TopologyService topologyService,
public HostMonitor(
DeviceService deviceService,
HostProvider hostProvider, PacketService packetService,
HostStore hostStore) {
this.hostService = hostService;
this.topologyService = topologyService;
PacketService packetService,
HostManager hostService) {
this.deviceService = deviceService;
this.hostProvider = hostProvider;
this.packetService = packetService;
this.hostStore = hostStore;
this.hostManager = hostService;
monitoredAddresses = new HashSet<>();
hostProviders = new ConcurrentHashMap<>();
probeRate = 30000; // milliseconds
timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
addDefaultAddresses();
}
public void addMonitoringFor(IpAddress ip) {
private void addDefaultAddresses() {
//monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
}
void addMonitoringFor(IpAddress ip) {
monitoredAddresses.add(ip);
}
public void stopMonitoring(IpAddress ip) {
void stopMonitoring(IpAddress ip) {
monitoredAddresses.remove(ip);
}
public void shutdown() {
void shutdown() {
timeout.cancel();
}
void registerHostProvider(HostProvider provider) {
hostProviders.put(provider.id(), provider);
}
void unregisterHostProvider(HostProvider provider) {
// TODO find out how to call this
}
@Override
public void run(Timeout timeout) throws Exception {
for (IpAddress ip : monitoredAddresses) {
Set<Host> hosts = Collections.emptySet(); //TODO hostService.getHostsByIp(ip);
// TODO have to convert right now because the HostService API uses IpPrefix
IpPrefix prefix = IpPrefix.valueOf(ip.toOctets());
Set<Host> hosts = hostManager.getHostsByIp(prefix);
if (hosts.isEmpty()) {
sendArpRequest(ip);
} else {
for (Host host : hosts) {
hostProvider.triggerProbe(host);
HostProvider provider = hostProviders.get(host.providerId());
if (provider != null) {
provider.triggerProbe(host);
}
}
}
}
......@@ -120,29 +137,26 @@ public class HostMonitor implements TimerTask {
* @param targetIp IP address to ARP for
*/
private void sendArpRequest(IpAddress targetIp) {
// Find ports with an IP address in the target's subnet and sent ARP
// probes out those ports.
for (Device device : deviceService.getDevices()) {
for (Port port : deviceService.getPorts(device.id())) {
ConnectPoint cp = new ConnectPoint(device.id(), port.number());
PortAddresses addresses = hostStore.getAddressBindingsForPort(cp);
PortAddresses addresses = hostManager.getAddressBindingsForPort(cp);
/*for (IpPrefix prefix : addresses.ips()) {
for (IpPrefix prefix : addresses.ips()) {
if (prefix.contains(targetIp)) {
sendProbe(device.id(), port, addresses, targetIp);
sendProbe(device.id(), port, targetIp,
prefix.toIpAddress(), addresses.mac());
}
}*/
}
}
}
// TODO case where no address was found.
// Broadcast out internal edge ports?
}
private void sendProbe(DeviceId deviceId, Port port, PortAddresses portAddresses,
IpAddress targetIp) {
Ethernet arpPacket = createArpFor(targetIp, portAddresses);
private void sendProbe(DeviceId deviceId, Port port, IpAddress targetIp,
IpAddress sourceIp, MacAddress sourceMac) {
Ethernet arpPacket = buildArpRequest(targetIp, sourceIp, sourceMac);
List<Instruction> instructions = new ArrayList<>();
instructions.add(Instructions.createOutput(port.number()));
......@@ -158,31 +172,26 @@ public class HostMonitor implements TimerTask {
packetService.emit(outboundPacket);
}
private Ethernet createArpFor(IpAddress targetIp, PortAddresses portAddresses) {
private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
MacAddress sourceMac) {
ARP arp = new ARP();
arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
byte[] sourceMacAddress;
if (portAddresses.mac() == null) {
sourceMacAddress = DEFAULT_MAC_ADDRESS;
} else {
sourceMacAddress = portAddresses.mac().getAddress();
}
.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setProtocolAddressLength((byte) IpPrefix.INET_LEN)
.setOpCode(ARP.OP_REQUEST);
arp.setSenderHardwareAddress(sourceMacAddress)
//TODO .setSenderProtocolAddress(portAddresses.ips().toOctets())
.setTargetHardwareAddress(ZERO_MAC_ADDRESS)
.setTargetProtocolAddress(targetIp.toOctets());
arp.setSenderHardwareAddress(sourceMac.getAddress())
.setSenderProtocolAddress(sourceIp.toOctets())
.setTargetHardwareAddress(ZERO_MAC_ADDRESS)
.setTargetProtocolAddress(targetIp.toOctets());
Ethernet ethernet = new Ethernet();
ethernet.setEtherType(Ethernet.TYPE_ARP)
.setDestinationMACAddress(BROADCAST_MAC)
.setSourceMACAddress(sourceMacAddress)
.setPayload(arp);
.setDestinationMACAddress(BROADCAST_MAC)
.setSourceMACAddress(sourceMac.getAddress())
.setPayload(arp);
return ethernet;
}
......
......@@ -250,6 +250,17 @@ public final class IpPrefix {
return new IpPrefix(version, host, netmask);
}
/**
* Returns an IpAddress of the bytes contained in this prefix.
* FIXME this is a hack for now and only works because IpPrefix doesn't
* mask the input bytes on creation.
*
* @return the IpAddress
*/
public IpAddress toIpAddress() {
return IpAddress.valueOf(octets);
}
public boolean isMasked() {
return mask() != 0;
}
......@@ -278,6 +289,17 @@ public final class IpPrefix {
return false;
}
public boolean contains(IpAddress address) {
// Need to get the network address because prefixes aren't automatically
// masked on creation
IpPrefix meMasked = network();
IpPrefix otherMasked =
IpPrefix.valueOf(address.octets, netmask).network();
return Arrays.equals(meMasked.octets, otherMasked.octets);
}
@Override
public int hashCode() {
final int prime = 31;
......@@ -303,6 +325,7 @@ public final class IpPrefix {
if (netmask != other.netmask) {
return false;
}
// TODO not quite right until we mask the input
if (!Arrays.equals(octets, other.octets)) {
return false;
}
......
......@@ -76,7 +76,7 @@ public class IpPrefixTest {
}
@Test
public void testContains() {
public void testContainsIpPrefix() {
IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
IpPrefix slash32 = IpPrefix.valueOf(BYTES1, 32);
IpPrefix differentSlash32 = IpPrefix.valueOf(BYTES2, 32);
......@@ -96,4 +96,17 @@ public class IpPrefixTest {
assertTrue(slash8.contains(slash31));
assertFalse(slash31.contains(slash8));
}
@Test
public void testContainsIpAddress() {
IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
IpAddress slash32 = IpAddress.valueOf(BYTES1, 32);
assertTrue(slash31.contains(slash32));
IpPrefix intf = IpPrefix.valueOf("192.168.10.101/24");
IpAddress addr = IpAddress.valueOf("192.168.10.1");
assertTrue(intf.contains(addr));
}
}
......