Multiple VxLAN tunnels - egress service
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / servicebindings / flowbased / utilities / FlowBasedServicesUtils.java
1 /*
2  * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
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.genius.interfacemanager.servicebindings.flowbased.utilities;
9
10 import com.google.common.collect.ImmutableBiMap;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import org.apache.commons.lang3.StringUtils;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.genius.interfacemanager.IfmConstants;
21 import org.opendaylight.genius.interfacemanager.IfmUtil;
22 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
23 import org.opendaylight.genius.mdsalutil.ActionInfo;
24 import org.opendaylight.genius.mdsalutil.InstructionInfo;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
28 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
29 import org.opendaylight.genius.mdsalutil.NwConstants;
30 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
31 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
32 import org.opendaylight.genius.mdsalutil.matches.MatchInPort;
33 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
34 import org.opendaylight.genius.mdsalutil.matches.MatchVlanVid;
35 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
36 import org.opendaylight.genius.utils.ServiceIndex;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteMetadataCase;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75 public class FlowBasedServicesUtils {
76     private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesUtils.class);
77     private static final int DEFAULT_DISPATCHER_PRIORITY = 10;
78
79     public enum ServiceMode {
80         INGRESS, EGRESS
81     }
82
83     public static final ImmutableBiMap<ServiceMode, Class<? extends ServiceModeBase>>
84         SERVICE_MODE_MAP = new ImmutableBiMap.Builder<ServiceMode, Class<? extends ServiceModeBase>>()
85             .put(ServiceMode.EGRESS, ServiceModeEgress.class).put(ServiceMode.INGRESS, ServiceModeIngress.class)
86             .build();
87
88     public static ServicesInfo getServicesInfoForInterface(String interfaceName,
89             Class<? extends ServiceModeBase> serviceMode, DataBroker dataBroker) {
90         ServicesInfoKey servicesInfoKey = new ServicesInfoKey(interfaceName, serviceMode);
91         InstanceIdentifier.InstanceIdentifierBuilder<ServicesInfo> servicesInfoIdentifierBuilder = InstanceIdentifier
92                 .builder(ServiceBindings.class).child(ServicesInfo.class, servicesInfoKey);
93         return IfmUtil.read(LogicalDatastoreType.CONFIGURATION, servicesInfoIdentifierBuilder.build(), dataBroker)
94                 .orNull();
95     }
96
97     public static NodeConnectorId getNodeConnectorIdFromInterface(String interfaceName, DataBroker dataBroker) {
98         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
99             .ietf.interfaces.rev140508.interfaces.state.Interface ifState = InterfaceManagerCommonUtils
100                 .getInterfaceState(interfaceName, dataBroker);
101         if (ifState != null) {
102             List<String> ofportIds = ifState.getLowerLayerIf();
103             return new NodeConnectorId(ofportIds.get(0));
104         }
105         return null;
106     }
107
108     public static NodeConnectorId getNodeConnectorIdFromInterface(
109             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
110                 .ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
111         if (ifState != null) {
112             List<String> ofportIds = ifState.getLowerLayerIf();
113             return new NodeConnectorId(ofportIds.get(0));
114         }
115         return null;
116     }
117
118     public static BigInteger getDpnIdFromInterface(
119             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
120                 .ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
121         NodeConnectorId nodeConnectorId = null;
122         if (ifState != null) {
123             List<String> ofportIds = ifState.getLowerLayerIf();
124             nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
125         }
126         return IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
127     }
128
129     public static List<MatchInfo> getMatchInfoForVlanPortAtIngressTable(BigInteger dpId, long portNo, Interface iface) {
130         List<MatchInfo> matches = new ArrayList<>();
131         matches.add(new MatchInPort(dpId, portNo));
132         int vlanId = 0;
133         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
134         if (l2vlan != null) {
135             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
136         }
137         if (vlanId >= 0  && l2vlan.getL2vlanMode() != IfL2vlan.L2vlanMode.Transparent) {
138             matches.add(new MatchVlanVid(vlanId));
139         }
140         return matches;
141     }
142
143     public static List<MatchInfo> getMatchInfoForTunnelPortAtIngressTable(BigInteger dpId, long portNo) {
144         List<MatchInfo> matches = new ArrayList<>();
145         matches.add(new MatchInPort(dpId, portNo));
146         return matches;
147     }
148
149     public static List<MatchInfo> getMatchInfoForDispatcherTable(BigInteger dpId, int interfaceTag,
150             short servicePriority) {
151         List<MatchInfo> matches = new ArrayList<>();
152         matches.add(new MatchMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, servicePriority),
153                 MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
154         return matches;
155     }
156
157     public static List<MatchInfoBase> getMatchInfoForEgressDispatcherTable(int interfaceTag, short serviceIndex) {
158         List<MatchInfoBase> matches = new ArrayList<>();
159         matches.add(new NxMatchRegister(NxmNxReg6.class,
160                 MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, serviceIndex)));
161         return matches;
162     }
163
164     public static void installInterfaceIngressFlow(BigInteger dpId, Interface iface, BoundServices boundServiceNew,
165             WriteTransaction writeTransaction, List<MatchInfo> matches, int lportTag, short tableId) {
166         List<Instruction> instructions = boundServiceNew.getAugmentation(StypeOpenflow.class).getInstruction();
167
168         int serviceInstructionsSize = instructions.size();
169         List<Instruction> instructionSet = new ArrayList<>();
170         int vlanId = 0;
171         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
172         if (l2vlan != null && l2vlan.getVlanId() != null) {
173             vlanId = l2vlan.getVlanId().getValue();
174         }
175         if (vlanId != 0) {
176             // incrementing instructionSize and using it as actionKey. Because
177             // it won't clash with any other instructions
178             int actionKey = ++serviceInstructionsSize;
179             instructionSet.add(MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey, ++serviceInstructionsSize));
180         }
181
182         if (lportTag != 0L) {
183             BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(instructions);
184             short index = boundServiceNew.getServicePriority();
185             BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, ++index, metadataValues[0],
186                     isExternal(iface));
187             BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(
188                     MetaDataUtil.METADATA_MASK_SERVICE_INDEX, MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG,
189                     metadataValues[1]);
190             instructionSet.add(
191                     MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++serviceInstructionsSize));
192         }
193
194         if (instructions != null && !instructions.isEmpty()) {
195             for (Instruction info : instructions) {
196                 // Skip meta data write as that is handled already
197                 if (info.getInstruction() instanceof WriteMetadataCase) {
198                     continue;
199                 } else if (info.getInstruction() instanceof WriteActionsCase) {
200                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
201                             ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
202                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
203                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
204                             ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
205                 }
206                 instructionSet.add(info);
207             }
208         }
209
210         String serviceRef = boundServiceNew.getServiceName();
211         String flowRef = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, iface.getName(), boundServiceNew,
212                 boundServiceNew.getServicePriority());
213         StypeOpenflow stypeOpenflow = boundServiceNew.getAugmentation(StypeOpenflow.class);
214         Flow ingressFlow = MDSALUtil.buildFlowNew(tableId, flowRef, stypeOpenflow.getFlowPriority(), serviceRef, 0, 0,
215                 stypeOpenflow.getFlowCookie(), matches, instructionSet);
216         installFlow(dpId, ingressFlow, writeTransaction);
217     }
218
219     public static void installFlow(BigInteger dpId, Flow flow, WriteTransaction writeTransaction) {
220         FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
221         Node nodeDpn = buildInventoryDpnNode(dpId);
222         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
223                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
224                 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
225
226         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, true);
227     }
228
229     private static Node buildInventoryDpnNode(BigInteger dpnId) {
230         NodeId nodeId = new NodeId("openflow:" + dpnId);
231         Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
232
233         return nodeDpn;
234     }
235
236     public static void installLPortDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
237             WriteTransaction writeTransaction, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
238         LOG.debug("Installing LPort Dispatcher Flow {}, {}", dpId, interfaceName);
239         String serviceRef = boundService.getServiceName();
240         List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(dpId, interfaceTag,
241                 currentServiceIndex);
242
243         // Get the metadata and mask from the service's write metadata
244         // instruction
245         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
246         List<Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
247         int instructionSize = serviceInstructions.size();
248         BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
249         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex,
250                 metadataValues[0]);
251         BigInteger metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
252
253         // build the final instruction for LPort Dispatcher table flow entry
254         List<Instruction> instructions = new ArrayList<>();
255         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize));
256         if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
257             for (Instruction info : serviceInstructions) {
258                 // Skip meta data write as that is handled already
259                 if (info.getInstruction() instanceof WriteMetadataCase) {
260                     continue;
261                 } else if (info.getInstruction() instanceof WriteActionsCase) {
262                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
263                             ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
264                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
265                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
266                             ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
267                 }
268                 instructions.add(info);
269             }
270         }
271
272         //////////////////////////////////////////
273         // FIXME: workaround for https://bugs.opendaylight.org/show_bug.cgi?id=7451
274         int flowPriority = DEFAULT_DISPATCHER_PRIORITY;
275         //////////////////////////////////////////
276
277         // build the flow and install it
278         String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, interfaceName, boundService,
279                 currentServiceIndex);
280         Flow ingressFlow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
281                 flowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches,
282                 instructions);
283         installFlow(dpId, ingressFlow, writeTransaction);
284     }
285
286     public static void installEgressDispatcherFlows(BigInteger dpId, BoundServices boundService, String interfaceName,
287             WriteTransaction writeTransaction, int interfaceTag, short currentServiceIndex, short nextServiceIndex,
288             Interface iface) {
289         LOG.debug("Installing Egress Dispatcher Flows on dpn : {}, for interface : {}", dpId, interfaceName);
290         installEgressDispatcherFlow(dpId, boundService, interfaceName, writeTransaction, interfaceTag,
291                 currentServiceIndex, nextServiceIndex);
292
293         // Install Split Horizon drop flow only for the default egress service -
294         // this flow drops traffic targeted to external interfaces if they
295         // arrived
296         // from an external interface (marked with the SH bit)
297         if (boundService.getServicePriority() == ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
298                 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
299             installEgressDispatcherSplitHorizonFlow(dpId, boundService, interfaceName, writeTransaction, interfaceTag,
300                     currentServiceIndex, iface);
301         }
302     }
303
304     private static void installEgressDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
305             WriteTransaction writeTransaction, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
306         String serviceRef = boundService.getServiceName();
307         List<? extends MatchInfoBase> matches = FlowBasedServicesUtils
308                 .getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
309
310         // Get the metadata and mask from the service's write metadata
311         // instruction
312         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
313         List<Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
314         int instructionSize = serviceInstructions.size();
315
316         // build the final instruction for LPort Dispatcher table flow entry
317         List<Instruction> instructions = new ArrayList<>();
318         if (boundService.getServicePriority() != ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
319                 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
320             BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
321             BigInteger metadataMask = MetaDataUtil.getWriteMetaDataMaskForEgressDispatcherTable();
322             instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadataValues[0], metadataMask,
323                 ++instructionSize));
324             instructions.add(MDSALUtil.buildAndGetSetReg6ActionInstruction(0, ++instructionSize, 0, 31,
325                     MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, nextServiceIndex)));
326         }
327         if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
328             for (Instruction info : serviceInstructions) {
329                 // Skip meta data write as that is handled already
330                 if (info.getInstruction() instanceof WriteMetadataCase) {
331                     continue;
332                 } else if (info.getInstruction() instanceof WriteActionsCase) {
333                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
334                             ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
335                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
336                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
337                             ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
338                 }
339                 instructions.add(info);
340             }
341         }
342
343         // build the flow and install it
344         String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName, boundService,
345                 currentServiceIndex);
346         Flow egressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
347                 boundService.getServicePriority(), serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches,
348                 instructions);
349         LOG.debug("Installing Egress Dispatcher Flow for interface : {}, with flow-ref : {}", interfaceName, flowRef);
350         installFlow(dpId, egressFlow, writeTransaction);
351     }
352
353     public static void installEgressDispatcherSplitHorizonFlow(BigInteger dpId, BoundServices boundService,
354             String interfaceName, WriteTransaction writeTransaction, int interfaceTag, short currentServiceIndex,
355             Interface iface) {
356         // only install split horizon drop flows for external interfaces
357         if (!isExternal(iface)) {
358             return;
359         }
360
361         if (LOG.isDebugEnabled()) {
362             LOG.debug("Installing split horizon drop flow for external interface {} on dpId {}", interfaceName, dpId);
363         }
364
365         BigInteger shFlagSet = BigInteger.ONE; // BigInteger.ONE is used for
366                                                 // checking the Split-Horizon
367                                                 // flag
368         List<MatchInfoBase> shMatches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag,
369                 currentServiceIndex);
370         shMatches.add(new MatchMetadata(shFlagSet, MetaDataUtil.METADATA_MASK_SH_FLAG));
371         List<InstructionInfo> shInstructions = new ArrayList<>();
372         List<ActionInfo> actionsInfos = new ArrayList<>();
373         actionsInfos.add(new ActionDrop());
374         shInstructions.add(new InstructionApplyActions(actionsInfos));
375
376         String flowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName,
377                 currentServiceIndex, shFlagSet);
378         String serviceRef = boundService.getServiceName();
379         // This must be higher priority than the egress flow
380         int splitHorizonFlowPriority = boundService.getServicePriority() + 1;
381         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
382         Flow egressSplitHorizonFlow = MDSALUtil.buildFlow(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
383                 splitHorizonFlowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), shMatches, shInstructions);
384
385         installFlow(dpId, egressSplitHorizonFlow, writeTransaction);
386     }
387
388     public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
389             BigInteger cookie, List<Instruction> instructions) {
390         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie).setFlowPriority(flowPriority)
391                 .setInstruction(instructions);
392         return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority)).setServiceName(serviceName)
393                 .setServicePriority(servicePriority).setServiceType(ServiceTypeFlowBased.class)
394                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
395     }
396
397     public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex) {
398         return InstanceIdentifier.builder(ServiceBindings.class)
399                 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
400                 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
401     }
402
403     public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex,
404             Class<? extends ServiceModeBase> serviceMode) {
405         return InstanceIdentifier.builder(ServiceBindings.class)
406                 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, serviceMode))
407                 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
408     }
409
410     public static void unbindDefaultEgressDispatcherService(DataBroker dataBroker, String interfaceName) {
411         IfmUtil.unbindService(dataBroker, interfaceName,
412                 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
413                         NwConstants.DEFAULT_EGRESS_SERVICE_INDEX), ServiceModeEgress.class));
414     }
415
416     public static void bindDefaultEgressDispatcherService(DataBroker dataBroker, List<ListenableFuture<Void>> futures,
417                                                           Interface interfaceInfo, String portNo,
418                                                           String interfaceName, int ifIndex) {
419         List<Instruction> instructions =
420                 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex, 0);
421         bindDefaultEgressDispatcherService(dataBroker, futures, interfaceName, instructions);
422     }
423
424     public static void bindDefaultEgressDispatcherService(DataBroker dataBroker, List<ListenableFuture<Void>> futures,
425             Interface interfaceInfo, String interfaceName, int ifIndex, long groupId) {
426         List<Instruction> instructions =
427              IfmUtil.getEgressInstructionsForInterface(interfaceInfo, StringUtils.EMPTY, null, true, ifIndex, groupId);
428         bindDefaultEgressDispatcherService(dataBroker, futures, interfaceName, instructions);
429     }
430
431     public static void bindDefaultEgressDispatcherService(DataBroker dataBroker, List<ListenableFuture<Void>> futures,
432             String interfaceName, List<Instruction> instructions) {
433         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
434         int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
435                                              NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
436         BoundServices
437                 serviceInfo =
438                 getBoundServices(String.format("%s.%s", "default", interfaceName),
439                         ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
440                                               NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
441                         priority, NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
442         IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
443         futures.add(tx.submit());
444     }
445
446     public static void removeIngressFlow(String interfaceName, BigInteger dpId, DataBroker dataBroker,
447             List<ListenableFuture<Void>> futures) {
448         if (dpId == null) {
449             return;
450         }
451         LOG.debug("Removing Ingress Flows for {}", interfaceName);
452         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
453         String flowKeyStr = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, interfaceName);
454         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
455         Node nodeDpn = buildInventoryDpnNode(dpId);
456         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
457                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
458                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey)
459                 .build();
460
461         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
462         futures.add(writeTransaction.submit());
463     }
464
465     public static void removeIngressFlow(String name, BoundServices serviceOld, BigInteger dpId,
466             WriteTransaction writeTransaction) {
467         String flowKeyStr = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, name, serviceOld,
468                 serviceOld.getServicePriority());
469         LOG.debug("Removing Ingress Flow {}", flowKeyStr);
470         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
471         Node nodeDpn = buildInventoryDpnNode(dpId);
472         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
473                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
474                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey)
475                 .build();
476
477         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
478     }
479
480     public static void removeLPortDispatcherFlow(BigInteger dpId, String iface, BoundServices boundServicesOld,
481             WriteTransaction writeTransaction, short currentServiceIndex) {
482         LOG.debug("Removing LPort Dispatcher Flows {}, {}", dpId, iface);
483
484         boundServicesOld.getAugmentation(StypeOpenflow.class);
485         // build the flow and install it
486         String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, iface, boundServicesOld,
487                 currentServiceIndex);
488         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
489         Node nodeDpn = buildInventoryDpnNode(dpId);
490         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
491                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
492                 .child(Table.class, new TableKey(NwConstants.LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
493                 .build();
494
495         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
496     }
497
498     public static void removeEgressDispatcherFlows(BigInteger dpId, String iface, BoundServices boundServicesOld,
499             WriteTransaction writeTransaction, short currentServiceIndex) {
500         LOG.debug("Removing Egress Dispatcher Flows {}, {}", dpId, iface);
501         removeEgressDispatcherFlow(dpId, iface, writeTransaction, currentServiceIndex, boundServicesOld);
502         removeEgressSplitHorizonDispatcherFlow(dpId, iface, writeTransaction, currentServiceIndex);
503     }
504
505     private static void removeEgressDispatcherFlow(BigInteger dpId, String iface, WriteTransaction writeTransaction,
506             short currentServiceIndex, BoundServices boundServicesOld) {
507         // build the flow and install it
508         String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface, boundServicesOld,
509                 currentServiceIndex);
510         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
511         Node nodeDpn = buildInventoryDpnNode(dpId);
512         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
513                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
514                 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
515                 .build();
516
517         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
518     }
519
520     public static void removeEgressSplitHorizonDispatcherFlow(BigInteger dpId, String iface,
521             WriteTransaction writeTransaction, short currentServiceIndex) {
522         // BigInteger.ONE is used for checking the Split-Horizon flag
523         BigInteger shFlagSet = BigInteger.ONE;
524         String shFlowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface,
525                 currentServiceIndex, shFlagSet);
526         FlowKey shFlowKey = new FlowKey(new FlowId(shFlowRef));
527         Node nodeDpn = buildInventoryDpnNode(dpId);
528         InstanceIdentifier<Flow> shFlowInstanceId = InstanceIdentifier.builder(Nodes.class)
529                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
530                 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE))
531                 .child(Flow.class, shFlowKey).build();
532
533         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, shFlowInstanceId);
534     }
535
536     public static String getFlowRef(short tableId, BigInteger dpnId, String infName) {
537         return String.format("%d:%s:%s", tableId, dpnId, infName);
538     }
539
540     private static String getFlowRef(BigInteger dpnId, short tableId, String iface, BoundServices service,
541             short currentServiceIndex) {
542         return String.valueOf(dpnId) + tableId + NwConstants.FLOWID_SEPARATOR + iface + NwConstants.FLOWID_SEPARATOR
543                 + currentServiceIndex;
544     }
545
546     private static String getSplitHorizonFlowRef(BigInteger dpnId, short tableId, String iface,
547             short currentServiceIndex, BigInteger shFlag) {
548         return new StringBuffer().append(dpnId).append(tableId).append(NwConstants.FLOWID_SEPARATOR).append(iface)
549                 .append(NwConstants.FLOWID_SEPARATOR).append(shFlag.toString()).toString();
550     }
551
552     /**
553      * This utility method returns an array of ServiceInfo in which index 0 will
554      * have the immediate lower priority service and index 1 will have the
555      * immediate higher priority service among the list of existing
556      * serviceInfos.
557      *
558      * @param serviceInfos
559      *            list of services bound
560      * @param currentServiceInfo
561      *            current service bound
562      * @return array bound services
563      */
564     public static BoundServices[] getHighAndLowPriorityService(List<BoundServices> serviceInfos,
565             BoundServices currentServiceInfo) {
566         // This will be used to hold the immediate higher service priority with respect to the currentServiceInfo
567         BoundServices higher = null;
568         // This will be used to hold the immediate lower service priority with respect to the currentServiceInfo
569         BoundServices lower = null;
570         if (serviceInfos == null || serviceInfos.isEmpty()) {
571             return new BoundServices[] { lower, higher };
572         }
573         List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
574         Collections.sort(availableServiceInfos, (serviceInfo1, serviceInfo2) -> serviceInfo1.getServicePriority()
575                 .compareTo(serviceInfo2.getServicePriority()));
576         for (BoundServices availableServiceInfo : availableServiceInfos) {
577             if (currentServiceInfo.getServicePriority() < availableServiceInfo.getServicePriority()) {
578                 lower = availableServiceInfo;
579                 break;
580             } else {
581                 higher = availableServiceInfo;
582             }
583         }
584         return new BoundServices[] { lower, higher };
585     }
586
587     public static BoundServices getHighestPriorityService(List<BoundServices> serviceInfos) {
588         List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
589         if (availableServiceInfos.isEmpty()) {
590             return null;
591         }
592         BoundServices highPriorityService = availableServiceInfos.get(0);
593         availableServiceInfos.remove(0);
594         for (BoundServices availableServiceInfo : availableServiceInfos) {
595             if (availableServiceInfo.getServicePriority() < highPriorityService.getServicePriority()) {
596                 highPriorityService = availableServiceInfo;
597             }
598         }
599         return highPriorityService;
600     }
601
602     public static void installLportIngressFlow(BigInteger dpId, long portNo, Interface iface,
603             List<ListenableFuture<Void>> futures, DataBroker dataBroker, int lportTag) {
604         int vlanId = 0;
605         boolean isVlanTransparent = false;
606
607         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
608         if (l2vlan != null) {
609             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
610             isVlanTransparent = l2vlan.getL2vlanMode() == IfL2vlan.L2vlanMode.Transparent;
611         }
612         int instructionKey = 0;
613
614         List<Instruction> instructions = new ArrayList<>();
615
616         final SplitHorizon splitHorizon = iface.getAugmentation(SplitHorizon.class);
617         boolean overrideSplitHorizonProtection = splitHorizon != null
618                 && splitHorizon.isOverrideSplitHorizonProtection();
619         int actionKey = 0;
620         List<Action> actions = new ArrayList<>();
621         if (vlanId != 0 && !isVlanTransparent) {
622             actions.add(MDSALUtil.createPopVlanAction(actionKey++));
623         }
624         if (overrideSplitHorizonProtection) {
625             actions.add(MDSALUtil.createNxOfInPortAction(actionKey++, 0));
626         }
627         if (!actions.isEmpty()) {
628             instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, instructionKey++));
629         }
630         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0, BigInteger.ZERO,
631                 isExternal(iface));
632         BigInteger metadataMask = MetaDataUtil
633                 .getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG);
634         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++));
635         instructions
636                 .add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE, instructionKey++));
637         int priority = isVlanTransparent ? 1
638                 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;
639         String flowRef = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, iface.getName());
640         List<MatchInfo> matches = getMatchInfoForVlanPortAtIngressTable(dpId, portNo, iface);
641         Flow ingressFlow = MDSALUtil.buildFlowNew(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, flowRef, priority, flowRef,
642                 0, 0, NwConstants.VLAN_TABLE_COOKIE, matches, instructions);
643         LOG.debug("Installing ingress flow {} for {}", flowRef, iface.getName());
644         WriteTransaction inventoryConfigShardTransaction = dataBroker.newWriteOnlyTransaction();
645         installFlow(dpId, ingressFlow, inventoryConfigShardTransaction);
646         futures.add(inventoryConfigShardTransaction.submit());
647     }
648
649     private static boolean isExternal(Interface iface) {
650         if (iface == null) {
651             return false;
652         }
653         IfExternal ifExternal = iface.getAugmentation(IfExternal.class);
654         return ifExternal != null && Boolean.TRUE.equals(ifExternal.isExternal());
655     }
656 }