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