blob: a02159c93332eb739d3044e15b4308e90819dc78 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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;
tomdb0d03f2014-08-27 16:34:15 -070017
jobind19b7142019-05-17 09:46:52 -040018import com.google.common.collect.Sets;
Pier Luigi9b1d6262017-02-02 22:31:34 -080019import org.onlab.packet.Ip6Address;
Simon Huntff663742015-05-14 13:33:05 -070020import org.onlab.packet.IpAddress;
21import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
soumya3e6f05e2016-08-05 15:11:11 -070023import org.onlab.util.Tools;
24import org.onosproject.cfg.ComponentConfigService;
Charles Chan888e20a2017-05-01 15:44:23 -070025import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.Host;
28import org.onosproject.net.HostId;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070029import org.onosproject.net.HostLocation;
Rafał Szaleckide5cf842018-11-17 13:30:01 +010030import org.onosproject.net.config.Config;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070031import org.onosproject.net.config.NetworkConfigEvent;
32import org.onosproject.net.config.NetworkConfigListener;
33import org.onosproject.net.config.NetworkConfigService;
34import org.onosproject.net.config.basics.BasicHostConfig;
Rafał Szaleckide5cf842018-11-17 13:30:01 +010035import org.onosproject.net.config.basics.HostAnnotationConfig;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.device.DeviceService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070037import org.onosproject.net.edge.EdgePortService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.host.HostAdminService;
39import org.onosproject.net.host.HostDescription;
40import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostListener;
42import org.onosproject.net.host.HostProvider;
43import org.onosproject.net.host.HostProviderRegistry;
44import org.onosproject.net.host.HostProviderService;
45import org.onosproject.net.host.HostService;
46import org.onosproject.net.host.HostStore;
47import org.onosproject.net.host.HostStoreDelegate;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070048import org.onosproject.net.intf.Interface;
49import org.onosproject.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.packet.PacketService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070051import org.onosproject.net.provider.AbstractListenerProviderRegistry;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.provider.AbstractProviderService;
soumya3e6f05e2016-08-05 15:11:11 -070053import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070054import org.osgi.service.component.annotations.Activate;
55import org.osgi.service.component.annotations.Component;
56import org.osgi.service.component.annotations.Deactivate;
57import org.osgi.service.component.annotations.Modified;
58import org.osgi.service.component.annotations.Reference;
59import org.osgi.service.component.annotations.ReferenceCardinality;
tomdb0d03f2014-08-27 16:34:15 -070060import org.slf4j.Logger;
tom5f38b3a2014-08-27 23:50:54 -070061
soumya3e6f05e2016-08-05 15:11:11 -070062import java.util.Dictionary;
jobind19b7142019-05-17 09:46:52 -040063import java.util.Map;
Rafał Szaleckide5cf842018-11-17 13:30:01 +010064import java.util.Optional;
Simon Huntff663742015-05-14 13:33:05 -070065import java.util.Set;
jobind19b7142019-05-17 09:46:52 -040066import java.util.concurrent.ConcurrentHashMap;
67import java.util.concurrent.Executors;
68import java.util.concurrent.ScheduledExecutorService;
69import java.util.concurrent.TimeUnit;
Simon Huntff663742015-05-14 13:33:05 -070070
71import static com.google.common.base.Preconditions.checkNotNull;
jobind19b7142019-05-17 09:46:52 -040072import static com.google.common.base.Strings.isNullOrEmpty;
Pier Luigi9b1d6262017-02-02 22:31:34 -080073import static org.onlab.packet.IPv6.getLinkLocalAddress;
jobind19b7142019-05-17 09:46:52 -040074import static org.onlab.util.Tools.get;
Ray Milkeyd04e2272018-10-16 18:20:18 -070075import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS;
76import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS_DEFAULT;
77import static org.onosproject.net.OsgiPropertyConstants.HM_GREEDY_LEARNING_IPV6;
78import static org.onosproject.net.OsgiPropertyConstants.HM_GREEDY_LEARNING_IPV6_DEFAULT;
79import static org.onosproject.net.OsgiPropertyConstants.HM_MONITOR_HOSTS;
80import static org.onosproject.net.OsgiPropertyConstants.HM_MONITOR_HOSTS_DEFAULT;
81import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE;
82import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE_DEFAULT;
jobind19b7142019-05-17 09:46:52 -040083import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS;
84import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
85import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER;
86import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER_DEFAULT;
87import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE;
88import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT;
89import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS;
90import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
91import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE;
92import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
93
Changhoon Yoon541ef712015-05-23 17:18:34 +090094import static org.onosproject.security.AppGuard.checkPermission;
Simon Huntffbad3b2017-05-16 15:37:51 -070095import static org.onosproject.security.AppPermission.Type.HOST_EVENT;
96import static org.onosproject.security.AppPermission.Type.HOST_READ;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070097import static org.slf4j.LoggerFactory.getLogger;
Simon Huntff663742015-05-14 13:33:05 -070098
tomdb0d03f2014-08-27 16:34:15 -070099/**
100 * Provides basic implementation of the host SB & NB APIs.
101 */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700102@Component(
103 immediate = true,
104 service = {
105 HostService.class,
106 HostAdminService.class,
107 HostProviderRegistry.class
108 },
109 property = {
jobind19b7142019-05-17 09:46:52 -0400110 HM_ALLOW_DUPLICATE_IPS + ":Boolean=" + HM_ALLOW_DUPLICATE_IPS_DEFAULT,
111 HM_MONITOR_HOSTS + ":Boolean=" + HM_MONITOR_HOSTS_DEFAULT,
112 HM_PROBE_RATE + ":Integer=" + HM_PROBE_RATE_DEFAULT,
113 HM_GREEDY_LEARNING_IPV6 + ":Boolean=" + HM_GREEDY_LEARNING_IPV6_DEFAULT,
114 HM_HOST_MOVE_TRACKER_ENABLE + ":Boolean=" + HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT,
115 HM_HOST_MOVED_THRESHOLD_IN_MILLIS + ":Integer=" + HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT,
116 HM_HOST_MOVE_COUNTER + ":Integer=" + HM_HOST_MOVE_COUNTER_DEFAULT,
117 HM_OFFENDING_HOST_EXPIRY_IN_MINS + ":Long=" + HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT,
118 HM_OFFENDING_HOST_THREADS_POOL_SIZE + ":Integer=" + HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT
119
120
Ray Milkeyd04e2272018-10-16 18:20:18 -0700121 }
122)
tom202175a2014-09-19 19:00:11 -0700123public class HostManager
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700124 extends AbstractListenerProviderRegistry<HostEvent, HostListener, HostProvider, HostProviderService>
tom89b63c52014-09-16 09:19:51 -0700125 implements HostService, HostAdminService, HostProviderRegistry {
tomdb0d03f2014-08-27 16:34:15 -0700126
tom5f38b3a2014-08-27 23:50:54 -0700127 private final Logger log = getLogger(getClass());
tomdb0d03f2014-08-27 16:34:15 -0700128
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700129 public static final String HOST_ID_NULL = "Host ID cannot be null";
tom5f38b3a2014-08-27 23:50:54 -0700130
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700131 private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
132
tomc78acee2014-09-24 15:16:55 -0700133 private HostStoreDelegate delegate = new InternalStoreDelegate();
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tom5bcc9462014-09-19 10:11:31 -0700136 protected HostStore store;
tom7869ad92014-09-09 14:32:08 -0700137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart70da5122014-10-01 16:37:42 -0700139 protected DeviceService deviceService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart70da5122014-10-01 16:37:42 -0700142 protected PacketService packetService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700145 protected NetworkConfigService networkConfigService;
146
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart4cb39882015-08-12 23:50:55 -0400148 protected InterfaceService interfaceService;
149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hartfb32a6e2015-09-01 12:12:14 +0200151 protected EdgePortService edgePortService;
152
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
soumya3e6f05e2016-08-05 15:11:11 -0700154 protected ComponentConfigService cfgService;
155
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700156 /** Enable removal of duplicate ip address. */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700157 private boolean allowDuplicateIps = HM_ALLOW_DUPLICATE_IPS_DEFAULT;
sdn94b00152016-08-30 02:12:32 -0700158
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700159 /** Enable/Disable monitoring of hosts. */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700160 private boolean monitorHosts = HM_MONITOR_HOSTS_DEFAULT;
sdn94b00152016-08-30 02:12:32 -0700161
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700162 /** Set the probe Rate in milli seconds. */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700163 private long probeRate = HM_PROBE_RATE_DEFAULT;
sdn94b00152016-08-30 02:12:32 -0700164
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700165 /** Enable/Disable greedy learning of IPv6 link local address. */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700166 private boolean greedyLearningIpv6 = HM_GREEDY_LEARNING_IPV6_DEFAULT;
Pier Luigi9b1d6262017-02-02 22:31:34 -0800167
jobind19b7142019-05-17 09:46:52 -0400168 /** Enable/Disable tracking of rogue host moves. */
169 private boolean hostMoveTrackerEnabled = HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT;
170
171 /** Host move threshold in milli seconds. */
172 private int hostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
173
174 /** If the host move happening within given threshold then increment the host move counter. */
175 private int hostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT;
176
177 /** Max value of the counter after which the host will not be considered as offending host. */
178 private long offendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
179
180 /** Default pool size of offending host clear executor thread. */
181 private int offendingHostClearThreadPool = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
182
Jonathan Hart70da5122014-10-01 16:37:42 -0700183 private HostMonitor monitor;
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100184 private HostAnnotationOperator hostAnnotationOperator;
jobind19b7142019-05-17 09:46:52 -0400185 private ScheduledExecutorService offendingHostUnblockExecutor = null;
186 private Map<HostId, HostMoveTracker> hostMoveTracker = new ConcurrentHashMap<>();
tomdb0d03f2014-08-27 16:34:15 -0700187
soumya3e6f05e2016-08-05 15:11:11 -0700188
tomdb0d03f2014-08-27 16:34:15 -0700189 @Activate
soumya3e6f05e2016-08-05 15:11:11 -0700190 public void activate(ComponentContext context) {
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100191 hostAnnotationOperator = new HostAnnotationOperator(networkConfigService);
tomc78acee2014-09-24 15:16:55 -0700192 store.setDelegate(delegate);
tom96dfcab2014-08-28 09:26:03 -0700193 eventDispatcher.addSink(HostEvent.class, listenerRegistry);
soumya3e6f05e2016-08-05 15:11:11 -0700194 cfgService.registerProperties(getClass());
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700195 networkConfigService.addListener(networkConfigListener);
Jonathan Hartfb32a6e2015-09-01 12:12:14 +0200196 monitor = new HostMonitor(packetService, this, interfaceService, edgePortService);
sdn94b00152016-08-30 02:12:32 -0700197 monitor.setProbeRate(probeRate);
Jonathan Hart8f6f1ea2014-10-03 16:05:19 -0700198 monitor.start();
sdn94b00152016-08-30 02:12:32 -0700199 cfgService.registerProperties(getClass());
jobind19b7142019-05-17 09:46:52 -0400200 modified(context);
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700201 log.info("Started");
tomdb0d03f2014-08-27 16:34:15 -0700202 }
203
sdn94b00152016-08-30 02:12:32 -0700204 @Deactivate
205 public void deactivate() {
206 store.unsetDelegate(delegate);
207 eventDispatcher.removeSink(HostEvent.class);
208 networkConfigService.removeListener(networkConfigListener);
209 cfgService.unregisterProperties(getClass(), false);
210 monitor.shutdown();
jobind19b7142019-05-17 09:46:52 -0400211 if (offendingHostUnblockExecutor != null) {
212 offendingHostUnblockExecutor.shutdown();
213 }
sdn94b00152016-08-30 02:12:32 -0700214 log.info("Stopped");
215 }
216
soumya3e6f05e2016-08-05 15:11:11 -0700217 @Modified
Simon Huntffbad3b2017-05-16 15:37:51 -0700218 public void modified(ComponentContext context) {
sdn94b00152016-08-30 02:12:32 -0700219 boolean oldValue = monitorHosts;
220 readComponentConfiguration(context);
221 if (probeRate > 0) {
222 monitor.setProbeRate(probeRate);
223 } else {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700224 log.warn("ProbeRate cannot be less than 0");
sdn94b00152016-08-30 02:12:32 -0700225 }
226
227 if (oldValue != monitorHosts) {
228 if (monitorHosts) {
229 startMonitoring();
230 } else {
231 stopMonitoring();
232 }
233 }
234 }
235
236 /**
237 * Extracts properties from the component configuration context.
238 *
239 * @param context the component context
240 */
241 private void readComponentConfiguration(ComponentContext context) {
soumya3e6f05e2016-08-05 15:11:11 -0700242 Dictionary<?, ?> properties = context.getProperties();
243 Boolean flag;
jobind19b7142019-05-17 09:46:52 -0400244 int newHostMoveThresholdInMillis;
245 int newHostMoveCounter;
246 int newOffendinghostPoolSize;
247 long newOffendingHostExpiryInMins;
sdn94b00152016-08-30 02:12:32 -0700248
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700249 flag = Tools.isPropertyEnabled(properties, HM_MONITOR_HOSTS);
sdn94b00152016-08-30 02:12:32 -0700250 if (flag == null) {
251 log.info("monitorHosts is not enabled " +
252 "using current value of {}", monitorHosts);
253 } else {
254 monitorHosts = flag;
255 log.info("Configured. monitorHosts {}",
Simon Huntffbad3b2017-05-16 15:37:51 -0700256 monitorHosts ? "enabled" : "disabled");
sdn94b00152016-08-30 02:12:32 -0700257 }
258
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700259 Long longValue = Tools.getLongProperty(properties, HM_PROBE_RATE);
sdn94b00152016-08-30 02:12:32 -0700260 if (longValue == null || longValue == 0) {
261 log.info("probeRate is not set sing default value of {}", probeRate);
262 } else {
263 probeRate = longValue;
264 log.info("Configured. probeRate {}", probeRate);
265 }
266
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700267 flag = Tools.isPropertyEnabled(properties, HM_ALLOW_DUPLICATE_IPS);
soumya3e6f05e2016-08-05 15:11:11 -0700268 if (flag == null) {
269 log.info("Removal of duplicate ip address is not configured");
270 } else {
271 allowDuplicateIps = flag;
272 log.info("Removal of duplicate ip address is {}",
273 allowDuplicateIps ? "disabled" : "enabled");
274 }
sdn94b00152016-08-30 02:12:32 -0700275
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700276 flag = Tools.isPropertyEnabled(properties, HM_GREEDY_LEARNING_IPV6);
Pier Luigi9b1d6262017-02-02 22:31:34 -0800277 if (flag == null) {
278 log.info("greedy learning is not enabled " +
279 "using current value of {}", greedyLearningIpv6);
280 } else {
281 greedyLearningIpv6 = flag;
282 log.info("Configured. greedyLearningIpv6 {}",
283 greedyLearningIpv6 ? "enabled" : "disabled");
284 }
jobind19b7142019-05-17 09:46:52 -0400285 flag = Tools.isPropertyEnabled(properties, HM_HOST_MOVE_TRACKER_ENABLE);
286 if (flag == null) {
287 log.info("Host move tracker is not configured " +
288 "using current value of {}", hostMoveTrackerEnabled);
289 } else {
290 hostMoveTrackerEnabled = flag;
291 log.info("Configured. hostMoveTrackerEnabled {}",
292 hostMoveTrackerEnabled ? "enabled" : "disabled");
Pier Luigi9b1d6262017-02-02 22:31:34 -0800293
jobind19b7142019-05-17 09:46:52 -0400294 //On enable cfg ,sets default configuration vales added , else use the default values
295 properties = context.getProperties();
296 try {
297 String s = get(properties, HM_HOST_MOVED_THRESHOLD_IN_MILLIS);
298 newHostMoveThresholdInMillis = isNullOrEmpty(s) ?
299 hostMoveThresholdInMillis : Integer.parseInt(s.trim());
300
301 s = get(properties, HM_HOST_MOVE_COUNTER);
302 newHostMoveCounter = isNullOrEmpty(s) ? hostMoveCounter : Integer.parseInt(s.trim());
303
304 s = get(properties, HM_OFFENDING_HOST_EXPIRY_IN_MINS);
305 newOffendingHostExpiryInMins = isNullOrEmpty(s) ?
306 offendingHostExpiryInMins : Integer.parseInt(s.trim());
307
308 s = get(properties, HM_OFFENDING_HOST_THREADS_POOL_SIZE);
309 newOffendinghostPoolSize = isNullOrEmpty(s) ?
310 offendingHostClearThreadPool : Integer.parseInt(s.trim());
311 } catch (NumberFormatException | ClassCastException e) {
312 newHostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT;
313 newHostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT;
314 newOffendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT;
315 newOffendinghostPoolSize = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT;
316 }
317 if (newHostMoveThresholdInMillis != hostMoveThresholdInMillis) {
318 hostMoveThresholdInMillis = newHostMoveThresholdInMillis;
319 }
320 if (newHostMoveCounter != hostMoveCounter) {
321 hostMoveCounter = newHostMoveCounter;
322 }
323 if (newOffendingHostExpiryInMins != offendingHostExpiryInMins) {
324 offendingHostExpiryInMins = newOffendingHostExpiryInMins;
325 }
326 if (hostMoveTrackerEnabled && offendingHostUnblockExecutor == null) {
327 setupThreadPool();
328 } else if (newOffendinghostPoolSize != offendingHostClearThreadPool
329 && offendingHostUnblockExecutor != null) {
330 offendingHostClearThreadPool = newOffendinghostPoolSize;
331 offendingHostUnblockExecutor.shutdown();
332 offendingHostUnblockExecutor = null;
333 setupThreadPool();
334 } else if (!hostMoveTrackerEnabled && offendingHostUnblockExecutor != null) {
335 offendingHostUnblockExecutor.shutdown();
336 offendingHostUnblockExecutor = null;
337 }
338 if (newOffendinghostPoolSize != offendingHostClearThreadPool) {
339 offendingHostClearThreadPool = newOffendinghostPoolSize;
340 }
341
342 log.debug("modified hostMoveThresholdInMillis: {}, hostMoveCounter: {}, " +
343 "offendingHostExpiryInMins: {} ", hostMoveThresholdInMillis,
344 hostMoveCounter, offendingHostExpiryInMins);
345 }
346 }
347
348 private synchronized void setupThreadPool() {
349 offendingHostUnblockExecutor = Executors.newScheduledThreadPool(offendingHostClearThreadPool);
soumya3e6f05e2016-08-05 15:11:11 -0700350 }
351
sdn94b00152016-08-30 02:12:32 -0700352 /**
353 * Starts monitoring the hosts by IP Address.
sdn94b00152016-08-30 02:12:32 -0700354 */
355 private void startMonitoring() {
356 store.getHosts().forEach(host -> {
Simon Huntffbad3b2017-05-16 15:37:51 -0700357 host.ipAddresses().forEach(ip -> {
358 monitor.addMonitoringFor(ip);
sdn94b00152016-08-30 02:12:32 -0700359 });
360 });
361 }
362
363 /**
364 * Stops monitoring the hosts by IP Address.
sdn94b00152016-08-30 02:12:32 -0700365 */
366 private void stopMonitoring() {
367 store.getHosts().forEach(host -> {
Simon Huntffbad3b2017-05-16 15:37:51 -0700368 host.ipAddresses().forEach(ip -> {
369 monitor.stopMonitoring(ip);
sdn94b00152016-08-30 02:12:32 -0700370 });
371 });
tomdb0d03f2014-08-27 16:34:15 -0700372 }
373
374 @Override
tom5f38b3a2014-08-27 23:50:54 -0700375 protected HostProviderService createProviderService(HostProvider provider) {
Jonathan Hart70da5122014-10-01 16:37:42 -0700376 monitor.registerHostProvider(provider);
tom5f38b3a2014-08-27 23:50:54 -0700377 return new InternalHostProviderService(provider);
tomdb0d03f2014-08-27 16:34:15 -0700378 }
379
tom7869ad92014-09-09 14:32:08 -0700380 @Override
381 public int getHostCount() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900382 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700383 return store.getHostCount();
384 }
385
386 @Override
387 public Iterable<Host> getHosts() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900388 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700389 return store.getHosts();
390 }
391
392 @Override
393 public Host getHost(HostId hostId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900394 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700395 checkNotNull(hostId, HOST_ID_NULL);
396 return store.getHost(hostId);
397 }
398
399 @Override
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700400 public Set<Host> getHostsByVlan(VlanId vlanId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900401 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700402 return store.getHosts(vlanId);
403 }
404
405 @Override
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700406 public Set<Host> getHostsByMac(MacAddress mac) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900407 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700408 checkNotNull(mac, "MAC address cannot be null");
409 return store.getHosts(mac);
410 }
411
412 @Override
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700413 public Set<Host> getHostsByIp(IpAddress ip) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900414 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700415 checkNotNull(ip, "IP address cannot be null");
416 return store.getHosts(ip);
417 }
418
419 @Override
420 public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
Charles Chanb51e8442020-03-08 17:41:11 -0700421 return getConnectedHosts(connectPoint, false);
422 }
423
424 @Override
425 public Set<Host> getConnectedHosts(ConnectPoint connectPoint, boolean matchAuxLocations) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900426 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700427 checkNotNull(connectPoint, "Connection point cannot be null");
Charles Chanb51e8442020-03-08 17:41:11 -0700428 return store.getConnectedHosts(connectPoint, matchAuxLocations);
tom7869ad92014-09-09 14:32:08 -0700429 }
430
431 @Override
432 public Set<Host> getConnectedHosts(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900433 checkPermission(HOST_READ);
tom7869ad92014-09-09 14:32:08 -0700434 checkNotNull(deviceId, "Device ID cannot be null");
435 return store.getConnectedHosts(deviceId);
436 }
437
438 @Override
Jonathan Hartac60c082014-09-23 08:55:17 -0700439 public void startMonitoringIp(IpAddress ip) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900440 checkPermission(HOST_EVENT);
Jonathan Hart70da5122014-10-01 16:37:42 -0700441 monitor.addMonitoringFor(ip);
Jonathan Hartfca736c2014-09-19 17:26:59 -0700442 }
443
444 @Override
Jonathan Hartac60c082014-09-23 08:55:17 -0700445 public void stopMonitoringIp(IpAddress ip) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900446 checkPermission(HOST_EVENT);
Jonathan Hart70da5122014-10-01 16:37:42 -0700447 monitor.stopMonitoring(ip);
Jonathan Hartac60c082014-09-23 08:55:17 -0700448 }
449
450 @Override
451 public void requestMac(IpAddress ip) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700452 // FIXME!!!! Auto-generated method stub
tom7869ad92014-09-09 14:32:08 -0700453 }
454
tom89b63c52014-09-16 09:19:51 -0700455 @Override
456 public void removeHost(HostId hostId) {
457 checkNotNull(hostId, HOST_ID_NULL);
Charles Chan009c3082015-11-10 14:18:04 -0800458 store.removeHost(hostId);
tom89b63c52014-09-16 09:19:51 -0700459 }
460
tomdb0d03f2014-08-27 16:34:15 -0700461 // Personalized host provider service issued to the supplied provider.
tom7869ad92014-09-09 14:32:08 -0700462 private class InternalHostProviderService
463 extends AbstractProviderService<HostProvider>
tomdb0d03f2014-08-27 16:34:15 -0700464 implements HostProviderService {
tomcfde0622014-09-09 11:02:42 -0700465 InternalHostProviderService(HostProvider provider) {
tomdb0d03f2014-08-27 16:34:15 -0700466 super(provider);
467 }
468
469 @Override
Ray Milkey718e4382018-12-05 09:08:01 -0800470 public void hostDetected(HostId hostId, HostDescription initialHostDescription, boolean replaceIps) {
Andrea Campanella852a7362020-09-23 11:42:21 +0200471 log.debug("Host Detected {}, {}", hostId, initialHostDescription);
Ray Milkey718e4382018-12-05 09:08:01 -0800472 HostDescription hostDescription = initialHostDescription;
tom7869ad92014-09-09 14:32:08 -0700473 checkNotNull(hostId, HOST_ID_NULL);
474 checkValidity();
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100475
476 BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class);
477 if (!isAllowed(cfg)) {
478 log.warn("Host {} is not allowed to be added into the contol domain", hostId);
479 return;
480 }
481
Ray Milkey718e4382018-12-05 09:08:01 -0800482 hostDescription = BasicHostOperator.combine(cfg, initialHostDescription);
Andrea Campanella852a7362020-09-23 11:42:21 +0200483
484 if (!allowDuplicateIps) {
485 removeDuplicates(hostId, hostDescription);
486 }
487
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100488 HostAnnotationConfig annoConfig = networkConfigService.getConfig(hostId, HostAnnotationConfig.class);
489 if (annoConfig != null) {
490 hostDescription = hostAnnotationOperator.combine(hostId, hostDescription, Optional.of(annoConfig));
491 }
492
jobind19b7142019-05-17 09:46:52 -0400493 if (!hostMoveTrackerEnabled) {
494 store.createOrUpdateHost(provider().id(), hostId,
495 hostDescription, replaceIps);
496 } else if (!shouldBlock(hostId, hostDescription.locations())) {
497 log.debug("Host move is allowed for host with Id: {} ", hostId);
498 store.createOrUpdateHost(provider().id(), hostId,
499 hostDescription, replaceIps);
500 } else {
501 log.info("Host move is NOT allowed for host with Id: {} , removing from host store ", hostId);
502 }
sdn94b00152016-08-30 02:12:32 -0700503
504 if (monitorHosts) {
505 hostDescription.ipAddress().forEach(ip -> {
506 monitor.addMonitoringFor(ip);
507 });
508 }
Pier Luigi9b1d6262017-02-02 22:31:34 -0800509
510 // Greedy learning of IPv6 host. We have to disable the greedy
511 // learning of configured hosts. Validate hosts each time will
Charles Chan82fac582017-09-12 12:09:22 -0700512 // overwrite the learnt information with the configured information.
Pier Luigi9b1d6262017-02-02 22:31:34 -0800513 if (greedyLearningIpv6) {
514 // Auto-generation of the IPv6 link local address
515 // using the mac address
516 Ip6Address targetIp6Address = Ip6Address.valueOf(
517 getLinkLocalAddress(hostId.mac().toBytes())
518 );
519 // If we already know this guy we don't need to do other
520 if (!hostDescription.ipAddress().contains(targetIp6Address)) {
521 Host host = store.getHost(hostId);
522 // Configured host, skip it.
523 if (host != null && host.configured()) {
524 return;
525 }
526 // Host does not exist in the store or the target is not known
527 if ((host == null || !host.ipAddresses().contains(targetIp6Address))) {
Charles Chan82fac582017-09-12 12:09:22 -0700528 // Use DAD to probe if interface MAC is not specified
529 MacAddress probeMac = interfaceService.getInterfacesByPort(hostDescription.location())
530 .stream().map(Interface::mac).findFirst().orElse(MacAddress.ONOS);
531 Ip6Address probeIp = !probeMac.equals(MacAddress.ONOS) ?
532 Ip6Address.valueOf(getLinkLocalAddress(probeMac.toBytes())) :
533 Ip6Address.ZERO;
Pier Luigi9b1d6262017-02-02 22:31:34 -0800534 // We send a probe using the monitoring service
535 monitor.sendProbe(
536 hostDescription.location(),
537 targetIp6Address,
Charles Chan82fac582017-09-12 12:09:22 -0700538 probeIp,
539 probeMac,
Pier Luigi9b1d6262017-02-02 22:31:34 -0800540 hostId.vlanId()
541 );
542 }
543 }
544 }
tomdb0d03f2014-08-27 16:34:15 -0700545 }
546
soumya3e6f05e2016-08-05 15:11:11 -0700547 // When a new IP is detected, remove that IP on other hosts if it exists
548 public void removeDuplicates(HostId hostId, HostDescription desc) {
549 desc.ipAddress().forEach(ip -> {
550 Set<Host> allHosts = store.getHosts(ip);
551 allHosts.forEach(eachHost -> {
552 if (!(eachHost.id().equals(hostId))) {
553 log.info("Duplicate ip {} found on host {} and {}", ip,
jobind19b7142019-05-17 09:46:52 -0400554 hostId.toString(), eachHost.id().toString());
soumya3e6f05e2016-08-05 15:11:11 -0700555 store.removeIp(eachHost.id(), ip);
556 }
557 });
558 });
Jonathan Hart38feb6e2016-08-29 22:54:16 +0000559 }
560
tomdb0d03f2014-08-27 16:34:15 -0700561 @Override
tom7869ad92014-09-09 14:32:08 -0700562 public void hostVanished(HostId hostId) {
563 checkNotNull(hostId, HOST_ID_NULL);
564 checkValidity();
pierventref9a00a52021-10-25 18:49:09 +0200565 // TODO SDFAB-718 rethink HostStore APIs to allow atomic operations
sdn94b00152016-08-30 02:12:32 -0700566 Host host = store.getHost(hostId);
Charles Chan29ecdee2017-02-22 18:46:56 -0800567
Charles Chan888e20a2017-05-01 15:44:23 -0700568 if (!allowedToChange(hostId)) {
569 log.info("Request to remove {} is ignored due to provider mismatch", hostId);
Charles Chan29ecdee2017-02-22 18:46:56 -0800570 return;
571 }
572
pierventref9a00a52021-10-25 18:49:09 +0200573 if (host == null) {
574 log.info("Request to remove {} is ignored due to host not present in the store", hostId);
575 return;
576 }
577
sdn94b00152016-08-30 02:12:32 -0700578 if (monitorHosts) {
579 host.ipAddresses().forEach(ip -> {
580 monitor.stopMonitoring(ip);
581 });
582 }
Charles Chan009c3082015-11-10 14:18:04 -0800583 store.removeHost(hostId);
tomdb0d03f2014-08-27 16:34:15 -0700584 }
samanwita palc40e5ed2015-09-24 11:01:51 -0700585
586 @Override
587 public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
588 checkNotNull(hostId, HOST_ID_NULL);
589 checkValidity();
Charles Chan888e20a2017-05-01 15:44:23 -0700590
591 if (!allowedToChange(hostId)) {
592 log.info("Request to remove {} from {} is ignored due to provider mismatch",
593 ipAddress, hostId);
594 return;
595 }
596
Charles Chan009c3082015-11-10 14:18:04 -0800597 store.removeIp(hostId, ipAddress);
samanwita palc40e5ed2015-09-24 11:01:51 -0700598 }
Charles Chan888e20a2017-05-01 15:44:23 -0700599
600 @Override
Charles Chanff79dd92018-06-01 16:33:48 -0700601 public void addLocationToHost(HostId hostId, HostLocation location) {
602 checkNotNull(hostId, HOST_ID_NULL);
603 checkValidity();
604
605 if (!allowedToChange(hostId)) {
Charles Chanb928b3f2018-07-23 15:37:08 -0700606 log.info("Request to add {} to {} is ignored due to provider mismatch",
Charles Chanff79dd92018-06-01 16:33:48 -0700607 location, hostId);
608 return;
609 }
610
611 store.appendLocation(hostId, location);
612 }
613
614 @Override
Charles Chan888e20a2017-05-01 15:44:23 -0700615 public void removeLocationFromHost(HostId hostId, HostLocation location) {
616 checkNotNull(hostId, HOST_ID_NULL);
617 checkValidity();
618
619 if (!allowedToChange(hostId)) {
620 log.info("Request to remove {} from {} is ignored due to provider mismatch",
621 location, hostId);
622 return;
623 }
624
625 store.removeLocation(hostId, location);
626 }
627
Charles Chand0c147a2017-09-14 14:00:10 -0700628 /**
629 * Providers should only be able to remove a host that is provided by itself,
630 * or a host that is not configured.
631 */
Charles Chan888e20a2017-05-01 15:44:23 -0700632 private boolean allowedToChange(HostId hostId) {
Charles Chan888e20a2017-05-01 15:44:23 -0700633 Host host = store.getHost(hostId);
Charles Chand0c147a2017-09-14 14:00:10 -0700634 return host == null || !host.configured() || host.providerId().equals(provider().id());
Charles Chan888e20a2017-05-01 15:44:23 -0700635 }
jobind19b7142019-05-17 09:46:52 -0400636
637
638 /**
639 * Deny host move if happening within the threshold time,
640 * track moved host to identify offending hosts.
641 *
642 * @param hostId host identifier
643 * @param locations host locations
644 */
645 private boolean shouldBlock(HostId hostId, Set<HostLocation> locations) {
646 Host host = store.getHost(hostId);
647 // If host is not present in host store means host added for hte first time.
648 if (host != null) {
649 if (host.suspended()) {
650 // Checks host is marked as offending in other onos cluster instance/local instance
651 log.debug("Host id {} is moving frequently hence host moving " +
652 "processing is ignored", hostId);
653 return true;
654 }
655 } else {
656 //host added for the first time.
657 return false;
658 }
659 HostMoveTracker hostMove = hostMoveTracker.computeIfAbsent(hostId, id -> new HostMoveTracker(locations));
660 if (Sets.difference(hostMove.getLocations(), locations).isEmpty() &&
661 Sets.difference(locations, hostMove.getLocations()).isEmpty()) {
662 log.debug("Not hostmove scenario: Host id: {}, Old Host Location: {}, New host Location: {}",
663 hostId, hostMove.getLocations(), locations);
664 return false; // It is not a host move scenario
665 } else if (hostMove.getCounter() >= hostMoveCounter && System.currentTimeMillis() - hostMove.getTimeStamp()
666 < hostMoveThresholdInMillis) {
667 //Check host move is crossed the threshold, then to mark as offending Host
668 log.debug("Host id {} is identified as offending host and entry is added in cache", hostId);
669 hostMove.resetHostMoveTracker(locations);
670 store.suspend(hostId);
671 //Set host suspended flag to false after given offendingHostExpiryInMins
672 offendingHostUnblockExecutor.schedule(new UnblockOffendingHost(hostId),
673 offendingHostExpiryInMins,
674 TimeUnit.MINUTES);
675 return true;
676 } else if (System.currentTimeMillis() - hostMove.getTimeStamp()
677 < hostMoveThresholdInMillis) {
678 //Increment the host move count as hostmove occured within the hostMoveThresholdInMillis time
679 hostMove.updateHostMoveTracker(locations);
680 log.debug("Updated the tracker with the host move registered for host: {}", hostId);
681 } else if (System.currentTimeMillis() - hostMove.getTimeStamp()
682 > hostMoveThresholdInMillis) {
683 //Hostmove is happened after hostMoveThresholdInMillis time so remove from host tracker.
684 hostMove.resetHostMoveTracker(locations);
685 store.unsuspend(hostId);
686 log.debug("Reset the tracker with the host move registered for host: {}", hostId);
687 }
688 return false;
689 }
690
691 // Set host suspended flag to false after given offendingHostExpiryInMins.
692 private final class UnblockOffendingHost implements Runnable {
693 private HostId hostId;
694
695 UnblockOffendingHost(HostId hostId) {
696 this.hostId = hostId;
697 }
698
699 @Override
700 public void run() {
701 // Set the host suspended flag to false
702 try {
703 store.unsuspend(hostId);
704 log.debug("Host {}: Marked host as unsuspended", hostId);
705 } catch (Exception ex) {
706 log.debug("Host {}: not present in host list", hostId);
707 }
708 }
709 }
tomdb0d03f2014-08-27 16:34:15 -0700710 }
tom7869ad92014-09-09 14:32:08 -0700711
jobind19b7142019-05-17 09:46:52 -0400712
tomc78acee2014-09-24 15:16:55 -0700713 // Store delegate to re-post events emitted from the store.
714 private class InternalStoreDelegate implements HostStoreDelegate {
715 @Override
716 public void notify(HostEvent event) {
717 post(event);
718 }
719 }
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700720
721 // listens for NetworkConfigEvents of type BasicHostConfig and removes
722 // links that the config does not allow
723 private class InternalNetworkConfigListener implements NetworkConfigListener {
724 @Override
Simon Huntffbad3b2017-05-16 15:37:51 -0700725 public boolean isRelevant(NetworkConfigEvent event) {
726 return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
727 || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100728 && (event.configClass().equals(BasicHostConfig.class)
729 || event.configClass().equals(HostAnnotationConfig.class));
Simon Huntffbad3b2017-05-16 15:37:51 -0700730 }
731
732 @Override
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700733 public void event(NetworkConfigEvent event) {
Simon Huntffbad3b2017-05-16 15:37:51 -0700734 log.debug("Detected host network config event {}", event.type());
735 HostEvent he = null;
736
737 HostId hostId = (HostId) event.subject();
738 BasicHostConfig cfg =
739 networkConfigService.getConfig(hostId, BasicHostConfig.class);
740
741 if (!isAllowed(cfg)) {
742 kickOutBadHost(hostId);
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100743 } else if (event.configClass().equals(BasicHostConfig.class)) {
Simon Huntffbad3b2017-05-16 15:37:51 -0700744 Host host = getHost(hostId);
745 HostDescription desc =
746 (host == null) ? null : BasicHostOperator.descriptionOf(host);
747 desc = BasicHostOperator.combine(cfg, desc);
748 if (desc != null) {
749 he = store.createOrUpdateHost(host.providerId(), hostId, desc, false);
750 }
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100751 } else if (event.configClass().equals(HostAnnotationConfig.class)) {
752 Host host = getHost(hostId);
Ray Milkey718e4382018-12-05 09:08:01 -0800753 HostProvider hp = (host == null) ? null : getProvider(host.providerId());
Rafał Szaleckide5cf842018-11-17 13:30:01 +0100754 HostDescription desc = (host == null) ? null : BasicHostOperator.descriptionOf(host);
755 Optional<Config> prevConfig = event.prevConfig();
756 log.debug("Host annotations: {} prevconfig {} desc {}", hostId, prevConfig, desc);
757 desc = hostAnnotationOperator.combine(hostId, desc, prevConfig);
758 if (desc != null && hp != null) {
759 log.debug("Host annotations update - updated host description :{}", desc.toString());
760 he = store.createOrUpdateHost(hp.id(), hostId, desc, false);
761 if (he != null && he.subject() != null) {
762 log.debug("Host annotations update - Host Event : {}", he.subject().annotations());
763 }
764 }
Simon Huntffbad3b2017-05-16 15:37:51 -0700765 }
766
767 if (he != null) {
768 post(he);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700769 }
770 }
771 }
772
Simon Huntffbad3b2017-05-16 15:37:51 -0700773 // by default allowed, otherwise check flag
774 private boolean isAllowed(BasicHostConfig cfg) {
775 return (cfg == null || cfg.isAllowed());
776 }
777
778 // removes the specified host, if it exists
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700779 private void kickOutBadHost(HostId hostId) {
Simon Huntffbad3b2017-05-16 15:37:51 -0700780 Host badHost = getHost(hostId);
781 if (badHost != null) {
782 removeHost(hostId);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700783 }
784 }
tomdb0d03f2014-08-27 16:34:15 -0700785}