package org.opendaylight.netvirt.vpnmanager.iplearn;
-import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
-import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
import org.opendaylight.genius.mdsalutil.NWUtil;
+import org.opendaylight.genius.utils.JvmGlobalLocks;
import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
+import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
import org.opendaylight.netvirt.vpnmanager.VpnUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefixBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortEventAction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.adjacency.list.Adjacency;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
+import org.opendaylight.yangtools.yang.common.Uint64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger LOG = LoggerFactory.getLogger(AbstractIpLearnNotificationHandler.class);
// temp where Key is VPNInstance+IP and value is timestamp
- private final Cache<Pair<String, String>, BigInteger> migrateIpCache;
+ private final Cache<Pair<String, String>, Uint64> migrateIpCache;
- protected final DataBroker dataBroker;
- protected final IdManagerService idManager;
- protected final IInterfaceManager interfaceManager;
protected final VpnConfig config;
protected final VpnUtil vpnUtil;
+ protected final INeutronVpnManager neutronVpnManager;
+ private long bootupTime = 0L;
- public AbstractIpLearnNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
- IInterfaceManager interfaceManager, VpnConfig vpnConfig, VpnUtil vpnUtil) {
- this.dataBroker = dataBroker;
- this.idManager = idManager;
- this.interfaceManager = interfaceManager;
+ public AbstractIpLearnNotificationHandler(VpnConfig vpnConfig, VpnUtil vpnUtil,
+ INeutronVpnManager neutronVpnManager) {
this.config = vpnConfig;
this.vpnUtil = vpnUtil;
+ this.neutronVpnManager = neutronVpnManager;
- long duration = config.getIpLearnTimeout() * 10;
+ long duration = config.getIpLearnTimeout().toJava() * 10;
long cacheSize = config.getMigrateIpCacheSize().longValue();
migrateIpCache =
CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterWrite(duration,
TimeUnit.MILLISECONDS).build();
+ this.bootupTime = System.currentTimeMillis();
}
protected void validateAndProcessIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac,
- IpAddress targetIP, BigInteger metadata) {
+ IpAddress targetIP, Uint64 metadata) {
List<Adjacency> adjacencies = vpnUtil.getAdjacenciesForVpnInterfaceFromConfig(srcInterface);
- IpVersionChoice srcIpVersion = vpnUtil.getIpVersionFromString(srcIP.stringValue());
- Uuid srcIpSubnetId = null;
- if (adjacencies != null) {
+ IpVersionChoice srcIpVersion = VpnUtil.getIpVersionFromString(srcIP.stringValue());
+ boolean isSrcIpVersionPartOfVpn = false;
+ if (adjacencies != null && !adjacencies.isEmpty()) {
for (Adjacency adj : adjacencies) {
IpPrefix ipPrefix = IpPrefixBuilder.getDefaultInstance(adj.getIpAddress());
// If extra/static route is configured, we should ignore for learning process
if (NWUtil.isIpAddressInRange(srcIP, ipPrefix)) {
return;
}
- IpVersionChoice currentAdjIpVersion = vpnUtil.getIpVersionFromString(adj.getIpAddress());
+ IpVersionChoice currentAdjIpVersion = VpnUtil.getIpVersionFromString(adj.getIpAddress());
if (srcIpVersion.isIpVersionChosen(currentAdjIpVersion)) {
- srcIpSubnetId = adj.getSubnetId();
+ isSrcIpVersionPartOfVpn = true;
}
}
+ //If srcIP version is not part of the srcInterface VPN Adjacency, ignore IpLearning process
+ if (!isSrcIpVersionPartOfVpn) {
+ return;
+ }
}
LOG.trace("ARP/NA Notification Response Received from interface {} and IP {} having MAC {}, learning MAC",
srcInterface, srcIP.stringValue(), srcMac.getValue());
- processIpLearning(srcInterface, srcIP, srcMac, metadata, targetIP, srcIpSubnetId);
+ processIpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
}
- protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, BigInteger metadata,
- IpAddress dstIP, Uuid srcIpSubnetId) {
- if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
- Optional<List<String>> vpnList = vpnUtil.getVpnHandlingIpv4AssociatedWithInterface(srcInterface);
- if (vpnList.isPresent()) {
- String srcIpToQuery = srcIP.stringValue();
- String destIpToQuery = dstIP.stringValue();
- for (String vpnName : vpnList.get()) {
- LOG.info("Received ARP/NA for sender MAC {} and sender IP {} via interface {}",
- srcMac.getValue(), srcIpToQuery, srcInterface);
- VpnPortipToPort vpnPortipToPort =
- vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
- if (vpnPortipToPort != null) {
- /* This is a well known neutron port and so should be ignored
- * from being discovered
- */
+ protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, Uint64 metadata,
+ IpAddress dstIP) {
+
+ if (!VpnUtil.isArpLearningEnabled()) {
+ LOG.trace("Not handling packet as ARP Based Learning is disabled");
+ return;
+ }
+ if (metadata == null || Objects.equals(metadata, Uint64.ZERO)) {
+ return;
+ }
+
+ Optional<List<String>> vpnList = vpnUtil.getVpnHandlingIpv4AssociatedWithInterface(srcInterface);
+ if (!vpnList.isPresent()) {
+ LOG.info("IP LEARN NO_RESOLVE: VPN not configured. Ignoring responding to ARP/NA requests from this"
+ + " Interface {}.", srcInterface);
+ return;
+ }
+
+ String srcIpToQuery = srcIP.stringValue();
+ String destIpToQuery = dstIP.stringValue();
+ for (String vpnName : vpnList.get()) {
+ LOG.info("Received ARP/NA for sender MAC {} and sender IP {} via interface {}", srcMac.getValue(),
+ srcIpToQuery, srcInterface);
+ final ReentrantLock lock = lockFor(vpnName, srcIpToQuery);
+ lock.lock();
+ try {
+ VpnPortipToPort vpnPortipToPort = vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
+ // Check if this IP belongs to external network
+ if (vpnPortipToPort == null) {
+ String extSubnetId = vpnUtil.getAssociatedExternalSubnet(srcIpToQuery);
+ if (extSubnetId != null) {
+ vpnPortipToPort =
+ vpnUtil.getNeutronPortFromVpnPortFixedIp(extSubnetId, srcIpToQuery);
+ }
+ }
+ if (vpnPortipToPort != null && !vpnPortipToPort.isLearntIp()) {
+ /*
+ * This is a well known neutron port and so should be ignored from being
+ * discovered...unless it is an Octavia VIP
+ */
+ String portName = vpnPortipToPort.getPortName();
+ Port neutronPort = neutronVpnManager.getNeutronPort(portName);
+
+ if (neutronPort == null) {
+ LOG.warn("{} should have been a neutron port but could not retrieve it. Aborting processing",
+ portName);
continue;
}
- if (srcIpSubnetId != null) {
- Subnetmap snMap = vpnUtil.getSubnetmapFromItsUuid(srcIpSubnetId);
- if (snMap != null && snMap.getVpnId() == null) {
- /* If the subnet is not part of vpn then it should be ignored
- * from being discovered. This use case will come for dual stack
- * network. i.e V6 or V4 subnet only part of VPN.
- */
- continue;
- }
+
+ if (!"Octavia".equals(neutronPort.getDeviceOwner())) {
+ LOG.debug("Neutron port {} is not an Octavia port, ignoring", portName);
+ continue;
}
- LearntVpnVipToPort learntVpnVipToPort = vpnUtil.getLearntVpnVipToPort(vpnName, srcIpToQuery);
- if (learntVpnVipToPort != null) {
- String oldPortName = learntVpnVipToPort.getPortName();
- String oldMac = learntVpnVipToPort.getMacAddress();
- if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
- //MAC has changed for requested IP
- LOG.info("ARP/NA Source IP/MAC data modified for IP {} with MAC {} and Port {}",
- srcIpToQuery, srcMac, srcInterface);
- synchronized ((vpnName + srcIpToQuery).intern()) {
- vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery,
- oldPortName, oldMac, LearntVpnVipToPortEventAction.Delete, null);
- putVpnIpToMigrateIpCache(vpnName, srcIpToQuery, srcMac);
- }
- }
- } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
- learnMacFromIncomingPacket(vpnName, srcInterface, srcIP, srcMac, dstIP);
+ }
+ // For IPs learnt before cluster-reboot/upgrade, GARP/ArpResponse is received
+ // within 300sec
+ // after reboot, it would be ignored.
+ if (vpnPortipToPort != null && vpnPortipToPort.isLearntIp()) {
+ if (System.currentTimeMillis()
+ < this.bootupTime + config.getBootDelayArpLearning().toJava() * 1000) {
+ LOG.trace("GARP/Arp Response not handled for IP {} vpnName {} for time {}s",
+ vpnPortipToPort.getPortFixedip(), vpnName, config.getBootDelayArpLearning());
+ continue;
}
}
- } else {
- LOG.info("IP LEARN NO_RESOLVE: VPN not configured. Ignoring responding to ARP/NA requests from this"
- + " Interface {}.", srcInterface);
- return;
-
+ LearntVpnVipToPort learntVpnVipToPort = vpnUtil.getLearntVpnVipToPort(vpnName, srcIpToQuery);
+ if (learntVpnVipToPort != null) {
+ String oldPortName = learntVpnVipToPort.getPortName();
+ String oldMac = learntVpnVipToPort.getMacAddress();
+ if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
+ // MAC has changed for requested IP
+ LOG.info("ARP/NA Source IP/MAC data modified for IP {} with MAC {} and Port {}", srcIpToQuery,
+ srcMac, srcInterface);
+ vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery, oldPortName, oldMac,
+ LearntVpnVipToPortEventAction.Delete, null);
+ putVpnIpToMigrateIpCache(vpnName, srcIpToQuery, srcMac);
+ }
+ } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
+ if (vpnPortipToPort != null && !vpnPortipToPort.getPortName().equals(srcInterface)) {
+ LOG.trace(
+ "LearntIp: {} vpnName {} is already present in VpnPortIpToPort with " + "PortName {} ",
+ srcIpToQuery, vpnName, vpnPortipToPort.getPortName());
+ vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery,
+ vpnPortipToPort.getPortName(), vpnPortipToPort.getMacAddress(),
+ LearntVpnVipToPortEventAction.Delete, null);
+ continue;
+ }
+ learnMacFromIncomingPacket(vpnName, srcInterface, srcIP, srcMac, dstIP);
+ }
+ } finally {
+ lock.unlock();
}
}
}
IpAddress dstIP) {
String srcIpToQuery = srcIP.stringValue();
String destIpToQuery = dstIP.stringValue();
- synchronized ((vpnName + srcIpToQuery).intern()) {
+ final ReentrantLock lock = lockFor(vpnName, srcIpToQuery);
+ lock.lock();
+ try {
vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery, srcInterface,
srcMac.getValue(), LearntVpnVipToPortEventAction.Add, null);
+ } finally {
+ lock.unlock();
}
}
}
LOG.debug("IP_MIGRATE_CACHE: add to dirty cache IP {} vpnName {} with MAC {}", ipToQuery, vpnName, srcMac);
migrateIpCache.put(new ImmutablePair<>(vpnName, ipToQuery),
- new BigInteger(String.valueOf(System.currentTimeMillis())));
+ Uint64.valueOf(String.valueOf(System.currentTimeMillis())));
}
private boolean isIpInMigrateCache(String vpnName, String ipToQuery) {
return false;
}
Pair<String, String> keyPair = new ImmutablePair<>(vpnName, ipToQuery);
- BigInteger prevTimeStampCached = migrateIpCache.getIfPresent(keyPair);
+ Uint64 prevTimeStampCached = migrateIpCache.getIfPresent(keyPair);
if (prevTimeStampCached == null) {
LOG.debug("IP_MIGRATE_CACHE: there is no IP {} vpnName {} in dirty cache, so learn it",
ipToQuery, vpnName);
return false;
}
- if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getIpLearnTimeout()) {
+ if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getIpLearnTimeout().toJava()) {
LOG.debug("IP_MIGRATE_CACHE: older than timeout value - remove from dirty cache IP {} vpnName {}",
ipToQuery, vpnName);
migrateIpCache.invalidate(keyPair);
ipToQuery, vpnName);
return true;
}
+
+ private static ReentrantLock lockFor(String vpnName, String srcIpToQuery) {
+ // FIXME: form an Identifier? That would side-step string concat here
+ return JvmGlobalLocks.getLockForString(vpnName + srcIpToQuery);
+ }
}