Automated RPD host relearn.
Change-Id: Ie35e2c51cc4d2aa62a7c230744ae54c05def207c
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 08ba908..7f935dc 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
@@ -45,10 +45,12 @@
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
import org.onlab.packet.MacAddress;
import org.onlab.packet.IpPrefix;
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;
@@ -59,6 +61,7 @@
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.IndirectDhcpRelayConfig;
import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
import org.onosproject.dhcprelay.store.DhcpRecord;
@@ -75,6 +78,7 @@
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
@@ -84,6 +88,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.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
@@ -98,6 +103,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CopyOnWriteArraySet;
+
import static org.onlab.util.Tools.groupedThreads;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
@@ -110,7 +117,6 @@
public class DhcpRelayManager implements DhcpRelayService {
public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
public static final String ROUTE_STORE_IMPL = "org.onosproject.routeservice.store.RouteStoreImpl";
-
private static final int DEFAULT_POOL_SIZE = 32;
private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
@@ -118,6 +124,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,
@@ -155,6 +162,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();
+ }
}
);
@@ -206,7 +222,16 @@
label = "Enable DhcpRelay Fpm")
protected boolean dhcpFpmEnabled = false;
+ @Property(name = "dhcpHostRelearnProbeInterval", intValue = 500,
+ label = "dhcp host relearn probe interval in millis")
+ protected int dhcpHostRelearnProbeInterval = 500;
+
+ @Property(name = "dhcpHostRelearnProbeCount", intValue = 3,
+ label = "dhcp host relearn probe count")
+ protected int dhcpHostRelearnProbeCount = 3;
+
private ScheduledExecutorService timerExecutor;
+ private ScheduledExecutorService executorService = null;
protected ExecutorService devEventExecutor;
private ExecutorService packetExecutor;
@@ -214,6 +239,9 @@
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.
*/
@@ -224,6 +252,7 @@
}
};
+
@Activate
protected void activate(ComponentContext context) {
//start the dhcp relay agent
@@ -253,6 +282,8 @@
"distributed", Boolean.TRUE.toString());
compCfgService.registerProperties(getClass());
+ executorService = Executors.newScheduledThreadPool(POOL_SIZE);
+
deviceService.addListener(deviceListener);
log.info("DHCP-RELAY Started");
@@ -272,7 +303,7 @@
packetExecutor.shutdown();
timerExecutor = null;
packetExecutor = null;
-
+ executorService.shutdown();
log.info("DHCP-RELAY Stopped");
}
@@ -344,6 +375,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);
@@ -354,6 +387,9 @@
if (ignoreDhcpConfig != null) {
updateConfig(ignoreDhcpConfig);
}
+ if (hostAutoRelearnConfig != null) {
+ updateConfig(hostAutoRelearnConfig);
+ }
}
/**
@@ -375,6 +411,9 @@
v4Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
v6Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
}
+ if (config instanceof HostAutoRelearnConfig) {
+ setHostAutoRelearnConfig((HostAutoRelearnConfig) config);
+ }
}
protected void removeConfig(Config config) {
@@ -422,10 +461,12 @@
public Collection<DhcpRecord> getDhcpRecords() {
return dhcpRelayStore.getDhcpRecords();
}
+
@Override
public void updateDhcpRecord(HostId hostId, DhcpRecord dhcpRecord) {
dhcpRelayStore.updateDhcpRecord(hostId, dhcpRecord);
}
+
@Override
public Optional<MacAddress> getDhcpServerMacAddress() {
// TODO: depreated it
@@ -498,6 +539,7 @@
private class DhcpRelayPacketProcessor implements PacketProcessor {
+
@Override
public void process(PacketContext context) {
packetExecutor.execute(() -> processInternal(context));
@@ -616,7 +658,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());
@@ -629,7 +672,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);
@@ -637,12 +680,40 @@
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 (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 message on CP {} Mac {} Vlan{}",
+ i, cp, i.macAddress(), i.vlanId());
+ 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();
@@ -664,6 +735,76 @@
}
}
+ 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;
+ }
+
+ byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
+ Ethernet ethernet = NeighborSolicitation.buildNdpSolicit(
+ this.ipv6Address,
+ Ip6Address.valueOf(senderIpAddress),
+ this.ipv6Address, //destip
+ MacAddress.valueOf(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) {
@@ -684,5 +825,4 @@
return dhcpFpmPrefixStore.removeFpmRecord(prefix);
}
-
}
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;
+ }
+}