blob: 9e24a1761cfcc7df5b14d36d6b517372d6c0d167 [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.driver.pipeline;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.EthType.EtherType;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.Pipeliner;
import org.onosproject.net.behaviour.PipelinerContext;
import org.onosproject.net.device.DeviceService;
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.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.criteria.Criterion.Type;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveStore;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.slf4j.Logger;
/**
* Driver for standard OpenVSwitch.
*/
public class OpenVSwitchPipeline extends DefaultSingleTablePipeline
implements Pipeliner {
private static final String VTN_APP_ID = "org.onosproject.app.vtn";
private final Logger log = getLogger(getClass());
private CoreService coreService;
private ServiceDirectory serviceDirectory;
protected FlowObjectiveStore flowObjectiveStore;
protected DeviceId deviceId;
protected ApplicationId appId;
protected FlowRuleService flowRuleService;
protected DeviceService deviceService;
private static final int TIME_OUT = 0;
private static final int CLASSIFIER_TABLE = 0;
private static final int ARP_TABLE = 10;
private static final int DNAT_TABLE = 20;
private static final int L3FWD_TABLE = 30;
private static final int SNAT_TABLE = 40;
private static final int MAC_TABLE = 50;
private static final int TABLE_MISS_PRIORITY = 0;
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
super.init(deviceId, context);
this.serviceDirectory = context.directory();
this.deviceId = deviceId;
coreService = serviceDirectory.get(CoreService.class);
flowRuleService = serviceDirectory.get(FlowRuleService.class);
flowObjectiveStore = context.store();
appId = coreService
.registerApplication("org.onosproject.driver.OpenVSwitchPipeline");
initializePipeline();
}
@Override
public void filter(FilteringObjective filteringObjective) {
super.filter(filteringObjective);
}
@Override
public void forward(ForwardingObjective fwd) {
if (!VTN_APP_ID.equals(fwd.appId().name())) {
super.forward(fwd);
return;
}
Collection<FlowRule> rules;
FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations
.builder();
rules = processForward(fwd);
switch (fwd.op()) {
case ADD:
rules.stream().filter(Objects::nonNull)
.forEach(flowOpsBuilder::add);
break;
case REMOVE:
rules.stream().filter(Objects::nonNull)
.forEach(flowOpsBuilder::remove);
break;
default:
fail(fwd, ObjectiveError.UNKNOWN);
log.warn("Unknown forwarding type {}", fwd.op());
}
flowRuleService.apply(flowOpsBuilder
.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
pass(fwd);
}
@Override
public void onError(FlowRuleOperations ops) {
fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
}
}));
}
@Override
public void next(NextObjective nextObjective) {
super.next(nextObjective);
}
private void initializePipeline() {
processClassifierTable(true);
processArpTable(true);
processDnatTable(true);
processL3fwdTable(true);
processSnatTable(true);
processMacTable(true);
}
private void processClassifierTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(MAC_TABLE);
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(CLASSIFIER_TABLE).build();
applyRules(install, rule);
}
private void processArpTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(MAC_TABLE);
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(ARP_TABLE).build();
applyRules(install, rule);
}
private void processDnatTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(MAC_TABLE);
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(DNAT_TABLE).build();
applyRules(install, rule);
}
private void processL3fwdTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(SNAT_TABLE);
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(L3FWD_TABLE).build();
applyRules(install, rule);
}
private void processSnatTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(MAC_TABLE);
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(SNAT_TABLE).build();
applyRules(install, rule);
}
private void processMacTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.drop();
FlowRule rule;
rule = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
.makePermanent().forTable(MAC_TABLE).build();
applyRules(install, rule);
}
private void applyRules(boolean install, FlowRule rule) {
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
ops = install ? ops.add(rule) : ops.remove(rule);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("ONOSW provisioned " + rule.tableId() + " table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("ONOSW failed to provision " + rule.tableId() + " table");
}
}));
}
private Collection<FlowRule> processForward(ForwardingObjective fwd) {
switch (fwd.flag()) {
case SPECIFIC:
return processSpecific(fwd);
case VERSATILE:
return processVersatile(fwd);
default:
fail(fwd, ObjectiveError.UNKNOWN);
log.warn("Unknown forwarding flag {}", fwd.flag());
}
return Collections.emptySet();
}
private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
log.debug("Processing versatile forwarding objective");
return Collections.emptyList();
}
private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
log.debug("Processing specific forwarding objective");
TrafficSelector selector = fwd.selector();
TrafficTreatment tb = fwd.treatment();
FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
.fromApp(fwd.appId()).withPriority(fwd.priority())
.forDevice(deviceId).withSelector(selector)
.withTreatment(tb).makeTemporary(TIME_OUT);
ruleBuilder.withPriority(fwd.priority());
if (fwd.permanent()) {
ruleBuilder.makePermanent();
}
Integer transition = null;
Integer forTable = null;
// MAC table flow rules
if ((selector.getCriterion(Type.TUNNEL_ID) != null && selector
.getCriterion(Type.ETH_DST) != null)
|| tb.allInstructions().contains(Instructions.createDrop())) {
forTable = MAC_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
// CLASSIFIER table flow rules
if (selector.getCriterion(Type.IN_PORT) != null) {
forTable = CLASSIFIER_TABLE;
if (selector.getCriterion(Type.ETH_SRC) != null
&& selector.getCriterion(Type.ETH_DST) != null) {
transition = L3FWD_TABLE;
} else if (selector.getCriterion(Type.ETH_SRC) != null
|| selector.getCriterion(Type.TUNNEL_ID) != null) {
transition = MAC_TABLE;
} else if (selector.getCriterion(Type.IPV4_DST) != null) {
transition = DNAT_TABLE;
}
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
// ARP table flow rules
if (selector.getCriterion(Type.ETH_TYPE) != null
&& selector.getCriterion(Type.ETH_TYPE).equals(Criteria
.matchEthType(EtherType.ARP.ethType().toShort()))) {
// CLASSIFIER table arp flow rules
if (selector.getCriterion(Type.TUNNEL_ID) == null) {
transition = ARP_TABLE;
forTable = CLASSIFIER_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
forTable = ARP_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
// L3FWD table flow rules
if (selector.getCriterion(Type.TUNNEL_ID) != null
&& selector.getCriterion(Type.IPV4_DST) != null) {
transition = MAC_TABLE;
forTable = L3FWD_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
// DNAT table flow rules
if (selector.getCriterion(Type.IPV4_DST) != null) {
transition = L3FWD_TABLE;
forTable = DNAT_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
// SNAT table flow rules
if (selector.getCriterion(Type.TUNNEL_ID) != null
&& selector.getCriterion(Type.IPV4_SRC) != null) {
transition = MAC_TABLE;
forTable = SNAT_TABLE;
return reassemblyFlowRule(ruleBuilder, tb, transition, forTable);
}
return Collections.singletonList(ruleBuilder.build());
}
private Collection<FlowRule> reassemblyFlowRule(FlowRule.Builder ruleBuilder,
TrafficTreatment tb,
Integer transition,
Integer forTable) {
if (transition != null) {
TrafficTreatment.Builder newTraffic = DefaultTrafficTreatment
.builder();
tb.allInstructions().forEach(t -> newTraffic.add(t));
newTraffic.transition(transition);
ruleBuilder.withTreatment(newTraffic.build());
} else {
ruleBuilder.withTreatment(tb);
}
if (forTable != null) {
ruleBuilder.forTable(forTable);
}
return Collections.singletonList(ruleBuilder.build());
}
private void fail(Objective obj, ObjectiveError error) {
if (obj.context().isPresent()) {
obj.context().get().onError(obj, error);
}
}
private void pass(Objective obj) {
if (obj.context().isPresent()) {
obj.context().get().onSuccess(obj);
}
}
}