2 * Copyright © 2017 Red Hat, Inc. and others.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.elan.utils;
10 import com.google.common.base.Optional;
11 import com.google.common.collect.Lists;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import javax.annotation.Nullable;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.genius.mdsalutil.MatchInfo;
24 import org.opendaylight.genius.mdsalutil.NwConstants;
25 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
26 import org.opendaylight.netvirt.elan.cache.ElanInterfaceCache;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 public class ElanDmacUtils {
40 private static final Logger LOG = LoggerFactory.getLogger(ElanDmacUtils.class);
42 private static final boolean SH_FLAG_SET = true;
43 private static final boolean SH_FLAG_UNSET = false;
45 private final DataBroker broker;
46 private final ElanItmUtils elanItmUtils;
47 private final ElanEtreeUtils elanEtreeUtils;
48 private final ElanInterfaceCache elanInterfaceCache;
51 public ElanDmacUtils(DataBroker broker, ElanItmUtils elanItmUtils, ElanEtreeUtils elanEtreeUtils,
52 ElanInterfaceCache elanInterfaceCache) {
54 this.elanItmUtils = elanItmUtils;
55 this.elanEtreeUtils = elanEtreeUtils;
56 this.elanInterfaceCache = elanInterfaceCache;
60 * Builds a Flow to be programmed in a DPN's DMAC table. This method must be
61 * used when the MAC is located in an External Device (TOR). The flow
62 * matches on the specified MAC and 1) sends the packet over the CSS-TOR
63 * tunnel if SHFlag is not set, or 2) drops it if SHFlag is set (what means
64 * the packet came from an external tunnel)
67 * DPN whose DMAC table is going to be modified
68 * @param extDeviceNodeId
69 * Hwvtep node where the mac is attached to
71 * ElanId to which the MAC is being added to
74 * @param dstMacAddress
75 * The mac address to be programmed
80 @SuppressWarnings("checkstyle:IllegalCatch")
81 public Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
82 Long vni, String dstMacAddress, String displayName) {
83 List<MatchInfo> mkMatches =
84 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, /* shFlag */ false, dstMacAddress);
85 List<Instruction> mkInstructions = new ArrayList<>();
87 List<Action> actions =
88 elanItmUtils.getExternalTunnelItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
89 mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
90 } catch (Exception e) {
91 LOG.error("Could not get Egress Actions for DpId {} externalNode {}", dpId, extDeviceNodeId, e);
94 return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE,
95 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress,
98 displayName, 0, /* idleTimeout */
100 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
104 * Installs a Flow in the specified DPN's DMAC table. The flow is for a MAC
105 * that is connected remotely in an External Device (TOR) and that is
106 * accessible through an external tunnel. It also installs the flow for
107 * dropping the packet if it came over an ITM tunnel (that is, if the
108 * Split-Horizon flag is set)
111 * Id of the DPN where the flow must be installed
112 * @param extDeviceNodeId
113 * the ext device node id
122 * @param interfaceName
125 * @return the dmac flows
127 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
128 String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
129 String interfaceName) {
130 synchronized (ElanUtils.getElanMacDPNKey(elanTag, macAddress, dpnId)) {
131 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
133 ResourceBatchingManager.getInstance().put(
134 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
136 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
137 ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
138 ElanUtils.getFlowIid(dropFlow, dpnId), dropFlow);
139 installEtreeDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName,
142 return Collections.emptyList();
146 * Installs or removes flows in DMAC table for MACs that are/were located in
147 * an external Elan Device.
150 * Id of the DPN where the DMAC table is going to be modified
152 * Id of the External Device where the MAC is located
156 * VNI of the LogicalSwitch to which the MAC belongs to, and that
157 * is associated with the ELAN
160 * @param elanInstanceName
161 * the elan instance name
163 * Indicates if flows must be installed or removed.
164 * @param interfaceName
166 * @see org.opendaylight.genius.mdsalutil.MDSALUtil.MdsalOp
168 public void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
169 String macAddress, String elanInstanceName, MDSALUtil.MdsalOp addOrRemove, String interfaceName) {
170 if (addOrRemove == MDSALUtil.MdsalOp.CREATION_OP) {
171 installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName,
173 } else if (addOrRemove == MDSALUtil.MdsalOp.REMOVAL_OP) {
174 deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress);
179 * Delete dmac flows to external mac.
185 * @param extDeviceNodeId
186 * the ext device node id
191 public List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
192 String extDeviceNodeId, String macToRemove) {
193 List<ListenableFuture<Void>> result = Lists.newArrayList(
194 removeFlowThatSendsThePacketOnAnExternalTunnel(elanTag, dpId, extDeviceNodeId, macToRemove),
195 removeTheDropFlow(elanTag, dpId, extDeviceNodeId, macToRemove));
196 result.addAll(deleteEtreeDmacFlowsToExternalMac(elanTag, dpId, extDeviceNodeId, macToRemove));
200 private List<ListenableFuture<Void>> deleteEtreeDmacFlowsToExternalMac(
201 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
202 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
203 if (etreeLeafTag != null) {
204 return Lists.newArrayList(
205 removeFlowThatSendsThePacketOnAnExternalTunnel(
206 etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove),
207 removeTheDropFlow(etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove));
209 return Collections.emptyList();
212 private ListenableFuture<Void> removeTheDropFlow(
213 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
215 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
217 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
218 return ResourceBatchingManager.getInstance().delete(
219 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
222 private ListenableFuture<Void> removeFlowThatSendsThePacketOnAnExternalTunnel(long elanTag, BigInteger dpId,
223 String extDeviceNodeId, String macToRemove) {
225 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
227 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
228 return ResourceBatchingManager.getInstance().delete(
229 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
232 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMac(
233 BigInteger dpnId, String extDeviceNodeId, Long elanTag,
234 Long vni, String macAddress, String displayName, String interfaceName) {
235 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
236 if (etreeLeafTag != null) {
237 return Lists.newArrayList(
238 buildEtreeDmacFlowDropIfPacketComingFromTunnel(
239 dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
240 buildEtreeDmacFlowForExternalRemoteMac(
241 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
243 return Collections.emptyList();
246 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMac(
247 BigInteger dpnId, String extDeviceNodeId, Long vni,
248 String macAddress, String displayName, String interfaceName,
249 EtreeLeafTagName etreeLeafTag) {
251 if (interfaceName == null) {
254 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
255 isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
256 == EtreeInterface.EtreeInterfaceType.Root : false;
259 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
260 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
261 return ResourceBatchingManager.getInstance().put(
262 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
264 return Futures.immediateFuture(null);
267 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnel(
268 BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
269 if (etreeLeafTag != null) {
270 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
271 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
272 return ResourceBatchingManager.getInstance().put(
273 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
276 return Futures.immediateFuture(null);
280 * Builds the flow that drops the packet if it came through an external
281 * tunnel, that is, if the Split-Horizon flag is set.
284 * DPN whose DMAC table is going to be modified
285 * @param extDeviceNodeId
286 * Hwvtep node where the mac is attached to
288 * ElanId to which the MAC is being added to
289 * @param dstMacAddress
290 * The mac address to be programmed
292 private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
293 Long elanTag, String dstMacAddress) {
294 List<MatchInfo> mkMatches =
295 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, SH_FLAG_SET, dstMacAddress);
296 List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
298 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
301 return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
302 "Drop", 0, /* idleTimeout */
304 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
307 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMacWithBatch(
308 BigInteger dpnId, String extDeviceNodeId, Long vni, String macAddress, String displayName,
309 @Nullable String interfaceName, EtreeLeafTagName etreeLeafTag) {
312 if (interfaceName == null) {
315 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
316 isRoot = etreeInterface.isPresent()
317 && etreeInterface.get().getEtreeInterfaceType() == EtreeInterface.EtreeInterfaceType.Root;
320 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
321 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
322 return ResourceBatchingManager.getInstance().put(
323 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
325 return Futures.immediateFuture(null);
328 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
329 BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
330 if (etreeLeafTag != null) {
331 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
332 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
333 return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
334 ElanUtils.getFlowIid(dropFlow, dpnId),dropFlow);
336 return Futures.immediateFuture(null);
339 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMacInBatch(
340 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
341 @Nullable String interfaceName) {
343 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
345 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
346 List<ListenableFuture<Void>> result = Lists.newArrayList(
347 ResourceBatchingManager.getInstance().put(
348 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow),
349 ResourceBatchingManager.getInstance().put(
350 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
352 result.addAll(installEtreeDmacFlowsToExternalRemoteMacInBatch(
353 dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName, interfaceName));
357 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMacInBatch(
358 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
359 @Nullable String interfaceName) {
361 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
362 if (etreeLeafTag != null) {
363 return Lists.newArrayList(
364 buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
365 dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
366 buildEtreeDmacFlowForExternalRemoteMacWithBatch(
367 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
369 return Collections.emptyList();