blob: 6dc71fc330038662db67e03eaf11eecca174a417 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.host.impl;
Jonathan Hartfca736c2014-09-19 17:26:59 -070017
Jonathan Hartfca736c2014-09-19 17:26:59 -070018import org.jboss.netty.util.Timeout;
19import org.jboss.netty.util.TimerTask;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080020import org.onlab.packet.ARP;
21import org.onlab.packet.Ethernet;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -080022import org.onlab.packet.ICMP6;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080023import org.onlab.packet.IpAddress;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -080024import org.onlab.packet.IPv6;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080025import org.onlab.packet.MacAddress;
26import org.onlab.packet.VlanId;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -080027import org.onlab.packet.ndp.NeighborDiscoveryOptions;
28import org.onlab.packet.ndp.NeighborSolicitation;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080029import org.onlab.util.Timer;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.Host;
34import org.onosproject.net.Port;
35import org.onosproject.net.device.DeviceService;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flow.instructions.Instruction;
39import org.onosproject.net.flow.instructions.Instructions;
40import org.onosproject.net.host.HostProvider;
41import org.onosproject.net.host.InterfaceIpAddress;
42import org.onosproject.net.host.PortAddresses;
43import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.OutboundPacket;
45import org.onosproject.net.packet.PacketService;
46import org.onosproject.net.provider.ProviderId;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080047
48import java.nio.ByteBuffer;
49import java.util.ArrayList;
50import java.util.Collections;
51import java.util.List;
52import java.util.Set;
53import java.util.concurrent.ConcurrentHashMap;
54import java.util.concurrent.ConcurrentMap;
55import java.util.concurrent.TimeUnit;
Jonathan Hartfca736c2014-09-19 17:26:59 -070056
Jonathan Hart87fbbad2014-09-23 08:43:50 -070057/**
58 * Monitors hosts on the dataplane to detect changes in host data.
Thomas Vachuska4b420772014-10-30 16:46:17 -070059 * <p>
Jonathan Hart87fbbad2014-09-23 08:43:50 -070060 * The HostMonitor can monitor hosts that have already been detected for
61 * changes. At an application's request, it can also monitor and actively
62 * probe for hosts that have not yet been detected (specified by IP address).
Thomas Vachuska4b420772014-10-30 16:46:17 -070063 * </p>
Jonathan Hart87fbbad2014-09-23 08:43:50 -070064 */
tom202175a2014-09-19 19:00:11 -070065public class HostMonitor implements TimerTask {
Jonathan Hart70da5122014-10-01 16:37:42 -070066 private DeviceService deviceService;
67 private PacketService packetService;
68 private HostManager hostManager;
Jonathan Hartfca736c2014-09-19 17:26:59 -070069
Jonathan Hart87fbbad2014-09-23 08:43:50 -070070 private final Set<IpAddress> monitoredAddresses;
Jonathan Hartfca736c2014-09-19 17:26:59 -070071
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070072 private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
Jonathan Hart70da5122014-10-01 16:37:42 -070073
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070074 private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080075 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070076 private long probeRate = DEFAULT_PROBE_RATE;
Jonathan Hartfca736c2014-09-19 17:26:59 -070077
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070078 private Timeout timeout;
Jonathan Hartfca736c2014-09-19 17:26:59 -070079
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070080 /**
81 * Creates a new host monitor.
82 *
83 * @param deviceService device service used to find edge ports
84 * @param packetService packet service used to send packets on the data plane
Jonathan Hartb4758a92014-09-24 10:46:45 -070085 * @param hostManager host manager used to look up host information and
86 * probe existing hosts
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070087 */
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070088 public HostMonitor(DeviceService deviceService, PacketService packetService,
Jonathan Hartb4758a92014-09-24 10:46:45 -070089 HostManager hostManager) {
Jonathan Hart70da5122014-10-01 16:37:42 -070090
Jonathan Hartfca736c2014-09-19 17:26:59 -070091 this.deviceService = deviceService;
Jonathan Hart87fbbad2014-09-23 08:43:50 -070092 this.packetService = packetService;
Jonathan Hartb4758a92014-09-24 10:46:45 -070093 this.hostManager = hostManager;
Jonathan Hartfca736c2014-09-19 17:26:59 -070094
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070095 monitoredAddresses = Collections.newSetFromMap(
96 new ConcurrentHashMap<IpAddress, Boolean>());
Jonathan Hart70da5122014-10-01 16:37:42 -070097 hostProviders = new ConcurrentHashMap<>();
Jonathan Hart70da5122014-10-01 16:37:42 -070098 }
99
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700100 /**
101 * Adds an IP address to be monitored by the host monitor. The monitor will
102 * periodically probe the host to detect changes.
103 *
104 * @param ip IP address of the host to monitor
105 */
Jonathan Hart70da5122014-10-01 16:37:42 -0700106 void addMonitoringFor(IpAddress ip) {
Jonathan Hartfca736c2014-09-19 17:26:59 -0700107 monitoredAddresses.add(ip);
108 }
109
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700110 /**
111 * Stops monitoring the given IP address.
112 *
113 * @param ip IP address to stop monitoring on
114 */
Jonathan Hart70da5122014-10-01 16:37:42 -0700115 void stopMonitoring(IpAddress ip) {
Jonathan Hartfca736c2014-09-19 17:26:59 -0700116 monitoredAddresses.remove(ip);
117 }
118
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700119 /**
120 * Starts the host monitor. Does nothing if the monitor is already running.
121 */
122 void start() {
123 synchronized (this) {
124 if (timeout == null) {
125 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
126 }
127 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700128 }
129
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700130 /**
131 * Stops the host monitor.
132 */
133 void shutdown() {
134 synchronized (this) {
135 timeout.cancel();
136 timeout = null;
137 }
138 }
139
140 /**
141 * Registers a host provider with the host monitor. The monitor can use the
142 * provider to probe hosts.
143 *
144 * @param provider the host provider to register
145 */
Jonathan Hart70da5122014-10-01 16:37:42 -0700146 void registerHostProvider(HostProvider provider) {
147 hostProviders.put(provider.id(), provider);
148 }
149
Jonathan Hartfca736c2014-09-19 17:26:59 -0700150 @Override
151 public void run(Timeout timeout) throws Exception {
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700152 for (IpAddress ip : monitoredAddresses) {
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700153 Set<Host> hosts = hostManager.getHostsByIp(ip);
Jonathan Hartfca736c2014-09-19 17:26:59 -0700154
155 if (hosts.isEmpty()) {
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800156 sendArpNdpRequest(ip);
Jonathan Hartfca736c2014-09-19 17:26:59 -0700157 } else {
158 for (Host host : hosts) {
Jonathan Hart70da5122014-10-01 16:37:42 -0700159 HostProvider provider = hostProviders.get(host.providerId());
Jonathan Hart34ed2fd2014-10-02 19:08:55 -0700160 if (provider == null) {
161 hostProviders.remove(host.providerId(), null);
162 } else {
Jonathan Hart70da5122014-10-01 16:37:42 -0700163 provider.triggerProbe(host);
164 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700165 }
166 }
167 }
168
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700169 this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
Jonathan Hartfca736c2014-09-19 17:26:59 -0700170 }
171
172 /**
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800173 * Sends an ARP or Neighbor Discovery Protocol request for the given IP
174 * address.
Jonathan Hartfca736c2014-09-19 17:26:59 -0700175 *
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800176 * @param targetIp IP address to send the request for
Jonathan Hartfca736c2014-09-19 17:26:59 -0700177 */
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800178 private void sendArpNdpRequest(IpAddress targetIp) {
179 // Find ports with an IP address in the target's subnet and sent ARP/ND
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700180 // probes out those ports.
181 for (Device device : deviceService.getDevices()) {
Jonathan Hartfca736c2014-09-19 17:26:59 -0700182 for (Port port : deviceService.getPorts(device.id())) {
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700183 ConnectPoint cp = new ConnectPoint(device.id(), port.number());
Jonathan Harta887ba82014-11-03 15:20:52 -0800184 Set<PortAddresses> portAddressSet =
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700185 hostManager.getAddressBindingsForPort(cp);
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700186
Jonathan Harta887ba82014-11-03 15:20:52 -0800187 for (PortAddresses portAddresses : portAddressSet) {
188 for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
189 if (ia.subnetAddress().contains(targetIp)) {
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800190 sendArpNdpProbe(device.id(), port, targetIp,
191 ia.ipAddress(),
192 portAddresses.mac(),
193 portAddresses.vlan());
Jonathan Harta887ba82014-11-03 15:20:52 -0800194 }
Jonathan Hart09585c62014-09-23 16:58:04 -0700195 }
Jonathan Hart70da5122014-10-01 16:37:42 -0700196 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700197 }
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700198 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700199 }
200
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800201 private void sendArpNdpProbe(DeviceId deviceId, Port port,
202 IpAddress targetIp,
203 IpAddress sourceIp, MacAddress sourceMac,
204 VlanId vlan) {
205 Ethernet probePacket = null;
206
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700207 if (targetIp.isIp4()) {
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800208 // IPv4: Use ARP
209 probePacket = buildArpRequest(targetIp, sourceIp, sourceMac,
210 vlan);
211 } else {
212 // IPv6: Use Neighbor Discovery
213 probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac,
214 vlan);
215 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700216
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700217 List<Instruction> instructions = new ArrayList<>();
218 instructions.add(Instructions.createOutput(port.number()));
219
tom9a693fd2014-10-03 11:32:19 -0700220 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800221 .setOutput(port.number())
222 .build();
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700223
224 OutboundPacket outboundPacket =
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800225 new DefaultOutboundPacket(deviceId, treatment,
226 ByteBuffer.wrap(probePacket.serialize()));
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700227
228 packetService.emit(outboundPacket);
229 }
230
Jonathan Hart70da5122014-10-01 16:37:42 -0700231 private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800232 MacAddress sourceMac, VlanId vlan) {
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700233
234 ARP arp = new ARP();
235 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
Jonathan Hart70da5122014-10-01 16:37:42 -0700236 .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
237 .setProtocolType(ARP.PROTO_TYPE_IP)
Pavlin Radoslavov52307e62014-10-29 15:07:37 -0700238 .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
Jonathan Hart70da5122014-10-01 16:37:42 -0700239 .setOpCode(ARP.OP_REQUEST);
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700240
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800241 arp.setSenderHardwareAddress(sourceMac.toBytes())
Jonathan Hart70da5122014-10-01 16:37:42 -0700242 .setSenderProtocolAddress(sourceIp.toOctets())
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800243 .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
Jonathan Hart70da5122014-10-01 16:37:42 -0700244 .setTargetProtocolAddress(targetIp.toOctets());
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700245
246 Ethernet ethernet = new Ethernet();
247 ethernet.setEtherType(Ethernet.TYPE_ARP)
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800248 .setDestinationMACAddress(MacAddress.BROADCAST)
249 .setSourceMACAddress(sourceMac)
Jonathan Hart70da5122014-10-01 16:37:42 -0700250 .setPayload(arp);
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700251
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800252 if (!vlan.equals(VlanId.NONE)) {
253 ethernet.setVlanID(vlan.toShort());
254 }
255
alshabibaf734ff2015-07-01 16:35:26 -0700256 ethernet.setPad(true);
257
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700258 return ethernet;
Jonathan Hartfca736c2014-09-19 17:26:59 -0700259 }
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800260
261 private Ethernet buildNdpRequest(IpAddress targetIp, IpAddress sourceIp,
262 MacAddress sourceMac, VlanId vlan) {
263
264 // Create the Ethernet packet
265 Ethernet ethernet = new Ethernet();
266 ethernet.setEtherType(Ethernet.TYPE_IPV6)
267 .setDestinationMACAddress(MacAddress.BROADCAST)
268 .setSourceMACAddress(sourceMac);
269 if (!vlan.equals(VlanId.NONE)) {
270 ethernet.setVlanID(vlan.toShort());
271 }
272
273 //
274 // Create the IPv6 packet
275 //
276 // TODO: The destination IP address should be the
277 // solicited-node multicast address
278 IPv6 ipv6 = new IPv6();
279 ipv6.setSourceAddress(sourceIp.toOctets());
280 ipv6.setDestinationAddress(targetIp.toOctets());
281 ipv6.setHopLimit((byte) 255);
282
283 // Create the ICMPv6 packet
284 ICMP6 icmp6 = new ICMP6();
285 icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
286 icmp6.setIcmpCode((byte) 0);
287
288 // Create the Neighbor Solication packet
289 NeighborSolicitation ns = new NeighborSolicitation();
290 ns.setTargetAddress(targetIp.toOctets());
291 ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
292 sourceMac.toBytes());
293
294 icmp6.setPayload(ns);
295 ipv6.setPayload(icmp6);
296 ethernet.setPayload(ipv6);
297
298 return ethernet;
299 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700300}