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