+
+ /**
+ * Installs a Flow in a DPN's DMAC table. The Flow is for a MAC that is
+ * connected remotely in another CSS and accessible through an internal
+ * tunnel. It also installs the flow for dropping the packet if it came over
+ * an ITM tunnel (that is, if the Split-Horizon flag is set)
+ *
+ * @param localDpId
+ * Id of the DPN where the MAC Addr is accessible locally
+ * @param remoteDpId
+ * Id of the DPN where the flow must be installed
+ * @param lportTag
+ * lportTag of the interface where the mac is connected to.
+ * @param elanTag
+ * Identifier of the ELAN
+ * @param macAddress
+ * MAC to be installed in remoteDpId's DMAC table
+ * @param displayName
+ * the display name
+ */
+ public static void installDmacFlowsToInternalRemoteMac(BigInteger localDpId, BigInteger remoteDpId, long lportTag,
+ long elanTag, String macAddress, String displayName) {
+ Flow flow = buildDmacFlowForInternalRemoteMac(localDpId, remoteDpId, lportTag, elanTag, macAddress, displayName);
+ mdsalMgr.installFlow(remoteDpId, flow);
+ }
+
+ /**
+ * Installs a Flow in the specified DPN's DMAC table. The flow is for a MAC
+ * that is connected remotely in an External Device (TOR) and that is
+ * accessible through an external tunnel. It also installs the flow for
+ * dropping the packet if it came over an ITM tunnel (that is, if the
+ * Split-Horizon flag is set)
+ *
+ * @param dpnId
+ * Id of the DPN where the flow must be installed
+ * @param extDeviceNodeId
+ * the ext device node id
+ * @param elanTag
+ * the elan tag
+ * @param vni
+ * the vni
+ * @param macAddress
+ * the mac address
+ * @param displayName
+ * the display name
+ */
+ public static List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
+ String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName) {
+ List<ListenableFuture<Void>> futures = new ArrayList<>();
+ synchronized (macAddress) {
+ Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName);
+ futures.add(mdsalMgr.installFlow(dpnId, flow));
+
+ Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
+ futures.add(mdsalMgr.installFlow(dpnId, dropFlow));
+ }
+ return futures;
+ }
+
+ public static List<MatchInfo> buildMatchesForElanTagShFlagAndDstMac(long elanTag, boolean shFlag, String macAddr) {
+ List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+ mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+ ElanUtils.getElanMetadataLabel(elanTag, shFlag), MetaDataUtil.METADATA_MASK_SERVICE_SH_FLAG }));
+ mkMatches.add(new MatchInfo(MatchFieldType.eth_dst, new String[] { macAddr }));
+
+ return mkMatches;
+ }
+
+ /**
+ * Builds a Flow to be programmed in a DPN's DMAC table. This method must be used when the MAC is located in an
+ * External Device (TOR).
+ * The flow matches on the specified MAC and
+ * 1) sends the packet over the CSS-TOR tunnel if SHFlag is not set, or
+ * 2) drops it if SHFlag is set (what means the packet came from an external tunnel)
+ *
+ * @param dpId DPN whose DMAC table is going to be modified
+ * @param extDeviceNodeId Hwvtep node where the mac is attached to
+ * @param elanTag ElanId to which the MAC is being added to
+ * @param vni the vni
+ * @param dstMacAddress The mac address to be programmed
+ * @param displayName the display name
+ * @return the flow
+ */
+ public static Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
+ Long vni, String dstMacAddress, String displayName ) {
+ List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, dstMacAddress);
+ List<Instruction> mkInstructions = new ArrayList<Instruction>();
+ try {
+ List<Action> actions = getExternalItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
+ mkInstructions.add( MDSALUtil.buildApplyActionsInstruction(actions) );
+ } catch (Exception e) {
+ logger.error("Could not get Egress Actions for DpId={} externalNode={}", dpId, extDeviceNodeId );
+ }
+
+ Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+ getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress, elanTag,
+ false),
+ 20, /* prio */
+ displayName, 0, /* idleTimeout */
+ 0, /* hardTimeout */
+ ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+ return flow;
+ }
+
+ /**
+ * Builds the flow that drops the packet if it came through an external tunnel, that is, if the Split-Horizon
+ * flag is set.
+ *
+ * @param dpnId DPN whose DMAC table is going to be modified
+ * @param extDeviceNodeId Hwvtep node where the mac is attached to
+ * @param elanTag ElanId to which the MAC is being added to
+ * @param dstMacAddress The mac address to be programmed
+ * @param displayName
+ * @return
+ */
+ private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
+ Long elanTag, String dstMacAddress) {
+ List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ true, dstMacAddress);
+ List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
+ String flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
+ elanTag, true);
+ Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
+ "Drop", 0, /* idleTimeout */
+ 0, /* hardTimeout */
+ ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+ return flow;
+ }
+
+ private static String getDmacDropFlowId(Long elanTag, String dstMacAddress) {
+ return new StringBuilder(ElanConstants.ELAN_DMAC_TABLE).append(elanTag).append(dstMacAddress).append("Drop")
+ .toString();
+ }
+
+ /**
+ * Builds a Flow to be programmed in a remote DPN's DMAC table. This method must be used when the MAC is located
+ * in another CSS.
+ *
+ * This flow consists in:
+ * Match:
+ * + elanTag in packet's metadata
+ * + packet going to a MAC known to be located in another DPN
+ * Actions:
+ * + set_tunnel_id(lportTag)
+ * + output on ITM internal tunnel interface with the other DPN
+ *
+ * @param localDpId the local dp id
+ * @param remoteDpId the remote dp id
+ * @param lportTag the lport tag
+ * @param elanTag the elan tag
+ * @param macAddress the mac address
+ * @param displayName the display name
+ * @return the flow
+ */
+ public static Flow buildDmacFlowForInternalRemoteMac(BigInteger localDpId, BigInteger remoteDpId, long lportTag,
+ long elanTag, String macAddress, String displayName) {
+ List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, macAddress);
+
+ List<Instruction> mkInstructions = new ArrayList<Instruction>();
+
+ try {
+ //List of Action for the provided Source and Destination DPIDs
+ List<Action> actions = getInternalItmEgressAction(localDpId, remoteDpId, lportTag);
+ mkInstructions.add( MDSALUtil.buildApplyActionsInstruction(actions) );
+ } catch (Exception e) {
+ logger.error("Could not get Egress Actions for localDpId={} remoteDpId={} lportTag={}",
+ localDpId, remoteDpId, lportTag);
+ }
+
+ Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+ getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, localDpId, remoteDpId, macAddress, elanTag),
+ 20, /* prio */
+ displayName, 0, /* idleTimeout */
+ 0, /* hardTimeout */
+ ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+ return flow;
+
+ }
+
+ /**
+ * Installs or removes flows in DMAC table for MACs that are/were located in
+ * an external Elan Device.
+ *
+ * @param dpId
+ * Id of the DPN where the DMAC table is going to be modified
+ * @param extNodeId
+ * Id of the External Device where the MAC is located
+ * @param elanTag
+ * Id of the ELAN
+ * @param vni
+ * VNI of the LogicalSwitch to which the MAC belongs to, and that
+ * is associated with the ELAN
+ * @param macAddress
+ * the mac address
+ * @param elanInstanceName
+ * the elan instance name
+ * @param addOrRemove
+ * Indicates if flows must be installed or removed.
+ * @see org.opendaylight.vpnservice.mdsalutil.MDSALUtil.MdsalOp
+ */
+ public static void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
+ String macAddress, String elanInstanceName, MdsalOp addOrRemove) {
+ if ( addOrRemove == MdsalOp.CREATION_OP ) {
+ ElanUtils.installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName);
+ } else if ( addOrRemove == MdsalOp.REMOVAL_OP ) {
+ ElanUtils.deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress );
+ }
+ }
+
+ /**
+ * Delete dmac flows to external mac.
+ *
+ * @param elanTag
+ * the elan tag
+ * @param dpId
+ * the dp id
+ * @param extDeviceNodeId
+ * the ext device node id
+ * @param macToRemove
+ * the mac to remove
+ */
+ public static List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
+ String extDeviceNodeId, String macToRemove ) {
+ List<ListenableFuture<Void>> futures = new ArrayList<>();
+ synchronized (macToRemove) {
+ // Removing the flows that sends the packet on an external tunnel
+ String flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId,
+ macToRemove, elanTag, false);
+ Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(ElanConstants.ELAN_DMAC_TABLE)
+ .build();
+ futures.add(mdsalMgr.removeFlow(dpId, flowToRemove));
+
+ // And now removing the drop flow
+ flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
+ elanTag, true);
+ flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(ElanConstants.ELAN_DMAC_TABLE)
+ .build();
+ futures.add(mdsalMgr.removeFlow(dpId, flowToRemove));
+ }
+ return futures;
+ }
+
+ /**
+ * Gets the dpid from interface.
+ *
+ * @param interfaceName
+ * the interface name
+ * @return the dpid from interface
+ */
+ public static BigInteger getDpidFromInterface(String interfaceName) {
+ BigInteger dpId = null;
+ Future<RpcResult<GetDpidFromInterfaceOutput>> output = interfaceMgrRpcService
+ .getDpidFromInterface(new GetDpidFromInterfaceInputBuilder().setIntfName(interfaceName).build());
+ try {
+ RpcResult<GetDpidFromInterfaceOutput> rpcResult = output.get();
+ if (rpcResult.isSuccessful()) {
+ dpId = rpcResult.getResult().getDpid();
+ }
+ } catch (NullPointerException | InterruptedException | ExecutionException e) {
+ logger.error("Failed to get the DPN ID: {} for interface {}: {} ", dpId, interfaceName, e);
+ }
+ return dpId;
+ }
+