60c264b4772ccd7fbc5561723bced82deb00c368
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / utils / ElanDmacUtils.java
1 /*
2  * Copyright © 2017 Red Hat, Inc. and others.
3  *
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
7  */
8 package org.opendaylight.netvirt.elan.utils;
9
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.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.cache.ElanInterfaceCache;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 @Singleton
38 public class ElanDmacUtils {
39     private static final Logger LOG = LoggerFactory.getLogger(ElanDmacUtils.class);
40
41     private static final boolean SH_FLAG_SET = true;
42     private static final boolean SH_FLAG_UNSET = false;
43
44     private final ElanItmUtils elanItmUtils;
45     private final ElanEtreeUtils elanEtreeUtils;
46     private final ElanInterfaceCache elanInterfaceCache;
47
48     @Inject
49     public ElanDmacUtils(ElanItmUtils elanItmUtils, ElanEtreeUtils elanEtreeUtils,
50             ElanInterfaceCache elanInterfaceCache) {
51         this.elanItmUtils = elanItmUtils;
52         this.elanEtreeUtils = elanEtreeUtils;
53         this.elanInterfaceCache = elanInterfaceCache;
54     }
55
56     /**
57      * Builds a Flow to be programmed in a DPN's DMAC table. This method must be
58      * used when the MAC is located in an External Device (TOR). The flow
59      * matches on the specified MAC and 1) sends the packet over the CSS-TOR
60      * tunnel if SHFlag is not set, or 2) drops it if SHFlag is set (what means
61      * the packet came from an external tunnel)
62      *
63      * @param dpId
64      *            DPN whose DMAC table is going to be modified
65      * @param extDeviceNodeId
66      *            Hwvtep node where the mac is attached to
67      * @param elanTag
68      *            ElanId to which the MAC is being added to
69      * @param vni
70      *            the vni
71      * @param dstMacAddress
72      *            The mac address to be programmed
73      * @param displayName
74      *            the display name
75      * @return the flow
76      */
77     @SuppressWarnings("checkstyle:IllegalCatch")
78     public Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
79             Long vni, String dstMacAddress, String displayName) {
80         List<MatchInfo> mkMatches =
81                 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, /* shFlag */ false, dstMacAddress);
82         List<Instruction> mkInstructions = new ArrayList<>();
83         try {
84             List<Action> actions =
85                     elanItmUtils.getExternalTunnelItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
86             mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
87         } catch (Exception e) {
88             LOG.error("Could not get Egress Actions for DpId {} externalNode {}", dpId, extDeviceNodeId, e);
89         }
90
91         return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE,
92                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress,
93                         elanTag, false),
94                 20, /* prio */
95                 displayName, 0, /* idleTimeout */
96                 0, /* hardTimeout */
97                 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
98     }
99
100     /**
101      * Delete dmac flows to external mac.
102      *
103      * @param elanTag
104      *            the elan tag
105      * @param dpId
106      *            the dp id
107      * @param extDeviceNodeId
108      *            the ext device node id
109      * @param macToRemove
110      *            the mac to remove
111      * @return dmac flow
112      */
113     public List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
114             String extDeviceNodeId, String macToRemove) {
115         List<ListenableFuture<Void>> result = Lists.newArrayList(
116             removeFlowThatSendsThePacketOnAnExternalTunnel(elanTag, dpId, extDeviceNodeId, macToRemove),
117             removeTheDropFlow(elanTag, dpId, extDeviceNodeId, macToRemove));
118         result.addAll(deleteEtreeDmacFlowsToExternalMac(elanTag, dpId, extDeviceNodeId, macToRemove));
119         return result;
120     }
121
122     private List<ListenableFuture<Void>> deleteEtreeDmacFlowsToExternalMac(
123             long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
124         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
125         if (etreeLeafTag != null) {
126             return Lists.newArrayList(
127                     removeFlowThatSendsThePacketOnAnExternalTunnel(
128                             etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove),
129                     removeTheDropFlow(etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove));
130         }
131         return Collections.emptyList();
132     }
133
134     private ListenableFuture<Void> removeTheDropFlow(
135             long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
136         String flowId =
137                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
138                         elanTag, true);
139         Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
140         return ResourceBatchingManager.getInstance().delete(
141                 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
142     }
143
144     private ListenableFuture<Void> removeFlowThatSendsThePacketOnAnExternalTunnel(long elanTag, BigInteger dpId,
145             String extDeviceNodeId, String macToRemove) {
146         String flowId =
147                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
148                         elanTag, false);
149         Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
150         return ResourceBatchingManager.getInstance().delete(
151                 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
152     }
153
154     /**
155      * Builds the flow that drops the packet if it came through an external
156      * tunnel, that is, if the Split-Horizon flag is set.
157      *
158      * @param dpnId
159      *            DPN whose DMAC table is going to be modified
160      * @param extDeviceNodeId
161      *            Hwvtep node where the mac is attached to
162      * @param elanTag
163      *            ElanId to which the MAC is being added to
164      * @param dstMacAddress
165      *            The mac address to be programmed
166      */
167     private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
168             Long elanTag, String dstMacAddress) {
169         List<MatchInfo> mkMatches =
170                 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, SH_FLAG_SET, dstMacAddress);
171         List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
172         String flowId =
173                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
174                         elanTag, true);
175
176         return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
177                 "Drop", 0, /* idleTimeout */
178                 0, /* hardTimeout */
179                 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
180     }
181
182     private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMacWithBatch(
183             BigInteger dpnId, String extDeviceNodeId, Long vni, String macAddress, String displayName,
184             @Nullable String interfaceName, EtreeLeafTagName etreeLeafTag) {
185
186         boolean isRoot;
187         if (interfaceName == null) {
188             isRoot = true;
189         } else {
190             Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
191             isRoot = etreeInterface.isPresent()
192                 && etreeInterface.get().getEtreeInterfaceType() == EtreeInterface.EtreeInterfaceType.Root;
193         }
194         if (isRoot) {
195             Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
196                     etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
197             return ResourceBatchingManager.getInstance().put(
198                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
199         }
200         return Futures.immediateFuture(null);
201     }
202
203     private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
204             BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
205         if (etreeLeafTag != null) {
206             Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
207                     etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
208             return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
209                     ElanUtils.getFlowIid(dropFlow, dpnId),dropFlow);
210         }
211         return Futures.immediateFuture(null);
212     }
213
214     public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMacInBatch(
215             BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
216             @Nullable String interfaceName) {
217
218         Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
219                 displayName);
220         Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
221         List<ListenableFuture<Void>> result = Lists.newArrayList(
222                 ResourceBatchingManager.getInstance().put(
223                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow),
224                 ResourceBatchingManager.getInstance().put(
225                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
226                         dropFlow));
227         result.addAll(installEtreeDmacFlowsToExternalRemoteMacInBatch(
228                 dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName, interfaceName));
229         return result;
230     }
231
232     private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMacInBatch(
233             BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
234             @Nullable String interfaceName) {
235
236         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
237         if (etreeLeafTag != null) {
238             return Lists.newArrayList(
239                 buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
240                         dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
241                 buildEtreeDmacFlowForExternalRemoteMacWithBatch(
242                         dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
243         }
244         return Collections.emptyList();
245     }
246 }