blob: e3652362e1bd4568f97c11689dc6ca2e42c6a803 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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.IPv6;
Charles Chana9458532017-05-26 11:55:12 -070023import org.onlab.packet.Ip4Address;
24import org.onlab.packet.Ip6Address;
Jonathan Hart4cb39882015-08-12 23:50:55 -040025import org.onlab.packet.IpAddress;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080026import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -080028import org.onlab.packet.ndp.NeighborSolicitation;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080029import org.onlab.util.Timer;
Jonathan Hart4cb39882015-08-12 23:50:55 -040030import org.onosproject.incubator.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.Host;
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020033import org.onosproject.net.edge.EdgePortService;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.host.HostProvider;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.packet.DefaultOutboundPacket;
38import org.onosproject.net.packet.OutboundPacket;
39import org.onosproject.net.packet.PacketService;
40import org.onosproject.net.provider.ProviderId;
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080043
44import java.nio.ByteBuffer;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080045import java.util.Collections;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080046import java.util.Set;
47import java.util.concurrent.ConcurrentHashMap;
48import java.util.concurrent.ConcurrentMap;
49import java.util.concurrent.TimeUnit;
Jonathan Hartfca736c2014-09-19 17:26:59 -070050
Jonathan Hart87fbbad2014-09-23 08:43:50 -070051/**
52 * Monitors hosts on the dataplane to detect changes in host data.
Thomas Vachuska4b420772014-10-30 16:46:17 -070053 * <p>
Jonathan Hart87fbbad2014-09-23 08:43:50 -070054 * The HostMonitor can monitor hosts that have already been detected for
55 * changes. At an application's request, it can also monitor and actively
56 * probe for hosts that have not yet been detected (specified by IP address).
Thomas Vachuska4b420772014-10-30 16:46:17 -070057 * </p>
Jonathan Hart87fbbad2014-09-23 08:43:50 -070058 */
tom202175a2014-09-19 19:00:11 -070059public class HostMonitor implements TimerTask {
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020060
61 private Logger log = LoggerFactory.getLogger(getClass());
62
Jonathan Hart70da5122014-10-01 16:37:42 -070063 private PacketService packetService;
64 private HostManager hostManager;
Jonathan Hart4cb39882015-08-12 23:50:55 -040065 private InterfaceService interfaceService;
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020066 private EdgePortService edgePortService;
Jonathan Hartfca736c2014-09-19 17:26:59 -070067
Jonathan Hart87fbbad2014-09-23 08:43:50 -070068 private final Set<IpAddress> monitoredAddresses;
Jonathan Hartfca736c2014-09-19 17:26:59 -070069
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070070 private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
Jonathan Hart70da5122014-10-01 16:37:42 -070071
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070072 private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080073 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
Jonathan Hart34ed2fd2014-10-02 19:08:55 -070074 private long probeRate = DEFAULT_PROBE_RATE;
Jonathan Hartfca736c2014-09-19 17:26:59 -070075
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070076 private Timeout timeout;
Jonathan Hartfca736c2014-09-19 17:26:59 -070077
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070078 /**
79 * Creates a new host monitor.
80 *
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070081 * @param packetService packet service used to send packets on the data plane
Jonathan Hartb4758a92014-09-24 10:46:45 -070082 * @param hostManager host manager used to look up host information and
83 * probe existing hosts
Jonathan Hart4cb39882015-08-12 23:50:55 -040084 * @param interfaceService interface service for interface information
Thomas Vachuska0ae45602015-09-22 17:06:19 -070085 * @param edgePortService edge port service
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -070086 */
Jonathan Hart4cb39882015-08-12 23:50:55 -040087 public HostMonitor(PacketService packetService, HostManager hostManager,
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020088 InterfaceService interfaceService,
89 EdgePortService edgePortService) {
Jonathan Hart70da5122014-10-01 16:37:42 -070090
Jonathan Hart87fbbad2014-09-23 08:43:50 -070091 this.packetService = packetService;
Jonathan Hartb4758a92014-09-24 10:46:45 -070092 this.hostManager = hostManager;
Jonathan Hart4cb39882015-08-12 23:50:55 -040093 this.interfaceService = interfaceService;
Jonathan Hartfb32a6e2015-09-01 12:12:14 +020094 this.edgePortService = edgePortService;
Jonathan Hartfca736c2014-09-19 17:26:59 -070095
Jonathan Hart4cb39882015-08-12 23:50:55 -040096 monitoredAddresses = Collections.newSetFromMap(new ConcurrentHashMap<>());
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 Hartf4edb312017-03-24 10:43:10 -0700107 if (monitoredAddresses.add(ip)) {
108 probe(ip);
109 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700110 }
111
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700112 /**
113 * Stops monitoring the given IP address.
114 *
115 * @param ip IP address to stop monitoring on
116 */
Jonathan Hart70da5122014-10-01 16:37:42 -0700117 void stopMonitoring(IpAddress ip) {
Jonathan Hartfca736c2014-09-19 17:26:59 -0700118 monitoredAddresses.remove(ip);
119 }
120
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700121 /**
122 * Starts the host monitor. Does nothing if the monitor is already running.
123 */
124 void start() {
125 synchronized (this) {
126 if (timeout == null) {
127 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
128 }
129 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700130 }
131
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700132 /**
133 * Stops the host monitor.
134 */
135 void shutdown() {
136 synchronized (this) {
137 timeout.cancel();
138 timeout = null;
139 }
140 }
141
sdn94b00152016-08-30 02:12:32 -0700142 /*
143 * Sets the probe rate.
144 */
145 void setProbeRate(long probeRate) {
146 this.probeRate = probeRate;
147 }
148
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700149 /**
150 * Registers a host provider with the host monitor. The monitor can use the
151 * provider to probe hosts.
152 *
153 * @param provider the host provider to register
154 */
Jonathan Hart70da5122014-10-01 16:37:42 -0700155 void registerHostProvider(HostProvider provider) {
156 hostProviders.put(provider.id(), provider);
157 }
158
Jonathan Hartfca736c2014-09-19 17:26:59 -0700159 @Override
160 public void run(Timeout timeout) throws Exception {
Jonathan Hart78613d22016-07-27 11:25:29 -0700161 monitoredAddresses.forEach(this::probe);
Jonathan Hartfca736c2014-09-19 17:26:59 -0700162
Satish Ke9d748f2015-11-24 19:05:01 +0530163 synchronized (this) {
164 this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
165 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700166 }
167
Jonathan Hart78613d22016-07-27 11:25:29 -0700168 private void probe(IpAddress ip) {
169 Set<Host> hosts = hostManager.getHostsByIp(ip);
170
171 if (hosts.isEmpty()) {
172 sendRequest(ip);
173 } else {
174 for (Host host : hosts) {
175 HostProvider provider = hostProviders.get(host.providerId());
176 if (provider == null) {
177 hostProviders.remove(host.providerId(), null);
178 } else {
179 provider.triggerProbe(host);
180 }
181 }
182 }
183 }
184
Jonathan Hartfca736c2014-09-19 17:26:59 -0700185 /**
Jonathan Hart39ee6482015-08-31 16:00:19 +0200186 * Sends an ARP or NDP request for the given IP address.
Jonathan Hartfca736c2014-09-19 17:26:59 -0700187 *
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800188 * @param targetIp IP address to send the request for
Jonathan Hartfca736c2014-09-19 17:26:59 -0700189 */
Jonathan Hart39ee6482015-08-31 16:00:19 +0200190 private void sendRequest(IpAddress targetIp) {
Charles Chan9238b382017-03-14 13:14:58 -0700191 interfaceService.getMatchingInterfaces(targetIp).forEach(intf -> {
192 if (!edgePortService.isEdgePoint(intf.connectPoint())) {
193 log.warn("Aborting attempt to send probe out non-edge port: {}", intf);
194 return;
Jonathan Hartfca736c2014-09-19 17:26:59 -0700195 }
Charles Chan9238b382017-03-14 13:14:58 -0700196
197 intf.ipAddressesList().stream()
198 .filter(ia -> ia.subnetAddress().contains(targetIp))
199 .forEach(ia -> {
Charles Chana9458532017-05-26 11:55:12 -0700200 // Use DAD to probe when interface MAC is not supplied,
201 // such that host will not learn ONOS dummy MAC from the probe.
202 IpAddress sourceIp;
203 if (!MacAddress.NONE.equals(intf.mac())) {
204 sourceIp = ia.ipAddress();
205 } else {
206 sourceIp = targetIp.isIp4() ? Ip4Address.ZERO : Ip6Address.ZERO;
207 }
208
Jonathan Hartf4edb312017-03-24 10:43:10 -0700209 log.debug("Sending probe for target:{} out of intf:{} vlan:{}",
Charles Chan9238b382017-03-14 13:14:58 -0700210 targetIp, intf.connectPoint(), intf.vlan());
Charles Chana9458532017-05-26 11:55:12 -0700211 sendProbe(intf.connectPoint(), targetIp, sourceIp,
Charles Chan9238b382017-03-14 13:14:58 -0700212 intf.mac(), intf.vlan());
213 // account for use-cases where tagged-vlan config is used
214 if (!intf.vlanTagged().isEmpty()) {
215 intf.vlanTagged().forEach(tag -> {
Jonathan Hartf4edb312017-03-24 10:43:10 -0700216 log.debug("Sending probe for target:{} out of intf:{} vlan:{}",
Charles Chan9238b382017-03-14 13:14:58 -0700217 targetIp, intf.connectPoint(), tag);
Charles Chana9458532017-05-26 11:55:12 -0700218 sendProbe(intf.connectPoint(), targetIp, sourceIp,
Charles Chan9238b382017-03-14 13:14:58 -0700219 intf.mac(), tag);
220 });
221 }
222 });
223 });
Jonathan Hartfca736c2014-09-19 17:26:59 -0700224 }
225
Pier Luigi9b1d6262017-02-02 22:31:34 -0800226 public void sendProbe(ConnectPoint connectPoint,
227 IpAddress targetIp,
228 IpAddress sourceIp,
229 MacAddress sourceMac,
230 VlanId vlan) {
Pier Ventre78e73f62016-12-02 19:59:28 -0800231 Ethernet probePacket;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800232
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700233 if (targetIp.isIp4()) {
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800234 // IPv4: Use ARP
Jonathan Hart39ee6482015-08-31 16:00:19 +0200235 probePacket = buildArpRequest(targetIp, sourceIp, sourceMac, vlan);
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800236 } else {
Pier Luigi9b1d6262017-02-02 22:31:34 -0800237 // IPv6: Use Neighbor Discovery. According to the NDP protocol,
238 // we should use the solicitation node address as IPv6 destination
239 // and the multicast mac address as Ethernet destination.
Pier Luigi37b687b2017-01-16 22:46:33 -0800240 byte[] destIp = IPv6.getSolicitNodeAddress(targetIp.toOctets());
Pier Ventre78e73f62016-12-02 19:59:28 -0800241 probePacket = NeighborSolicitation.buildNdpSolicit(
242 targetIp.toOctets(),
243 sourceIp.toOctets(),
244 destIp,
245 sourceMac.toBytes(),
Pier Luigi37b687b2017-01-16 22:46:33 -0800246 IPv6.getMCastMacAddress(destIp),
Pier Ventre78e73f62016-12-02 19:59:28 -0800247 vlan
248 );
249 }
250
251 if (probePacket == null) {
252 log.warn("Not able to build the probe packet");
253 return;
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800254 }
Jonathan Hartfca736c2014-09-19 17:26:59 -0700255
tom9a693fd2014-10-03 11:32:19 -0700256 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jonathan Hart4cb39882015-08-12 23:50:55 -0400257 .setOutput(connectPoint.port())
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800258 .build();
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700259
260 OutboundPacket outboundPacket =
Jonathan Hart4cb39882015-08-12 23:50:55 -0400261 new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800262 ByteBuffer.wrap(probePacket.serialize()));
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700263
264 packetService.emit(outboundPacket);
265 }
266
Jonathan Hart70da5122014-10-01 16:37:42 -0700267 private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800268 MacAddress sourceMac, VlanId vlan) {
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700269
270 ARP arp = new ARP();
271 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
Jonathan Hart70da5122014-10-01 16:37:42 -0700272 .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
273 .setProtocolType(ARP.PROTO_TYPE_IP)
Pavlin Radoslavov52307e62014-10-29 15:07:37 -0700274 .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
Jonathan Hart70da5122014-10-01 16:37:42 -0700275 .setOpCode(ARP.OP_REQUEST);
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700276
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800277 arp.setSenderHardwareAddress(sourceMac.toBytes())
Jonathan Hart70da5122014-10-01 16:37:42 -0700278 .setSenderProtocolAddress(sourceIp.toOctets())
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800279 .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
Jonathan Hart70da5122014-10-01 16:37:42 -0700280 .setTargetProtocolAddress(targetIp.toOctets());
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700281
282 Ethernet ethernet = new Ethernet();
283 ethernet.setEtherType(Ethernet.TYPE_ARP)
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800284 .setDestinationMACAddress(MacAddress.BROADCAST)
285 .setSourceMACAddress(sourceMac)
Jonathan Hart70da5122014-10-01 16:37:42 -0700286 .setPayload(arp);
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700287
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800288 if (!vlan.equals(VlanId.NONE)) {
289 ethernet.setVlanID(vlan.toShort());
290 }
291
alshabibaf734ff2015-07-01 16:35:26 -0700292 ethernet.setPad(true);
293
Jonathan Hart87fbbad2014-09-23 08:43:50 -0700294 return ethernet;
Jonathan Hartfca736c2014-09-19 17:26:59 -0700295 }
Pavlin Radoslavov89edb542015-02-23 13:50:29 -0800296
Jonathan Hartfca736c2014-09-19 17:26:59 -0700297}