/* * 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.cordvtn; import org.onlab.packet.Ip4Address; import org.onlab.util.ItemNotFoundException; import org.onosproject.core.ApplicationId; import org.onosproject.net.DeviceId; import org.onosproject.net.Port; import org.onosproject.net.behaviour.ExtensionTreatmentResolver; import org.onosproject.net.driver.DefaultDriverData; import org.onosproject.net.driver.DefaultDriverHandler; import org.onosproject.net.driver.Driver; import org.onosproject.net.driver.DriverHandler; import org.onosproject.net.driver.DriverService; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.instructions.ExtensionPropertyException; import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.flowobjective.DefaultForwardingObjective; import org.onosproject.net.flowobjective.FlowObjectiveService; import org.onosproject.net.flowobjective.ForwardingObjective; import org.slf4j.Logger; import java.util.List; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST; import static org.slf4j.LoggerFactory.getLogger; /** * Populates rules for virtual tenant network. */ public final class CordVtnRuleInstaller { protected final Logger log = getLogger(getClass()); private static final int DEFAULT_PRIORITY = 5000; private final ApplicationId appId; private final FlowObjectiveService flowObjectiveService; private final DriverService driverService; private final String tunnelType; /** * Creates a new rule installer. * * @param appId application id * @param flowObjectiveService flow objective service * @param driverService driver service * @param tunnelType tunnel type */ public CordVtnRuleInstaller(ApplicationId appId, FlowObjectiveService flowObjectiveService, DriverService driverService, String tunnelType) { this.appId = appId; this.flowObjectiveService = flowObjectiveService; this.driverService = driverService; this.tunnelType = checkNotNull(tunnelType); } /** * Installs flow rules for tunnel in traffic. * * @param deviceId device id to install flow rules * @param inPort in port * @param dstInfos list of destination info */ public void installFlowRulesTunnelIn(DeviceId deviceId, Port inPort, List dstInfos) { dstInfos.stream().forEach(dstInfo -> { ForwardingObjective.Builder fBuilder = vtnRulesSameNode(inPort, dstInfo); if (fBuilder != null) { flowObjectiveService.forward(deviceId, fBuilder.add()); } }); } /** * Installs flow rules for local in traffic. * * @param deviceId device id to install flow rules * @param inPort in port * @param dstInfos list of destination info */ public void installFlowRulesLocalIn(DeviceId deviceId, Port inPort, List dstInfos) { dstInfos.stream().forEach(dstInfo -> { ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ? vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo); if (fBuilder != null) { flowObjectiveService.forward(deviceId, fBuilder.add()); } }); } /** * Uninstalls flow rules associated with a given port from a given device. * * @param deviceId device id * @param inPort port associated with removed host * @param dstInfos list of destination info */ public void uninstallFlowRules(DeviceId deviceId, Port inPort, List dstInfos) { dstInfos.stream().forEach(dstInfo -> { ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ? vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo); if (fBuilder != null) { flowObjectiveService.forward(deviceId, fBuilder.remove()); } }); } /** * Returns forwarding objective builder to provision basic virtual tenant network. * This method cares for the traffics whose source and destination device is the same. * * @param inPort in port * @param dstInfo destination information * @return forwarding objective builder */ private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) { checkArgument(inPort.element().id().equals(dstInfo.output().element().id())); TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); sBuilder.matchInPort(inPort.number()) .matchEthDst(dstInfo.mac()); if (isTunnelPort(inPort)) { sBuilder.matchTunnelId(dstInfo.tunnelId()); } tBuilder.setOutput(dstInfo.output().number()); return DefaultForwardingObjective.builder() .withSelector(sBuilder.build()) .withTreatment(tBuilder.build()) .withPriority(DEFAULT_PRIORITY) .withFlag(ForwardingObjective.Flag.VERSATILE) .fromApp(appId) .makePermanent(); } /** * Returns forwarding objective builder to provision basic virtual tenant network. * This method cares for the traffics whose source and destination is not the same. * * @param deviceId device id to install flow rules * @param inPort in port * @param dstInfo destination information * @return forwarding objective, or null if it fails to build it */ private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) { checkArgument(isTunnelPort(dstInfo.output())); TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); ExtensionTreatment extTreatment = getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address()); if (extTreatment == null) { return null; } sBuilder.matchInPort(inPort.number()) .matchEthDst(dstInfo.mac()); tBuilder.extension(extTreatment, deviceId) .setTunnelId(dstInfo.tunnelId()) .setOutput(dstInfo.output().number()); return DefaultForwardingObjective.builder() .withSelector(sBuilder.build()) .withTreatment(tBuilder.build()) .withPriority(DEFAULT_PRIORITY) .withFlag(ForwardingObjective.Flag.VERSATILE) .fromApp(appId) .makePermanent(); } /** * Checks if a given port is tunnel interface or not. * It assumes the tunnel interface contains tunnelType string in its name. * * @param port port * @return true if the port is tunnel interface, false otherwise. */ private boolean isTunnelPort(Port port) { return port.annotations().value("portName").contains(tunnelType); } /** * Returns extension instruction to set tunnel destination. * * @param deviceId device id * @param remoteIp tunnel destination address * @return extension treatment or null if it fails to get instruction */ private ExtensionTreatment getTunnelDstInstruction(DeviceId deviceId, Ip4Address remoteIp) { try { Driver driver = driverService.getDriver(deviceId); DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId)); ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class); ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type()); treatment.setPropertyValue("tunnelDst", remoteIp); return treatment; } catch (ItemNotFoundException | UnsupportedOperationException | ExtensionPropertyException e) { log.error("Failed to get extension instruction to set tunnel dst {}", deviceId); return null; } } }