aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java')
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java288
1 files changed, 288 insertions, 0 deletions
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
new file mode 100644
index 00000000..fe252368
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -0,0 +1,288 @@
+/*
+ * 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.net.host.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborDiscoveryOptions;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.util.Timer;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.ProviderId;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Monitors hosts on the dataplane to detect changes in host data.
+ * <p>
+ * The HostMonitor can monitor hosts that have already been detected for
+ * changes. At an application's request, it can also monitor and actively
+ * probe for hosts that have not yet been detected (specified by IP address).
+ * </p>
+ */
+public class HostMonitor implements TimerTask {
+ private PacketService packetService;
+ private HostManager hostManager;
+ private InterfaceService interfaceService;
+
+ private final Set<IpAddress> monitoredAddresses;
+
+ private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
+
+ private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
+ private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
+ private long probeRate = DEFAULT_PROBE_RATE;
+
+ private Timeout timeout;
+
+ /**
+ * Creates a new host monitor.
+ *
+ * @param packetService packet service used to send packets on the data plane
+ * @param hostManager host manager used to look up host information and
+ * probe existing hosts
+ * @param interfaceService interface service for interface information
+ */
+ public HostMonitor(PacketService packetService, HostManager hostManager,
+ InterfaceService interfaceService) {
+
+ this.packetService = packetService;
+ this.hostManager = hostManager;
+ this.interfaceService = interfaceService;
+
+ monitoredAddresses = Collections.newSetFromMap(new ConcurrentHashMap<>());
+ hostProviders = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Adds an IP address to be monitored by the host monitor. The monitor will
+ * periodically probe the host to detect changes.
+ *
+ * @param ip IP address of the host to monitor
+ */
+ void addMonitoringFor(IpAddress ip) {
+ monitoredAddresses.add(ip);
+ }
+
+ /**
+ * Stops monitoring the given IP address.
+ *
+ * @param ip IP address to stop monitoring on
+ */
+ void stopMonitoring(IpAddress ip) {
+ monitoredAddresses.remove(ip);
+ }
+
+ /**
+ * Starts the host monitor. Does nothing if the monitor is already running.
+ */
+ void start() {
+ synchronized (this) {
+ if (timeout == null) {
+ timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+ /**
+ * Stops the host monitor.
+ */
+ void shutdown() {
+ synchronized (this) {
+ timeout.cancel();
+ timeout = null;
+ }
+ }
+
+ /**
+ * Registers a host provider with the host monitor. The monitor can use the
+ * provider to probe hosts.
+ *
+ * @param provider the host provider to register
+ */
+ void registerHostProvider(HostProvider provider) {
+ hostProviders.put(provider.id(), provider);
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ for (IpAddress ip : monitoredAddresses) {
+ Set<Host> hosts = hostManager.getHostsByIp(ip);
+
+ if (hosts.isEmpty()) {
+ sendArpNdpRequest(ip);
+ } else {
+ for (Host host : hosts) {
+ HostProvider provider = hostProviders.get(host.providerId());
+ if (provider == null) {
+ hostProviders.remove(host.providerId(), null);
+ } else {
+ provider.triggerProbe(host);
+ }
+ }
+ }
+ }
+
+ this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Sends an ARP or Neighbor Discovery Protocol request for the given IP
+ * address.
+ *
+ * @param targetIp IP address to send the request for
+ */
+ private void sendArpNdpRequest(IpAddress targetIp) {
+ Interface intf = interfaceService.getMatchingInterface(targetIp);
+
+ if (intf == null) {
+ return;
+ }
+
+ for (InterfaceIpAddress ia : intf.ipAddresses()) {
+ if (ia.subnetAddress().contains(targetIp)) {
+ sendArpNdpProbe(intf.connectPoint(), targetIp, ia.ipAddress(),
+ intf.mac(), intf.vlan());
+ }
+ }
+ }
+
+ private void sendArpNdpProbe(ConnectPoint connectPoint,
+ IpAddress targetIp,
+ IpAddress sourceIp, MacAddress sourceMac,
+ VlanId vlan) {
+ Ethernet probePacket = null;
+
+ if (targetIp.isIp4()) {
+ // IPv4: Use ARP
+ probePacket = buildArpRequest(targetIp, sourceIp, sourceMac,
+ vlan);
+ } else {
+ // IPv6: Use Neighbor Discovery
+ probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac,
+ vlan);
+ }
+
+ List<Instruction> instructions = new ArrayList<>();
+ instructions.add(Instructions.createOutput(connectPoint.port()));
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(connectPoint.port())
+ .build();
+
+ OutboundPacket outboundPacket =
+ new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
+ ByteBuffer.wrap(probePacket.serialize()));
+
+ packetService.emit(outboundPacket);
+ }
+
+ private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
+ MacAddress sourceMac, VlanId vlan) {
+
+ 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(sourceMac.toBytes())
+ .setSenderProtocolAddress(sourceIp.toOctets())
+ .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+ .setTargetProtocolAddress(targetIp.toOctets());
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setEtherType(Ethernet.TYPE_ARP)
+ .setDestinationMACAddress(MacAddress.BROADCAST)
+ .setSourceMACAddress(sourceMac)
+ .setPayload(arp);
+
+ if (!vlan.equals(VlanId.NONE)) {
+ ethernet.setVlanID(vlan.toShort());
+ }
+
+ ethernet.setPad(true);
+
+ return ethernet;
+ }
+
+ private Ethernet buildNdpRequest(IpAddress targetIp, IpAddress sourceIp,
+ MacAddress sourceMac, VlanId vlan) {
+
+ // Create the Ethernet packet
+ Ethernet ethernet = new Ethernet();
+ ethernet.setEtherType(Ethernet.TYPE_IPV6)
+ .setDestinationMACAddress(MacAddress.BROADCAST)
+ .setSourceMACAddress(sourceMac);
+ if (!vlan.equals(VlanId.NONE)) {
+ ethernet.setVlanID(vlan.toShort());
+ }
+
+ //
+ // Create the IPv6 packet
+ //
+ // TODO: The destination IP address should be the
+ // solicited-node multicast address
+ IPv6 ipv6 = new IPv6();
+ ipv6.setSourceAddress(sourceIp.toOctets());
+ ipv6.setDestinationAddress(targetIp.toOctets());
+ ipv6.setHopLimit((byte) 255);
+
+ // Create the ICMPv6 packet
+ ICMP6 icmp6 = new ICMP6();
+ icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
+ icmp6.setIcmpCode((byte) 0);
+
+ // Create the Neighbor Solication packet
+ NeighborSolicitation ns = new NeighborSolicitation();
+ ns.setTargetAddress(targetIp.toOctets());
+ ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
+ sourceMac.toBytes());
+
+ icmp6.setPayload(ns);
+ ipv6.setPayload(icmp6);
+ ethernet.setPayload(ipv6);
+
+ return ethernet;
+ }
+}