| /* |
| * Copyright 2016-present 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 org.onlab.osgi.ServiceDirectory; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.MacAddress; |
| 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.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.Criterion; |
| import org.onosproject.net.flow.criteria.EthTypeCriterion; |
| import org.onosproject.net.flow.criteria.IPCriterion; |
| import org.onosproject.net.flow.criteria.PortCriterion; |
| import org.onosproject.net.flow.criteria.TunnelIdCriterion; |
| import org.onosproject.net.flow.criteria.UdpPortCriterion; |
| import org.onosproject.net.flow.instructions.Instruction; |
| 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; |
| |
| import java.util.Optional; |
| |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Driver for OpenstackSwitching. |
| */ |
| public class OpenstackPipeline extends DefaultSingleTablePipeline |
| implements Pipeliner { |
| |
| 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 static final int SRC_VNI_TABLE = 0; |
| protected static final int ACL_TABLE = 1; |
| protected static final int CT_TABLE = 2; |
| protected static final int JUMP_TABLE = 3; |
| protected static final int ROUTING_TABLE = 4; |
| protected static final int FORWARDING_TABLE = 5; |
| protected static final int DUMMY_TABLE = 10; |
| protected static final int LAST_TABLE = FORWARDING_TABLE; |
| |
| private static final int DROP_PRIORITY = 0; |
| private static final int HIGH_PRIORITY = 30000; |
| private static final int TIME_OUT = 0; |
| private static final int DHCP_SERVER_PORT = 67; |
| private static final String VIRTUAL_GATEWAY_MAC = "fe:00:00:00:00:02"; |
| |
| |
| @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.OpenstackPipeline"); |
| |
| initializePipeline(); |
| } |
| |
| @Override |
| public void filter(FilteringObjective filteringObjective) { |
| super.filter(filteringObjective); |
| } |
| |
| @Override |
| public void next(NextObjective nextObjective) { |
| super.next(nextObjective); |
| } |
| |
| @Override |
| public void forward(ForwardingObjective forwardingObjective) { |
| FlowRule flowRule; |
| |
| switch (forwardingObjective.flag()) { |
| case SPECIFIC: |
| flowRule = processSpecific(forwardingObjective); |
| break; |
| case VERSATILE: |
| flowRule = processVersatile(forwardingObjective); |
| break; |
| default: |
| fail(forwardingObjective, ObjectiveError.UNKNOWN); |
| log.warn("Unknown forwarding flag {}", forwardingObjective.flag()); |
| return; |
| } |
| |
| if (forwardingObjective.op().equals(Objective.Operation.ADD)) { |
| applyRules(true, flowRule); |
| } else { |
| applyRules(false, flowRule); |
| } |
| |
| } |
| |
| private void initializePipeline() { |
| connectTables(SRC_VNI_TABLE, ACL_TABLE); // Table 0 -> Table 1 |
| //FIXME CT table needs to be reconstructed using OVS 2.5 connection tracking feature. |
| connectTables(CT_TABLE, JUMP_TABLE); // Table 2 -> Table 3 |
| setUpTableMissEntry(ACL_TABLE); |
| setupJumpTable(); |
| } |
| |
| private void connectTables(int fromTable, int toTable) { |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| |
| treatment.transition(toTable); |
| |
| FlowRule flowRule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DROP_PRIORITY) |
| .fromApp(appId) |
| .makePermanent() |
| .forTable(fromTable) |
| .build(); |
| |
| applyRules(true, flowRule); |
| } |
| |
| private void setUpTableMissEntry(int table) { |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| |
| treatment.drop(); |
| |
| FlowRule flowRule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DROP_PRIORITY) |
| .fromApp(appId) |
| .makePermanent() |
| .forTable(table) |
| .build(); |
| |
| applyRules(true, flowRule); |
| } |
| |
| private void setupJumpTable() { |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| |
| selector.matchEthDst(MacAddress.valueOf(VIRTUAL_GATEWAY_MAC)); |
| treatment.transition(ROUTING_TABLE); |
| |
| FlowRule flowRule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(HIGH_PRIORITY) |
| .fromApp(appId) |
| .makePermanent() |
| .forTable(JUMP_TABLE) |
| .build(); |
| |
| applyRules(true, flowRule); |
| |
| selector = DefaultTrafficSelector.builder(); |
| treatment = DefaultTrafficTreatment.builder(); |
| |
| treatment.transition(FORWARDING_TABLE); |
| |
| flowRule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DROP_PRIORITY) |
| .fromApp(appId) |
| .makePermanent() |
| .forTable(JUMP_TABLE) |
| .build(); |
| |
| applyRules(true, flowRule); |
| } |
| |
| private void applyRules(boolean install, FlowRule flowRule) { |
| FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); |
| |
| flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule); |
| |
| flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() { |
| @Override |
| public void onSuccess(FlowRuleOperations ops) { |
| log.debug("Provisioned vni or forwarding table"); |
| } |
| |
| @Override |
| public void onError(FlowRuleOperations ops) { |
| log.debug("Failed to privision vni or forwarding table"); |
| } |
| })); |
| } |
| |
| private FlowRule processVersatile(ForwardingObjective forwardingObjective) { |
| log.debug("Processing versatile forwarding objective"); |
| |
| FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(forwardingObjective.selector()) |
| .withTreatment(forwardingObjective.treatment()) |
| .withPriority(forwardingObjective.priority()) |
| .fromApp(forwardingObjective.appId()); |
| |
| if (forwardingObjective.permanent()) { |
| ruleBuilder.makePermanent(); |
| } else { |
| ruleBuilder.makeTemporary(TIME_OUT); |
| } |
| |
| //ARP & DHCP Rule |
| EthTypeCriterion ethCriterion = |
| (EthTypeCriterion) forwardingObjective.selector().getCriterion(Criterion.Type.ETH_TYPE); |
| UdpPortCriterion udpPortCriterion = (UdpPortCriterion) forwardingObjective |
| .selector().getCriterion(Criterion.Type.UDP_DST); |
| if (ethCriterion != null) { |
| if (ethCriterion.ethType().toShort() == Ethernet.TYPE_ARP || |
| ethCriterion.ethType().toShort() == Ethernet.TYPE_LLDP) { |
| ruleBuilder.forTable(SRC_VNI_TABLE); |
| return ruleBuilder.build(); |
| } else if (udpPortCriterion != null && udpPortCriterion.udpPort().toInt() == DHCP_SERVER_PORT) { |
| ruleBuilder.forTable(SRC_VNI_TABLE); |
| return ruleBuilder.build(); |
| } |
| } |
| |
| return null; |
| } |
| |
| private FlowRule processSpecific(ForwardingObjective forwardingObjective) { |
| log.debug("Processing specific forwarding objective"); |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| |
| |
| Optional<Instruction> group = forwardingObjective.treatment().immediate().stream() |
| .filter(i -> i.type() == Instruction.Type.GROUP).findAny(); |
| int tableType = tableType(forwardingObjective); |
| if (tableType != LAST_TABLE && !group.isPresent()) { |
| treatment.transition(nextTable(tableType)); |
| } |
| forwardingObjective.treatment().allInstructions().stream() |
| .filter(i -> i.type() != Instruction.Type.NOACTION).forEach(treatment::add); |
| |
| FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(forwardingObjective.selector()) |
| .withTreatment(treatment.build()) |
| .withPriority(forwardingObjective.priority()) |
| .fromApp(forwardingObjective.appId()) |
| .forTable(tableType); |
| |
| if (forwardingObjective.permanent()) { |
| ruleBuilder.makePermanent(); |
| } else { |
| ruleBuilder.makeTemporary(TIME_OUT); |
| } |
| |
| return ruleBuilder.build(); |
| } |
| |
| int tableType(ForwardingObjective fo) { |
| |
| IPCriterion ipSrc = (IPCriterion) fo.selector().getCriterion(Criterion.Type.IPV4_SRC); |
| IPCriterion ipDst = (IPCriterion) fo.selector().getCriterion(Criterion.Type.IPV4_DST); |
| TunnelIdCriterion tunnelId = |
| (TunnelIdCriterion) fo.selector().getCriterion(Criterion.Type.TUNNEL_ID); |
| PortCriterion inPort = (PortCriterion) fo.selector().getCriterion(Criterion.Type.IN_PORT); |
| Optional<Instruction> output = fo.treatment().immediate().stream() |
| .filter(i -> i.type() == Instruction.Type.OUTPUT).findAny(); |
| Optional<Instruction> group = fo.treatment().immediate().stream() |
| .filter(i -> i.type() == Instruction.Type.GROUP).findAny(); |
| |
| // TODO: Add the Connection Tracking Table |
| if (inPort != null) { |
| return SRC_VNI_TABLE; |
| } else if (output.isPresent()) { |
| return FORWARDING_TABLE; |
| } else if ((ipSrc != null && ipSrc.ip().prefixLength() == 32 && |
| ipDst != null && ipDst.ip().prefixLength() == 32) || |
| (ipSrc != null && ipSrc.ip().prefixLength() == 32 && ipDst == null) || |
| (ipDst != null && ipDst.ip().prefixLength() == 32 && ipSrc == null)) { |
| return ACL_TABLE; |
| } else if ((tunnelId != null && ipSrc != null && ipDst != null) || group.isPresent()) { |
| return ROUTING_TABLE; |
| } |
| |
| return DUMMY_TABLE; |
| } |
| |
| int nextTable(int baseTable) { |
| |
| return baseTable + 1; |
| } |
| |
| private void fail(Objective obj, ObjectiveError error) { |
| obj.context().ifPresent(context -> context.onError(obj, error)); |
| } |
| } |
| |