/*
- * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
+ * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
*/
package org.opendaylight.genius.interfacemanager.servicebindings.flowbased.utilities;
-import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
+import com.google.common.util.concurrent.ListenableFuture;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.genius.interfacemanager.IfmConstants;
import org.opendaylight.genius.interfacemanager.IfmUtil;
import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
+import org.opendaylight.genius.mdsalutil.InstructionInfo;
+import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.genius.mdsalutil.MatchFieldType;
import org.opendaylight.genius.mdsalutil.MatchInfo;
import org.opendaylight.genius.mdsalutil.MatchInfoBase;
-import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.genius.mdsalutil.MetaDataUtil;
import org.opendaylight.genius.mdsalutil.NwConstants;
-import org.opendaylight.genius.mdsalutil.NxMatchInfo;
import org.opendaylight.genius.mdsalutil.NxMatchFieldType;
+import org.opendaylight.genius.mdsalutil.NxMatchInfo;
+import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
import org.opendaylight.genius.utils.ServiceIndex;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteMetadataCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public enum ServiceMode {
INGRESS,
- EGRESS;
+ EGRESS
}
public static final ImmutableBiMap SERVICE_MODE_MAP =
ServicesInfoKey servicesInfoKey = new ServicesInfoKey(interfaceName,serviceMode);
InstanceIdentifier.InstanceIdentifierBuilder<ServicesInfo> servicesInfoIdentifierBuilder =
InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class, servicesInfoKey);
- Optional<ServicesInfo> servicesInfoOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION,
- servicesInfoIdentifierBuilder.build(), dataBroker);
-
- if (servicesInfoOptional.isPresent()) {
- return servicesInfoOptional.get();
- }
-
- return null;
+ return IfmUtil.read(LogicalDatastoreType.CONFIGURATION, servicesInfoIdentifierBuilder.build(),
+ dataBroker).orNull();
}
public static NodeConnectorId getNodeConnectorIdFromInterface(String interfaceName, DataBroker dataBroker) {
List<String> ofportIds = ifState.getLowerLayerIf();
nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
}
- return new BigInteger(IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId));
+ return IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
}
public static List<MatchInfo> getMatchInfoForVlanPortAtIngressTable(BigInteger dpId, long portNo, Interface iface) {
return matches;
}
- public static List<NxMatchInfo> getMatchInfoForEgressDispatcherTable(int interfaceTag, short serviceIndex) {
- List<NxMatchInfo> matches = new ArrayList<>();
+ public static List<MatchInfoBase> getMatchInfoForEgressDispatcherTable(int interfaceTag, short serviceIndex) {
+ List<MatchInfoBase> matches = new ArrayList<>();
matches.add(new NxMatchInfo(NxMatchFieldType.nxm_reg_6, new long[] {
MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, serviceIndex)}));
return matches;
// Skip meta data write as that is handled already
if (info.getInstruction() instanceof WriteMetadataCase) {
continue;
+ } else if (info.getInstruction() instanceof WriteActionsCase) {
+ info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
+ } else if (info.getInstruction() instanceof ApplyActionsCase) {
+ info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
}
instructionSet.add(info);
}
WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
LOG.debug("Installing LPort Dispatcher Flow {}, {}", dpId, interfaceName);
String serviceRef = boundService.getServiceName();
- List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(dpId,
- interfaceTag, currentServiceIndex);
+ List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(dpId, interfaceTag, currentServiceIndex);
// Get the metadata and mask from the service's write metadata instruction
StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
// Skip meta data write as that is handled already
if (info.getInstruction() instanceof WriteMetadataCase) {
continue;
+ } else if (info.getInstruction() instanceof WriteActionsCase) {
+ info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
+ } else if (info.getInstruction() instanceof ApplyActionsCase) {
+ info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
}
instructions.add(info);
}
installFlow(dpId, ingressFlow, t);
}
- public static void installEgressDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
- WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
+ public static void installEgressDispatcherFlows(BigInteger dpId, BoundServices boundService, String interfaceName,
+ WriteTransaction t, int interfaceTag, short currentServiceIndex,
+ short nextServiceIndex, Interface iface) {
LOG.debug("Installing Egress Dispatcher Flows {}, {}", dpId, interfaceName);
+ installEgressDispatcherFlow(dpId, boundService, interfaceName, t, interfaceTag, currentServiceIndex, nextServiceIndex);
+
+ // Install Split Horizon drop flow only for the default egress service - this flow drops traffic targeted to external interfaces if they arrived
+ // from an external interface (marked with the SH bit)
+ if (boundService.getServicePriority() == ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
+ installEgressDispatcherSplitHorizonFlow(dpId, boundService, interfaceName, t, interfaceTag, currentServiceIndex, iface);
+ }
+ }
+
+ private static void installEgressDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
+ WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
String serviceRef = boundService.getServiceName();
- List<? extends MatchInfoBase> matches;
- matches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
+ List<? extends MatchInfoBase> matches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
// Get the metadata and mask from the service's write metadata instruction
StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
int instructionSize = serviceInstructions.size();
// build the final instruction for LPort Dispatcher table flow entry
- List<Instruction> instructions = new ArrayList<Instruction>();
+ List<Instruction> instructions = new ArrayList<>();
if(boundService.getServicePriority() != ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
// Skip meta data write as that is handled already
if (info.getInstruction() instanceof WriteMetadataCase) {
continue;
+ } else if (info.getInstruction() instanceof WriteActionsCase) {
+ info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
+ } else if (info.getInstruction() instanceof ApplyActionsCase) {
+ info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
+ ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
}
instructions.add(info);
}
// build the flow and install it
String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName, boundService, currentServiceIndex);
- Flow ingressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
+ Flow egressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
boundService.getServicePriority(), serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches, instructions);
- installFlow(dpId, ingressFlow, t);
+ installFlow(dpId, egressFlow, t);
}
+ public static void installEgressDispatcherSplitHorizonFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
+ WriteTransaction t, int interfaceTag, short currentServiceIndex, Interface iface) {
+ // only install split horizon drop flows for external interfaces
+ if (!isExternal(iface)) {
+ return;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Installing split horizon drop flow for external interface {} on dpId {}", interfaceName, dpId);
+ }
+
+ BigInteger shFlagSet = BigInteger.ONE; // BigInteger.ONE is used for checking the Split-Horizon flag
+ StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
+ List<MatchInfoBase> shMatches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
+ shMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { shFlagSet, MetaDataUtil.METADATA_MASK_SH_FLAG }));
+ List<InstructionInfo> shInstructions = new ArrayList<>();
+ List<ActionInfo> actionsInfos = new ArrayList<>();
+ actionsInfos.add(new ActionDrop());
+ shInstructions.add(new InstructionApplyActions(actionsInfos));
+
+ String flowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName, currentServiceIndex, shFlagSet);
+ String serviceRef = boundService.getServiceName();
+ int splitHorizonFlowPriority = boundService.getServicePriority() + 1; // this must be higher priority than the egress flow
+ Flow egressSplitHorizonFlow = MDSALUtil.buildFlow(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
+ splitHorizonFlowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), shMatches, shInstructions);
+
+ installFlow(dpId, egressSplitHorizonFlow, t);
+ }
public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
BigInteger cookie, List<Instruction> instructions) {
.child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
}
+ public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex, Class<? extends ServiceModeBase> serviceMode) {
+ return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
+ new ServicesInfoKey(interfaceName, serviceMode))
+ .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
+ }
+
public static void unbindDefaultEgressDispatcherService(DataBroker dataBroker, String interfaceName) {
IfmUtil.unbindService(dataBroker, interfaceName, buildServiceId(interfaceName,
- ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)),
- ServiceModeEgress.class);
+ ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
+ ServiceModeEgress.class));
}
public static void bindDefaultEgressDispatcherService(DataBroker dataBroker, List<ListenableFuture<Void>> futures,
t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
}
- public static void removeEgressDispatcherFlow(BigInteger dpId, String iface, BoundServices boundServicesOld, WriteTransaction t, short currentServiceIndex) {
+ public static void removeEgressDispatcherFlows(BigInteger dpId, String iface, BoundServices boundServicesOld, WriteTransaction t, short currentServiceIndex) {
LOG.debug("Removing Egress Dispatcher Flows {}, {}", dpId, iface);
+ removeEgressDispatcherFlow(dpId, iface, t, currentServiceIndex, boundServicesOld);
+ removeEgressSplitHorizonDispatcherFlow(dpId, iface, t, currentServiceIndex);
+ }
+
+ private static void removeEgressDispatcherFlow(BigInteger dpId, String iface, WriteTransaction t,
+ short currentServiceIndex, BoundServices boundServicesOld) {
// build the flow and install it
String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface, boundServicesOld, currentServiceIndex);
FlowKey flowKey = new FlowKey(new FlowId(flowRef));
t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
}
+ public static void removeEgressSplitHorizonDispatcherFlow(BigInteger dpId, String iface, WriteTransaction t, short currentServiceIndex) {
+ BigInteger shFlagSet = BigInteger.ONE; // BigInteger.ONE is used for checking the Split-Horizon flag
+ String shFlowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface, currentServiceIndex, shFlagSet);
+ FlowKey shFlowKey = new FlowKey(new FlowId(shFlowRef));
+ Node nodeDpn = buildInventoryDpnNode(dpId);
+ InstanceIdentifier<Flow> shFlowInstanceId = InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, shFlowKey).build();
+
+ t.delete(LogicalDatastoreType.CONFIGURATION, shFlowInstanceId);
+ }
+
private static String getFlowRef(BigInteger dpnId, short tableId, String iface, BoundServices service, short currentServiceIndex) {
- return new StringBuffer().append(dpnId).append(tableId).append(NwConstants.FLOWID_SEPARATOR)
- .append(iface).append(NwConstants.FLOWID_SEPARATOR).append(currentServiceIndex).toString();
+ return String.valueOf(dpnId) + tableId + NwConstants.FLOWID_SEPARATOR +
+ iface + NwConstants.FLOWID_SEPARATOR + currentServiceIndex;
}
+ private static String getSplitHorizonFlowRef(BigInteger dpnId, short tableId, String iface, short currentServiceIndex, BigInteger shFlag) {
+ return new StringBuffer().append(dpnId).append(tableId).append(NwConstants.FLOWID_SEPARATOR)
+ .append(iface).append(NwConstants.FLOWID_SEPARATOR).append(shFlag.toString()).toString();
+ }
/**
* This util method returns an array of ServiceInfo in which index 0 will
* have the immediate lower priority service and index 1 will have the
return new BoundServices[]{lower, higher};
}
List <BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
- Collections.sort(availableServiceInfos, new Comparator<BoundServices>() {
- @Override
- public int compare(BoundServices serviceInfo1, BoundServices serviceInfo2) {
- return serviceInfo1.getServicePriority().compareTo(serviceInfo2.getServicePriority());
- }
- });
+ Collections.sort(availableServiceInfos,
+ (serviceInfo1, serviceInfo2) -> serviceInfo1.getServicePriority().compareTo(serviceInfo2.getServicePriority()));
for (BoundServices availableServiceInfo: availableServiceInfos) {
if (currentServiceInfo.getServicePriority() < availableServiceInfo.getServicePriority()) {
lower = availableServiceInfo;
BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0, BigInteger.ZERO, isExternal(iface));
BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG);
List<Instruction> instructions = new ArrayList<>();
+
+ final SplitHorizon splitHorizon = iface.getAugmentation(SplitHorizon.class);
+ boolean overrideSplitHorizonProtection = splitHorizon != null && splitHorizon.isOverrideSplitHorizonProtection();
+ int actionKey = 0;
+ List<Action> actions = new ArrayList<>();
if (vlanId != 0 && !isVlanTransparent) {
- instructions.add(MDSALUtil.buildAndGetPopVlanActionInstruction(lportTag, instructionKey++));
+ actions.add(MDSALUtil.createPopVlanAction(actionKey++));
}
+ if (overrideSplitHorizonProtection) {
+ actions.add(MDSALUtil.createNxOfInPortAction(actionKey++,0));
+ }
+ if (actions.size() != 0) {
+ instructions.add(MDSALUtil.buildApplyActionsInstruction(actions,instructionKey++));
+ }
+
instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++));
instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE, instructionKey++));
int priority = isVlanTransparent ? 1 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;