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.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.MatchInfo;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
25 import org.opendaylight.netvirt.elan.ElanException;
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
79 * @throws ElanException in case of issues creating the flow objects
81 @SuppressWarnings("checkstyle:IllegalCatch")
82 public Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
83 Long vni, String dstMacAddress, String displayName) throws ElanException {
84 List<MatchInfo> mkMatches =
85 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, /* shFlag */ false, dstMacAddress);
86 List<Instruction> mkInstructions = new ArrayList<>();
88 List<Action> actions =
89 elanItmUtils.getExternalTunnelItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
90 mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
91 } catch (Exception e) {
92 LOG.error("Could not get Egress Actions for DpId=" + dpId + ", externalNode=" + extDeviceNodeId, e);
95 return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE,
96 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress,
99 displayName, 0, /* idleTimeout */
101 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
105 * Installs a Flow in the specified DPN's DMAC table. The flow is for a MAC
106 * that is connected remotely in an External Device (TOR) and that is
107 * accessible through an external tunnel. It also installs the flow for
108 * dropping the packet if it came over an ITM tunnel (that is, if the
109 * Split-Horizon flag is set)
112 * Id of the DPN where the flow must be installed
113 * @param extDeviceNodeId
114 * the ext device node id
123 * @param interfaceName
126 * @return the dmac flows
127 * @throws ElanException in case of issues creating the flow objects
129 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
130 String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
131 String interfaceName) throws ElanException {
132 synchronized (ElanUtils.getElanMacDPNKey(elanTag, macAddress, dpnId)) {
133 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
135 ResourceBatchingManager.getInstance().put(
136 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
138 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
139 ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
140 ElanUtils.getFlowIid(dropFlow, dpnId), dropFlow);
141 installEtreeDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName,
144 return Collections.emptyList();
148 * Installs or removes flows in DMAC table for MACs that are/were located in
149 * an external Elan Device.
152 * Id of the DPN where the DMAC table is going to be modified
154 * Id of the External Device where the MAC is located
158 * VNI of the LogicalSwitch to which the MAC belongs to, and that
159 * is associated with the ELAN
162 * @param elanInstanceName
163 * the elan instance name
165 * Indicates if flows must be installed or removed.
166 * @param interfaceName
168 * @throws ElanException in case of issues creating the flow objects
169 * @see org.opendaylight.genius.mdsalutil.MDSALUtil.MdsalOp
171 public void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
172 String macAddress, String elanInstanceName, MDSALUtil.MdsalOp addOrRemove, String interfaceName)
173 throws ElanException {
174 if (addOrRemove == MDSALUtil.MdsalOp.CREATION_OP) {
175 installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName,
177 } else if (addOrRemove == MDSALUtil.MdsalOp.REMOVAL_OP) {
178 deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress);
183 * Delete dmac flows to external mac.
189 * @param extDeviceNodeId
190 * the ext device node id
195 public List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
196 String extDeviceNodeId, String macToRemove) {
197 List<ListenableFuture<Void>> result = Lists.newArrayList(
198 removeFlowThatSendsThePacketOnAnExternalTunnel(elanTag, dpId, extDeviceNodeId, macToRemove),
199 removeTheDropFlow(elanTag, dpId, extDeviceNodeId, macToRemove));
200 result.addAll(deleteEtreeDmacFlowsToExternalMac(elanTag, dpId, extDeviceNodeId, macToRemove));
204 private List<ListenableFuture<Void>> deleteEtreeDmacFlowsToExternalMac(
205 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
206 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
207 if (etreeLeafTag != null) {
208 return Lists.newArrayList(
209 removeFlowThatSendsThePacketOnAnExternalTunnel(
210 etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove),
211 removeTheDropFlow(etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove));
213 return Collections.emptyList();
216 private ListenableFuture<Void> removeTheDropFlow(
217 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
219 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
221 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
222 return ResourceBatchingManager.getInstance().delete(
223 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
226 private ListenableFuture<Void> removeFlowThatSendsThePacketOnAnExternalTunnel(long elanTag, BigInteger dpId,
227 String extDeviceNodeId, String macToRemove) {
229 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
231 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
232 return ResourceBatchingManager.getInstance().delete(
233 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
236 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMac(
237 BigInteger dpnId, String extDeviceNodeId, Long elanTag,
238 Long vni, String macAddress, String displayName, String interfaceName) throws ElanException {
239 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
240 if (etreeLeafTag != null) {
241 return Lists.newArrayList(
242 buildEtreeDmacFlowDropIfPacketComingFromTunnel(
243 dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
244 buildEtreeDmacFlowForExternalRemoteMac(
245 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
247 return Collections.emptyList();
250 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMac(
251 BigInteger dpnId, String extDeviceNodeId, Long vni,
252 String macAddress, String displayName, String interfaceName,
253 EtreeLeafTagName etreeLeafTag) throws ElanException {
255 if (interfaceName == null) {
258 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
259 isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
260 == EtreeInterface.EtreeInterfaceType.Root : false;
263 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
264 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
265 return ResourceBatchingManager.getInstance().put(
266 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
268 return Futures.immediateFuture(null);
271 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnel(
272 BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
273 if (etreeLeafTag != null) {
274 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
275 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
276 return ResourceBatchingManager.getInstance().put(
277 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
280 return Futures.immediateFuture(null);
284 * Builds the flow that drops the packet if it came through an external
285 * tunnel, that is, if the Split-Horizon flag is set.
288 * DPN whose DMAC table is going to be modified
289 * @param extDeviceNodeId
290 * Hwvtep node where the mac is attached to
292 * ElanId to which the MAC is being added to
293 * @param dstMacAddress
294 * The mac address to be programmed
296 private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
297 Long elanTag, String dstMacAddress) {
298 List<MatchInfo> mkMatches =
299 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, SH_FLAG_SET, dstMacAddress);
300 List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
302 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
305 return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
306 "Drop", 0, /* idleTimeout */
308 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
311 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMacWithBatch(
312 BigInteger dpnId, String extDeviceNodeId, Long vni, String macAddress, String displayName,
313 String interfaceName, EtreeLeafTagName etreeLeafTag)throws ElanException {
316 if (interfaceName == null) {
319 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
320 isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
321 == EtreeInterface.EtreeInterfaceType.Root : false;
324 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
325 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
326 return ResourceBatchingManager.getInstance().put(
327 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
329 return Futures.immediateFuture(null);
332 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
333 BigInteger dpnId, String extDeviceNodeId, Long elanTag, String macAddress,EtreeLeafTagName etreeLeafTag) {
334 if (etreeLeafTag != null) {
335 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
336 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
337 return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
338 ElanUtils.getFlowIid(dropFlow, dpnId),dropFlow);
340 return Futures.immediateFuture(null);
343 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMacInBatch(
344 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
345 String interfaceName) throws ElanException {
347 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
349 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
350 List<ListenableFuture<Void>> result = Lists.newArrayList(
351 ResourceBatchingManager.getInstance().put(
352 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow),
353 ResourceBatchingManager.getInstance().put(
354 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
356 result.addAll(installEtreeDmacFlowsToExternalRemoteMacInBatch(
357 dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName, interfaceName));
361 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMacInBatch(
362 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
363 String interfaceName) throws ElanException {
365 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
366 if (etreeLeafTag != null) {
367 return Lists.newArrayList(
368 buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
369 dpnId, extDeviceNodeId, elanTag, macAddress, etreeLeafTag),
370 buildEtreeDmacFlowForExternalRemoteMacWithBatch(
371 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
373 return Collections.emptyList();