/*
* Copyright (c) 2014, 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,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.sfc.ofrenderer.openflow;
import com.google.common.net.InetAddresses;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.opendaylight.genius.mdsalutil.NwConstants;
import org.opendaylight.sfc.genius.util.appcoexistence.SfcTableIndexMapper;
import org.opendaylight.sfc.ofrenderer.sfg.GroupBucketInfo;
import org.opendaylight.sfc.ovs.provider.SfcOvsUtil;
import org.opendaylight.sfc.util.openflow.OpenflowConstants;
import org.opendaylight.sfc.util.openflow.SfcOpenflowUtils;
import org.opendaylight.sfc.util.openflow.writer.SfcOfFlowWriterInterface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.BucketId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* This class writes Openflow Flow Entries to the SFF once an SFF has been configured.
*
*
* @author Brady Johnson (brady.allen.johnson@ericsson.com)
*
* @version 0.1
*
* @since 2014-08-07
*/
public class SfcOfFlowProgrammerImpl implements SfcOfFlowProgrammerInterface {
private static final Logger LOG = LoggerFactory.getLogger(SfcOfFlowProgrammerImpl.class);
public static final int COOKIE_BIGINT_HEX_RADIX = 16;
// A common SFC Transport Egress Cookie Base String, allowing
// all SFC flows to be matched by using cookieStr.startsWith()
public static final String TRANSPORT_EGRESS_COOKIE_STR_BASE = "BA5EBA11";
// The 000001** cookies are for NSH VXGPE Transport Egress flows
public static final String TRANSPORT_EGRESS_NSH_VXGPE_COOKIE = "00000101";
public static final String TRANSPORT_EGRESS_NSH_VXGPE_NSC_COOKIE = "00000102";
public static final String TRANSPORT_EGRESS_NSH_VXGPE_LASTHOP_COOKIE = "00000103";
public static final String TRANSPORT_EGRESS_NSH_VXGPE_APPCOEXIST_COOKIE = "00000104";
// The 000002** cookies are for NSH Eth Transport Egress flows
public static final String TRANSPORT_EGRESS_NSH_ETH_COOKIE = "00000201";
public static final String TRANSPORT_EGRESS_NSH_ETH_LOGICAL_COOKIE = "00000202";
public static final String TRANSPORT_EGRESS_NSH_ETH_LASTHOP_COOKIE = "00000203";
public static final String TRANSPORT_EGRESS_NSH_ETH_NSC_LASTHOP_COOKIE = "00000204";
// The 000003** cookies are for VXGEP NSH Transport Egress flows
public static final String TRANSPORT_EGRESS_VLAN_COOKIE = "00000301";
public static final String TRANSPORT_EGRESS_VLAN_SF_COOKIE = "00000302";
public static final String TRANSPORT_EGRESS_VLAN_LASTHOP_COOKIE = "00000303";
// The 000004** cookies are for VXGEP NSH Transport Egress flows
public static final String TRANSPORT_EGRESS_MPLS_COOKIE = "00000401";
public static final String TRANSPORT_EGRESS_MPLS_LASTHOP_COOKIE = "00000402";
public static final String TRANSPORT_EGRESS_MAX_COOKIE = "00000FFF";
private static final String INGRESS_TRANSPORT_ARP_FLOW_NAME_LITERAL = "ingress_Transport_Arp_Flow";
private static final String NEXT_HOP_FLOW_NAME_LITERAL = "nextHop";
// The 000005** cookies are for MAC Chaining Transport Egress flows
public static final String TRANSPORT_EGRESS_MAC_CHAINING_COOKIE = "00000501";
// Which bits in the metadata field to set, Assuming 4095 PathId's
public static final BigInteger METADATA_MASK_SFP_MATCH = new BigInteger("FFFFFFFFFFFFFFFF",
COOKIE_BIGINT_HEX_RADIX);
public static final short TABLE_INDEX_CLASSIFIER = 0;
public static final short TABLE_INDEX_TRANSPORT_INGRESS = 1;
public static final short TABLE_INDEX_PATH_MAPPER = 2;
public static final short TABLE_INDEX_PATH_MAPPER_ACL = 3;
public static final short TABLE_INDEX_NEXT_HOP = 4;
public static final short TABLE_INDEX_TRANSPORT_EGRESS = 10;
public static final short TABLE_INDEX_MAX_OFFSET = TABLE_INDEX_TRANSPORT_EGRESS;
public static final int FLOW_PRIORITY_TRANSPORT_INGRESS = 250;
public static final int FLOW_PRIORITY_ARP_TRANSPORT_INGRESS = 300;
public static final int FLOW_PRIORITY_PATH_MAPPER = 350;
public static final int FLOW_PRIORITY_PATH_MAPPER_ACL = 450;
public static final int FLOW_PRIORITY_NEXT_HOP = 550;
public static final int FLOW_PRIORITY_TRANSPORT_EGRESS = 650;
public static final int FLOW_PRIORITY_MATCH_ANY = 5;
public static final int FLOW_PRIORITY_CLASSIFIER = 1000;
private static final int PKTIN_IDLE_TIMEOUT = 60;
private static final String EMPTY_SWITCH_PORT = "";
public static final short APP_COEXISTENCE_NOT_SET = -1;
// Instance variables
private short tableBase;
// Used for app-coexistence
private short tableEgress;
private Long flowRspId;
private SfcOfFlowWriterInterface sfcOfFlowWriter = null;
private SfcTableIndexMapper tableIndexMapper = null;
public SfcOfFlowProgrammerImpl() {
this.tableBase = APP_COEXISTENCE_NOT_SET;
this.tableEgress = APP_COEXISTENCE_NOT_SET;
this.flowRspId = new Long(0);
}
public SfcOfFlowProgrammerImpl(SfcOfFlowWriterInterface sfcOfFlowWriter) {
this.tableBase = APP_COEXISTENCE_NOT_SET;
this.tableEgress = APP_COEXISTENCE_NOT_SET;
this.flowRspId = 0L;
this.sfcOfFlowWriter = sfcOfFlowWriter;
}
@Override
public void setFlowWriter(SfcOfFlowWriterInterface sfcOfFlowWriter) {
this.sfcOfFlowWriter = sfcOfFlowWriter;
}
// This method should only be called by SfcOfRenderer.close()
@Override
public void shutdown() throws ExecutionException, InterruptedException {
this.sfcOfFlowWriter.shutdown();
}
//
// Getters/Setters
//
@Override
public short getTableBase() {
return tableBase;
}
@Override
public void setTableBase(short tableBase) {
this.tableBase = tableBase;
}
@Override
public short getTableEgress() {
return tableEgress;
}
@Override
public void setTableEgress(short tableEgress) {
this.tableEgress = tableEgress;
}
@Override
public short getMaxTableOffset() {
return TABLE_INDEX_MAX_OFFSET;
}
@Override
public void setFlowRspId(Long rspId) {
this.flowRspId = rspId;
}
@Override
public Set<NodeId> deleteRspFlows(final long rspId) {
sfcOfFlowWriter.deleteRspFlows(rspId);
Set<NodeId> nodes = sfcOfFlowWriter.clearSffsIfNoRspExists();
sfcOfFlowWriter.deleteFlowSet();
return nodes;
}
@Override
public void flushFlows() {
this.sfcOfFlowWriter.flushFlows();
}
@Override
public void purgeFlows() {
this.sfcOfFlowWriter.purgeFlows();
}
/**
* Check if the given cookie belongs to the Classification table.
*
* @param cookie
* - the cookie to compare
* @return true if the cookie belongs to the Classification table, false
* otherwise
*/
public boolean compareClassificationTableCookie(FlowCookie cookie) {
if (cookie == null || cookie.getValue() == null) {
return false;
}
return cookie.toString().toUpperCase().startsWith(TRANSPORT_EGRESS_COOKIE_STR_BASE);
}
//
// Configure the MatchAny entry specifying if it should drop or goto the
// next table
// Classifier MatchAny will go to TransportIngress
// TransportIngress MatchAny will drop
// PathMapper MatchAny will go to PathMapperAcl
// PathMapperAcl MatchAny will go to NextHop
// NextHop MatchAny will go to TransportEgress
// TransportEgress MatchAny will drop
//
/**
* Set the match any flow in the Classifier table to go to the Transport
* Ingress table.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureClassifierTableMatchAny(final String sffNodeName) {
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// We dont need this flow with App Coexistence.
return;
}
FlowBuilder flowBuilder = configureTableMatchAnyFlow(getTableId(TABLE_INDEX_CLASSIFIER),
getTableId(TABLE_INDEX_TRANSPORT_INGRESS));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set DPDK output flow in the Classifier table for OVS DPDK.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureClassifierTableDpdkOutput(final String sffNodeName, Long outPort) {
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// We dont need this flow with App Coexistence.
return;
}
// Create the match criteria
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchInPort(match, new NodeConnectorId(OutputPortValues.LOCAL.toString()));
int order = 0;
// Action output
List<Action> actionList = new ArrayList<>();
String outPortStr = "output:" + outPort.toString();
actionList.add(SfcOpenflowUtils.createActionOutPort(outPortStr, order++));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder classifierDpdkOutputFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_CLASSIFIER),
FLOW_PRIORITY_CLASSIFIER, "classifier_dpdk_output", match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, classifierDpdkOutputFlow);
}
/**
* Set DPDK input flow in the Classifier table for OVS DPDK.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureClassifierTableDpdkInput(final String sffNodeName, Long inPort) {
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// We dont need this flow with App Coexistence.
return;
}
// Create the match criteria
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchInPort(match, new NodeId(sffNodeName), inPort);
int order = 0;
// Action NORMAL
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionNormal(order++));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder classifierDpdkInputFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_CLASSIFIER),
FLOW_PRIORITY_CLASSIFIER, "classifier_dpdk_input", match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, classifierDpdkInputFlow);
}
/**
* Set the match any flow in the Transport Ingress table to drop.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureTransportIngressTableMatchAny(final String sffNodeName) {
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// We dont need this flow with App Coexistence.
return;
}
FlowBuilder flowBuilder = configureTableMatchAnyDropFlow(getTableId(TABLE_INDEX_TRANSPORT_INGRESS));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set the match any flow in the Transport Ingress table to resubmit.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nextTableId
* - the table to resubmit
*/
@Override
public void configureTransportIngressTableMatchAnyResubmit(final String sffNodeName, short nextTableId) {
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// We dont need this flow with App Coexistence.
return;
}
FlowBuilder flowBuilder = configureTableMatchAnyFlowResubmit(getTableId(TABLE_INDEX_TRANSPORT_INGRESS),
nextTableId);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set the match any flow in the Path Mapper table to go to the Path Mapper
* ACL table.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configurePathMapperTableMatchAny(final String sffNodeName) {
FlowBuilder flowBuilder = configureTableMatchAnyFlow(getTableId(TABLE_INDEX_PATH_MAPPER),
getTableId(TABLE_INDEX_PATH_MAPPER_ACL));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set the match any flow in the Path Mapper ACL table to go to the Next Hop
* table.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configurePathMapperAclTableMatchAny(final String sffNodeName) {
FlowBuilder flowBuilder = configureTableMatchAnyFlow(getTableId(TABLE_INDEX_PATH_MAPPER_ACL),
getTableId(TABLE_INDEX_NEXT_HOP));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set the match any flow in the Next Hop table to go to the Transport
* Egress table.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureNextHopTableMatchAny(final String sffNodeName) {
FlowBuilder flowBuilder = configureTableMatchAnyFlow(getTableId(TABLE_INDEX_NEXT_HOP),
getTableId(TABLE_INDEX_TRANSPORT_EGRESS));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Set the match any flow in the Transport Egress table to drop.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureTransportEgressTableMatchAny(final String sffNodeName) {
// This is the last table, cant set next table AND doDrop should be
// false
FlowBuilder flowBuilder = configureTableMatchAnyDropFlow(getTableId(TABLE_INDEX_TRANSPORT_EGRESS));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, flowBuilder);
}
/**
* Internal util method to create the Match Any Drop flow.
*
* @param tableId
* - the table to write to
*
* @return the created flow
*/
private FlowBuilder configureTableMatchAnyDropFlow(short tableId) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTableMatchAnyDropFlow tableId [{}]", tableId);
// Add our drop action to a list
List<Action> actionList = new ArrayList<Action>() {
{
add(SfcOpenflowUtils.createActionDropPacket(0));
}
};
// Match any
MatchBuilder match = new MatchBuilder();
// Finish up the instructions
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(tableId, FLOW_PRIORITY_MATCH_ANY, "MatchAny", match, isb);
}
/**
* Internal util method to create the Match Any flow.
*
* @param tableId
* - the table to write to
* @param nextTableId
* - the next table to go to
*
* @return the created flow
*/
private FlowBuilder configureTableMatchAnyFlow(short tableId, short nextTableId) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTableMatchAnyFlow, tableId [{}] nextTableId [{}]", tableId,
nextTableId);
// Match any
MatchBuilder match = new MatchBuilder();
InstructionsBuilder isb = SfcOpenflowUtils.appendGotoTableInstruction(new InstructionsBuilder(), nextTableId);
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(tableId, FLOW_PRIORITY_MATCH_ANY, "MatchAny", match, isb);
}
/**
* Internal util method to create the Match Any flow and resubmit packets.
*
* @param tableId
* - the table to write to
* @param nextTableId
* - the next table to resubmit
*
* @return the created flow
*/
private FlowBuilder configureTableMatchAnyFlowResubmit(short tableId, short nextTableId) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTableMatchAnyFlowResubmit, tableId [{}] nextTableId [{}]", tableId,
nextTableId);
// Match any
MatchBuilder match = new MatchBuilder();
// Resubmit is an action not an instruction
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionResubmitTable(nextTableId, actionList.size()));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(tableId, FLOW_PRIORITY_MATCH_ANY, "MatchAny", match, isb);
}
//
// Configure Table 1, Transport Ingress
//
/**
* Configure IPv4 Transport Ingress flows. 2 flows will be created, one for
* TCP and another for UDP.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureIpv4TransportIngressFlow(final String sffNodeName) {
FlowBuilder transportIngressFlowTcp = configureTransportIngressFlow(SfcOpenflowUtils.ETHERTYPE_IPV4,
SfcOpenflowUtils.IP_PROTOCOL_TCP);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlowTcp);
FlowBuilder transportIngressFlowUdp = configureTransportIngressFlow(SfcOpenflowUtils.ETHERTYPE_IPV4,
SfcOpenflowUtils.IP_PROTOCOL_UDP);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlowUdp);
}
/**
* Configure a VLAN Transport Ingress flow.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureVlanTransportIngressFlow(final String sffNodeName) {
// vlan match
// For some reason it didnt match setting etherType=0x8100
VlanMatchBuilder vlanBuilder = new VlanMatchBuilder();
VlanIdBuilder vlanIdBuilder = new VlanIdBuilder();
vlanIdBuilder.setVlanIdPresent(true);
vlanBuilder.setVlanId(vlanIdBuilder.build());
MatchBuilder match = new MatchBuilder();
match.setVlanMatch(vlanBuilder.build());
FlowBuilder transportIngressFlow = configureTransportIngressFlow(match);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlow);
}
/**
* Configure a Transport Ingress flow.
*
* @param sffNodeName - the SFF to write the flow to
*/
@Override
public void configureMacChainingTransportIngressFlow(final String sffNodeName) {
MatchBuilder match = new MatchBuilder();
FlowBuilder transportIngressFlow = configureTransportIngressFlow(match);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlow);
}
/**
* Configure a NshVxgpe Transport Ingress flow, by matching on EtherType
* IPv4.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureNshVxgpeTransportIngressFlow(final String sffNodeName, final long nshNsp, final short nshNsi) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp);
FlowBuilder transportIngressFlow = configureTransportIngressFlow(match, getTableId(TABLE_INDEX_NEXT_HOP));
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlow);
}
/**
* Configure an MPLS Transport Ingress flow, by matching on EtherType MPLS
* Ucast.
*
* @param sffNodeName
* - the SFF to write the flow to
*/
@Override
public void configureMplsTransportIngressFlow(final String sffNodeName) {
FlowBuilder transportIngressFlow = configureTransportIngressFlow(SfcOpenflowUtils.ETHERTYPE_MPLS_UCAST);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportIngressFlow);
}
/**
* Simple pass through with default args for ipProtocol and nextTable.
*
* @param etherType
* - the etherType protocol to set in the match
*
* @return a FlowBuilder with the created Transport Ingress flow
*/
private FlowBuilder configureTransportIngressFlow(long etherType) {
return configureTransportIngressFlow(etherType, (short) -1, getTableId(TABLE_INDEX_PATH_MAPPER));
}
/**
* Simple pass through with default args for nextTable.
*
* @param etherType
* - the etherType protocol to set in the match
* @param ipProtocol
* - the IP protocol to set in the match
*
* @return a FlowBuilder with the created Transport Ingress flow
*/
private FlowBuilder configureTransportIngressFlow(long etherType, short ipProtocol) {
return configureTransportIngressFlow(etherType, ipProtocol, getTableId(TABLE_INDEX_PATH_MAPPER));
}
private FlowBuilder configureTransportIngressFlow(long etherType, short ipProtocol, short nextTable) {
MatchBuilder match = new MatchBuilder();
if (ipProtocol > 0) {
SfcOpenflowUtils.addMatchIpProtocol(match, ipProtocol);
}
SfcOpenflowUtils.addMatchEtherType(match, etherType);
return configureTransportIngressFlow(match, nextTable);
}
private FlowBuilder configureTransportIngressFlow(MatchBuilder match) {
return configureTransportIngressFlow(match, getTableId(TABLE_INDEX_PATH_MAPPER));
}
/**
* Internal util method used by the previously defined
* configureTransportIngressFlow() methods.
*
* @param match
* - the MatchBuilder object which will create the matches
* @param nextTable
* - the nextTable to jump to upon matching
*
* @return a FlowBuilder with the created Transport Ingress flow
*/
private FlowBuilder configureTransportIngressFlow(MatchBuilder match, short nextTable) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTransportIngressFlow");
InstructionsBuilder isb = SfcOpenflowUtils.appendGotoTableInstruction(new InstructionsBuilder(), nextTable);
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_INGRESS),
FLOW_PRIORITY_TRANSPORT_INGRESS, "ingress_Transport_Flow", match, isb);
}
/**
* Create an ARP responder flow in the Transport Ingress table. This flow is
* intended to respond to SF ARP messages, and is only created for SFs of
* type TCP-Proxy.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param mac
* - the SFF mac
*/
@Override
public void configureArpTransportIngressFlow(final String sffNodeName, final String mac) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTransportArpIngressThread, sff [{}] mac [{}]", sffNodeName, mac);
// Create the matching criteria
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchEtherType(match, SfcOpenflowUtils.ETHERTYPE_ARP);
SfcOpenflowUtils.addMatchArpRequest(match);
int order = 0;
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionNxMoveEthSrcToEthDstAction(order++));
actionList.add(SfcOpenflowUtils.createActionSetDlSrc(mac, order++));
actionList.add(SfcOpenflowUtils.createActionNxLoadArpOpAction(SfcOpenflowUtils.ARP_REPLY, order++));
actionList.add(SfcOpenflowUtils.createActionNxLoadArpShaAction(mac, order++));
actionList.add(SfcOpenflowUtils.createActionNxMoveArpShaToArpThaAction(order++));
actionList.add(SfcOpenflowUtils.createActionNxMoveArpTpaToRegAction(order++));
actionList.add(SfcOpenflowUtils.createActionNxMoveArpSpaToArpTpaAction(order++));
actionList.add(SfcOpenflowUtils.createActionNxMoveRegToArpSpaAction(order++));
actionList.add(SfcOpenflowUtils.createActionOutPort(OutputPortValues.INPORT.toString(), order++));
// Put our Instruction in a list of Instructions
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder arpTransportIngressFlow = SfcOpenflowUtils.createFlowBuilder(
getTableId(TABLE_INDEX_TRANSPORT_INGRESS), FLOW_PRIORITY_ARP_TRANSPORT_INGRESS,
INGRESS_TRANSPORT_ARP_FLOW_NAME_LITERAL, match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, arpTransportIngressFlow);
}
@Override
public void configureNshVxgpeSfLoopbackEncapsulatedEgressFlow(final String sffNodeName, final String sfIp,
final short vxlanUdpPort, final long sffPort) {
// Create the match criteria
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchEtherType(match, SfcOpenflowUtils.ETHERTYPE_IPV4);
SfcOpenflowUtils.addMatchIpProtocol(match, SfcOpenflowUtils.IP_PROTOCOL_UDP);
SfcOpenflowUtils.addMatchDstIpv4(match, sfIp, 32);
SfcOpenflowUtils.addMatchDstUdpPort(match, vxlanUdpPort);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionOutPort((int) sffPort, 0));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder sfFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_INGRESS),
FLOW_PRIORITY_ARP_TRANSPORT_INGRESS, INGRESS_TRANSPORT_ARP_FLOW_NAME_LITERAL, match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, sfFlow);
}
@Override
public void configureNshVxgpeSfReturnLoopbackIngressFlow(final String sffNodeName, final short vxlanUdpPort,
final long sffPort) {
// Create the match criteria
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchEtherType(match, SfcOpenflowUtils.ETHERTYPE_IPV4);
SfcOpenflowUtils.addMatchIpProtocol(match, SfcOpenflowUtils.IP_PROTOCOL_UDP);
SfcOpenflowUtils.addMatchDstUdpPort(match, vxlanUdpPort);
SfcOpenflowUtils.addMatchInPort(match, new NodeId(sffNodeName), sffPort);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionOutPort(OutputPortValues.LOCAL.toString(), 0));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder sfFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_INGRESS),
FLOW_PRIORITY_ARP_TRANSPORT_INGRESS, INGRESS_TRANSPORT_ARP_FLOW_NAME_LITERAL, match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, sfFlow);
}
//
// Configure Table 2, PathMapper
//
/**
* Create an MPLS Path Mapper flow. This flow will match on the MPLS label,
* and set the RSP ID in the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param mplsLabel
* - the mplsLabel to match on
* @param pathId
* - the RSP ID to write to the metadata
* @param isSf
* - if the flow is for an SF or SFF
*/
@Override
public void configureMplsPathMapperFlow(final String sffNodeName, final long mplsLabel, long pathId, boolean isSf) {
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchMplsLabel(match, mplsLabel);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionPopMpls(0));
FlowBuilder pathMapperFlow;
if (isSf) {
pathMapperFlow = configurePathMapperSfFlow(pathId, match, actionList);
} else {
pathMapperFlow = configurePathMapperFlow(pathId, match, actionList);
}
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, pathMapperFlow);
}
/**
* Create a VLAN Path Mapper flow. This flow will match on the VLAN tag, and
* set the RSP ID in the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param vlan
* - the vlan tag to match on
* @param pathId
* - the RSP ID to write to the metadata
* @param isSf
* - if the flow is for an SF or SFF
*/
@Override
public void configureVlanPathMapperFlow(final String sffNodeName, final int vlan, long pathId, boolean isSf) {
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchVlan(match, vlan);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionPopVlan(0));
FlowBuilder pathMapperFlow;
if (isSf) {
pathMapperFlow = configurePathMapperSfFlow(pathId, match, actionList);
} else {
pathMapperFlow = configurePathMapperFlow(pathId, match, actionList);
}
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, pathMapperFlow);
}
/**
* Simple pass through for SF Path Mapper flows.
*
* @param pathId
* - the RSP ID to write to the metadata
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configurePathMapperSfFlow(final long pathId, MatchBuilder match, List<Action> actionList) {
SfcOpenflowUtils.addMatchDscp(match, (short) pathId);
return configurePathMapperFlow(pathId, match, actionList, FLOW_PRIORITY_PATH_MAPPER + 10);
}
/**
* Simple pass through with default arg for flowPriority.
*
* @param pathId
* - the RSP ID to write to the metadata
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configurePathMapperFlow(final long pathId, MatchBuilder match, List<Action> actionList) {
return configurePathMapperFlow(pathId, match, actionList, FLOW_PRIORITY_PATH_MAPPER);
}
/**
* Internal util method used by the previously defined
* configurePathMapperFlow() methods.
*
* @param pathId
* - the RSP ID to write to the metadata
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configurePathMapperFlow(final long pathId, MatchBuilder match, List<Action> actionList,
int flowPriority) {
LOG.debug("SfcProviderSffFlowWriter.configurePathMapperFlow sff [{}] pathId [{}]", pathId);
// Apply actions take actions instantly - so, does not matter which
// order they take - let's assume first
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
SfcOpenflowUtils.appendMetadataInstruction(isb, getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
SfcOpenflowUtils.appendGotoTableInstruction(isb, getTableId(TABLE_INDEX_NEXT_HOP));
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_PATH_MAPPER), flowPriority,
NEXT_HOP_FLOW_NAME_LITERAL, match, isb);
}
//
// Table 3, PathMapper ACL
//
/**
* This table is populated as a result of PktIn for TCP Proxy SFs. It
* matches on Src/Dst IP and writes the path ID to the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param pktSrcIpStr
* - Src IP to match on
* @param pktDstIpStr
* - Src IP to match on
* @param pathId
* - the RSP ID to write to the metadata
*/
public void configurePathMapperAclFlow(final String sffNodeName, final String pktSrcIpStr, final String pktDstIpStr,
short pathId) {
LOG.debug("SfcProviderSffFlowWriter.configurePathMapperAclFlow sff [{}] srcIp [{}] dstIp [{}] pathId [{}]",
sffNodeName, pktSrcIpStr, pktDstIpStr, pathId);
// Match on the Src and Dst IPs
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchEtherType(match, SfcOpenflowUtils.ETHERTYPE_IPV4);
SfcOpenflowUtils.addMatchSrcIpv4(match, pktSrcIpStr, 32);
SfcOpenflowUtils.addMatchDstIpv4(match, pktDstIpStr, 32);
InstructionsBuilder isb = SfcOpenflowUtils.appendMetadataInstruction(new InstructionsBuilder(),
getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
SfcOpenflowUtils.appendGotoTableInstruction(isb, getTableId(TABLE_INDEX_NEXT_HOP));
// Create and configure the FlowBuilder
FlowBuilder ingressFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_PATH_MAPPER_ACL),
FLOW_PRIORITY_PATH_MAPPER_ACL, NEXT_HOP_FLOW_NAME_LITERAL, match, isb);
// Set an idle timeout on this flow
ingressFlow.setIdleTimeout(PKTIN_IDLE_TIMEOUT);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, ingressFlow);
}
//
// Table 4, NextHop
//
/**
* Configure the MAC/VLAN Next Hop by matching on the SrcMac and on the
* pathId stored in the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param pathId
* - the RSP ID to write to the metadata
* @param srcMac
* - Src mac to match on
* @param dstMac
* - Dst mac to set on the packet
*/
@Override
public void configureMacNextHopFlow(final String sffNodeName, final long pathId, final String srcMac,
final String dstMac) {
int flowPriority = FLOW_PRIORITY_NEXT_HOP;
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchMetada(match, getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
if (srcMac != null) {
SfcOpenflowUtils.addMatchSrcMac(match, srcMac);
} else {
// If the srcMac is null, then the packet is entering SFC and we
// dont know
// from where. Make it a lower priority, and only match on the
// pathId
flowPriority -= 10;
}
List<Action> actionList = new ArrayList<>();
if (dstMac != null) {
// Set the DL (Data Link) Dest Mac Address
actionList.add(SfcOpenflowUtils.createActionSetDlDst(dstMac, actionList.size()));
}
FlowBuilder nextHopFlow = configureNextHopFlow(match, actionList, flowPriority);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, nextHopFlow);
}
/**
* Configure the Next Hop by matching on the virtual MAC address containing path and hop ID
* for MAC chaining encapsulation.
*
* @param sffNodeName
* the SFF to write the flow to
* @param vmac
* the virtual mac for this hop
* @param dstSfMac
* destination mac of next SF
* @param nextVMac
* next hop virtual mac
* @param l2Transparent
* indicates if the SF do not swap MAC addresses (l2 transparent)
*/
@Override
public void configureMacChainingNextHopFlow(final String sffNodeName, final String vmac, final String dstSfMac,
final String nextVMac, final boolean l2Transparent) {
MatchBuilder match = new MatchBuilder();
// for L2 transparent, MAC addres are not touched so match in the src MAC
if (l2Transparent) {
SfcOpenflowUtils.addMatchSrcMac(match, vmac);
// for non L2 transparent, MAC addres are reversed so match in the dest MAC
} else {
SfcOpenflowUtils.addMatchDstMac(match, vmac);
}
List<Action> actionList = new ArrayList<>();
// If the packet is going to other SFF we do not change vMAC because the hop is the same
if (nextVMac != null) {
actionList.add(SfcOpenflowUtils.createActionSetDlSrc(nextVMac, actionList.size()));
}
if (dstSfMac != null) {
actionList.add(SfcOpenflowUtils.createActionSetDlDst(dstSfMac, actionList.size()));
}
FlowBuilder nextHopFlow = configureNextHopFlow(match, actionList, FLOW_PRIORITY_NEXT_HOP);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, nextHopFlow);
}
/**
* Configure the NshVxgpe NSH Next Hop by matching on the NSH pathId and
* index stored in the NSH header.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param dstIp
* - the VxLan GPE tunnel destination IP
* @param nshNsp
* - NSH Service Path to match on
* @param nshNsi
* - NSH Index to match on
*/
@Override
public void configureNshVxgpeNextHopFlow(final String sffNodeName, final String dstIp, final String nshProxyIp,
final long nshNsp,
final short nshNsi) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
List<Action> actionList = new ArrayList<>();
if (nshProxyIp != null) {
Action actionSetNwDst = SfcOpenflowUtils.createActionNxSetTunIpv4Dst(nshProxyIp, actionList.size());
actionList.add(actionSetNwDst);
if (dstIp != null) {
int ip = InetAddresses.coerceToInteger(InetAddresses.forString(dstIp));
long ipl = ip & 0xffffffffL;
Action actionSetNshC3 = SfcOpenflowUtils.createActionNxLoadNshc3(ipl, actionList.size());
actionList.add(actionSetNshC3);
}
} else if (dstIp != null) {
Action actionSetNwDst = SfcOpenflowUtils.createActionNxSetTunIpv4Dst(dstIp, actionList.size());
actionList.add(actionSetNwDst);
}
FlowBuilder nextHopFlow = configureNextHopFlow(match, actionList);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, nextHopFlow);
}
/**
* Configure the NshEth Next Hop by matching on the NSH pathId and index
* stored in the NSH header.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source Mac
* @param dstMac
* - the destination Mac
* @param nsp
* - NSH Service Path to match on
* @param nsi
* - NSH Index to match on
*/
@Override
public void configureNshEthNextHopFlow(String sffNodeName, String srcMac, String dstMac, long nsp, short nsi) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nsp, nsi);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionNxLoadEncapEthSrc(srcMac, actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxLoadEncapEthDst(dstMac, actionList.size()));
FlowBuilder nextHopFlow = configureNextHopFlow(match, actionList);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, nextHopFlow);
}
/**
* Simple pass through with default arg for flowPriority.
*
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configureNextHopFlow(MatchBuilder match, List<Action> actionList) {
return configureNextHopFlow(match, actionList, FLOW_PRIORITY_NEXT_HOP);
}
/**
* Internal util method used by the previously defined
* configureNextHopFlow() methods.
*
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
* @param flowPriority
* - the priority to set on the flow
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configureNextHopFlow(MatchBuilder match, List<Action> actionList, int flowPriority) {
LOG.debug("SfcProviderSffFlowWriter.configureNextHopFlow");
// this apply actions has an 'order' of 0 - first one executed
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
SfcOpenflowUtils.appendGotoTableInstruction(isb, getTableId(TABLE_INDEX_TRANSPORT_EGRESS));
// Create and configure the FlowBuilder
return SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_NEXT_HOP), flowPriority,
NEXT_HOP_FLOW_NAME_LITERAL, match, isb);
}
//
// Table 10, Transport Egress
//
@Override
public void configureMacChainingSfTransportEgressFlow(final String sffNodeName, final String dstMac,
String port, final String vmac) {
MatchBuilder match = new MatchBuilder();
int flowPriority = FLOW_PRIORITY_TRANSPORT_EGRESS;
SfcOpenflowUtils.addMatchDstMac(match, dstMac);
List<Action> actionList = new ArrayList<>();
if (vmac != null) {
// when packets are forwarded to other SFFs we write back the virtual MAC, because it was overwritten to
// keep state between tables
actionList.add(SfcOpenflowUtils.createActionSetDlDst(vmac, actionList.size()));
}
FlowBuilder transportEgressFlow = configureTransportEgressFlow(
match, actionList, port, flowPriority, TRANSPORT_EGRESS_MAC_CHAINING_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
@Override
public void configureVlanSfTransportEgressFlow(final String sffNodeName, final String srcMac, final String dstMac,
final int dstVlan, String port, final long pathId, boolean doPktin) {
// Match on the metadata pathId
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchMetada(match, getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
// In order to set the IP DSCP, we need to match IPv4
SfcOpenflowUtils.addMatchEtherType(match, SfcOpenflowUtils.ETHERTYPE_IPV4);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionWriteDscp((short) pathId, actionList.size()));
actionList.add(SfcOpenflowUtils.createActionPushVlan(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionSetVlanId(dstVlan, actionList.size()));
if (doPktin) {
// Notice TCP SYN matching is only supported in OpenFlow 1.5
SfcOpenflowUtils.addMatchTcpSyn(match);
actionList
.add(SfcOpenflowUtils.createActionPktIn(SfcOpenflowUtils.PKT_LENGTH_IP_HEADER, actionList.size()));
}
FlowBuilder transportEgressFlow = configureMacTransportEgressFlow(match, actionList, port, pathId, srcMac,
dstMac, TRANSPORT_EGRESS_VLAN_SF_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* Configure the VLAN LastHop Transport Egress flow by matching on the RSP
* path ID in the metadata. The only difference between this method and
* configureVlanTransportEgressFlow() is that this method checks for App
* Coexistence.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param dstVlan
* - the VLAN tag to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
*/
@Override
public void configureVlanLastHopTransportEgressFlow(final String sffNodeName, final String srcMac,
final String dstMac, final int dstVlan, final String port, final long pathId) {
// App coexistence
String switchPort = port;
if (getTableEgress() > APP_COEXISTENCE_NOT_SET) {
switchPort = EMPTY_SWITCH_PORT;
}
configureVlanTransportEgressFlow(sffNodeName, srcMac, dstMac, dstVlan, switchPort, pathId,
TRANSPORT_EGRESS_VLAN_LASTHOP_COOKIE);
}
/**
* Configure the VLAN Transport Egress flow by matching on the RSP path ID
* in the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param dstVlan
* - the VLAN tag to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
*/
@Override
public void configureVlanTransportEgressFlow(final String sffNodeName, final String srcMac, final String dstMac,
final int dstVlan, String port, final long pathId) {
configureVlanTransportEgressFlow(sffNodeName, srcMac, dstMac, dstVlan, port, pathId,
TRANSPORT_EGRESS_VLAN_COOKIE);
}
/**
* Internal Util method used for above VLAN Transport Egress methods.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param dstVlan
* - the VLAN tag to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
* @param cookieStr
* - The cookie to use
*/
public void configureVlanTransportEgressFlow(final String sffNodeName, final String srcMac, final String dstMac,
final int dstVlan, String port, final long pathId, final String cookieStr) {
// Match on the metadata pathId
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchMetada(match, getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionPushVlan(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionSetVlanId(dstVlan, actionList.size()));
FlowBuilder transportEgressFlow = configureMacTransportEgressFlow(match, actionList, port, pathId, srcMac,
dstMac, cookieStr);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* Configure the MPLS Last Hop Transport Egress flow by matching on the RSP
* path ID in the metadata. The only difference between this method and
* configureMplsTransportEgressFlow() is that this method checks for App
* Coexistence.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param mplsLabel
* - the MPLS label tag to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
*/
@Override
public void configureMplsLastHopTransportEgressFlow(final String sffNodeName, final String srcMac,
final String dstMac, final long mplsLabel, final String port, final long pathId) {
// App coexistence
String switchPort = port;
if (getTableEgress() > APP_COEXISTENCE_NOT_SET) {
switchPort = EMPTY_SWITCH_PORT;
}
configureMplsTransportEgressFlow(sffNodeName, srcMac, dstMac, mplsLabel, switchPort, pathId,
TRANSPORT_EGRESS_MPLS_LASTHOP_COOKIE);
}
/**
* Configure the MPLS Transport Egress flow by matching on the RSP path ID
* in the metadata.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param mplsLabel
* - the MPLS label tag to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
*/
@Override
public void configureMplsTransportEgressFlow(final String sffNodeName, final String srcMac, final String dstMac,
final long mplsLabel, String port, final long pathId) {
configureMplsTransportEgressFlow(sffNodeName, srcMac, dstMac, mplsLabel, port, pathId,
TRANSPORT_EGRESS_MPLS_COOKIE);
}
/**
* Internal Util method used for above MPLS Transport Egress methods.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the destination MAC to match on
* @param mplsLabel
* - the mpls label to write to the packet
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
* @param cookieStr
* - the cookie to set on the flow
*/
public void configureMplsTransportEgressFlow(final String sffNodeName, final String srcMac, final String dstMac,
final long mplsLabel, String port, final long pathId, final String cookieStr) {
// Match on the metadata pathId
MatchBuilder match = new MatchBuilder();
SfcOpenflowUtils.addMatchMetada(match, getMetadataSFP(pathId), METADATA_MASK_SFP_MATCH);
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionPushMpls(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionSetMplsLabel(mplsLabel, actionList.size()));
FlowBuilder transportEgressFlow = configureMacTransportEgressFlow(match, actionList, port, pathId, srcMac,
dstMac, cookieStr);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* Configure the VxLAN GPE NSH Transport Egress flow by matching on the NSP
* and NSI.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nshNsp
* - the NSH Service Path to match on
* @param nshNsi
* - the NSH Service Index to match on
* @param port
* - the switch port to send the packet out on
*/
@Override
public void configureNshVxgpeLastHopTransportEgressFlow(final String sffNodeName, final long nshNsp,
final short nshNsi, String port) {
// When outputing to an outport, if inport==outport, then according to
// the openflow spec, the packet will be dropped. To avoid this, outport
// must be set to INPORT. This method writes 2 flows to avoid this
// situation:
// flow1:
// if inport==port, actions=output:INPORT (higher priority than flow2)
// flow2:
// actions=output:port (flow2 is basically the else condition)
Long vxgpePort = SfcOvsUtil.getVxlanGpeOfPort(sffNodeName);
String inportStr = OutputPortValues.INPORT.toString();
if (vxgpePort != null) {
String vxgpePortStr = "output:" + vxgpePort.toString();
configureNshVxgpeLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, vxgpePortStr, inportStr);
configureNshVxgpeLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, inportStr, vxgpePortStr);
} else {
configureNshVxgpeLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, port, port);
}
}
/**
* Simple call through for configureNshVxgpeLastHopTransportEgressFlow().
*/
private void configureNshVxgpeLastHopTransportEgressFlowPorts(final String sffNodeName, final long nshNsp,
final short nshNsi, String inport, String outport) {
int flowPriority = FLOW_PRIORITY_TRANSPORT_EGRESS;
String theOutPortToSet = outport;
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
if (!inport.startsWith(OutputPortValues.INPORT.toString())) {
// if we output to a port that's the same as the import, the packet
// will be dropped
SfcOpenflowUtils.addMatchInPort(match, new NodeConnectorId(inport));
theOutPortToSet = OutputPortValues.INPORT.toString();
flowPriority += 5;
}
// On the last hop Copy/Move Nsi, Nsp, Nsc1=>TunIpv4Dst, and
// Nsc2=>TunId(Vnid)
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionNxMoveNshMdtype(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNshNp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsi(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc1ToTunIpv4DstRegister(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc2ToTunIdRegister(actionList.size()));
/* Need to set TUN_GPE_NP for VxLAN-gpe port */
actionList
.add(SfcOpenflowUtils.createActionNxLoadTunGpeNp(OpenflowConstants.TUN_GPE_NP_NSH, actionList.size()));
FlowBuilder transportEgressFlow = configureTransportEgressFlow(match, actionList, theOutPortToSet, flowPriority,
TRANSPORT_EGRESS_NSH_VXGPE_LASTHOP_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* Configure the last hop VxLAN GPE NSH Transport Egress flow by matching on
* the NSP and NSI and NSH C1 not set. If NSH C1 is set, then the packets will
* be handled by configureNshEthLastHopTransportEgressFlow() which will have a
* lower priority than this flow.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nshNsp
* - the NSH Service Path to match on
* @param nshNsi
* - the NSH Service Index to match on
* @param macAddress
* - mac address to set as source mac address after removing
* Eth-NSH encapsulation
*/
@Override
public void configureNshNscEthLastHopTransportEgressFlow(final String sffNodeName, final long nshNsp,
final short nshNsi, MacAddress macAddress) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
SfcOpenflowUtils.addMatchNshNsc1(match, 0L);
// On the last hop:
// 1. remove nsh header
// 2. Change src mac to the mac of the last SF
// 3. resubmit to the ingress dispatcher table
List<Action> actionList = new ArrayList<>();
// Pop NSH
actionList.add(SfcOpenflowUtils.createActionNxPopNsh(actionList.size()));
// Change source mac address
actionList.add(SfcOpenflowUtils.createActionSetDlSrc(macAddress.getValue(), actionList.size()));
// Proceed with other services
actionList.add(SfcOpenflowUtils.createActionResubmitTable(
NwConstants.LPORT_DISPATCHER_TABLE, actionList.size()));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Make the cookie
BigInteger cookie = new BigInteger(
TRANSPORT_EGRESS_COOKIE_STR_BASE + TRANSPORT_EGRESS_NSH_ETH_NSC_LASTHOP_COOKIE,
COOKIE_BIGINT_HEX_RADIX);
// Create and return the flow
FlowBuilder fb = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_EGRESS),
FLOW_PRIORITY_TRANSPORT_EGRESS + 10, cookie, "last hop egress flow", match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, fb);
}
/**
* Configure the last hop VxLAN GPE NSH Transport Egress flow by matching on
* the NSP and NSI. This flow is used when NSH C1/C2 is set. The flow written by
* configureNshNscEthLastHopTransportEgressFlow() determines that NSH C1 is NOT
* set, so this flow is effectively the else condition to NSH C1 not being set.
* Write NSH C1 to Ipv4TunDst and NSH C2 to Vnid.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nshNsp
* - the NSH Service Path to match on
* @param nshNsi
* - the NSH Service Index to match on
*/
@Override
public void configureNshEthLastHopTransportEgressFlow(final String sffNodeName, final long nshNsp,
final short nshNsi) {
// When outputing to an outport, if inport==outport, then according to
// the openflow spec, the packet will be dropped. To avoid this, outport
// must be set to INPORT. This method writes 2 flows to avoid this
// situation:
// flow1:
// if inport==port, actions=output:INPORT (higher priority than flow2)
// flow2:
// actions=output:port (flow2 is basically the else condition)
Long vxgpePort = SfcOvsUtil.getVxlanGpeOfPort(sffNodeName);
String inportStr = OutputPortValues.INPORT.toString();
if (vxgpePort != null) {
String vxgpePortStr = "output:" + vxgpePort.toString();
configureNshEthLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, vxgpePortStr, inportStr);
configureNshEthLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, inportStr, vxgpePortStr);
} else {
configureNshEthLastHopTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, inportStr, inportStr);
}
}
/**
* Simple call through for configureNshEthLastHopTransportEgressFlow().
*/
private void configureNshEthLastHopTransportEgressFlowPorts(final String sffNodeName, final long nshNsp,
final short nshNsi, final String inport, final String outport) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
int flowPriority = FLOW_PRIORITY_TRANSPORT_EGRESS;
String theOutPortToSet = outport;
if (!inport.startsWith(OutputPortValues.INPORT.toString())) {
// if we output to a port that's the same as the import, the packet
// will be dropped
SfcOpenflowUtils.addMatchInPort(match, new NodeConnectorId(inport));
theOutPortToSet = OutputPortValues.INPORT.toString();
flowPriority += 5;
}
// On the last hop:
// 1. remove nsh header
// 3. Write C1 to Ipv4TunDst
// 4. Write C2 to Vnid
// 5. Egress to the specified port
List<Action> actionList = new ArrayList<>();
// Pop NSH
actionList.add(SfcOpenflowUtils.createActionNxPopNsh(actionList.size()));
// Write C1 to Ipv4TunDst
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc1ToTunIpv4DstRegister(actionList.size()));
// Write C2 to Vnid
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc2ToTunIdRegister(actionList.size()));
// Egress to port
actionList.add(SfcOpenflowUtils.createActionOutPort(theOutPortToSet, actionList.size()));
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Make the cookie
BigInteger cookie = new BigInteger(
TRANSPORT_EGRESS_COOKIE_STR_BASE + TRANSPORT_EGRESS_NSH_ETH_LASTHOP_COOKIE,
COOKIE_BIGINT_HEX_RADIX);
// Create and return the flow
FlowBuilder fb = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_EGRESS),
flowPriority, cookie, "last hop egress flow", match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, fb);
}
@Override
public void configureNshVxgpeTransportEgressFlow(final String sffNodeName, final long nshNsp, final short nshNsi,
String port) {
// When outputing to an outport, if inport==outport, then according to
// the openflow spec, the packet will be dropped. To avoid this, outport
// must be set to INPORT. This method writes 2 flows to avoid this
// situation:
// flow1:
// if inport==port, actions=output:INPORT (higher priority than flow2)
// flow2:
// actions=output:port (flow2 is basically the else condition)
Long vxgpePort = SfcOvsUtil.getVxlanGpeOfPort(sffNodeName);
String inportStr = OutputPortValues.INPORT.toString();
if (vxgpePort != null) {
String vxgpePortStr = "output:" + vxgpePort.toString();
configureNshVxgpeTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, vxgpePortStr, inportStr);
configureNshVxgpeTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, inportStr, vxgpePortStr);
} else {
configureNshVxgpeTransportEgressFlowPorts(sffNodeName, nshNsp, nshNsi, port, port);
}
}
/**
* Simple call through for configureNshVxgpeTransportEgressFlow().
*/
private void configureNshVxgpeTransportEgressFlowPorts(final String sffNodeName, final long nshNsp,
final short nshNsi, String inport, String outport) {
int flowPriority = FLOW_PRIORITY_TRANSPORT_EGRESS;
String theOutPortToSet = outport;
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
if (!inport.startsWith(OutputPortValues.INPORT.toString())) {
// if we output to a port that's the same as the import, the packet
// will be dropped
SfcOpenflowUtils.addMatchInPort(match, new NodeConnectorId(inport));
theOutPortToSet = OutputPortValues.INPORT.toString();
flowPriority += 5;
}
List<Action> actionList = new ArrayList<>();
// Copy/Move Nsc1/Nsc2 to the next hop
actionList.add(SfcOpenflowUtils.createActionNxMoveNshMdtype(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNshNp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc1(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc2(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc3(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc4(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveTunIdRegister(actionList.size()));
/* Need to set TUN_GPE_NP for VxLAN-gpe port */
actionList
.add(SfcOpenflowUtils.createActionNxLoadTunGpeNp(OpenflowConstants.TUN_GPE_NP_NSH, actionList.size()));
FlowBuilder transportEgressFlow = configureTransportEgressFlow(match, actionList, theOutPortToSet, flowPriority,
TRANSPORT_EGRESS_NSH_VXGPE_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* For NSH, Return the packet to INPORT if the NSH Nsc1 Register is not
* present (==0) If it is present, it will be handled by the flow created in
* ConfigureTransportEgressFlowThread() This flow will have a higher
* priority than the flow created in ConfigureTransportEgressFlowThread().
* The NSH Nsc1 Register is usually set by the ingress classifier to
* communicate how to reach the egress classifier.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nshNsp
* - the NSH Service Path to match on
* @param nshNsi
* - the NSH Service Index to match on
* @param port
* - the switch port to send the packet out on
*/
@Override
public void configureNshNscTransportEgressFlow(final String sffNodeName, final long nshNsp, final short nshNsi,
String port) {
LOG.debug("ConfigureNshNscTransportEgressFlowThread, sff [{}] nsp [{}] nsi [{}] port [{}]", sffNodeName, nshNsp,
nshNsi, port);
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
SfcOpenflowUtils.addMatchNshNsc1(match, 0L);
/* Need to set TUN_GPE_NP for VxLAN-gpe port */
List<Action> actionList = new ArrayList<>();
actionList
.add(SfcOpenflowUtils.createActionNxLoadTunGpeNp(OpenflowConstants.TUN_GPE_NP_NSH, actionList.size()));
FlowBuilder transportEgressFlow = configureTransportEgressFlow(match, actionList, port,
FLOW_PRIORITY_TRANSPORT_EGRESS + 10, TRANSPORT_EGRESS_NSH_VXGPE_NSC_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
@Override
public void configureNshVxgpeAppCoexistTransportEgressFlow(final String sffNodeName, final long nshNsp,
final short nshNsi, final String sffIp) {
// This flow only needs to be created if App Coexistence is being used
if (getTableEgress() == APP_COEXISTENCE_NOT_SET) {
LOG.debug("configureNshVxgpeAppCoexistTransportEgressFlow NO AppCoexistence configured, skipping flow");
return;
}
// Create a match checking if C1 is set to this SFF
// Assuming IPv4
int ip = InetAddresses.coerceToInteger(InetAddresses.forString(sffIp));
long ipl = ip & 0xffffffffL;
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
SfcOpenflowUtils.addMatchNshNsc1(match, ipl);
// Copy/Move Nsi, Nsp, Nsc1=>TunIpv4Dst, and Nsc2=>TunId(Vnid)
List<Action> actionList = new ArrayList<>();
actionList.add(SfcOpenflowUtils.createActionNxMoveNshMdtype(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNshNp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsi(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc1ToTunIpv4DstRegister(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc2ToTunIdRegister(actionList.size()));
/* Need to set TUN_GPE_NP for VxLAN-gpe port */
actionList
.add(SfcOpenflowUtils.createActionNxLoadTunGpeNp(OpenflowConstants.TUN_GPE_NP_NSH, actionList.size()));
FlowBuilder transportEgressFlow = configureTransportEgressFlow(match, actionList, EMPTY_SWITCH_PORT,
FLOW_PRIORITY_TRANSPORT_EGRESS + 10, TRANSPORT_EGRESS_NSH_VXGPE_APPCOEXIST_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
/**
* Configure the NSH Ethernet Transport Egress flow by matching on the NSP
* and NSI.
*
* @param sffNodeName
* - the SFF to write the flow to
* @param nshNsp
* - the NSH Service Path to match on
* @param nshNsi
* - the NSH Service Index to match on
* @param port
* - the switch port to send the packet out on
*/
@Override
public void configureNshEthTransportEgressFlow(String sffNodeName, long nshNsp, short nshNsi, String port) {
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
List<Action> actionList = new ArrayList<>();
// Copy/Move Nsc1/Nsc2/Nsi/Nsp to the next hop
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc1(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsc2(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsi(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNsp(actionList.size()));
actionList.add(SfcOpenflowUtils.createActionNxMoveNshMdtype(actionList.size()));
// Dont need to set Ethernet EtherType
// Set NSH NextProtocol to Ethernet
actionList.add(SfcOpenflowUtils.createActionNxLoadNshNp(OpenflowConstants.NSH_NP_ETH, actionList.size()));
// Ethernet encap is performed in configureNshEthNextHopFlow()
// while setting the next hop outer MAC addresses
FlowBuilder transportEgressFlow = configureTransportEgressFlow(match, actionList, port,
FLOW_PRIORITY_TRANSPORT_EGRESS, TRANSPORT_EGRESS_NSH_ETH_COOKIE);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, transportEgressFlow);
}
@Override
public void configureNshEthTransportEgressFlow(String sffOpenflowNodeName, long nsp, short nsi,
List<Action> actionList) {
configureTransportEgressFlow(sffOpenflowNodeName, nsp, nsi, actionList, FLOW_PRIORITY_TRANSPORT_EGRESS,
TRANSPORT_EGRESS_NSH_ETH_LOGICAL_COOKIE);
}
@Override
public void setTableIndexMapper(SfcTableIndexMapper tableIndexMapper) {
this.tableIndexMapper = tableIndexMapper;
}
/**
* Simple pass through with logic for src/dstMac.
*
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
* @param port
* - the switch port to send the packet out on
* @param pathId
* - the RSP path id to match on
* @param srcMac
* - the source MAC to write to the packet
* @param dstMac
* - the dest MAC to match against
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configureMacTransportEgressFlow(MatchBuilder match, List<Action> actionList, String port,
final long pathId, final String srcMac, final String dstMac, String cookieStr) {
// Optionally match on the dstMac
int flowPriority = FLOW_PRIORITY_TRANSPORT_EGRESS;
if (dstMac != null) {
SfcOpenflowUtils.addMatchDstMac(match, dstMac);
// If the dstMac is null, then the packet is leaving SFC and
// we dont know to where. Make it a lower priority, and only
// match on the pathId
flowPriority += 10;
}
// Set the macSrc
if (srcMac != null) {
actionList.add(SfcOpenflowUtils.createActionSetDlSrc(srcMac, actionList.size()));
}
return configureTransportEgressFlow(match, actionList, port, flowPriority, cookieStr);
}
/**
* Internal util method used by the previously defined
* configureTransportEgressFlow() methods.
*
* @param match
* -already created matches
* @param actionList
* - a list of actions already created
* @param port
* - the switch port to send the packet out on
* @param flowPriority
* - the priority to set on the flow
*
* @return a FlowBuilder with the created Path Mapper flow
*/
private FlowBuilder configureTransportEgressFlow(MatchBuilder match, List<Action> actionList, String port,
int flowPriority, String cookieStr) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureTransportEgressFlow");
if (port.equals(EMPTY_SWITCH_PORT) && getTableEgress() > APP_COEXISTENCE_NOT_SET) {
// Application Coexistence:
// Instead of egressing the packet out a port, send it to
// a different application pipeline on this same switch
actionList.add(SfcOpenflowUtils.createActionResubmitTable(getTableEgress(), actionList.size()));
} else {
actionList.add(SfcOpenflowUtils.createActionOutPort(port, actionList.size()));
}
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Make the cookie
BigInteger cookie = new BigInteger(TRANSPORT_EGRESS_COOKIE_STR_BASE + cookieStr, COOKIE_BIGINT_HEX_RADIX);
// Create and return the flow
return SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_TRANSPORT_EGRESS), flowPriority, cookie,
"default_egress_flow", match, isb);
}
private void configureTransportEgressFlow(String openflowName, long nshNsp, short nshNsi, List<Action> actionList,
int flowPriority, String cookieStr) {
LOG.debug("configureTransportEgressFlow:starting. ofname:{} instructions: {}", openflowName, actionList);
MatchBuilder match = SfcOpenflowUtils.getNshMatches(nshNsp, nshNsi);
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Make the cookie
BigInteger cookie = new BigInteger(TRANSPORT_EGRESS_COOKIE_STR_BASE + cookieStr, COOKIE_BIGINT_HEX_RADIX);
FlowBuilder transportEgressFlowBuilder = SfcOpenflowUtils.createFlowBuilder(
getTableId(TABLE_INDEX_TRANSPORT_EGRESS), flowPriority, cookie, "default egress flow", match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, openflowName, transportEgressFlowBuilder);
}
@Override
public void configureGroup(String sffNodeName, String openflowNodeId, String sfgName, long sfgId, int groupType,
List<GroupBucketInfo> bucketInfos, boolean isAddGroup) {
LOG.debug("configuring group: sffName {}, groupName {}, ofNodeId {}, id {}, type {}", sffNodeName, sfgName,
openflowNodeId, sfgId, groupType);
GroupBuilder gb = new GroupBuilder();
gb.setBarrier(true);
gb.setGroupType(GroupTypes.forValue(groupType));
gb.setGroupName(sfgName);
gb.setGroupId(new GroupId(sfgId));
List<Bucket> buckets = new ArrayList<>();
BucketBuilder bb = new BucketBuilder();
for (GroupBucketInfo bucketInfo : bucketInfos) {
LOG.debug("building bucket {}", bucketInfo);
buckets.add(buildBucket(bb, bucketInfo));
}
BucketsBuilder bbs = new BucketsBuilder();
bbs.setBucket(buckets);
gb.setBuckets(bbs.build());
String nodeName = openflowNodeId != null ? openflowNodeId : sffNodeName;
sfcOfFlowWriter.writeGroupToDataStore(nodeName, gb, isAddGroup);
}
private Bucket buildBucket(BucketBuilder bb, GroupBucketInfo bucketInfo) {
int order = 0;
BucketId bucketId = new BucketId((long) bucketInfo.getIndex());
bb.setBucketId(bucketId);
bb.setKey(new BucketKey(bucketId));
String sfMac = bucketInfo.getSfMac();
String sfIp = bucketInfo.getSfIp();
List<Action> actionList = new ArrayList<>();
if (sfMac != null) {
// Set the DL (Data Link) Dest Mac Address
Action actionDstMac = SfcOpenflowUtils.createActionSetDlDst(sfMac, order);
order++;
actionList.add(actionDstMac);
}
if (sfIp != null) {
Action actionSetNwDst = SfcOpenflowUtils.createActionSetNwDst(sfIp, 32, order);
order++;
actionList.add(actionSetNwDst);
}
Action actionOutPort = SfcOpenflowUtils.createActionOutPort(bucketInfo.getOutPort(), order);
actionList.add(actionOutPort);
bb.setAction(actionList);
return bb.build();
}
@Override
public void configureGroupNextHopFlow(String sffNodeName, long sfpId, String srcMac, long groupId,
String groupName) {
LOG.debug("SfcProviderSffFlowWriter.ConfigureGroupNextHopFlow sffName [{}] sfpId [{}] srcMac [{}] groupId[{}]",
sffNodeName, sfpId, srcMac, groupId);
int flowPriority = FLOW_PRIORITY_NEXT_HOP;
//
// Create the matching criteria
MatchBuilder match = new MatchBuilder();
// Match on the either the metadata sfpId or the NSH NSP and NSI
SfcOpenflowUtils.addMatchMetada(match, getMetadataSFP(sfpId), METADATA_MASK_SFP_MATCH);
// match on the src mac
if (srcMac != null) {
SfcOpenflowUtils.addMatchSrcMac(match, srcMac);
} else {
// If the srcMac is null, then the packet is entering SFC and we
// dont know
// from where. Make it a lower priority, and only match on the
// pathId
flowPriority -= 10;
}
GroupActionBuilder groupActionBuilder = new GroupActionBuilder();
groupActionBuilder.setGroupId(groupId);
groupActionBuilder.setGroup(groupName);
GroupActionCaseBuilder groupActionCaseBuilder = new GroupActionCaseBuilder();
groupActionCaseBuilder.setGroupAction(groupActionBuilder.build());
ActionBuilder actionBuilder = new ActionBuilder();
actionBuilder.setAction(groupActionCaseBuilder.build());
actionBuilder.setOrder(0);
actionBuilder.setKey(new ActionKey(0));
Action groupAction = actionBuilder.build();
// Create the Actions
List<Action> actionList = new ArrayList<>();
actionList.add(groupAction);
// Create an Apply Action
InstructionsBuilder isb = SfcOpenflowUtils.wrapActionsIntoApplyActionsInstruction(actionList);
// Create and configure the FlowBuilder
FlowBuilder nextHopFlow = SfcOpenflowUtils.createFlowBuilder(getTableId(TABLE_INDEX_NEXT_HOP), flowPriority,
NEXT_HOP_FLOW_NAME_LITERAL, match, isb);
sfcOfFlowWriter.writeFlow(flowRspId, sffNodeName, nextHopFlow);
}
private static BigInteger getMetadataSFP(long sfpId) {
return BigInteger.valueOf(sfpId).and(METADATA_MASK_SFP_MATCH);
}
/**
* getTableId Having a TableBase allows us to "offset" the SFF tables by
* this.tableBase tables. This is used for App Coexistence. When a
* {@link SfcTableIndexMapper} has been provided, it is used (this is
* another way of performing App coexistence)
*
* @param tableIndex
* - the table to offset
* @return the resulting table id
*/
private short getTableId(short tableIndex) {
// A transport processor can provide a table index mapper in order
// to retrieve table positions
if (tableIndexMapper != null && tableIndexMapper.getTableIndex(tableIndex).isPresent()) {
return tableIndexMapper.getTableIndex(tableIndex).get();
}
if (getTableBase() > APP_COEXISTENCE_NOT_SET) {
// App Coexistence
if (tableIndex == TABLE_INDEX_TRANSPORT_INGRESS) {
// With AppCoexistence the TransportIngress table is now table 0
return 0;
} else {
// Need to subtract 2 to compensate for:
// - TABLE_INDEX_CLASSIFIER=0 - which is not used for
// AppCoexistence
// - TABLE_INDEX_TRANSPORT_INGRESS=1 - which is table 0 for
// AppCoexistence
// Example: tableBase=20, TABLE_INDEX_PATH_MAPPER=2, should
// return 20
return (short) (getTableBase() + tableIndex - 2);
}
} else {
return tableIndex;
}
}
}