Automated RPD host relearn.
Change-Id: Ia501ca80c448a7f8f9c31dc2b6513c739d69be6d
diff --git a/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
index a8541a6..ad67681 100644
--- a/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -26,10 +26,12 @@
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborSolicitation;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
@@ -40,8 +42,10 @@
import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
import org.onosproject.dhcprelay.config.DhcpServerConfig;
import org.onosproject.dhcprelay.config.EnableDhcpFpmConfig;
+import org.onosproject.dhcprelay.config.HostAutoRelearnConfig;
import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
+import org.onosproject.mastership.MastershipService;
import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
import org.onosproject.dhcprelay.store.DhcpRecord;
import org.onosproject.dhcprelay.store.DhcpRelayStore;
@@ -49,6 +53,7 @@
import org.onosproject.net.Device;
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.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
@@ -62,6 +67,7 @@
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
+import org.onosproject.net.Port;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.packet.DefaultOutboundPacket;
@@ -93,6 +99,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -103,6 +110,10 @@
import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_FPM_ENABLED_DEFAULT;
import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_POLL_INTERVAL;
import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_POLL_INTERVAL_DEFAULT;
+import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_PROBE_INTERVAL;
+import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_PROBE_INTERVAL_DEFAULT;
+import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_PROBE_COUNT;
+import static org.onosproject.dhcprelay.OsgiPropertyConstants.DHCP_PROBE_COUNT_DEFAULT;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
@@ -115,7 +126,9 @@
property = {
ARP_ENABLED + ":Boolean=" + ARP_ENABLED_DEFAULT,
DHCP_POLL_INTERVAL + ":Integer=" + DHCP_POLL_INTERVAL_DEFAULT,
- DHCP_FPM_ENABLED + ":Boolean=" + DHCP_FPM_ENABLED_DEFAULT
+ DHCP_FPM_ENABLED + ":Boolean=" + DHCP_FPM_ENABLED_DEFAULT,
+ DHCP_PROBE_INTERVAL + ":Integer=" + DHCP_PROBE_INTERVAL_DEFAULT,
+ DHCP_PROBE_COUNT + ":Integer=" + DHCP_PROBE_COUNT_DEFAULT
}
)
public class DhcpRelayManager implements DhcpRelayService {
@@ -129,6 +142,7 @@
.build();
private final Logger log = LoggerFactory.getLogger(getClass());
private final InternalConfigListener cfgListener = new InternalConfigListener();
+ protected CopyOnWriteArraySet hostAutoRelearnEnabledDevices = new CopyOnWriteArraySet();
private final Set<ConfigFactory> factories = ImmutableSet.of(
new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
@@ -166,6 +180,15 @@
public EnableDhcpFpmConfig createConfig() {
return new EnableDhcpFpmConfig();
}
+ },
+ new ConfigFactory<ApplicationId, HostAutoRelearnConfig>(APP_SUBJECT_FACTORY,
+ HostAutoRelearnConfig.class,
+ HostAutoRelearnConfig.KEY,
+ true) {
+ @Override
+ public HostAutoRelearnConfig createConfig() {
+ return new HostAutoRelearnConfig();
+ }
}
);
@@ -174,6 +197,9 @@
protected NetworkConfigRegistry cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
@@ -214,13 +240,22 @@
/** Enable DhcpRelay Fpm. */
protected boolean dhcpFpmEnabled = DHCP_FPM_ENABLED_DEFAULT;
+ /** Dhcp host relearn probe interval in millis. */
+ protected int dhcpHostRelearnProbeInterval = DHCP_PROBE_INTERVAL_DEFAULT;
+
+ /** Dhcp host relearn probe count. */
+ protected int dhcpHostRelearnProbeCount = DHCP_PROBE_COUNT_DEFAULT;
+
private ScheduledExecutorService timerExecutor;
+ private ScheduledExecutorService executorService = null;
protected ExecutorService devEventExecutor;
private ExecutorService packetExecutor;
protected DeviceListener deviceListener = new InternalDeviceListener();
private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
private ApplicationId appId;
+ private static final int POOL_SIZE = 10;
+ private static final int HOST_PROBE_INIT_DELAY = 500;
/**
* One second timer.
@@ -261,6 +296,8 @@
"distributed", Boolean.TRUE.toString());
compCfgService.registerProperties(getClass());
+ executorService = Executors.newScheduledThreadPool(POOL_SIZE);
+
deviceService.addListener(deviceListener);
log.info("DHCP-RELAY Started");
@@ -280,6 +317,7 @@
packetExecutor.shutdown();
timerExecutor = null;
packetExecutor = null;
+ executorService.shutdown();
log.info("DHCP-RELAY Stopped");
}
@@ -352,6 +390,8 @@
cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
IgnoreDhcpConfig ignoreDhcpConfig =
cfgService.getConfig(appId, IgnoreDhcpConfig.class);
+ HostAutoRelearnConfig hostAutoRelearnConfig =
+ cfgService.getConfig(appId, HostAutoRelearnConfig.class);
if (defaultConfig != null) {
updateConfig(defaultConfig);
@@ -362,6 +402,9 @@
if (ignoreDhcpConfig != null) {
updateConfig(ignoreDhcpConfig);
}
+ if (hostAutoRelearnConfig != null) {
+ updateConfig(hostAutoRelearnConfig);
+ }
}
/**
@@ -383,6 +426,9 @@
v4Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
v6Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
}
+ if (config instanceof HostAutoRelearnConfig) {
+ setHostAutoRelearnConfig((HostAutoRelearnConfig) config);
+ }
}
protected void removeConfig(Config config) {
@@ -624,7 +670,8 @@
public boolean isRelevant(NetworkConfigEvent event) {
if (event.configClass().equals(DefaultDhcpRelayConfig.class) ||
event.configClass().equals(IndirectDhcpRelayConfig.class) ||
- event.configClass().equals(IgnoreDhcpConfig.class)) {
+ event.configClass().equals(IgnoreDhcpConfig.class) ||
+ event.configClass().equals(HostAutoRelearnConfig.class)) {
return true;
}
log.debug("Ignore irrelevant event class {}", event.configClass().getName());
@@ -637,7 +684,7 @@
@Override
public void event(DeviceEvent event) {
if (devEventExecutor != null) {
- Device device = event.subject();
+ final Device device = event.subject();
switch (event.type()) {
case DEVICE_ADDED:
devEventExecutor.execute(this::updateIgnoreVlanConfigs);
@@ -645,12 +692,44 @@
case DEVICE_AVAILABILITY_CHANGED:
devEventExecutor.execute(() -> deviceAvailabilityChanged(device));
break;
+ case PORT_UPDATED:
+ Port port = event.port();
+ devEventExecutor.execute(() -> portUpdatedEventHandler(device, port));
+ break;
default:
break;
}
}
}
+ private void portUpdatedEventHandler(Device device, Port port) {
+ if (!mastershipService.isLocalMaster(device.id())) {
+ log.warn("This instance is not the master for the device {}", device.id());
+ return;
+ }
+ if (hostAutoRelearnEnabledDevices.contains(device.id()) && port.isEnabled()) {
+ ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+ HostLocation hostLocation = new HostLocation(cp, 0);
+ Set<DhcpRecord> records = dhcpRelayStore.getDhcpRecords()
+ .stream()
+ .filter(i -> i.directlyConnected())
+ .filter(i -> i.locations().contains(hostLocation))
+ .collect(Collectors.toSet());
+
+ for (DhcpRecord i : records) {
+ //found a dhcprecord matching the connect point of the port event
+ log.debug("portUpdatedEventHandler:DHCP record {}, sending msg on CP {} Mac {} Vlan{} DeviceId {}",
+ i, cp, i.macAddress(), i.vlanId(), device.id());
+ if (i.ip4Address().isPresent()) {
+ log.warn("Sending host relearn probe for v4 not supported for Mac {} Vlan{} ip {}",
+ i.macAddress(), i.vlanId(), i.ip4Address());
+ } else if (i.ip6Address().isPresent()) {
+ sendHostRelearnProbe(cp, i.macAddress(), i.vlanId(), i.ip6Address());
+ }
+ }
+ }
+ }
+
private void deviceAvailabilityChanged(Device device) {
if (deviceService.isAvailable(device.id())) {
updateIgnoreVlanConfigs();
@@ -672,6 +751,83 @@
}
}
+ private void setHostAutoRelearnConfig(HostAutoRelearnConfig config) {
+ hostAutoRelearnEnabledDevices.clear();
+ if (config == null) {
+ return;
+ }
+ hostAutoRelearnEnabledDevices.addAll(config.hostAutoRelearnEnabledDevices());
+ }
+
+ // Packet transmission class.
+ private class PktTransmitter implements Runnable {
+
+ MacAddress mac;
+ VlanId vlanId;
+ Ip6Address ipv6Address;
+ ConnectPoint connectPoint;
+
+ PktTransmitter(MacAddress mac, VlanId vlanId, Ip6Address ipv6Address, ConnectPoint connectPoint) {
+ this.mac = mac;
+ this.vlanId = vlanId;
+ this.ipv6Address = ipv6Address;
+ this.connectPoint = connectPoint;
+ }
+
+ @Override
+ public void run() {
+ log.debug("Host Relearn probe packet transmission activated for Mac {} Vlan {} Ip {} ConnectPt {}",
+ mac, vlanId, ipv6Address, connectPoint);
+ if (mac == null || vlanId == null || ipv6Address == null || connectPoint == null) {
+ return;
+ }
+
+ Interface senderInterface = interfaceService.getInterfacesByPort(connectPoint)
+ .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanId))
+ .findFirst().orElse(null);
+ if (senderInterface == null) {
+ log.warn("Cannot get sender interface for from packet, abort... vlan {}", vlanId.toString());
+ }
+ MacAddress senderMacAddress = senderInterface.mac();
+ byte[] senderIpAddress = IPv6.getLinkLocalAddress(senderMacAddress.toBytes());
+ byte[] destIp = IPv6.getSolicitNodeAddress(ipv6Address.toOctets());
+
+ Ethernet ethernet = NeighborSolicitation.buildNdpSolicit(
+ this.ipv6Address,
+ Ip6Address.valueOf(senderIpAddress),
+ Ip6Address.valueOf(destIp), //destip
+ senderMacAddress,
+ this.mac,
+ this.vlanId);
+ sendHostRelearnProbeToConnectPoint(ethernet, connectPoint);
+
+ log.debug("Host Relearn Probe transmission completed.");
+ }
+ }
+
+ //Create packet and schedule transmitter thread.
+ private void sendHostRelearnProbe(ConnectPoint connectPoint, MacAddress mac, VlanId vlanId,
+ Optional<Ip6Address> ipv6Address) {
+ PktTransmitter nsTransmitter = new PktTransmitter(mac, vlanId, ipv6Address.get(), connectPoint);
+ executorService.schedule(nsTransmitter, HOST_PROBE_INIT_DELAY, TimeUnit.MILLISECONDS);
+ }
+
+ // Send Host Relearn Probe packets to ConnectPoint
+ private void sendHostRelearnProbeToConnectPoint(Ethernet nsPacket, ConnectPoint connectPoint) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
+ treatment, ByteBuffer.wrap(nsPacket.serialize()));
+ int counter = 0;
+ try {
+ while (counter < dhcpHostRelearnProbeCount) {
+ packetService.emit(outboundPacket);
+ counter++;
+ Thread.sleep(dhcpHostRelearnProbeInterval);
+ }
+ } catch (Exception e) {
+ log.error("Exception while emmiting packet {}", e.getMessage(), e);
+ }
+ }
public Optional<FpmRecord> getFpmRecord(IpPrefix prefix) {
diff --git a/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/OsgiPropertyConstants.java b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/OsgiPropertyConstants.java
index 7e794fe..207f766 100644
--- a/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/OsgiPropertyConstants.java
+++ b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/OsgiPropertyConstants.java
@@ -29,6 +29,12 @@
static final String DHCP_FPM_ENABLED = "dhcpFpmEnabled";
static final boolean DHCP_FPM_ENABLED_DEFAULT = false;
+ static final String DHCP_PROBE_INTERVAL = "dhcpHostRelearnProbeInterval";
+ static final int DHCP_PROBE_INTERVAL_DEFAULT = 500;
+
+ static final String DHCP_PROBE_COUNT = "dhcpHostRelearnProbeCount";
+ static final int DHCP_PROBE_COUNT_DEFAULT = 3;
+
static final String LEARN_ROUTE_FROM_LEASE_QUERY = "learnRouteFromLeasequery";
static final boolean LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT = false;
}
diff --git a/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/config/HostAutoRelearnConfig.java b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/config/HostAutoRelearnConfig.java
new file mode 100644
index 0000000..85b8c9a
--- /dev/null
+++ b/apps/dhcprelay/app/src/main/java/org/onosproject/dhcprelay/config/HostAutoRelearnConfig.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019-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.dhcprelay.config;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Dhcp Host Auto Relearn Config.
+ */
+public class HostAutoRelearnConfig extends Config<ApplicationId> {
+ public static final String KEY = "hostAutoRelearnEnabledDevices";
+
+ @Override
+ public boolean isValid() {
+ if (array == null) {
+ return false;
+ }
+ try {
+ for (JsonNode node : array) {
+ DeviceId.deviceId(node.asText());
+ }
+ } catch (IllegalArgumentException ex) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns Set of Devices on which Host Auto Relearn is enabled.
+ *
+ * @return Set of DeviceId where Host Auto Relearn is enabled.
+ */
+
+ public Set<DeviceId> hostAutoRelearnEnabledDevices() {
+ Set<DeviceId> enabledDevices = Sets.newHashSet();
+
+ array.forEach(node -> {
+ DeviceId deviceId = DeviceId.deviceId(node.asText());
+ enabledDevices.add(deviceId);
+ });
+
+ return enabledDevices;
+ }
+}