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
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=" + dpId + ", externalNode=" + 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
126 * @throws ElanException in case of issues creating the flow objects
128 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
129 String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
130 String interfaceName) throws ElanException {
131 synchronized (ElanUtils.getElanMacDPNKey(elanTag, macAddress, dpnId)) {
132 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
134 ResourceBatchingManager.getInstance().put(
135 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
137 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
138 ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
139 ElanUtils.getFlowIid(dropFlow, dpnId), dropFlow);
140 installEtreeDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName,
143 return Collections.emptyList();
147 * Installs or removes flows in DMAC table for MACs that are/were located in
148 * an external Elan Device.
151 * Id of the DPN where the DMAC table is going to be modified
153 * Id of the External Device where the MAC is located
157 * VNI of the LogicalSwitch to which the MAC belongs to, and that
158 * is associated with the ELAN
161 * @param elanInstanceName
162 * the elan instance name
164 * Indicates if flows must be installed or removed.
165 * @param interfaceName
167 * @throws ElanException in case of issues creating the flow objects
168 * @see org.opendaylight.genius.mdsalutil.MDSALUtil.MdsalOp
170 public void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
171 String macAddress, String elanInstanceName, MDSALUtil.MdsalOp addOrRemove, String interfaceName)
172 throws ElanException {
173 if (addOrRemove == MDSALUtil.MdsalOp.CREATION_OP) {
174 installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName,
176 } else if (addOrRemove == MDSALUtil.MdsalOp.REMOVAL_OP) {
177 deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress);
182 * Delete dmac flows to external mac.
188 * @param extDeviceNodeId
189 * the ext device node id
194 public List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
195 String extDeviceNodeId, String macToRemove) {
196 List<ListenableFuture<Void>> result = Lists.newArrayList(
197 removeFlowThatSendsThePacketOnAnExternalTunnel(elanTag, dpId, extDeviceNodeId, macToRemove),
198 removeTheDropFlow(elanTag, dpId, extDeviceNodeId, macToRemove));
199 result.addAll(deleteEtreeDmacFlowsToExternalMac(elanTag, dpId, extDeviceNodeId, macToRemove));
203 private List<ListenableFuture<Void>> deleteEtreeDmacFlowsToExternalMac(
204 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
205 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
206 if (etreeLeafTag != null) {
207 return Lists.newArrayList(
208 removeFlowThatSendsThePacketOnAnExternalTunnel(
209 etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove),
210 removeTheDropFlow(etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove));
212 return Collections.emptyList();
215 private ListenableFuture<Void> removeTheDropFlow(
216 long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
218 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
220 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
221 return ResourceBatchingManager.getInstance().delete(
222 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
225 private ListenableFuture<Void> removeFlowThatSendsThePacketOnAnExternalTunnel(long elanTag, BigInteger dpId,
226 String extDeviceNodeId, String macToRemove) {
228 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
230 Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
231 return ResourceBatchingManager.getInstance().delete(
232 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
235 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMac(
236 BigInteger dpnId, String extDeviceNodeId, Long elanTag,
237 Long vni, String macAddress, String displayName, String interfaceName) throws ElanException {
238 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
239 if (etreeLeafTag != null) {
240 return Lists.newArrayList(
241 buildEtreeDmacFlowDropIfPacketComingFromTunnel(
242 dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
243 buildEtreeDmacFlowForExternalRemoteMac(
244 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
246 return Collections.emptyList();
249 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMac(
250 BigInteger dpnId, String extDeviceNodeId, Long vni,
251 String macAddress, String displayName, String interfaceName,
252 EtreeLeafTagName etreeLeafTag) {
254 if (interfaceName == null) {
257 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
258 isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
259 == EtreeInterface.EtreeInterfaceType.Root : false;
262 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
263 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
264 return ResourceBatchingManager.getInstance().put(
265 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
267 return Futures.immediateFuture(null);
270 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnel(
271 BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
272 if (etreeLeafTag != null) {
273 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
274 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
275 return ResourceBatchingManager.getInstance().put(
276 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
279 return Futures.immediateFuture(null);
283 * Builds the flow that drops the packet if it came through an external
284 * tunnel, that is, if the Split-Horizon flag is set.
287 * DPN whose DMAC table is going to be modified
288 * @param extDeviceNodeId
289 * Hwvtep node where the mac is attached to
291 * ElanId to which the MAC is being added to
292 * @param dstMacAddress
293 * The mac address to be programmed
295 private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
296 Long elanTag, String dstMacAddress) {
297 List<MatchInfo> mkMatches =
298 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, SH_FLAG_SET, dstMacAddress);
299 List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
301 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
304 return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
305 "Drop", 0, /* idleTimeout */
307 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
310 private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMacWithBatch(
311 BigInteger dpnId, String extDeviceNodeId, Long vni, String macAddress, String displayName,
312 String interfaceName, EtreeLeafTagName etreeLeafTag) {
315 if (interfaceName == null) {
318 Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
319 isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
320 == EtreeInterface.EtreeInterfaceType.Root : false;
323 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
324 etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
325 return ResourceBatchingManager.getInstance().put(
326 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
328 return Futures.immediateFuture(null);
331 private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
332 BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
333 if (etreeLeafTag != null) {
334 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
335 etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
336 return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
337 ElanUtils.getFlowIid(dropFlow, dpnId),dropFlow);
339 return Futures.immediateFuture(null);
342 public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMacInBatch(
343 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
344 String interfaceName) throws ElanException {
346 Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
348 Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
349 List<ListenableFuture<Void>> result = Lists.newArrayList(
350 ResourceBatchingManager.getInstance().put(
351 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow),
352 ResourceBatchingManager.getInstance().put(
353 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
355 result.addAll(installEtreeDmacFlowsToExternalRemoteMacInBatch(
356 dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName, interfaceName));
360 private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMacInBatch(
361 BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
362 String interfaceName) throws ElanException {
364 EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
365 if (etreeLeafTag != null) {
366 return Lists.newArrayList(
367 buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
368 dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
369 buildEtreeDmacFlowForExternalRemoteMacWithBatch(
370 dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
372 return Collections.emptyList();