| /* |
| * Copyright 2014-present Open Networking Foundation |
| * |
| * 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 com.google.common.collect.Sets; |
| import org.onlab.packet.Ip6Address; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.VlanId; |
| import org.onlab.util.Tools; |
| import org.onosproject.cfg.ComponentConfigService; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.HostLocation; |
| import org.onosproject.net.config.Config; |
| import org.onosproject.net.config.NetworkConfigEvent; |
| import org.onosproject.net.config.NetworkConfigListener; |
| import org.onosproject.net.config.NetworkConfigService; |
| import org.onosproject.net.config.basics.BasicHostConfig; |
| import org.onosproject.net.config.basics.HostAnnotationConfig; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.edge.EdgePortService; |
| import org.onosproject.net.host.HostAdminService; |
| import org.onosproject.net.host.HostDescription; |
| import org.onosproject.net.host.HostEvent; |
| import org.onosproject.net.host.HostListener; |
| import org.onosproject.net.host.HostProvider; |
| import org.onosproject.net.host.HostProviderRegistry; |
| import org.onosproject.net.host.HostProviderService; |
| import org.onosproject.net.host.HostService; |
| import org.onosproject.net.host.HostStore; |
| import org.onosproject.net.host.HostStoreDelegate; |
| import org.onosproject.net.intf.Interface; |
| import org.onosproject.net.intf.InterfaceService; |
| import org.onosproject.net.packet.PacketService; |
| import org.onosproject.net.provider.AbstractListenerProviderRegistry; |
| import org.onosproject.net.provider.AbstractProviderService; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Modified; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.slf4j.Logger; |
| |
| import java.util.Dictionary; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Preconditions.checkState; |
| import static com.google.common.base.Strings.isNullOrEmpty; |
| import static org.onlab.packet.IPv6.getLinkLocalAddress; |
| import static org.onlab.util.Tools.get; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_ALLOW_DUPLICATE_IPS_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_GREEDY_LEARNING_IPV6; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_GREEDY_LEARNING_IPV6_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_MONITOR_HOSTS; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_MONITOR_HOSTS_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_PROBE_RATE_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_COUNTER_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE; |
| import static org.onosproject.net.OsgiPropertyConstants.HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT; |
| |
| import static org.onosproject.security.AppGuard.checkPermission; |
| import static org.onosproject.security.AppPermission.Type.HOST_EVENT; |
| import static org.onosproject.security.AppPermission.Type.HOST_READ; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Provides basic implementation of the host SB & NB APIs. |
| */ |
| @Component( |
| immediate = true, |
| service = { |
| HostService.class, |
| HostAdminService.class, |
| HostProviderRegistry.class |
| }, |
| property = { |
| HM_ALLOW_DUPLICATE_IPS + ":Boolean=" + HM_ALLOW_DUPLICATE_IPS_DEFAULT, |
| HM_MONITOR_HOSTS + ":Boolean=" + HM_MONITOR_HOSTS_DEFAULT, |
| HM_PROBE_RATE + ":Integer=" + HM_PROBE_RATE_DEFAULT, |
| HM_GREEDY_LEARNING_IPV6 + ":Boolean=" + HM_GREEDY_LEARNING_IPV6_DEFAULT, |
| HM_HOST_MOVE_TRACKER_ENABLE + ":Boolean=" + HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT, |
| HM_HOST_MOVED_THRESHOLD_IN_MILLIS + ":Integer=" + HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT, |
| HM_HOST_MOVE_COUNTER + ":Integer=" + HM_HOST_MOVE_COUNTER_DEFAULT, |
| HM_OFFENDING_HOST_EXPIRY_IN_MINS + ":Long=" + HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT, |
| HM_OFFENDING_HOST_THREADS_POOL_SIZE + ":Integer=" + HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT |
| |
| |
| } |
| ) |
| public class HostManager |
| extends AbstractListenerProviderRegistry<HostEvent, HostListener, HostProvider, HostProviderService> |
| implements HostService, HostAdminService, HostProviderRegistry { |
| |
| private final Logger log = getLogger(getClass()); |
| |
| public static final String HOST_ID_NULL = "Host ID cannot be null"; |
| |
| private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener(); |
| |
| private HostStoreDelegate delegate = new InternalStoreDelegate(); |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected HostStore store; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected DeviceService deviceService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected PacketService packetService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected NetworkConfigService networkConfigService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected InterfaceService interfaceService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected EdgePortService edgePortService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ComponentConfigService cfgService; |
| |
| /** Enable removal of duplicate ip address. */ |
| private boolean allowDuplicateIps = HM_ALLOW_DUPLICATE_IPS_DEFAULT; |
| |
| /** Enable/Disable monitoring of hosts. */ |
| private boolean monitorHosts = HM_MONITOR_HOSTS_DEFAULT; |
| |
| /** Set the probe Rate in milli seconds. */ |
| private long probeRate = HM_PROBE_RATE_DEFAULT; |
| |
| /** Enable/Disable greedy learning of IPv6 link local address. */ |
| private boolean greedyLearningIpv6 = HM_GREEDY_LEARNING_IPV6_DEFAULT; |
| |
| /** Enable/Disable tracking of rogue host moves. */ |
| private boolean hostMoveTrackerEnabled = HM_HOST_MOVE_TRACKER_ENABLE_DEFAULT; |
| |
| /** Host move threshold in milli seconds. */ |
| private int hostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT; |
| |
| /** If the host move happening within given threshold then increment the host move counter. */ |
| private int hostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT; |
| |
| /** Max value of the counter after which the host will not be considered as offending host. */ |
| private long offendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT; |
| |
| /** Default pool size of offending host clear executor thread. */ |
| private int offendingHostClearThreadPool = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT; |
| |
| private HostMonitor monitor; |
| private HostAnnotationOperator hostAnnotationOperator; |
| private ScheduledExecutorService offendingHostUnblockExecutor = null; |
| private Map<HostId, HostMoveTracker> hostMoveTracker = new ConcurrentHashMap<>(); |
| |
| |
| @Activate |
| public void activate(ComponentContext context) { |
| hostAnnotationOperator = new HostAnnotationOperator(networkConfigService); |
| store.setDelegate(delegate); |
| eventDispatcher.addSink(HostEvent.class, listenerRegistry); |
| cfgService.registerProperties(getClass()); |
| networkConfigService.addListener(networkConfigListener); |
| monitor = new HostMonitor(packetService, this, interfaceService, edgePortService); |
| monitor.setProbeRate(probeRate); |
| monitor.start(); |
| cfgService.registerProperties(getClass()); |
| modified(context); |
| log.info("Started"); |
| } |
| |
| @Deactivate |
| public void deactivate() { |
| store.unsetDelegate(delegate); |
| eventDispatcher.removeSink(HostEvent.class); |
| networkConfigService.removeListener(networkConfigListener); |
| cfgService.unregisterProperties(getClass(), false); |
| monitor.shutdown(); |
| if (offendingHostUnblockExecutor != null) { |
| offendingHostUnblockExecutor.shutdown(); |
| } |
| log.info("Stopped"); |
| } |
| |
| @Modified |
| public void modified(ComponentContext context) { |
| boolean oldValue = monitorHosts; |
| readComponentConfiguration(context); |
| if (probeRate > 0) { |
| monitor.setProbeRate(probeRate); |
| } else { |
| log.warn("ProbeRate cannot be less than 0"); |
| } |
| |
| if (oldValue != monitorHosts) { |
| if (monitorHosts) { |
| startMonitoring(); |
| } else { |
| stopMonitoring(); |
| } |
| } |
| } |
| |
| /** |
| * Extracts properties from the component configuration context. |
| * |
| * @param context the component context |
| */ |
| private void readComponentConfiguration(ComponentContext context) { |
| Dictionary<?, ?> properties = context.getProperties(); |
| Boolean flag; |
| int newHostMoveThresholdInMillis; |
| int newHostMoveCounter; |
| int newOffendinghostPoolSize; |
| long newOffendingHostExpiryInMins; |
| |
| flag = Tools.isPropertyEnabled(properties, HM_MONITOR_HOSTS); |
| if (flag == null) { |
| log.info("monitorHosts is not enabled " + |
| "using current value of {}", monitorHosts); |
| } else { |
| monitorHosts = flag; |
| log.info("Configured. monitorHosts {}", |
| monitorHosts ? "enabled" : "disabled"); |
| } |
| |
| Long longValue = Tools.getLongProperty(properties, HM_PROBE_RATE); |
| if (longValue == null || longValue == 0) { |
| log.info("probeRate is not set sing default value of {}", probeRate); |
| } else { |
| probeRate = longValue; |
| log.info("Configured. probeRate {}", probeRate); |
| } |
| |
| flag = Tools.isPropertyEnabled(properties, HM_ALLOW_DUPLICATE_IPS); |
| if (flag == null) { |
| log.info("Removal of duplicate ip address is not configured"); |
| } else { |
| allowDuplicateIps = flag; |
| log.info("Removal of duplicate ip address is {}", |
| allowDuplicateIps ? "disabled" : "enabled"); |
| } |
| |
| flag = Tools.isPropertyEnabled(properties, HM_GREEDY_LEARNING_IPV6); |
| if (flag == null) { |
| log.info("greedy learning is not enabled " + |
| "using current value of {}", greedyLearningIpv6); |
| } else { |
| greedyLearningIpv6 = flag; |
| log.info("Configured. greedyLearningIpv6 {}", |
| greedyLearningIpv6 ? "enabled" : "disabled"); |
| } |
| flag = Tools.isPropertyEnabled(properties, HM_HOST_MOVE_TRACKER_ENABLE); |
| if (flag == null) { |
| log.info("Host move tracker is not configured " + |
| "using current value of {}", hostMoveTrackerEnabled); |
| } else { |
| hostMoveTrackerEnabled = flag; |
| log.info("Configured. hostMoveTrackerEnabled {}", |
| hostMoveTrackerEnabled ? "enabled" : "disabled"); |
| |
| //On enable cfg ,sets default configuration vales added , else use the default values |
| properties = context.getProperties(); |
| try { |
| String s = get(properties, HM_HOST_MOVED_THRESHOLD_IN_MILLIS); |
| newHostMoveThresholdInMillis = isNullOrEmpty(s) ? |
| hostMoveThresholdInMillis : Integer.parseInt(s.trim()); |
| |
| s = get(properties, HM_HOST_MOVE_COUNTER); |
| newHostMoveCounter = isNullOrEmpty(s) ? hostMoveCounter : Integer.parseInt(s.trim()); |
| |
| s = get(properties, HM_OFFENDING_HOST_EXPIRY_IN_MINS); |
| newOffendingHostExpiryInMins = isNullOrEmpty(s) ? |
| offendingHostExpiryInMins : Integer.parseInt(s.trim()); |
| |
| s = get(properties, HM_OFFENDING_HOST_THREADS_POOL_SIZE); |
| newOffendinghostPoolSize = isNullOrEmpty(s) ? |
| offendingHostClearThreadPool : Integer.parseInt(s.trim()); |
| } catch (NumberFormatException | ClassCastException e) { |
| newHostMoveThresholdInMillis = HM_HOST_MOVED_THRESHOLD_IN_MILLIS_DEFAULT; |
| newHostMoveCounter = HM_HOST_MOVE_COUNTER_DEFAULT; |
| newOffendingHostExpiryInMins = HM_OFFENDING_HOST_EXPIRY_IN_MINS_DEFAULT; |
| newOffendinghostPoolSize = HM_OFFENDING_HOST_THREADS_POOL_SIZE_DEFAULT; |
| } |
| if (newHostMoveThresholdInMillis != hostMoveThresholdInMillis) { |
| hostMoveThresholdInMillis = newHostMoveThresholdInMillis; |
| } |
| if (newHostMoveCounter != hostMoveCounter) { |
| hostMoveCounter = newHostMoveCounter; |
| } |
| if (newOffendingHostExpiryInMins != offendingHostExpiryInMins) { |
| offendingHostExpiryInMins = newOffendingHostExpiryInMins; |
| } |
| if (hostMoveTrackerEnabled && offendingHostUnblockExecutor == null) { |
| setupThreadPool(); |
| } else if (newOffendinghostPoolSize != offendingHostClearThreadPool |
| && offendingHostUnblockExecutor != null) { |
| offendingHostClearThreadPool = newOffendinghostPoolSize; |
| offendingHostUnblockExecutor.shutdown(); |
| offendingHostUnblockExecutor = null; |
| setupThreadPool(); |
| } else if (!hostMoveTrackerEnabled && offendingHostUnblockExecutor != null) { |
| offendingHostUnblockExecutor.shutdown(); |
| offendingHostUnblockExecutor = null; |
| } |
| if (newOffendinghostPoolSize != offendingHostClearThreadPool) { |
| offendingHostClearThreadPool = newOffendinghostPoolSize; |
| } |
| |
| log.debug("modified hostMoveThresholdInMillis: {}, hostMoveCounter: {}, " + |
| "offendingHostExpiryInMins: {} ", hostMoveThresholdInMillis, |
| hostMoveCounter, offendingHostExpiryInMins); |
| } |
| } |
| |
| private synchronized void setupThreadPool() { |
| offendingHostUnblockExecutor = Executors.newScheduledThreadPool(offendingHostClearThreadPool); |
| } |
| |
| /** |
| * Starts monitoring the hosts by IP Address. |
| */ |
| private void startMonitoring() { |
| store.getHosts().forEach(host -> { |
| host.ipAddresses().forEach(ip -> { |
| monitor.addMonitoringFor(ip); |
| }); |
| }); |
| } |
| |
| /** |
| * Stops monitoring the hosts by IP Address. |
| */ |
| private void stopMonitoring() { |
| store.getHosts().forEach(host -> { |
| host.ipAddresses().forEach(ip -> { |
| monitor.stopMonitoring(ip); |
| }); |
| }); |
| } |
| |
| @Override |
| protected HostProviderService createProviderService(HostProvider provider) { |
| monitor.registerHostProvider(provider); |
| return new InternalHostProviderService(provider); |
| } |
| |
| @Override |
| public int getHostCount() { |
| checkPermission(HOST_READ); |
| return store.getHostCount(); |
| } |
| |
| @Override |
| public Iterable<Host> getHosts() { |
| checkPermission(HOST_READ); |
| return store.getHosts(); |
| } |
| |
| @Override |
| public Host getHost(HostId hostId) { |
| checkPermission(HOST_READ); |
| checkNotNull(hostId, HOST_ID_NULL); |
| return store.getHost(hostId); |
| } |
| |
| @Override |
| public Set<Host> getHostsByVlan(VlanId vlanId) { |
| checkPermission(HOST_READ); |
| return store.getHosts(vlanId); |
| } |
| |
| @Override |
| public Set<Host> getHostsByMac(MacAddress mac) { |
| checkPermission(HOST_READ); |
| checkNotNull(mac, "MAC address cannot be null"); |
| return store.getHosts(mac); |
| } |
| |
| @Override |
| public Set<Host> getHostsByIp(IpAddress ip) { |
| checkPermission(HOST_READ); |
| checkNotNull(ip, "IP address cannot be null"); |
| return store.getHosts(ip); |
| } |
| |
| @Override |
| public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { |
| return getConnectedHosts(connectPoint, false); |
| } |
| |
| @Override |
| public Set<Host> getConnectedHosts(ConnectPoint connectPoint, boolean matchAuxLocations) { |
| checkPermission(HOST_READ); |
| checkNotNull(connectPoint, "Connection point cannot be null"); |
| return store.getConnectedHosts(connectPoint, matchAuxLocations); |
| } |
| |
| @Override |
| public Set<Host> getConnectedHosts(DeviceId deviceId) { |
| checkPermission(HOST_READ); |
| checkNotNull(deviceId, "Device ID cannot be null"); |
| return store.getConnectedHosts(deviceId); |
| } |
| |
| @Override |
| public void startMonitoringIp(IpAddress ip) { |
| checkPermission(HOST_EVENT); |
| monitor.addMonitoringFor(ip); |
| } |
| |
| @Override |
| public void stopMonitoringIp(IpAddress ip) { |
| checkPermission(HOST_EVENT); |
| monitor.stopMonitoring(ip); |
| } |
| |
| @Override |
| public void requestMac(IpAddress ip) { |
| // FIXME!!!! Auto-generated method stub |
| } |
| |
| @Override |
| public void removeHost(HostId hostId) { |
| checkNotNull(hostId, HOST_ID_NULL); |
| store.removeHost(hostId); |
| } |
| |
| // Personalized host provider service issued to the supplied provider. |
| private class InternalHostProviderService |
| extends AbstractProviderService<HostProvider> |
| implements HostProviderService { |
| InternalHostProviderService(HostProvider provider) { |
| super(provider); |
| } |
| |
| @Override |
| public void hostDetected(HostId hostId, HostDescription initialHostDescription, boolean replaceIps) { |
| HostDescription hostDescription = initialHostDescription; |
| checkNotNull(hostId, HOST_ID_NULL); |
| checkValidity(); |
| hostDescription = validateHost(hostDescription, hostId); |
| |
| if (!allowDuplicateIps) { |
| removeDuplicates(hostId, hostDescription); |
| } |
| |
| BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class); |
| if (!isAllowed(cfg)) { |
| log.warn("Host {} is not allowed to be added into the contol domain", hostId); |
| return; |
| } |
| |
| hostDescription = BasicHostOperator.combine(cfg, initialHostDescription); |
| HostAnnotationConfig annoConfig = networkConfigService.getConfig(hostId, HostAnnotationConfig.class); |
| if (annoConfig != null) { |
| hostDescription = hostAnnotationOperator.combine(hostId, hostDescription, Optional.of(annoConfig)); |
| } |
| |
| if (!hostMoveTrackerEnabled) { |
| store.createOrUpdateHost(provider().id(), hostId, |
| hostDescription, replaceIps); |
| } else if (!shouldBlock(hostId, hostDescription.locations())) { |
| log.debug("Host move is allowed for host with Id: {} ", hostId); |
| store.createOrUpdateHost(provider().id(), hostId, |
| hostDescription, replaceIps); |
| } else { |
| log.info("Host move is NOT allowed for host with Id: {} , removing from host store ", hostId); |
| } |
| |
| if (monitorHosts) { |
| hostDescription.ipAddress().forEach(ip -> { |
| monitor.addMonitoringFor(ip); |
| }); |
| } |
| |
| // Greedy learning of IPv6 host. We have to disable the greedy |
| // learning of configured hosts. Validate hosts each time will |
| // overwrite the learnt information with the configured information. |
| if (greedyLearningIpv6) { |
| // Auto-generation of the IPv6 link local address |
| // using the mac address |
| Ip6Address targetIp6Address = Ip6Address.valueOf( |
| getLinkLocalAddress(hostId.mac().toBytes()) |
| ); |
| // If we already know this guy we don't need to do other |
| if (!hostDescription.ipAddress().contains(targetIp6Address)) { |
| Host host = store.getHost(hostId); |
| // Configured host, skip it. |
| if (host != null && host.configured()) { |
| return; |
| } |
| // Host does not exist in the store or the target is not known |
| if ((host == null || !host.ipAddresses().contains(targetIp6Address))) { |
| // Use DAD to probe if interface MAC is not specified |
| MacAddress probeMac = interfaceService.getInterfacesByPort(hostDescription.location()) |
| .stream().map(Interface::mac).findFirst().orElse(MacAddress.ONOS); |
| Ip6Address probeIp = !probeMac.equals(MacAddress.ONOS) ? |
| Ip6Address.valueOf(getLinkLocalAddress(probeMac.toBytes())) : |
| Ip6Address.ZERO; |
| // We send a probe using the monitoring service |
| monitor.sendProbe( |
| hostDescription.location(), |
| targetIp6Address, |
| probeIp, |
| probeMac, |
| hostId.vlanId() |
| ); |
| } |
| } |
| } |
| } |
| |
| // When a new IP is detected, remove that IP on other hosts if it exists |
| public void removeDuplicates(HostId hostId, HostDescription desc) { |
| desc.ipAddress().forEach(ip -> { |
| Set<Host> allHosts = store.getHosts(ip); |
| allHosts.forEach(eachHost -> { |
| if (!(eachHost.id().equals(hostId))) { |
| log.info("Duplicate ip {} found on host {} and {}", ip, |
| hostId.toString(), eachHost.id().toString()); |
| store.removeIp(eachHost.id(), ip); |
| } |
| }); |
| }); |
| } |
| |
| // returns a HostDescription made from the union of the BasicHostConfig |
| // annotations if it exists |
| private HostDescription validateHost(HostDescription hostDescription, HostId hostId) { |
| BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class); |
| checkState(cfg == null || cfg.isAllowed(), "Host {} is not allowed", hostId); |
| |
| return BasicHostOperator.combine(cfg, hostDescription); |
| } |
| |
| @Override |
| public void hostVanished(HostId hostId) { |
| checkNotNull(hostId, HOST_ID_NULL); |
| checkValidity(); |
| Host host = store.getHost(hostId); |
| |
| if (!allowedToChange(hostId)) { |
| log.info("Request to remove {} is ignored due to provider mismatch", hostId); |
| return; |
| } |
| |
| if (monitorHosts) { |
| host.ipAddresses().forEach(ip -> { |
| monitor.stopMonitoring(ip); |
| }); |
| } |
| store.removeHost(hostId); |
| } |
| |
| @Override |
| public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { |
| checkNotNull(hostId, HOST_ID_NULL); |
| checkValidity(); |
| |
| if (!allowedToChange(hostId)) { |
| log.info("Request to remove {} from {} is ignored due to provider mismatch", |
| ipAddress, hostId); |
| return; |
| } |
| |
| store.removeIp(hostId, ipAddress); |
| } |
| |
| @Override |
| public void addLocationToHost(HostId hostId, HostLocation location) { |
| checkNotNull(hostId, HOST_ID_NULL); |
| checkValidity(); |
| |
| if (!allowedToChange(hostId)) { |
| log.info("Request to add {} to {} is ignored due to provider mismatch", |
| location, hostId); |
| return; |
| } |
| |
| store.appendLocation(hostId, location); |
| } |
| |
| @Override |
| public void removeLocationFromHost(HostId hostId, HostLocation location) { |
| checkNotNull(hostId, HOST_ID_NULL); |
| checkValidity(); |
| |
| if (!allowedToChange(hostId)) { |
| log.info("Request to remove {} from {} is ignored due to provider mismatch", |
| location, hostId); |
| return; |
| } |
| |
| store.removeLocation(hostId, location); |
| } |
| |
| /** |
| * Providers should only be able to remove a host that is provided by itself, |
| * or a host that is not configured. |
| */ |
| private boolean allowedToChange(HostId hostId) { |
| Host host = store.getHost(hostId); |
| return host == null || !host.configured() || host.providerId().equals(provider().id()); |
| } |
| |
| |
| /** |
| * Deny host move if happening within the threshold time, |
| * track moved host to identify offending hosts. |
| * |
| * @param hostId host identifier |
| * @param locations host locations |
| */ |
| private boolean shouldBlock(HostId hostId, Set<HostLocation> locations) { |
| Host host = store.getHost(hostId); |
| // If host is not present in host store means host added for hte first time. |
| if (host != null) { |
| if (host.suspended()) { |
| // Checks host is marked as offending in other onos cluster instance/local instance |
| log.debug("Host id {} is moving frequently hence host moving " + |
| "processing is ignored", hostId); |
| return true; |
| } |
| } else { |
| //host added for the first time. |
| return false; |
| } |
| HostMoveTracker hostMove = hostMoveTracker.computeIfAbsent(hostId, id -> new HostMoveTracker(locations)); |
| if (Sets.difference(hostMove.getLocations(), locations).isEmpty() && |
| Sets.difference(locations, hostMove.getLocations()).isEmpty()) { |
| log.debug("Not hostmove scenario: Host id: {}, Old Host Location: {}, New host Location: {}", |
| hostId, hostMove.getLocations(), locations); |
| return false; // It is not a host move scenario |
| } else if (hostMove.getCounter() >= hostMoveCounter && System.currentTimeMillis() - hostMove.getTimeStamp() |
| < hostMoveThresholdInMillis) { |
| //Check host move is crossed the threshold, then to mark as offending Host |
| log.debug("Host id {} is identified as offending host and entry is added in cache", hostId); |
| hostMove.resetHostMoveTracker(locations); |
| store.suspend(hostId); |
| //Set host suspended flag to false after given offendingHostExpiryInMins |
| offendingHostUnblockExecutor.schedule(new UnblockOffendingHost(hostId), |
| offendingHostExpiryInMins, |
| TimeUnit.MINUTES); |
| return true; |
| } else if (System.currentTimeMillis() - hostMove.getTimeStamp() |
| < hostMoveThresholdInMillis) { |
| //Increment the host move count as hostmove occured within the hostMoveThresholdInMillis time |
| hostMove.updateHostMoveTracker(locations); |
| log.debug("Updated the tracker with the host move registered for host: {}", hostId); |
| } else if (System.currentTimeMillis() - hostMove.getTimeStamp() |
| > hostMoveThresholdInMillis) { |
| //Hostmove is happened after hostMoveThresholdInMillis time so remove from host tracker. |
| hostMove.resetHostMoveTracker(locations); |
| store.unsuspend(hostId); |
| log.debug("Reset the tracker with the host move registered for host: {}", hostId); |
| } |
| return false; |
| } |
| |
| // Set host suspended flag to false after given offendingHostExpiryInMins. |
| private final class UnblockOffendingHost implements Runnable { |
| private HostId hostId; |
| |
| UnblockOffendingHost(HostId hostId) { |
| this.hostId = hostId; |
| } |
| |
| @Override |
| public void run() { |
| // Set the host suspended flag to false |
| try { |
| store.unsuspend(hostId); |
| log.debug("Host {}: Marked host as unsuspended", hostId); |
| } catch (Exception ex) { |
| log.debug("Host {}: not present in host list", hostId); |
| } |
| } |
| } |
| } |
| |
| |
| // Store delegate to re-post events emitted from the store. |
| private class InternalStoreDelegate implements HostStoreDelegate { |
| @Override |
| public void notify(HostEvent event) { |
| post(event); |
| } |
| } |
| |
| // listens for NetworkConfigEvents of type BasicHostConfig and removes |
| // links that the config does not allow |
| private class InternalNetworkConfigListener implements NetworkConfigListener { |
| @Override |
| public boolean isRelevant(NetworkConfigEvent event) { |
| return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED |
| || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) |
| && (event.configClass().equals(BasicHostConfig.class) |
| || event.configClass().equals(HostAnnotationConfig.class)); |
| } |
| |
| @Override |
| public void event(NetworkConfigEvent event) { |
| log.debug("Detected host network config event {}", event.type()); |
| HostEvent he = null; |
| |
| HostId hostId = (HostId) event.subject(); |
| BasicHostConfig cfg = |
| networkConfigService.getConfig(hostId, BasicHostConfig.class); |
| |
| if (!isAllowed(cfg)) { |
| kickOutBadHost(hostId); |
| } else if (event.configClass().equals(BasicHostConfig.class)) { |
| Host host = getHost(hostId); |
| HostDescription desc = |
| (host == null) ? null : BasicHostOperator.descriptionOf(host); |
| desc = BasicHostOperator.combine(cfg, desc); |
| if (desc != null) { |
| he = store.createOrUpdateHost(host.providerId(), hostId, desc, false); |
| } |
| } else if (event.configClass().equals(HostAnnotationConfig.class)) { |
| Host host = getHost(hostId); |
| HostProvider hp = (host == null) ? null : getProvider(host.providerId()); |
| HostDescription desc = (host == null) ? null : BasicHostOperator.descriptionOf(host); |
| Optional<Config> prevConfig = event.prevConfig(); |
| log.debug("Host annotations: {} prevconfig {} desc {}", hostId, prevConfig, desc); |
| desc = hostAnnotationOperator.combine(hostId, desc, prevConfig); |
| if (desc != null && hp != null) { |
| log.debug("Host annotations update - updated host description :{}", desc.toString()); |
| he = store.createOrUpdateHost(hp.id(), hostId, desc, false); |
| if (he != null && he.subject() != null) { |
| log.debug("Host annotations update - Host Event : {}", he.subject().annotations()); |
| } |
| } |
| } |
| |
| if (he != null) { |
| post(he); |
| } |
| } |
| } |
| |
| // by default allowed, otherwise check flag |
| private boolean isAllowed(BasicHostConfig cfg) { |
| return (cfg == null || cfg.isAllowed()); |
| } |
| |
| // removes the specified host, if it exists |
| private void kickOutBadHost(HostId hostId) { |
| Host badHost = getHost(hostId); |
| if (badHost != null) { |
| removeHost(hostId); |
| } |
| } |
| } |