package org.opendaylight.netvirt.elan.internal;
import com.google.common.base.Optional;
+
import java.math.BigInteger;
-import java.util.Arrays;
+import java.util.Collections;
import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
-import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.genius.mdsalutil.MetaDataUtil;
import org.opendaylight.genius.mdsalutil.NWUtil;
import org.opendaylight.genius.mdsalutil.NwConstants;
import org.opendaylight.genius.mdsalutil.packet.Ethernet;
+import org.opendaylight.netvirt.elan.evpn.utils.EvpnUtils;
import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
import org.opendaylight.netvirt.elan.utils.ElanUtils;
+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.yang.types.rev130715.PhysAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._if.indexes._interface.map.IfIndexInterface;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
@SuppressWarnings("deprecation")
public class ElanPacketInHandler implements PacketProcessingListener {
- private static final Logger logger = LoggerFactory.getLogger(ElanPacketInHandler.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ElanPacketInHandler.class);
private final DataBroker broker;
private final IInterfaceManager interfaceManager;
private final ElanUtils elanUtils;
private final ElanL2GatewayUtils elanL2GatewayUtils;
+ private final EvpnUtils evpnUtils;
- public ElanPacketInHandler(DataBroker dataBroker, final IInterfaceManager interfaceManager, ElanUtils elanUtils) {
+ public ElanPacketInHandler(DataBroker dataBroker, final IInterfaceManager interfaceManager, ElanUtils elanUtils,
+ EvpnUtils evpnUtils) {
broker = dataBroker;
this.interfaceManager = interfaceManager;
this.elanUtils = elanUtils;
this.elanL2GatewayUtils = elanUtils.getElanL2GatewayUtils();
+ this.evpnUtils = evpnUtils;
}
@Override
Class<? extends PacketInReason> pktInReason = notification.getPacketInReason();
short tableId = notification.getTableId().getValue();
if (pktInReason == NoMatch.class && tableId == NwConstants.ELAN_SMAC_TABLE) {
+ ElanManagerCounters.unknown_smac_pktin_rcv.inc();
try {
byte[] data = notification.getPayload();
Ethernet res = new Ethernet();
res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
byte[] srcMac = res.getSourceMACAddress();
- String macAddress = NWUtil.toStringMacAddress(srcMac);
- PhysAddress physAddress = new PhysAddress(macAddress);
- BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
- long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
+ final String macAddress = NWUtil.toStringMacAddress(srcMac);
+ final BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
+ final long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
Optional<IfIndexInterface> interfaceInfoOp = elanUtils.getInterfaceInfoByInterfaceTag(portTag);
if (!interfaceInfoOp.isPresent()) {
- logger.warn("There is no interface for given portTag {}", portTag);
+ LOG.warn("There is no interface for given portTag {}", portTag);
return;
}
String interfaceName = interfaceInfoOp.get().getInterfaceName();
- logger.debug("Received a packet with srcMac: {} ElanTag: {} PortTag: {} InterfaceName: {}", macAddress,
+ LOG.debug("Received a packet with srcMac: {} ElanTag: {} PortTag: {} InterfaceName: {}", macAddress,
elanTag, portTag, interfaceName);
ElanTagName elanTagName = elanUtils.getElanInfoByElanTag(elanTag);
if (elanTagName == null) {
- logger.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
+ LOG.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
return;
}
String elanName = elanTagName.getName();
- MacEntry macEntry = elanUtils.getInterfaceMacEntriesOperationalDataPath(interfaceName, physAddress);
- if (macEntry != null && macEntry.getInterface() == interfaceName) {
- BigInteger macTimeStamp = macEntry.getControllerLearnedForwardingEntryTimestamp();
- if (System.currentTimeMillis() > macTimeStamp.longValue() + 2000) {
- /*
- * Protection time expired. Even though the MAC has been
- * learnt (it is in the cache) the packets are punted to
- * controller. Which means, the the flows were not
- * successfully created in the DPN, but the MAC entry
- * has been added successfully in the cache.
- *
- * So, the cache has to be cleared and the flows and
- * cache should be recreated (clearing of cache is
- * required so that the timestamp is updated).
- */
- InstanceIdentifier<MacEntry> macEntryId = ElanUtils
- .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
- ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
- } else {
- // Protection time running. Ignore packets for 2 seconds
- return;
- }
- } else if (macEntry != null) {
- // MAC address has moved. Overwrite the mapping and replace
- // MAC flows
- long macTimeStamp = macEntry.getControllerLearnedForwardingEntryTimestamp().longValue();
- if (System.currentTimeMillis() > macTimeStamp + 1000) {
-
- InstanceIdentifier<MacEntry> macEntryId = ElanUtils
- .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
- ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
- tryAndRemoveInvalidMacEntry(elanName, macEntry);
- } else {
- // New FEs flood their packets on all interfaces. This
- // can lead
- // to many contradicting packet_ins. Ignore all packets
- // received
- // within 1s after the first packet_in
- return;
- }
+ PhysAddress physAddress = new PhysAddress(macAddress);
+ MacEntry oldMacEntry = elanUtils.getMacEntryForElanInstance(elanName, physAddress).orNull();
+ boolean isVlanOrFlatProviderIface = interfaceManager.isExternalInterface(interfaceName);
+
+ Optional<IpAddress> srcIpAddress = elanUtils.getSourceIpAddress(res);
+ MacEntry newMacEntry = null;
+ BigInteger timeStamp = new BigInteger(String.valueOf(System.currentTimeMillis()));
+ if (!srcIpAddress.isPresent()) {
+ newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
+ .setKey(new MacEntryKey(physAddress))
+ .setControllerLearnedForwardingEntryTimestamp(timeStamp)
+ .setIsStaticAddress(false).build();
+ } else {
+ newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
+ .setIpPrefix(srcIpAddress.get()).setKey(new MacEntryKey(physAddress))
+ .setControllerLearnedForwardingEntryTimestamp(timeStamp)
+ .setIsStaticAddress(false).build();
}
- BigInteger timeStamp = new BigInteger(String.valueOf((long) System.currentTimeMillis()));
- macEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
- .setKey(new MacEntryKey(physAddress)).setControllerLearnedForwardingEntryTimestamp(timeStamp)
- .setIsStaticAddress(false).build();
- InstanceIdentifier<MacEntry> macEntryId = ElanUtils
- .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
- MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, macEntryId,
- macEntry);
- InstanceIdentifier<MacEntry> elanMacEntryId = ElanUtils.getMacEntryOperationalDataPath(elanName,
- physAddress);
- MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, elanMacEntryId,
- macEntry);
- ElanInstance elanInstance = elanUtils.getElanInstanceByName(elanName);
- WriteTransaction flowWritetx = broker.newWriteOnlyTransaction();
- elanUtils.setupMacFlows(elanInstance,
- interfaceManager.getInterfaceInfo(interfaceName),
- elanInstance.getMacTimeout(), macAddress, flowWritetx);
- flowWritetx.submit();
-
- BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
- elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
- Arrays.asList(physAddress));
- } catch (Exception e) {
- logger.trace("Failed to decode packet: {}", e);
+ if (srcIpAddress.isPresent()) {
+ String prefix = srcIpAddress.get().getIpv4Address().getValue();
+ InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+ ElanInstance elanInstance = elanUtils.getElanInstanceByName(broker, elanName);
+ evpnUtils.advertisePrefix(elanInstance, macAddress, prefix, interfaceName, interfaceInfo.getDpId());
+ }
+ final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+ enqueueJobForMacSpecificTasks(macAddress, elanTag, interfaceName, elanName, physAddress, oldMacEntry,
+ newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
+
+ ElanInstance elanInstance = ElanUtils.getElanInstanceByName(broker, elanName);
+ InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+ if (interfaceInfo == null) {
+ LOG.trace("Interface:{} is not present under Config DS", interfaceName);
+ return;
+ }
+ enqueueJobForDPNSpecificTasks(macAddress, elanTag, interfaceName, physAddress, elanInstance,
+ interfaceInfo, oldMacEntry, newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
+
+
+ } catch (PacketException e) {
+ LOG.error("Failed to decode packet: {}", notification, e);
}
}
+ }
+ private void enqueueJobForMacSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
+ String elanName, PhysAddress physAddress,
+ MacEntry oldMacEntry, MacEntry newMacEntry,
+ final boolean isVlanOrFlatProviderIface,
+ final DataStoreJobCoordinator portDataStoreCoordinator) {
+ portDataStoreCoordinator.enqueueJob(ElanUtils.getElanMacKey(elanTag, macAddress), () -> {
+ WriteTransaction writeTx = broker.newWriteOnlyTransaction();
+ if (oldMacEntry != null && oldMacEntry.getInterface().equals(interfaceName)) {
+ // This should never occur because of ovs temporary mac learning
+ ElanManagerCounters.unknown_smac_pktin_forwarding_entries_removed.inc();
+ } else if (oldMacEntry != null && !isVlanOrFlatProviderIface) {
+ long macTimeStamp = oldMacEntry.getControllerLearnedForwardingEntryTimestamp().longValue();
+ if (System.currentTimeMillis() > macTimeStamp + 1000) {
+ InstanceIdentifier<MacEntry> macEntryId = ElanUtils
+ .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName,
+ physAddress);
+ writeTx.delete(LogicalDatastoreType.OPERATIONAL, macEntryId);
+ } else {
+ // New FEs flood their packets on all interfaces. This
+ // can lead
+ // to many contradicting packet_ins. Ignore all packets
+ // received
+ // within 1s after the first packet_in
+ ElanManagerCounters.unknown_smac_pktin_mac_migration_ignored_due_to_protection.inc();
+ }
+ } else if (oldMacEntry != null) {
+ ElanManagerCounters.unknown_smac_pktin_removed_for_relearned.inc();
+ }
+ // This check is required only to update elan-forwarding-tables when mac is learned
+ // in ports (example: VM interfaces) other than on vlan provider port.
+ if (!isVlanOrFlatProviderIface && oldMacEntry == null) {
+ InstanceIdentifier<MacEntry> elanMacEntryId =
+ ElanUtils.getMacEntryOperationalDataPath(elanName, physAddress);
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, elanMacEntryId, newMacEntry,
+ WriteTransaction.CREATE_MISSING_PARENTS);
+ }
+ return Collections.singletonList(writeTx.submit());
+ });
+ }
+
+ private void enqueueJobForDPNSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
+ PhysAddress physAddress, ElanInstance elanInstance,
+ InterfaceInfo interfaceInfo, MacEntry oldMacEntry,
+ MacEntry newMacEntry, boolean isVlanOrFlatProviderIface,
+ final DataStoreJobCoordinator portDataStoreCoordinator) {
+ portDataStoreCoordinator
+ .enqueueJob(ElanUtils.getElanMacDPNKey(elanTag, macAddress, interfaceInfo.getDpId()), () -> {
+ macMigrationFlowsCleanup(interfaceName, elanInstance, oldMacEntry, isVlanOrFlatProviderIface);
+ BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
+ elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
+ Collections.singletonList(physAddress));
+ ElanManagerCounters.unknown_smac_pktin_learned.inc();
+ WriteTransaction flowWritetx = broker.newWriteOnlyTransaction();
+ elanUtils.setupMacFlows(elanInstance, interfaceInfo, elanInstance.getMacTimeout(),
+ macAddress, !isVlanOrFlatProviderIface, flowWritetx);
+ InstanceIdentifier<MacEntry> macEntryId =
+ ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
+ flowWritetx.put(LogicalDatastoreType.OPERATIONAL, macEntryId, newMacEntry,
+ WriteTransaction.CREATE_MISSING_PARENTS);
+ return Collections.singletonList(flowWritetx.submit());
+ });
+ }
+
+ private void macMigrationFlowsCleanup(String interfaceName, ElanInstance elanInstance, MacEntry macEntry,
+ boolean isVlanOrFlatProviderIface) {
+ if (macEntry != null && !macEntry.getInterface().equals(interfaceName)
+ && !isVlanOrFlatProviderIface) {
+ tryAndRemoveInvalidMacEntry(elanInstance.getElanInstanceName(), macEntry);
+ ElanManagerCounters.unknown_smac_pktin_flows_removed_for_relearned.inc();
+ }
}
/*
* Static MAC having been added on a wrong ELAN.
*/
private void tryAndRemoveInvalidMacEntry(String elanName, MacEntry macEntry) {
- ElanInstance elanInfo = elanUtils.getElanInstanceByName(elanName);
+ ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, elanName);
if (elanInfo == null) {
- logger.warn(String.format("MAC %s is been added (either statically or dynamically) for an invalid Elan %s. "
- + "Manual cleanup may be necessary", macEntry.getMacAddress(), elanName));
+ LOG.warn("MAC {} is been added (either statically or dynamically) for an invalid Elan {}. "
+ + "Manual cleanup may be necessary", macEntry.getMacAddress(), elanName);
return;
}
InterfaceInfo oldInterfaceLport = interfaceManager.getInterfaceInfo(macEntry.getInterface());
if (oldInterfaceLport == null) {
- logger.warn(
- String.format(
- "MAC %s is been added (either statically or dynamically) on an invalid Logical Port %s. "
- + "Manual cleanup may be necessary",
- macEntry.getMacAddress(), macEntry.getInterface()));
+ LOG.warn("MAC {} is been added (either statically or dynamically) on an invalid Logical Port {}. "
+ + "Manual cleanup may be necessary",
+ macEntry.getMacAddress(), macEntry.getInterface());
return;
}
WriteTransaction flowDeletetx = broker.newWriteOnlyTransaction();
elanUtils.deleteMacFlows(elanInfo, oldInterfaceLport, macEntry, flowDeletetx);
flowDeletetx.submit();
- elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, Arrays.asList(macEntry.getMacAddress()));
+ elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo,
+ Collections.singletonList(macEntry.getMacAddress()));
}
}