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.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.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.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.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 VpnConfig config;
protected final VpnUtil vpnUtil;
protected final INeutronVpnManager neutronVpnManager;
+ private long bootupTime = 0L;
public AbstractIpLearnNotificationHandler(VpnConfig vpnConfig, VpnUtil vpnUtil,
INeutronVpnManager neutronVpnManager) {
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());
+ IpVersionChoice srcIpVersion = VpnUtil.getIpVersionFromString(srcIP.stringValue());
boolean isSrcIpVersionPartOfVpn = false;
if (adjacencies != null && !adjacencies.isEmpty()) {
for (Adjacency adj : adjacencies) {
if (NWUtil.isIpAddressInRange(srcIP, ipPrefix)) {
return;
}
- IpVersionChoice currentAdjIpVersion = vpnUtil.getIpVersionFromString(adj.getIpAddress());
+ IpVersionChoice currentAdjIpVersion = VpnUtil.getIpVersionFromString(adj.getIpAddress());
if (srcIpVersion.isIpVersionChosen(currentAdjIpVersion)) {
isSrcIpVersionPartOfVpn = true;
}
processIpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
}
- protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, BigInteger metadata,
+ protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, Uint64 metadata,
IpAddress dstIP) {
- if (metadata == null || Objects.equals(metadata, BigInteger.ZERO)) {
+ if (!VpnUtil.isArpLearningEnabled()) {
+ LOG.trace("Not handling packet as ARP Based Learning is disabled");
+ return;
+ }
+ if (metadata == null || Objects.equals(metadata, Uint64.ZERO)) {
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);
- VpnPortipToPort vpnPortipToPort =
- vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
- if (vpnPortipToPort != null) {
- /* 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;
+ 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 (!"Octavia".equals(neutronPort.getDeviceOwner())) {
- LOG.debug("Neutron port {} is not an Octavia port, ignoring", portName);
- 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);
+ // 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;
+ }
+ }
+ 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);
}
- } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
- 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);
+ }
}