blob: 9272fe5d4737b7a3bf9784e5b28bbe1356d87ce0 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.corsa;
import com.google.common.collect.ImmutableSet;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.meter.Band;
import org.onosproject.net.meter.DefaultBand;
import org.onosproject.net.meter.DefaultMeterRequest;
import org.onosproject.net.meter.Meter;
import org.onosproject.net.meter.MeterId;
import org.onosproject.net.meter.MeterRequest;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.Collections;
import static org.onosproject.net.flow.FlowRule.Builder;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Implementation of the Corsa pipeline handler for pipeline version 3.
*/
public class CorsaPipelineV3 extends AbstractCorsaPipeline {
private final Logger log = getLogger(getClass());
protected static final int PORT_BASED_PROTO_TABLE = 0;
protected static final int VLAN_CHECK_TABLE = 1;
protected static final int VLAN_MAC_XLATE_TABLE = 2;
protected static final int VLAN_CIRCUIT_TABLE = 3;
protected static final int PRIORITY_MAP_TABLE = 4;
protected static final int L3_IF_MAC_DA_TABLE = 5;
protected static final int ETHER_TABLE = 6;
protected static final int FIB_TABLE = 7;
protected static final int LOCAL_TABLE = 9;
protected static final byte MAX_VLAN_PCP = 7;
protected MeterId defaultMeterId = null;
@Override
protected CorsaTrafficTreatment processNextTreatment(TrafficTreatment treatment) {
TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
treatment.immediate().stream()
.filter(i -> {
switch (i.type()) {
case L2MODIFICATION:
L2ModificationInstruction l2i = (L2ModificationInstruction) i;
if (l2i instanceof L2ModificationInstruction.ModVlanIdInstruction ||
l2i instanceof L2ModificationInstruction.ModEtherInstruction) {
return true;
}
case OUTPUT:
return true;
default:
return false;
}
}).forEach(i -> tb.add(i));
TrafficTreatment t = tb.build();
boolean isPresentModVlanId = false;
boolean isPresentModEthSrc = false;
boolean isPresentModEthDst = false;
boolean isPresentOutpuPort = false;
for (Instruction instruction : t.immediate()) {
switch (instruction.type()) {
case L2MODIFICATION:
L2ModificationInstruction l2i = (L2ModificationInstruction) instruction;
if (l2i instanceof L2ModificationInstruction.ModVlanIdInstruction) {
isPresentModVlanId = true;
}
if (l2i instanceof L2ModificationInstruction.ModEtherInstruction) {
L2ModificationInstruction.L2SubType subType = l2i.subtype();
if (subType.equals(L2ModificationInstruction.L2SubType.ETH_SRC)) {
isPresentModEthSrc = true;
} else if (subType.equals(L2ModificationInstruction.L2SubType.ETH_DST)) {
isPresentModEthDst = true;
}
}
break;
case OUTPUT:
isPresentOutpuPort = true;
default:
}
}
CorsaTrafficTreatmentType type = CorsaTrafficTreatmentType.ACTIONS;
/**
* This represents the allowed group for CorsaPipelinev3
*/
if (isPresentModVlanId &&
isPresentModEthSrc &&
isPresentModEthDst &&
isPresentOutpuPort) {
type = CorsaTrafficTreatmentType.GROUP;
}
CorsaTrafficTreatment corsaTreatment = new CorsaTrafficTreatment(type, t);
return corsaTreatment;
}
@Override
protected TrafficTreatment.Builder processSpecificRoutingTreatment() {
return DefaultTrafficTreatment.builder().deferred();
}
@Override
protected Builder processSpecificRoutingRule(Builder rb) {
return rb.forTable(FIB_TABLE);
}
@Override
protected Collection<FlowRule> processSpecificSwitch(ForwardingObjective fwd) {
TrafficSelector filteredSelector =
DefaultTrafficSelector.builder()
.matchInPort(
((PortCriterion) fwd.selector().getCriterion(Criterion.Type.IN_PORT)).port())
.matchVlanId(
((VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID)).vlanId())
.build();
Builder ruleBuilder = DefaultFlowRule.builder()
.fromApp(fwd.appId())
.withPriority(fwd.priority())
.forDevice(deviceId)
.withSelector(filteredSelector)
.forTable(VLAN_CIRCUIT_TABLE);
if (fwd.treatment() != null) {
ruleBuilder.withTreatment(fwd.treatment());
} else {
if (fwd.nextId() != null) {
NextObjective nextObjective = pendingNext.getIfPresent(fwd.nextId());
if (nextObjective != null) {
pendingNext.invalidate(fwd.nextId());
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
.setVlanPcp((byte) 0)
.setQueue(0)
.meter(defaultMeterId);
nextObjective.next().forEach(trafficTreatment -> {
trafficTreatment.allInstructions().forEach(instruction -> {
treatment.add(instruction);
});
});
ruleBuilder.withTreatment(treatment.build());
} else {
log.warn("The group left!");
fwd.context().ifPresent(c -> c.onError(fwd, ObjectiveError.GROUPMISSING));
return ImmutableSet.of();
}
} else {
log.warn("Missing NextObjective ID for ForwardingObjective {}", fwd.id());
fail(fwd, ObjectiveError.BADPARAMS);
return ImmutableSet.of();
}
}
if (fwd.permanent()) {
ruleBuilder.makePermanent();
} else {
ruleBuilder.makeTemporary(fwd.timeout());
}
return Collections.singletonList(ruleBuilder.build());
}
@Override
protected Collection<FlowRule> processArpTraffic(ForwardingObjective fwd, Builder rule) {
//TODO
return ImmutableSet.of();
}
@Override
protected Collection<FlowRule> processLinkDiscovery(ForwardingObjective fwd, Builder rule) {
//TODO
return ImmutableSet.of();
}
@Override
protected Collection<FlowRule> processIpTraffic(ForwardingObjective fwd, Builder rule) {
//TODO
return ImmutableSet.of();
}
@Override
protected Builder processEthFiler(FilteringObjective filt, EthCriterion eth, PortCriterion port) {
log.debug("adding rule for MAC: {}", eth.mac());
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchEthDst(eth.mac());
selector.matchInPort(port.port());
treatment.transition(ETHER_TABLE);
return DefaultFlowRule.builder()
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.makePermanent()
.forTable(L3_IF_MAC_DA_TABLE);
}
@Override
protected Builder processVlanFiler(FilteringObjective filt, VlanIdCriterion vlan, PortCriterion port) {
log.debug("adding rule for VLAN: {}", vlan.vlanId());
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchVlanId(vlan.vlanId());
selector.matchInPort(port.port());
/* Static treatment for VLAN_CIRCUIT_TABLE */
treatment.setVlanPcp(MAX_VLAN_PCP);
treatment.setQueue(0);
treatment.meter(MeterId.meterId(defaultMeterId.id())); /* use default meter (Green) */
treatment.transition(L3_IF_MAC_DA_TABLE);
return DefaultFlowRule.builder()
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.makePermanent()
.forTable(VLAN_CIRCUIT_TABLE);
}
@Override
protected Builder processIpFilter(FilteringObjective filt, IPCriterion ip, PortCriterion port) {
log.debug("adding rule for IP: {}", ip.ip());
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchIPDst(ip.ip());
treatment.transition(LOCAL_TABLE);
return DefaultFlowRule.builder()
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(HIGHEST_PRIORITY)
.makePermanent()
.forTable(FIB_TABLE);
}
@Override
public void initializePipeline() {
processMeterTable(true);
processPortBasedProtoTable(true); /* Table 0 */
processVlanCheckTable(true); /* Table 1 */
processVlanMacXlateTable(true); /* Table 2 */
processVlanCircuitTable(true); /* Table 3 */
processPriorityMapTable(true); /* Table 4 */
processL3IFMacDATable(true); /* Table 5 */
processEtherTable(true); /* Table 6 */
processFibTable(true); /* Table 7 */
processLocalTable(true); /* Table 9 */
}
protected void processMeterTable(boolean install) {
//Green meter : Pass all traffic
Band dropBand = DefaultBand.builder()
.ofType(Band.Type.DROP)
.withRate(0xFFFFFFFF) /* Max Rate */
.build();
MeterRequest.Builder ops = DefaultMeterRequest.builder()
.forDevice(deviceId)
.withBands(Collections.singletonList(dropBand))
.fromApp(appId);
Meter meter = meterService.submit(install ? ops.add() : ops.remove());
defaultMeterId = meter.id();
}
protected void processPortBasedProtoTable(boolean install) {
/* Default action */
processTableMissGoTo(install, PORT_BASED_PROTO_TABLE, VLAN_CHECK_TABLE, "Provisioned port-based table");
}
protected void processVlanCheckTable(boolean install) {
/* Default action */
processTableMissDrop(install, VLAN_CHECK_TABLE, "Provisioned vlantable drop");
processTaggedPackets(install);
}
/* Tagged packets to VLAN_MAC_XLATE */
protected void processTaggedPackets(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchVlanId(VlanId.ANY);
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(VLAN_MAC_XLATE_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(VLAN_CHECK_TABLE).build();
processFlowRule(install, rule, "Provisioned vlan table tagged packets");
}
protected void processVlanMacXlateTable(boolean install) {
/* Default action */
processTableMissGoTo(install, VLAN_MAC_XLATE_TABLE, VLAN_CIRCUIT_TABLE, "Provisioned vlan mac table");
}
protected void processVlanCircuitTable(boolean install) {
/* Default action */
processTableMissDrop(install, VLAN_CIRCUIT_TABLE, "Provisioned vlan circuit");
}
private void processPriorityMapTable(boolean install) {
/* Not required currently */
}
protected void processL3IFMacDATable(boolean install) {
int table = L3_IF_MAC_DA_TABLE;
/* Default action */
processTableMissDrop(install, table, "Provisioned l3 table drop");
/* Allow MAC broadcast frames on all ports */
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthDst(MacAddress.BROADCAST);
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(ETHER_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(table).build();
processFlowRule(install, rule, "Provisioned l3 table");
}
protected void processEtherTable(boolean install) {
int table = ETHER_TABLE;
/* Default action */
processTableMissDrop(install, table, "Provisioned ether type table drop");
/* Arp to controller */
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.TYPE_ARP);
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.punt();
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(table).build();
processFlowRule(install, rule, "Provisioned ether type table arp");
/* IP to FIB_TABLE */
selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
treatment = DefaultTrafficTreatment.builder();
treatment.transition(FIB_TABLE);
rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(table).build();
processFlowRule(install, rule, "Provisioned ether type table ip");
}
protected void processFibTable(boolean install) {
/* Default action */
processTableMissDrop(install, FIB_TABLE, "Provisioned fib drop");
}
private void processLocalTable(boolean install) {
int table = LOCAL_TABLE;
/* Default action */
processTableMissDrop(install, table, "Provisioned local table drop");
/* Send all protocols to controller */
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.punt();
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(CONTROLLER_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(table).build();
processFlowRule(install, rule, "Provisioned ether type table to controller");
}
}